blob: faee92af64f9597abb8c28ffb905b3947a85742f [file] [log] [blame]
[email protected]f7817822009-09-24 05:11:581// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// 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]cb4b1e02009-10-29 21:46:2112#include "base/message_loop.h"
[email protected]f7817822009-09-24 05:11:5813#include "base/registry.h" // to find IE and firefox
14#include "base/scoped_handle.h"
15#include "base/scoped_comptr_win.h"
16#include "base/string_util.h"
17#include "base/win_util.h"
18#include "chrome/common/chrome_switches.h"
[email protected]7bc272f2009-12-09 01:09:2819#include "chrome_frame/utils.h"
[email protected]cb4b1e02009-10-29 21:46:2120#include "testing/gtest/include/gtest/gtest.h"
[email protected]f7817822009-09-24 05:11:5821
22namespace chrome_frame_test {
23
24const wchar_t kIEImageName[] = L"iexplore.exe";
25const wchar_t kIEBrokerImageName[] = L"ieuser.exe";
26const wchar_t kFirefoxImageName[] = L"firefox.exe";
27const wchar_t kOperaImageName[] = L"opera.exe";
28const wchar_t kSafariImageName[] = L"safari.exe";
29const wchar_t kChromeImageName[] = L"chrome.exe";
30
31bool IsTopLevelWindow(HWND window) {
32 long style = GetWindowLong(window, GWL_STYLE); // NOLINT
33 if (!(style & WS_CHILD))
34 return true;
35
36 HWND parent = GetParent(window);
37 if (!parent)
38 return true;
39
40 if (parent == GetDesktopWindow())
41 return true;
42
43 return false;
44}
45
46// Callback function for EnumThreadWindows.
47BOOL CALLBACK CloseWindowsThreadCallback(HWND hwnd, LPARAM param) {
48 int& count = *reinterpret_cast<int*>(param);
49 if (IsWindowVisible(hwnd)) {
50 if (IsWindowEnabled(hwnd)) {
51 DWORD results = 0;
52 if (!::SendMessageTimeout(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0, SMTO_BLOCK,
53 10000, &results)) {
[email protected]421e6142010-01-15 22:14:2554 LOG(WARNING) << "Window hung: " << StringPrintf(L"%08X", hwnd);
[email protected]f7817822009-09-24 05:11:5855 }
56 count++;
57 } else {
58 DLOG(WARNING) << "Skipping disabled window: "
59 << StringPrintf(L"%08X", hwnd);
60 }
61 }
62 return TRUE; // continue enumeration
63}
64
65// Attempts to close all non-child, visible windows on the given thread.
66// The return value is the number of visible windows a close request was
67// sent to.
68int CloseVisibleTopLevelWindowsOnThread(DWORD thread_id) {
69 int window_close_attempts = 0;
70 EnumThreadWindows(thread_id, CloseWindowsThreadCallback,
71 reinterpret_cast<LPARAM>(&window_close_attempts));
72 return window_close_attempts;
73}
74
75// Enumerates the threads of a process and attempts to close visible non-child
76// windows on all threads of the process.
77// The return value is the number of visible windows a close request was
78// sent to.
79int CloseVisibleWindowsOnAllThreads(HANDLE process) {
80 DWORD process_id = ::GetProcessId(process);
81 if (process_id == 0) {
82 NOTREACHED();
83 return 0;
84 }
85
86 ScopedHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));
87 if (!snapshot.IsValid()) {
88 NOTREACHED();
89 return 0;
90 }
91
92 int window_close_attempts = 0;
93 THREADENTRY32 te = { sizeof(THREADENTRY32) };
94 if (Thread32First(snapshot, &te)) {
95 do {
96 if (RTL_CONTAINS_FIELD(&te, te.dwSize, th32OwnerProcessID) &&
97 te.th32OwnerProcessID == process_id) {
98 window_close_attempts +=
99 CloseVisibleTopLevelWindowsOnThread(te.th32ThreadID);
100 }
101 te.dwSize = sizeof(te);
102 } while (Thread32Next(snapshot, &te));
103 }
104
105 return window_close_attempts;
106}
107
108class ForegroundHelperWindow : public CWindowImpl<ForegroundHelperWindow> {
109 public:
110BEGIN_MSG_MAP(ForegroundHelperWindow)
111 MESSAGE_HANDLER(WM_HOTKEY, OnHotKey)
112END_MSG_MAP()
113
114 HRESULT SetForeground(HWND window) {
115 DCHECK(::IsWindow(window));
116 if (NULL == Create(NULL, NULL, NULL, WS_POPUP))
117 return AtlHresultFromLastError();
118
[email protected]9c0179d2009-11-18 19:19:20119 static const int kHotKeyId = 0x0000baba;
120 static const int kHotKeyWaitTimeout = 2000;
[email protected]f7817822009-09-24 05:11:58121
122 SetWindowLongPtr(GWLP_USERDATA, reinterpret_cast<ULONG_PTR>(window));
[email protected]9c0179d2009-11-18 19:19:20123 RegisterHotKey(m_hWnd, kHotKeyId, 0, VK_F22);
[email protected]f7817822009-09-24 05:11:58124
125 MSG msg = {0};
126 PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
127
[email protected]9c0179d2009-11-18 19:19:20128 SendVirtualKey(VK_F22, false);
129 // There are scenarios where the WM_HOTKEY is not dispatched by the
130 // the corresponding foreground thread. To prevent us from indefinitely
131 // waiting for the hotkey, we set a timer and exit the loop.
132 SetTimer(kHotKeyId, kHotKeyWaitTimeout, NULL);
[email protected]f7817822009-09-24 05:11:58133
134 while (GetMessage(&msg, NULL, 0, 0)) {
135 TranslateMessage(&msg);
136 DispatchMessage(&msg);
[email protected]9c0179d2009-11-18 19:19:20137 if (msg.message == WM_HOTKEY)
[email protected]f7817822009-09-24 05:11:58138 break;
[email protected]9c0179d2009-11-18 19:19:20139 else if (msg.message == WM_TIMER) {
140 SetForegroundWindow(window);
141 break;
142 }
[email protected]f7817822009-09-24 05:11:58143 }
144
[email protected]9c0179d2009-11-18 19:19:20145 UnregisterHotKey(m_hWnd, kHotKeyId);
146 KillTimer(kHotKeyId);
[email protected]f7817822009-09-24 05:11:58147 DestroyWindow();
[email protected]f7817822009-09-24 05:11:58148 return S_OK;
149 }
150
151 LRESULT OnHotKey(UINT msg, WPARAM wp, LPARAM lp, BOOL& handled) { // NOLINT
152 HWND window = reinterpret_cast<HWND>(GetWindowLongPtr(GWLP_USERDATA));
153 SetForegroundWindow(window);
154 return 1;
155 }
156};
157
158bool ForceSetForegroundWindow(HWND window) {
159 if (GetForegroundWindow() == window)
160 return true;
161 ForegroundHelperWindow foreground_helper_window;
162 HRESULT hr = foreground_helper_window.SetForeground(window);
163 return SUCCEEDED(hr);
164}
165
166struct PidAndWindow {
167 base::ProcessId pid;
168 HWND hwnd;
169};
170
171BOOL CALLBACK FindWindowInProcessCallback(HWND hwnd, LPARAM param) {
172 PidAndWindow* paw = reinterpret_cast<PidAndWindow*>(param);
173 base::ProcessId pid;
174 GetWindowThreadProcessId(hwnd, &pid);
175 if (pid == paw->pid && IsWindowVisible(hwnd)) {
176 paw->hwnd = hwnd;
177 return FALSE;
178 }
179
180 return TRUE;
181}
182
183bool EnsureProcessInForeground(base::ProcessId process_id) {
184 HWND hwnd = GetForegroundWindow();
185 base::ProcessId current_foreground_pid = 0;
186 DWORD active_thread_id = GetWindowThreadProcessId(hwnd,
187 &current_foreground_pid);
188 if (current_foreground_pid == process_id)
189 return true;
190
191 PidAndWindow paw = { process_id };
192 EnumWindows(FindWindowInProcessCallback, reinterpret_cast<LPARAM>(&paw));
193 if (!IsWindow(paw.hwnd)) {
194 DLOG(ERROR) << "failed to find process window";
195 return false;
196 }
197
198 bool ret = ForceSetForegroundWindow(paw.hwnd);
[email protected]421e6142010-01-15 22:14:25199 LOG_IF(ERROR, !ret) << "ForceSetForegroundWindow: " << ret;
[email protected]f7817822009-09-24 05:11:58200
201 return ret;
202}
203
204// Iterates through all the characters in the string and simulates
205// keyboard input. The input goes to the currently active application.
206bool SendString(const wchar_t* string) {
207 DCHECK(string != NULL);
208
209 INPUT input[2] = {0};
210 input[0].type = INPUT_KEYBOARD;
211 input[0].ki.dwFlags = KEYEVENTF_UNICODE; // to avoid shift, etc.
212 input[1] = input[0];
213 input[1].ki.dwFlags |= KEYEVENTF_KEYUP;
214
215 for (const wchar_t* p = string; *p; p++) {
216 input[0].ki.wScan = input[1].ki.wScan = *p;
217 SendInput(2, input, sizeof(INPUT));
218 }
219
220 return true;
221}
222
[email protected]bb3bb6d2009-10-30 01:34:21223void SendVirtualKey(int16 key, bool extended) {
[email protected]f7817822009-09-24 05:11:58224 INPUT input = { INPUT_KEYBOARD };
225 input.ki.wVk = key;
[email protected]bb3bb6d2009-10-30 01:34:21226 input.ki.dwFlags = extended ? KEYEVENTF_EXTENDEDKEY : 0;
[email protected]f7817822009-09-24 05:11:58227 SendInput(1, &input, sizeof(input));
[email protected]bb3bb6d2009-10-30 01:34:21228 input.ki.dwFlags = (extended ? KEYEVENTF_EXTENDEDKEY : 0) | KEYEVENTF_KEYUP;
[email protected]f7817822009-09-24 05:11:58229 SendInput(1, &input, sizeof(input));
230}
231
232void SendChar(char c) {
[email protected]bb3bb6d2009-10-30 01:34:21233 SendVirtualKey(VkKeyScanA(c), false);
[email protected]f7817822009-09-24 05:11:58234}
235
236void SendString(const char* s) {
237 while (*s) {
238 SendChar(*s);
239 s++;
240 }
241}
242
243// Sends a keystroke to the currently active application with optional
244// modifiers set.
245bool SendMnemonic(WORD mnemonic_char, bool shift_pressed, bool control_pressed,
246 bool alt_pressed) {
247 INPUT special_keys[3] = {0};
248 for (int index = 0; index < arraysize(special_keys); ++index) {
249 special_keys[index].type = INPUT_KEYBOARD;
250 special_keys[index].ki.dwFlags = 0;
251 }
252
253 int num_special_keys = 0;
254 if (shift_pressed) {
255 special_keys[num_special_keys].ki.wVk = VK_SHIFT;
256 num_special_keys++;
257 }
258
259 if (control_pressed) {
260 special_keys[num_special_keys].ki.wVk = VK_CONTROL;
261 num_special_keys++;
262 }
263
264 if (alt_pressed) {
265 special_keys[num_special_keys].ki.wVk = VK_MENU;
266 num_special_keys++;
267 }
268
269 // Depress the modifiers.
270 SendInput(num_special_keys, special_keys, sizeof(INPUT));
271
272 Sleep(100);
273
274 INPUT mnemonic = {0};
275 mnemonic.type = INPUT_KEYBOARD;
276 mnemonic.ki.wVk = mnemonic_char;
277
278 // Depress and release the mnemonic.
279 SendInput(1, &mnemonic, sizeof(INPUT));
280 Sleep(100);
281
282 mnemonic.ki.dwFlags |= KEYEVENTF_KEYUP;
283 SendInput(1, &mnemonic, sizeof(INPUT));
284 Sleep(100);
285
286 // Now release the modifiers.
287 for (int index = 0; index < num_special_keys; index++) {
288 special_keys[index].ki.dwFlags |= KEYEVENTF_KEYUP;
289 }
290
291 SendInput(num_special_keys, special_keys, sizeof(INPUT));
292 Sleep(100);
293
294 return true;
295}
296
297std::wstring GetExecutableAppPath(const std::wstring& file) {
298 std::wstring kAppPathsKey =
299 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\";
300
301 std::wstring app_path;
302 RegKey key(HKEY_LOCAL_MACHINE, (kAppPathsKey + file).c_str());
303 if (key.Handle()) {
304 key.ReadValue(NULL, &app_path);
305 }
306
307 return app_path;
308}
309
310std::wstring FormatCommandForApp(const std::wstring& exe_name,
311 const std::wstring& argument) {
312 std::wstring reg_path(StringPrintf(L"Applications\\%ls\\shell\\open\\command",
313 exe_name.c_str()));
314 RegKey key(HKEY_CLASSES_ROOT, reg_path.c_str());
315
316 std::wstring command;
317 if (key.Handle()) {
318 key.ReadValue(NULL, &command);
319 int found = command.find(L"%1");
320 if (found >= 0) {
321 command.replace(found, 2, argument);
322 }
323 }
324 return command;
325}
326
327base::ProcessHandle LaunchExecutable(const std::wstring& executable,
328 const std::wstring& argument) {
329 base::ProcessHandle process = NULL;
330 std::wstring path = GetExecutableAppPath(executable);
331 if (path.empty()) {
332 path = FormatCommandForApp(executable, argument);
333 if (path.empty()) {
334 DLOG(ERROR) << "Failed to find executable: " << executable;
335 } else {
[email protected]51343d5a2009-10-26 22:39:33336 CommandLine cmdline = CommandLine::FromString(path);
[email protected]f7817822009-09-24 05:11:58337 base::LaunchApp(cmdline, false, false, &process);
338 }
339 } else {
[email protected]d1540c82009-10-27 00:18:32340 CommandLine cmdline((FilePath(path)));
[email protected]f7817822009-09-24 05:11:58341 cmdline.AppendLooseValue(argument);
342 base::LaunchApp(cmdline, false, false, &process);
343 }
344 return process;
345}
346
347base::ProcessHandle LaunchFirefox(const std::wstring& url) {
348 return LaunchExecutable(kFirefoxImageName, url);
349}
350
351base::ProcessHandle LaunchSafari(const std::wstring& url) {
352 return LaunchExecutable(kSafariImageName, url);
353}
354
355base::ProcessHandle LaunchChrome(const std::wstring& url) {
356 return LaunchExecutable(kChromeImageName,
357 StringPrintf(L"--%ls ", switches::kNoFirstRun) + url);
358}
359
360base::ProcessHandle LaunchOpera(const std::wstring& url) {
361 // NOTE: For Opera tests to work it must be configured to start up with
362 // a blank page. There is an command line switch, -nosession, that's supposed
363 // to avoid opening up the previous session, but that switch is not working.
364 // TODO(tommi): Include a special ini file (opera6.ini) for opera and launch
365 // with our required settings. This file is by default stored here:
366 // "%USERPROFILE%\Application Data\Opera\Opera\profile\opera6.ini"
367 return LaunchExecutable(kOperaImageName, url);
368}
369
370base::ProcessHandle LaunchIEOnVista(const std::wstring& url) {
371 typedef HRESULT (WINAPI* IELaunchURLPtr)(
372 const wchar_t* url,
373 PROCESS_INFORMATION *pi,
374 VOID *info);
375
376 IELaunchURLPtr launch;
377 PROCESS_INFORMATION pi = {0};
378 IELAUNCHURLINFO info = {sizeof info, 0};
379 HMODULE h = LoadLibrary(L"ieframe.dll");
380 if (!h)
381 return NULL;
382 launch = reinterpret_cast<IELaunchURLPtr>(GetProcAddress(h, "IELaunchURL"));
383 HRESULT hr = launch(url.c_str(), &pi, &info);
384 FreeLibrary(h);
385 if (SUCCEEDED(hr))
386 CloseHandle(pi.hThread);
387 return pi.hProcess;
388}
389
390base::ProcessHandle LaunchIE(const std::wstring& url) {
391 if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) {
392 return LaunchIEOnVista(url);
393 } else {
394 return LaunchExecutable(kIEImageName, url);
395 }
396}
397
398int CloseAllIEWindows() {
399 int ret = 0;
400
401 ScopedComPtr<IShellWindows> windows;
402 HRESULT hr = ::CoCreateInstance(__uuidof(ShellWindows), NULL, CLSCTX_ALL,
403 IID_IShellWindows, reinterpret_cast<void**>(windows.Receive()));
404 DCHECK(SUCCEEDED(hr));
405
406 if (SUCCEEDED(hr)) {
407 long count = 0; // NOLINT
408 windows->get_Count(&count);
409 VARIANT i = { VT_I4 };
410 for (i.lVal = 0; i.lVal < count; ++i.lVal) {
411 ScopedComPtr<IDispatch> folder;
412 windows->Item(i, folder.Receive());
413 if (folder != NULL) {
414 ScopedComPtr<IWebBrowser2> browser;
415 if (SUCCEEDED(browser.QueryFrom(folder))) {
416 browser->Quit();
417 ++ret;
418 }
419 }
420 }
421 }
422
423 return ret;
424}
425
[email protected]a1800e82009-11-19 00:53:23426void ShowChromeFrameContextMenu() {
[email protected]cb4b1e02009-10-29 21:46:21427 static const int kChromeFrameContextMenuTimeout = 500;
428 HWND renderer_window = GetChromeRendererWindow();
429 EXPECT_TRUE(IsWindow(renderer_window));
430
[email protected]24a20e0d2009-11-13 17:46:40431 SetKeyboardFocusToWindow(renderer_window, 100, 100);
432
[email protected]cb4b1e02009-10-29 21:46:21433 // Bring up the context menu in the Chrome renderer window.
434 PostMessage(renderer_window, WM_RBUTTONDOWN, MK_RBUTTON, MAKELPARAM(50, 50));
435 PostMessage(renderer_window, WM_RBUTTONUP, MK_RBUTTON, MAKELPARAM(50, 50));
436
437 MessageLoop::current()->PostDelayedTask(
438 FROM_HERE,
439 NewRunnableFunction(SelectAboutChromeFrame),
440 kChromeFrameContextMenuTimeout);
441}
442
[email protected]24a20e0d2009-11-13 17:46:40443void SetKeyboardFocusToWindow(HWND window, int x, int y) {
[email protected]43e225652009-12-10 23:34:16444 HWND top_level_window = window;
445 if (!IsTopLevelWindow(top_level_window)) {
446 top_level_window = GetAncestor(window, GA_ROOT);
[email protected]24a20e0d2009-11-13 17:46:40447 }
[email protected]43e225652009-12-10 23:34:16448 ForceSetForegroundWindow(top_level_window);
[email protected]24a20e0d2009-11-13 17:46:40449
450 POINT cursor_position = {130, 130};
451 ClientToScreen(window, &cursor_position);
452
453 double screen_width = ::GetSystemMetrics( SM_CXSCREEN ) - 1;
454 double screen_height = ::GetSystemMetrics( SM_CYSCREEN ) - 1;
455 double location_x = cursor_position.x * (65535.0f / screen_width);
456 double location_y = cursor_position.y * (65535.0f / screen_height);
457
458 INPUT input_info = {0};
459 input_info.type = INPUT_MOUSE;
460 input_info.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
461 input_info.mi.dx = static_cast<long>(location_x);
462 input_info.mi.dy = static_cast<long>(location_y);
463 ::SendInput(1, &input_info, sizeof(INPUT));
464
465 Sleep(10);
466
467 input_info.mi.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE;
468 ::SendInput(1, &input_info, sizeof(INPUT));
469
470 Sleep(10);
471
472 input_info.mi.dwFlags = MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE;
473 ::SendInput(1, &input_info, sizeof(INPUT));
474}
475
476void SendInputToWindow(HWND window, const std::string& input_string) {
[email protected]2cc1b3b02009-11-23 23:08:18477 const unsigned long kIntervalBetweenInput = 100;
[email protected]24a20e0d2009-11-13 17:46:40478
479 for (size_t index = 0; index < input_string.length(); index++) {
480 bool is_upper_case = isupper(input_string[index]);
481 if (is_upper_case) {
482 INPUT input = { INPUT_KEYBOARD };
483 input.ki.wVk = VK_SHIFT;
484 input.ki.dwFlags = 0;
485 SendInput(1, &input, sizeof(input));
[email protected]2cc1b3b02009-11-23 23:08:18486 Sleep(kIntervalBetweenInput);
[email protected]24a20e0d2009-11-13 17:46:40487 }
488
489 // The WM_KEYDOWN and WM_KEYUP messages for characters always contain
490 // the uppercase character codes.
491 SendVirtualKey(toupper(input_string[index]), false);
[email protected]2cc1b3b02009-11-23 23:08:18492 Sleep(kIntervalBetweenInput);
[email protected]24a20e0d2009-11-13 17:46:40493
494 if (is_upper_case) {
495 INPUT input = { INPUT_KEYBOARD };
496 input.ki.wVk = VK_SHIFT;
497 input.ki.dwFlags = KEYEVENTF_KEYUP;
498 SendInput(1, &input, sizeof(input));
[email protected]2cc1b3b02009-11-23 23:08:18499 Sleep(kIntervalBetweenInput);
[email protected]24a20e0d2009-11-13 17:46:40500 }
501 }
502}
503
[email protected]cb4b1e02009-10-29 21:46:21504void SelectAboutChromeFrame() {
505 // Send a key up message to enable the About chrome frame option to be
506 // selected followed by a return to select it.
[email protected]bb3bb6d2009-10-30 01:34:21507 SendVirtualKey(VK_UP, true);
508 SendVirtualKey(VK_RETURN, false);
[email protected]cb4b1e02009-10-29 21:46:21509}
510
511BOOL CALLBACK FindChromeRendererWindowProc(
512 HWND window, LPARAM lParam) {
513 HWND* target_window = reinterpret_cast<HWND*>(lParam);
514 wchar_t class_name[MAX_PATH] = {0};
515
516 GetClassName(window, class_name, arraysize(class_name));
517 if (!_wcsicmp(class_name, L"Chrome_RenderWidgetHostHWND")) {
518 *target_window = window;
519 return FALSE;
520 }
521
522 return TRUE;
523}
524
525BOOL CALLBACK EnumHostBrowserWindowProc(
526 HWND window, LPARAM lParam) {
527 EnumChildWindows(window, FindChromeRendererWindowProc, lParam);
528 HWND* target_window = reinterpret_cast<HWND*>(lParam);
529 if (IsWindow(*target_window))
530 return FALSE;
531 return TRUE;
532}
533
534HWND GetChromeRendererWindow() {
535 HWND chrome_window = NULL;
536 EnumWindows(EnumHostBrowserWindowProc,
537 reinterpret_cast<LPARAM>(&chrome_window));
538 return chrome_window;
539}
540
[email protected]42d7c022009-11-20 22:44:58541
542LowIntegrityToken::LowIntegrityToken() : impersonated_(false) {
543}
544
545LowIntegrityToken::~LowIntegrityToken() {
546 RevertToSelf();
547}
548
549BOOL LowIntegrityToken::RevertToSelf() {
550 BOOL ok = TRUE;
551 if (impersonated_) {
552 DCHECK(IsImpersonated());
553 ok = ::RevertToSelf();
554 if (ok)
555 impersonated_ = false;
556 }
557
558 return ok;
559}
560
561BOOL LowIntegrityToken::Impersonate() {
562 DCHECK(!impersonated_);
563 DCHECK(!IsImpersonated());
564 HANDLE process_token_handle = NULL;
565 BOOL ok = ::OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE,
566 &process_token_handle);
567 if (!ok) {
568 DLOG(ERROR) << "::OpenProcessToken failed: " << GetLastError();
569 return ok;
570 }
571
572 ScopedHandle process_token(process_token_handle);
573 // Create impersonation low integrity token.
574 HANDLE impersonation_token_handle = NULL;
575 ok = ::DuplicateTokenEx(process_token,
576 TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_ADJUST_DEFAULT, NULL,
577 SecurityImpersonation, TokenImpersonation, &impersonation_token_handle);
578 if (!ok) {
579 DLOG(ERROR) << "::DuplicateTokenEx failed: " << GetLastError();
580 return ok;
581 }
582
583 // TODO: sandbox/src/restricted_token_utils.cc has SetTokenIntegrityLevel
584 // function already.
585 ScopedHandle impersonation_token(impersonation_token_handle);
586 PSID integrity_sid = NULL;
587 TOKEN_MANDATORY_LABEL tml = {0};
588 ok = ::ConvertStringSidToSid(SDDL_ML_LOW, &integrity_sid);
589 if (!ok) {
590 DLOG(ERROR) << "::ConvertStringSidToSid failed: " << GetLastError();
591 return ok;
592 }
593
594 tml.Label.Attributes = SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED;
595 tml.Label.Sid = integrity_sid;
596 ok = ::SetTokenInformation(impersonation_token, TokenIntegrityLevel,
597 &tml, sizeof(tml) + ::GetLengthSid(integrity_sid));
598 ::LocalFree(integrity_sid);
599 if (!ok) {
600 DLOG(ERROR) << "::SetTokenInformation failed: " << GetLastError();
601 return ok;
602 }
603
604 // Switch current thread to low integrity.
605 ok = ::ImpersonateLoggedOnUser(impersonation_token);
606 if (ok) {
607 impersonated_ = true;
608 } else {
609 DLOG(ERROR) << "::ImpersonateLoggedOnUser failed: " << GetLastError();
610 }
611
612 return ok;
613}
614
615bool LowIntegrityToken::IsImpersonated() {
616 HANDLE token = NULL;
617 if (!::OpenThreadToken(::GetCurrentThread(), 0, false, &token) &&
618 ::GetLastError() != ERROR_NO_TOKEN) {
619 return true;
620 }
621
622 if (token)
623 ::CloseHandle(token);
624
625 return false;
626}
627
[email protected]7bc272f2009-12-09 01:09:28628HRESULT LaunchIEAsComServer(IWebBrowser2** web_browser) {
629 if (!web_browser)
630 return E_INVALIDARG;
631
632 HRESULT hr = S_OK;
633 DWORD cocreate_flags = CLSCTX_LOCAL_SERVER;
634 chrome_frame_test::LowIntegrityToken token;
635 // Vista has a bug which manifests itself when a medium integrity process
636 // launches a COM server like IE which runs in protected mode due to UAC.
637 // This causes the IWebBrowser2 interface which is returned to be useless,
638 // i.e it does not receive any events, etc. Our workaround for this is
639 // to impersonate a low integrity token and then launch IE.
640 if (win_util::GetWinVersion() == win_util::WINVERSION_VISTA) {
641 // Create medium integrity browser that will launch IE broker.
642 ScopedComPtr<IWebBrowser2> medium_integrity_browser;
643 hr = medium_integrity_browser.CreateInstance(CLSID_InternetExplorer, NULL,
644 CLSCTX_LOCAL_SERVER);
645 if (FAILED(hr))
646 return hr;
647 medium_integrity_browser->Quit();
648 // Broker remains alive.
649 if (!token.Impersonate()) {
650 hr = HRESULT_FROM_WIN32(GetLastError());
651 return hr;
652 }
653
654 cocreate_flags |= CLSCTX_ENABLE_CLOAKING;
655 }
656
657 hr = ::CoCreateInstance(CLSID_InternetExplorer, NULL,
658 cocreate_flags, IID_IWebBrowser2,
659 reinterpret_cast<void**>(web_browser));
660 // ~LowIntegrityToken() will switch integrity back to medium.
661 return hr;
662}
663
664_ATL_FUNC_INFO WebBrowserEventSink::kNavigateErrorInfo = {
665 CC_STDCALL, VT_EMPTY, 5, {
666 VT_DISPATCH,
667 VT_VARIANT | VT_BYREF,
668 VT_VARIANT | VT_BYREF,
669 VT_VARIANT | VT_BYREF,
670 VT_BOOL | VT_BYREF,
671 }
672};
673
674_ATL_FUNC_INFO WebBrowserEventSink::kNavigateComplete2Info = {
675 CC_STDCALL, VT_EMPTY, 2, {
676 VT_DISPATCH,
677 VT_VARIANT | VT_BYREF
678 }
679};
680
681_ATL_FUNC_INFO WebBrowserEventSink::kBeforeNavigate2Info = {
682 CC_STDCALL, VT_EMPTY, 7, {
683 VT_DISPATCH,
684 VT_VARIANT | VT_BYREF,
685 VT_VARIANT | VT_BYREF,
686 VT_VARIANT | VT_BYREF,
687 VT_VARIANT | VT_BYREF,
688 VT_VARIANT | VT_BYREF,
689 VT_BOOL | VT_BYREF
690 }
691};
692
693_ATL_FUNC_INFO WebBrowserEventSink::kNewWindow3Info = {
694 CC_STDCALL, VT_EMPTY, 5, {
695 VT_DISPATCH | VT_BYREF,
696 VT_BOOL | VT_BYREF,
697 VT_UINT,
698 VT_BSTR,
699 VT_BSTR
700 }
701};
702
703_ATL_FUNC_INFO WebBrowserEventSink::kVoidMethodInfo = {
704 CC_STDCALL, VT_EMPTY, 0, {NULL}};
705
706_ATL_FUNC_INFO WebBrowserEventSink::kDocumentCompleteInfo = {
707 CC_STDCALL, VT_EMPTY, 2, {
708 VT_DISPATCH,
709 VT_VARIANT | VT_BYREF
710 }
711};
712
713// WebBrowserEventSink member defines
714void WebBrowserEventSink::Uninitialize() {
715 DisconnectFromChromeFrame();
716 if (web_browser2_.get()) {
717 DispEventUnadvise(web_browser2_);
718 web_browser2_->Quit();
719 web_browser2_.Release();
720 }
721}
722
723STDMETHODIMP WebBrowserEventSink::OnBeforeNavigate2Internal(
724 IDispatch* dispatch, VARIANT* url, VARIANT* flags,
725 VARIANT* target_frame_name, VARIANT* post_data, VARIANT* headers,
726 VARIANT_BOOL* cancel) {
727 DLOG(INFO) << __FUNCTION__;
728 // Reset any existing reference to chrome frame since this is a new
729 // navigation.
730 chrome_frame_ = NULL;
731 return OnBeforeNavigate2(dispatch, url, flags, target_frame_name,
732 post_data, headers, cancel);
733}
734
735STDMETHODIMP_(void) WebBrowserEventSink::OnNavigateComplete2Internal(
736 IDispatch* dispatch, VARIANT* url) {
737 DLOG(INFO) << __FUNCTION__;
738 ConnectToChromeFrame();
739 OnNavigateComplete2(dispatch, url);
740}
741
742STDMETHODIMP_(void) WebBrowserEventSink::OnDocumentCompleteInternal(
743 IDispatch* dispatch, VARIANT* url) {
744 DLOG(INFO) << __FUNCTION__;
745 OnDocumentComplete(dispatch, url);
746}
747
748HRESULT WebBrowserEventSink::OnLoadInternal(const VARIANT* param) {
749 DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
750 OnLoad(param->bstrVal);
751 return S_OK;
752}
753
754HRESULT WebBrowserEventSink::OnLoadErrorInternal(const VARIANT* param) {
755 DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
756 OnLoadError(param->bstrVal);
757 return S_OK;
758}
759
760HRESULT WebBrowserEventSink::OnMessageInternal(const VARIANT* param) {
761 DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
762 OnMessage(param->bstrVal);
763 return S_OK;
764}
765
766HRESULT WebBrowserEventSink::LaunchIEAndNavigate(
767 const std::wstring& navigate_url) {
768 HRESULT hr = LaunchIEAsComServer(web_browser2_.Receive());
769 EXPECT_EQ(S_OK, hr);
770 if (hr == S_OK) {
771 web_browser2_->put_Visible(VARIANT_TRUE);
772 hr = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2);
773 EXPECT_TRUE(hr == S_OK);
774 hr = Navigate(navigate_url);
775 }
776 return hr;
777}
778
779HRESULT WebBrowserEventSink::Navigate(const std::wstring& navigate_url) {
780 VARIANT empty = ScopedVariant::kEmptyVariant;
781 ScopedVariant url;
782 url.Set(navigate_url.c_str());
783
784 HRESULT hr = S_OK;
785 hr = web_browser2_->Navigate2(url.AsInput(), &empty, &empty, &empty, &empty);
786 EXPECT_TRUE(hr == S_OK);
787 return hr;
788}
789
790void WebBrowserEventSink::SetFocusToChrome() {
791 chrome_frame_test::SetKeyboardFocusToWindow(
792 GetAttachedChromeRendererWindow(), 1, 1);
793}
794
795void WebBrowserEventSink::SendInputToChrome(
796 const std::string& input_string) {
797 chrome_frame_test::SendInputToWindow(
798 GetAttachedChromeRendererWindow(), input_string);
799}
800
801void WebBrowserEventSink::ConnectToChromeFrame() {
802 DCHECK(web_browser2_);
803 ScopedComPtr<IShellBrowser> shell_browser;
804 DoQueryService(SID_STopLevelBrowser, web_browser2_,
805 shell_browser.Receive());
806
807 if (shell_browser) {
808 ScopedComPtr<IShellView> shell_view;
809 shell_browser->QueryActiveShellView(shell_view.Receive());
810 if (shell_view) {
811 shell_view->GetItemObject(SVGIO_BACKGROUND, __uuidof(IChromeFrame),
812 reinterpret_cast<void**>(chrome_frame_.Receive()));
813 }
814
815 if (chrome_frame_) {
816 ScopedVariant onmessage(onmessage_.ToDispatch());
817 ScopedVariant onloaderror(onloaderror_.ToDispatch());
818 ScopedVariant onload(onload_.ToDispatch());
819 EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onmessage(onmessage));
820 EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onloaderror(onloaderror));
821 EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onload(onload));
822 }
823 }
824}
825
826void WebBrowserEventSink::DisconnectFromChromeFrame() {
827 if (chrome_frame_) {
828 ScopedVariant dummy(static_cast<IDispatch*>(NULL));
829 chrome_frame_->put_onmessage(dummy);
830 chrome_frame_->put_onload(dummy);
831 chrome_frame_->put_onloaderror(dummy);
832 chrome_frame_.Release();
833 }
834}
835
836HWND WebBrowserEventSink::GetAttachedChromeRendererWindow() {
837 DCHECK(chrome_frame_);
838 HWND renderer_window = NULL;
839 ScopedComPtr<IOleWindow> ole_window;
840 ole_window.QueryFrom(chrome_frame_);
841 EXPECT_TRUE(ole_window.get());
842
843 if (ole_window) {
844 HWND activex_window = NULL;
845 ole_window->GetWindow(&activex_window);
846 EXPECT_TRUE(IsWindow(activex_window));
847
848 // chrome tab window is the first (and the only) child of activex
849 HWND chrome_tab_window = GetWindow(activex_window, GW_CHILD);
850 EXPECT_TRUE(IsWindow(chrome_tab_window));
851 renderer_window = GetWindow(chrome_tab_window, GW_CHILD);
852 }
853
854 DCHECK(IsWindow(renderer_window));
855 return renderer_window;
856}
857
858HRESULT WebBrowserEventSink::SetWebBrowser(IWebBrowser2* web_browser2) {
859 DCHECK(web_browser2_.get() == NULL);
860 web_browser2_ = web_browser2;
861 web_browser2_->put_Visible(VARIANT_TRUE);
862 HRESULT hr = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2);
863 return hr;
864}
865
[email protected]f7817822009-09-24 05:11:58866} // namespace chrome_frame_test