blob: e50d6652732776de93d19be80811a9aeeb2491cb [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]d9f92192010-06-23 14:51:3216#include "base/platform_thread.h"
17#include "base/process_util.h"
[email protected]f7817822009-09-24 05:11:5818#include "base/registry.h" // to find IE and firefox
[email protected]ba443b72010-05-14 23:34:3819#include "base/scoped_bstr_win.h"
[email protected]f7817822009-09-24 05:11:5820#include "base/scoped_handle.h"
21#include "base/scoped_comptr_win.h"
[email protected]d55194ca2010-03-11 18:25:4522#include "base/utf_string_conversions.h"
[email protected]f7817822009-09-24 05:11:5823#include "base/win_util.h"
24#include "chrome/common/chrome_switches.h"
[email protected]bc73b4e52010-03-26 04:16:2025#include "chrome/common/chrome_paths.h"
26#include "chrome/common/chrome_paths_internal.h"
[email protected]7bc272f2009-12-09 01:09:2827#include "chrome_frame/utils.h"
[email protected]bc73b4e52010-03-26 04:16:2028
[email protected]cb4b1e02009-10-29 21:46:2129#include "testing/gtest/include/gtest/gtest.h"
[email protected]f7817822009-09-24 05:11:5830
31namespace chrome_frame_test {
32
[email protected]7aa8d972010-02-17 04:24:4933const int kDefaultWaitForIEToTerminateMs = 10 * 1000;
34
[email protected]f7817822009-09-24 05:11:5835const wchar_t kIEImageName[] = L"iexplore.exe";
36const wchar_t kIEBrokerImageName[] = L"ieuser.exe";
37const wchar_t kFirefoxImageName[] = L"firefox.exe";
38const wchar_t kOperaImageName[] = L"opera.exe";
39const wchar_t kSafariImageName[] = L"safari.exe";
[email protected]bcd840c2010-05-27 10:43:0040const char kChromeImageName[] = "chrome.exe";
[email protected]d8b83b212010-04-07 18:27:5241const wchar_t kIEProfileName[] = L"iexplore";
[email protected]128f9d92010-04-12 15:58:5242const wchar_t kChromeLauncher[] = L"chrome_launcher.exe";
[email protected]ba443b72010-05-14 23:34:3843const int kChromeFrameLongNavigationTimeoutInSeconds = 10;
[email protected]f7817822009-09-24 05:11:5844
[email protected]f7817822009-09-24 05:11:5845// Callback function for EnumThreadWindows.
46BOOL CALLBACK CloseWindowsThreadCallback(HWND hwnd, LPARAM param) {
47 int& count = *reinterpret_cast<int*>(param);
48 if (IsWindowVisible(hwnd)) {
49 if (IsWindowEnabled(hwnd)) {
50 DWORD results = 0;
51 if (!::SendMessageTimeout(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0, SMTO_BLOCK,
52 10000, &results)) {
[email protected]421e6142010-01-15 22:14:2553 LOG(WARNING) << "Window hung: " << StringPrintf(L"%08X", hwnd);
[email protected]f7817822009-09-24 05:11:5854 }
55 count++;
56 } else {
57 DLOG(WARNING) << "Skipping disabled window: "
58 << StringPrintf(L"%08X", hwnd);
59 }
60 }
61 return TRUE; // continue enumeration
62}
63
64// Attempts to close all non-child, visible windows on the given thread.
65// The return value is the number of visible windows a close request was
66// sent to.
67int CloseVisibleTopLevelWindowsOnThread(DWORD thread_id) {
68 int window_close_attempts = 0;
69 EnumThreadWindows(thread_id, CloseWindowsThreadCallback,
70 reinterpret_cast<LPARAM>(&window_close_attempts));
71 return window_close_attempts;
72}
73
74// Enumerates the threads of a process and attempts to close visible non-child
75// windows on all threads of the process.
76// The return value is the number of visible windows a close request was
77// sent to.
78int CloseVisibleWindowsOnAllThreads(HANDLE process) {
79 DWORD process_id = ::GetProcessId(process);
80 if (process_id == 0) {
81 NOTREACHED();
82 return 0;
83 }
84
85 ScopedHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));
86 if (!snapshot.IsValid()) {
87 NOTREACHED();
88 return 0;
89 }
90
91 int window_close_attempts = 0;
92 THREADENTRY32 te = { sizeof(THREADENTRY32) };
93 if (Thread32First(snapshot, &te)) {
94 do {
95 if (RTL_CONTAINS_FIELD(&te, te.dwSize, th32OwnerProcessID) &&
96 te.th32OwnerProcessID == process_id) {
97 window_close_attempts +=
98 CloseVisibleTopLevelWindowsOnThread(te.th32ThreadID);
99 }
100 te.dwSize = sizeof(te);
101 } while (Thread32Next(snapshot, &te));
102 }
103
104 return window_close_attempts;
105}
106
[email protected]f7817822009-09-24 05:11:58107std::wstring GetExecutableAppPath(const std::wstring& file) {
108 std::wstring kAppPathsKey =
109 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\";
110
111 std::wstring app_path;
112 RegKey key(HKEY_LOCAL_MACHINE, (kAppPathsKey + file).c_str());
113 if (key.Handle()) {
114 key.ReadValue(NULL, &app_path);
115 }
116
117 return app_path;
118}
119
120std::wstring FormatCommandForApp(const std::wstring& exe_name,
121 const std::wstring& argument) {
122 std::wstring reg_path(StringPrintf(L"Applications\\%ls\\shell\\open\\command",
123 exe_name.c_str()));
124 RegKey key(HKEY_CLASSES_ROOT, reg_path.c_str());
125
126 std::wstring command;
127 if (key.Handle()) {
128 key.ReadValue(NULL, &command);
129 int found = command.find(L"%1");
130 if (found >= 0) {
131 command.replace(found, 2, argument);
132 }
133 }
134 return command;
135}
136
137base::ProcessHandle LaunchExecutable(const std::wstring& executable,
138 const std::wstring& argument) {
139 base::ProcessHandle process = NULL;
140 std::wstring path = GetExecutableAppPath(executable);
141 if (path.empty()) {
142 path = FormatCommandForApp(executable, argument);
143 if (path.empty()) {
[email protected]7ec1f4e2010-05-19 18:43:21144 LOG(ERROR) << "Failed to find executable: " << executable;
[email protected]f7817822009-09-24 05:11:58145 } else {
[email protected]51343d5a2009-10-26 22:39:33146 CommandLine cmdline = CommandLine::FromString(path);
[email protected]7ec1f4e2010-05-19 18:43:21147 if (!base::LaunchApp(cmdline, false, false, &process)) {
148 LOG(ERROR) << "LaunchApp failed: " << ::GetLastError();
149 }
[email protected]f7817822009-09-24 05:11:58150 }
151 } else {
[email protected]d1540c82009-10-27 00:18:32152 CommandLine cmdline((FilePath(path)));
[email protected]f7817822009-09-24 05:11:58153 cmdline.AppendLooseValue(argument);
[email protected]7ec1f4e2010-05-19 18:43:21154 if (!base::LaunchApp(cmdline, false, false, &process)) {
155 LOG(ERROR) << "LaunchApp failed: " << ::GetLastError();
156 }
[email protected]f7817822009-09-24 05:11:58157 }
158 return process;
159}
160
161base::ProcessHandle LaunchFirefox(const std::wstring& url) {
162 return LaunchExecutable(kFirefoxImageName, url);
163}
164
165base::ProcessHandle LaunchSafari(const std::wstring& url) {
166 return LaunchExecutable(kSafariImageName, url);
167}
168
169base::ProcessHandle LaunchChrome(const std::wstring& url) {
[email protected]bcd840c2010-05-27 10:43:00170 FilePath path;
[email protected]d23ca372010-02-16 20:36:09171 PathService::Get(base::DIR_MODULE, &path);
[email protected]bcd840c2010-05-27 10:43:00172 path = path.AppendASCII(kChromeImageName);
[email protected]d23ca372010-02-16 20:36:09173
[email protected]bcd840c2010-05-27 10:43:00174 CommandLine cmd(path);
[email protected]5714151f2010-02-16 22:26:20175 std::wstring args = L"--";
176 args += ASCIIToWide(switches::kNoFirstRun);
177 args += L" ";
[email protected]d23ca372010-02-16 20:36:09178 args += url;
179 cmd.AppendLooseValue(args);
180
181 base::ProcessHandle process = NULL;
182 base::LaunchApp(cmd, false, false, &process);
183 return process;
[email protected]f7817822009-09-24 05:11:58184}
185
186base::ProcessHandle LaunchOpera(const std::wstring& url) {
187 // NOTE: For Opera tests to work it must be configured to start up with
188 // a blank page. There is an command line switch, -nosession, that's supposed
189 // to avoid opening up the previous session, but that switch is not working.
190 // TODO(tommi): Include a special ini file (opera6.ini) for opera and launch
191 // with our required settings. This file is by default stored here:
192 // "%USERPROFILE%\Application Data\Opera\Opera\profile\opera6.ini"
193 return LaunchExecutable(kOperaImageName, url);
194}
195
196base::ProcessHandle LaunchIEOnVista(const std::wstring& url) {
197 typedef HRESULT (WINAPI* IELaunchURLPtr)(
198 const wchar_t* url,
199 PROCESS_INFORMATION *pi,
200 VOID *info);
201
202 IELaunchURLPtr launch;
203 PROCESS_INFORMATION pi = {0};
204 IELAUNCHURLINFO info = {sizeof info, 0};
205 HMODULE h = LoadLibrary(L"ieframe.dll");
[email protected]7ec1f4e2010-05-19 18:43:21206 if (!h) {
207 LOG(ERROR) << "Failed to load ieframe.dll: " << ::GetLastError();
[email protected]f7817822009-09-24 05:11:58208 return NULL;
[email protected]7ec1f4e2010-05-19 18:43:21209 }
[email protected]f7817822009-09-24 05:11:58210 launch = reinterpret_cast<IELaunchURLPtr>(GetProcAddress(h, "IELaunchURL"));
[email protected]7ec1f4e2010-05-19 18:43:21211 CHECK(launch);
[email protected]f7817822009-09-24 05:11:58212 HRESULT hr = launch(url.c_str(), &pi, &info);
213 FreeLibrary(h);
[email protected]7ec1f4e2010-05-19 18:43:21214 if (SUCCEEDED(hr)) {
[email protected]f7817822009-09-24 05:11:58215 CloseHandle(pi.hThread);
[email protected]7ec1f4e2010-05-19 18:43:21216 } else {
217 LOG(ERROR) << ::StringPrintf("IELaunchURL failed: 0x%08X", hr);
218 }
[email protected]f7817822009-09-24 05:11:58219 return pi.hProcess;
220}
221
222base::ProcessHandle LaunchIE(const std::wstring& url) {
223 if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) {
224 return LaunchIEOnVista(url);
225 } else {
226 return LaunchExecutable(kIEImageName, url);
227 }
228}
229
230int CloseAllIEWindows() {
231 int ret = 0;
232
233 ScopedComPtr<IShellWindows> windows;
234 HRESULT hr = ::CoCreateInstance(__uuidof(ShellWindows), NULL, CLSCTX_ALL,
235 IID_IShellWindows, reinterpret_cast<void**>(windows.Receive()));
236 DCHECK(SUCCEEDED(hr));
237
238 if (SUCCEEDED(hr)) {
239 long count = 0; // NOLINT
240 windows->get_Count(&count);
241 VARIANT i = { VT_I4 };
242 for (i.lVal = 0; i.lVal < count; ++i.lVal) {
243 ScopedComPtr<IDispatch> folder;
244 windows->Item(i, folder.Receive());
245 if (folder != NULL) {
246 ScopedComPtr<IWebBrowser2> browser;
247 if (SUCCEEDED(browser.QueryFrom(folder))) {
248 browser->Quit();
249 ++ret;
250 }
251 }
252 }
253 }
254
255 return ret;
256}
257
[email protected]42d7c022009-11-20 22:44:58258
259LowIntegrityToken::LowIntegrityToken() : impersonated_(false) {
260}
261
262LowIntegrityToken::~LowIntegrityToken() {
263 RevertToSelf();
264}
265
266BOOL LowIntegrityToken::RevertToSelf() {
267 BOOL ok = TRUE;
268 if (impersonated_) {
269 DCHECK(IsImpersonated());
270 ok = ::RevertToSelf();
271 if (ok)
272 impersonated_ = false;
273 }
274
275 return ok;
276}
277
278BOOL LowIntegrityToken::Impersonate() {
279 DCHECK(!impersonated_);
280 DCHECK(!IsImpersonated());
281 HANDLE process_token_handle = NULL;
282 BOOL ok = ::OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE,
283 &process_token_handle);
284 if (!ok) {
285 DLOG(ERROR) << "::OpenProcessToken failed: " << GetLastError();
286 return ok;
287 }
288
289 ScopedHandle process_token(process_token_handle);
290 // Create impersonation low integrity token.
291 HANDLE impersonation_token_handle = NULL;
292 ok = ::DuplicateTokenEx(process_token,
293 TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_ADJUST_DEFAULT, NULL,
294 SecurityImpersonation, TokenImpersonation, &impersonation_token_handle);
295 if (!ok) {
296 DLOG(ERROR) << "::DuplicateTokenEx failed: " << GetLastError();
297 return ok;
298 }
299
[email protected]7ec1f4e2010-05-19 18:43:21300 // TODO(stoyan): sandbox/src/restricted_token_utils.cc has
301 // SetTokenIntegrityLevel function already.
[email protected]42d7c022009-11-20 22:44:58302 ScopedHandle impersonation_token(impersonation_token_handle);
303 PSID integrity_sid = NULL;
304 TOKEN_MANDATORY_LABEL tml = {0};
305 ok = ::ConvertStringSidToSid(SDDL_ML_LOW, &integrity_sid);
306 if (!ok) {
307 DLOG(ERROR) << "::ConvertStringSidToSid failed: " << GetLastError();
308 return ok;
309 }
310
311 tml.Label.Attributes = SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED;
312 tml.Label.Sid = integrity_sid;
313 ok = ::SetTokenInformation(impersonation_token, TokenIntegrityLevel,
314 &tml, sizeof(tml) + ::GetLengthSid(integrity_sid));
315 ::LocalFree(integrity_sid);
316 if (!ok) {
317 DLOG(ERROR) << "::SetTokenInformation failed: " << GetLastError();
318 return ok;
319 }
320
321 // Switch current thread to low integrity.
322 ok = ::ImpersonateLoggedOnUser(impersonation_token);
323 if (ok) {
324 impersonated_ = true;
325 } else {
326 DLOG(ERROR) << "::ImpersonateLoggedOnUser failed: " << GetLastError();
327 }
328
329 return ok;
330}
331
332bool LowIntegrityToken::IsImpersonated() {
333 HANDLE token = NULL;
334 if (!::OpenThreadToken(::GetCurrentThread(), 0, false, &token) &&
335 ::GetLastError() != ERROR_NO_TOKEN) {
336 return true;
337 }
338
339 if (token)
340 ::CloseHandle(token);
341
342 return false;
343}
344
[email protected]7bc272f2009-12-09 01:09:28345HRESULT LaunchIEAsComServer(IWebBrowser2** web_browser) {
346 if (!web_browser)
347 return E_INVALIDARG;
348
[email protected]7aa8d972010-02-17 04:24:49349 AllowSetForegroundWindow(ASFW_ANY);
350
[email protected]7bc272f2009-12-09 01:09:28351 HRESULT hr = S_OK;
352 DWORD cocreate_flags = CLSCTX_LOCAL_SERVER;
353 chrome_frame_test::LowIntegrityToken token;
354 // Vista has a bug which manifests itself when a medium integrity process
355 // launches a COM server like IE which runs in protected mode due to UAC.
356 // This causes the IWebBrowser2 interface which is returned to be useless,
357 // i.e it does not receive any events, etc. Our workaround for this is
358 // to impersonate a low integrity token and then launch IE.
359 if (win_util::GetWinVersion() == win_util::WINVERSION_VISTA) {
360 // Create medium integrity browser that will launch IE broker.
361 ScopedComPtr<IWebBrowser2> medium_integrity_browser;
362 hr = medium_integrity_browser.CreateInstance(CLSID_InternetExplorer, NULL,
363 CLSCTX_LOCAL_SERVER);
364 if (FAILED(hr))
365 return hr;
366 medium_integrity_browser->Quit();
367 // Broker remains alive.
368 if (!token.Impersonate()) {
369 hr = HRESULT_FROM_WIN32(GetLastError());
370 return hr;
371 }
372
373 cocreate_flags |= CLSCTX_ENABLE_CLOAKING;
374 }
375
376 hr = ::CoCreateInstance(CLSID_InternetExplorer, NULL,
377 cocreate_flags, IID_IWebBrowser2,
378 reinterpret_cast<void**>(web_browser));
379 // ~LowIntegrityToken() will switch integrity back to medium.
380 return hr;
381}
382
[email protected]bc73b4e52010-03-26 04:16:20383FilePath GetProfilePath(const std::wstring& profile_name) {
384 FilePath profile_path;
385 chrome::GetChromeFrameUserDataDirectory(&profile_path);
386 return profile_path.Append(profile_name);
387}
388
[email protected]7bc272f2009-12-09 01:09:28389_ATL_FUNC_INFO WebBrowserEventSink::kNavigateErrorInfo = {
390 CC_STDCALL, VT_EMPTY, 5, {
391 VT_DISPATCH,
392 VT_VARIANT | VT_BYREF,
393 VT_VARIANT | VT_BYREF,
394 VT_VARIANT | VT_BYREF,
395 VT_BOOL | VT_BYREF,
396 }
397};
398
399_ATL_FUNC_INFO WebBrowserEventSink::kNavigateComplete2Info = {
400 CC_STDCALL, VT_EMPTY, 2, {
401 VT_DISPATCH,
402 VT_VARIANT | VT_BYREF
403 }
404};
405
406_ATL_FUNC_INFO WebBrowserEventSink::kBeforeNavigate2Info = {
407 CC_STDCALL, VT_EMPTY, 7, {
408 VT_DISPATCH,
409 VT_VARIANT | VT_BYREF,
410 VT_VARIANT | VT_BYREF,
411 VT_VARIANT | VT_BYREF,
412 VT_VARIANT | VT_BYREF,
413 VT_VARIANT | VT_BYREF,
414 VT_BOOL | VT_BYREF
415 }
416};
417
[email protected]552fb6012010-02-03 17:24:29418_ATL_FUNC_INFO WebBrowserEventSink::kNewWindow2Info = {
419 CC_STDCALL, VT_EMPTY, 2, {
420 VT_DISPATCH | VT_BYREF,
421 VT_BOOL | VT_BYREF,
422 }
423};
424
[email protected]7bc272f2009-12-09 01:09:28425_ATL_FUNC_INFO WebBrowserEventSink::kNewWindow3Info = {
426 CC_STDCALL, VT_EMPTY, 5, {
427 VT_DISPATCH | VT_BYREF,
428 VT_BOOL | VT_BYREF,
429 VT_UINT,
430 VT_BSTR,
431 VT_BSTR
432 }
433};
434
435_ATL_FUNC_INFO WebBrowserEventSink::kVoidMethodInfo = {
436 CC_STDCALL, VT_EMPTY, 0, {NULL}};
437
438_ATL_FUNC_INFO WebBrowserEventSink::kDocumentCompleteInfo = {
439 CC_STDCALL, VT_EMPTY, 2, {
440 VT_DISPATCH,
441 VT_VARIANT | VT_BYREF
442 }
443};
444
[email protected]7aa8d972010-02-17 04:24:49445_ATL_FUNC_INFO WebBrowserEventSink::kFileDownloadInfo = {
446 CC_STDCALL, VT_EMPTY, 2, {
447 VT_BOOL,
448 VT_BOOL | VT_BYREF
449 }
450};
451
[email protected]7bc272f2009-12-09 01:09:28452// WebBrowserEventSink member defines
[email protected]552fb6012010-02-03 17:24:29453void WebBrowserEventSink::Attach(IDispatch* browser_disp) {
454 EXPECT_TRUE(NULL != browser_disp);
[email protected]d55194ca2010-03-11 18:25:45455 if (browser_disp) {
[email protected]552fb6012010-02-03 17:24:29456 EXPECT_HRESULT_SUCCEEDED(web_browser2_.QueryFrom(browser_disp));
457 EXPECT_TRUE(S_OK == DispEventAdvise(web_browser2_,
458 &DIID_DWebBrowserEvents2));
459 }
460}
461
[email protected]7bc272f2009-12-09 01:09:28462void WebBrowserEventSink::Uninitialize() {
463 DisconnectFromChromeFrame();
464 if (web_browser2_.get()) {
[email protected]b82d09c2010-04-26 06:04:02465 if (m_dwEventCookie != 0xFEFEFEFE) {
466 CoDisconnectObject(this, 0);
467 DispEventUnadvise(web_browser2_);
468 }
[email protected]7aa8d972010-02-17 04:24:49469
470 ScopedHandle process;
471 // process_id_to_wait_for_ is set when we receive OnQuit.
472 // So, we should only attempt to wait for the browser if we know that
473 // the browser is truly quitting and if this instance actually launched
474 // the browser.
475 if (process_id_to_wait_for_) {
476 if (is_main_browser_object_) {
477 process.Set(OpenProcess(SYNCHRONIZE, FALSE, process_id_to_wait_for_));
478 DLOG_IF(ERROR, !process.IsValid())
479 << StringPrintf("OpenProcess failed: %i", ::GetLastError());
480 }
481 process_id_to_wait_for_ = 0;
482 } else {
483 DLOG_IF(ERROR, is_main_browser_object_)
484 << "Main browser event object did not have a valid the process id.";
485 web_browser2_->Quit();
486 }
487
[email protected]7bc272f2009-12-09 01:09:28488 web_browser2_.Release();
[email protected]7aa8d972010-02-17 04:24:49489
490 if (process) {
491 DWORD max_wait = kDefaultWaitForIEToTerminateMs;
492 while (true) {
493 base::Time start = base::Time::Now();
494 HANDLE wait_for = process;
495 DWORD wait = MsgWaitForMultipleObjects(1, &wait_for, FALSE, max_wait,
496 QS_ALLINPUT);
497 if (wait == WAIT_OBJECT_0 + 1) {
498 MSG msg;
499 while (PeekMessage(&msg, NULL, 0, 0, TRUE) > 0) {
500 TranslateMessage(&msg);
501 DispatchMessage(&msg);
502 }
503 } else if (wait == WAIT_OBJECT_0) {
504 break;
505 } else {
506 DCHECK(wait == WAIT_TIMEOUT);
507 DLOG(ERROR) << "Wait for IE timed out";
508 break;
509 }
510
511 base::TimeDelta elapsed = base::Time::Now() - start;
512 ULARGE_INTEGER ms;
513 ms.QuadPart = elapsed.InMilliseconds();
[email protected]7ec1f4e2010-05-19 18:43:21514 DCHECK_EQ(ms.HighPart, 0U);
[email protected]7aa8d972010-02-17 04:24:49515 if (ms.LowPart > max_wait) {
516 DLOG(ERROR) << "Wait for IE timed out (2)";
517 break;
518 } else {
519 max_wait -= ms.LowPart;
520 }
521 }
522 }
[email protected]7bc272f2009-12-09 01:09:28523 }
524}
525
526STDMETHODIMP WebBrowserEventSink::OnBeforeNavigate2Internal(
527 IDispatch* dispatch, VARIANT* url, VARIANT* flags,
528 VARIANT* target_frame_name, VARIANT* post_data, VARIANT* headers,
529 VARIANT_BOOL* cancel) {
[email protected]7aa8d972010-02-17 04:24:49530 DLOG(INFO) << __FUNCTION__
531 << StringPrintf("%ls - 0x%08X", url->bstrVal, this);
[email protected]7bc272f2009-12-09 01:09:28532 // Reset any existing reference to chrome frame since this is a new
533 // navigation.
[email protected]9ed9fcf2010-04-21 20:34:37534 DisconnectFromChromeFrame();
[email protected]fe7a6b52010-03-03 12:43:20535 OnBeforeNavigate2(dispatch, url, flags, target_frame_name, post_data,
536 headers, cancel);
537 return S_OK;
[email protected]7bc272f2009-12-09 01:09:28538}
539
540STDMETHODIMP_(void) WebBrowserEventSink::OnNavigateComplete2Internal(
541 IDispatch* dispatch, VARIANT* url) {
542 DLOG(INFO) << __FUNCTION__;
543 ConnectToChromeFrame();
544 OnNavigateComplete2(dispatch, url);
545}
546
547STDMETHODIMP_(void) WebBrowserEventSink::OnDocumentCompleteInternal(
548 IDispatch* dispatch, VARIANT* url) {
549 DLOG(INFO) << __FUNCTION__;
550 OnDocumentComplete(dispatch, url);
551}
552
[email protected]7aa8d972010-02-17 04:24:49553STDMETHODIMP_(void) WebBrowserEventSink::OnFileDownloadInternal(
554 VARIANT_BOOL active_doc, VARIANT_BOOL* cancel) {
555 DLOG(INFO) << __FUNCTION__ << StringPrintf(" 0x%08X ad=%i", this, active_doc);
556 OnFileDownload(active_doc, cancel);
557 // Always cancel file downloads in tests.
558 *cancel = VARIANT_TRUE;
559}
560
[email protected]552fb6012010-02-03 17:24:29561STDMETHODIMP_(void) WebBrowserEventSink::OnNewWindow3Internal(
562 IDispatch** dispatch, VARIANT_BOOL* cancel, DWORD flags, BSTR url_context,
563 BSTR url) {
564 DLOG(INFO) << __FUNCTION__;
565 if (!dispatch) {
566 NOTREACHED() << "Invalid argument - dispatch";
567 return;
568 }
569
570 // Call the OnNewWindow3 with original args
571 OnNewWindow3(dispatch, cancel, flags, url_context, url);
572
573 // Note that |dispatch| is an [in/out] argument. IE is asking listeners if
574 // they want to use a IWebBrowser2 of their choice for the new window.
575 // Since we need to listen on events on the new browser, we create one
576 // if needed.
577 if (!*dispatch) {
578 ScopedComPtr<IDispatch> new_browser;
579 HRESULT hr = new_browser.CreateInstance(CLSID_InternetExplorer, NULL,
580 CLSCTX_LOCAL_SERVER);
581 DCHECK(SUCCEEDED(hr) && new_browser);
582 *dispatch = new_browser.Detach();
583 }
584
585 if (*dispatch)
586 OnNewBrowserWindow(*dispatch, url);
587}
588
[email protected]7bc272f2009-12-09 01:09:28589HRESULT WebBrowserEventSink::OnLoadInternal(const VARIANT* param) {
590 DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
[email protected]9ed9fcf2010-04-21 20:34:37591 if (chrome_frame_) {
592 OnLoad(param->bstrVal);
593 } else {
594 DLOG(WARNING) << "Invalid chrome frame pointer";
595 }
[email protected]7bc272f2009-12-09 01:09:28596 return S_OK;
597}
598
599HRESULT WebBrowserEventSink::OnLoadErrorInternal(const VARIANT* param) {
600 DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
[email protected]9ed9fcf2010-04-21 20:34:37601 if (chrome_frame_) {
602 OnLoadError(param->bstrVal);
603 } else {
604 DLOG(WARNING) << "Invalid chrome frame pointer";
605 }
[email protected]7bc272f2009-12-09 01:09:28606 return S_OK;
607}
608
609HRESULT WebBrowserEventSink::OnMessageInternal(const VARIANT* param) {
[email protected]4e676aa2010-02-12 18:19:07610 DLOG(INFO) << __FUNCTION__ << " " << param;
[email protected]9ed9fcf2010-04-21 20:34:37611 if (!chrome_frame_.get()) {
612 DLOG(WARNING) << "Invalid chrome frame pointer";
613 return S_OK;
614 }
615
[email protected]4e676aa2010-02-12 18:19:07616 ScopedVariant data, origin, source;
617 if (param && (V_VT(param) == VT_DISPATCH)) {
618 wchar_t* properties[] = { L"data", L"origin", L"source" };
619 const int prop_count = arraysize(properties);
620 DISPID ids[prop_count] = {0};
621
622 HRESULT hr = param->pdispVal->GetIDsOfNames(IID_NULL, properties,
623 prop_count, LOCALE_SYSTEM_DEFAULT, ids);
624 if (SUCCEEDED(hr)) {
625 DISPPARAMS params = { 0 };
626 EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[0], IID_NULL,
627 LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
628 data.Receive(), NULL, NULL));
629 EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[1], IID_NULL,
630 LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
631 origin.Receive(), NULL, NULL));
632 EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[2], IID_NULL,
633 LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
634 source.Receive(), NULL, NULL));
635 }
636 }
637
638 OnMessage(V_BSTR(&data), V_BSTR(&origin), V_BSTR(&source));
[email protected]7bc272f2009-12-09 01:09:28639 return S_OK;
640}
641
642HRESULT WebBrowserEventSink::LaunchIEAndNavigate(
643 const std::wstring& navigate_url) {
[email protected]7aa8d972010-02-17 04:24:49644 is_main_browser_object_ = true;
[email protected]7bc272f2009-12-09 01:09:28645 HRESULT hr = LaunchIEAsComServer(web_browser2_.Receive());
646 EXPECT_EQ(S_OK, hr);
647 if (hr == S_OK) {
648 web_browser2_->put_Visible(VARIANT_TRUE);
649 hr = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2);
650 EXPECT_TRUE(hr == S_OK);
651 hr = Navigate(navigate_url);
652 }
[email protected]4d783f72010-04-23 23:24:10653
654 DLOG_IF(WARNING, FAILED(hr)) << "Failed to launch IE. Error:" << hr;
[email protected]7bc272f2009-12-09 01:09:28655 return hr;
656}
657
658HRESULT WebBrowserEventSink::Navigate(const std::wstring& navigate_url) {
659 VARIANT empty = ScopedVariant::kEmptyVariant;
660 ScopedVariant url;
661 url.Set(navigate_url.c_str());
662
663 HRESULT hr = S_OK;
664 hr = web_browser2_->Navigate2(url.AsInput(), &empty, &empty, &empty, &empty);
665 EXPECT_TRUE(hr == S_OK);
666 return hr;
667}
668
669void WebBrowserEventSink::SetFocusToChrome() {
[email protected]4e676aa2010-02-12 18:19:07670 simulate_input::SetKeyboardFocusToWindow(GetRendererWindow());
[email protected]7bc272f2009-12-09 01:09:28671}
672
[email protected]4e676aa2010-02-12 18:19:07673void WebBrowserEventSink::SendKeys(const wchar_t* input_string) {
674 SetFocusToChrome();
[email protected]2d9ebb012010-03-09 18:57:00675 simulate_input::SendStringW(input_string);
[email protected]4e676aa2010-02-12 18:19:07676}
677
678void WebBrowserEventSink::SendMouseClick(int x, int y,
679 simulate_input::MouseButton button) {
680 simulate_input::SendMouseClick(GetRendererWindow(), x, y, button);
[email protected]7bc272f2009-12-09 01:09:28681}
682
[email protected]ba443b72010-05-14 23:34:38683void WebBrowserEventSink::SendMouseClickToIE(int x, int y,
684 simulate_input::MouseButton button) {
685 simulate_input::SendMouseClick(GetIERendererWindow(), x, y, button);
686}
687
[email protected]7bc272f2009-12-09 01:09:28688void WebBrowserEventSink::ConnectToChromeFrame() {
689 DCHECK(web_browser2_);
[email protected]9d05f7f2010-04-08 18:36:23690 if (chrome_frame_.get())
691 return;
[email protected]7bc272f2009-12-09 01:09:28692 ScopedComPtr<IShellBrowser> shell_browser;
693 DoQueryService(SID_STopLevelBrowser, web_browser2_,
694 shell_browser.Receive());
695
696 if (shell_browser) {
697 ScopedComPtr<IShellView> shell_view;
698 shell_browser->QueryActiveShellView(shell_view.Receive());
699 if (shell_view) {
700 shell_view->GetItemObject(SVGIO_BACKGROUND, __uuidof(IChromeFrame),
701 reinterpret_cast<void**>(chrome_frame_.Receive()));
702 }
703
704 if (chrome_frame_) {
705 ScopedVariant onmessage(onmessage_.ToDispatch());
706 ScopedVariant onloaderror(onloaderror_.ToDispatch());
707 ScopedVariant onload(onload_.ToDispatch());
708 EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onmessage(onmessage));
709 EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onloaderror(onloaderror));
710 EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onload(onload));
711 }
712 }
713}
714
715void WebBrowserEventSink::DisconnectFromChromeFrame() {
716 if (chrome_frame_) {
[email protected]086f367d52010-04-23 21:01:50717 // Use a local ref counted copy of the IChromeFrame interface as the
718 // outgoing calls could cause the interface to be deleted due to a message
719 // pump running in the context of the outgoing call.
720 ScopedComPtr<IChromeFrame> chrome_frame(chrome_frame_);
[email protected]7bc272f2009-12-09 01:09:28721 chrome_frame_.Release();
[email protected]086f367d52010-04-23 21:01:50722 ScopedVariant dummy(static_cast<IDispatch*>(NULL));
723 chrome_frame->put_onmessage(dummy);
724 chrome_frame->put_onload(dummy);
725 chrome_frame->put_onloaderror(dummy);
[email protected]7bc272f2009-12-09 01:09:28726 }
727}
728
[email protected]4e676aa2010-02-12 18:19:07729HWND WebBrowserEventSink::GetRendererWindow() {
[email protected]7bc272f2009-12-09 01:09:28730 DCHECK(chrome_frame_);
731 HWND renderer_window = NULL;
732 ScopedComPtr<IOleWindow> ole_window;
733 ole_window.QueryFrom(chrome_frame_);
734 EXPECT_TRUE(ole_window.get());
735
736 if (ole_window) {
737 HWND activex_window = NULL;
738 ole_window->GetWindow(&activex_window);
739 EXPECT_TRUE(IsWindow(activex_window));
740
741 // chrome tab window is the first (and the only) child of activex
742 HWND chrome_tab_window = GetWindow(activex_window, GW_CHILD);
743 EXPECT_TRUE(IsWindow(chrome_tab_window));
744 renderer_window = GetWindow(chrome_tab_window, GW_CHILD);
745 }
746
[email protected]4e676aa2010-02-12 18:19:07747 EXPECT_TRUE(IsWindow(renderer_window));
[email protected]7bc272f2009-12-09 01:09:28748 return renderer_window;
749}
750
[email protected]ba443b72010-05-14 23:34:38751HWND WebBrowserEventSink::GetIERendererWindow() {
752 DCHECK(web_browser2_);
753 HWND renderer_window = NULL;
754 ScopedComPtr<IDispatch> doc;
755 HRESULT hr = web_browser2_->get_Document(doc.Receive());
756 EXPECT_HRESULT_SUCCEEDED(hr);
757 EXPECT_TRUE(doc);
758 if (doc) {
759 ScopedComPtr<IOleWindow> ole_window;
760 ole_window.QueryFrom(doc);
761 EXPECT_TRUE(ole_window);
762 if (ole_window) {
763 ole_window->GetWindow(&renderer_window);
764 }
765 }
766 return renderer_window;
767}
768
[email protected]7bc272f2009-12-09 01:09:28769HRESULT WebBrowserEventSink::SetWebBrowser(IWebBrowser2* web_browser2) {
770 DCHECK(web_browser2_.get() == NULL);
[email protected]7aa8d972010-02-17 04:24:49771 DCHECK(!is_main_browser_object_);
[email protected]7bc272f2009-12-09 01:09:28772 web_browser2_ = web_browser2;
773 web_browser2_->put_Visible(VARIANT_TRUE);
774 HRESULT hr = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2);
775 return hr;
776}
777
[email protected]7aa8d972010-02-17 04:24:49778HRESULT WebBrowserEventSink::CloseWebBrowser() {
[email protected]f55bd4862010-05-27 15:38:07779 DCHECK_EQ(process_id_to_wait_for_, 0u);
[email protected]7aa8d972010-02-17 04:24:49780 if (!web_browser2_)
781 return E_FAIL;
[email protected]1da35172010-04-22 02:30:03782
783 DisconnectFromChromeFrame();
[email protected]4381198d2010-04-08 22:17:36784 web_browser2_->Quit();
[email protected]7aa8d972010-02-17 04:24:49785 return S_OK;
786}
787
[email protected]ba443b72010-05-14 23:34:38788void WebBrowserEventSink::ExpectRendererWindowHasFocus() {
[email protected]4e676aa2010-02-12 18:19:07789 HWND renderer_window = GetRendererWindow();
790 EXPECT_TRUE(IsWindow(renderer_window));
791
792 for (HWND first_child = renderer_window;
793 IsWindow(first_child); first_child = GetWindow(first_child, GW_CHILD)) {
794 renderer_window = first_child;
795 }
796
797 wchar_t class_name[MAX_PATH] = {0};
798 GetClassName(renderer_window, class_name, arraysize(class_name));
799 EXPECT_EQ(0, _wcsicmp(class_name, L"Chrome_RenderWidgetHostHWND"));
800
801 DWORD renderer_thread = 0;
802 DWORD renderer_process = 0;
803 renderer_thread = GetWindowThreadProcessId(renderer_window,
804 &renderer_process);
805
806 ASSERT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, TRUE));
807 HWND focus_window = GetFocus();
808 EXPECT_TRUE(focus_window == renderer_window);
809 EXPECT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, FALSE));
810}
811
[email protected]ba443b72010-05-14 23:34:38812void WebBrowserEventSink::ExpectIERendererWindowHasFocus() {
813 HWND renderer_window = GetIERendererWindow();
814 EXPECT_TRUE(IsWindow(renderer_window));
815
816 DWORD renderer_thread = 0;
817 DWORD renderer_process = 0;
818 renderer_thread = GetWindowThreadProcessId(renderer_window,
819 &renderer_process);
820
821 ASSERT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, TRUE));
822 HWND focus_window = GetFocus();
823 EXPECT_TRUE(focus_window == renderer_window);
824 EXPECT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, FALSE));
825}
826
827void WebBrowserEventSink::ExpectAddressBarUrl(
828 const std::wstring& expected_url) {
829 DCHECK(web_browser2_);
830 if (web_browser2_) {
831 ScopedBstr address_bar_url;
832 EXPECT_EQ(S_OK, web_browser2_->get_LocationURL(address_bar_url.Receive()));
833 EXPECT_EQ(expected_url, std::wstring(address_bar_url));
834 }
835}
836
[email protected]4e676aa2010-02-12 18:19:07837void WebBrowserEventSink::Exec(const GUID* cmd_group_guid, DWORD command_id,
838 DWORD cmd_exec_opt, VARIANT* in_args,
839 VARIANT* out_args) {
840 ScopedComPtr<IOleCommandTarget> shell_browser_cmd_target;
841 DoQueryService(SID_STopLevelBrowser, web_browser2_,
842 shell_browser_cmd_target.Receive());
843 ASSERT_TRUE(NULL != shell_browser_cmd_target);
844 EXPECT_HRESULT_SUCCEEDED(shell_browser_cmd_target->Exec(cmd_group_guid,
845 command_id, cmd_exec_opt, in_args, out_args));
846}
847
[email protected]e0b42122010-03-04 21:18:52848void WebBrowserEventSink::WatchChromeWindow(const wchar_t* window_class) {
849 DCHECK(window_class);
850 window_watcher_.AddObserver(this, WideToUTF8(window_class));
851}
852
853void WebBrowserEventSink::StopWatching() {
854 window_watcher_.RemoveObserver(this);
855}
856
[email protected]1da35172010-04-22 02:30:03857std::wstring GetExeVersion(const std::wstring& exe_path) {
858 scoped_ptr<FileVersionInfo> ie_version_info(
859 FileVersionInfo::CreateFileVersionInfo(FilePath(exe_path)));
860 return ie_version_info->product_version();
861}
862
863IEVersion GetInstalledIEVersion() {
864 std::wstring path = chrome_frame_test::GetExecutableAppPath(kIEImageName);
865 std::wstring version = GetExeVersion(path);
866
867 switch (version[0]) {
868 case '6':
869 return IE_6;
870 case '7':
871 return IE_7;
872 case '8':
873 return IE_8;
874 default:
875 break;
876 }
877
878 return IE_UNSUPPORTED;
879}
880
[email protected]d8b83b212010-04-07 18:27:52881FilePath GetProfilePathForIE() {
882 FilePath profile_path;
883 // Browsers without IDeleteBrowsingHistory in non-priv mode
884 // have their profiles moved into "Temporary Internet Files".
885 // The code below basically retrieves the version of IE and computes
886 // the profile directory accordingly.
[email protected]1da35172010-04-22 02:30:03887 if (GetInstalledIEVersion() == IE_8) {
[email protected]d8b83b212010-04-07 18:27:52888 profile_path = GetProfilePath(kIEProfileName);
889 } else {
890 profile_path = GetIETemporaryFilesFolder();
891 profile_path = profile_path.Append(L"Google Chrome Frame");
892 }
893 return profile_path;
894}
895
[email protected]3f8a1582010-06-02 21:12:48896void DelaySendExtendedKeysEnter(TimedMsgLoop* loop, int delay, char c,
897 int repeat, simulate_input::Modifier mod) {
898 const unsigned int kInterval = 25;
899 unsigned int next_delay = delay;
900 for (int i = 0; i < repeat; i++) {
901 loop->PostDelayedTask(FROM_HERE, NewRunnableFunction(
902 simulate_input::SendExtendedKey, c, mod), next_delay);
903 next_delay += kInterval;
904 }
905
906 loop->PostDelayedTask(FROM_HERE, NewRunnableFunction(
907 simulate_input::SendCharA, VK_RETURN, simulate_input::NONE), next_delay);
908}
909
[email protected]d9f92192010-06-23 14:51:32910base::ProcessHandle StartCrashService() {
911 FilePath exe_dir;
912 if (!PathService::Get(base::DIR_EXE, &exe_dir)) {
913 DCHECK(false);
914 return NULL;
915 }
916
917 base::ProcessHandle crash_service = NULL;
918
919 FilePath crash_service_path = exe_dir.AppendASCII("crash_service.exe");
920 if (!base::LaunchApp(crash_service_path.value(), false, false,
921 &crash_service)) {
922 DLOG(ERROR) << "Couldn't start crash_service.exe";
923 return NULL;
924 }
925
926 DLOG(INFO) << "Started crash_service.exe so you know if a test crashes!";
927 // This sleep is to ensure that the crash service is done initializing, i.e
928 // the pipe creation, etc.
929 Sleep(500);
930 return crash_service;
931}
932
[email protected]f7817822009-09-24 05:11:58933} // namespace chrome_frame_test