• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <atlbase.h>
6 #include <atlcom.h>
7 
8 #include "base/bind.h"
9 #include "base/threading/thread.h"
10 #include "base/win/scoped_comptr.h"
11 #include "base/win/scoped_handle.h"
12 #include "chrome_frame/bho.h"
13 //#include "chrome_frame/urlmon_moniker.h"
14 #include "chrome_frame/test/chrome_frame_test_utils.h"
15 #include "chrome_frame/test/test_server.h"
16 #include "chrome_frame/test/urlmon_moniker_tests.h"
17 #include "gmock/gmock.h"
18 #include "gtest/gtest.h"
19 
20 #define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING
21 #include "testing/gmock_mutant.h"
22 
23 using testing::_;
24 using testing::CreateFunctor;
25 using testing::Eq;
26 using testing::Invoke;
27 using testing::SetArgumentPointee;
28 using testing::StrEq;
29 using testing::Return;
30 using testing::DoAll;
31 using testing::WithArgs;
32 
33 
34 static const base::TimeDelta kUrlmonMonikerTimeout =
35      base::TimeDelta::FromSeconds(5);
36 
37 namespace {
38 const char kTestContent[] = "<html><head>"
39     "<meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\" />"
40     "</head><body>Test HTML content</body></html>";
41 }  // end namespace
42 
43 class UrlmonMonikerTest : public testing::Test {
44  protected:
UrlmonMonikerTest()45   UrlmonMonikerTest() {
46   }
47 };
48 
TEST_F(UrlmonMonikerTest,MonikerPatch)49 TEST_F(UrlmonMonikerTest, MonikerPatch) {
50   EXPECT_TRUE(MonikerPatch::Initialize());
51   EXPECT_TRUE(MonikerPatch::Initialize());  // Should be ok to call twice.
52   MonikerPatch::Uninitialize();
53 }
54 
55 // Runs an HTTP server on a worker thread that has a message loop.
56 class RunTestServer : public base::Thread {
57  public:
RunTestServer()58   RunTestServer()
59       : base::Thread("TestServer"),
60       default_response_("/", kTestContent),
61       ready_(::CreateEvent(NULL, TRUE, FALSE, NULL)) {
62   }
63 
~RunTestServer()64   ~RunTestServer() {
65     Stop();
66   }
67 
Start()68   bool Start() {
69     bool ret = StartWithOptions(Options(base::MessageLoop::TYPE_UI, 0));
70     if (ret) {
71       message_loop()->PostTask(FROM_HERE,
72                                base::Bind(&RunTestServer::StartServer, this));
73       wait_until_ready();
74     }
75     return ret;
76   }
77 
StartServer(RunTestServer * me)78   static void StartServer(RunTestServer* me) {
79     me->server_.reset(new test_server::SimpleWebServer(43210));
80     me->server_->AddResponse(&me->default_response_);
81     ::SetEvent(me->ready_);
82   }
83 
wait_until_ready()84   bool wait_until_ready() {
85     return ::WaitForSingleObject(ready_, kUrlmonMonikerTimeout.InMilliseconds())
86            == WAIT_OBJECT_0;
87   }
88 
89  protected:
90   scoped_ptr<test_server::SimpleWebServer> server_;
91   test_server::SimpleResponse default_response_;
92   base::win::ScopedHandle ready_;
93 };
94 
95 // Helper class for running tests that rely on the NavigationManager.
96 class UrlmonMonikerTestManager {
97  public:
UrlmonMonikerTestManager(const wchar_t * test_url)98   explicit UrlmonMonikerTestManager(const wchar_t* test_url) {
99     EXPECT_TRUE(MonikerPatch::Initialize());
100   }
101 
~UrlmonMonikerTestManager()102   ~UrlmonMonikerTestManager() {
103     MonikerPatch::Uninitialize();
104   }
105 
loop()106   chrome_frame_test::TimedMsgLoop& loop() {
107     return loop_;
108   }
109 
110  protected:
111   chrome_frame_test::TimedMsgLoop loop_;
112 };
113 
ACTION_P(SetBindInfo,is_async)114 ACTION_P(SetBindInfo, is_async) {
115   DWORD* flags = arg0;
116   BINDINFO* bind_info = arg1;
117 
118   DCHECK(flags);
119   DCHECK(bind_info);
120   DCHECK(bind_info->cbSize >= sizeof(BINDINFO));
121 
122   *flags = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
123   if (is_async)
124     *flags |= BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE;
125 
126   bind_info->dwBindVerb = BINDVERB_GET;
127   memset(&bind_info->stgmedData, 0, sizeof(STGMEDIUM));
128   bind_info->grfBindInfoF = 0;
129   bind_info->szCustomVerb = NULL;
130 }
131 
132 // Wraps the MockBindStatusCallbackImpl mock object and allows the user
133 // to specify expectations on the callback object.
134 class UrlmonMonikerTestCallback {
135  public:
UrlmonMonikerTestCallback(UrlmonMonikerTestManager * mgr)136   explicit UrlmonMonikerTestCallback(UrlmonMonikerTestManager* mgr)
137       : mgr_(mgr), clip_format_(0) {
138   }
139 
~UrlmonMonikerTestCallback()140   ~UrlmonMonikerTestCallback() {
141   }
142 
143   typedef enum GetBindInfoExpectations {
144     EXPECT_NO_CALL,
145     REQUEST_SYNCHRONOUS,
146     REQUEST_ASYNCHRONOUS,
147   } GET_BIND_INFO_EXPECTATION;
148 
149   // Sets gmock expectations for the IBindStatusCallback mock object.
SetCallbackExpectations(GetBindInfoExpectations bind_info_handling,HRESULT data_available_response,bool quit_loop_on_stop)150   void SetCallbackExpectations(GetBindInfoExpectations bind_info_handling,
151                                HRESULT data_available_response,
152                                bool quit_loop_on_stop) {
153     EXPECT_CALL(callback_, OnProgress(_, _, _, _))
154         .WillRepeatedly(Return(S_OK));
155 
156     if (bind_info_handling == REQUEST_ASYNCHRONOUS) {
157       EXPECT_CALL(callback_, GetBindInfo(_, _))
158           .WillOnce(DoAll(SetBindInfo(true), Return(S_OK)));
159     } else if (bind_info_handling == REQUEST_SYNCHRONOUS) {
160       EXPECT_CALL(callback_, GetBindInfo(_, _))
161           .WillOnce(DoAll(SetBindInfo(false), Return(S_OK)));
162     } else {
163       DCHECK(bind_info_handling == EXPECT_NO_CALL);
164     }
165 
166     EXPECT_CALL(callback_, OnStartBinding(_, _))
167         .WillOnce(Return(S_OK));
168 
169     EXPECT_CALL(callback_, OnDataAvailable(_, _, _, _))
170         .WillRepeatedly(Return(data_available_response));
171 
172     if (quit_loop_on_stop) {
173       // When expecting asynchronous
174       EXPECT_CALL(callback_, OnStopBinding(data_available_response, _))
175           .WillOnce(DoAll(QUIT_LOOP(mgr_->loop()), Return(S_OK)));
176     } else {
177       EXPECT_CALL(callback_, OnStopBinding(data_available_response, _))
178           .WillOnce(Return(S_OK));
179     }
180   }
181 
CreateUrlMonikerAndBindToStorage(const wchar_t * url,IBindCtx ** bind_ctx)182   HRESULT CreateUrlMonikerAndBindToStorage(const wchar_t* url,
183                                            IBindCtx** bind_ctx) {
184     base::win::ScopedComPtr<IMoniker> moniker;
185     HRESULT hr = CreateURLMoniker(NULL, url, moniker.Receive());
186     EXPECT_TRUE(moniker != NULL);
187     if (moniker) {
188       base::win::ScopedComPtr<IBindCtx> context;
189       ::CreateAsyncBindCtx(0, callback(), NULL, context.Receive());
190       DCHECK(context);
191       base::win::ScopedComPtr<IStream> stream;
192       hr = moniker->BindToStorage(context, NULL, IID_IStream,
193           reinterpret_cast<void**>(stream.Receive()));
194       if (SUCCEEDED(hr) && bind_ctx)
195         *bind_ctx = context.Detach();
196     }
197     return hr;
198   }
199 
callback()200   IBindStatusCallback* callback() {
201     return &callback_;
202   }
203 
204  protected:
205   CComObjectStackEx<MockBindStatusCallbackImpl> callback_;
206   UrlmonMonikerTestManager* mgr_;
207   CLIPFORMAT clip_format_;
208 };
209 
210 /*
211 
212 // Tests synchronously binding to a moniker and downloading the target.
213 TEST_F(UrlmonMonikerTest, BindToStorageSynchronous) {
214   const wchar_t test_url[] = L"http://localhost:43210/";
215   UrlmonMonikerTestManager test(test_url);
216   UrlmonMonikerTestCallback callback(&test);
217 
218   RunTestServer server_thread;
219   EXPECT_TRUE(server_thread.Start());
220 
221   callback.SetCallbackExpectations(
222       UrlmonMonikerTestCallback::REQUEST_SYNCHRONOUS, S_OK, false);
223 
224   base::win::ScopedComPtr<IBindCtx> bind_ctx;
225   HRESULT hr = callback.CreateUrlMonikerAndBindToStorage(test_url,
226                                                          bind_ctx.Receive());
227   // The download should have happened synchronously, so we don't expect
228   // MK_S_ASYNCHRONOUS or any errors.
229   EXPECT_EQ(S_OK, hr);
230 
231   IBindCtx* release = bind_ctx.Detach();
232   EXPECT_EQ(0, release->Release());
233 
234   server_thread.Stop();
235 }
236 
237 // Tests asynchronously binding to a moniker and downloading the target.
238 TEST_F(UrlmonMonikerTest, BindToStorageAsynchronous) {
239   const wchar_t test_url[] = L"http://localhost:43210/";
240   UrlmonMonikerTestManager test(test_url);
241   UrlmonMonikerTestCallback callback(&test);
242 
243   test_server::SimpleWebServer server(43210);
244   test_server::SimpleResponse default_response("/", kTestContent);
245   server.AddResponse(&default_response);
246 
247   callback.SetCallbackExpectations(
248       UrlmonMonikerTestCallback::REQUEST_ASYNCHRONOUS, S_OK, true);
249 
250   base::win::ScopedComPtr<IBindCtx> bind_ctx;
251   HRESULT hr = callback.CreateUrlMonikerAndBindToStorage(test_url,
252                                                          bind_ctx.Receive());
253   EXPECT_EQ(MK_S_ASYNCHRONOUS, hr);
254   test.loop().RunFor(kUrlmonMonikerTimeout);
255 
256   IBindCtx* release = bind_ctx.Detach();
257   EXPECT_EQ(0, release->Release());
258 }
259 
260 // Responds with the Chrome mime type.
261 class ResponseWithContentType : public test_server::SimpleResponse {
262  public:
263   ResponseWithContentType(const char* request_path,
264                           const std::string& contents)
265     : test_server::SimpleResponse(request_path, contents) {
266   }
267   virtual bool GetContentType(std::string* content_type) const {
268     *content_type = WideToASCII(kChromeMimeType);
269     return true;
270   }
271 };
272 
273 // Downloads a document asynchronously and then verifies that the downloaded
274 // contents were cached and the cache contents are correct.
275 // TODO(tommi): Fix and re-enable.
276 //  http://code.google.com/p/chromium/issues/detail?id=39415
277 TEST_F(UrlmonMonikerTest, BindToStorageSwitchContent) {
278   const wchar_t test_url[] = L"http://localhost:43210/";
279   UrlmonMonikerTestManager test(test_url);
280   UrlmonMonikerTestCallback callback(&test);
281 
282   test_server::SimpleWebServer server(43210);
283   ResponseWithContentType default_response("/", kTestContent);
284   server.AddResponse(&default_response);
285 
286   callback.SetCallbackExpectations(
287       UrlmonMonikerTestCallback::REQUEST_ASYNCHRONOUS, INET_E_TERMINATED_BIND,
288       true);
289 
290   HRESULT hr = callback.CreateUrlMonikerAndBindToStorage(test_url, NULL);
291   EXPECT_EQ(MK_S_ASYNCHRONOUS, hr);
292   test.loop().RunFor(kUrlmonMonikerTimeout);
293 
294   scoped_refptr<RequestData> request_data(
295       test.nav_manager().GetActiveRequestData(test_url));
296   EXPECT_TRUE(request_data != NULL);
297 
298   if (request_data) {
299     EXPECT_EQ(request_data->GetCachedContentSize(),
300               arraysize(kTestContent) - 1);
301     base::win::ScopedComPtr<IStream> stream;
302     request_data->GetResetCachedContentStream(stream.Receive());
303     EXPECT_TRUE(stream != NULL);
304     if (stream) {
305       char buffer[0xffff];
306       DWORD read = 0;
307       stream->Read(buffer, sizeof(buffer), &read);
308       EXPECT_EQ(read, arraysize(kTestContent) - 1);
309       EXPECT_EQ(0, memcmp(buffer, kTestContent, read));
310     }
311   }
312 }
313 
314 // Fetches content asynchronously first to cache it and then
315 // verifies that fetching the cached content the same way works as expected
316 // and happens synchronously.
317 TEST_F(UrlmonMonikerTest, BindToStorageCachedContent) {
318   const wchar_t test_url[] = L"http://localhost:43210/";
319   UrlmonMonikerTestManager test(test_url);
320   UrlmonMonikerTestCallback callback(&test);
321 
322   test_server::SimpleWebServer server(43210);
323   ResponseWithContentType default_response("/", kTestContent);
324   server.AddResponse(&default_response);
325 
326   // First set of expectations.  Download the contents
327   // asynchronously.  This should populate the cache so that
328   // the second request should be served synchronously without
329   // going to the server.
330   callback.SetCallbackExpectations(
331       UrlmonMonikerTestCallback::REQUEST_ASYNCHRONOUS, INET_E_TERMINATED_BIND,
332       true);
333 
334   HRESULT hr = callback.CreateUrlMonikerAndBindToStorage(test_url, NULL);
335   EXPECT_EQ(MK_S_ASYNCHRONOUS, hr);
336   test.loop().RunFor(kUrlmonMonikerTimeout);
337 
338   scoped_refptr<RequestData> request_data(
339       test.nav_manager().GetActiveRequestData(test_url));
340   EXPECT_TRUE(request_data != NULL);
341 
342   if (request_data) {
343     // This time, just accept the content as normal.
344     UrlmonMonikerTestCallback callback2(&test);
345     callback2.SetCallbackExpectations(
346         UrlmonMonikerTestCallback::EXPECT_NO_CALL, S_OK, false);
347     hr = callback2.CreateUrlMonikerAndBindToStorage(test_url, NULL);
348     // S_OK means that the operation completed synchronously.
349     // Otherwise we'd get MK_S_ASYNCHRONOUS.
350     EXPECT_EQ(S_OK, hr);
351   }
352 }
353 
354 */
355