blob: e8d388941290cc6b840fc044c4c4e6007caf8246 [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
17#include "base/scoped_handle.h"
18#include "base/scoped_comptr_win.h"
[email protected]d55194ca2010-03-11 18:25:4519#include "base/utf_string_conversions.h"
[email protected]f7817822009-09-24 05:11:5820#include "base/win_util.h"
21#include "chrome/common/chrome_switches.h"
[email protected]bc73b4e52010-03-26 04:16:2022#include "chrome/common/chrome_paths.h"
23#include "chrome/common/chrome_paths_internal.h"
[email protected]7bc272f2009-12-09 01:09:2824#include "chrome_frame/utils.h"
[email protected]bc73b4e52010-03-26 04:16:2025
[email protected]cb4b1e02009-10-29 21:46:2126#include "testing/gtest/include/gtest/gtest.h"
[email protected]f7817822009-09-24 05:11:5827
28namespace chrome_frame_test {
29
[email protected]7aa8d972010-02-17 04:24:4930const int kDefaultWaitForIEToTerminateMs = 10 * 1000;
31
[email protected]f7817822009-09-24 05:11:5832const wchar_t kIEImageName[] = L"iexplore.exe";
33const wchar_t kIEBrokerImageName[] = L"ieuser.exe";
34const wchar_t kFirefoxImageName[] = L"firefox.exe";
35const wchar_t kOperaImageName[] = L"opera.exe";
36const wchar_t kSafariImageName[] = L"safari.exe";
37const wchar_t kChromeImageName[] = L"chrome.exe";
[email protected]d8b83b212010-04-07 18:27:5238const wchar_t kIEProfileName[] = L"iexplore";
[email protected]128f9d92010-04-12 15:58:5239const wchar_t kChromeLauncher[] = L"chrome_launcher.exe";
[email protected]f7817822009-09-24 05:11:5840
[email protected]f7817822009-09-24 05:11:5841// Callback function for EnumThreadWindows.
42BOOL CALLBACK CloseWindowsThreadCallback(HWND hwnd, LPARAM param) {
43 int& count = *reinterpret_cast<int*>(param);
44 if (IsWindowVisible(hwnd)) {
45 if (IsWindowEnabled(hwnd)) {
46 DWORD results = 0;
47 if (!::SendMessageTimeout(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0, SMTO_BLOCK,
48 10000, &results)) {
[email protected]421e6142010-01-15 22:14:2549 LOG(WARNING) << "Window hung: " << StringPrintf(L"%08X", hwnd);
[email protected]f7817822009-09-24 05:11:5850 }
51 count++;
52 } else {
53 DLOG(WARNING) << "Skipping disabled window: "
54 << StringPrintf(L"%08X", hwnd);
55 }
56 }
57 return TRUE; // continue enumeration
58}
59
60// Attempts to close all non-child, visible windows on the given thread.
61// The return value is the number of visible windows a close request was
62// sent to.
63int CloseVisibleTopLevelWindowsOnThread(DWORD thread_id) {
64 int window_close_attempts = 0;
65 EnumThreadWindows(thread_id, CloseWindowsThreadCallback,
66 reinterpret_cast<LPARAM>(&window_close_attempts));
67 return window_close_attempts;
68}
69
70// Enumerates the threads of a process and attempts to close visible non-child
71// windows on all threads of the process.
72// The return value is the number of visible windows a close request was
73// sent to.
74int CloseVisibleWindowsOnAllThreads(HANDLE process) {
75 DWORD process_id = ::GetProcessId(process);
76 if (process_id == 0) {
77 NOTREACHED();
78 return 0;
79 }
80
81 ScopedHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));
82 if (!snapshot.IsValid()) {
83 NOTREACHED();
84 return 0;
85 }
86
87 int window_close_attempts = 0;
88 THREADENTRY32 te = { sizeof(THREADENTRY32) };
89 if (Thread32First(snapshot, &te)) {
90 do {
91 if (RTL_CONTAINS_FIELD(&te, te.dwSize, th32OwnerProcessID) &&
92 te.th32OwnerProcessID == process_id) {
93 window_close_attempts +=
94 CloseVisibleTopLevelWindowsOnThread(te.th32ThreadID);
95 }
96 te.dwSize = sizeof(te);
97 } while (Thread32Next(snapshot, &te));
98 }
99
100 return window_close_attempts;
101}
102
[email protected]f7817822009-09-24 05:11:58103std::wstring GetExecutableAppPath(const std::wstring& file) {
104 std::wstring kAppPathsKey =
105 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\";
106
107 std::wstring app_path;
108 RegKey key(HKEY_LOCAL_MACHINE, (kAppPathsKey + file).c_str());
109 if (key.Handle()) {
110 key.ReadValue(NULL, &app_path);
111 }
112
113 return app_path;
114}
115
116std::wstring FormatCommandForApp(const std::wstring& exe_name,
117 const std::wstring& argument) {
118 std::wstring reg_path(StringPrintf(L"Applications\\%ls\\shell\\open\\command",
119 exe_name.c_str()));
120 RegKey key(HKEY_CLASSES_ROOT, reg_path.c_str());
121
122 std::wstring command;
123 if (key.Handle()) {
124 key.ReadValue(NULL, &command);
125 int found = command.find(L"%1");
126 if (found >= 0) {
127 command.replace(found, 2, argument);
128 }
129 }
130 return command;
131}
132
133base::ProcessHandle LaunchExecutable(const std::wstring& executable,
134 const std::wstring& argument) {
135 base::ProcessHandle process = NULL;
136 std::wstring path = GetExecutableAppPath(executable);
137 if (path.empty()) {
138 path = FormatCommandForApp(executable, argument);
139 if (path.empty()) {
140 DLOG(ERROR) << "Failed to find executable: " << executable;
141 } else {
[email protected]51343d5a2009-10-26 22:39:33142 CommandLine cmdline = CommandLine::FromString(path);
[email protected]f7817822009-09-24 05:11:58143 base::LaunchApp(cmdline, false, false, &process);
144 }
145 } else {
[email protected]d1540c82009-10-27 00:18:32146 CommandLine cmdline((FilePath(path)));
[email protected]f7817822009-09-24 05:11:58147 cmdline.AppendLooseValue(argument);
148 base::LaunchApp(cmdline, false, false, &process);
149 }
150 return process;
151}
152
153base::ProcessHandle LaunchFirefox(const std::wstring& url) {
154 return LaunchExecutable(kFirefoxImageName, url);
155}
156
157base::ProcessHandle LaunchSafari(const std::wstring& url) {
158 return LaunchExecutable(kSafariImageName, url);
159}
160
161base::ProcessHandle LaunchChrome(const std::wstring& url) {
[email protected]d23ca372010-02-16 20:36:09162 std::wstring path;
163 PathService::Get(base::DIR_MODULE, &path);
164 file_util::AppendToPath(&path, kChromeImageName);
165
166 FilePath exe_path(path);
167 CommandLine cmd(exe_path);
[email protected]5714151f2010-02-16 22:26:20168 std::wstring args = L"--";
169 args += ASCIIToWide(switches::kNoFirstRun);
170 args += L" ";
[email protected]d23ca372010-02-16 20:36:09171 args += url;
172 cmd.AppendLooseValue(args);
173
174 base::ProcessHandle process = NULL;
175 base::LaunchApp(cmd, false, false, &process);
176 return process;
[email protected]f7817822009-09-24 05:11:58177}
178
179base::ProcessHandle LaunchOpera(const std::wstring& url) {
180 // NOTE: For Opera tests to work it must be configured to start up with
181 // a blank page. There is an command line switch, -nosession, that's supposed
182 // to avoid opening up the previous session, but that switch is not working.
183 // TODO(tommi): Include a special ini file (opera6.ini) for opera and launch
184 // with our required settings. This file is by default stored here:
185 // "%USERPROFILE%\Application Data\Opera\Opera\profile\opera6.ini"
186 return LaunchExecutable(kOperaImageName, url);
187}
188
189base::ProcessHandle LaunchIEOnVista(const std::wstring& url) {
190 typedef HRESULT (WINAPI* IELaunchURLPtr)(
191 const wchar_t* url,
192 PROCESS_INFORMATION *pi,
193 VOID *info);
194
195 IELaunchURLPtr launch;
196 PROCESS_INFORMATION pi = {0};
197 IELAUNCHURLINFO info = {sizeof info, 0};
198 HMODULE h = LoadLibrary(L"ieframe.dll");
199 if (!h)
200 return NULL;
201 launch = reinterpret_cast<IELaunchURLPtr>(GetProcAddress(h, "IELaunchURL"));
202 HRESULT hr = launch(url.c_str(), &pi, &info);
203 FreeLibrary(h);
204 if (SUCCEEDED(hr))
205 CloseHandle(pi.hThread);
206 return pi.hProcess;
207}
208
209base::ProcessHandle LaunchIE(const std::wstring& url) {
210 if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) {
211 return LaunchIEOnVista(url);
212 } else {
213 return LaunchExecutable(kIEImageName, url);
214 }
215}
216
217int CloseAllIEWindows() {
218 int ret = 0;
219
220 ScopedComPtr<IShellWindows> windows;
221 HRESULT hr = ::CoCreateInstance(__uuidof(ShellWindows), NULL, CLSCTX_ALL,
222 IID_IShellWindows, reinterpret_cast<void**>(windows.Receive()));
223 DCHECK(SUCCEEDED(hr));
224
225 if (SUCCEEDED(hr)) {
226 long count = 0; // NOLINT
227 windows->get_Count(&count);
228 VARIANT i = { VT_I4 };
229 for (i.lVal = 0; i.lVal < count; ++i.lVal) {
230 ScopedComPtr<IDispatch> folder;
231 windows->Item(i, folder.Receive());
232 if (folder != NULL) {
233 ScopedComPtr<IWebBrowser2> browser;
234 if (SUCCEEDED(browser.QueryFrom(folder))) {
235 browser->Quit();
236 ++ret;
237 }
238 }
239 }
240 }
241
242 return ret;
243}
244
[email protected]42d7c022009-11-20 22:44:58245
246LowIntegrityToken::LowIntegrityToken() : impersonated_(false) {
247}
248
249LowIntegrityToken::~LowIntegrityToken() {
250 RevertToSelf();
251}
252
253BOOL LowIntegrityToken::RevertToSelf() {
254 BOOL ok = TRUE;
255 if (impersonated_) {
256 DCHECK(IsImpersonated());
257 ok = ::RevertToSelf();
258 if (ok)
259 impersonated_ = false;
260 }
261
262 return ok;
263}
264
265BOOL LowIntegrityToken::Impersonate() {
266 DCHECK(!impersonated_);
267 DCHECK(!IsImpersonated());
268 HANDLE process_token_handle = NULL;
269 BOOL ok = ::OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE,
270 &process_token_handle);
271 if (!ok) {
272 DLOG(ERROR) << "::OpenProcessToken failed: " << GetLastError();
273 return ok;
274 }
275
276 ScopedHandle process_token(process_token_handle);
277 // Create impersonation low integrity token.
278 HANDLE impersonation_token_handle = NULL;
279 ok = ::DuplicateTokenEx(process_token,
280 TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_ADJUST_DEFAULT, NULL,
281 SecurityImpersonation, TokenImpersonation, &impersonation_token_handle);
282 if (!ok) {
283 DLOG(ERROR) << "::DuplicateTokenEx failed: " << GetLastError();
284 return ok;
285 }
286
287 // TODO: sandbox/src/restricted_token_utils.cc has SetTokenIntegrityLevel
288 // function already.
289 ScopedHandle impersonation_token(impersonation_token_handle);
290 PSID integrity_sid = NULL;
291 TOKEN_MANDATORY_LABEL tml = {0};
292 ok = ::ConvertStringSidToSid(SDDL_ML_LOW, &integrity_sid);
293 if (!ok) {
294 DLOG(ERROR) << "::ConvertStringSidToSid failed: " << GetLastError();
295 return ok;
296 }
297
298 tml.Label.Attributes = SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED;
299 tml.Label.Sid = integrity_sid;
300 ok = ::SetTokenInformation(impersonation_token, TokenIntegrityLevel,
301 &tml, sizeof(tml) + ::GetLengthSid(integrity_sid));
302 ::LocalFree(integrity_sid);
303 if (!ok) {
304 DLOG(ERROR) << "::SetTokenInformation failed: " << GetLastError();
305 return ok;
306 }
307
308 // Switch current thread to low integrity.
309 ok = ::ImpersonateLoggedOnUser(impersonation_token);
310 if (ok) {
311 impersonated_ = true;
312 } else {
313 DLOG(ERROR) << "::ImpersonateLoggedOnUser failed: " << GetLastError();
314 }
315
316 return ok;
317}
318
319bool LowIntegrityToken::IsImpersonated() {
320 HANDLE token = NULL;
321 if (!::OpenThreadToken(::GetCurrentThread(), 0, false, &token) &&
322 ::GetLastError() != ERROR_NO_TOKEN) {
323 return true;
324 }
325
326 if (token)
327 ::CloseHandle(token);
328
329 return false;
330}
331
[email protected]7bc272f2009-12-09 01:09:28332HRESULT LaunchIEAsComServer(IWebBrowser2** web_browser) {
333 if (!web_browser)
334 return E_INVALIDARG;
335
[email protected]7aa8d972010-02-17 04:24:49336 AllowSetForegroundWindow(ASFW_ANY);
337
[email protected]7bc272f2009-12-09 01:09:28338 HRESULT hr = S_OK;
339 DWORD cocreate_flags = CLSCTX_LOCAL_SERVER;
340 chrome_frame_test::LowIntegrityToken token;
341 // Vista has a bug which manifests itself when a medium integrity process
342 // launches a COM server like IE which runs in protected mode due to UAC.
343 // This causes the IWebBrowser2 interface which is returned to be useless,
344 // i.e it does not receive any events, etc. Our workaround for this is
345 // to impersonate a low integrity token and then launch IE.
346 if (win_util::GetWinVersion() == win_util::WINVERSION_VISTA) {
347 // Create medium integrity browser that will launch IE broker.
348 ScopedComPtr<IWebBrowser2> medium_integrity_browser;
349 hr = medium_integrity_browser.CreateInstance(CLSID_InternetExplorer, NULL,
350 CLSCTX_LOCAL_SERVER);
351 if (FAILED(hr))
352 return hr;
353 medium_integrity_browser->Quit();
354 // Broker remains alive.
355 if (!token.Impersonate()) {
356 hr = HRESULT_FROM_WIN32(GetLastError());
357 return hr;
358 }
359
360 cocreate_flags |= CLSCTX_ENABLE_CLOAKING;
361 }
362
363 hr = ::CoCreateInstance(CLSID_InternetExplorer, NULL,
364 cocreate_flags, IID_IWebBrowser2,
365 reinterpret_cast<void**>(web_browser));
366 // ~LowIntegrityToken() will switch integrity back to medium.
367 return hr;
368}
369
[email protected]bc73b4e52010-03-26 04:16:20370FilePath GetProfilePath(const std::wstring& profile_name) {
371 FilePath profile_path;
372 chrome::GetChromeFrameUserDataDirectory(&profile_path);
373 return profile_path.Append(profile_name);
374}
375
[email protected]7bc272f2009-12-09 01:09:28376_ATL_FUNC_INFO WebBrowserEventSink::kNavigateErrorInfo = {
377 CC_STDCALL, VT_EMPTY, 5, {
378 VT_DISPATCH,
379 VT_VARIANT | VT_BYREF,
380 VT_VARIANT | VT_BYREF,
381 VT_VARIANT | VT_BYREF,
382 VT_BOOL | VT_BYREF,
383 }
384};
385
386_ATL_FUNC_INFO WebBrowserEventSink::kNavigateComplete2Info = {
387 CC_STDCALL, VT_EMPTY, 2, {
388 VT_DISPATCH,
389 VT_VARIANT | VT_BYREF
390 }
391};
392
393_ATL_FUNC_INFO WebBrowserEventSink::kBeforeNavigate2Info = {
394 CC_STDCALL, VT_EMPTY, 7, {
395 VT_DISPATCH,
396 VT_VARIANT | VT_BYREF,
397 VT_VARIANT | VT_BYREF,
398 VT_VARIANT | VT_BYREF,
399 VT_VARIANT | VT_BYREF,
400 VT_VARIANT | VT_BYREF,
401 VT_BOOL | VT_BYREF
402 }
403};
404
[email protected]552fb6012010-02-03 17:24:29405_ATL_FUNC_INFO WebBrowserEventSink::kNewWindow2Info = {
406 CC_STDCALL, VT_EMPTY, 2, {
407 VT_DISPATCH | VT_BYREF,
408 VT_BOOL | VT_BYREF,
409 }
410};
411
[email protected]7bc272f2009-12-09 01:09:28412_ATL_FUNC_INFO WebBrowserEventSink::kNewWindow3Info = {
413 CC_STDCALL, VT_EMPTY, 5, {
414 VT_DISPATCH | VT_BYREF,
415 VT_BOOL | VT_BYREF,
416 VT_UINT,
417 VT_BSTR,
418 VT_BSTR
419 }
420};
421
422_ATL_FUNC_INFO WebBrowserEventSink::kVoidMethodInfo = {
423 CC_STDCALL, VT_EMPTY, 0, {NULL}};
424
425_ATL_FUNC_INFO WebBrowserEventSink::kDocumentCompleteInfo = {
426 CC_STDCALL, VT_EMPTY, 2, {
427 VT_DISPATCH,
428 VT_VARIANT | VT_BYREF
429 }
430};
431
[email protected]7aa8d972010-02-17 04:24:49432_ATL_FUNC_INFO WebBrowserEventSink::kFileDownloadInfo = {
433 CC_STDCALL, VT_EMPTY, 2, {
434 VT_BOOL,
435 VT_BOOL | VT_BYREF
436 }
437};
438
[email protected]7bc272f2009-12-09 01:09:28439// WebBrowserEventSink member defines
[email protected]552fb6012010-02-03 17:24:29440void WebBrowserEventSink::Attach(IDispatch* browser_disp) {
441 EXPECT_TRUE(NULL != browser_disp);
[email protected]d55194ca2010-03-11 18:25:45442 if (browser_disp) {
[email protected]552fb6012010-02-03 17:24:29443 EXPECT_HRESULT_SUCCEEDED(web_browser2_.QueryFrom(browser_disp));
444 EXPECT_TRUE(S_OK == DispEventAdvise(web_browser2_,
445 &DIID_DWebBrowserEvents2));
446 }
447}
448
[email protected]7bc272f2009-12-09 01:09:28449void WebBrowserEventSink::Uninitialize() {
450 DisconnectFromChromeFrame();
451 if (web_browser2_.get()) {
452 DispEventUnadvise(web_browser2_);
[email protected]7aa8d972010-02-17 04:24:49453
454 ScopedHandle process;
455 // process_id_to_wait_for_ is set when we receive OnQuit.
456 // So, we should only attempt to wait for the browser if we know that
457 // the browser is truly quitting and if this instance actually launched
458 // the browser.
459 if (process_id_to_wait_for_) {
460 if (is_main_browser_object_) {
461 process.Set(OpenProcess(SYNCHRONIZE, FALSE, process_id_to_wait_for_));
462 DLOG_IF(ERROR, !process.IsValid())
463 << StringPrintf("OpenProcess failed: %i", ::GetLastError());
464 }
465 process_id_to_wait_for_ = 0;
466 } else {
467 DLOG_IF(ERROR, is_main_browser_object_)
468 << "Main browser event object did not have a valid the process id.";
469 web_browser2_->Quit();
470 }
471
[email protected]7bc272f2009-12-09 01:09:28472 web_browser2_.Release();
[email protected]7aa8d972010-02-17 04:24:49473
474 if (process) {
475 DWORD max_wait = kDefaultWaitForIEToTerminateMs;
476 while (true) {
477 base::Time start = base::Time::Now();
478 HANDLE wait_for = process;
479 DWORD wait = MsgWaitForMultipleObjects(1, &wait_for, FALSE, max_wait,
480 QS_ALLINPUT);
481 if (wait == WAIT_OBJECT_0 + 1) {
482 MSG msg;
483 while (PeekMessage(&msg, NULL, 0, 0, TRUE) > 0) {
484 TranslateMessage(&msg);
485 DispatchMessage(&msg);
486 }
487 } else if (wait == WAIT_OBJECT_0) {
488 break;
489 } else {
490 DCHECK(wait == WAIT_TIMEOUT);
491 DLOG(ERROR) << "Wait for IE timed out";
492 break;
493 }
494
495 base::TimeDelta elapsed = base::Time::Now() - start;
496 ULARGE_INTEGER ms;
497 ms.QuadPart = elapsed.InMilliseconds();
498 DCHECK(ms.HighPart == 0);
499 if (ms.LowPart > max_wait) {
500 DLOG(ERROR) << "Wait for IE timed out (2)";
501 break;
502 } else {
503 max_wait -= ms.LowPart;
504 }
505 }
506 }
[email protected]7bc272f2009-12-09 01:09:28507 }
508}
509
510STDMETHODIMP WebBrowserEventSink::OnBeforeNavigate2Internal(
511 IDispatch* dispatch, VARIANT* url, VARIANT* flags,
512 VARIANT* target_frame_name, VARIANT* post_data, VARIANT* headers,
513 VARIANT_BOOL* cancel) {
[email protected]7aa8d972010-02-17 04:24:49514 DLOG(INFO) << __FUNCTION__
515 << StringPrintf("%ls - 0x%08X", url->bstrVal, this);
[email protected]7bc272f2009-12-09 01:09:28516 // Reset any existing reference to chrome frame since this is a new
517 // navigation.
[email protected]9ed9fcf2010-04-21 20:34:37518 DisconnectFromChromeFrame();
[email protected]fe7a6b52010-03-03 12:43:20519 OnBeforeNavigate2(dispatch, url, flags, target_frame_name, post_data,
520 headers, cancel);
521 return S_OK;
[email protected]7bc272f2009-12-09 01:09:28522}
523
524STDMETHODIMP_(void) WebBrowserEventSink::OnNavigateComplete2Internal(
525 IDispatch* dispatch, VARIANT* url) {
526 DLOG(INFO) << __FUNCTION__;
527 ConnectToChromeFrame();
528 OnNavigateComplete2(dispatch, url);
529}
530
531STDMETHODIMP_(void) WebBrowserEventSink::OnDocumentCompleteInternal(
532 IDispatch* dispatch, VARIANT* url) {
533 DLOG(INFO) << __FUNCTION__;
534 OnDocumentComplete(dispatch, url);
535}
536
[email protected]7aa8d972010-02-17 04:24:49537STDMETHODIMP_(void) WebBrowserEventSink::OnFileDownloadInternal(
538 VARIANT_BOOL active_doc, VARIANT_BOOL* cancel) {
539 DLOG(INFO) << __FUNCTION__ << StringPrintf(" 0x%08X ad=%i", this, active_doc);
540 OnFileDownload(active_doc, cancel);
541 // Always cancel file downloads in tests.
542 *cancel = VARIANT_TRUE;
543}
544
[email protected]552fb6012010-02-03 17:24:29545STDMETHODIMP_(void) WebBrowserEventSink::OnNewWindow3Internal(
546 IDispatch** dispatch, VARIANT_BOOL* cancel, DWORD flags, BSTR url_context,
547 BSTR url) {
548 DLOG(INFO) << __FUNCTION__;
549 if (!dispatch) {
550 NOTREACHED() << "Invalid argument - dispatch";
551 return;
552 }
553
554 // Call the OnNewWindow3 with original args
555 OnNewWindow3(dispatch, cancel, flags, url_context, url);
556
557 // Note that |dispatch| is an [in/out] argument. IE is asking listeners if
558 // they want to use a IWebBrowser2 of their choice for the new window.
559 // Since we need to listen on events on the new browser, we create one
560 // if needed.
561 if (!*dispatch) {
562 ScopedComPtr<IDispatch> new_browser;
563 HRESULT hr = new_browser.CreateInstance(CLSID_InternetExplorer, NULL,
564 CLSCTX_LOCAL_SERVER);
565 DCHECK(SUCCEEDED(hr) && new_browser);
566 *dispatch = new_browser.Detach();
567 }
568
569 if (*dispatch)
570 OnNewBrowserWindow(*dispatch, url);
571}
572
[email protected]7bc272f2009-12-09 01:09:28573HRESULT WebBrowserEventSink::OnLoadInternal(const VARIANT* param) {
574 DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
[email protected]9ed9fcf2010-04-21 20:34:37575 if (chrome_frame_) {
576 OnLoad(param->bstrVal);
577 } else {
578 DLOG(WARNING) << "Invalid chrome frame pointer";
579 }
[email protected]7bc272f2009-12-09 01:09:28580 return S_OK;
581}
582
583HRESULT WebBrowserEventSink::OnLoadErrorInternal(const VARIANT* param) {
584 DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
[email protected]9ed9fcf2010-04-21 20:34:37585 if (chrome_frame_) {
586 OnLoadError(param->bstrVal);
587 } else {
588 DLOG(WARNING) << "Invalid chrome frame pointer";
589 }
[email protected]7bc272f2009-12-09 01:09:28590 return S_OK;
591}
592
593HRESULT WebBrowserEventSink::OnMessageInternal(const VARIANT* param) {
[email protected]4e676aa2010-02-12 18:19:07594 DLOG(INFO) << __FUNCTION__ << " " << param;
[email protected]9ed9fcf2010-04-21 20:34:37595 if (!chrome_frame_.get()) {
596 DLOG(WARNING) << "Invalid chrome frame pointer";
597 return S_OK;
598 }
599
[email protected]4e676aa2010-02-12 18:19:07600 ScopedVariant data, origin, source;
601 if (param && (V_VT(param) == VT_DISPATCH)) {
602 wchar_t* properties[] = { L"data", L"origin", L"source" };
603 const int prop_count = arraysize(properties);
604 DISPID ids[prop_count] = {0};
605
606 HRESULT hr = param->pdispVal->GetIDsOfNames(IID_NULL, properties,
607 prop_count, LOCALE_SYSTEM_DEFAULT, ids);
608 if (SUCCEEDED(hr)) {
609 DISPPARAMS params = { 0 };
610 EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[0], IID_NULL,
611 LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
612 data.Receive(), NULL, NULL));
613 EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[1], IID_NULL,
614 LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
615 origin.Receive(), NULL, NULL));
616 EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[2], IID_NULL,
617 LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
618 source.Receive(), NULL, NULL));
619 }
620 }
621
622 OnMessage(V_BSTR(&data), V_BSTR(&origin), V_BSTR(&source));
[email protected]7bc272f2009-12-09 01:09:28623 return S_OK;
624}
625
626HRESULT WebBrowserEventSink::LaunchIEAndNavigate(
627 const std::wstring& navigate_url) {
[email protected]7aa8d972010-02-17 04:24:49628 is_main_browser_object_ = true;
[email protected]7bc272f2009-12-09 01:09:28629 HRESULT hr = LaunchIEAsComServer(web_browser2_.Receive());
630 EXPECT_EQ(S_OK, hr);
631 if (hr == S_OK) {
632 web_browser2_->put_Visible(VARIANT_TRUE);
633 hr = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2);
634 EXPECT_TRUE(hr == S_OK);
635 hr = Navigate(navigate_url);
636 }
637 return hr;
638}
639
640HRESULT WebBrowserEventSink::Navigate(const std::wstring& navigate_url) {
641 VARIANT empty = ScopedVariant::kEmptyVariant;
642 ScopedVariant url;
643 url.Set(navigate_url.c_str());
644
645 HRESULT hr = S_OK;
646 hr = web_browser2_->Navigate2(url.AsInput(), &empty, &empty, &empty, &empty);
647 EXPECT_TRUE(hr == S_OK);
648 return hr;
649}
650
651void WebBrowserEventSink::SetFocusToChrome() {
[email protected]4e676aa2010-02-12 18:19:07652 simulate_input::SetKeyboardFocusToWindow(GetRendererWindow());
[email protected]7bc272f2009-12-09 01:09:28653}
654
[email protected]4e676aa2010-02-12 18:19:07655void WebBrowserEventSink::SendKeys(const wchar_t* input_string) {
656 SetFocusToChrome();
[email protected]2d9ebb012010-03-09 18:57:00657 simulate_input::SendStringW(input_string);
[email protected]4e676aa2010-02-12 18:19:07658}
659
660void WebBrowserEventSink::SendMouseClick(int x, int y,
661 simulate_input::MouseButton button) {
662 simulate_input::SendMouseClick(GetRendererWindow(), x, y, button);
[email protected]7bc272f2009-12-09 01:09:28663}
664
665void WebBrowserEventSink::ConnectToChromeFrame() {
666 DCHECK(web_browser2_);
[email protected]9d05f7f2010-04-08 18:36:23667 if (chrome_frame_.get())
668 return;
[email protected]7bc272f2009-12-09 01:09:28669 ScopedComPtr<IShellBrowser> shell_browser;
670 DoQueryService(SID_STopLevelBrowser, web_browser2_,
671 shell_browser.Receive());
672
673 if (shell_browser) {
674 ScopedComPtr<IShellView> shell_view;
675 shell_browser->QueryActiveShellView(shell_view.Receive());
676 if (shell_view) {
677 shell_view->GetItemObject(SVGIO_BACKGROUND, __uuidof(IChromeFrame),
678 reinterpret_cast<void**>(chrome_frame_.Receive()));
679 }
680
681 if (chrome_frame_) {
682 ScopedVariant onmessage(onmessage_.ToDispatch());
683 ScopedVariant onloaderror(onloaderror_.ToDispatch());
684 ScopedVariant onload(onload_.ToDispatch());
685 EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onmessage(onmessage));
686 EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onloaderror(onloaderror));
687 EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onload(onload));
688 }
689 }
690}
691
692void WebBrowserEventSink::DisconnectFromChromeFrame() {
693 if (chrome_frame_) {
694 ScopedVariant dummy(static_cast<IDispatch*>(NULL));
695 chrome_frame_->put_onmessage(dummy);
696 chrome_frame_->put_onload(dummy);
697 chrome_frame_->put_onloaderror(dummy);
698 chrome_frame_.Release();
699 }
700}
701
[email protected]4e676aa2010-02-12 18:19:07702HWND WebBrowserEventSink::GetRendererWindow() {
[email protected]7bc272f2009-12-09 01:09:28703 DCHECK(chrome_frame_);
704 HWND renderer_window = NULL;
705 ScopedComPtr<IOleWindow> ole_window;
706 ole_window.QueryFrom(chrome_frame_);
707 EXPECT_TRUE(ole_window.get());
708
709 if (ole_window) {
710 HWND activex_window = NULL;
711 ole_window->GetWindow(&activex_window);
712 EXPECT_TRUE(IsWindow(activex_window));
713
714 // chrome tab window is the first (and the only) child of activex
715 HWND chrome_tab_window = GetWindow(activex_window, GW_CHILD);
716 EXPECT_TRUE(IsWindow(chrome_tab_window));
717 renderer_window = GetWindow(chrome_tab_window, GW_CHILD);
718 }
719
[email protected]4e676aa2010-02-12 18:19:07720 EXPECT_TRUE(IsWindow(renderer_window));
[email protected]7bc272f2009-12-09 01:09:28721 return renderer_window;
722}
723
724HRESULT WebBrowserEventSink::SetWebBrowser(IWebBrowser2* web_browser2) {
725 DCHECK(web_browser2_.get() == NULL);
[email protected]7aa8d972010-02-17 04:24:49726 DCHECK(!is_main_browser_object_);
[email protected]7bc272f2009-12-09 01:09:28727 web_browser2_ = web_browser2;
728 web_browser2_->put_Visible(VARIANT_TRUE);
729 HRESULT hr = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2);
730 return hr;
731}
732
[email protected]7aa8d972010-02-17 04:24:49733HRESULT WebBrowserEventSink::CloseWebBrowser() {
734 DCHECK(process_id_to_wait_for_ == 0);
735 if (!web_browser2_)
736 return E_FAIL;
[email protected]4381198d2010-04-08 22:17:36737 web_browser2_->Quit();
[email protected]7aa8d972010-02-17 04:24:49738 return S_OK;
739}
740
[email protected]4e676aa2010-02-12 18:19:07741void WebBrowserEventSink::ExpectRendererWindowHasfocus() {
742 HWND renderer_window = GetRendererWindow();
743 EXPECT_TRUE(IsWindow(renderer_window));
744
745 for (HWND first_child = renderer_window;
746 IsWindow(first_child); first_child = GetWindow(first_child, GW_CHILD)) {
747 renderer_window = first_child;
748 }
749
750 wchar_t class_name[MAX_PATH] = {0};
751 GetClassName(renderer_window, class_name, arraysize(class_name));
752 EXPECT_EQ(0, _wcsicmp(class_name, L"Chrome_RenderWidgetHostHWND"));
753
754 DWORD renderer_thread = 0;
755 DWORD renderer_process = 0;
756 renderer_thread = GetWindowThreadProcessId(renderer_window,
757 &renderer_process);
758
759 ASSERT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, TRUE));
760 HWND focus_window = GetFocus();
761 EXPECT_TRUE(focus_window == renderer_window);
762 EXPECT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, FALSE));
763}
764
765void WebBrowserEventSink::Exec(const GUID* cmd_group_guid, DWORD command_id,
766 DWORD cmd_exec_opt, VARIANT* in_args,
767 VARIANT* out_args) {
768 ScopedComPtr<IOleCommandTarget> shell_browser_cmd_target;
769 DoQueryService(SID_STopLevelBrowser, web_browser2_,
770 shell_browser_cmd_target.Receive());
771 ASSERT_TRUE(NULL != shell_browser_cmd_target);
772 EXPECT_HRESULT_SUCCEEDED(shell_browser_cmd_target->Exec(cmd_group_guid,
773 command_id, cmd_exec_opt, in_args, out_args));
774}
775
[email protected]e0b42122010-03-04 21:18:52776void WebBrowserEventSink::WatchChromeWindow(const wchar_t* window_class) {
777 DCHECK(window_class);
778 window_watcher_.AddObserver(this, WideToUTF8(window_class));
779}
780
781void WebBrowserEventSink::StopWatching() {
782 window_watcher_.RemoveObserver(this);
783}
784
[email protected]d8b83b212010-04-07 18:27:52785FilePath GetProfilePathForIE() {
786 FilePath profile_path;
787 // Browsers without IDeleteBrowsingHistory in non-priv mode
788 // have their profiles moved into "Temporary Internet Files".
789 // The code below basically retrieves the version of IE and computes
790 // the profile directory accordingly.
791 std::wstring path = chrome_frame_test::GetExecutableAppPath(kIEImageName);
792 scoped_ptr<FileVersionInfo> ie_version_info(
793 FileVersionInfo::CreateFileVersionInfo(FilePath(path)));
794 std::wstring ie_version = ie_version_info->product_version();
795 if (ie_version[0] == L'8') {
796 profile_path = GetProfilePath(kIEProfileName);
797 } else {
798 profile_path = GetIETemporaryFilesFolder();
799 profile_path = profile_path.Append(L"Google Chrome Frame");
800 }
801 return profile_path;
802}
803
[email protected]f7817822009-09-24 05:11:58804} // namespace chrome_frame_test