1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CHROME_FRAME_TEST_CHROME_FRAME_TEST_UTILS_H_ 6 #define CHROME_FRAME_TEST_CHROME_FRAME_TEST_UTILS_H_ 7 8 #include <windows.h> 9 10 #include <atlbase.h> 11 #include <atlwin.h> 12 13 #include <string> 14 15 #include "base/basictypes.h" 16 #include "base/cancelable_callback.h" 17 #include "base/compiler_specific.h" 18 #include "base/files/file_path.h" 19 #include "base/memory/scoped_ptr.h" 20 #include "base/message_loop/message_loop.h" 21 #include "base/process/process_handle.h" 22 #include "base/run_loop.h" 23 #include "base/test/test_reg_util_win.h" 24 #include "base/time/time.h" 25 #include "base/time/time.h" 26 #include "base/win/registry.h" 27 #include "base/win/scoped_comptr.h" 28 #include "chrome_frame/chrome_tab.h" 29 #include "chrome_frame/test/simulate_input.h" 30 #include "chrome_frame/test_utils.h" 31 #include "chrome_frame/utils.h" 32 33 #include "gtest/gtest.h" 34 35 // Needed for CreateFunctor. 36 #define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING 37 #include "testing/gmock_mutant.h" 38 39 interface IWebBrowser2; 40 41 namespace base { 42 class FilePath; 43 } 44 45 namespace chrome_frame_test { 46 47 int CloseVisibleWindowsOnAllThreads(HANDLE process); 48 49 base::ProcessHandle LaunchIE(const std::wstring& url); 50 base::ProcessHandle LaunchChrome(const std::wstring& url, 51 const base::FilePath& user_data_dir); 52 53 // Attempts to close all open IE windows. 54 // The return value is the number of windows closed. 55 // @note: this function requires COM to be initialized on the calling thread. 56 // Since the caller might be running in either MTA or STA, the function does 57 // not perform this initialization itself. 58 int CloseAllIEWindows(); 59 60 // Saves a screen snapshot to the desktop and logs its location. 61 bool TakeSnapshotAndLog(); 62 63 extern const wchar_t kIEImageName[]; 64 extern const wchar_t kIEBrokerImageName[]; 65 extern const char kChromeImageName[]; 66 extern const wchar_t kChromeLauncher[]; 67 extern const base::TimeDelta kChromeFrameLongNavigationTimeout; 68 extern const base::TimeDelta kChromeFrameVeryLongNavigationTimeout; 69 70 // Temporarily impersonate the current thread to low integrity for the lifetime 71 // of the object. Destructor will automatically revert integrity level. 72 class LowIntegrityToken { 73 public: 74 LowIntegrityToken(); 75 ~LowIntegrityToken(); 76 BOOL Impersonate(); 77 BOOL RevertToSelf(); 78 protected: 79 static bool IsImpersonated(); 80 bool impersonated_; 81 }; 82 83 // This class implements the COM IMessageFilter interface and is used to detect 84 // hung outgoing COM calls which are typically to IE. If IE is hung for any 85 // reason the chrome frame tests should not hang indefinitely. This class 86 // basically cancels the outgoing call if the WM_TIMER which is set by the 87 // TimedMsgLoop object is posted to the message queue. 88 class HungCOMCallDetector 89 : public CComObjectRootEx<CComMultiThreadModel>, 90 public IMessageFilter, 91 public CWindowImpl<HungCOMCallDetector> { 92 public: 93 HungCOMCallDetector() 94 : is_hung_(false) { 95 } 96 97 ~HungCOMCallDetector() { 98 TearDown(); 99 } 100 101 BEGIN_MSG_MAP(HungCOMCallDetector) 102 MESSAGE_HANDLER(WM_TIMER, OnTimer) 103 END_MSG_MAP() 104 105 BEGIN_COM_MAP(HungCOMCallDetector) 106 COM_INTERFACE_ENTRY(IMessageFilter) 107 END_COM_MAP() 108 109 static CComObject<HungCOMCallDetector>* Setup(int timeout_seconds) { 110 CComObject<HungCOMCallDetector>* this_instance = NULL; 111 CComObject<HungCOMCallDetector>::CreateInstance(&this_instance); 112 EXPECT_TRUE(this_instance != NULL); 113 scoped_refptr<HungCOMCallDetector> ref_instance = this_instance; 114 115 HRESULT hr = ref_instance->Initialize(timeout_seconds); 116 if (FAILED(hr)) { 117 LOG(ERROR) << "Failed to Initialize Hung COM call detector. Error:" 118 << hr; 119 return NULL; 120 } 121 return this_instance; 122 } 123 124 void TearDown() { 125 if (prev_filter_) { 126 base::win::ScopedComPtr<IMessageFilter> prev_filter; 127 CoRegisterMessageFilter(prev_filter_.get(), prev_filter.Receive()); 128 DCHECK(prev_filter.IsSameObject(this)); 129 prev_filter_.Release(); 130 } 131 if (IsWindow()) { 132 DestroyWindow(); 133 m_hWnd = NULL; 134 } 135 } 136 137 STDMETHOD_(DWORD, HandleInComingCall)(DWORD call_type, 138 HTASK caller, 139 DWORD tick_count, 140 LPINTERFACEINFO interface_info) { 141 return SERVERCALL_ISHANDLED; 142 } 143 144 STDMETHOD_(DWORD, RetryRejectedCall)(HTASK callee, 145 DWORD tick_count, 146 DWORD reject_type) { 147 return -1; 148 } 149 150 STDMETHOD_(DWORD, MessagePending)(HTASK callee, 151 DWORD tick_count, 152 DWORD pending_type) { 153 MSG msg = {0}; 154 if (is_hung_) { 155 return PENDINGMSG_CANCELCALL; 156 } 157 return PENDINGMSG_WAITDEFPROCESS; 158 } 159 160 bool is_hung() const { 161 return is_hung_; 162 } 163 164 LRESULT OnTimer(UINT msg, WPARAM wp, LPARAM lp, BOOL& handled) { // NOLINT 165 is_hung_ = true; 166 return 1; 167 } 168 169 private: 170 HRESULT Initialize(int timeout_seconds) { 171 // Create a window for the purpose of setting a windows timer for 172 // detecting whether any outgoing COM call issued in this context 173 // hangs. 174 // We then register a COM message filter which basically looks for the 175 // timer posted to this window and cancels the outgoing call. 176 Create(HWND_MESSAGE); 177 if (!IsWindow()) { 178 LOG(ERROR) << "Failed to create window. Error:" << GetLastError(); 179 return E_FAIL; 180 } 181 182 HRESULT hr = CoRegisterMessageFilter(this, prev_filter_.Receive()); 183 if (FAILED(hr)) { 184 LOG(ERROR) << "Failed to register message filter. Error:" << hr; 185 return hr; 186 } 187 188 static const int kHungDetectTimerId = 0x0000baba; 189 SetTimer(kHungDetectTimerId, 1000 * (timeout_seconds + 40), NULL); 190 return S_OK; 191 } 192 193 // used to detect if outgoing COM calls hung. 194 bool is_hung_; 195 base::win::ScopedComPtr<IMessageFilter> prev_filter_; 196 }; 197 198 // MessageLoopForUI wrapper that runs only for a limited time. 199 // We need a UI message loop in the main thread. 200 class TimedMsgLoop { 201 public: 202 TimedMsgLoop() : snapshot_on_timeout_(false), quit_loop_invoked_(false) { 203 } 204 205 void set_snapshot_on_timeout(bool value) { 206 snapshot_on_timeout_ = value; 207 } 208 209 void RunFor(base::TimeDelta duration) { 210 quit_loop_invoked_ = false; 211 if (snapshot_on_timeout_) 212 timeout_closure_.Reset(base::Bind(&TimedMsgLoop::SnapshotAndQuit)); 213 else 214 timeout_closure_.Reset(base::MessageLoop::QuitClosure()); 215 loop_.PostDelayedTask(FROM_HERE, timeout_closure_.callback(), duration); 216 loop_.base::MessageLoop::Run(); 217 timeout_closure_.Cancel(); 218 } 219 220 void PostTask(const tracked_objects::Location& from_here, 221 const base::Closure& task) { 222 loop_.PostTask(from_here, task); 223 } 224 225 void PostDelayedTask(const tracked_objects::Location& from_here, 226 const base::Closure& task, base::TimeDelta delay) { 227 loop_.PostDelayedTask(from_here, task, delay); 228 } 229 230 void Quit() { 231 // Quit after no delay. 232 QuitAfter(base::TimeDelta()); 233 } 234 235 void QuitAfter(base::TimeDelta delay) { 236 timeout_closure_.Cancel(); 237 quit_loop_invoked_ = true; 238 loop_.PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(), delay); 239 } 240 241 bool WasTimedOut() const { 242 return !quit_loop_invoked_; 243 } 244 245 void RunUntilIdle() { 246 loop_.RunUntilIdle(); 247 } 248 249 private: 250 static void SnapshotAndQuit() { 251 TakeSnapshotAndLog(); 252 base::MessageLoop::current()->Quit(); 253 } 254 255 base::MessageLoopForUI loop_; 256 base::CancelableClosure timeout_closure_; 257 bool snapshot_on_timeout_; 258 bool quit_loop_invoked_; 259 }; 260 261 // Saves typing. It's somewhat hard to create a wrapper around 262 // testing::InvokeWithoutArgs since it returns a 263 // non-public (testing::internal) type. 264 #define QUIT_LOOP(loop) testing::InvokeWithoutArgs(\ 265 testing::CreateFunctor(&loop, &chrome_frame_test::TimedMsgLoop::Quit)) 266 267 #define QUIT_LOOP_SOON(loop, delay) testing::InvokeWithoutArgs(\ 268 testing::CreateFunctor(&loop, &chrome_frame_test::TimedMsgLoop::QuitAfter, \ 269 delay)) 270 271 // Launches IE as a COM server and returns the corresponding IWebBrowser2 272 // interface pointer. 273 // Returns S_OK on success. 274 HRESULT LaunchIEAsComServer(IWebBrowser2** web_browser); 275 276 // Returns the path of the exe passed in. 277 std::wstring GetExecutableAppPath(const std::wstring& file); 278 279 // Returns the profile path to be used for IE. This varies as per version. 280 base::FilePath GetProfilePathForIE(); 281 282 // Returns the version of the exe passed in. 283 std::wstring GetExeVersion(const std::wstring& exe_path); 284 285 // Returns the version of Internet Explorer on the machine. 286 IEVersion GetInstalledIEVersion(); 287 288 // Returns the folder for CF test data. 289 base::FilePath GetTestDataFolder(); 290 291 // Returns the folder for Selenium core. 292 base::FilePath GetSeleniumTestFolder(); 293 294 // Returns the path portion of the url. 295 std::wstring GetPathFromUrl(const std::wstring& url); 296 297 // Returns the path and query portion of the url. 298 std::wstring GetPathAndQueryFromUrl(const std::wstring& url); 299 300 // Adds the CF meta tag to the html page. Returns true if successful. 301 bool AddCFMetaTag(std::string* html_data); 302 303 // Get text data from the clipboard. 304 std::wstring GetClipboardText(); 305 306 // Destroys the clipboard for the current thread. This function must be called 307 // if GetClipboardText() or SetClipboardText() have been invoked. 308 void DestroyClipboard(); 309 310 // Puts the given text data on the clipboard. All previous items on the 311 // clipboard are removed. 312 void SetClipboardText(const std::wstring& text); 313 314 // A convenience class to close all open IE windows at the end 315 // of a scope. It's more convenient to do it this way than to 316 // explicitly call chrome_frame_test::CloseAllIEWindows at the 317 // end of a test since part of the test's cleanup code may be 318 // in object destructors that would run after CloseAllIEWindows 319 // would get called. 320 // Ideally all IE windows should be closed when this happens so 321 // if the test ran normally, we should not have any windows to 322 // close at this point. 323 class CloseIeAtEndOfScope { 324 public: 325 CloseIeAtEndOfScope() {} 326 ~CloseIeAtEndOfScope(); 327 }; 328 329 // Starts the Chrome crash service which enables us to gather crash dumps 330 // during test runs. 331 base::ProcessHandle StartCrashService(); 332 333 // Used in tests where we reference the registry and don't want to run into 334 // problems where existing registry settings might conflict with the 335 // expectations of the test. 336 class ScopedVirtualizeHklmAndHkcu { 337 public: 338 ScopedVirtualizeHklmAndHkcu(); 339 ~ScopedVirtualizeHklmAndHkcu(); 340 341 protected: 342 registry_util::RegistryOverrideManager override_manager_; 343 }; 344 345 // Attempts to kill all the processes on the current machine that were launched 346 // from the given executable name, ending them with the given exit code. If 347 // filter is non-null, then only processes selected by the filter are killed. 348 // Returns true if all processes were able to be killed off, false if at least 349 // one couldn't be killed. 350 // NOTE: This function is based on the base\process_util.h helper function 351 // KillProcesses. Takes in the wait flag as a parameter. 352 bool KillProcesses(const std::wstring& executable_name, int exit_code, 353 bool wait); 354 355 // Returns the type of test bed, PER_USER or SYSTEM_LEVEL. 356 ScopedChromeFrameRegistrar::RegistrationType GetTestBedType(); 357 358 // Clears IE8 session restore history. 359 void ClearIESessionHistory(); 360 361 // Returns a local IPv4 address for the current machine. The address 362 // corresponding to a NIC is preferred over the loopback address. 363 std::string GetLocalIPv4Address(); 364 365 } // namespace chrome_frame_test 366 367 // TODO(tommi): This is a temporary workaround while we're getting our 368 // Singleton story straight. Ideally each test should clear up any singletons 369 // it might have created, but test cases do not implicitly have their own 370 // AtExitManager, so we have this workaround method for tests that depend on 371 // "fresh" singletons. The implementation is in chrome_frame_unittest_main.cc. 372 void DeleteAllSingletons(); 373 374 #endif // CHROME_FRAME_TEST_CHROME_FRAME_TEST_UTILS_H_ 375