diff options
-rw-r--r-- | chrome_frame/chrome_frame.gyp | 55 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_reporting.cc | 13 | ||||
-rw-r--r-- | chrome_frame/crash_reporting/crash_report.cc | 16 | ||||
-rw-r--r-- | chrome_frame/test/chrome_frame_test_utils.cc | 239 | ||||
-rw-r--r-- | chrome_frame/test/chrome_frame_test_utils.h | 188 | ||||
-rw-r--r-- | chrome_frame/test/chrome_frame_unittests.cc | 305 | ||||
-rw-r--r-- | chrome_frame/test/chrome_frame_unittests.h | 127 | ||||
-rw-r--r-- | chrome_frame/test/reliability/page_load_test.cc | 624 | ||||
-rw-r--r-- | chrome_frame/test/reliability/page_load_test.h | 17 | ||||
-rw-r--r-- | chrome_frame/test/reliability/reliability_test_suite.h | 30 | ||||
-rw-r--r-- | chrome_frame/test/reliability/run_all_unittests.cc | 10 | ||||
-rw-r--r-- | chrome_frame/utils.cc | 35 | ||||
-rw-r--r-- | chrome_frame/utils.h | 15 |
13 files changed, 1283 insertions, 391 deletions
diff --git a/chrome_frame/chrome_frame.gyp b/chrome_frame/chrome_frame.gyp index 870f9da..905e34f 100644 --- a/chrome_frame/chrome_frame.gyp +++ b/chrome_frame/chrome_frame.gyp @@ -446,6 +446,8 @@ 'test/net/test_automation_provider.h', 'test/net/test_automation_resource_message_filter.cc', 'test/net/test_automation_resource_message_filter.h', + 'chrome_tab.h', + 'chrome_tab.idl', ], 'include_dirs': [ # To allow including "chrome_tab.h" @@ -465,6 +467,59 @@ }, { + 'target_name': 'chrome_frame_reliability_tests', + 'type': 'executable', + 'dependencies': [ + '../build/temp_gyp/googleurl.gyp:googleurl', + '../chrome/chrome.gyp:browser', + '../chrome/chrome.gyp:utility', + '../testing/gtest.gyp:gtest', + '../base/allocator/allocator.gyp:allocator', + 'chrome_frame_npapi', + 'chrome_frame_strings', + ], + 'sources': [ + 'test/reliability/run_all_unittests.cc', + 'test/reliability/page_load_test.cc', + 'test/reliability/page_load_test.h', + 'test/reliability/reliability_test_suite.h', + 'test/chrome_frame_test_utils.cc', + 'test/chrome_frame_test_utils.h', + 'chrome_tab.h', + 'chrome_tab.idl', + '../base/test/test_file_util_win.cc', + '../chrome/test/ui/ui_test.cc', + '../chrome/test/ui/ui_test_suite.cc', + '../chrome/test/ui/ui_test_suite.h', + '../chrome/test/chrome_process_util_win.cc', + '../chrome/test/chrome_process_util.cc', + '../chrome/test/chrome_process_util.h', + '../chrome/test/chrome_process_util_win.cc', + ], + 'include_dirs': [ + # To allow including "chrome_tab.h" + '<(INTERMEDIATE_DIR)', + ], + 'resource_include_dirs': [ + '<(INTERMEDIATE_DIR)', + ], + 'conditions': [ + ['OS=="win"', { + 'sources': [ + '<(SHARED_INTERMEDIATE_DIR)/chrome_frame/chrome_frame_resources.rc', + '<(SHARED_INTERMEDIATE_DIR)/chrome_frame/chrome_frame_strings.rc', + ], + 'dependencies': [ + # TODO(slightlyoff): Get automation targets working on OS X + '../chrome/chrome.gyp:automation', + '../chrome/installer/installer.gyp:installer_util', + '../google_update/google_update.gyp:google_update', + ] + }], + ], + }, + + { 'target_name': 'chrome_frame_npapi', 'type': 'static_library', 'dependencies': [ diff --git a/chrome_frame/chrome_frame_reporting.cc b/chrome_frame/chrome_frame_reporting.cc index 170a710..688651e 100644 --- a/chrome_frame/chrome_frame_reporting.cc +++ b/chrome_frame/chrome_frame_reporting.cc @@ -11,6 +11,7 @@ #include "chrome/installer/util/google_update_settings.h" #include "chrome/installer/util/install_util.h" #include "chrome_frame/chrome_frame_reporting.h" +#include "chrome_frame/utils.h" // Well known SID for the system principal. const wchar_t kSystemPrincipalSid[] = L"S-1-5-18"; @@ -46,11 +47,13 @@ google_breakpad::CustomClientInfo* GetCustomInfo(const wchar_t* dll_path) { extern "C" IMAGE_DOS_HEADER __ImageBase; bool InitializeCrashReporting() { - // We want to use the Google Update crash reporting. We need to check if the - // user allows it first. - if (!GoogleUpdateSettings::GetCollectStatsConsent()) - return true; - + // In headless mode we want crashes to be reported back. + if (!IsHeadlessMode()) { + // We want to use the Google Update crash reporting. We need to check if the + // user allows it first. + if (!GoogleUpdateSettings::GetCollectStatsConsent()) + return true; + } // Build the pipe name. It can be either: // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18" // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>" diff --git a/chrome_frame/crash_reporting/crash_report.cc b/chrome_frame/crash_reporting/crash_report.cc index 1c594c5..f858941 100644 --- a/chrome_frame/crash_reporting/crash_report.cc +++ b/chrome_frame/crash_reporting/crash_report.cc @@ -1,3 +1,4 @@ + // Copyright (c) 2009 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. @@ -10,10 +11,13 @@ #include "breakpad/src/client/windows/handler/exception_handler.h" #include "chrome_frame/crash_reporting/vectored_handler.h" #include "chrome_frame/crash_reporting/vectored_handler-impl.h" +#include "chrome_frame/utils.h" namespace { // TODO(joshia): factor out common code with chrome used for crash reporting const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\"; +const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices"; + google_breakpad::ExceptionHandler* g_breakpad = NULL; __declspec(naked) @@ -56,6 +60,15 @@ class Win32VEHTraits : public VEHTraitsBase { extern "C" IMAGE_DOS_HEADER __ImageBase; +std::wstring GetCrashServerPipeName(const std::wstring& user_sid) { + if (IsHeadlessMode()) + return kChromePipeName; + + std::wstring pipe_name = kGoogleUpdatePipeName; + pipe_name += user_sid; + return pipe_name; +} + bool InitializeVectoredCrashReporting( bool full_dump, const wchar_t* user_sid, @@ -66,8 +79,7 @@ bool InitializeVectoredCrashReporting( if (g_breakpad) return true; - std::wstring pipe_name(kGoogleUpdatePipeName); - pipe_name += user_sid; + std::wstring pipe_name = GetCrashServerPipeName(user_sid); if (dump_path.empty()) { return false; diff --git a/chrome_frame/test/chrome_frame_test_utils.cc b/chrome_frame/test/chrome_frame_test_utils.cc index 8777651..9f2a6de 100644 --- a/chrome_frame/test/chrome_frame_test_utils.cc +++ b/chrome_frame/test/chrome_frame_test_utils.cc @@ -16,6 +16,7 @@ #include "base/string_util.h" #include "base/win_util.h" #include "chrome/common/chrome_switches.h" +#include "chrome_frame/utils.h" #include "testing/gtest/include/gtest/gtest.h" namespace chrome_frame_test { @@ -623,4 +624,242 @@ bool LowIntegrityToken::IsImpersonated() { return false; } +HRESULT LaunchIEAsComServer(IWebBrowser2** web_browser) { + if (!web_browser) + return E_INVALIDARG; + + HRESULT hr = S_OK; + DWORD cocreate_flags = CLSCTX_LOCAL_SERVER; + chrome_frame_test::LowIntegrityToken token; + // Vista has a bug which manifests itself when a medium integrity process + // launches a COM server like IE which runs in protected mode due to UAC. + // This causes the IWebBrowser2 interface which is returned to be useless, + // i.e it does not receive any events, etc. Our workaround for this is + // to impersonate a low integrity token and then launch IE. + if (win_util::GetWinVersion() == win_util::WINVERSION_VISTA) { + // Create medium integrity browser that will launch IE broker. + ScopedComPtr<IWebBrowser2> medium_integrity_browser; + hr = medium_integrity_browser.CreateInstance(CLSID_InternetExplorer, NULL, + CLSCTX_LOCAL_SERVER); + if (FAILED(hr)) + return hr; + medium_integrity_browser->Quit(); + // Broker remains alive. + if (!token.Impersonate()) { + hr = HRESULT_FROM_WIN32(GetLastError()); + return hr; + } + + cocreate_flags |= CLSCTX_ENABLE_CLOAKING; + } + + hr = ::CoCreateInstance(CLSID_InternetExplorer, NULL, + cocreate_flags, IID_IWebBrowser2, + reinterpret_cast<void**>(web_browser)); + // ~LowIntegrityToken() will switch integrity back to medium. + return hr; +} + +_ATL_FUNC_INFO WebBrowserEventSink::kNavigateErrorInfo = { + CC_STDCALL, VT_EMPTY, 5, { + VT_DISPATCH, + VT_VARIANT | VT_BYREF, + VT_VARIANT | VT_BYREF, + VT_VARIANT | VT_BYREF, + VT_BOOL | VT_BYREF, + } +}; + +_ATL_FUNC_INFO WebBrowserEventSink::kNavigateComplete2Info = { + CC_STDCALL, VT_EMPTY, 2, { + VT_DISPATCH, + VT_VARIANT | VT_BYREF + } +}; + +_ATL_FUNC_INFO WebBrowserEventSink::kBeforeNavigate2Info = { + CC_STDCALL, VT_EMPTY, 7, { + VT_DISPATCH, + VT_VARIANT | VT_BYREF, + VT_VARIANT | VT_BYREF, + VT_VARIANT | VT_BYREF, + VT_VARIANT | VT_BYREF, + VT_VARIANT | VT_BYREF, + VT_BOOL | VT_BYREF + } +}; + +_ATL_FUNC_INFO WebBrowserEventSink::kNewWindow3Info = { + CC_STDCALL, VT_EMPTY, 5, { + VT_DISPATCH | VT_BYREF, + VT_BOOL | VT_BYREF, + VT_UINT, + VT_BSTR, + VT_BSTR + } +}; + +_ATL_FUNC_INFO WebBrowserEventSink::kVoidMethodInfo = { + CC_STDCALL, VT_EMPTY, 0, {NULL}}; + +_ATL_FUNC_INFO WebBrowserEventSink::kDocumentCompleteInfo = { + CC_STDCALL, VT_EMPTY, 2, { + VT_DISPATCH, + VT_VARIANT | VT_BYREF + } +}; + +// WebBrowserEventSink member defines +void WebBrowserEventSink::Uninitialize() { + DisconnectFromChromeFrame(); + if (web_browser2_.get()) { + DispEventUnadvise(web_browser2_); + web_browser2_->Quit(); + web_browser2_.Release(); + } +} + +STDMETHODIMP WebBrowserEventSink::OnBeforeNavigate2Internal( + IDispatch* dispatch, VARIANT* url, VARIANT* flags, + VARIANT* target_frame_name, VARIANT* post_data, VARIANT* headers, + VARIANT_BOOL* cancel) { + DLOG(INFO) << __FUNCTION__; + // Reset any existing reference to chrome frame since this is a new + // navigation. + chrome_frame_ = NULL; + return OnBeforeNavigate2(dispatch, url, flags, target_frame_name, + post_data, headers, cancel); +} + +STDMETHODIMP_(void) WebBrowserEventSink::OnNavigateComplete2Internal( + IDispatch* dispatch, VARIANT* url) { + DLOG(INFO) << __FUNCTION__; + ConnectToChromeFrame(); + OnNavigateComplete2(dispatch, url); +} + +STDMETHODIMP_(void) WebBrowserEventSink::OnDocumentCompleteInternal( + IDispatch* dispatch, VARIANT* url) { + DLOG(INFO) << __FUNCTION__; + OnDocumentComplete(dispatch, url); +} + +HRESULT WebBrowserEventSink::OnLoadInternal(const VARIANT* param) { + DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal; + OnLoad(param->bstrVal); + return S_OK; +} + +HRESULT WebBrowserEventSink::OnLoadErrorInternal(const VARIANT* param) { + DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal; + OnLoadError(param->bstrVal); + return S_OK; +} + +HRESULT WebBrowserEventSink::OnMessageInternal(const VARIANT* param) { + DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal; + OnMessage(param->bstrVal); + return S_OK; +} + +HRESULT WebBrowserEventSink::LaunchIEAndNavigate( + const std::wstring& navigate_url) { + HRESULT hr = LaunchIEAsComServer(web_browser2_.Receive()); + EXPECT_EQ(S_OK, hr); + if (hr == S_OK) { + web_browser2_->put_Visible(VARIANT_TRUE); + hr = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2); + EXPECT_TRUE(hr == S_OK); + hr = Navigate(navigate_url); + } + return hr; +} + +HRESULT WebBrowserEventSink::Navigate(const std::wstring& navigate_url) { + VARIANT empty = ScopedVariant::kEmptyVariant; + ScopedVariant url; + url.Set(navigate_url.c_str()); + + HRESULT hr = S_OK; + hr = web_browser2_->Navigate2(url.AsInput(), &empty, &empty, &empty, &empty); + EXPECT_TRUE(hr == S_OK); + return hr; +} + +void WebBrowserEventSink::SetFocusToChrome() { + chrome_frame_test::SetKeyboardFocusToWindow( + GetAttachedChromeRendererWindow(), 1, 1); +} + +void WebBrowserEventSink::SendInputToChrome( + const std::string& input_string) { + chrome_frame_test::SendInputToWindow( + GetAttachedChromeRendererWindow(), input_string); +} + +void WebBrowserEventSink::ConnectToChromeFrame() { + DCHECK(web_browser2_); + ScopedComPtr<IShellBrowser> shell_browser; + DoQueryService(SID_STopLevelBrowser, web_browser2_, + shell_browser.Receive()); + + if (shell_browser) { + ScopedComPtr<IShellView> shell_view; + shell_browser->QueryActiveShellView(shell_view.Receive()); + if (shell_view) { + shell_view->GetItemObject(SVGIO_BACKGROUND, __uuidof(IChromeFrame), + reinterpret_cast<void**>(chrome_frame_.Receive())); + } + + if (chrome_frame_) { + ScopedVariant onmessage(onmessage_.ToDispatch()); + ScopedVariant onloaderror(onloaderror_.ToDispatch()); + ScopedVariant onload(onload_.ToDispatch()); + EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onmessage(onmessage)); + EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onloaderror(onloaderror)); + EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onload(onload)); + } + } +} + +void WebBrowserEventSink::DisconnectFromChromeFrame() { + if (chrome_frame_) { + ScopedVariant dummy(static_cast<IDispatch*>(NULL)); + chrome_frame_->put_onmessage(dummy); + chrome_frame_->put_onload(dummy); + chrome_frame_->put_onloaderror(dummy); + chrome_frame_.Release(); + } +} + +HWND WebBrowserEventSink::GetAttachedChromeRendererWindow() { + DCHECK(chrome_frame_); + HWND renderer_window = NULL; + ScopedComPtr<IOleWindow> ole_window; + ole_window.QueryFrom(chrome_frame_); + EXPECT_TRUE(ole_window.get()); + + if (ole_window) { + HWND activex_window = NULL; + ole_window->GetWindow(&activex_window); + EXPECT_TRUE(IsWindow(activex_window)); + + // chrome tab window is the first (and the only) child of activex + HWND chrome_tab_window = GetWindow(activex_window, GW_CHILD); + EXPECT_TRUE(IsWindow(chrome_tab_window)); + renderer_window = GetWindow(chrome_tab_window, GW_CHILD); + } + + DCHECK(IsWindow(renderer_window)); + return renderer_window; +} + +HRESULT WebBrowserEventSink::SetWebBrowser(IWebBrowser2* web_browser2) { + DCHECK(web_browser2_.get() == NULL); + web_browser2_ = web_browser2; + web_browser2_->put_Visible(VARIANT_TRUE); + HRESULT hr = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2); + return hr; +} + } // 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 2947539..277fc52 100644 --- a/chrome_frame/test/chrome_frame_test_utils.h +++ b/chrome_frame/test/chrome_frame_test_utils.h @@ -5,10 +5,25 @@ #ifndef CHROME_FRAME_TEST_CHROME_FRAME_TEST_UTILS_H_ #define CHROME_FRAME_TEST_CHROME_FRAME_TEST_UTILS_H_ +#include <atlbase.h> +#include <atlcom.h> +#include <string> +#include <exdisp.h> +#include <exdispid.h> +#include <mshtml.h> +#include <shlguid.h> +#include <shobjidl.h> #include <windows.h> #include "base/basictypes.h" +#include "base/message_loop.h" #include "base/process_util.h" +#include "base/scoped_comptr_win.h" +#include "base/scoped_variant_win.h" + +#include "chrome_frame/test_utils.h" +// Include without path to make GYP build see it. +#include "chrome_tab.h" // NOLINT namespace chrome_frame_test { @@ -90,6 +105,179 @@ class LowIntegrityToken { bool impersonated_; }; +// MessageLoopForUI wrapper that runs only for a limited time. +// We need a UI message loop in the main thread. +class TimedMsgLoop { + public: + void RunFor(int seconds) { + QuitAfter(seconds); + loop_.MessageLoop::Run(); + } + + void PostDelayedTask( + const tracked_objects::Location& from_here, Task* task, int64 delay_ms) { + loop_.PostDelayedTask(from_here, task, delay_ms); + } + + void Quit() { + loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask); + } + + void QuitAfter(int seconds) { + loop_.PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask, 1000 * seconds); + } + + MessageLoopForUI loop_; +}; + +// Launches IE as a COM server and returns the corresponding IWebBrowser2 +// interface pointer. +// Returns S_OK on success. +HRESULT LaunchIEAsComServer(IWebBrowser2** web_browser); + +#ifndef DISPID_NEWPROCESS +#define DISPID_NEWPROCESS 284 +#endif // DISPID_NEWPROCESS + +// This class sets up event sinks to the IWebBrowser interface. Currently it +// subscribes to the following events:- +// 1. DISPID_BEFORENAVIGATE2 +// 2. DISPID_NAVIGATEERROR +// 3. DISPID_NAVIGATECOMPLETE2 +// 4. DISPID_NEWWINDOW3 +// 5. DISPID_DOCUMENTCOMPLETE +// Other events can be subscribed to on an if needed basis. +class WebBrowserEventSink + : public CComObjectRootEx<CComMultiThreadModel>, + public IDispEventSimpleImpl<0, WebBrowserEventSink, + &DIID_DWebBrowserEvents2> { + public: + typedef IDispEventSimpleImpl<0, WebBrowserEventSink, + &DIID_DWebBrowserEvents2> DispEventsImpl; + WebBrowserEventSink() + : ALLOW_THIS_IN_INITIALIZER_LIST( + onmessage_(this, &WebBrowserEventSink::OnMessageInternal)), + ALLOW_THIS_IN_INITIALIZER_LIST( + onloaderror_(this, &WebBrowserEventSink::OnLoadErrorInternal)), + ALLOW_THIS_IN_INITIALIZER_LIST( + onload_(this, &WebBrowserEventSink::OnLoadInternal)) { + } + + ~WebBrowserEventSink() { + Uninitialize(); + } + + void Uninitialize(); + + // Helper function to launch IE and navigate to a URL. + // Returns S_OK on success, S_FALSE if the test was not run, other + // errors on failure. + HRESULT LaunchIEAndNavigate(const std::wstring& navigate_url); + + virtual HRESULT Navigate(const std::wstring& navigate_url); + + // Set input focus to chrome frame window. + void SetFocusToChrome(); + + // Send keyboard input to the renderer window hosted in chrome using + // SendInput API + void SendInputToChrome(const std::string& input_string); + +BEGIN_COM_MAP(WebBrowserEventSink) +END_COM_MAP() + +BEGIN_SINK_MAP(WebBrowserEventSink) + SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_BEFORENAVIGATE2, + OnBeforeNavigate2Internal, &kBeforeNavigate2Info) + SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_DOWNLOADBEGIN, + OnDownloadBegin, &kVoidMethodInfo) + SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NAVIGATECOMPLETE2, + OnNavigateComplete2Internal, &kNavigateComplete2Info) + SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NAVIGATEERROR, + OnNavigateError, &kNavigateErrorInfo) + SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NEWWINDOW3, + OnNewWindow3, &kNewWindow3Info) + SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, + OnDocumentCompleteInternal, &kDocumentCompleteInfo) +END_SINK_MAP() + + STDMETHOD_(void, OnNavigateError)(IDispatch* dispatch, VARIANT* url, + VARIANT* frame_name, VARIANT* status_code, + VARIANT* cancel) { + DLOG(INFO) << __FUNCTION__; + } + + STDMETHOD(OnBeforeNavigate2)(IDispatch* dispatch, VARIANT* url, VARIANT* + flags, VARIANT* target_frame_name, + VARIANT* post_data, VARIANT* headers, + VARIANT_BOOL* cancel) { + return S_OK; + } + + STDMETHOD(OnBeforeNavigate2Internal)(IDispatch* dispatch, VARIANT* url, + VARIANT* flags, + VARIANT* target_frame_name, + VARIANT* post_data, VARIANT* headers, + VARIANT_BOOL* cancel); + STDMETHOD_(void, OnDownloadBegin)() {} + STDMETHOD_(void, OnNavigateComplete2Internal)(IDispatch* dispatch, + VARIANT* url); + STDMETHOD_(void, OnNavigateComplete2)(IDispatch* dispatch, VARIANT* url) {} + STDMETHOD_(void, OnNewWindow3)(IDispatch** dispatch, VARIANT_BOOL* Cancel, + DWORD flags, BSTR url_context, BSTR url) {} + + STDMETHOD_(void, OnDocumentCompleteInternal)(IDispatch* dispatch, + VARIANT* url); + + STDMETHOD_(void, OnDocumentComplete)(IDispatch* dispatch, + VARIANT* url) {} +#ifdef _DEBUG + STDMETHOD(Invoke)(DISPID dispid, REFIID riid, + LCID lcid, WORD flags, DISPPARAMS* params, VARIANT* result, + EXCEPINFO* except_info, UINT* arg_error) { + DLOG(INFO) << __FUNCTION__ << L" disp id :" << dispid; + return DispEventsImpl::Invoke(dispid, riid, lcid, flags, params, result, + except_info, arg_error); + } +#endif // _DEBUG + + // Chrome frame callbacks + virtual void OnLoad(const wchar_t* url) {} + virtual void OnLoadError(const wchar_t* url) {} + virtual void OnMessage(const wchar_t* message) {} + + IWebBrowser2* web_browser2() { + return web_browser2_.get(); + } + + HRESULT SetWebBrowser(IWebBrowser2* web_browser2); + + protected: + // IChromeFrame callbacks + HRESULT OnLoadInternal(const VARIANT* param); + HRESULT OnLoadErrorInternal(const VARIANT* param); + HRESULT OnMessageInternal(const VARIANT* param); + + void ConnectToChromeFrame(); + void DisconnectFromChromeFrame(); + HWND GetAttachedChromeRendererWindow(); + + public: + ScopedComPtr<IWebBrowser2> web_browser2_; + ScopedComPtr<IChromeFrame> chrome_frame_; + DispCallback<WebBrowserEventSink> onmessage_; + DispCallback<WebBrowserEventSink> onloaderror_; + DispCallback<WebBrowserEventSink> onload_; + + protected: + static _ATL_FUNC_INFO kBeforeNavigate2Info; + static _ATL_FUNC_INFO kNavigateComplete2Info; + static _ATL_FUNC_INFO kNavigateErrorInfo; + static _ATL_FUNC_INFO kNewWindow3Info; + static _ATL_FUNC_INFO kVoidMethodInfo; + static _ATL_FUNC_INFO kDocumentCompleteInfo; +}; + } // namespace chrome_frame_test #endif // CHROME_FRAME_TEST_CHROME_FRAME_TEST_UTILS_H_ diff --git a/chrome_frame/test/chrome_frame_unittests.cc b/chrome_frame/test/chrome_frame_unittests.cc index 81edf87..ea7de65 100644 --- a/chrome_frame/test/chrome_frame_unittests.cc +++ b/chrome_frame/test/chrome_frame_unittests.cc @@ -37,48 +37,6 @@ const wchar_t kDocRoot[] = L"chrome_frame\\test\\data"; const int kLongWaitTimeout = 60 * 1000; const int kShortWaitTimeout = 25 * 1000; -_ATL_FUNC_INFO WebBrowserEventSink::kNavigateErrorInfo = { - CC_STDCALL, VT_EMPTY, 5, { - VT_DISPATCH, - VT_VARIANT | VT_BYREF, - VT_VARIANT | VT_BYREF, - VT_VARIANT | VT_BYREF, - VT_BOOL | VT_BYREF, - } -}; - -_ATL_FUNC_INFO WebBrowserEventSink::kNavigateComplete2Info = { - CC_STDCALL, VT_EMPTY, 2, { - VT_DISPATCH, - VT_VARIANT | VT_BYREF - } -}; - -_ATL_FUNC_INFO WebBrowserEventSink::kBeforeNavigate2Info = { - CC_STDCALL, VT_EMPTY, 7, { - VT_DISPATCH, - VT_VARIANT | VT_BYREF, - VT_VARIANT | VT_BYREF, - VT_VARIANT | VT_BYREF, - VT_VARIANT | VT_BYREF, - VT_VARIANT | VT_BYREF, - VT_BOOL | VT_BYREF - } -}; - -_ATL_FUNC_INFO WebBrowserEventSink::kNewWindow3Info = { - CC_STDCALL, VT_EMPTY, 5, { - VT_DISPATCH | VT_BYREF, - VT_BOOL | VT_BYREF, - VT_UINT, - VT_BSTR, - VT_BSTR - } -}; - -_ATL_FUNC_INFO WebBrowserEventSink::kVoidMethodInfo = { - CC_STDCALL, VT_EMPTY, 0, {NULL}}; - void ChromeFrameTestWithWebServer::CloseAllBrowsers() { // Web browsers tend to relaunch themselves in other processes, meaning the // KillProcess stuff above might not have actually cleaned up all our browser @@ -582,7 +540,6 @@ TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_CFInstanceDefaultCtor) { SimpleBrowserTest(FIREFOX, kCFIDefaultCtorPage, L"CFInstanceDefaultCtor"); } - const wchar_t kCFInstallBasicTestPage[] = L"files/CFInstall_basic.html"; TEST_F(ChromeFrameTestWithWebServer, FullTabIE_CFInstallBasic) { @@ -850,49 +807,25 @@ template <> struct RunnableMethodTraits<ChromeFrameAutomationClient> { void ReleaseCallee(ChromeFrameAutomationClient* obj) {} }; -// MessageLoopForUI wrapper that runs only for a limited time. -// We need a UI message loop in the main thread. -struct TimedMsgLoop { - public: - void RunFor(int seconds) { - QuitAfter(seconds); - loop_.MessageLoop::Run(); - } - - void PostDelayedTask( - const tracked_objects::Location& from_here, Task* task, int64 delay_ms) { - loop_.PostDelayedTask(from_here, task, delay_ms); - } - - void Quit() { - loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask); - } - - void QuitAfter(int seconds) { - loop_.PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask, 1000 * seconds); - } - - MessageLoopForUI loop_; -}; - -template <> struct RunnableMethodTraits<TimedMsgLoop> { - void RetainCallee(TimedMsgLoop* obj) {} - void ReleaseCallee(TimedMsgLoop* obj) {} +template <> struct RunnableMethodTraits<chrome_frame_test::TimedMsgLoop> { + void RetainCallee(chrome_frame_test::TimedMsgLoop* obj) {} + void ReleaseCallee(chrome_frame_test::TimedMsgLoop* obj) {} }; // Saves typing. It's somewhat hard to create a wrapper around // testing::InvokeWithoutArgs since it returns a // non-public (testing::internal) type. #define QUIT_LOOP(loop) testing::InvokeWithoutArgs(\ - CreateFunctor(&loop, &TimedMsgLoop::Quit)) + CreateFunctor(&loop, &chrome_frame_test::TimedMsgLoop::Quit)) #define QUIT_LOOP_SOON(loop, seconds) testing::InvokeWithoutArgs(\ - CreateFunctor(&loop, &TimedMsgLoop::QuitAfter, seconds)) + CreateFunctor(&loop, &chrome_frame_test::TimedMsgLoop::QuitAfter, \ + seconds)) // We mock ChromeFrameDelegate only. The rest is with real AutomationProxy TEST(CFACWithChrome, CreateTooFast) { MockCFDelegate cfd; - TimedMsgLoop loop; + chrome_frame_test::TimedMsgLoop loop; int timeout = 0; // Chrome cannot send Hello message so fast. const std::wstring profile = L"Adam.N.Epilinter"; @@ -915,7 +848,7 @@ TEST(CFACWithChrome, CreateTooFast) { // that this is an unexpected call, and still to execute and action. TEST(CFACWithChrome, CreateNotSoFast) { MockCFDelegate cfd; - TimedMsgLoop loop; + chrome_frame_test::TimedMsgLoop loop; const std::wstring profile = L"Adam.N.Epilinter"; int timeout = 10000; @@ -950,7 +883,7 @@ MATCHER_P(EqNavigationInfoUrl, url, "IPC::NavigationInfo matcher") { TEST(CFACWithChrome, NavigateOk) { MockCFDelegate cfd; - TimedMsgLoop loop; + chrome_frame_test::TimedMsgLoop loop; const std::wstring profile = L"Adam.N.Epilinter"; const std::string url = "about:version"; int timeout = 10000; @@ -990,7 +923,7 @@ TEST(CFACWithChrome, NavigateOk) { // Bug: http://b/issue?id=2033644 TEST(CFACWithChrome, DISABLED_NavigateFailed) { MockCFDelegate cfd; - TimedMsgLoop loop; + chrome_frame_test::TimedMsgLoop loop; const std::wstring profile = L"Adam.N.Epilinter"; const std::string url = "http://127.0.0.3:65412/"; int timeout = 10000; @@ -1041,7 +974,7 @@ class CFACMockTest : public testing::Test { public: MockProxyFactory factory_; MockCFDelegate cfd_; - TimedMsgLoop loop_; + chrome_frame_test::TimedMsgLoop loop_; MockAutomationProxy proxy_; scoped_ptr<AutomationHandleTracker> tracker_; MockAutomationMessageSender dummy_sender_; @@ -1205,170 +1138,12 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_SubIFrame) { SimpleBrowserTest(IE, kSubIFrameTestPage, L"sub_frame"); } -HRESULT LaunchIEAsComServer(IWebBrowser2** web_browser) { - if (!web_browser) - return E_INVALIDARG; - - HRESULT hr = S_OK; - DWORD cocreate_flags = CLSCTX_LOCAL_SERVER; - chrome_frame_test::LowIntegrityToken token; - if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) { - // Create medium integrity browser that will launch IE broker. - ScopedComPtr<IWebBrowser2> medium_integrity_browser; - hr = medium_integrity_browser.CreateInstance(CLSID_InternetExplorer, NULL, - CLSCTX_LOCAL_SERVER); - if (FAILED(hr)) - return hr; - medium_integrity_browser->Quit(); - // Broker remains alive. - if (!token.Impersonate()) { - hr = HRESULT_FROM_WIN32(GetLastError()); - return hr; - } - - cocreate_flags |= CLSCTX_ENABLE_CLOAKING; - } - - hr = ::CoCreateInstance(CLSID_InternetExplorer, NULL, - cocreate_flags, IID_IWebBrowser2, - reinterpret_cast<void**>(web_browser)); - // ~LowIntegrityToken() will switch integrity back to medium. - return hr; -} - -// WebBrowserEventSink member defines -void WebBrowserEventSink::Uninitialize() { - chrome_frame_ = NULL; - if (web_browser2_.get()) { - DispEventUnadvise(web_browser2_); - web_browser2_->Quit(); - web_browser2_.Release(); - } -} - -STDMETHODIMP WebBrowserEventSink::OnBeforeNavigate2Internal( - IDispatch* dispatch, VARIANT* url, VARIANT* flags, - VARIANT* target_frame_name, VARIANT* post_data, VARIANT* headers, - VARIANT_BOOL* cancel) { - DLOG(INFO) << __FUNCTION__; - // Reset any existing reference to chrome frame since this is a new - // navigation. - chrome_frame_ = NULL; - return OnBeforeNavigate2(dispatch, url, flags, target_frame_name, - post_data, headers, cancel); -} - -STDMETHODIMP_(void) WebBrowserEventSink::OnNavigateComplete2Internal( - IDispatch* dispatch, VARIANT* url) { - DLOG(INFO) << __FUNCTION__; - ConnectToChromeFrame(); - OnNavigateComplete2(dispatch, url); -} - -HRESULT WebBrowserEventSink::OnLoadInternal(const VARIANT* param) { - DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal; - OnLoad(param->bstrVal); - return S_OK; -} - -HRESULT WebBrowserEventSink::OnLoadErrorInternal(const VARIANT* param) { - DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal; - OnLoadError(param->bstrVal); - return S_OK; -} - -HRESULT WebBrowserEventSink::OnMessageInternal(const VARIANT* param) { - DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal; - OnMessage(param->bstrVal); - return S_OK; -} - -HRESULT WebBrowserEventSink::LaunchIEAndNavigate( - const std::wstring& navigate_url) { - HRESULT hr = LaunchIEAsComServer(web_browser2_.Receive()); - EXPECT_EQ(S_OK, hr); - if (hr == S_OK) { - web_browser2_->put_Visible(VARIANT_TRUE); - hr = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2); - EXPECT_TRUE(hr == S_OK); - hr = Navigate(navigate_url); - } - return hr; -} - -HRESULT WebBrowserEventSink::Navigate(const std::wstring& navigate_url) { - VARIANT empty = ScopedVariant::kEmptyVariant; - ScopedVariant url; - url.Set(navigate_url.c_str()); - - HRESULT hr = S_OK; - hr = web_browser2_->Navigate2(url.AsInput(), &empty, &empty, &empty, &empty); - EXPECT_TRUE(hr == S_OK); - return hr; -} - -void WebBrowserEventSink::SetFocusToChrome() { - chrome_frame_test::SetKeyboardFocusToWindow(GetChromeRendererWindow(), 1, 1); -} - -void WebBrowserEventSink::SendInputToChrome( - const std::string& input_string) { - chrome_frame_test::SendInputToWindow(GetChromeRendererWindow(), input_string); -} - -void WebBrowserEventSink::ConnectToChromeFrame() { - DCHECK(web_browser2_); - ScopedComPtr<IShellBrowser> shell_browser; - DoQueryService(SID_STopLevelBrowser, web_browser2_, - shell_browser.Receive()); - - if (shell_browser) { - ScopedComPtr<IShellView> shell_view; - shell_browser->QueryActiveShellView(shell_view.Receive()); - if (shell_view) { - shell_view->GetItemObject(SVGIO_BACKGROUND, __uuidof(IChromeFrame), - reinterpret_cast<void**>(chrome_frame_.Receive())); - } - - if (chrome_frame_) { - ScopedVariant onmessage(onmessage_.ToDispatch()); - ScopedVariant onloaderror(onloaderror_.ToDispatch()); - ScopedVariant onload(onload_.ToDispatch()); - EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onmessage(onmessage)); - EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onloaderror(onloaderror)); - EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onload(onload)); - } - } -} - -HWND WebBrowserEventSink::GetChromeRendererWindow() { - DCHECK(chrome_frame_); - HWND renderer_window = NULL; - ScopedComPtr<IOleWindow> ole_window; - ole_window.QueryFrom(chrome_frame_); - EXPECT_TRUE(ole_window.get()); - - if (ole_window) { - HWND activex_window = NULL; - ole_window->GetWindow(&activex_window); - EXPECT_TRUE(IsWindow(activex_window)); - - // chrome tab window is the first (and the only) child of activex - HWND chrome_tab_window = GetWindow(activex_window, GW_CHILD); - EXPECT_TRUE(IsWindow(chrome_tab_window)); - renderer_window = GetWindow(chrome_tab_window, GW_CHILD); - } - - DCHECK(IsWindow(renderer_window)); - return renderer_window; -} - const int kChromeFrameLaunchDelay = 5; const int kChromeFrameLongNavigationTimeoutInSeconds = 10; // This class provides functionality to add expectations to IE full tab mode // tests. -class MockWebBrowserEventSink : public WebBrowserEventSink { +class MockWebBrowserEventSink : public chrome_frame_test::WebBrowserEventSink { public: // Needed to support PostTask. static bool ImplementsThreadSafeReferenceCounting() { @@ -1412,7 +1187,7 @@ using testing::_; const wchar_t kChromeFrameFileUrl[] = L"cf:file:///C:/"; TEST(ChromeFrameTest, FullTabModeIE_DisallowedUrls) { - TimedMsgLoop loop; + chrome_frame_test::TimedMsgLoop loop; // If a navigation fails then IE issues a navigation to an interstitial // page. Catch this to track navigation errors as the NavigateError // notification does not seem to fire reliably. @@ -1457,7 +1232,7 @@ const wchar_t kChromeFrameFullTabWindowOpenPopupUrl[] = // instance make it back to IE and then transitions back to Chrome as the // window.open target page is supposed to render within Chrome. TEST_F(ChromeFrameTestWithWebServer, DISABLED_FullTabModeIE_WindowOpen) { - TimedMsgLoop loop; + chrome_frame_test::TimedMsgLoop loop; CComObjectStackEx<MockWebBrowserEventSink> mock; ::testing::InSequence sequence; @@ -1517,7 +1292,7 @@ const wchar_t kChromeFrameAboutVersion[] = // 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_AboutChromeFrame) { - TimedMsgLoop loop; + chrome_frame_test::TimedMsgLoop loop; CComObjectStackEx<MockWebBrowserEventSink> mock; EXPECT_CALL(mock, @@ -1554,7 +1329,7 @@ TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_AboutChromeFrame) { const wchar_t kChromeFrameFullTabModeKeyEventUrl[] = L"files/keyevent.html"; TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_ChromeFrameKeyboardTest) { - TimedMsgLoop loop; + chrome_frame_test::TimedMsgLoop loop; ASSERT_TRUE(LaunchBrowser(IE, kChromeFrameFullTabModeKeyEventUrl)); @@ -1590,7 +1365,7 @@ template <typename T> T** ReceivePointer(scoped_refptr<T>& p) { // NOLINT // Full tab mode back/forward test // Launch and navigate chrome frame to a set of URLs and test back forward TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_BackForward) { - TimedMsgLoop loop; + chrome_frame_test::TimedMsgLoop loop; CComObjectStackEx<MockWebBrowserEventSink> mock; ::testing::InSequence sequence; // Everything in sequence @@ -1604,8 +1379,9 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_BackForward) { .WillOnce(testing::Return()); EXPECT_CALL(mock, OnLoad(testing::StrEq(kSubFrameUrl1))) .WillOnce(testing::IgnoreResult(testing::InvokeWithoutArgs( - CreateFunctor(&mock, &WebBrowserEventSink::Navigate, - std::wstring(kSubFrameUrl2))))); + CreateFunctor( + &mock, &chrome_frame_test::WebBrowserEventSink::Navigate, + std::wstring(kSubFrameUrl2))))); // Navigate to url 3 after the previous navigation is complete EXPECT_CALL(mock, @@ -1617,8 +1393,9 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_BackForward) { .WillOnce(testing::Return()); EXPECT_CALL(mock, OnLoad(testing::StrEq(kSubFrameUrl2))) .WillOnce(testing::IgnoreResult(testing::InvokeWithoutArgs( - CreateFunctor(&mock, &WebBrowserEventSink::Navigate, - std::wstring(kSubFrameUrl3))))); + CreateFunctor( + &mock, &chrome_frame_test::WebBrowserEventSink::Navigate, + std::wstring(kSubFrameUrl3))))); // We have reached url 3 and have two back entries for url 1 & 2 // Go back to url 2 now @@ -1659,7 +1436,7 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_BackForward) { EXPECT_CALL(mock, OnLoad(testing::StrEq(kSubFrameUrl1))) .WillOnce(testing::DoAll( testing::InvokeWithoutArgs(CreateFunctor(&mock, - &WebBrowserEventSink::Uninitialize)), + &chrome_frame_test::WebBrowserEventSink::Uninitialize)), testing::IgnoreResult(testing::InvokeWithoutArgs( &chrome_frame_test::CloseAllIEWindows)), QUIT_LOOP_SOON(loop, 2))); @@ -1671,13 +1448,15 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_BackForward) { ASSERT_TRUE(mock.web_browser2() != NULL); loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); + mock.Uninitialize(); + chrome_frame_test::CloseAllIEWindows(); } const wchar_t kChromeFrameAboutBlankUrl[] = L"cf:about:blank"; TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_ChromeFrameFocusTest) { - TimedMsgLoop loop; + chrome_frame_test::TimedMsgLoop loop; ASSERT_TRUE(LaunchBrowser(IE, kChromeFrameAboutBlankUrl)); @@ -1710,7 +1489,7 @@ const wchar_t kAnchor3Url[] = L"http://localhost:1337/files/anchor.html#a3"; TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_BackForwardAnchor) { const char tab_enter_keystrokes[] = { VK_TAB, VK_RETURN, 0 }; static const std::string tab_enter(tab_enter_keystrokes); - TimedMsgLoop loop; + chrome_frame_test::TimedMsgLoop loop; CComObjectStackEx<MockWebBrowserEventSink> mock; ::testing::InSequence sequence; // Everything in sequence @@ -1736,10 +1515,12 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_BackForwardAnchor) { EXPECT_CALL(mock, OnLoad(testing::StrEq(kAnchorUrl))) .WillOnce(testing::DoAll( testing::InvokeWithoutArgs(CreateFunctor(&mock, - &WebBrowserEventSink::SetFocusToChrome)), + &chrome_frame_test::WebBrowserEventSink::SetFocusToChrome)), testing::InvokeWithoutArgs(CreateFunctor(&loop, - &TimedMsgLoop::PostDelayedTask, FROM_HERE, NewRunnableMethod( - &mock, &WebBrowserEventSink::SendInputToChrome, + &chrome_frame_test::TimedMsgLoop::PostDelayedTask, FROM_HERE, + NewRunnableMethod( + &mock, + &chrome_frame_test::WebBrowserEventSink::SendInputToChrome, std::string(tab_enter)), 0)))); EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, testing::StrCaseEq(kAnchor1Url)), @@ -1754,8 +1535,12 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_BackForwardAnchor) { // Forward: 0 EXPECT_CALL(mock, OnLoad(testing::StrEq(kAnchor1Url))) .WillOnce(testing::InvokeWithoutArgs( - CreateFunctor(&loop, &TimedMsgLoop::PostDelayedTask, FROM_HERE, - NewRunnableMethod(&mock, &WebBrowserEventSink::SendInputToChrome, + CreateFunctor( + &loop, &chrome_frame_test::TimedMsgLoop::PostDelayedTask, + FROM_HERE, + NewRunnableMethod( + &mock, + &chrome_frame_test::WebBrowserEventSink::SendInputToChrome, std::string(tab_enter)), 0))); EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, testing::StrCaseEq(kAnchor2Url)), @@ -1770,8 +1555,12 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_BackForwardAnchor) { // Forward: 0 EXPECT_CALL(mock, OnLoad(testing::StrEq(kAnchor2Url))) .WillOnce(testing::InvokeWithoutArgs( - CreateFunctor(&loop, &TimedMsgLoop::PostDelayedTask, FROM_HERE, - NewRunnableMethod(&mock, &WebBrowserEventSink::SendInputToChrome, + CreateFunctor( + &loop, &chrome_frame_test::TimedMsgLoop::PostDelayedTask, + FROM_HERE, + NewRunnableMethod( + &mock, + &chrome_frame_test::WebBrowserEventSink::SendInputToChrome, std::string(tab_enter)), 0))); EXPECT_CALL(mock, OnBeforeNavigate2(_, testing::Field(&VARIANT::bstrVal, testing::StrCaseEq(kAnchor3Url)), @@ -1847,7 +1636,7 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_BackForwardAnchor) { EXPECT_CALL(mock, OnLoad(testing::StrEq(kAnchor3Url))) .WillOnce(testing::DoAll( testing::InvokeWithoutArgs(CreateFunctor(&mock, - &WebBrowserEventSink::Uninitialize)), + &chrome_frame_test::WebBrowserEventSink::Uninitialize)), testing::IgnoreResult(testing::InvokeWithoutArgs( &chrome_frame_test::CloseAllIEWindows)), QUIT_LOOP_SOON(loop, 2))); @@ -1859,4 +1648,6 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_BackForwardAnchor) { ASSERT_TRUE(mock.web_browser2() != NULL); loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); + mock.Uninitialize(); + chrome_frame_test::CloseAllIEWindows(); } diff --git a/chrome_frame/test/chrome_frame_unittests.h b/chrome_frame/test/chrome_frame_unittests.h index b009faf..f5bd3cb 100644 --- a/chrome_frame/test/chrome_frame_unittests.h +++ b/chrome_frame/test/chrome_frame_unittests.h @@ -104,132 +104,5 @@ class ChromeFrameTestWithWebServer: public testing::Test { ChromeFrameHTTPServer server_; }; -// This class sets up event sinks to the IWebBrowser interface. Currently it -// subscribes to the following events:- -// 1. DISPID_BEFORENAVIGATE2 -// 2. DISPID_NAVIGATEERROR -// 3. DISPID_NAVIGATECOMPLETE2 -// 4. DISPID_NEWWINDOW3 -// Other events can be subscribed to on an if needed basis. -class WebBrowserEventSink - : public CComObjectRootEx<CComSingleThreadModel>, - public IDispEventSimpleImpl<0, WebBrowserEventSink, - &DIID_DWebBrowserEvents2> { - public: - typedef IDispEventSimpleImpl<0, WebBrowserEventSink, - &DIID_DWebBrowserEvents2> DispEventsImpl; - WebBrowserEventSink() - : ALLOW_THIS_IN_INITIALIZER_LIST( - onmessage_(this, &WebBrowserEventSink::OnMessageInternal)), - ALLOW_THIS_IN_INITIALIZER_LIST( - onloaderror_(this, &WebBrowserEventSink::OnLoadErrorInternal)), - ALLOW_THIS_IN_INITIALIZER_LIST( - onload_(this, &WebBrowserEventSink::OnLoadInternal)) { - } - - ~WebBrowserEventSink() { - Uninitialize(); - } - - void Uninitialize(); - - // Helper function to launch IE and navigate to a URL. - // Returns S_OK on success, S_FALSE if the test was not run, other - // errors on failure. - HRESULT LaunchIEAndNavigate(const std::wstring& navigate_url); - - HRESULT Navigate(const std::wstring& navigate_url); - - // Set input focus to chrome frame window. - void SetFocusToChrome(); - - // Send keyboard input to the renderer window hosted in chrome using - // SendInput API - void SendInputToChrome(const std::string& input_string); - -BEGIN_COM_MAP(WebBrowserEventSink) -END_COM_MAP() - -BEGIN_SINK_MAP(WebBrowserEventSink) - SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_BEFORENAVIGATE2, - OnBeforeNavigate2Internal, &kBeforeNavigate2Info) - SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_DOWNLOADBEGIN, - OnDownloadBegin, &kVoidMethodInfo) - SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NAVIGATECOMPLETE2, - OnNavigateComplete2Internal, &kNavigateComplete2Info) - SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NAVIGATEERROR, - OnNavigateError, &kNavigateErrorInfo) - SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NEWWINDOW3, - OnNewWindow3, &kNewWindow3Info) -END_SINK_MAP() - - STDMETHOD_(void, OnNavigateError)(IDispatch* dispatch, VARIANT* url, - VARIANT* frame_name, VARIANT* status_code, - VARIANT* cancel) { - DLOG(INFO) << __FUNCTION__; - } - - STDMETHOD(OnBeforeNavigate2)(IDispatch* dispatch, VARIANT* url, VARIANT* - flags, VARIANT* target_frame_name, - VARIANT* post_data, VARIANT* headers, - VARIANT_BOOL* cancel) { - return S_OK; - } - - STDMETHOD(OnBeforeNavigate2Internal)(IDispatch* dispatch, VARIANT* url, - VARIANT* flags, - VARIANT* target_frame_name, - VARIANT* post_data, VARIANT* headers, - VARIANT_BOOL* cancel); - STDMETHOD_(void, OnDownloadBegin)() {} - STDMETHOD_(void, OnNavigateComplete2Internal)(IDispatch* dispatch, - VARIANT* url); - STDMETHOD_(void, OnNavigateComplete2)(IDispatch* dispatch, VARIANT* url) {} - STDMETHOD_(void, OnNewWindow3)(IDispatch** dispatch, VARIANT_BOOL* Cancel, - DWORD flags, BSTR url_context, BSTR url) {} - -#ifdef _DEBUG - STDMETHOD(Invoke)(DISPID dispid, REFIID riid, - LCID lcid, WORD flags, DISPPARAMS* params, VARIANT* result, - EXCEPINFO* except_info, UINT* arg_error) { - DLOG(INFO) << __FUNCTION__ << L" disp id :" << dispid; - return DispEventsImpl::Invoke(dispid, riid, lcid, flags, params, result, - except_info, arg_error); - } -#endif // _DEBUG - - // Chrome frame callbacks - virtual void OnLoad(const wchar_t* url) {} - virtual void OnLoadError(const wchar_t* url) {} - virtual void OnMessage(const wchar_t* message) {} - - IWebBrowser2* web_browser2() { - return web_browser2_.get(); - } - - protected: - // IChromeFrame callbacks - HRESULT OnLoadInternal(const VARIANT* param); - HRESULT OnLoadErrorInternal(const VARIANT* param); - HRESULT OnMessageInternal(const VARIANT* param); - - void ConnectToChromeFrame(); - HWND GetChromeRendererWindow(); - - public: - ScopedComPtr<IWebBrowser2> web_browser2_; - ScopedComPtr<IChromeFrame> chrome_frame_; - DispCallback<WebBrowserEventSink> onmessage_; - DispCallback<WebBrowserEventSink> onloaderror_; - DispCallback<WebBrowserEventSink> onload_; - - protected: - static _ATL_FUNC_INFO kBeforeNavigate2Info; - static _ATL_FUNC_INFO kNavigateComplete2Info; - static _ATL_FUNC_INFO kNavigateErrorInfo; - static _ATL_FUNC_INFO kNewWindow3Info; - static _ATL_FUNC_INFO kVoidMethodInfo; -}; - #endif // CHROME_FRAME_TEST_CHROME_FRAME_UNITTESTS_H_ diff --git a/chrome_frame/test/reliability/page_load_test.cc b/chrome_frame/test/reliability/page_load_test.cc new file mode 100644 index 0000000..dd869e5 --- /dev/null +++ b/chrome_frame/test/reliability/page_load_test.cc @@ -0,0 +1,624 @@ +// Copyright (c) 2006-2009 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. +// +// This file provides reliablity tests which run for ChromeFrame. +// +// Usage: +// <reliability test exe> --list=file --startline=start --endline=end [...] +// Upon invocation, it visits each of the URLs on line numbers between start +// and end, inclusive, stored in the input file. The line number starts from 1. +// +// Optional Switches: +// --iterations=num: goes through the list of URLs constructed in usage 2 or 3 +// num times. +// --continuousload: continuously visits the list of URLs without restarting +// browser for each page load. +// --memoryusage: prints out memory usage when visiting each page. +// --logfile=filepath: saves the visit log to the specified path. +// --timeout=millisecond: time out as specified in millisecond during each +// page load. +// --nopagedown: won't simulate page down key presses after page load. +// --noclearprofile: do not clear profile dir before firing up each time. +// --savedebuglog: save Chrome, V8, and test debug log for each page loaded. +#include <fstream> +#include <iostream> + +#include "base/command_line.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/file_version_info.h" +#include "base/keyboard_codes.h" +#include "base/i18n/time_formatting.h" +#include "base/path_service.h" +#include "base/registry.h" +#include "base/string_util.h" +#include "base/test/test_file_util.h" +#include "base/time.h" +#include "chrome/browser/net/url_fixer_upper.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_paths_internal.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/logging_chrome.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/pref_service.h" +#include "chrome_frame/test/chrome_frame_test_utils.h" +#include "chrome/test/automation/automation_messages.h" +#include "chrome/test/automation/automation_proxy.h" +#include "chrome/test/automation/browser_proxy.h" +#include "chrome/test/automation/tab_proxy.h" +#include "chrome/test/automation/window_proxy.h" +#include "chrome/test/ui/ui_test.h" +#include "chrome/test/reliability/page_load_test.h" +#include "chrome_frame/utils.h" +#include "net/base/net_util.h" + + +namespace { + +// See comments at the beginning of the file for the definition of switches. +const char kListSwitch[] = "list"; +const char kStartIndexSwitch[] = "startline"; +const char kEndIndexSwitch[] = "endline"; +const char kIterationSwitch[] = "iterations"; +const char kContinuousLoadSwitch[] = "continuousload"; +const char kMemoryUsageSwitch[] = "memoryusage"; +const char kLogFileSwitch[] = "logfile"; +const char kTimeoutSwitch[] = "timeout"; +const char kNoPageDownSwitch[] = "nopagedown"; +const char kNoClearProfileSwitch[] = "noclearprofile"; +const char kSaveDebugLogSwitch[] = "savedebuglog"; + +// These are copied from v8 definitions as we cannot include them. +const char kV8LogFileSwitch[] = "logfile"; +const char kV8LogFileDefaultName[] = "v8.log"; + +// String name of local chrome dll for looking up file information. +const wchar_t kChromeDll[] = L"chrome.dll"; + +FilePath g_url_file_path; +int32 g_start_index = 1; +int32 g_end_index = kint32max; +int32 g_iterations = 1; +bool g_memory_usage = false; +bool g_continuous_load = false; +bool g_browser_existing = false; +bool g_page_down = true; +bool g_clear_profile = true; +std::string g_end_url; +FilePath g_log_file_path; +bool g_save_debug_log = false; +FilePath g_chrome_log_path; +FilePath g_v8_log_path; +FilePath g_test_log_path; +bool g_stand_alone = false; + +const int kUrlNavigationTimeoutSeconds = 20; +int g_timeout_ms = kUrlNavigationTimeoutSeconds; + +// Overrides a number of IWebBrowser2 event sink handlers as provided by the +// base chrome_frame_test::WebBrowserEventSink class and provides functionality +// for reliability testing. +class WebBrowserEventSinkImpl : public chrome_frame_test::WebBrowserEventSink { + public: + typedef chrome_frame_test::WebBrowserEventSink Base; + + WebBrowserEventSinkImpl() + : navigation_started_(false), + navigation_completed_(false), + message_loop_(NULL), + is_chrome_frame_navigation_(false) {} + + ~WebBrowserEventSinkImpl() {} + + STDMETHOD_(void, OnDocumentComplete)(IDispatch* dispatch, VARIANT* url) { + if (url->bstrVal && !_wcsicmp(url->bstrVal, url_.c_str())) { + navigation_started_ = false; + // If this is a chrome frame navigation then the OnDocumentComplete event + // does not indicate that we actually finished navigation. For that we + // have to wait for the OnLoad notification from ChromeFrame to arrive. + if (!is_chrome_frame_navigation_) { + navigation_completed_ = true; + DCHECK(message_loop_); + message_loop_->Quit(); + } + } + } + + virtual void OnLoad(const wchar_t* url) { + navigation_completed_ = true; + DCHECK(message_loop_); + message_loop_->Quit(); + } + + virtual HRESULT Navigate(const std::wstring& navigate_url) { + if (StartsWith(navigate_url, L"cf:", true)) { + is_chrome_frame_navigation_ = true; + url_ = navigate_url.substr(wcslen(L"cf:")); + } else { + url_ = navigate_url; + } + navigation_started_ = true; + navigation_completed_ = false; + + return Base::Navigate(navigate_url); + } + + bool navigation_started() const { + return navigation_started_; + } + + bool navigation_completed() const { + return navigation_completed_; + } + + const std::wstring& url() const { + return url_; + } + + bool is_chrome_frame_navigation() const { + return is_chrome_frame_navigation_; + } + + void set_message_loop(chrome_frame_test::TimedMsgLoop* message_loop) { + message_loop_ = message_loop; + } + + private: + bool navigation_started_; + bool navigation_completed_; + std::wstring url_; + chrome_frame_test::TimedMsgLoop* message_loop_; + bool is_chrome_frame_navigation_; +}; + +class PageLoadTest : public testing::Test { + public: + enum NavigationResult { + NAVIGATION_ERROR = 0, + NAVIGATION_SUCCESS, + }; + + typedef struct { + // These are results from the test automation that drives Chrome + NavigationResult result; + int crash_dump_count; + // These are stability metrics recorded by Chrome itself + bool browser_clean_exit; + int browser_launch_count; + int page_load_count; + int browser_crash_count; + int renderer_crash_count; + int plugin_crash_count; + } NavigationMetrics; + + PageLoadTest() {} + + // Accept URL as std::string here because the url may also act as a test id + // and needs to be logged in its original format even if invalid. + void NavigateToURLLogResult(const std::string& url_string, + std::ofstream& log_file, + NavigationMetrics* metrics_output) { + GURL url(url_string); + NavigationMetrics metrics = {NAVIGATION_ERROR}; + std::ofstream test_log; + + // Create a test log. + g_test_log_path = FilePath(FILE_PATH_LITERAL("test_log.log")); + test_log.open(g_test_log_path.value().c_str()); + + // Check file version info for chrome dll. + scoped_ptr<FileVersionInfo> file_info; +#if defined(OS_WIN) + file_info.reset(FileVersionInfo::CreateFileVersionInfo(kChromeDll)); +#elif defined(OS_LINUX) || defined(OS_MACOSX) + // TODO(fmeawad): the version retrieved here belongs to the test module and + // not the chrome binary, need to be changed to chrome binary instead. + file_info.reset(FileVersionInfo::CreateFileVersionInfoForCurrentModule()); +#endif // !defined(OS_WIN) + std::wstring last_change = file_info->last_change(); + test_log << "Last Change: "; + test_log << last_change << std::endl; + + + // Log timestamp for test start. + base::Time time_now = base::Time::Now(); + double time_start = time_now.ToDoubleT(); + test_log << "Test Start: "; + test_log << base::TimeFormatFriendlyDateAndTime(time_now) << std::endl; + + HRESULT hr = E_FAIL; + + chrome_frame_test::TimedMsgLoop message_loop; + + CComObjectStack<WebBrowserEventSinkImpl> browser_event_sink; + browser_event_sink.set_message_loop(&message_loop); + + if (!g_continuous_load && !g_browser_existing) { + ScopedComPtr<IWebBrowser2> web_browser2; + hr = chrome_frame_test::LaunchIEAsComServer(web_browser2.Receive()); + EXPECT_HRESULT_SUCCEEDED(hr); + EXPECT_TRUE(web_browser2.get() != NULL); + + EXPECT_HRESULT_SUCCEEDED(browser_event_sink.SetWebBrowser(web_browser2)); + g_browser_existing = true; + } + + // Log Browser Launched time. + time_now = base::Time::Now(); + test_log << "browser_launched_seconds="; + test_log << (time_now.ToDoubleT() - time_start) << std::endl; + + // This is essentially what NavigateToURL does except we don't fire + // assertion when page loading fails. We log the result instead. + hr = browser_event_sink.Navigate(UTF8ToWide(url.spec())); + if (SUCCEEDED(hr)) { + message_loop.RunFor(g_timeout_ms); + if (browser_event_sink.navigation_completed()) + metrics.result = NAVIGATION_SUCCESS; + } + + // Log navigate complete time. + time_now = base::Time::Now(); + test_log << "navigate_complete_seconds="; + test_log << (time_now.ToDoubleT() - time_start) << std::endl; + + if (!g_continuous_load) { + browser_event_sink.Uninitialize(); + chrome_frame_test::CloseAllIEWindows(); + g_browser_existing = false; + } + + // Log end of test time. + time_now = base::Time::Now(); + test_log << "total_duration_seconds="; + test_log << (time_now.ToDoubleT() - time_start) << std::endl; + + // Get navigation result and metrics, and optionally write to the log file + // provided. The log format is: + // <url> <navigation_result> <browser_crash_count> <renderer_crash_count> + // <plugin_crash_count> <crash_dump_count> [chrome_log=<path> + // v8_log=<path>] crash_dump=<path> + if (log_file.is_open()) { + log_file << url_string; + switch (metrics.result) { + case NAVIGATION_ERROR: + log_file << " error"; + break; + case NAVIGATION_SUCCESS: + log_file << " success"; + break; + default: + break; + } + } + + // Get stability metrics recorded by Chrome itself. + if (browser_event_sink.is_chrome_frame_navigation()) { + GetStabilityMetrics(&metrics); + } + + if (log_file.is_open()) { + log_file << " " << metrics.browser_crash_count \ + // The renderer crash count is flaky due to 1183283. + // Ignore the count since we also catch crash by + // crash_dump_count. + << " " << 0 \ + << " " << metrics.plugin_crash_count \ + << " " << metrics.crash_dump_count; + } + + // Close test log. + test_log.close(); + + if (log_file.is_open() && g_save_debug_log && !g_continuous_load) + SaveDebugLogs(log_file); + + // Log revision information for Chrome build under test. + log_file << " " << "revision=" << last_change; + + // Get crash dumps. + LogOrDeleteNewCrashDumps(log_file, &metrics); + + if (log_file.is_open()) { + log_file << std::endl; + } + + if (metrics_output) { + *metrics_output = metrics; + } + } + + void NavigateThroughURLList(std::ofstream& log_file) { + std::ifstream file(g_url_file_path.value().c_str()); + ASSERT_TRUE(file.is_open()); + + // We navigate to URLs in the following order. + // CF -> CF -> host -> CF -> CF -> host. + for (int line_index = 1; + line_index <= g_end_index && !file.eof(); + ++line_index) { + std::string url_str; + std::getline(file, url_str); + + if (file.fail()) { + break; + } + + // Every 3rd URL goes into the host browser. + if (line_index % 3 != 0) { + std::string actual_url; + actual_url = "cf:"; + actual_url += url_str; + url_str = actual_url; + } + + if (g_start_index <= line_index) { + NavigateToURLLogResult(url_str, log_file, NULL); + } + } + + file.close(); + } + + protected: + virtual void SetUp() { + // Initialize crash_dumps_dir_path_. + PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dumps_dir_path_); + file_util::FileEnumerator enumerator(crash_dumps_dir_path_, + false, // not recursive + file_util::FileEnumerator::FILES); + for (FilePath path = enumerator.Next(); !path.value().empty(); + path = enumerator.Next()) { + if (path.MatchesExtension(FILE_PATH_LITERAL(".dmp"))) + crash_dumps_[path.BaseName()] = true; + } + + if (g_clear_profile) { + FilePath user_data_dir; + chrome::GetChromeFrameUserDataDirectory(&user_data_dir); + ASSERT_TRUE(file_util::DieFileDie(user_data_dir, true)); + } + + SetConfigBool(kChromeFrameHeadlessMode, true); + } + + virtual void TearDown() { + DeleteConfigValue(kChromeFrameHeadlessMode); + } + + FilePath ConstructSavedDebugLogPath(const FilePath& debug_log_path, + int index) { + std::string suffix("_"); + suffix.append(IntToString(index)); + return debug_log_path.InsertBeforeExtensionASCII(suffix); + } + + void SaveDebugLog(const FilePath& log_path, const std::wstring& log_id, + std::ofstream& log_file, int index) { + if (!log_path.empty()) { + FilePath saved_log_file_path = + ConstructSavedDebugLogPath(log_path, index); + if (file_util::Move(log_path, saved_log_file_path)) { + log_file << " " << log_id << "=" << saved_log_file_path.value(); + } + } + } + + // Rename the chrome and v8 debug log files if existing, and save the file + // paths in the log_file provided. + void SaveDebugLogs(std::ofstream& log_file) { + static int url_count = 1; + SaveDebugLog(g_chrome_log_path, L"chrome_log", log_file, url_count); + SaveDebugLog(g_v8_log_path, L"v8_log", log_file, url_count); + SaveDebugLog(g_test_log_path, L"test_log", log_file, url_count); + url_count++; + } + + // If a log_file is provided, log the crash dump with the given path; + // otherwise, delete the crash dump file. + void LogOrDeleteCrashDump(std::ofstream& log_file, + FilePath crash_dump_file_name) { + FilePath crash_dump_file_path(crash_dumps_dir_path_); + crash_dump_file_path = crash_dump_file_path.Append(crash_dump_file_name); + FilePath crash_text_file_path = + crash_dump_file_path.ReplaceExtension(FILE_PATH_LITERAL("txt")); + + if (log_file.is_open()) { + crash_dumps_[crash_dump_file_name] = true; + log_file << " crash_dump=" << crash_dump_file_path.value().c_str(); + } else { + ASSERT_TRUE(file_util::DieFileDie( + crash_dump_file_path, false)); + ASSERT_TRUE(file_util::DieFileDie( + crash_text_file_path, false)); + } + } + + // Check whether there are new .dmp files. Additionally, write + // " crash_dump=<full path name of the .dmp file>" + // to log_file. + void LogOrDeleteNewCrashDumps(std::ofstream& log_file, + NavigationMetrics* metrics) { + int num_dumps = 0; + + file_util::FileEnumerator enumerator(crash_dumps_dir_path_, + false, // not recursive + file_util::FileEnumerator::FILES); + for (FilePath path = enumerator.Next(); !path.value().empty(); + path = enumerator.Next()) { + if (path.MatchesExtension(FILE_PATH_LITERAL(".dmp")) && + !crash_dumps_[path.BaseName()]) { + LogOrDeleteCrashDump(log_file, path.BaseName()); + num_dumps++; + } + } + if (metrics) + metrics->crash_dump_count = num_dumps; + } + + // Get a PrefService whose contents correspond to the Local State file + // that was saved by the app as it closed. The caller takes ownership of the + // returned PrefService object. + PrefService* GetLocalState() { + FilePath local_state_path; + chrome::GetChromeFrameUserDataDirectory(&local_state_path); + + PrefService* local_state = new PrefService(local_state_path); + return local_state; + } + + void GetStabilityMetrics(NavigationMetrics* metrics) { + if (!metrics) + return; + scoped_ptr<PrefService> local_state(GetLocalState()); + if (!local_state.get()) + return; + local_state->RegisterBooleanPref(prefs::kStabilityExitedCleanly, false); + local_state->RegisterIntegerPref(prefs::kStabilityLaunchCount, -1); + local_state->RegisterIntegerPref(prefs::kStabilityPageLoadCount, -1); + local_state->RegisterIntegerPref(prefs::kStabilityCrashCount, 0); + local_state->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0); + + metrics->browser_clean_exit = + local_state->GetBoolean(prefs::kStabilityExitedCleanly); + metrics->browser_launch_count = + local_state->GetInteger(prefs::kStabilityLaunchCount); + metrics->page_load_count = + local_state->GetInteger(prefs::kStabilityPageLoadCount); + metrics->browser_crash_count = + local_state->GetInteger(prefs::kStabilityCrashCount); + metrics->renderer_crash_count = + local_state->GetInteger(prefs::kStabilityRendererCrashCount); + // TODO(huanr) + metrics->plugin_crash_count = 0; + + if (!metrics->browser_clean_exit) + metrics->browser_crash_count++; + } + + FilePath GetSampleDataDir() { + FilePath test_dir; + PathService::Get(chrome::DIR_TEST_DATA, &test_dir); + test_dir = test_dir.AppendASCII("reliability"); + test_dir = test_dir.AppendASCII("sample_pages"); + return test_dir; + } + + // The pathname of Chrome's crash dumps directory. + FilePath crash_dumps_dir_path_; + + // The set of all the crash dumps we have seen. Each crash generates a + // .dmp and a .txt file in the crash dumps directory. We only store the + // .dmp files in this set. + // + // The set is implemented as a std::map. The key is the file name, and + // the value is false (the file is not in the set) or true (the file is + // in the set). The initial value for any key in std::map is 0 (false), + // which in this case means a new file is not in the set initially, + // exactly the semantics we want. + std::map<FilePath, bool> crash_dumps_; +}; + +TEST_F(PageLoadTest, IEFullTabMode_Reliability) { + std::ofstream log_file; + + if (!g_log_file_path.empty()) { + log_file.open(g_log_file_path.value().c_str()); + } + + EXPECT_FALSE(g_url_file_path.empty()); + + for (int k = 0; k < g_iterations; ++k) { + NavigateThroughURLList(log_file); + } + + log_file.close(); +} + +} // namespace + +namespace { + void ReportHandler(const std::string& str) { + // Ignore report events. + } +} + +void SetPageRange(const CommandLine& parsed_command_line) { + // If calling into this function, we are running as a standalone program. + g_stand_alone = true; + + // Since we use --enable-dcheck for reliability tests, suppress the error + // dialog in the test process. + logging::SetLogReportHandler(ReportHandler); + + if (parsed_command_line.HasSwitch(kStartIndexSwitch)) { + ASSERT_TRUE( + StringToInt(WideToUTF16(parsed_command_line.GetSwitchValue( + kStartIndexSwitch)), &g_start_index)); + ASSERT_GT(g_start_index, 0); + } + + if (parsed_command_line.HasSwitch(kEndIndexSwitch)) { + ASSERT_TRUE( + StringToInt(WideToUTF16(parsed_command_line.GetSwitchValue( + kEndIndexSwitch)), &g_end_index)); + ASSERT_GT(g_end_index, 0); + } + + ASSERT_TRUE(g_end_index >= g_start_index); + + if (parsed_command_line.HasSwitch(kListSwitch)) + g_url_file_path = parsed_command_line.GetSwitchValuePath(kListSwitch); + + if (parsed_command_line.HasSwitch(kIterationSwitch)) { + ASSERT_TRUE( + StringToInt(WideToUTF16(parsed_command_line.GetSwitchValue( + kIterationSwitch)), &g_iterations)); + ASSERT_GT(g_iterations, 0); + } + + if (parsed_command_line.HasSwitch(kMemoryUsageSwitch)) + g_memory_usage = true; + + if (parsed_command_line.HasSwitch(kContinuousLoadSwitch)) + g_continuous_load = true; + + if (parsed_command_line.HasSwitch(kLogFileSwitch)) + g_log_file_path = parsed_command_line.GetSwitchValuePath(kLogFileSwitch); + + if (parsed_command_line.HasSwitch(kTimeoutSwitch)) { + ASSERT_TRUE( + StringToInt(WideToUTF16(parsed_command_line.GetSwitchValue( + kTimeoutSwitch)), &g_timeout_ms)); + ASSERT_GT(g_timeout_ms, 0); + } + + if (parsed_command_line.HasSwitch(kNoPageDownSwitch)) + g_page_down = false; + + if (parsed_command_line.HasSwitch(kNoClearProfileSwitch)) + g_clear_profile = false; + + if (parsed_command_line.HasSwitch(kSaveDebugLogSwitch)) { + g_save_debug_log = true; + g_chrome_log_path = logging::GetLogFileName(); + // We won't get v8 log unless --no-sandbox is specified. + if (parsed_command_line.HasSwitch(switches::kNoSandbox)) { + PathService::Get(base::DIR_CURRENT, &g_v8_log_path); + g_v8_log_path = g_v8_log_path.AppendASCII(kV8LogFileDefaultName); + // The command line switch may override the default v8 log path. + if (parsed_command_line.HasSwitch(switches::kJavaScriptFlags)) { + CommandLine v8_command_line( + parsed_command_line.GetSwitchValuePath(switches::kJavaScriptFlags)); + if (v8_command_line.HasSwitch(kV8LogFileSwitch)) { + g_v8_log_path = v8_command_line.GetSwitchValuePath(kV8LogFileSwitch); + if (!file_util::AbsolutePath(&g_v8_log_path)) + g_v8_log_path = FilePath(); + } + } + } + } +} + diff --git a/chrome_frame/test/reliability/page_load_test.h b/chrome_frame/test/reliability/page_load_test.h new file mode 100644 index 0000000..f5aac19 --- /dev/null +++ b/chrome_frame/test/reliability/page_load_test.h @@ -0,0 +1,17 @@ +// Copyright (c) 2006-2009 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. +// +// This file declares helper functions necessary to run reliablity test under +// UI test framework. + +#ifndef CHROME_FRAME_TEST_RELIABILITY_PAGE_LOAD_TEST_H_ +#define CHROME_FRAME_TEST_RELIABILITY_PAGE_LOAD_TEST_H_ + +#include "base/command_line.h" + +// Parse the command line options and set the page range accordingly. +void SetPageRange(const CommandLine&); + +#endif // CHROME_FRAME_TEST_RELIABILITY_PAGE_LOAD_TEST_H_ + diff --git a/chrome_frame/test/reliability/reliability_test_suite.h b/chrome_frame/test/reliability/reliability_test_suite.h new file mode 100644 index 0000000..4e41ede --- /dev/null +++ b/chrome_frame/test/reliability/reliability_test_suite.h @@ -0,0 +1,30 @@ +// Copyright (c) 2006-2009 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_RELIABILITY_RELIABILITY_TEST_SUITE_H_ +#define CHROME_FRAME_TEST_RELIABILITY_RELIABILITY_TEST_SUITE_H_ + +#include "chrome_frame/test/reliability/page_load_test.h" +#include "chrome/test/ui/ui_test_suite.h" + +class ReliabilityTestSuite : public UITestSuite { + public: + ReliabilityTestSuite(int argc, char** argv) : UITestSuite(argc, argv) { + } + + protected: + virtual void Initialize() { + CoInitializeEx(NULL, COINIT_MULTITHREADED); + SetPageRange(*CommandLine::ForCurrentProcess()); + UITestSuite::Initialize(); + } + + virtual void Shutdown() { + CoUninitialize(); + UITestSuite::Shutdown(); + } +}; + +#endif // CHROME_FRAME_TEST_RELIABILITY_RELIABILITY_TEST_SUITE_H_ + diff --git a/chrome_frame/test/reliability/run_all_unittests.cc b/chrome_frame/test/reliability/run_all_unittests.cc new file mode 100644 index 0000000..734a801 --- /dev/null +++ b/chrome_frame/test/reliability/run_all_unittests.cc @@ -0,0 +1,10 @@ +// Copyright (c) 2006-2009 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/reliability/reliability_test_suite.h" + +int main(int argc, char **argv) { + return ReliabilityTestSuite(argc, argv).Run(); +} + diff --git a/chrome_frame/utils.cc b/chrome_frame/utils.cc index 1edbee9..b97e461 100644 --- a/chrome_frame/utils.cc +++ b/chrome_frame/utils.cc @@ -47,6 +47,10 @@ const wchar_t kDevChannelName[] = L"-dev"; const wchar_t kChromeAttachExternalTabPrefix[] = L"attach_external_tab"; +// Indicates that we are running in a test environment, where execptions, etc +// are handled by the chrome test crash server. +const wchar_t kChromeFrameHeadlessMode[] = L"ChromeFrameHeadlessMode"; + HRESULT UtilRegisterTypeLib(HINSTANCE tlb_instance, LPCOLESTR index, bool for_current_user_only) { @@ -546,6 +550,31 @@ bool GetConfigBool(bool default_value, const wchar_t* value_name) { return (value != FALSE); } +bool SetConfigInt(const wchar_t* value_name, int value) { + RegKey config_key; + if (config_key.Open(HKEY_CURRENT_USER, kChromeFrameConfigKey, + KEY_SET_VALUE)) { + if (config_key.WriteValue(value_name, value)) { + return true; + } + } + + return false; +} + +bool SetConfigBool(const wchar_t* value_name, bool value) { + return SetConfigInt(value_name, value); +} + +bool DeleteConfigValue(const wchar_t* value_name) { + RegKey config_key; + if (config_key.Open(HKEY_CURRENT_USER, kChromeFrameConfigKey, + KEY_WRITE)) { + return config_key.DeleteValue(value_name); + } + return false; +} + bool IsOptInUrl(const wchar_t* url) { RegKey config_key; if (!config_key.Open(HKEY_CURRENT_USER, kChromeFrameConfigKey, KEY_READ)) @@ -667,3 +696,9 @@ bool IsSubFrameRequest(IUnknown* service_provider) { return is_non_top_level_request; } + +bool IsHeadlessMode() { + bool headless = GetConfigBool(false, kChromeFrameHeadlessMode); + return headless; +} + diff --git a/chrome_frame/utils.h b/chrome_frame/utils.h index a685691..f71bb7d 100644 --- a/chrome_frame/utils.h +++ b/chrome_frame/utils.h @@ -181,6 +181,19 @@ bool GetConfigBool(bool default_value, const wchar_t* value_name); // Gets an integer configuration value from the registry. int GetConfigInt(int default_value, const wchar_t* value_name); +// Sets an integer configuration value in the registry. +bool SetConfigInt(const wchar_t* value_name, int value); + +// Sets a boolean integer configuration value in the registry. +bool SetConfigBool(const wchar_t* value_name, bool value); + +// Deletes the configuration value passed in. +bool DeleteConfigValue(const wchar_t* value_name); + +// Returns true if we are running in headless mode in which case we need to +// gather crash dumps, etc to send them to the crash server. +bool IsHeadlessMode(); + // Check if this url is opting into Chrome Frame based on static settings. bool IsOptInUrl(const wchar_t* url); @@ -278,4 +291,6 @@ STDMETHODIMP QueryInterfaceIfDelegateSupports(void* obj, REFIID iid, #define COM_INTERFACE_BLIND_DELEGATE() \ COM_INTERFACE_ENTRY_FUNC_BLIND(0, CheckOutgoingInterface<_ComMapClass>) +extern const wchar_t kChromeFrameHeadlessMode[]; + #endif // CHROME_FRAME_UTILS_H_ |