| OLD | NEW |
| (Empty) |
| 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_MOCK_IE_EVENT_SINK_TEST_H_ | |
| 6 #define CHROME_FRAME_TEST_MOCK_IE_EVENT_SINK_TEST_H_ | |
| 7 | |
| 8 #include <atlbase.h> | |
| 9 #include <atlcom.h> | |
| 10 #include <string> | |
| 11 #include <vector> | |
| 12 | |
| 13 #include "base/files/file_path.h" | |
| 14 #include "base/strings/string_number_conversions.h" | |
| 15 #include "base/strings/stringprintf.h" | |
| 16 #include "base/strings/utf_string_conversions.h" | |
| 17 #include "base/win/object_watcher.h" | |
| 18 #include "chrome_frame/test/chrome_frame_test_utils.h" | |
| 19 #include "chrome_frame/test/chrome_frame_ui_test_utils.h" | |
| 20 #include "chrome_frame/test/ie_event_sink.h" | |
| 21 #include "chrome_frame/test/test_server.h" | |
| 22 #include "chrome_frame/test/test_with_web_server.h" | |
| 23 #include "chrome_frame/test/win_event_receiver.h" | |
| 24 #include "testing/gmock/include/gmock/gmock.h" | |
| 25 #include "testing/gtest/include/gtest/gtest.h" | |
| 26 #include "third_party/xulrunner-sdk/win/include/accessibility/AccessibleEventId.
h" | |
| 27 | |
| 28 namespace chrome_frame_test { | |
| 29 | |
| 30 // Convenience enum for specifying whether a load occurred in IE or CF. | |
| 31 enum LoadedInRenderer { | |
| 32 IN_IE = 0, | |
| 33 IN_CF | |
| 34 }; | |
| 35 | |
| 36 // This mocks an IEEventListener, providing methods for expecting certain | |
| 37 // sequences of events. | |
| 38 class MockIEEventSink : public IEEventListener { | |
| 39 public: | |
| 40 MockIEEventSink() { | |
| 41 CComObject<IEEventSink>::CreateInstance(&event_sink_); | |
| 42 event_sink_->AddRef(); | |
| 43 } | |
| 44 | |
| 45 ~MockIEEventSink() { | |
| 46 Detach(); | |
| 47 int reference_count = event_sink_->reference_count(); | |
| 48 LOG_IF(ERROR, reference_count != 1) | |
| 49 << "Event sink is still referenced externally: ref count = " | |
| 50 << reference_count; | |
| 51 event_sink_->Release(); | |
| 52 } | |
| 53 | |
| 54 // Override IEEventListener methods. | |
| 55 MOCK_METHOD7(OnBeforeNavigate2, void (IDispatch* dispatch, // NOLINT | |
| 56 VARIANT* url, | |
| 57 VARIANT* flags, | |
| 58 VARIANT* target_frame_name, | |
| 59 VARIANT* post_data, | |
| 60 VARIANT* headers, | |
| 61 VARIANT_BOOL* cancel)); | |
| 62 MOCK_METHOD2(OnNavigateComplete2, void (IDispatch* dispatch, // NOLINT | |
| 63 VARIANT* url)); | |
| 64 MOCK_METHOD5(OnNewWindow3, void (IDispatch** dispatch, // NOLINT | |
| 65 VARIANT_BOOL* cancel, | |
| 66 DWORD flags, | |
| 67 BSTR url_context, | |
| 68 BSTR url)); | |
| 69 MOCK_METHOD2(OnNewWindow2, void (IDispatch** dispatch, // NOLINT | |
| 70 VARIANT_BOOL* cancel)); | |
| 71 MOCK_METHOD5(OnNavigateError, void (IDispatch* dispatch, // NOLINT | |
| 72 VARIANT* url, | |
| 73 VARIANT* frame_name, | |
| 74 VARIANT* status_code, | |
| 75 VARIANT* cancel)); | |
| 76 MOCK_METHOD2(OnFileDownload, void (VARIANT_BOOL active_doc, // NOLINT | |
| 77 VARIANT_BOOL* cancel)); | |
| 78 MOCK_METHOD0(OnQuit, void ()); // NOLINT | |
| 79 MOCK_METHOD1(OnLoadError, void (const wchar_t* url)); // NOLINT | |
| 80 MOCK_METHOD3(OnMessage, void (const wchar_t* message, // NOLINT | |
| 81 const wchar_t* origin, | |
| 82 const wchar_t* source)); | |
| 83 MOCK_METHOD2(OnNewBrowserWindow, void (IDispatch* dispatch, // NOLINT | |
| 84 const wchar_t* url)); | |
| 85 | |
| 86 // Convenience OnLoad method which is called once when a page is loaded with | |
| 87 // |is_cf| set to whether the renderer is CF or not. | |
| 88 MOCK_METHOD2(OnLoad, void (bool is_cf, const wchar_t* url)); // NOLINT | |
| 89 | |
| 90 // Attach |dispatch| to the event sink and begin listening to the source's | |
| 91 // events. | |
| 92 void Attach(IDispatch* dispatch) { | |
| 93 event_sink_->set_listener(this); | |
| 94 event_sink_->Attach(dispatch); | |
| 95 } | |
| 96 | |
| 97 void Detach() { | |
| 98 event_sink_->set_listener(NULL); | |
| 99 event_sink_->Uninitialize(); | |
| 100 } | |
| 101 | |
| 102 // Expect a normal navigation to |url| to occur in CF or IE. | |
| 103 void ExpectNavigation(bool is_cf, const std::wstring& url); | |
| 104 | |
| 105 // Similar as above, but should be used in cases where IE6 with CF may not | |
| 106 // generate an OnBeforeNavigate event during the navigation. This occurs | |
| 107 // during in-page navigation (via anchors), form submission, window.open | |
| 108 // to target "self", and possibly others. | |
| 109 void ExpectNavigationOptionalBefore(bool is_cf, const std::wstring& url); | |
| 110 | |
| 111 // Expect a navigation in a new window created by a window.open call to |url|. | |
| 112 // |parent_cf| signifies whether the parent frame was loaded in CF, while | |
| 113 // |new_window_cf| signifies whether to expect the new page to be loaded in | |
| 114 // CF. | |
| 115 void ExpectJavascriptWindowOpenNavigation(bool parent_cf, bool new_window_cf, | |
| 116 const std::wstring& url); | |
| 117 | |
| 118 // Expect a new window to open. The new event sink will be attached to | |
| 119 // |new_window_mock|. | |
| 120 void ExpectNewWindow(MockIEEventSink* new_window_mock); | |
| 121 | |
| 122 // Expects any and all navigations. | |
| 123 void ExpectAnyNavigations(); | |
| 124 | |
| 125 void ExpectDocumentReadystate(int ready_state); | |
| 126 | |
| 127 IEEventSink* event_sink() { return event_sink_; } | |
| 128 | |
| 129 private: | |
| 130 // Override IE's OnDocumentComplete to call our OnLoad, iff it is IE actually | |
| 131 // rendering the page. | |
| 132 virtual void OnDocumentComplete(IDispatch* dispatch, VARIANT* url); | |
| 133 | |
| 134 // Override CF's OnLoad to call our OnLoad. | |
| 135 virtual void OnLoad(const wchar_t* url) { | |
| 136 OnLoad(IN_CF, url); | |
| 137 } | |
| 138 | |
| 139 // Helper method for expecting navigations. |before_cardinality| specifies | |
| 140 // the cardinality for the BeforeNavigate expectation and | |
| 141 // |complete_cardinality| specifies the cardinality for the NavigateComplete | |
| 142 // expectation. Returns the set of expectations added. | |
| 143 // Note: Prefer making a new Expect... method before making this public. | |
| 144 testing::ExpectationSet ExpectNavigationCardinality(const std::wstring& url, | |
| 145 testing::Cardinality before_cardinality, | |
| 146 testing::Cardinality complete_cardinality); | |
| 147 | |
| 148 // It may be necessary to create this on the heap. Otherwise, if the | |
| 149 // reference count is greater than zero, a debug assert will be triggered | |
| 150 // in the destructor. This happens at least when IE crashes. In that case, | |
| 151 // DispEventUnadvise and CoDisconnectObject are not sufficient to decrement | |
| 152 // the reference count. | |
| 153 // TODO(kkania): Investigate if the above is true. | |
| 154 CComObject<IEEventSink>* event_sink_; | |
| 155 }; | |
| 156 | |
| 157 // This mocks a PropertyNotifySinkListener, providing methods for | |
| 158 // expecting certain sequences of events. | |
| 159 class MockPropertyNotifySinkListener : public PropertyNotifySinkListener { | |
| 160 public: | |
| 161 MockPropertyNotifySinkListener() : cookie_(0), sink_(NULL) { | |
| 162 CComObject<PropertyNotifySinkImpl>::CreateInstance(&sink_); | |
| 163 sink_->AddRef(); | |
| 164 sink_->set_listener(this); | |
| 165 } | |
| 166 | |
| 167 ~MockPropertyNotifySinkListener() { | |
| 168 Detach(); | |
| 169 sink_->set_listener(NULL); | |
| 170 LOG_IF(ERROR, sink_->m_dwRef != 1) | |
| 171 << "Event sink is still referenced externally: ref count = " | |
| 172 << sink_->m_dwRef; | |
| 173 sink_->Release(); | |
| 174 } | |
| 175 | |
| 176 // Override PropertyNotifySinkListener methods. | |
| 177 MOCK_METHOD1(OnChanged, void (DISPID dispid)); // NOLINT | |
| 178 | |
| 179 bool Attach(IUnknown* obj) { | |
| 180 DCHECK_EQ(cookie_, 0UL); | |
| 181 DCHECK(obj); | |
| 182 HRESULT hr = AtlAdvise(obj, sink_->GetUnknown(), IID_IPropertyNotifySink, | |
| 183 &cookie_); | |
| 184 if (SUCCEEDED(hr)) { | |
| 185 event_source_ = obj; | |
| 186 } else { | |
| 187 LOG(ERROR) << base::StringPrintf("AtlAdvise: 0x%08X", hr); | |
| 188 cookie_ = 0; | |
| 189 } | |
| 190 | |
| 191 return SUCCEEDED(hr); | |
| 192 } | |
| 193 | |
| 194 void Detach() { | |
| 195 if (event_source_) { | |
| 196 DCHECK_NE(cookie_, 0UL); | |
| 197 AtlUnadvise(event_source_, IID_IPropertyNotifySink, cookie_); | |
| 198 event_source_.Release(); | |
| 199 cookie_ = 0; | |
| 200 } | |
| 201 } | |
| 202 | |
| 203 private: | |
| 204 CComObject<PropertyNotifySinkImpl>* sink_; | |
| 205 DWORD cookie_; | |
| 206 base::win::ScopedComPtr<IUnknown> event_source_; | |
| 207 }; | |
| 208 | |
| 209 // Allows tests to observe when processes exit. | |
| 210 class MockObjectWatcherDelegate : public base::win::ObjectWatcher::Delegate { | |
| 211 public: | |
| 212 // base::win::ObjectWatcher::Delegate implementation | |
| 213 MOCK_METHOD1(OnObjectSignaled, void (HANDLE process_handle)); // NOLINT | |
| 214 | |
| 215 virtual ~MockObjectWatcherDelegate() { | |
| 216 // Would be nice to free them when OnObjectSignaled is called, too, but | |
| 217 // it doesn't seem worth it. | |
| 218 for (std::vector<HANDLE>::iterator it = process_handles_.begin(); | |
| 219 it != process_handles_.end(); ++it) { | |
| 220 ::CloseHandle(*it); | |
| 221 } | |
| 222 } | |
| 223 | |
| 224 // Registers this instance to watch |process_handle| for termination. | |
| 225 void WatchProcess(HANDLE process_handle) { | |
| 226 process_handles_.push_back(process_handle); | |
| 227 object_watcher_.StartWatching(process_handle, this); | |
| 228 } | |
| 229 | |
| 230 // Registers this instance to watch |hwnd|'s owning process for termination. | |
| 231 void WatchProcessForHwnd(HWND hwnd) { | |
| 232 DWORD pid = 0; | |
| 233 ::GetWindowThreadProcessId(hwnd, &pid); | |
| 234 EXPECT_TRUE(pid); | |
| 235 if (pid != 0) { | |
| 236 HANDLE process_handle = ::OpenProcess(SYNCHRONIZE, FALSE, pid); | |
| 237 EXPECT_TRUE(process_handle); | |
| 238 if (process_handle != NULL) { | |
| 239 WatchProcess(process_handle); | |
| 240 } | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 private: | |
| 245 std::vector<HANDLE> process_handles_; | |
| 246 base::win::ObjectWatcher object_watcher_; | |
| 247 }; | |
| 248 | |
| 249 // Mocks a window observer so that tests can detect new windows. | |
| 250 class MockWindowObserver : public WindowObserver { | |
| 251 public: | |
| 252 // WindowObserver implementation | |
| 253 MOCK_METHOD1(OnWindowOpen, void (HWND hwnd)); // NOLINT | |
| 254 MOCK_METHOD1(OnWindowClose, void (HWND hwnd)); // NOLINT | |
| 255 | |
| 256 void WatchWindow(std::string caption_pattern, std::string class_pattern) { | |
| 257 window_watcher_.AddObserver(this, caption_pattern, class_pattern); | |
| 258 } | |
| 259 | |
| 260 private: | |
| 261 WindowWatchdog window_watcher_; | |
| 262 }; | |
| 263 | |
| 264 class MockAccEventObserver : public AccEventObserver { | |
| 265 public: | |
| 266 MOCK_METHOD1(OnAccDocLoad, void (HWND)); // NOLINT | |
| 267 MOCK_METHOD3(OnAccValueChange, void (HWND, AccObject*, // NOLINT | |
| 268 const std::wstring&)); | |
| 269 MOCK_METHOD1(OnMenuPopup, void (HWND)); // NOLINT | |
| 270 MOCK_METHOD2(OnTextCaretMoved, void (HWND, AccObject*)); // NOLINT | |
| 271 }; | |
| 272 | |
| 273 // This test fixture provides common methods needed for testing CF | |
| 274 // integration with IE. gMock is used to verify that IE is reporting correct | |
| 275 // navigational events and MockWebServer is used to verify that the correct | |
| 276 // requests are going out. | |
| 277 class MockIEEventSinkTest { | |
| 278 public: | |
| 279 MockIEEventSinkTest(); | |
| 280 MockIEEventSinkTest(int port, const std::wstring& address, | |
| 281 const base::FilePath& root_dir); | |
| 282 | |
| 283 ~MockIEEventSinkTest() { | |
| 284 // Detach manually here so that it occurs before |last_resort_close_ie_| | |
| 285 // is destroyed. | |
| 286 ie_mock_.Detach(); | |
| 287 } | |
| 288 | |
| 289 // Launches IE as a COM server and sets |ie_mock_| as the event sink, then | |
| 290 // navigates to the given url. Then the timed message loop is run until | |
| 291 // |ie_mock_| receives OnQuit or the timeout is exceeded. | |
| 292 void LaunchIEAndNavigate(const std::wstring& url); | |
| 293 | |
| 294 // Same as above but allows the timeout to be specified. | |
| 295 void LaunchIENavigateAndLoop(const std::wstring& url, | |
| 296 base::TimeDelta timeout); | |
| 297 | |
| 298 // Returns the url for the test file given. |relative_path| should be | |
| 299 // relative to the test data directory. | |
| 300 std::wstring GetTestUrl(const std::wstring& relative_path); | |
| 301 | |
| 302 // Returns the absolute FilePath for the test file given. |relative_path| | |
| 303 // should be relative to the test data directory. | |
| 304 base::FilePath GetTestFilePath(const std::wstring& relative_path); | |
| 305 | |
| 306 // Returns the url for an html page just containing some text. Iff |use_cf| | |
| 307 // is true, the chrome_frame meta tag will be included too. | |
| 308 std::wstring GetSimplePageUrl() { | |
| 309 return GetTestUrl(L"simple.html"); | |
| 310 } | |
| 311 | |
| 312 // Returns the title of the html page at |GetSimplePageUrl()|. | |
| 313 std::wstring GetSimplePageTitle() { | |
| 314 return L"simple web page"; | |
| 315 } | |
| 316 | |
| 317 // Returns the url for an html page just containing one link to the simple | |
| 318 // page mentioned above. | |
| 319 std::wstring GetLinkPageUrl() { | |
| 320 return GetTestUrl(L"link.html"); | |
| 321 } | |
| 322 | |
| 323 // Returns the title of the html page at |GetLinkPageUrl()|. | |
| 324 std::wstring GetLinkPageTitle() { | |
| 325 return L"link"; | |
| 326 } | |
| 327 | |
| 328 // Returns the url for an html page containing several anchors pointing | |
| 329 // to different parts of the page. |index| specifies what fragment to | |
| 330 // append to the url. If zero, no fragment is appended. The highest fragment | |
| 331 // is #a4. | |
| 332 std::wstring GetAnchorPageUrl(int index) { | |
| 333 DCHECK_LT(index, 5); | |
| 334 std::wstring base_name = L"anchor.html"; | |
| 335 if (index > 0) | |
| 336 base_name += std::wstring(L"#a") + base::IntToString16(index); | |
| 337 return GetTestUrl(base_name); | |
| 338 } | |
| 339 | |
| 340 // Returns the title of the html page at |GetAnchorPageUrl()|. | |
| 341 std::wstring GetAnchorPageTitle() { | |
| 342 return L"Chrome Frame Test"; | |
| 343 } | |
| 344 | |
| 345 // Returns the url for an html page that will, when clicked, open a new window | |
| 346 // to |target|. | |
| 347 std::wstring GetWindowOpenUrl(const wchar_t* target) { | |
| 348 return GetTestUrl(std::wstring(L"window_open.html?").append(target)); | |
| 349 } | |
| 350 | |
| 351 // Returns the title of the html page at |GetWindowOpenUrl()|. | |
| 352 std::wstring GetWindowOpenTitle() { | |
| 353 return L"window open"; | |
| 354 } | |
| 355 | |
| 356 protected: | |
| 357 CloseIeAtEndOfScope last_resort_close_ie_; | |
| 358 chrome_frame_test::TimedMsgLoop loop_; | |
| 359 testing::StrictMock<MockIEEventSink> ie_mock_; | |
| 360 testing::StrictMock<MockWebServer> server_mock_; | |
| 361 scoped_refptr<HungCOMCallDetector> hung_call_detector_; | |
| 362 private: | |
| 363 DISALLOW_COPY_AND_ASSIGN(MockIEEventSinkTest); | |
| 364 }; | |
| 365 | |
| 366 } // namespace chrome_frame_test | |
| 367 | |
| 368 #endif // CHROME_FRAME_TEST_MOCK_IE_EVENT_SINK_TEST_H_ | |
| OLD | NEW |