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 #include "chrome_frame/test/mock_ie_event_sink_test.h" 6 7 #include <sstream> 8 9 #include "base/strings/utf_string_conversions.h" 10 #include "base/win/scoped_variant.h" 11 #include "chrome_frame/test/mock_ie_event_sink_actions.h" 12 13 // Needed for CreateFunctor. 14 #define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING 15 #include "testing/gmock_mutant.h" 16 17 using testing::_; 18 using testing::Cardinality; 19 using testing::Exactly; 20 using testing::ExpectationSet; 21 using testing::InSequence; 22 using testing::StrCaseEq; 23 24 namespace chrome_frame_test { 25 26 // MockIEEventSink methods 27 void MockIEEventSink::OnDocumentComplete(IDispatch* dispatch, VARIANT* url) { 28 if (!event_sink_->IsCFRendering()) { 29 HWND renderer_window = event_sink_->GetRendererWindowSafe(); 30 if (renderer_window) { 31 ::NotifyWinEvent(IA2_EVENT_DOCUMENT_LOAD_COMPLETE, 32 renderer_window, 33 OBJID_CLIENT, 0L); 34 } else { 35 VLOG(1) << "Browser does not have renderer window"; 36 } 37 OnLoad(IN_IE, V_BSTR(url)); 38 } 39 } 40 41 ExpectationSet MockIEEventSink::ExpectNavigationCardinality( 42 const std::wstring& url, Cardinality before_cardinality, 43 Cardinality complete_cardinality) { 44 ExpectationSet navigation; 45 if (url.empty()) { 46 navigation += EXPECT_CALL(*this, OnBeforeNavigate2(_, _, _, _, _, _, _)) 47 .Times(before_cardinality).RetiresOnSaturation(); 48 } else { 49 navigation += EXPECT_CALL(*this, OnBeforeNavigate2(_, 50 testing::Field(&VARIANT::bstrVal, 51 StrCaseEq(url)), _, _, _, _, _)) 52 .Times(before_cardinality).RetiresOnSaturation(); 53 } 54 55 // Hack: OnFileDownload may occur zero or once (for reasons not understood) 56 // before each OnNavigateComplete2 which causes problems for tests expecting 57 // things in sequence. To redress this, expectations which allow multiple 58 // calls are split into expect statements expecting exactly one call or at 59 // most one call. 60 // TODO(kkania): Consider avoiding this problem by creating a mock without 61 // the OnFileDownload call or by removing the dependency of some tests on 62 // InSequence. 63 LOG_IF(WARNING, complete_cardinality.ConservativeUpperBound() > 1000) 64 << "Cardinality upper bound may be too great to be split up into single " 65 "expect statements. If you do not require this navigation to be in " 66 "sequence, do not call this method."; 67 int call_count = 0; 68 InSequence expect_in_sequence_for_scope; 69 while (!complete_cardinality.IsSaturatedByCallCount(call_count)) { 70 navigation += EXPECT_CALL(*this, OnFileDownload(_, _)) 71 .Times(testing::AtMost(1)) 72 .WillOnce(testing::SetArgumentPointee<1>(VARIANT_TRUE)) 73 .RetiresOnSaturation(); 74 75 Cardinality split_complete_cardinality = testing::Exactly(1); 76 if (complete_cardinality.IsSatisfiedByCallCount(call_count)) 77 split_complete_cardinality = testing::AtMost(1); 78 79 if (url.empty()) { 80 navigation += EXPECT_CALL(*this, OnNavigateComplete2(_, _)) 81 .Times(split_complete_cardinality) 82 .RetiresOnSaturation(); 83 } else { 84 navigation += EXPECT_CALL(*this, OnNavigateComplete2(_, 85 testing::Field(&VARIANT::bstrVal, 86 StrCaseEq(url)))) 87 .Times(split_complete_cardinality) 88 .RetiresOnSaturation(); 89 } 90 call_count++; 91 } 92 return navigation; 93 } 94 95 void MockIEEventSink::ExpectNavigation(bool is_cf, const std::wstring& url) { 96 InSequence expect_in_sequence_for_scope; 97 if (is_cf || GetInstalledIEVersion() == IE_9) { 98 ExpectNavigationCardinality(url, Exactly(1), testing::Between(1, 2)); 99 } else { 100 ExpectNavigationCardinality(url, Exactly(1), Exactly(1)); 101 } 102 } 103 104 void MockIEEventSink::ExpectNavigationOptionalBefore(bool is_cf, 105 const std::wstring& url) { 106 InSequence expect_in_sequence_for_scope; 107 if (is_cf && GetInstalledIEVersion() == IE_6) { 108 ExpectNavigationCardinality(url, testing::AtMost(1), 109 testing::Between(1, 2)); 110 } else { 111 ExpectNavigation(is_cf, url); 112 } 113 } 114 115 void MockIEEventSink::ExpectJavascriptWindowOpenNavigation( 116 bool parent_cf, bool new_window_cf, const std::wstring& url) { 117 if (parent_cf) { 118 InSequence expect_in_sequence_for_scope; 119 ExpectNavigation(IN_CF, L""); 120 ExpectNavigationCardinality(L"", testing::AtMost(1), 121 testing::Between(1, 2)); 122 } else { 123 if (new_window_cf) { 124 ExpectNavigationCardinality(url, testing::AtMost(1), testing::AtMost(1)); 125 // Sometimes an extra load occurs here for some reason. 126 EXPECT_CALL(*this, OnLoad(IN_IE, StrCaseEq(url))) 127 .Times(testing::AtMost(1)); 128 ExpectNavigationCardinality(url, testing::AtMost(1), 129 testing::Between(1, 2)); 130 } else { 131 ExpectNavigation(IN_IE, url); 132 } 133 } 134 } 135 136 void MockIEEventSink::ExpectNewWindow(MockIEEventSink* new_window_mock) { 137 DCHECK(new_window_mock); 138 139 // IE8 seems to fire one of these events based on version. 140 EXPECT_CALL(*this, OnNewWindow2(_, _)) 141 .Times(testing::AtMost(1)); 142 EXPECT_CALL(*this, OnNewWindow3(_, _, _, _, _)) 143 .Times(testing::AtMost(1)); 144 145 EXPECT_CALL(*this, OnNewBrowserWindow(_, _)) 146 .WillOnce(testing::WithArgs<0>(testing::Invoke(testing::CreateFunctor( 147 new_window_mock, &MockIEEventSink::Attach)))); 148 } 149 150 void MockIEEventSink::ExpectAnyNavigations() { 151 EXPECT_CALL(*this, OnBeforeNavigate2(_, _, _, _, _, _, _)) 152 .Times(testing::AnyNumber()); 153 EXPECT_CALL(*this, OnFileDownload(VARIANT_TRUE, _)) 154 .Times(testing::AnyNumber()); 155 EXPECT_CALL(*this, OnNavigateComplete2(_, _)) 156 .Times(testing::AnyNumber()); 157 } 158 159 void MockIEEventSink::ExpectDocumentReadystate(int ready_state) { 160 base::win::ScopedComPtr<IWebBrowser2> browser(event_sink_->web_browser2()); 161 EXPECT_TRUE(browser != NULL); 162 if (browser) { 163 base::win::ScopedComPtr<IDispatch> document; 164 browser->get_Document(document.Receive()); 165 EXPECT_TRUE(document != NULL); 166 if (document) { 167 DISPPARAMS params = { 0 }; 168 base::win::ScopedVariant result; 169 EXPECT_HRESULT_SUCCEEDED(document->Invoke(DISPID_READYSTATE, IID_NULL, 170 LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, 171 result.Receive(), NULL, NULL)); 172 EXPECT_EQ(VT_I4, result.type()); 173 if (result.type() == VT_I4) { 174 EXPECT_EQ(ready_state, static_cast<int>(V_I4(&result))); 175 } 176 } 177 } 178 } 179 180 // MockIEEventSinkTest methods 181 MockIEEventSinkTest::MockIEEventSinkTest() 182 : server_mock_(1337, ASCIIToWide(chrome_frame_test::GetLocalIPv4Address()), 183 GetTestDataFolder()) { 184 loop_.set_snapshot_on_timeout(true); 185 EXPECT_CALL(server_mock_, Get(_, StrCaseEq(L"/favicon.ico"), _)) 186 .WillRepeatedly(SendFast("HTTP/1.1 404 Not Found", "")); 187 } 188 189 MockIEEventSinkTest::MockIEEventSinkTest(int port, const std::wstring& address, 190 const base::FilePath& root_dir) 191 : server_mock_(port, address, root_dir) { 192 loop_.set_snapshot_on_timeout(true); 193 EXPECT_CALL(server_mock_, Get(_, StrCaseEq(L"/favicon.ico"), _)) 194 .WillRepeatedly(SendFast("HTTP/1.1 404 Not Found", "")); 195 } 196 197 void MockIEEventSinkTest::LaunchIEAndNavigate(const std::wstring& url) { 198 LaunchIENavigateAndLoop(url, kChromeFrameLongNavigationTimeout); 199 } 200 201 void MockIEEventSinkTest::LaunchIENavigateAndLoop(const std::wstring& url, 202 base::TimeDelta timeout) { 203 if (GetInstalledIEVersion() >= IE_8) { 204 chrome_frame_test::ClearIESessionHistory(); 205 } 206 hung_call_detector_ = HungCOMCallDetector::Setup(ceil(timeout.InSecondsF())); 207 EXPECT_TRUE(hung_call_detector_ != NULL); 208 209 IEEventSink::SetAbnormalShutdown(false); 210 211 EXPECT_CALL(ie_mock_, OnQuit()) 212 .WillOnce(QUIT_LOOP(loop_)); 213 214 HRESULT hr = ie_mock_.event_sink()->LaunchIEAndNavigate(url, &ie_mock_); 215 ASSERT_HRESULT_SUCCEEDED(hr); 216 if (hr != S_FALSE) { 217 ASSERT_TRUE(ie_mock_.event_sink()->web_browser2() != NULL); 218 loop_.RunFor(timeout); 219 } 220 221 if (hung_call_detector_) { 222 IEEventSink::SetAbnormalShutdown(hung_call_detector_->is_hung()); 223 hung_call_detector_->TearDown(); 224 } 225 } 226 227 base::FilePath MockIEEventSinkTest::GetTestFilePath( 228 const std::wstring& relative_path) { 229 return server_mock_.root_dir().Append(relative_path); 230 } 231 232 std::wstring MockIEEventSinkTest::GetTestUrl( 233 const std::wstring& relative_path) { 234 return server_mock_.Resolve(relative_path.c_str()); 235 } 236 237 } // namespace chrome_frame_test 238