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