blob: 80f27869f59475fa6acd210a6660399b9d1b2da7 [file] [log] [blame]
[email protected]bc73b4e52010-03-26 04:16:201// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]f7817822009-09-24 05:11:582// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome_frame/test/chrome_frame_test_utils.h"
6
7#include <atlbase.h>
8#include <atlwin.h>
9#include <iepmapi.h>
[email protected]42d7c022009-11-20 22:44:5810#include <sddl.h>
[email protected]f7817822009-09-24 05:11:5811
[email protected]d8b83b212010-04-07 18:27:5212#include "base/file_version_info.h"
[email protected]d23ca372010-02-16 20:36:0913#include "base/file_util.h"
[email protected]cb4b1e02009-10-29 21:46:2114#include "base/message_loop.h"
[email protected]d23ca372010-02-16 20:36:0915#include "base/path_service.h"
[email protected]f7817822009-09-24 05:11:5816#include "base/registry.h" // to find IE and firefox
[email protected]ba443b72010-05-14 23:34:3817#include "base/scoped_bstr_win.h"
[email protected]f7817822009-09-24 05:11:5818#include "base/scoped_handle.h"
19#include "base/scoped_comptr_win.h"
[email protected]d55194ca2010-03-11 18:25:4520#include "base/utf_string_conversions.h"
[email protected]f7817822009-09-24 05:11:5821#include "base/win_util.h"
22#include "chrome/common/chrome_switches.h"
[email protected]bc73b4e52010-03-26 04:16:2023#include "chrome/common/chrome_paths.h"
24#include "chrome/common/chrome_paths_internal.h"
[email protected]7bc272f2009-12-09 01:09:2825#include "chrome_frame/utils.h"
[email protected]bc73b4e52010-03-26 04:16:2026
[email protected]cb4b1e02009-10-29 21:46:2127#include "testing/gtest/include/gtest/gtest.h"
[email protected]f7817822009-09-24 05:11:5828
29namespace chrome_frame_test {
30
[email protected]7aa8d972010-02-17 04:24:4931const int kDefaultWaitForIEToTerminateMs = 10 * 1000;
32
[email protected]f7817822009-09-24 05:11:5833const wchar_t kIEImageName[] = L"iexplore.exe";
34const wchar_t kIEBrokerImageName[] = L"ieuser.exe";
35const wchar_t kFirefoxImageName[] = L"firefox.exe";
36const wchar_t kOperaImageName[] = L"opera.exe";
37const wchar_t kSafariImageName[] = L"safari.exe";
38const wchar_t kChromeImageName[] = L"chrome.exe";
[email protected]d8b83b212010-04-07 18:27:5239const wchar_t kIEProfileName[] = L"iexplore";
[email protected]128f9d92010-04-12 15:58:5240const wchar_t kChromeLauncher[] = L"chrome_launcher.exe";
[email protected]ba443b72010-05-14 23:34:3841const int kChromeFrameLongNavigationTimeoutInSeconds = 10;
[email protected]f7817822009-09-24 05:11:5842
[email protected]f7817822009-09-24 05:11:5843// Callback function for EnumThreadWindows.
44BOOL CALLBACK CloseWindowsThreadCallback(HWND hwnd, LPARAM param) {
45 int& count = *reinterpret_cast<int*>(param);
46 if (IsWindowVisible(hwnd)) {
47 if (IsWindowEnabled(hwnd)) {
48 DWORD results = 0;
49 if (!::SendMessageTimeout(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0, SMTO_BLOCK,
50 10000, &results)) {
[email protected]421e6142010-01-15 22:14:2551 LOG(WARNING) << "Window hung: " << StringPrintf(L"%08X", hwnd);
[email protected]f7817822009-09-24 05:11:5852 }
53 count++;
54 } else {
55 DLOG(WARNING) << "Skipping disabled window: "
56 << StringPrintf(L"%08X", hwnd);
57 }
58 }
59 return TRUE; // continue enumeration
60}
61
62// Attempts to close all non-child, visible windows on the given thread.
63// The return value is the number of visible windows a close request was
64// sent to.
65int CloseVisibleTopLevelWindowsOnThread(DWORD thread_id) {
66 int window_close_attempts = 0;
67 EnumThreadWindows(thread_id, CloseWindowsThreadCallback,
68 reinterpret_cast<LPARAM>(&window_close_attempts));
69 return window_close_attempts;
70}
71
72// Enumerates the threads of a process and attempts to close visible non-child
73// windows on all threads of the process.
74// The return value is the number of visible windows a close request was
75// sent to.
76int CloseVisibleWindowsOnAllThreads(HANDLE process) {
77 DWORD process_id = ::GetProcessId(process);
78 if (process_id == 0) {
79 NOTREACHED();
80 return 0;
81 }
82
83 ScopedHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));
84 if (!snapshot.IsValid()) {
85 NOTREACHED();
86 return 0;
87 }
88
89 int window_close_attempts = 0;
90 THREADENTRY32 te = { sizeof(THREADENTRY32) };
91 if (Thread32First(snapshot, &te)) {
92 do {
93 if (RTL_CONTAINS_FIELD(&te, te.dwSize, th32OwnerProcessID) &&
94 te.th32OwnerProcessID == process_id) {
95 window_close_attempts +=
96 CloseVisibleTopLevelWindowsOnThread(te.th32ThreadID);
97 }
98 te.dwSize = sizeof(te);
99 } while (Thread32Next(snapshot, &te));
100 }
101
102 return window_close_attempts;
103}
104
[email protected]f7817822009-09-24 05:11:58105std::wstring GetExecutableAppPath(const std::wstring& file) {
106 std::wstring kAppPathsKey =
107 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\";
108
109 std::wstring app_path;
110 RegKey key(HKEY_LOCAL_MACHINE, (kAppPathsKey + file).c_str());
111 if (key.Handle()) {
112 key.ReadValue(NULL, &app_path);
113 }
114
115 return app_path;
116}
117
118std::wstring FormatCommandForApp(const std::wstring& exe_name,
119 const std::wstring& argument) {
120 std::wstring reg_path(StringPrintf(L"Applications\\%ls\\shell\\open\\command",
121 exe_name.c_str()));
122 RegKey key(HKEY_CLASSES_ROOT, reg_path.c_str());
123
124 std::wstring command;
125 if (key.Handle()) {
126 key.ReadValue(NULL, &command);
127 int found = command.find(L"%1");
128 if (found >= 0) {
129 command.replace(found, 2, argument);
130 }
131 }
132 return command;
133}
134
135base::ProcessHandle LaunchExecutable(const std::wstring& executable,
136 const std::wstring& argument) {
137 base::ProcessHandle process = NULL;
138 std::wstring path = GetExecutableAppPath(executable);
139 if (path.empty()) {
140 path = FormatCommandForApp(executable, argument);
141 if (path.empty()) {
[email protected]7ec1f4e2010-05-19 18:43:21142 LOG(ERROR) << "Failed to find executable: " << executable;
[email protected]f7817822009-09-24 05:11:58143 } else {
[email protected]51343d5a2009-10-26 22:39:33144 CommandLine cmdline = CommandLine::FromString(path);
[email protected]7ec1f4e2010-05-19 18:43:21145 if (!base::LaunchApp(cmdline, false, false, &process)) {
146 LOG(ERROR) << "LaunchApp failed: " << ::GetLastError();
147 }
[email protected]f7817822009-09-24 05:11:58148 }
149 } else {
[email protected]d1540c82009-10-27 00:18:32150 CommandLine cmdline((FilePath(path)));
[email protected]f7817822009-09-24 05:11:58151 cmdline.AppendLooseValue(argument);
[email protected]7ec1f4e2010-05-19 18:43:21152 if (!base::LaunchApp(cmdline, false, false, &process)) {
153 LOG(ERROR) << "LaunchApp failed: " << ::GetLastError();
154 }
[email protected]f7817822009-09-24 05:11:58155 }
156 return process;
157}
158
159base::ProcessHandle LaunchFirefox(const std::wstring& url) {
160 return LaunchExecutable(kFirefoxImageName, url);
161}
162
163base::ProcessHandle LaunchSafari(const std::wstring& url) {
164 return LaunchExecutable(kSafariImageName, url);
165}
166
167base::ProcessHandle LaunchChrome(const std::wstring& url) {
[email protected]d23ca372010-02-16 20:36:09168 std::wstring path;
169 PathService::Get(base::DIR_MODULE, &path);
170 file_util::AppendToPath(&path, kChromeImageName);
171
172 FilePath exe_path(path);
173 CommandLine cmd(exe_path);
[email protected]5714151f2010-02-16 22:26:20174 std::wstring args = L"--";
175 args += ASCIIToWide(switches::kNoFirstRun);
176 args += L" ";
[email protected]d23ca372010-02-16 20:36:09177 args += url;
178 cmd.AppendLooseValue(args);
179
180 base::ProcessHandle process = NULL;
181 base::LaunchApp(cmd, false, false, &process);
182 return process;
[email protected]f7817822009-09-24 05:11:58183}
184
185base::ProcessHandle LaunchOpera(const std::wstring& url) {
186 // NOTE: For Opera tests to work it must be configured to start up with
187 // a blank page. There is an command line switch, -nosession, that's supposed
188 // to avoid opening up the previous session, but that switch is not working.
189 // TODO(tommi): Include a special ini file (opera6.ini) for opera and launch
190 // with our required settings. This file is by default stored here:
191 // "%USERPROFILE%\Application Data\Opera\Opera\profile\opera6.ini"
192 return LaunchExecutable(kOperaImageName, url);
193}
194
195base::ProcessHandle LaunchIEOnVista(const std::wstring& url) {
196 typedef HRESULT (WINAPI* IELaunchURLPtr)(
197 const wchar_t* url,
198 PROCESS_INFORMATION *pi,
199 VOID *info);
200
201 IELaunchURLPtr launch;
202 PROCESS_INFORMATION pi = {0};
203 IELAUNCHURLINFO info = {sizeof info, 0};
204 HMODULE h = LoadLibrary(L"ieframe.dll");
[email protected]7ec1f4e2010-05-19 18:43:21205 if (!h) {
206 LOG(ERROR) << "Failed to load ieframe.dll: " << ::GetLastError();
[email protected]f7817822009-09-24 05:11:58207 return NULL;
[email protected]7ec1f4e2010-05-19 18:43:21208 }
[email protected]f7817822009-09-24 05:11:58209 launch = reinterpret_cast<IELaunchURLPtr>(GetProcAddress(h, "IELaunchURL"));
[email protected]7ec1f4e2010-05-19 18:43:21210 CHECK(launch);
[email protected]f7817822009-09-24 05:11:58211 HRESULT hr = launch(url.c_str(), &pi, &info);
212 FreeLibrary(h);
[email protected]7ec1f4e2010-05-19 18:43:21213 if (SUCCEEDED(hr)) {
[email protected]f7817822009-09-24 05:11:58214 CloseHandle(pi.hThread);
[email protected]7ec1f4e2010-05-19 18:43:21215 } else {
216 LOG(ERROR) << ::StringPrintf("IELaunchURL failed: 0x%08X", hr);
217 }
[email protected]f7817822009-09-24 05:11:58218 return pi.hProcess;
219}
220
221base::ProcessHandle LaunchIE(const std::wstring& url) {
222 if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) {
223 return LaunchIEOnVista(url);
224 } else {
225 return LaunchExecutable(kIEImageName, url);
226 }
227}
228
229int CloseAllIEWindows() {
230 int ret = 0;
231
232 ScopedComPtr<IShellWindows> windows;
233 HRESULT hr = ::CoCreateInstance(__uuidof(ShellWindows), NULL, CLSCTX_ALL,
234 IID_IShellWindows, reinterpret_cast<void**>(windows.Receive()));
235 DCHECK(SUCCEEDED(hr));
236
237 if (SUCCEEDED(hr)) {
238 long count = 0; // NOLINT
239 windows->get_Count(&count);
240 VARIANT i = { VT_I4 };
241 for (i.lVal = 0; i.lVal < count; ++i.lVal) {
242 ScopedComPtr<IDispatch> folder;
243 windows->Item(i, folder.Receive());
244 if (folder != NULL) {
245 ScopedComPtr<IWebBrowser2> browser;
246 if (SUCCEEDED(browser.QueryFrom(folder))) {
247 browser->Quit();
248 ++ret;
249 }
250 }
251 }
252 }
253
254 return ret;
255}
256
[email protected]42d7c022009-11-20 22:44:58257
258LowIntegrityToken::LowIntegrityToken() : impersonated_(false) {
259}
260
261LowIntegrityToken::~LowIntegrityToken() {
262 RevertToSelf();
263}
264
265BOOL LowIntegrityToken::RevertToSelf() {
266 BOOL ok = TRUE;
267 if (impersonated_) {
268 DCHECK(IsImpersonated());
269 ok = ::RevertToSelf();
270 if (ok)
271 impersonated_ = false;
272 }
273
274 return ok;
275}
276
277BOOL LowIntegrityToken::Impersonate() {
278 DCHECK(!impersonated_);
279 DCHECK(!IsImpersonated());
280 HANDLE process_token_handle = NULL;
281 BOOL ok = ::OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE,
282 &process_token_handle);
283 if (!ok) {
284 DLOG(ERROR) << "::OpenProcessToken failed: " << GetLastError();
285 return ok;
286 }
287
288 ScopedHandle process_token(process_token_handle);
289 // Create impersonation low integrity token.
290 HANDLE impersonation_token_handle = NULL;
291 ok = ::DuplicateTokenEx(process_token,
292 TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_ADJUST_DEFAULT, NULL,
293 SecurityImpersonation, TokenImpersonation, &impersonation_token_handle);
294 if (!ok) {
295 DLOG(ERROR) << "::DuplicateTokenEx failed: " << GetLastError();
296 return ok;
297 }
298
[email protected]7ec1f4e2010-05-19 18:43:21299 // TODO(stoyan): sandbox/src/restricted_token_utils.cc has
300 // SetTokenIntegrityLevel function already.
[email protected]42d7c022009-11-20 22:44:58301 ScopedHandle impersonation_token(impersonation_token_handle);
302 PSID integrity_sid = NULL;
303 TOKEN_MANDATORY_LABEL tml = {0};
304 ok = ::ConvertStringSidToSid(SDDL_ML_LOW, &integrity_sid);
305 if (!ok) {
306 DLOG(ERROR) << "::ConvertStringSidToSid failed: " << GetLastError();
307 return ok;
308 }
309
310 tml.Label.Attributes = SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED;
311 tml.Label.Sid = integrity_sid;
312 ok = ::SetTokenInformation(impersonation_token, TokenIntegrityLevel,
313 &tml, sizeof(tml) + ::GetLengthSid(integrity_sid));
314 ::LocalFree(integrity_sid);
315 if (!ok) {
316 DLOG(ERROR) << "::SetTokenInformation failed: " << GetLastError();
317 return ok;
318 }
319
320 // Switch current thread to low integrity.
321 ok = ::ImpersonateLoggedOnUser(impersonation_token);
322 if (ok) {
323 impersonated_ = true;
324 } else {
325 DLOG(ERROR) << "::ImpersonateLoggedOnUser failed: " << GetLastError();
326 }
327
328 return ok;
329}
330
331bool LowIntegrityToken::IsImpersonated() {
332 HANDLE token = NULL;
333 if (!::OpenThreadToken(::GetCurrentThread(), 0, false, &token) &&
334 ::GetLastError() != ERROR_NO_TOKEN) {
335 return true;
336 }
337
338 if (token)
339 ::CloseHandle(token);
340
341 return false;
342}
343
[email protected]7bc272f2009-12-09 01:09:28344HRESULT LaunchIEAsComServer(IWebBrowser2** web_browser) {
345 if (!web_browser)
346 return E_INVALIDARG;
347
[email protected]7aa8d972010-02-17 04:24:49348 AllowSetForegroundWindow(ASFW_ANY);
349
[email protected]7bc272f2009-12-09 01:09:28350 HRESULT hr = S_OK;
351 DWORD cocreate_flags = CLSCTX_LOCAL_SERVER;
352 chrome_frame_test::LowIntegrityToken token;
353 // Vista has a bug which manifests itself when a medium integrity process
354 // launches a COM server like IE which runs in protected mode due to UAC.
355 // This causes the IWebBrowser2 interface which is returned to be useless,
356 // i.e it does not receive any events, etc. Our workaround for this is
357 // to impersonate a low integrity token and then launch IE.
358 if (win_util::GetWinVersion() == win_util::WINVERSION_VISTA) {
359 // Create medium integrity browser that will launch IE broker.
360 ScopedComPtr<IWebBrowser2> medium_integrity_browser;
361 hr = medium_integrity_browser.CreateInstance(CLSID_InternetExplorer, NULL,
362 CLSCTX_LOCAL_SERVER);
363 if (FAILED(hr))
364 return hr;
365 medium_integrity_browser->Quit();
366 // Broker remains alive.
367 if (!token.Impersonate()) {
368 hr = HRESULT_FROM_WIN32(GetLastError());
369 return hr;
370 }
371
372 cocreate_flags |= CLSCTX_ENABLE_CLOAKING;
373 }
374
375 hr = ::CoCreateInstance(CLSID_InternetExplorer, NULL,
376 cocreate_flags, IID_IWebBrowser2,
377 reinterpret_cast<void**>(web_browser));
378 // ~LowIntegrityToken() will switch integrity back to medium.
379 return hr;
380}
381
[email protected]bc73b4e52010-03-26 04:16:20382FilePath GetProfilePath(const std::wstring& profile_name) {
383 FilePath profile_path;
384 chrome::GetChromeFrameUserDataDirectory(&profile_path);
385 return profile_path.Append(profile_name);
386}
387
[email protected]7bc272f2009-12-09 01:09:28388_ATL_FUNC_INFO WebBrowserEventSink::kNavigateErrorInfo = {
389 CC_STDCALL, VT_EMPTY, 5, {
390 VT_DISPATCH,
391 VT_VARIANT | VT_BYREF,
392 VT_VARIANT | VT_BYREF,
393 VT_VARIANT | VT_BYREF,
394 VT_BOOL | VT_BYREF,
395 }
396};
397
398_ATL_FUNC_INFO WebBrowserEventSink::kNavigateComplete2Info = {
399 CC_STDCALL, VT_EMPTY, 2, {
400 VT_DISPATCH,
401 VT_VARIANT | VT_BYREF
402 }
403};
404
405_ATL_FUNC_INFO WebBrowserEventSink::kBeforeNavigate2Info = {
406 CC_STDCALL, VT_EMPTY, 7, {
407 VT_DISPATCH,
408 VT_VARIANT | VT_BYREF,
409 VT_VARIANT | VT_BYREF,
410 VT_VARIANT | VT_BYREF,
411 VT_VARIANT | VT_BYREF,
412 VT_VARIANT | VT_BYREF,
413 VT_BOOL | VT_BYREF
414 }
415};
416
[email protected]552fb6012010-02-03 17:24:29417_ATL_FUNC_INFO WebBrowserEventSink::kNewWindow2Info = {
418 CC_STDCALL, VT_EMPTY, 2, {
419 VT_DISPATCH | VT_BYREF,
420 VT_BOOL | VT_BYREF,
421 }
422};
423
[email protected]7bc272f2009-12-09 01:09:28424_ATL_FUNC_INFO WebBrowserEventSink::kNewWindow3Info = {
425 CC_STDCALL, VT_EMPTY, 5, {
426 VT_DISPATCH | VT_BYREF,
427 VT_BOOL | VT_BYREF,
428 VT_UINT,
429 VT_BSTR,
430 VT_BSTR
431 }
432};
433
434_ATL_FUNC_INFO WebBrowserEventSink::kVoidMethodInfo = {
435 CC_STDCALL, VT_EMPTY, 0, {NULL}};
436
437_ATL_FUNC_INFO WebBrowserEventSink::kDocumentCompleteInfo = {
438 CC_STDCALL, VT_EMPTY, 2, {
439 VT_DISPATCH,
440 VT_VARIANT | VT_BYREF
441 }
442};
443
[email protected]7aa8d972010-02-17 04:24:49444_ATL_FUNC_INFO WebBrowserEventSink::kFileDownloadInfo = {
445 CC_STDCALL, VT_EMPTY, 2, {
446 VT_BOOL,
447 VT_BOOL | VT_BYREF
448 }
449};
450
[email protected]7bc272f2009-12-09 01:09:28451// WebBrowserEventSink member defines
[email protected]552fb6012010-02-03 17:24:29452void WebBrowserEventSink::Attach(IDispatch* browser_disp) {
453 EXPECT_TRUE(NULL != browser_disp);
[email protected]d55194ca2010-03-11 18:25:45454 if (browser_disp) {
[email protected]552fb6012010-02-03 17:24:29455 EXPECT_HRESULT_SUCCEEDED(web_browser2_.QueryFrom(browser_disp));
456 EXPECT_TRUE(S_OK == DispEventAdvise(web_browser2_,
457 &DIID_DWebBrowserEvents2));
458 }
459}
460
[email protected]7bc272f2009-12-09 01:09:28461void WebBrowserEventSink::Uninitialize() {
462 DisconnectFromChromeFrame();
463 if (web_browser2_.get()) {
[email protected]b82d09c2010-04-26 06:04:02464 if (m_dwEventCookie != 0xFEFEFEFE) {
465 CoDisconnectObject(this, 0);
466 DispEventUnadvise(web_browser2_);
467 }
[email protected]7aa8d972010-02-17 04:24:49468
469 ScopedHandle process;
470 // process_id_to_wait_for_ is set when we receive OnQuit.
471 // So, we should only attempt to wait for the browser if we know that
472 // the browser is truly quitting and if this instance actually launched
473 // the browser.
474 if (process_id_to_wait_for_) {
475 if (is_main_browser_object_) {
476 process.Set(OpenProcess(SYNCHRONIZE, FALSE, process_id_to_wait_for_));
477 DLOG_IF(ERROR, !process.IsValid())
478 << StringPrintf("OpenProcess failed: %i", ::GetLastError());
479 }
480 process_id_to_wait_for_ = 0;
481 } else {
482 DLOG_IF(ERROR, is_main_browser_object_)
483 << "Main browser event object did not have a valid the process id.";
484 web_browser2_->Quit();
485 }
486
[email protected]7bc272f2009-12-09 01:09:28487 web_browser2_.Release();
[email protected]7aa8d972010-02-17 04:24:49488
489 if (process) {
490 DWORD max_wait = kDefaultWaitForIEToTerminateMs;
491 while (true) {
492 base::Time start = base::Time::Now();
493 HANDLE wait_for = process;
494 DWORD wait = MsgWaitForMultipleObjects(1, &wait_for, FALSE, max_wait,
495 QS_ALLINPUT);
496 if (wait == WAIT_OBJECT_0 + 1) {
497 MSG msg;
498 while (PeekMessage(&msg, NULL, 0, 0, TRUE) > 0) {
499 TranslateMessage(&msg);
500 DispatchMessage(&msg);
501 }
502 } else if (wait == WAIT_OBJECT_0) {
503 break;
504 } else {
505 DCHECK(wait == WAIT_TIMEOUT);
506 DLOG(ERROR) << "Wait for IE timed out";
507 break;
508 }
509
510 base::TimeDelta elapsed = base::Time::Now() - start;
511 ULARGE_INTEGER ms;
512 ms.QuadPart = elapsed.InMilliseconds();
[email protected]7ec1f4e2010-05-19 18:43:21513 DCHECK_EQ(ms.HighPart, 0U);
[email protected]7aa8d972010-02-17 04:24:49514 if (ms.LowPart > max_wait) {
515 DLOG(ERROR) << "Wait for IE timed out (2)";
516 break;
517 } else {
518 max_wait -= ms.LowPart;
519 }
520 }
521 }
[email protected]7bc272f2009-12-09 01:09:28522 }
523}
524
525STDMETHODIMP WebBrowserEventSink::OnBeforeNavigate2Internal(
526 IDispatch* dispatch, VARIANT* url, VARIANT* flags,
527 VARIANT* target_frame_name, VARIANT* post_data, VARIANT* headers,
528 VARIANT_BOOL* cancel) {
[email protected]7aa8d972010-02-17 04:24:49529 DLOG(INFO) << __FUNCTION__
530 << StringPrintf("%ls - 0x%08X", url->bstrVal, this);
[email protected]7bc272f2009-12-09 01:09:28531 // Reset any existing reference to chrome frame since this is a new
532 // navigation.
[email protected]9ed9fcf2010-04-21 20:34:37533 DisconnectFromChromeFrame();
[email protected]fe7a6b52010-03-03 12:43:20534 OnBeforeNavigate2(dispatch, url, flags, target_frame_name, post_data,
535 headers, cancel);
536 return S_OK;
[email protected]7bc272f2009-12-09 01:09:28537}
538
539STDMETHODIMP_(void) WebBrowserEventSink::OnNavigateComplete2Internal(
540 IDispatch* dispatch, VARIANT* url) {
541 DLOG(INFO) << __FUNCTION__;
542 ConnectToChromeFrame();
543 OnNavigateComplete2(dispatch, url);
544}
545
546STDMETHODIMP_(void) WebBrowserEventSink::OnDocumentCompleteInternal(
547 IDispatch* dispatch, VARIANT* url) {
548 DLOG(INFO) << __FUNCTION__;
549 OnDocumentComplete(dispatch, url);
550}
551
[email protected]7aa8d972010-02-17 04:24:49552STDMETHODIMP_(void) WebBrowserEventSink::OnFileDownloadInternal(
553 VARIANT_BOOL active_doc, VARIANT_BOOL* cancel) {
554 DLOG(INFO) << __FUNCTION__ << StringPrintf(" 0x%08X ad=%i", this, active_doc);
555 OnFileDownload(active_doc, cancel);
556 // Always cancel file downloads in tests.
557 *cancel = VARIANT_TRUE;
558}
559
[email protected]552fb6012010-02-03 17:24:29560STDMETHODIMP_(void) WebBrowserEventSink::OnNewWindow3Internal(
561 IDispatch** dispatch, VARIANT_BOOL* cancel, DWORD flags, BSTR url_context,
562 BSTR url) {
563 DLOG(INFO) << __FUNCTION__;
564 if (!dispatch) {
565 NOTREACHED() << "Invalid argument - dispatch";
566 return;
567 }
568
569 // Call the OnNewWindow3 with original args
570 OnNewWindow3(dispatch, cancel, flags, url_context, url);
571
572 // Note that |dispatch| is an [in/out] argument. IE is asking listeners if
573 // they want to use a IWebBrowser2 of their choice for the new window.
574 // Since we need to listen on events on the new browser, we create one
575 // if needed.
576 if (!*dispatch) {
577 ScopedComPtr<IDispatch> new_browser;
578 HRESULT hr = new_browser.CreateInstance(CLSID_InternetExplorer, NULL,
579 CLSCTX_LOCAL_SERVER);
580 DCHECK(SUCCEEDED(hr) && new_browser);
581 *dispatch = new_browser.Detach();
582 }
583
584 if (*dispatch)
585 OnNewBrowserWindow(*dispatch, url);
586}
587
[email protected]7bc272f2009-12-09 01:09:28588HRESULT WebBrowserEventSink::OnLoadInternal(const VARIANT* param) {
589 DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
[email protected]9ed9fcf2010-04-21 20:34:37590 if (chrome_frame_) {
591 OnLoad(param->bstrVal);
592 } else {
593 DLOG(WARNING) << "Invalid chrome frame pointer";
594 }
[email protected]7bc272f2009-12-09 01:09:28595 return S_OK;
596}
597
598HRESULT WebBrowserEventSink::OnLoadErrorInternal(const VARIANT* param) {
599 DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
[email protected]9ed9fcf2010-04-21 20:34:37600 if (chrome_frame_) {
601 OnLoadError(param->bstrVal);
602 } else {
603 DLOG(WARNING) << "Invalid chrome frame pointer";
604 }
[email protected]7bc272f2009-12-09 01:09:28605 return S_OK;
606}
607
608HRESULT WebBrowserEventSink::OnMessageInternal(const VARIANT* param) {
[email protected]4e676aa2010-02-12 18:19:07609 DLOG(INFO) << __FUNCTION__ << " " << param;
[email protected]9ed9fcf2010-04-21 20:34:37610 if (!chrome_frame_.get()) {
611 DLOG(WARNING) << "Invalid chrome frame pointer";
612 return S_OK;
613 }
614
[email protected]4e676aa2010-02-12 18:19:07615 ScopedVariant data, origin, source;
616 if (param && (V_VT(param) == VT_DISPATCH)) {
617 wchar_t* properties[] = { L"data", L"origin", L"source" };
618 const int prop_count = arraysize(properties);
619 DISPID ids[prop_count] = {0};
620
621 HRESULT hr = param->pdispVal->GetIDsOfNames(IID_NULL, properties,
622 prop_count, LOCALE_SYSTEM_DEFAULT, ids);
623 if (SUCCEEDED(hr)) {
624 DISPPARAMS params = { 0 };
625 EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[0], IID_NULL,
626 LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
627 data.Receive(), NULL, NULL));
628 EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[1], IID_NULL,
629 LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
630 origin.Receive(), NULL, NULL));
631 EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[2], IID_NULL,
632 LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
633 source.Receive(), NULL, NULL));
634 }
635 }
636
637 OnMessage(V_BSTR(&data), V_BSTR(&origin), V_BSTR(&source));
[email protected]7bc272f2009-12-09 01:09:28638 return S_OK;
639}
640
641HRESULT WebBrowserEventSink::LaunchIEAndNavigate(
642 const std::wstring& navigate_url) {
[email protected]7aa8d972010-02-17 04:24:49643 is_main_browser_object_ = true;
[email protected]7bc272f2009-12-09 01:09:28644 HRESULT hr = LaunchIEAsComServer(web_browser2_.Receive());
645 EXPECT_EQ(S_OK, hr);
646 if (hr == S_OK) {
647 web_browser2_->put_Visible(VARIANT_TRUE);
648 hr = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2);
649 EXPECT_TRUE(hr == S_OK);
650 hr = Navigate(navigate_url);
651 }
[email protected]4d783f72010-04-23 23:24:10652
653 DLOG_IF(WARNING, FAILED(hr)) << "Failed to launch IE. Error:" << hr;
[email protected]7bc272f2009-12-09 01:09:28654 return hr;
655}
656
657HRESULT WebBrowserEventSink::Navigate(const std::wstring& navigate_url) {
658 VARIANT empty = ScopedVariant::kEmptyVariant;
659 ScopedVariant url;
660 url.Set(navigate_url.c_str());
661
662 HRESULT hr = S_OK;
663 hr = web_browser2_->Navigate2(url.AsInput(), &empty, &empty, &empty, &empty);
664 EXPECT_TRUE(hr == S_OK);
665 return hr;
666}
667
668void WebBrowserEventSink::SetFocusToChrome() {
[email protected]4e676aa2010-02-12 18:19:07669 simulate_input::SetKeyboardFocusToWindow(GetRendererWindow());
[email protected]7bc272f2009-12-09 01:09:28670}
671
[email protected]4e676aa2010-02-12 18:19:07672void WebBrowserEventSink::SendKeys(const wchar_t* input_string) {
673 SetFocusToChrome();
[email protected]2d9ebb012010-03-09 18:57:00674 simulate_input::SendStringW(input_string);
[email protected]4e676aa2010-02-12 18:19:07675}
676
677void WebBrowserEventSink::SendMouseClick(int x, int y,
678 simulate_input::MouseButton button) {
679 simulate_input::SendMouseClick(GetRendererWindow(), x, y, button);
[email protected]7bc272f2009-12-09 01:09:28680}
681
[email protected]ba443b72010-05-14 23:34:38682void WebBrowserEventSink::SendMouseClickToIE(int x, int y,
683 simulate_input::MouseButton button) {
684 simulate_input::SendMouseClick(GetIERendererWindow(), x, y, button);
685}
686
[email protected]7bc272f2009-12-09 01:09:28687void WebBrowserEventSink::ConnectToChromeFrame() {
688 DCHECK(web_browser2_);
[email protected]9d05f7f2010-04-08 18:36:23689 if (chrome_frame_.get())
690 return;
[email protected]7bc272f2009-12-09 01:09:28691 ScopedComPtr<IShellBrowser> shell_browser;
692 DoQueryService(SID_STopLevelBrowser, web_browser2_,
693 shell_browser.Receive());
694
695 if (shell_browser) {
696 ScopedComPtr<IShellView> shell_view;
697 shell_browser->QueryActiveShellView(shell_view.Receive());
698 if (shell_view) {
699 shell_view->GetItemObject(SVGIO_BACKGROUND, __uuidof(IChromeFrame),
700 reinterpret_cast<void**>(chrome_frame_.Receive()));
701 }
702
703 if (chrome_frame_) {
704 ScopedVariant onmessage(onmessage_.ToDispatch());
705 ScopedVariant onloaderror(onloaderror_.ToDispatch());
706 ScopedVariant onload(onload_.ToDispatch());
707 EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onmessage(onmessage));
708 EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onloaderror(onloaderror));
709 EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onload(onload));
710 }
711 }
712}
713
714void WebBrowserEventSink::DisconnectFromChromeFrame() {
715 if (chrome_frame_) {
[email protected]086f367d52010-04-23 21:01:50716 // Use a local ref counted copy of the IChromeFrame interface as the
717 // outgoing calls could cause the interface to be deleted due to a message
718 // pump running in the context of the outgoing call.
719 ScopedComPtr<IChromeFrame> chrome_frame(chrome_frame_);
[email protected]7bc272f2009-12-09 01:09:28720 chrome_frame_.Release();
[email protected]086f367d52010-04-23 21:01:50721 ScopedVariant dummy(static_cast<IDispatch*>(NULL));
722 chrome_frame->put_onmessage(dummy);
723 chrome_frame->put_onload(dummy);
724 chrome_frame->put_onloaderror(dummy);
[email protected]7bc272f2009-12-09 01:09:28725 }
726}
727
[email protected]4e676aa2010-02-12 18:19:07728HWND WebBrowserEventSink::GetRendererWindow() {
[email protected]7bc272f2009-12-09 01:09:28729 DCHECK(chrome_frame_);
730 HWND renderer_window = NULL;
731 ScopedComPtr<IOleWindow> ole_window;
732 ole_window.QueryFrom(chrome_frame_);
733 EXPECT_TRUE(ole_window.get());
734
735 if (ole_window) {
736 HWND activex_window = NULL;
737 ole_window->GetWindow(&activex_window);
738 EXPECT_TRUE(IsWindow(activex_window));
739
740 // chrome tab window is the first (and the only) child of activex
741 HWND chrome_tab_window = GetWindow(activex_window, GW_CHILD);
742 EXPECT_TRUE(IsWindow(chrome_tab_window));
743 renderer_window = GetWindow(chrome_tab_window, GW_CHILD);
744 }
745
[email protected]4e676aa2010-02-12 18:19:07746 EXPECT_TRUE(IsWindow(renderer_window));
[email protected]7bc272f2009-12-09 01:09:28747 return renderer_window;
748}
749
[email protected]ba443b72010-05-14 23:34:38750HWND WebBrowserEventSink::GetIERendererWindow() {
751 DCHECK(web_browser2_);
752 HWND renderer_window = NULL;
753 ScopedComPtr<IDispatch> doc;
754 HRESULT hr = web_browser2_->get_Document(doc.Receive());
755 EXPECT_HRESULT_SUCCEEDED(hr);
756 EXPECT_TRUE(doc);
757 if (doc) {
758 ScopedComPtr<IOleWindow> ole_window;
759 ole_window.QueryFrom(doc);
760 EXPECT_TRUE(ole_window);
761 if (ole_window) {
762 ole_window->GetWindow(&renderer_window);
763 }
764 }
765 return renderer_window;
766}
767
[email protected]7bc272f2009-12-09 01:09:28768HRESULT WebBrowserEventSink::SetWebBrowser(IWebBrowser2* web_browser2) {
769 DCHECK(web_browser2_.get() == NULL);
[email protected]7aa8d972010-02-17 04:24:49770 DCHECK(!is_main_browser_object_);
[email protected]7bc272f2009-12-09 01:09:28771 web_browser2_ = web_browser2;
772 web_browser2_->put_Visible(VARIANT_TRUE);
773 HRESULT hr = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2);
774 return hr;
775}
776
[email protected]7aa8d972010-02-17 04:24:49777HRESULT WebBrowserEventSink::CloseWebBrowser() {
[email protected]7ec1f4e2010-05-19 18:43:21778 DCHECK_EQ(process_id_to_wait_for_, 0);
[email protected]7aa8d972010-02-17 04:24:49779 if (!web_browser2_)
780 return E_FAIL;
[email protected]1da35172010-04-22 02:30:03781
782 DisconnectFromChromeFrame();
[email protected]4381198d2010-04-08 22:17:36783 web_browser2_->Quit();
[email protected]7aa8d972010-02-17 04:24:49784 return S_OK;
785}
786
[email protected]ba443b72010-05-14 23:34:38787void WebBrowserEventSink::ExpectRendererWindowHasFocus() {
[email protected]4e676aa2010-02-12 18:19:07788 HWND renderer_window = GetRendererWindow();
789 EXPECT_TRUE(IsWindow(renderer_window));
790
791 for (HWND first_child = renderer_window;
792 IsWindow(first_child); first_child = GetWindow(first_child, GW_CHILD)) {
793 renderer_window = first_child;
794 }
795
796 wchar_t class_name[MAX_PATH] = {0};
797 GetClassName(renderer_window, class_name, arraysize(class_name));
798 EXPECT_EQ(0, _wcsicmp(class_name, L"Chrome_RenderWidgetHostHWND"));
799
800 DWORD renderer_thread = 0;
801 DWORD renderer_process = 0;
802 renderer_thread = GetWindowThreadProcessId(renderer_window,
803 &renderer_process);
804
805 ASSERT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, TRUE));
806 HWND focus_window = GetFocus();
807 EXPECT_TRUE(focus_window == renderer_window);
808 EXPECT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, FALSE));
809}
810
[email protected]ba443b72010-05-14 23:34:38811void WebBrowserEventSink::ExpectIERendererWindowHasFocus() {
812 HWND renderer_window = GetIERendererWindow();
813 EXPECT_TRUE(IsWindow(renderer_window));
814
815 DWORD renderer_thread = 0;
816 DWORD renderer_process = 0;
817 renderer_thread = GetWindowThreadProcessId(renderer_window,
818 &renderer_process);
819
820 ASSERT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, TRUE));
821 HWND focus_window = GetFocus();
822 EXPECT_TRUE(focus_window == renderer_window);
823 EXPECT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, FALSE));
824}
825
826void WebBrowserEventSink::ExpectAddressBarUrl(
827 const std::wstring& expected_url) {
828 DCHECK(web_browser2_);
829 if (web_browser2_) {
830 ScopedBstr address_bar_url;
831 EXPECT_EQ(S_OK, web_browser2_->get_LocationURL(address_bar_url.Receive()));
832 EXPECT_EQ(expected_url, std::wstring(address_bar_url));
833 }
834}
835
[email protected]4e676aa2010-02-12 18:19:07836void WebBrowserEventSink::Exec(const GUID* cmd_group_guid, DWORD command_id,
837 DWORD cmd_exec_opt, VARIANT* in_args,
838 VARIANT* out_args) {
839 ScopedComPtr<IOleCommandTarget> shell_browser_cmd_target;
840 DoQueryService(SID_STopLevelBrowser, web_browser2_,
841 shell_browser_cmd_target.Receive());
842 ASSERT_TRUE(NULL != shell_browser_cmd_target);
843 EXPECT_HRESULT_SUCCEEDED(shell_browser_cmd_target->Exec(cmd_group_guid,
844 command_id, cmd_exec_opt, in_args, out_args));
845}
846
[email protected]e0b42122010-03-04 21:18:52847void WebBrowserEventSink::WatchChromeWindow(const wchar_t* window_class) {
848 DCHECK(window_class);
849 window_watcher_.AddObserver(this, WideToUTF8(window_class));
850}
851
852void WebBrowserEventSink::StopWatching() {
853 window_watcher_.RemoveObserver(this);
854}
855
[email protected]1da35172010-04-22 02:30:03856std::wstring GetExeVersion(const std::wstring& exe_path) {
857 scoped_ptr<FileVersionInfo> ie_version_info(
858 FileVersionInfo::CreateFileVersionInfo(FilePath(exe_path)));
859 return ie_version_info->product_version();
860}
861
862IEVersion GetInstalledIEVersion() {
863 std::wstring path = chrome_frame_test::GetExecutableAppPath(kIEImageName);
864 std::wstring version = GetExeVersion(path);
865
866 switch (version[0]) {
867 case '6':
868 return IE_6;
869 case '7':
870 return IE_7;
871 case '8':
872 return IE_8;
873 default:
874 break;
875 }
876
877 return IE_UNSUPPORTED;
878}
879
[email protected]d8b83b212010-04-07 18:27:52880FilePath GetProfilePathForIE() {
881 FilePath profile_path;
882 // Browsers without IDeleteBrowsingHistory in non-priv mode
883 // have their profiles moved into "Temporary Internet Files".
884 // The code below basically retrieves the version of IE and computes
885 // the profile directory accordingly.
[email protected]1da35172010-04-22 02:30:03886 if (GetInstalledIEVersion() == IE_8) {
[email protected]d8b83b212010-04-07 18:27:52887 profile_path = GetProfilePath(kIEProfileName);
888 } else {
889 profile_path = GetIETemporaryFilesFolder();
890 profile_path = profile_path.Append(L"Google Chrome Frame");
891 }
892 return profile_path;
893}
894
[email protected]f7817822009-09-24 05:11:58895} // namespace chrome_frame_test