diff options
author | amit@chromium.org <amit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-12 18:19:07 +0000 |
---|---|---|
committer | amit@chromium.org <amit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-12 18:19:07 +0000 |
commit | 4e676aa41d39aada2731e64f2807611cfad2c785 (patch) | |
tree | 409c055c2d5c1849e707e93b97f55e5afcdaf6b1 /chrome_frame | |
parent | aedd87e5b5e0ee568309eba54b31cb8aa12cd6e6 (diff) | |
download | chromium_src-4e676aa41d39aada2731e64f2807611cfad2c785.zip chromium_src-4e676aa41d39aada2731e64f2807611cfad2c785.tar.gz chromium_src-4e676aa41d39aada2731e64f2807611cfad2c785.tar.bz2 |
First batch of context menu tests
Refactored various methods to send keyboard and mouse
input. Fixed the context menu focus issue on IE7.
Improved existing tests to make them less flaky and
added 3 new tests for context menu items.
BUG=34673
TEST=new tests added
Review URL: http://codereview.chromium.org/604014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@38905 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame')
-rw-r--r-- | chrome_frame/chrome_frame.gyp | 6 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_plugin.h | 13 | ||||
-rw-r--r-- | chrome_frame/test/chrome_frame_test_utils.cc | 397 | ||||
-rw-r--r-- | chrome_frame/test/chrome_frame_test_utils.h | 59 | ||||
-rw-r--r-- | chrome_frame/test/data/keyevent.html | 25 | ||||
-rw-r--r-- | chrome_frame/test/data/xmlhttprequest_authorization_header_test.html | 12 | ||||
-rw-r--r-- | chrome_frame/test/net/dialog_watchdog.cc | 12 | ||||
-rw-r--r-- | chrome_frame/test/simulate_input.cc | 223 | ||||
-rw-r--r-- | chrome_frame/test/simulate_input.h | 60 | ||||
-rw-r--r-- | chrome_frame/test/test_mock_with_web_server.cc | 494 | ||||
-rw-r--r-- | chrome_frame/test/test_mock_with_web_server.h | 4 | ||||
-rw-r--r-- | chrome_frame/test/test_with_web_server.cc | 92 | ||||
-rw-r--r-- | chrome_frame/utils.cc | 9 | ||||
-rw-r--r-- | chrome_frame/utils.h | 4 |
14 files changed, 871 insertions, 539 deletions
diff --git a/chrome_frame/chrome_frame.gyp b/chrome_frame/chrome_frame.gyp index ee4e9c8..a158134 100644 --- a/chrome_frame/chrome_frame.gyp +++ b/chrome_frame/chrome_frame.gyp @@ -205,6 +205,8 @@ 'test/proxy_factory_mock.cc', 'test/proxy_factory_mock.h', 'test/run_all_unittests.cc', + 'test/simulate_input.cc', + 'test/simulate_input.h', 'test/test_mock_with_web_server.cc', 'test/test_mock_with_web_server.h', 'test/test_server.cc', @@ -341,6 +343,8 @@ '../net/url_request/url_request_unittest.h', 'test/chrome_frame_test_utils.cc', 'test/chrome_frame_test_utils.h', + 'test/simulate_input.cc', + 'test/simulate_input.h', 'test/test_server.cc', 'test/test_server.h', 'test/net/dialog_watchdog.cc', @@ -402,6 +406,8 @@ 'test/reliability/reliability_test_suite.h', 'test/chrome_frame_test_utils.cc', 'test/chrome_frame_test_utils.h', + 'test/simulate_input.cc', + 'test/simulate_input.h', 'chrome_tab.h', 'chrome_tab.idl', '../base/test/test_file_util_win.cc', diff --git a/chrome_frame/chrome_frame_plugin.h b/chrome_frame/chrome_frame_plugin.h index 0e16001..293cb99 100644 --- a/chrome_frame/chrome_frame_plugin.h +++ b/chrome_frame/chrome_frame_plugin.h @@ -107,12 +107,19 @@ END_MSG_MAP() if (!copy) return; - T* pThis = static_cast<T*>(this); - if (pThis->PreProcessContextMenu(copy)) { + T* self = static_cast<T*>(this); + if (self->PreProcessContextMenu(copy)) { + // In order for the context menu to handle keyboard input, give the + // ActiveX window focus. + ignore_setfocus_ = true; + SetFocus(GetWindow()); + ignore_setfocus_ = false; UINT flags = align_flags | TPM_LEFTBUTTON | TPM_RETURNCMD | TPM_RECURSE; UINT selected = TrackPopupMenuEx(copy, flags, params.screen_x, params.screen_y, GetWindow(), NULL); - if (selected != 0 && !pThis->HandleContextMenuCommand(selected, params)) { + // Menu is over now give focus back to chrome + GiveFocusToChrome(); + if (selected != 0 && !self->HandleContextMenuCommand(selected, params)) { automation_client_->SendContextMenuCommandToChromeFrame(selected); } } diff --git a/chrome_frame/test/chrome_frame_test_utils.cc b/chrome_frame/test/chrome_frame_test_utils.cc index 0b8176d..79f388b 100644 --- a/chrome_frame/test/chrome_frame_test_utils.cc +++ b/chrome_frame/test/chrome_frame_test_utils.cc @@ -28,21 +28,6 @@ const wchar_t kOperaImageName[] = L"opera.exe"; const wchar_t kSafariImageName[] = L"safari.exe"; const wchar_t kChromeImageName[] = L"chrome.exe"; -bool IsTopLevelWindow(HWND window) { - long style = GetWindowLong(window, GWL_STYLE); // NOLINT - if (!(style & WS_CHILD)) - return true; - - HWND parent = GetParent(window); - if (!parent) - return true; - - if (parent == GetDesktopWindow()) - return true; - - return false; -} - // Callback function for EnumThreadWindows. BOOL CALLBACK CloseWindowsThreadCallback(HWND hwnd, LPARAM param) { int& count = *reinterpret_cast<int*>(param); @@ -105,195 +90,6 @@ int CloseVisibleWindowsOnAllThreads(HANDLE process) { return window_close_attempts; } -class ForegroundHelperWindow : public CWindowImpl<ForegroundHelperWindow> { - public: -BEGIN_MSG_MAP(ForegroundHelperWindow) - MESSAGE_HANDLER(WM_HOTKEY, OnHotKey) -END_MSG_MAP() - - HRESULT SetForeground(HWND window) { - DCHECK(::IsWindow(window)); - if (NULL == Create(NULL, NULL, NULL, WS_POPUP)) - return AtlHresultFromLastError(); - - static const int kHotKeyId = 0x0000baba; - static const int kHotKeyWaitTimeout = 2000; - - SetWindowLongPtr(GWLP_USERDATA, reinterpret_cast<ULONG_PTR>(window)); - RegisterHotKey(m_hWnd, kHotKeyId, 0, VK_F22); - - MSG msg = {0}; - PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); - - SendVirtualKey(VK_F22, false); - // There are scenarios where the WM_HOTKEY is not dispatched by the - // the corresponding foreground thread. To prevent us from indefinitely - // waiting for the hotkey, we set a timer and exit the loop. - SetTimer(kHotKeyId, kHotKeyWaitTimeout, NULL); - - while (GetMessage(&msg, NULL, 0, 0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - if (msg.message == WM_HOTKEY) - break; - else if (msg.message == WM_TIMER) { - SetForegroundWindow(window); - break; - } - } - - UnregisterHotKey(m_hWnd, kHotKeyId); - KillTimer(kHotKeyId); - DestroyWindow(); - return S_OK; - } - - LRESULT OnHotKey(UINT msg, WPARAM wp, LPARAM lp, BOOL& handled) { // NOLINT - HWND window = reinterpret_cast<HWND>(GetWindowLongPtr(GWLP_USERDATA)); - SetForegroundWindow(window); - return 1; - } -}; - -bool ForceSetForegroundWindow(HWND window) { - if (GetForegroundWindow() == window) - return true; - ForegroundHelperWindow foreground_helper_window; - HRESULT hr = foreground_helper_window.SetForeground(window); - return SUCCEEDED(hr); -} - -struct PidAndWindow { - base::ProcessId pid; - HWND hwnd; -}; - -BOOL CALLBACK FindWindowInProcessCallback(HWND hwnd, LPARAM param) { - PidAndWindow* paw = reinterpret_cast<PidAndWindow*>(param); - base::ProcessId pid; - GetWindowThreadProcessId(hwnd, &pid); - if (pid == paw->pid && IsWindowVisible(hwnd)) { - paw->hwnd = hwnd; - return FALSE; - } - - return TRUE; -} - -bool EnsureProcessInForeground(base::ProcessId process_id) { - HWND hwnd = GetForegroundWindow(); - base::ProcessId current_foreground_pid = 0; - DWORD active_thread_id = GetWindowThreadProcessId(hwnd, - ¤t_foreground_pid); - if (current_foreground_pid == process_id) - return true; - - PidAndWindow paw = { process_id }; - EnumWindows(FindWindowInProcessCallback, reinterpret_cast<LPARAM>(&paw)); - if (!IsWindow(paw.hwnd)) { - DLOG(ERROR) << "failed to find process window"; - return false; - } - - bool ret = ForceSetForegroundWindow(paw.hwnd); - LOG_IF(ERROR, !ret) << "ForceSetForegroundWindow: " << ret; - - return ret; -} - -// Iterates through all the characters in the string and simulates -// keyboard input. The input goes to the currently active application. -bool SendString(const wchar_t* string) { - DCHECK(string != NULL); - - INPUT input[2] = {0}; - input[0].type = INPUT_KEYBOARD; - input[0].ki.dwFlags = KEYEVENTF_UNICODE; // to avoid shift, etc. - input[1] = input[0]; - input[1].ki.dwFlags |= KEYEVENTF_KEYUP; - - for (const wchar_t* p = string; *p; p++) { - input[0].ki.wScan = input[1].ki.wScan = *p; - SendInput(2, input, sizeof(INPUT)); - } - - return true; -} - -void SendVirtualKey(int16 key, bool extended) { - INPUT input = { INPUT_KEYBOARD }; - input.ki.wVk = key; - input.ki.dwFlags = extended ? KEYEVENTF_EXTENDEDKEY : 0; - SendInput(1, &input, sizeof(input)); - input.ki.dwFlags = (extended ? KEYEVENTF_EXTENDEDKEY : 0) | KEYEVENTF_KEYUP; - SendInput(1, &input, sizeof(input)); -} - -void SendChar(char c) { - SendVirtualKey(VkKeyScanA(c), false); -} - -void SendString(const char* s) { - while (*s) { - SendChar(*s); - s++; - } -} - -// Sends a keystroke to the currently active application with optional -// modifiers set. -bool SendMnemonic(WORD mnemonic_char, bool shift_pressed, bool control_pressed, - bool alt_pressed) { - INPUT special_keys[3] = {0}; - for (int index = 0; index < arraysize(special_keys); ++index) { - special_keys[index].type = INPUT_KEYBOARD; - special_keys[index].ki.dwFlags = 0; - } - - int num_special_keys = 0; - if (shift_pressed) { - special_keys[num_special_keys].ki.wVk = VK_SHIFT; - num_special_keys++; - } - - if (control_pressed) { - special_keys[num_special_keys].ki.wVk = VK_CONTROL; - num_special_keys++; - } - - if (alt_pressed) { - special_keys[num_special_keys].ki.wVk = VK_MENU; - num_special_keys++; - } - - // Depress the modifiers. - SendInput(num_special_keys, special_keys, sizeof(INPUT)); - - Sleep(100); - - INPUT mnemonic = {0}; - mnemonic.type = INPUT_KEYBOARD; - mnemonic.ki.wVk = mnemonic_char; - - // Depress and release the mnemonic. - SendInput(1, &mnemonic, sizeof(INPUT)); - Sleep(100); - - mnemonic.ki.dwFlags |= KEYEVENTF_KEYUP; - SendInput(1, &mnemonic, sizeof(INPUT)); - Sleep(100); - - // Now release the modifiers. - for (int index = 0; index < num_special_keys; index++) { - special_keys[index].ki.dwFlags |= KEYEVENTF_KEYUP; - } - - SendInput(num_special_keys, special_keys, sizeof(INPUT)); - Sleep(100); - - return true; -} - std::wstring GetExecutableAppPath(const std::wstring& file) { std::wstring kAppPathsKey = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"; @@ -423,121 +219,6 @@ int CloseAllIEWindows() { return ret; } -void ShowChromeFrameContextMenu() { - static const int kChromeFrameContextMenuTimeout = 500; - HWND renderer_window = GetChromeRendererWindow(); - EXPECT_TRUE(IsWindow(renderer_window)); - - SetKeyboardFocusToWindow(renderer_window, 100, 100); - - // Bring up the context menu in the Chrome renderer window. - PostMessage(renderer_window, WM_RBUTTONDOWN, MK_RBUTTON, MAKELPARAM(50, 50)); - PostMessage(renderer_window, WM_RBUTTONUP, MK_RBUTTON, MAKELPARAM(50, 50)); - - MessageLoop::current()->PostDelayedTask( - FROM_HERE, - NewRunnableFunction(SelectAboutChromeFrame), - kChromeFrameContextMenuTimeout); -} - -void SetKeyboardFocusToWindow(HWND window, int x, int y) { - HWND top_level_window = window; - if (!IsTopLevelWindow(top_level_window)) { - top_level_window = GetAncestor(window, GA_ROOT); - } - ForceSetForegroundWindow(top_level_window); - - POINT cursor_position = {130, 130}; - ClientToScreen(window, &cursor_position); - - double screen_width = ::GetSystemMetrics( SM_CXSCREEN ) - 1; - double screen_height = ::GetSystemMetrics( SM_CYSCREEN ) - 1; - double location_x = cursor_position.x * (65535.0f / screen_width); - double location_y = cursor_position.y * (65535.0f / screen_height); - - INPUT input_info = {0}; - input_info.type = INPUT_MOUSE; - input_info.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; - input_info.mi.dx = static_cast<long>(location_x); - input_info.mi.dy = static_cast<long>(location_y); - ::SendInput(1, &input_info, sizeof(INPUT)); - - Sleep(10); - - input_info.mi.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE; - ::SendInput(1, &input_info, sizeof(INPUT)); - - Sleep(10); - - input_info.mi.dwFlags = MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE; - ::SendInput(1, &input_info, sizeof(INPUT)); -} - -void SendInputToWindow(HWND window, const std::string& input_string) { - const unsigned long kIntervalBetweenInput = 100; - - for (size_t index = 0; index < input_string.length(); index++) { - bool is_upper_case = isupper(input_string[index]); - if (is_upper_case) { - INPUT input = { INPUT_KEYBOARD }; - input.ki.wVk = VK_SHIFT; - input.ki.dwFlags = 0; - SendInput(1, &input, sizeof(input)); - Sleep(kIntervalBetweenInput); - } - - // The WM_KEYDOWN and WM_KEYUP messages for characters always contain - // the uppercase character codes. - SendVirtualKey(toupper(input_string[index]), false); - Sleep(kIntervalBetweenInput); - - if (is_upper_case) { - INPUT input = { INPUT_KEYBOARD }; - input.ki.wVk = VK_SHIFT; - input.ki.dwFlags = KEYEVENTF_KEYUP; - SendInput(1, &input, sizeof(input)); - Sleep(kIntervalBetweenInput); - } - } -} - -void SelectAboutChromeFrame() { - // Send a key up message to enable the About chrome frame option to be - // selected followed by a return to select it. - SendVirtualKey(VK_UP, true); - SendVirtualKey(VK_RETURN, false); -} - -BOOL CALLBACK FindChromeRendererWindowProc( - HWND window, LPARAM lParam) { - HWND* target_window = reinterpret_cast<HWND*>(lParam); - wchar_t class_name[MAX_PATH] = {0}; - - GetClassName(window, class_name, arraysize(class_name)); - if (!_wcsicmp(class_name, L"Chrome_RenderWidgetHostHWND")) { - *target_window = window; - return FALSE; - } - - return TRUE; -} - -BOOL CALLBACK EnumHostBrowserWindowProc( - HWND window, LPARAM lParam) { - EnumChildWindows(window, FindChromeRendererWindowProc, lParam); - HWND* target_window = reinterpret_cast<HWND*>(lParam); - if (IsWindow(*target_window)) - return FALSE; - return TRUE; -} - -HWND GetChromeRendererWindow() { - HWND chrome_window = NULL; - EnumWindows(EnumHostBrowserWindowProc, - reinterpret_cast<LPARAM>(&chrome_window)); - return chrome_window; -} - LowIntegrityToken::LowIntegrityToken() : impersonated_(false) { } @@ -802,8 +483,30 @@ HRESULT WebBrowserEventSink::OnLoadErrorInternal(const VARIANT* param) { } HRESULT WebBrowserEventSink::OnMessageInternal(const VARIANT* param) { - DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal; - OnMessage(param->bstrVal); + DLOG(INFO) << __FUNCTION__ << " " << param; + ScopedVariant data, origin, source; + if (param && (V_VT(param) == VT_DISPATCH)) { + wchar_t* properties[] = { L"data", L"origin", L"source" }; + const int prop_count = arraysize(properties); + DISPID ids[prop_count] = {0}; + + HRESULT hr = param->pdispVal->GetIDsOfNames(IID_NULL, properties, + prop_count, LOCALE_SYSTEM_DEFAULT, ids); + if (SUCCEEDED(hr)) { + DISPPARAMS params = { 0 }; + EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[0], IID_NULL, + LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, + data.Receive(), NULL, NULL)); + EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[1], IID_NULL, + LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, + origin.Receive(), NULL, NULL)); + EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[2], IID_NULL, + LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, + source.Receive(), NULL, NULL)); + } + } + + OnMessage(V_BSTR(&data), V_BSTR(&origin), V_BSTR(&source)); return S_OK; } @@ -832,12 +535,17 @@ HRESULT WebBrowserEventSink::Navigate(const std::wstring& navigate_url) { } void WebBrowserEventSink::SetFocusToChrome() { - chrome_frame_test::SetKeyboardFocusToWindow(GetTabWindow(), 1, 1); + simulate_input::SetKeyboardFocusToWindow(GetRendererWindow()); } -void WebBrowserEventSink::SendInputToChrome( - const std::string& input_string) { - chrome_frame_test::SendInputToWindow(GetTabWindow(), input_string); +void WebBrowserEventSink::SendKeys(const wchar_t* input_string) { + SetFocusToChrome(); + simulate_input::SendString(input_string); +} + +void WebBrowserEventSink::SendMouseClick(int x, int y, + simulate_input::MouseButton button) { + simulate_input::SendMouseClick(GetRendererWindow(), x, y, button); } void WebBrowserEventSink::ConnectToChromeFrame() { @@ -875,7 +583,7 @@ void WebBrowserEventSink::DisconnectFromChromeFrame() { } } -HWND WebBrowserEventSink::GetTabWindow() { +HWND WebBrowserEventSink::GetRendererWindow() { DCHECK(chrome_frame_); HWND renderer_window = NULL; ScopedComPtr<IOleWindow> ole_window; @@ -893,7 +601,7 @@ HWND WebBrowserEventSink::GetTabWindow() { renderer_window = GetWindow(chrome_tab_window, GW_CHILD); } - DCHECK(IsWindow(renderer_window)); + EXPECT_TRUE(IsWindow(renderer_window)); return renderer_window; } @@ -905,4 +613,39 @@ HRESULT WebBrowserEventSink::SetWebBrowser(IWebBrowser2* web_browser2) { return hr; } +void WebBrowserEventSink::ExpectRendererWindowHasfocus() { + HWND renderer_window = GetRendererWindow(); + EXPECT_TRUE(IsWindow(renderer_window)); + + for (HWND first_child = renderer_window; + IsWindow(first_child); first_child = GetWindow(first_child, GW_CHILD)) { + renderer_window = first_child; + } + + wchar_t class_name[MAX_PATH] = {0}; + GetClassName(renderer_window, class_name, arraysize(class_name)); + EXPECT_EQ(0, _wcsicmp(class_name, L"Chrome_RenderWidgetHostHWND")); + + DWORD renderer_thread = 0; + DWORD renderer_process = 0; + renderer_thread = GetWindowThreadProcessId(renderer_window, + &renderer_process); + + ASSERT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, TRUE)); + HWND focus_window = GetFocus(); + EXPECT_TRUE(focus_window == renderer_window); + EXPECT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, FALSE)); +} + +void WebBrowserEventSink::Exec(const GUID* cmd_group_guid, DWORD command_id, + DWORD cmd_exec_opt, VARIANT* in_args, + VARIANT* out_args) { + ScopedComPtr<IOleCommandTarget> shell_browser_cmd_target; + DoQueryService(SID_STopLevelBrowser, web_browser2_, + shell_browser_cmd_target.Receive()); + ASSERT_TRUE(NULL != shell_browser_cmd_target); + EXPECT_HRESULT_SUCCEEDED(shell_browser_cmd_target->Exec(cmd_group_guid, + command_id, cmd_exec_opt, in_args, out_args)); +} + } // namespace chrome_frame_test diff --git a/chrome_frame/test/chrome_frame_test_utils.h b/chrome_frame/test/chrome_frame_test_utils.h index 8c0983f..95a0a4f 100644 --- a/chrome_frame/test/chrome_frame_test_utils.h +++ b/chrome_frame/test/chrome_frame_test_utils.h @@ -22,35 +22,13 @@ #include "base/scoped_variant_win.h" #include "chrome_frame/test_utils.h" +#include "chrome_frame/test/simulate_input.h" // Include without path to make GYP build see it. #include "chrome_tab.h" // NOLINT namespace chrome_frame_test { -bool IsTopLevelWindow(HWND window); int CloseVisibleWindowsOnAllThreads(HANDLE process); -bool ForceSetForegroundWindow(HWND window); -bool EnsureProcessInForeground(base::ProcessId process_id); - -// Iterates through all the characters in the string and simulates -// keyboard input. The input goes to the currently active application. -bool SendString(const wchar_t* s); - -// Sends a virtual key such as VK_TAB, VK_RETURN or a character that has been -// translated to a virtual key. -// The extended flag indicates if this is an extended key -void SendVirtualKey(int16 key, bool extended); - -// Translates a single char to a virtual key and calls SendVirtualKey. -void SendChar(char c); - -// Sends an ascii string, char by char (calls SendChar for each). -void SendString(const char* s); - -// Sends a keystroke to the currently active application with optional -// modifiers set. -bool SendMnemonic(WORD mnemonic_char, bool shift_pressed, bool control_pressed, - bool alt_pressed); base::ProcessHandle LaunchFirefox(const std::wstring& url); base::ProcessHandle LaunchOpera(const std::wstring& url); @@ -72,26 +50,6 @@ extern const wchar_t kOperaImageName[]; extern const wchar_t kSafariImageName[]; extern const wchar_t kChromeImageName[]; -// Displays the chrome frame context menu by posting mouse move messages to -// Chrome -void ShowChromeFrameContextMenu(); - -// Sends keyboard messages to the chrome frame context menu to select the About -// Chrome frame option. -void SelectAboutChromeFrame(); - -// Returns a handle to the chrome frame render widget child window. -// Returns NULL on failure. -HWND GetChromeRendererWindow(); - -// Sends the specified input to the window passed in. -void SendInputToWindow(HWND window, const std::string& input_string); - -// Helper function to set keyboard focus to a window. This is achieved by -// sending a mouse move followed by a mouse down/mouse up combination to the -// window. -void SetKeyboardFocusToWindow(HWND window, int x, int y); - // Temporarily impersonate the current thread to low integrity for the lifetime // of the object. Destructor will automatically revert integrity level. class LowIntegrityToken { @@ -202,7 +160,14 @@ class WebBrowserEventSink // Send keyboard input to the renderer window hosted in chrome using // SendInput API - void SendInputToChrome(const std::string& input_string); + void SendKeys(const wchar_t* input_string); + + // Send mouse click to the renderer window hosted in chrome using + // SendInput API + void SendMouseClick(int x, int y, simulate_input::MouseButton button); + + void Exec(const GUID* cmd_group_guid, DWORD command_id, + DWORD cmd_exec_opt, VARIANT* in_args, VARIANT* out_args); BEGIN_COM_MAP(WebBrowserEventSink) END_COM_MAP() @@ -257,7 +222,8 @@ END_SINK_MAP() // Chrome frame callbacks virtual void OnLoad(const wchar_t* url) {} virtual void OnLoadError(const wchar_t* url) {} - virtual void OnMessage(const wchar_t* message) {} + virtual void OnMessage(const wchar_t* message, const wchar_t* origin, + const wchar_t* source) {} IWebBrowser2* web_browser2() { return web_browser2_.get(); @@ -266,6 +232,7 @@ END_SINK_MAP() virtual void OnNewBrowserWindow(IDispatch* new_window, const wchar_t* url) {} HRESULT SetWebBrowser(IWebBrowser2* web_browser2); + void ExpectRendererWindowHasfocus(); protected: STDMETHOD(OnBeforeNavigate2Internal)(IDispatch* dispatch, VARIANT* url, @@ -288,7 +255,7 @@ END_SINK_MAP() void ConnectToChromeFrame(); void DisconnectFromChromeFrame(); - HWND GetTabWindow(); + HWND GetRendererWindow(); public: ScopedComPtr<IWebBrowser2> web_browser2_; diff --git a/chrome_frame/test/data/keyevent.html b/chrome_frame/test/data/keyevent.html index 73c5e40..36f0fbf 100644 --- a/chrome_frame/test/data/keyevent.html +++ b/chrome_frame/test/data/keyevent.html @@ -6,31 +6,26 @@ src="chrome_frame_tester_helpers.js"></script> <script type="text/javascript"> - function ValidateUserAgent() { - if (isRunningInMSIE()) { - onFailure("FullTab_KeyboardTest", 1, "Failed"); - } - } - - var key_count = 0; var input_string = ""; function OnKeyPress() { - if (key_count <= 6) { - input_string += String.fromCharCode(event.keyCode).toString(); - } + appendStatus("Key pressed: " + + String.fromCharCode(event.keyCode).toString()); + input_string += String.fromCharCode(event.keyCode).toString(); - if (input_string == "Chrome") { - onSuccess("FullTab_KeyboardTest", 1); - } else if (key_count >= 6) { - onFailure("FullTab_KeyboardTest", 1, "Invalid input string"); + if (input_string.length >= 6) { + appendStatus("Sending message: " + input_string); + window.externalHost.postMessage(input_string); } } </script> </head> - <body onLoad="setTimeout(ValidateUserAgent, 100);" onkeypress="OnKeyPress()"> + <body onkeypress="OnKeyPress()"> ChromeFrame full tab mode keyboard test. Verifies that keypress events make it correctly into ChromeFrame. + <div id="statusPanel" style="border: 1px solid red; width: 100%"> + Keyboard test running.... + </div> </body> </html> diff --git a/chrome_frame/test/data/xmlhttprequest_authorization_header_test.html b/chrome_frame/test/data/xmlhttprequest_authorization_header_test.html index 361f9f9d..3c23ddf 100644 --- a/chrome_frame/test/data/xmlhttprequest_authorization_header_test.html +++ b/chrome_frame/test/data/xmlhttprequest_authorization_header_test.html @@ -16,10 +16,10 @@ } function SendXHRRequest() { + var test_name = "FullTabModeIE_XHRAuthHeaderTest"; var xhr = getXHRObject(); if (!xhr) { - onFailure("FullTab_XMLHttpRequestAuthorizationHeaderTest", 1, - "Failed to get XHR object"); + onFailure(test_name, 1, "Failed to get XHR object"); } xhr.open("GET", "http://localhost:1337/echoheader?Authorization", false); @@ -30,15 +30,13 @@ var pos = xhr.responseText.indexOf("Basic"); if (pos >= 0) { appendStatus("Received authorization header: " + xhr.responseText); - onSuccess("FullTab_XMLHttpRequestAuthorizationHeaderTest", 1); + onSuccess(test_name, 1); } else { - onFailure("FullTab_XMLHttpRequestAuthorizationHeaderTest", 1, - "Failed to find authorization header in response."); + onFailure(test_name, 1, "Failed to find auth header in response."); } } catch (e) { appendStatus("XHR send failed. Error: " + e.description); - onFailure("FullTab_XMLHttpRequestAuthorizationHeaderTest", 1, - "Failed to send XHR request"); + onFailure(test_name, 1, "Failed to send XHR request"); } } </script> diff --git a/chrome_frame/test/net/dialog_watchdog.cc b/chrome_frame/test/net/dialog_watchdog.cc index 8fd753a..8b2323a 100644 --- a/chrome_frame/test/net/dialog_watchdog.cc +++ b/chrome_frame/test/net/dialog_watchdog.cc @@ -10,7 +10,7 @@ #include "base/scoped_comptr_win.h" #include "base/string_util.h" -#include "chrome_frame/test/chrome_frame_test_utils.h" +#include "chrome_frame/test/simulate_input.h" #include "chrome_frame/function_stub.h" namespace { @@ -54,17 +54,17 @@ bool SupplyProxyCredentials::OnDialogDetected(HWND hwnd, // We can't use SetWindowText to set the username/password, so simulate // keyboard input instead. - chrome_frame_test::ForceSetForegroundWindow(hwnd); + simulate_input::ForceSetForegroundWindow(hwnd); CHECK(SetFocusToAccessibleWindow(props.username_)); - chrome_frame_test::SendString(username_.c_str()); + simulate_input::SendString(username_.c_str()); Sleep(100); - chrome_frame_test::SendVirtualKey(VK_TAB, false); + simulate_input::SendChar(static_cast<char>(VK_TAB), false, false); Sleep(100); - chrome_frame_test::SendString(password_.c_str()); + simulate_input::SendString(password_.c_str()); Sleep(100); - chrome_frame_test::SendVirtualKey(VK_RETURN, false); + simulate_input::SendChar(static_cast<char>(VK_RETURN), false, false); return true; } diff --git a/chrome_frame/test/simulate_input.cc b/chrome_frame/test/simulate_input.cc new file mode 100644 index 0000000..82002a9 --- /dev/null +++ b/chrome_frame/test/simulate_input.cc @@ -0,0 +1,223 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome_frame/test/simulate_input.h" + +#include <atlbase.h> +#include <atlwin.h> + +#include "chrome_frame/utils.h" + +namespace simulate_input { + +class ForegroundHelperWindow : public CWindowImpl<ForegroundHelperWindow> { + public: +BEGIN_MSG_MAP(ForegroundHelperWindow) + MESSAGE_HANDLER(WM_HOTKEY, OnHotKey) +END_MSG_MAP() + + ForegroundHelperWindow() : window_(NULL) {} + + HRESULT SetForeground(HWND window) { + DCHECK(::IsWindow(window)); + window_ = window; + if (NULL == Create(NULL, NULL, NULL, WS_POPUP)) + return AtlHresultFromLastError(); + + static const int kHotKeyId = 0x0000baba; + static const int kHotKeyWaitTimeout = 2000; + + RegisterHotKey(m_hWnd, kHotKeyId, 0, VK_F22); + + MSG msg = {0}; + PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); + + SendMnemonic(VK_F22, false, false, false, false, false); + // There are scenarios where the WM_HOTKEY is not dispatched by the + // the corresponding foreground thread. To prevent us from indefinitely + // waiting for the hotkey, we set a timer and exit the loop. + SetTimer(kHotKeyId, kHotKeyWaitTimeout, NULL); + + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + if (msg.message == WM_HOTKEY) { + break; + } + if (msg.message == WM_TIMER) { + SetForegroundWindow(window); + break; + } + } + + UnregisterHotKey(m_hWnd, kHotKeyId); + KillTimer(kHotKeyId); + DestroyWindow(); + return S_OK; + } + + LRESULT OnHotKey(UINT msg, WPARAM wp, LPARAM lp, BOOL& handled) { // NOLINT + SetForegroundWindow(window_); + return 1; + } + private: + HWND window_; +}; + +bool ForceSetForegroundWindow(HWND window) { + if (GetForegroundWindow() == window) + return true; + ForegroundHelperWindow foreground_helper_window; + HRESULT hr = foreground_helper_window.SetForeground(window); + return SUCCEEDED(hr); +} + +struct PidAndWindow { + base::ProcessId pid; + HWND hwnd; +}; + +BOOL CALLBACK FindWindowInProcessCallback(HWND hwnd, LPARAM param) { + PidAndWindow* paw = reinterpret_cast<PidAndWindow*>(param); + base::ProcessId pid; + GetWindowThreadProcessId(hwnd, &pid); + if (pid == paw->pid && IsWindowVisible(hwnd)) { + paw->hwnd = hwnd; + return FALSE; + } + + return TRUE; +} + +bool EnsureProcessInForeground(base::ProcessId process_id) { + HWND hwnd = GetForegroundWindow(); + base::ProcessId current_foreground_pid = 0; + DWORD active_thread_id = GetWindowThreadProcessId(hwnd, + ¤t_foreground_pid); + if (current_foreground_pid == process_id) + return true; + + PidAndWindow paw = { process_id }; + EnumWindows(FindWindowInProcessCallback, reinterpret_cast<LPARAM>(&paw)); + if (!IsWindow(paw.hwnd)) { + DLOG(ERROR) << "failed to find process window"; + return false; + } + + bool ret = ForceSetForegroundWindow(paw.hwnd); + LOG_IF(ERROR, !ret) << "ForceSetForegroundWindow: " << ret; + + return ret; +} + +void SendChar(char c, bool control, bool alt) { + SendMnemonic(toupper(c), !!isupper(c), control, alt, false, false); +} + +void SendChar(wchar_t c, bool control, bool alt) { + SendMnemonic(towupper(c), !!iswupper(c), control, alt, false, true); +} + +// Sends a keystroke to the currently active application with optional +// modifiers set. +bool SendMnemonic(WORD mnemonic_char, bool shift_pressed, bool control_pressed, + bool alt_pressed, bool extended, bool unicode) { + INPUT keys[4] = {0}; // Keyboard events + int key_count = 0; // Number of generated events + + if (shift_pressed) { + keys[key_count].type = INPUT_KEYBOARD; + keys[key_count].ki.wVk = VK_SHIFT; + key_count++; + } + + if (control_pressed) { + keys[key_count].type = INPUT_KEYBOARD; + keys[key_count].ki.wVk = VK_CONTROL; + key_count++; + } + + if (alt_pressed) { + keys[key_count].type = INPUT_KEYBOARD; + keys[key_count].ki.wVk = VK_MENU; + key_count++; + } + + keys[key_count].type = INPUT_KEYBOARD; + keys[key_count].ki.wVk = mnemonic_char; + if (extended) + keys[key_count].ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; + if (unicode) + keys[key_count].ki.dwFlags |= KEYEVENTF_UNICODE; + key_count++; + + // Send key downs + for (int i = 0; i < key_count; i++) { + SendInput(1, &keys[ i ], sizeof(keys[0])); + keys[i].ki.dwFlags |= KEYEVENTF_KEYUP; + } + + // Now send key ups in reverse order + for (int i = key_count; i; i--) { + SendInput(1, &keys[ i - 1 ], sizeof(keys[0])); + } + + return true; +} + +void SetKeyboardFocusToWindow(HWND window) { + SendMouseClick(window, 1, 1, LEFT); +} + +void SendMouseClick(HWND window, int x, int y, MouseButton button) { + if (!IsWindow(window)) { + NOTREACHED() << "Invalid window handle."; + return; + } + + HWND top_level_window = window; + if (!IsTopLevelWindow(top_level_window)) { + top_level_window = GetAncestor(window, GA_ROOT); + } + + ForceSetForegroundWindow(top_level_window); + + POINT cursor_position = {x, y}; + ClientToScreen(window, &cursor_position); + + // TODO(joshia): Fix this. GetSystemMetrics(SM_CXSCREEN) will + // retrieve screen size of the primarary monitor only. And monitors + // arrangement could be pretty arbitrary. + double screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1; + double screen_height = ::GetSystemMetrics(SM_CYSCREEN) - 1; + double location_x = cursor_position.x * (65535.0f / screen_width); + double location_y = cursor_position.y * (65535.0f / screen_height); + + // Take advantage of button flag bitmask layout + unsigned int button_flag = MOUSEEVENTF_LEFTDOWN << (button + button); + + INPUT input_info = {0}; + input_info.type = INPUT_MOUSE; + input_info.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; + input_info.mi.dx = static_cast<LONG>(location_x); + input_info.mi.dy = static_cast<LONG>(location_y); + ::SendInput(1, &input_info, sizeof(INPUT)); + + Sleep(10); + + input_info.mi.dwFlags = button_flag | MOUSEEVENTF_ABSOLUTE; + ::SendInput(1, &input_info, sizeof(INPUT)); + + Sleep(10); + + input_info.mi.dwFlags = (button_flag << 1) | MOUSEEVENTF_ABSOLUTE; + ::SendInput(1, &input_info, sizeof(INPUT)); +} + +bool SendExtendedKey(WORD key, bool shift, bool control, bool alt) { + return SendMnemonic(key, shift, control, alt, true, false); +} + +} // namespace simulate_input + diff --git a/chrome_frame/test/simulate_input.h b/chrome_frame/test/simulate_input.h new file mode 100644 index 0000000..562362a --- /dev/null +++ b/chrome_frame/test/simulate_input.h @@ -0,0 +1,60 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_FRAME_TEST_SIMULATE_INPUT_H_ +#define CHROME_FRAME_TEST_SIMULATE_INPUT_H_ + +#include <windows.h> + +#include "base/basictypes.h" +#include "base/process_util.h" + +namespace simulate_input { + +// Bring a window into foreground to receive user input. +// Note that this may not work on +bool ForceSetForegroundWindow(HWND window); + +// Looks for a top level window owned by the given process id and +// calls ForceSetForegroundWindow on it. +bool EnsureProcessInForeground(base::ProcessId process_id); + +// Helper function to set keyboard focus to a window. This is achieved by +// sending a mouse move followed by a mouse down/mouse up combination to the +// window. +void SetKeyboardFocusToWindow(HWND window); + +// Sends a keystroke to the currently active application with optional +// modifiers set. +bool SendMnemonic(WORD mnemonic_char, bool shift_pressed, bool control_pressed, + bool alt_pressed, bool extended, bool unicode); + +// Sends a mouse click to the window passed in. +enum MouseButton { LEFT, RIGHT, MIDDLE, X }; +void SendMouseClick(HWND window, int x, int y, MouseButton button); + +// Translates a single char to a virtual key and calls SendVirtualKey. +void SendChar(char c, bool control, bool alt); +void SendChar(wchar_t c, bool control, bool alt); + +// Sends extended keystroke to the currently active application with optional +// modifiers set. +bool SendExtendedKey(WORD key, bool shift, bool control, bool alt); + +// Iterates through all the characters in the string and simulates +// keyboard input. The input goes to the currently active application. +template <typename char_type> +void SendString(const char_type* s) { + while (*s) { + char_type ch = *s; + SendChar(ch, false, false); + Sleep(100); + s++; + } +} + +} // end namespace simulate_input + +#endif // CHROME_FRAME_TEST_SIMULATE_INPUT_H_ + diff --git a/chrome_frame/test/test_mock_with_web_server.cc b/chrome_frame/test/test_mock_with_web_server.cc index e024dea..76079b4 100644 --- a/chrome_frame/test/test_mock_with_web_server.cc +++ b/chrome_frame/test/test_mock_with_web_server.cc @@ -19,6 +19,8 @@ const int kChromeFrameLaunchDelay = 5; const int kChromeFrameLongNavigationTimeoutInSeconds = 10; const wchar_t kChromeFrameFileUrl[] = L"gcf:file:///C:/"; +const wchar_t enter_key[] = { VK_RETURN, 0 }; +const wchar_t tab_enter_keys[] = { VK_TAB, VK_RETURN, 0 }; TEST(ChromeFrameTest, FullTabModeIE_DisallowedUrls) { chrome_frame_test::TimedMsgLoop loop; @@ -56,10 +58,80 @@ TEST(ChromeFrameTest, FullTabModeIE_DisallowedUrls) { chrome_frame_test::CloseAllIEWindows(); } -const wchar_t kChromeFrameFullTabWindowOpenTestUrl[] = +const wchar_t kKeyEventUrl[] = L"http://localhost:1337/files/keyevent.html"; + +// Marking this test FLAKY as it fails at times on the buildbot. +// http://code.google.com/p/chromium/issues/detail?id=26549 +TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_KeyboardTest) { + chrome_frame_test::TimedMsgLoop loop; + CComObjectStackEx<MockWebBrowserEventSink> mock; + + EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, + testing::StrCaseEq(kKeyEventUrl)), + _, _, _, _, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return(S_OK)); + EXPECT_CALL(mock, OnNavigateComplete2(_, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return()); + + const wchar_t* input = L"Chrome"; + EXPECT_CALL(mock, OnLoad(testing::StrEq(kKeyEventUrl))) + .WillOnce(testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableMethod( + &mock, + &MockWebBrowserEventSink::SendKeys, input), 500))); + + EXPECT_CALL(mock, OnMessage(testing::StrEq(input), _, _)) + .WillOnce(QUIT_LOOP(loop)); + + HRESULT hr = mock.LaunchIEAndNavigate(kKeyEventUrl); + ASSERT_HRESULT_SUCCEEDED(hr); + if (hr == S_FALSE) + return; + + loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); + + mock.Uninitialize(); + chrome_frame_test::CloseAllIEWindows(); +} + +const wchar_t kAboutVersionUrl[] = L"gcf:about:version"; +const wchar_t kAboutVersion[] = L"about:version"; + +TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_FocusTest) { + chrome_frame_test::TimedMsgLoop loop; + CComObjectStackEx<MockWebBrowserEventSink> mock; + + EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, + testing::StrCaseEq(kAboutVersionUrl)), + _, _, _, _, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return(S_OK)); + EXPECT_CALL(mock, OnNavigateComplete2(_, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return()); + + EXPECT_CALL(mock, OnLoad(testing::StrEq(kAboutVersion))) + .WillOnce(testing::DoAll( + testing::InvokeWithoutArgs(CreateFunctor(&mock, + &MockWebBrowserEventSink::ExpectRendererWindowHasfocus)), + testing::InvokeWithoutArgs(CreateFunctor(&mock, + &MockWebBrowserEventSink::Uninitialize)), + testing::IgnoreResult(testing::InvokeWithoutArgs( + &chrome_frame_test::CloseAllIEWindows)), + QUIT_LOOP_SOON(loop, 2))); + + HRESULT hr = mock.LaunchIEAndNavigate(kAboutVersionUrl); + + // Allow some time for chrome to be launched. + loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); + + mock.Uninitialize(); + chrome_frame_test::CloseAllIEWindows(); +} + +const wchar_t kFullTabWindowOpenTestUrl[] = L"http://localhost:1337/files/chrome_frame_window_open.html"; -const wchar_t kChromeFrameFullTabWindowOpenPopupUrl[] = +const wchar_t kFullTabWindowOpenPopupUrl[] = L"http://localhost:1337/files/chrome_frame_window_open_popup.html"; // This test checks if window.open calls issued by a full tab mode ChromeFrame @@ -80,23 +152,19 @@ TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_WindowOpenInChrome) { EXPECT_CALL(mock, OnBeforeNavigate2( _, testing::Field(&VARIANT::bstrVal, - testing::StrCaseEq(kChromeFrameFullTabWindowOpenTestUrl)), + testing::StrCaseEq(kFullTabWindowOpenTestUrl)), _, _, _, _, _)) .Times(testing::AnyNumber()).WillRepeatedly(testing::Return(S_OK)); EXPECT_CALL(mock, OnNavigateComplete2(_, _)) .Times(testing::AnyNumber()).WillRepeatedly(testing::Return()); + const wchar_t* input = L"A"; EXPECT_CALL(mock, - OnLoad(testing::StrEq(kChromeFrameFullTabWindowOpenTestUrl))) - .WillOnce(testing::DoAll( - testing::InvokeWithoutArgs(CreateFunctor(&mock, - &chrome_frame_test::WebBrowserEventSink::SetFocusToChrome)), - testing::InvokeWithoutArgs(CreateFunctor(&loop, - &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, - NewRunnableMethod( - &mock, - &chrome_frame_test::WebBrowserEventSink::SendInputToChrome, - std::string("A")), 0)))); + OnLoad(testing::StrEq(kFullTabWindowOpenTestUrl))) + .WillOnce(testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableMethod( + &mock, &MockWebBrowserEventSink::SendKeys, input), 500))); // Watch for new window CComObjectStackEx<MockWebBrowserEventSink> new_window_mock; @@ -113,7 +181,7 @@ TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_WindowOpenInChrome) { EXPECT_CALL(new_window_mock, OnNavigateComplete2(_, _)) .Times(testing::AnyNumber()).WillRepeatedly(testing::Return()); EXPECT_CALL(new_window_mock, - OnLoad(testing::StrEq(kChromeFrameFullTabWindowOpenPopupUrl))) + OnLoad(testing::StrEq(kFullTabWindowOpenPopupUrl))) .WillOnce(testing::DoAll( testing::InvokeWithoutArgs(CreateFunctor(&mock, &MockWebBrowserEventSink::Uninitialize)), @@ -123,7 +191,7 @@ TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_WindowOpenInChrome) { &chrome_frame_test::CloseAllIEWindows)), QUIT_LOOP_SOON(loop, 2))); - HRESULT hr = mock.LaunchIEAndNavigate(kChromeFrameFullTabWindowOpenTestUrl); + HRESULT hr = mock.LaunchIEAndNavigate(kFullTabWindowOpenTestUrl); ASSERT_HRESULT_SUCCEEDED(hr); if (hr == S_FALSE) return; @@ -134,17 +202,13 @@ TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_WindowOpenInChrome) { mock.Uninitialize(); chrome_frame_test::CloseAllIEWindows(); - ASSERT_TRUE(CheckResultFile(L"ChromeFrameWindowOpenPopup", "OK")); } const wchar_t kSubFrameUrl1[] = L"http://localhost:1337/files/sub_frame1.html"; -const wchar_t kChromeFrameAboutVersion[] = - L"gcf:about:version"; - // This test launches chrome frame in full tab mode in IE by having IE navigate -// to gcf:about:blank. It then looks for the chrome renderer window and posts +// to a url. It then looks for the chrome renderer window and posts // the WM_RBUTTONDOWN/WM_RBUTTONUP messages to it, which bring up the context // menu. This is followed by keyboard messages sent via SendInput to select // the About chrome frame menu option, which would then bring up a new window @@ -157,8 +221,7 @@ TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_AboutChromeFrame) { chrome_frame_test::TimedMsgLoop loop; CComObjectStackEx<MockWebBrowserEventSink> mock; - EXPECT_CALL(mock, - OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, + EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, testing::StrCaseEq(kSubFrameUrl1)), _, _, _, _, _)) .Times(testing::AnyNumber()).WillRepeatedly(testing::Return(S_OK)); @@ -166,13 +229,44 @@ TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_AboutChromeFrame) { .Times(testing::AnyNumber()).WillRepeatedly(testing::Return()); EXPECT_CALL(mock, OnLoad(testing::StrEq(kSubFrameUrl1))) - .WillOnce(testing::InvokeWithoutArgs( - &chrome_frame_test::ShowChromeFrameContextMenu)); + .WillOnce(testing::DoAll( + testing::InvokeWithoutArgs(CreateFunctor(&mock, + &MockWebBrowserEventSink::SendMouseClick, 10, 10, + simulate_input::RIGHT)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendExtendedKey, VK_UP, false, false, false), + 500)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendString<wchar_t>, &enter_key[0]), + 600)))); - EXPECT_CALL(mock, - OnNewWindow3(_, _, _, _, - testing::StrCaseEq(kChromeFrameAboutVersion))) - .WillOnce(QUIT_LOOP(loop)); + // Watch for new window + CComObjectStackEx<MockWebBrowserEventSink> new_window_mock; + EXPECT_CALL(mock, OnNewWindow3(_, _, _, _, + testing::StrCaseEq(kAboutVersionUrl))); + EXPECT_CALL(mock, OnNewBrowserWindow(_, _)) + .WillOnce(testing::WithArgs<0>( + testing::Invoke(CreateFunctor(&new_window_mock, + &MockWebBrowserEventSink::Attach)))); + + // Expect navigations on the new mock + EXPECT_CALL(new_window_mock, OnBeforeNavigate2(_, _, _, _, _, _, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return(S_OK)); + EXPECT_CALL(new_window_mock, OnNavigateComplete2(_, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return()); + EXPECT_CALL(new_window_mock, OnLoad(testing::StrEq(kAboutVersion))) + .WillOnce(testing::DoAll( + testing::InvokeWithoutArgs(CreateFunctor(&mock, + &MockWebBrowserEventSink::Uninitialize)), + testing::InvokeWithoutArgs(CreateFunctor(&new_window_mock, + &MockWebBrowserEventSink::Uninitialize)), + testing::IgnoreResult(testing::InvokeWithoutArgs( + &chrome_frame_test::CloseAllIEWindows)), + QUIT_LOOP_SOON(loop, 2))); HRESULT hr = mock.LaunchIEAndNavigate(kSubFrameUrl1); ASSERT_HRESULT_SUCCEEDED(hr); @@ -180,7 +274,6 @@ TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_AboutChromeFrame) { return; ASSERT_TRUE(mock.web_browser2() != NULL); - loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); mock.Uninitialize(); @@ -346,8 +439,6 @@ const wchar_t kAnchor3Url[] = L"http://localhost:1337/files/anchor.html#a3"; // Marking this test FLAKY as it fails at times on the buildbot. // http://code.google.com/p/chromium/issues/detail?id=26549 TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_BackForwardAnchor) { - const char tab_enter_keystrokes[] = { VK_TAB, VK_RETURN, 0 }; - static const std::string tab_enter(tab_enter_keystrokes); chrome_frame_test::TimedMsgLoop loop; CComObjectStackEx<MockWebBrowserEventSink> mock; ::testing::InSequence sequence; // Everything in sequence @@ -390,10 +481,9 @@ TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_BackForwardAnchor) { &chrome_frame_test::WebBrowserEventSink::SetFocusToChrome)), testing::InvokeWithoutArgs(CreateFunctor(&loop, &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, - NewRunnableMethod( - &mock, - &chrome_frame_test::WebBrowserEventSink::SendInputToChrome, - std::string(tab_enter)), 0)))); + NewRunnableFunction( + &simulate_input::SendString<wchar_t>, + &tab_enter_keys[0]), 200)))); EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, testing::StrCaseEq(kAnchor1Url)), _, _, _, _, _)) @@ -410,10 +500,9 @@ TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_BackForwardAnchor) { CreateFunctor( &loop, &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, - NewRunnableMethod( - &mock, - &chrome_frame_test::WebBrowserEventSink::SendInputToChrome, - std::string(tab_enter)), 0))); + NewRunnableFunction( + &simulate_input::SendString<wchar_t>, + &tab_enter_keys[0]), 200))); EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, testing::StrCaseEq(kAnchor2Url)), _, _, _, _, _)) @@ -430,10 +519,9 @@ TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_BackForwardAnchor) { CreateFunctor( &loop, &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, - NewRunnableMethod( - &mock, - &chrome_frame_test::WebBrowserEventSink::SendInputToChrome, - std::string(tab_enter)), 0))); + NewRunnableFunction( + &simulate_input::SendString<wchar_t>, + &tab_enter_keys[0]), 200))); EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, testing::StrCaseEq(kAnchor3Url)), _, _, _, _, _)) @@ -550,11 +638,10 @@ TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_ViewSource) { VARIANT empty = ScopedVariant::kEmptyVariant; EXPECT_CALL(mock, OnLoad(testing::StrEq(kAnchorUrl))) - .WillOnce(testing::IgnoreResult(testing::InvokeWithoutArgs( - CreateFunctor(ReceivePointer(mock.web_browser2_), - &IWebBrowser2::ExecWB, + .WillOnce(testing::InvokeWithoutArgs( + CreateFunctor(&mock, &MockWebBrowserEventSink::Exec, &CGID_MSHTML, static_cast<OLECMDID>(IDM_VIEWSOURCE), - OLECMDEXECOPT_DONTPROMPTUSER, &empty, &empty)))); + OLECMDEXECOPT_DONTPROMPTUSER, &empty, &empty))); // Expect notification for view-source window, handle new window event // and attach a new mock to the received web browser @@ -600,14 +687,13 @@ TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_ViewSource) { chrome_frame_test::CloseAllIEWindows(); } -const wchar_t kChromeFrameFullTabModeBeforeUnloadEventTest[] = +const wchar_t kFullTabModeBeforeUnloadEventTest[] = L"http://localhost:1337/files/fulltab_before_unload_event_test.html"; -const wchar_t kChromeFrameFullTabModeBeforeUnloadEventMain[] = +const wchar_t kFullTabModeBeforeUnloadEventMain[] = L"http://localhost:1337/files/fulltab_before_unload_event_main.html"; -TEST_F(ChromeFrameTestWithWebServer, - FullTabModeIE_ChromeFrameUnloadEventTest) { +TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_UnloadEventTest) { chrome_frame_test::TimedMsgLoop loop; CComObjectStackEx<MockWebBrowserEventSink> mock; ::testing::InSequence sequence; // Everything in sequence @@ -618,7 +704,7 @@ TEST_F(ChromeFrameTestWithWebServer, mock, OnBeforeNavigate2( _, testing::Field(&VARIANT::bstrVal, - testing::StrCaseEq(kChromeFrameFullTabModeBeforeUnloadEventTest)), + testing::StrCaseEq(kFullTabModeBeforeUnloadEventTest)), _, _, _, _, _)) .WillOnce(testing::Return(S_OK)); @@ -629,7 +715,7 @@ TEST_F(ChromeFrameTestWithWebServer, mock, OnBeforeNavigate2( _, testing::Field(&VARIANT::bstrVal, - testing::StrCaseEq(kChromeFrameFullTabModeBeforeUnloadEventTest)), + testing::StrCaseEq(kFullTabModeBeforeUnloadEventTest)), _, _, _, _, _)) .Times(testing::AnyNumber()).WillRepeatedly(testing::Return(S_OK)); @@ -644,7 +730,7 @@ TEST_F(ChromeFrameTestWithWebServer, mock, OnBeforeNavigate2( _, testing::Field(&VARIANT::bstrVal, - testing::StrCaseEq(kChromeFrameFullTabModeBeforeUnloadEventMain)), + testing::StrCaseEq(kFullTabModeBeforeUnloadEventMain)), _, _, _, _, _)) .WillOnce(testing::Return(S_OK)); @@ -655,7 +741,7 @@ TEST_F(ChromeFrameTestWithWebServer, mock, OnBeforeNavigate2( _, testing::Field(&VARIANT::bstrVal, - testing::StrCaseEq(kChromeFrameFullTabModeBeforeUnloadEventMain)), + testing::StrCaseEq(kFullTabModeBeforeUnloadEventMain)), _, _, _, _, _)) .Times(testing::AnyNumber()).WillRepeatedly(testing::Return(S_OK)); @@ -664,7 +750,7 @@ TEST_F(ChromeFrameTestWithWebServer, EXPECT_CALL(mock, OnLoad(_)).WillOnce(testing::Return()); - EXPECT_CALL(mock, OnMessage(_)) + EXPECT_CALL(mock, OnMessage(_, _, _)) .WillOnce(testing::DoAll( testing::InvokeWithoutArgs(CreateFunctor(&mock, &chrome_frame_test::WebBrowserEventSink::Uninitialize)), @@ -672,8 +758,299 @@ TEST_F(ChromeFrameTestWithWebServer, &chrome_frame_test::CloseAllIEWindows)), QUIT_LOOP_SOON(loop, 2))); - HRESULT hr = - mock.LaunchIEAndNavigate(kChromeFrameFullTabModeBeforeUnloadEventTest); + HRESULT hr = mock.LaunchIEAndNavigate(kFullTabModeBeforeUnloadEventTest); + ASSERT_HRESULT_SUCCEEDED(hr); + if (hr == S_FALSE) + return; + + ASSERT_TRUE(mock.web_browser2() != NULL); + loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); + mock.Uninitialize(); + chrome_frame_test::CloseAllIEWindows(); +} + +// Test Back/Forward from context menu. Loads page 1 in chrome and page 2 +// in IE. Then it tests back and forward using context menu +TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_ContextMenuBackForward) { + chrome_frame_test::TimedMsgLoop loop; + CComObjectStackEx<MockWebBrowserEventSink> mock; + + ::testing::InSequence sequence; // Everything in sequence + EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, + testing::StrCaseEq(kSubFrameUrl1)), + _, _, _, _, _)) + .WillOnce(testing::Return(S_OK)); + EXPECT_CALL(mock, OnNavigateComplete2(_, _)).WillOnce(testing::Return()); + EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, + testing::StrCaseEq(kSubFrameUrl1)), + _, _, _, _, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return(S_OK)); + EXPECT_CALL(mock, OnNavigateComplete2(_, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return()); + + // Navigate to url 2 after the previous navigation is complete. + EXPECT_CALL(mock, OnLoad(testing::StrEq(kSubFrameUrl1))) + .WillOnce(testing::IgnoreResult(testing::InvokeWithoutArgs( + CreateFunctor(&mock, + &chrome_frame_test::WebBrowserEventSink::Navigate, + std::wstring(kSubFrameUrl2))))); + + EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, + testing::StrCaseEq(kSubFrameUrl2)), + _, _, _, _, _)) + .WillOnce(testing::Return(S_OK)); + EXPECT_CALL(mock, OnNavigateComplete2(_, _)).WillOnce(testing::Return()); + EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, + testing::StrCaseEq(kSubFrameUrl2)), + _, _, _, _, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return(S_OK)); + EXPECT_CALL(mock, OnNavigateComplete2(_, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return()); + + // Go back using Rt-Click + DOWN + ENTER + EXPECT_CALL(mock, OnLoad(testing::StrEq(kSubFrameUrl2))) + .WillOnce(testing::DoAll( + testing::InvokeWithoutArgs(CreateFunctor(&mock, + &MockWebBrowserEventSink::SendMouseClick, 10, 10, + simulate_input::RIGHT)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendExtendedKey, VK_DOWN, false, false, + false), + 500)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendString<wchar_t>, &enter_key[0]), + 600)))); + + EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, + testing::StrCaseEq(kSubFrameUrl1)), + _, _, _, _, _)) + .WillOnce(testing::Return(S_OK)); + EXPECT_CALL(mock, OnNavigateComplete2(_, _)).WillOnce(testing::Return()); + EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, + testing::StrCaseEq(kSubFrameUrl1)), + _, _, _, _, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return(S_OK)); + EXPECT_CALL(mock, OnNavigateComplete2(_, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return()); + + // Go forward using Rt-Click + DOWN + DOWN + ENTER + EXPECT_CALL(mock, OnLoad(testing::StrEq(kSubFrameUrl1))) + .WillOnce(testing::DoAll( + testing::InvokeWithoutArgs(CreateFunctor(&mock, + &MockWebBrowserEventSink::SendMouseClick, 10, 10, + simulate_input::RIGHT)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendExtendedKey, VK_DOWN, false, false, + false), + 500)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendExtendedKey, VK_DOWN, false, false, + false), + 600)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendString<wchar_t>, &enter_key[0]), + 700)))); + + EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, + testing::StrCaseEq(kSubFrameUrl2)), + _, _, _, _, _)) + .WillOnce(testing::Return(S_OK)); + EXPECT_CALL(mock, OnNavigateComplete2(_, _)).WillOnce(testing::Return()); + EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, + testing::StrCaseEq(kSubFrameUrl2)), + _, _, _, _, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return(S_OK)); + EXPECT_CALL(mock, OnNavigateComplete2(_, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return()); + + EXPECT_CALL(mock, OnLoad(testing::StrEq(kSubFrameUrl2))) + .WillOnce(testing::DoAll( + testing::InvokeWithoutArgs(CreateFunctor(&mock, + &MockWebBrowserEventSink::Uninitialize)), + testing::IgnoreResult(testing::InvokeWithoutArgs( + &chrome_frame_test::CloseAllIEWindows)), + QUIT_LOOP_SOON(loop, 2))); + + HRESULT hr = mock.LaunchIEAndNavigate(kSubFrameUrl1); + ASSERT_HRESULT_SUCCEEDED(hr); + if (hr == S_FALSE) + return; + + ASSERT_TRUE(mock.web_browser2() != NULL); + loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); + mock.Uninitialize(); + chrome_frame_test::CloseAllIEWindows(); +} + +// Test Reload from context menu. +TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_ContextMenuReload) { + chrome_frame_test::TimedMsgLoop loop; + CComObjectStackEx<MockWebBrowserEventSink> mock; + + ::testing::InSequence sequence; // Everything in sequence + EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, + testing::StrCaseEq(kSubFrameUrl1)), + _, _, _, _, _)) + .WillOnce(testing::Return(S_OK)); + EXPECT_CALL(mock, OnNavigateComplete2(_, _)).WillOnce(testing::Return()); + EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, + testing::StrCaseEq(kSubFrameUrl1)), + _, _, _, _, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return(S_OK)); + EXPECT_CALL(mock, OnNavigateComplete2(_, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return()); + + // Reload using Rt-Click + DOWN + DOWN + DOWN + ENTER + EXPECT_CALL(mock, OnLoad(testing::StrEq(kSubFrameUrl1))) + .WillOnce(testing::DoAll( + testing::InvokeWithoutArgs(CreateFunctor(&mock, + &MockWebBrowserEventSink::SendMouseClick, 10, 10, + simulate_input::RIGHT)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendExtendedKey, VK_DOWN, false, false, + false), + 500)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendExtendedKey, VK_DOWN, false, false, + false), + 600)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendExtendedKey, VK_DOWN, false, false, + false), + 700)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendString<wchar_t>, &enter_key[0]), + 800)))); + + // Go forward using Rt-Click + DOWN + DOWN + ENTER + EXPECT_CALL(mock, OnLoad(testing::StrEq(kSubFrameUrl1))) + .WillOnce(testing::DoAll( + testing::InvokeWithoutArgs(CreateFunctor(&mock, + &MockWebBrowserEventSink::Uninitialize)), + testing::IgnoreResult(testing::InvokeWithoutArgs( + &chrome_frame_test::CloseAllIEWindows)), + QUIT_LOOP_SOON(loop, 2))); + + HRESULT hr = mock.LaunchIEAndNavigate(kSubFrameUrl1); + ASSERT_HRESULT_SUCCEEDED(hr); + if (hr == S_FALSE) + return; + + ASSERT_TRUE(mock.web_browser2() != NULL); + loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); + mock.Uninitialize(); + chrome_frame_test::CloseAllIEWindows(); +} + +// Test view source using context menu +TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_ContextMenuViewSource) { + chrome_frame_test::TimedMsgLoop loop; + CComObjectStackEx<MockWebBrowserEventSink> mock; + CComObjectStackEx<MockWebBrowserEventSink> view_source_mock; + ::testing::InSequence sequence; // Everything in sequence + + // After navigation invoke view soruce action using IWebBrowser2::ExecWB + EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, + testing::StrCaseEq(kAnchorUrl)), + _, _, _, _, _)) + .WillOnce(testing::Return(S_OK)); + EXPECT_CALL(mock, OnNavigateComplete2(_, _)) + .WillOnce(testing::Return()); + EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, + testing::StrCaseEq(kAnchorUrl)), + _, _, _, _, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return(S_OK)); + EXPECT_CALL(mock, OnNavigateComplete2(_, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return()); + + // view soruce using Rt-Click + UP + UP + UP + UP + ENTER + EXPECT_CALL(mock, OnLoad(testing::StrEq(kAnchorUrl))) + .WillOnce(testing::DoAll( + testing::InvokeWithoutArgs(CreateFunctor(&mock, + &MockWebBrowserEventSink::SendMouseClick, 10, 10, + simulate_input::RIGHT)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendExtendedKey, VK_UP, false, false, + false), + 500)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendExtendedKey, VK_UP, false, false, + false), + 600)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendExtendedKey, VK_UP, false, false, + false), + 700)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendExtendedKey, VK_UP, false, false, + false), + 800)), + testing::InvokeWithoutArgs(CreateFunctor(&loop, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableFunction( + simulate_input::SendString<wchar_t>, &enter_key[0]), + 900)))); + + // Expect notification for view-source window, handle new window event + // and attach a new mock to the received web browser + std::wstring view_source_url; + view_source_url += UTF8ToWide(chrome::kViewSourceScheme); + view_source_url += L":"; + view_source_url += kAnchorUrl; + std::wstring url_in_new_window = kChromeProtocolPrefix; + url_in_new_window += view_source_url; + + EXPECT_CALL(mock, OnNewWindow3(_, _, _, _, + testing::StrCaseEq(url_in_new_window))); + EXPECT_CALL(mock, OnNewBrowserWindow(_, _)) + .WillOnce(testing::WithArgs<0>( + testing::Invoke(CreateFunctor(&view_source_mock, + &MockWebBrowserEventSink::Attach)))); + + // Expect navigations on the new mock + EXPECT_CALL(view_source_mock, OnBeforeNavigate2(_, + testing::Field(&VARIANT::bstrVal, + testing::StrCaseEq(url_in_new_window)), _, _, _, _, _)) + .Times(testing::AnyNumber()).WillRepeatedly(testing::Return(S_OK)); + EXPECT_CALL(view_source_mock, OnNavigateComplete2(_, _)) + .WillOnce(testing::Return()); + EXPECT_CALL(view_source_mock, OnLoad(testing::StrEq(view_source_url))) + .WillOnce(testing::DoAll( + testing::InvokeWithoutArgs(CreateFunctor(&mock, + &MockWebBrowserEventSink::Uninitialize)), + testing::InvokeWithoutArgs(CreateFunctor(&view_source_mock, + &MockWebBrowserEventSink::Uninitialize)), + testing::IgnoreResult(testing::InvokeWithoutArgs( + &chrome_frame_test::CloseAllIEWindows)), + QUIT_LOOP_SOON(loop, 2))); + + HRESULT hr = mock.LaunchIEAndNavigate(kAnchorUrl); ASSERT_HRESULT_SUCCEEDED(hr); if (hr == S_FALSE) return; @@ -684,3 +1061,4 @@ TEST_F(ChromeFrameTestWithWebServer, chrome_frame_test::CloseAllIEWindows(); } + diff --git a/chrome_frame/test/test_mock_with_web_server.h b/chrome_frame/test/test_mock_with_web_server.h index 346e38d..57a6e99 100644 --- a/chrome_frame/test/test_mock_with_web_server.h +++ b/chrome_frame/test/test_mock_with_web_server.h @@ -55,7 +55,9 @@ class MockWebBrowserEventSink : public chrome_frame_test::WebBrowserEventSink { MOCK_METHOD1(OnLoad, void (const wchar_t* url)); // NOLINT MOCK_METHOD1(OnLoadError, void (const wchar_t* url)); // NOLINT - MOCK_METHOD1(OnMessage, void (const wchar_t* message)); // NOLINT + MOCK_METHOD3(OnMessage, void (const wchar_t* message, + const wchar_t* origin, + const wchar_t* source)); // NOLINT MOCK_METHOD2(OnNewBrowserWindow, void (IDispatch* dispatch, // NOLINT const wchar_t* url)); }; diff --git a/chrome_frame/test/test_with_web_server.cc b/chrome_frame/test/test_with_web_server.cc index 5ae8ac9..7f39e41 100644 --- a/chrome_frame/test/test_with_web_server.cc +++ b/chrome_frame/test/test_with_web_server.cc @@ -120,7 +120,7 @@ void ChromeFrameTestWithWebServer::CloseBrowser() { } bool ChromeFrameTestWithWebServer::BringBrowserToTop() { - return chrome_frame_test::EnsureProcessInForeground( + return simulate_input::EnsureProcessInForeground( GetProcessId(browser_handle_)); } @@ -314,7 +314,7 @@ TEST_F(ChromeFrameTestWithWebServer, WidgetModeOpera_ObjectFocus) { i < 5 && (ok = CheckResultFile(L"ObjectFocus", "OK")) == false; ++i) { Sleep(300); - chrome_frame_test::SendMnemonic(VK_TAB, false, false, false); + simulate_input::SendMnemonic(VK_TAB, false, false, false, false, false); } ASSERT_TRUE(ok); } @@ -698,64 +698,12 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_SubIFrame) { SimpleBrowserTest(IE, kSubIFrameTestPage, L"sub_frame"); } -const wchar_t kChromeFrameFullTabModeKeyEventUrl[] = L"files/keyevent.html"; - -// Marking this test FLAKY as it fails at times on the buildbot. -// http://code.google.com/p/chromium/issues/detail?id=26549 -TEST_F(ChromeFrameTestWithWebServer, - FLAKY_FullTabModeIE_ChromeFrameKeyboardTest) { - chrome_frame_test::TimedMsgLoop loop; - - ASSERT_TRUE(LaunchBrowser(IE, kChromeFrameFullTabModeKeyEventUrl)); - - // Allow some time for chrome to be launched. - loop.RunFor(kChromeFrameLaunchDelay); - - HWND renderer_window = chrome_frame_test::GetChromeRendererWindow(); - EXPECT_TRUE(IsWindow(renderer_window)); - - chrome_frame_test::SetKeyboardFocusToWindow(renderer_window, 1, 1); - chrome_frame_test::SendInputToWindow(renderer_window, "Chrome"); - - loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); - - chrome_frame_test::CloseAllIEWindows(); - ASSERT_TRUE(CheckResultFile(L"FullTab_KeyboardTest", "OK")); -} - -const wchar_t kChromeFrameAboutBlankUrl[] = L"gcf:about:blank"; - -TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_ChromeFrameFocusTest) { - chrome_frame_test::TimedMsgLoop loop; - - ASSERT_TRUE(LaunchBrowser(IE, kChromeFrameAboutBlankUrl)); - - // Allow some time for chrome to be launched. - loop.RunFor(kChromeFrameLaunchDelay); - - HWND renderer_window = chrome_frame_test::GetChromeRendererWindow(); - EXPECT_TRUE(IsWindow(renderer_window)); - - DWORD renderer_thread_id = 0; - DWORD renderer_process_id = 0; - renderer_thread_id = GetWindowThreadProcessId(renderer_window, - &renderer_process_id); - - AttachThreadInput(GetCurrentThreadId(), renderer_thread_id, TRUE); - HWND focus_window = GetFocus(); - EXPECT_TRUE(focus_window == renderer_window); - AttachThreadInput(GetCurrentThreadId(), renderer_thread_id, FALSE); - - chrome_frame_test::CloseAllIEWindows(); -} - -const wchar_t kChromeFrameFullTabModeXMLHttpRequestTestUrl[] = +const wchar_t kXMLHttpRequestTestUrl[] = L"files/xmlhttprequest_test.html"; -TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_ChromeFrameXHRTest) { +TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_XHRTest) { chrome_frame_test::TimedMsgLoop loop; - - ASSERT_TRUE(LaunchBrowser(IE, kChromeFrameFullTabModeXMLHttpRequestTestUrl)); + ASSERT_TRUE(LaunchBrowser(IE, kXMLHttpRequestTestUrl)); loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); @@ -779,31 +727,25 @@ TEST_F(ChromeFrameTestWithWebServer, L"WidgetMode_MultipleInstancesTest"); } -const wchar_t kChromeFrameFullTabModeXMLHttpRequestAuthHeaderTestUrl[] = +const wchar_t kXHRAuthHeaderTestUrl[] = L"files/xmlhttprequest_authorization_header_test.html"; -TEST_F(ChromeFrameTestWithWebServer, - FullTabModeIE_ChromeFrameXHRAuthHeaderTest) { +TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_XHRAuthHeaderTest) { chrome_frame_test::TimedMsgLoop loop; - - ASSERT_TRUE(LaunchBrowser( - IE, kChromeFrameFullTabModeXMLHttpRequestAuthHeaderTestUrl)); + ASSERT_TRUE(LaunchBrowser(IE, kXHRAuthHeaderTestUrl)); loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); chrome_frame_test::CloseAllIEWindows(); - ASSERT_TRUE( - CheckResultFile(L"FullTab_XMLHttpRequestAuthorizationHeaderTest", "OK")); + ASSERT_TRUE(CheckResultFile(L"FullTabModeIE_XHRAuthHeaderTest", "OK")); } -const wchar_t kChromeFrameFullTabModeDeleteCookieTest[] = +const wchar_t kDeleteCookieTest[] = L"files/fulltab_delete_cookie_test.html"; -TEST_F(ChromeFrameTestWithWebServer, - FullTabModeIE_ChromeFrameDeleteCookieTest) { +TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_DeleteCookieTest) { chrome_frame_test::TimedMsgLoop loop; - - ASSERT_TRUE(LaunchBrowser(IE, kChromeFrameFullTabModeDeleteCookieTest)); + ASSERT_TRUE(LaunchBrowser(IE, kDeleteCookieTest)); loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); @@ -811,14 +753,14 @@ TEST_F(ChromeFrameTestWithWebServer, ASSERT_TRUE(CheckResultFile(L"FullTab_DeleteCookieTest", "OK")); } -const wchar_t kChromeFrameFullTabModeAnchorUrlNavigate[] = +const wchar_t kAnchorUrlNavigate[] = L"files/fulltab_anchor_url_navigate.html#chrome_frame"; +// http://code.google.com/p/chromium/issues/detail?id=35341 TEST_F(ChromeFrameTestWithWebServer, - FullTabModeIE_ChromeFrameAnchorUrlNavigateTest) { + FLAKY_FullTabModeIE_AnchorUrlNavigateTest) { chrome_frame_test::TimedMsgLoop loop; - - ASSERT_TRUE(LaunchBrowser(IE, kChromeFrameFullTabModeAnchorUrlNavigate)); + ASSERT_TRUE(LaunchBrowser(IE, kAnchorUrlNavigate)); loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); @@ -832,8 +774,6 @@ TEST_F(ChromeFrameTestWithWebServer, TEST_F(ChromeFrameTestWithWebServer, DISABLED_FullTabModeIE_TestPostReissue) { // Test whether POST-ing a form from an mshtml page to a CF page will cause // the request to get reissued. It should not. - - FilePath source_path; PathService::Get(base::DIR_SOURCE_ROOT, &source_path); source_path = source_path.Append(FILE_PATH_LITERAL("chrome_frame")) diff --git a/chrome_frame/utils.cc b/chrome_frame/utils.cc index 620865d..bd803bf 100644 --- a/chrome_frame/utils.cc +++ b/chrome_frame/utils.cc @@ -857,3 +857,12 @@ std::wstring GetActualUrlFromMoniker(IMoniker* moniker, return moniker_url; } +bool IsTopLevelWindow(HWND window) { + long style = GetWindowLong(window, GWL_STYLE); // NOLINT + if (!(style & WS_CHILD)) + return true; + + HWND parent = GetParent(window); + return !parent || (parent == GetDesktopWindow()); +} + diff --git a/chrome_frame/utils.h b/chrome_frame/utils.h index 2cbb277..440c1b0 100644 --- a/chrome_frame/utils.h +++ b/chrome_frame/utils.h @@ -344,4 +344,8 @@ std::wstring GetActualUrlFromMoniker(IMoniker* moniker, IBindCtx* bind_context, const std::wstring& bho_url); +// Checks if a window is a top level window +bool IsTopLevelWindow(HWND window); + + #endif // CHROME_FRAME_UTILS_H_ |