blob: 2937df40fd408277163a8a92c6aab7654f2ddff8 [file] [log] [blame]
[email protected]6f9832e2012-04-11 19:45:181// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]051236f2010-03-12 22:06:142// 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
[email protected]80751052011-11-12 17:10:588#include "base/bind.h"
[email protected]34b99632011-01-01 01:01:069#include "base/threading/thread.h"
[email protected]8ee65ba2011-04-12 20:53:2310#include "base/win/scoped_comptr.h"
11#include "base/win/scoped_handle.h"
[email protected]051236f2010-03-12 22:06:1412#include "chrome_frame/bho.h"
[email protected]97965e12010-04-09 00:51:1013//#include "chrome_frame/urlmon_moniker.h"
[email protected]051236f2010-03-12 22:06:1414#include "chrome_frame/test/chrome_frame_test_utils.h"
[email protected]8ee65ba2011-04-12 20:53:2315#include "chrome_frame/test/test_server.h"
[email protected]051236f2010-03-12 22:06:1416#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
23using testing::_;
24using testing::CreateFunctor;
25using testing::Eq;
26using testing::Invoke;
27using testing::SetArgumentPointee;
28using testing::StrEq;
29using testing::Return;
[email protected]97965e12010-04-09 00:51:1030using testing::DoAll;
[email protected]051236f2010-03-12 22:06:1431using testing::WithArgs;
32
[email protected]97965e12010-04-09 00:51:1033
[email protected]6f9832e2012-04-11 19:45:1834static const base::TimeDelta kUrlmonMonikerTimeout =
35 base::TimeDelta::FromSeconds(5);
[email protected]051236f2010-03-12 22:06:1436
37namespace {
38const 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
43class UrlmonMonikerTest : public testing::Test {
44 protected:
45 UrlmonMonikerTest() {
46 }
47};
48
49TEST_F(UrlmonMonikerTest, MonikerPatch) {
[email protected]515f2492011-01-14 10:36:2850 EXPECT_TRUE(MonikerPatch::Initialize());
51 EXPECT_TRUE(MonikerPatch::Initialize()); // Should be ok to call twice.
[email protected]051236f2010-03-12 22:06:1452 MonikerPatch::Uninitialize();
53}
54
55// Runs an HTTP server on a worker thread that has a message loop.
56class RunTestServer : public base::Thread {
57 public:
58 RunTestServer()
59 : base::Thread("TestServer"),
60 default_response_("/", kTestContent),
61 ready_(::CreateEvent(NULL, TRUE, FALSE, NULL)) {
62 }
63
[email protected]d583c3a2011-11-02 15:31:5664 ~RunTestServer() {
65 Stop();
66 }
67
[email protected]051236f2010-03-12 22:06:1468 bool Start() {
69 bool ret = StartWithOptions(Options(MessageLoop::TYPE_UI, 0));
70 if (ret) {
71 message_loop()->PostTask(FROM_HERE,
[email protected]80751052011-11-12 17:10:5872 base::Bind(&RunTestServer::StartServer, this));
[email protected]051236f2010-03-12 22:06:1473 wait_until_ready();
74 }
75 return ret;
76 }
77
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
84 bool wait_until_ready() {
[email protected]6f9832e2012-04-11 19:45:1885 return ::WaitForSingleObject(ready_, kUrlmonMonikerTimeout.InMilliseconds())
[email protected]051236f2010-03-12 22:06:1486 == WAIT_OBJECT_0;
87 }
88
89 protected:
90 scoped_ptr<test_server::SimpleWebServer> server_;
91 test_server::SimpleResponse default_response_;
[email protected]b90d7e802011-01-09 16:32:2092 base::win::ScopedHandle ready_;
[email protected]051236f2010-03-12 22:06:1493};
94
95// Helper class for running tests that rely on the NavigationManager.
96class UrlmonMonikerTestManager {
97 public:
98 explicit UrlmonMonikerTestManager(const wchar_t* test_url) {
[email protected]515f2492011-01-14 10:36:2899 EXPECT_TRUE(MonikerPatch::Initialize());
[email protected]051236f2010-03-12 22:06:14100 }
101
102 ~UrlmonMonikerTestManager() {
103 MonikerPatch::Uninitialize();
[email protected]051236f2010-03-12 22:06:14104 }
105
106 chrome_frame_test::TimedMsgLoop& loop() {
107 return loop_;
108 }
109
[email protected]051236f2010-03-12 22:06:14110 protected:
[email protected]051236f2010-03-12 22:06:14111 chrome_frame_test::TimedMsgLoop loop_;
112};
113
[email protected]97965e12010-04-09 00:51:10114ACTION_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
[email protected]051236f2010-03-12 22:06:14132// Wraps the MockBindStatusCallbackImpl mock object and allows the user
133// to specify expectations on the callback object.
134class UrlmonMonikerTestCallback {
135 public:
136 explicit UrlmonMonikerTestCallback(UrlmonMonikerTestManager* mgr)
[email protected]97965e12010-04-09 00:51:10137 : mgr_(mgr), clip_format_(0) {
[email protected]051236f2010-03-12 22:06:14138 }
139
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.
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(_, _))
[email protected]97965e12010-04-09 00:51:10158 .WillOnce(DoAll(SetBindInfo(true), Return(S_OK)));
[email protected]051236f2010-03-12 22:06:14159 } else if (bind_info_handling == REQUEST_SYNCHRONOUS) {
160 EXPECT_CALL(callback_, GetBindInfo(_, _))
[email protected]97965e12010-04-09 00:51:10161 .WillOnce(DoAll(SetBindInfo(false), Return(S_OK)));
[email protected]051236f2010-03-12 22:06:14162 } 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
182 HRESULT CreateUrlMonikerAndBindToStorage(const wchar_t* url,
183 IBindCtx** bind_ctx) {
[email protected]8ee65ba2011-04-12 20:53:23184 base::win::ScopedComPtr<IMoniker> moniker;
[email protected]051236f2010-03-12 22:06:14185 HRESULT hr = CreateURLMoniker(NULL, url, moniker.Receive());
186 EXPECT_TRUE(moniker != NULL);
187 if (moniker) {
[email protected]8ee65ba2011-04-12 20:53:23188 base::win::ScopedComPtr<IBindCtx> context;
[email protected]051236f2010-03-12 22:06:14189 ::CreateAsyncBindCtx(0, callback(), NULL, context.Receive());
190 DCHECK(context);
[email protected]8ee65ba2011-04-12 20:53:23191 base::win::ScopedComPtr<IStream> stream;
[email protected]051236f2010-03-12 22:06:14192 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
200 IBindStatusCallback* callback() {
201 return &callback_;
202 }
203
204 protected:
205 CComObjectStackEx<MockBindStatusCallbackImpl> callback_;
206 UrlmonMonikerTestManager* mgr_;
[email protected]97965e12010-04-09 00:51:10207 CLIPFORMAT clip_format_;
[email protected]051236f2010-03-12 22:06:14208};
209
[email protected]c8b1a3b2010-04-09 01:22:55210/*
211
[email protected]051236f2010-03-12 22:06:14212// Tests synchronously binding to a moniker and downloading the target.
213TEST_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
[email protected]8ee65ba2011-04-12 20:53:23224 base::win::ScopedComPtr<IBindCtx> bind_ctx;
[email protected]051236f2010-03-12 22:06:14225 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();
[email protected]051236f2010-03-12 22:06:14235}
236
237// Tests asynchronously binding to a moniker and downloading the target.
238TEST_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
[email protected]8ee65ba2011-04-12 20:53:23250 base::win::ScopedComPtr<IBindCtx> bind_ctx;
[email protected]051236f2010-03-12 22:06:14251 HRESULT hr = callback.CreateUrlMonikerAndBindToStorage(test_url,
252 bind_ctx.Receive());
253 EXPECT_EQ(MK_S_ASYNCHRONOUS, hr);
[email protected]6f9832e2012-04-11 19:45:18254 test.loop().RunFor(kUrlmonMonikerTimeout);
[email protected]051236f2010-03-12 22:06:14255
256 IBindCtx* release = bind_ctx.Detach();
257 EXPECT_EQ(0, release->Release());
[email protected]051236f2010-03-12 22:06:14258}
259
[email protected]fb716b302010-03-26 02:45:20260// Responds with the Chrome mime type.
261class 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
[email protected]051236f2010-03-12 22:06:14273// Downloads a document asynchronously and then verifies that the downloaded
274// contents were cached and the cache contents are correct.
[email protected]f6d0a662010-03-26 01:23:53275// TODO(tommi): Fix and re-enable.
276// http://code.google.com/p/chromium/issues/detail?id=39415
[email protected]fb716b302010-03-26 02:45:20277TEST_F(UrlmonMonikerTest, BindToStorageSwitchContent) {
[email protected]051236f2010-03-12 22:06:14278 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);
[email protected]fb716b302010-03-26 02:45:20283 ResponseWithContentType default_response("/", kTestContent);
[email protected]051236f2010-03-12 22:06:14284 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);
[email protected]6f9832e2012-04-11 19:45:18292 test.loop().RunFor(kUrlmonMonikerTimeout);
[email protected]051236f2010-03-12 22:06:14293
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);
[email protected]8ee65ba2011-04-12 20:53:23301 base::win::ScopedComPtr<IStream> stream;
[email protected]051236f2010-03-12 22:06:14302 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.
317TEST_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);
[email protected]fb716b302010-03-26 02:45:20323 ResponseWithContentType default_response("/", kTestContent);
[email protected]051236f2010-03-12 22:06:14324 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);
[email protected]6f9832e2012-04-11 19:45:18336 test.loop().RunFor(kUrlmonMonikerTimeout);
[email protected]051236f2010-03-12 22:06:14337
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
[email protected]97965e12010-04-09 00:51:10354*/