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 <string> 6 7 #include "base/rand_util.h" 8 #include "chrome/common/url_constants.h" 9 #include "chrome_frame/test/mock_ie_event_sink_actions.h" 10 #include "chrome_frame/test/mock_ie_event_sink_test.h" 11 #include "components/autofill/core/browser/webdata/autofill_table.h" 12 #include "components/webdata/common/web_database.h" 13 #include "components/webdata/common/webdata_constants.h" 14 15 using testing::_; 16 17 namespace chrome_frame_test { 18 19 class DeleteBrowsingHistoryTest 20 : public MockIEEventSinkTest, 21 public testing::Test { 22 public: 23 DeleteBrowsingHistoryTest() {} 24 25 virtual void SetUp() { 26 // We will use the OnAccLoad event to monitor page loads, so we ignore 27 // these. 28 ie_mock_.ExpectAnyNavigations(); 29 ie_mock2_.ExpectAnyNavigations(); 30 ie_mock3_.ExpectAnyNavigations(); 31 EXPECT_CALL(acc_observer_, OnAccDocLoad(_)).Times(testing::AnyNumber()); 32 33 // Use a random image_path to ensure that a prior run does not 34 // interfere with our expectations about caching. 35 image_path_ = L"/" + RandomChars(32); 36 topHtml = 37 "<html><head>" 38 "<meta http-equiv=\"x-ua-compatible\" content=\"chrome=1\" />" 39 "</head>" 40 "<body>" 41 "<form method=\"POST\" action=\"/form\">" 42 "<input title=\"username\" type=\"text\" name=\"username\" />" 43 "<input type=\"submit\" title=\"Submit\" name=\"Submit\" />" 44 "</form>" 45 "<img alt=\"Blank image.\" src=\"" + WideToASCII(image_path_) + "\" />" 46 "This is some text.</body></html>"; 47 } 48 49 protected: 50 std::wstring image_path_; 51 std::string topHtml; 52 53 testing::NiceMock<MockAccEventObserver> acc_observer_; 54 MockWindowObserver delete_browsing_history_window_observer_mock_; 55 MockObjectWatcherDelegate ie_process_exit_watcher_mock_; 56 57 testing::StrictMock<MockIEEventSink> ie_mock2_; 58 testing::StrictMock<MockIEEventSink> ie_mock3_; 59 60 // Returns a string of |count| lowercase random characters. 61 static std::wstring RandomChars(int count) { 62 srand(static_cast<unsigned int>(time(NULL))); 63 std::wstring str; 64 for (int i = 0; i < count; ++i) 65 str += L'a' + base::RandInt(0, 25); 66 return str; 67 } 68 }; 69 70 namespace { 71 72 const wchar_t* kFormFieldName = L"username"; 73 const wchar_t* kFormFieldValue = L"test_username"; 74 75 const char* kHtmlHttpHeaders = 76 "HTTP/1.1 200 OK\r\n" 77 "Connection: close\r\n" 78 "Content-Type: text/html\r\n"; 79 const char* kFormResultHtml = 80 "<html><head><meta http-equiv=\"x-ua-compatible\" content=\"chrome=1\" />" 81 "</head><body>Nice work.</body></html>"; 82 const char* kBlankPngResponse[] = { 83 "HTTP/1.1 200 OK\r\n" 84 "Connection: close\r\n" 85 "Content-Type: image/png\r\n" 86 "Cache-Control: max-age=3600, must-revalidate\r\n", 87 "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" 88 "\x00\x00\x00\x01\x00\x00\x00\x01\x01\x03\x00\x00\x00\x25\xdb\x56" 89 "\xca\x00\x00\x00\x03\x50\x4c\x54\x45\x00\x00\x00\xa7\x7a\x3d\xda" 90 "\x00\x00\x00\x01\x74\x52\x4e\x53\x00\x40\xe6\xd8\x66\x00\x00\x00" 91 "\x0a\x49\x44\x41\x54\x08\xd7\x63\x60\x00\x00\x00\x02\x00\x01\xe2" 92 "\x21\xbc\x33\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82"}; 93 94 const size_t kBlankPngFileLength = 95; 95 } // anonymous namespace 96 97 // Looks up |element_name| in the Chrome form data DB and ensures that the 98 // results match |matcher|. 99 ACTION_P2(ExpectFormValuesForElementNameMatch, element_name, matcher) { 100 base::FilePath root_path; 101 GetChromeFrameProfilePath(kIexploreProfileName, &root_path); 102 base::FilePath profile_path( 103 root_path.Append(L"Default").Append(kWebDataFilename)); 104 105 autofill::AutofillTable autofill_table("en-US"); 106 WebDatabase web_database; 107 web_database.AddTable(&autofill_table); 108 sql::InitStatus init_status = web_database.Init(profile_path); 109 EXPECT_EQ(sql::INIT_OK, init_status); 110 111 if (init_status == sql::INIT_OK) { 112 std::vector<string16> values; 113 autofill_table.GetFormValuesForElementName( 114 element_name, L"", &values, 9999); 115 EXPECT_THAT(values, matcher); 116 } 117 } 118 119 // Launch |ie_mock| and navigate it to |url|. 120 ACTION_P2(LaunchThisIEAndNavigate, ie_mock, url) { 121 EXPECT_HRESULT_SUCCEEDED(ie_mock->event_sink()->LaunchIEAndNavigate(url, 122 ie_mock)); 123 } 124 125 // Listens for OnAccLoad and OnLoad events for an IE instance and 126 // sends a single signal once both have been received. 127 // 128 // Allows tests to wait for both events to occur irrespective of their relative 129 // ordering. 130 class PageLoadHelper { 131 public: 132 explicit PageLoadHelper(testing::StrictMock<MockIEEventSink>* ie_mock) 133 : received_acc_load_(false), 134 received_on_load_(false), 135 ie_mock_(ie_mock) { 136 EXPECT_CALL(*ie_mock_, OnLoad(_, _)) 137 .Times(testing::AnyNumber()) 138 .WillRepeatedly(testing::InvokeWithoutArgs( 139 this, &PageLoadHelper::HandleOnLoad)); 140 EXPECT_CALL(acc_observer_, OnAccDocLoad(_)) 141 .Times(testing::AnyNumber()) 142 .WillRepeatedly(testing::Invoke(this, &PageLoadHelper::HandleAccLoad)); 143 } 144 145 void HandleAccLoad(HWND hwnd) { 146 ReconcileHwnds(hwnd, &acc_loaded_hwnds_, &on_loaded_hwnds_); 147 } 148 149 void HandleOnLoad() { 150 HWND hwnd = ie_mock_->event_sink()->GetRendererWindow(); 151 ReconcileHwnds(hwnd, &on_loaded_hwnds_, &acc_loaded_hwnds_); 152 } 153 154 MOCK_METHOD0(OnLoadComplete, void()); 155 156 private: 157 void ReconcileHwnds(HWND signaled_hwnd, 158 std::set<HWND>* signaled_hwnd_set, 159 std::set<HWND>* other_hwnd_set) { 160 if (other_hwnd_set->erase(signaled_hwnd) != 0) { 161 OnLoadComplete(); 162 } else { 163 signaled_hwnd_set->insert(signaled_hwnd); 164 } 165 } 166 std::set<HWND> acc_loaded_hwnds_; 167 std::set<HWND> on_loaded_hwnds_; 168 bool received_acc_load_; 169 bool received_on_load_; 170 testing::StrictMock<MockIEEventSink>* ie_mock_; 171 testing::NiceMock<MockAccEventObserver> acc_observer_; 172 }; 173 174 TEST_F(DeleteBrowsingHistoryTest, DISABLED_CFDeleteBrowsingHistory) { 175 if (GetInstalledIEVersion() < IE_8) { 176 LOG(ERROR) << "Test does not apply to IE versions < 8."; 177 return; 178 } 179 180 PageLoadHelper load_helper(&ie_mock_); 181 PageLoadHelper load_helper2(&ie_mock2_); 182 PageLoadHelper load_helper3(&ie_mock3_); 183 184 delete_browsing_history_window_observer_mock_.WatchWindow( 185 "Delete Browsing History", ""); 186 187 // For some reason, this page is occasionally being cached, so we randomize 188 // its name to ensure that, at least the first time we request it, it is 189 // retrieved. 190 std::wstring top_name = RandomChars(32); 191 std::wstring top_url = server_mock_.Resolve(top_name); 192 std::wstring top_path = L"/" + top_name; 193 194 // Even still, it might not be hit the second or third time, so let's just 195 // not worry about how often or whether it's called 196 EXPECT_CALL(server_mock_, Get(_, testing::StrEq(top_path), _)) 197 .Times(testing::AnyNumber()) 198 .WillRepeatedly(SendFast(kHtmlHttpHeaders, topHtml)); 199 200 testing::InSequence expect_in_sequence_for_scope; 201 202 // First launch will hit the server, requesting top.html and then image_path_ 203 EXPECT_CALL(server_mock_, Get(_, testing::StrEq(image_path_), _)) 204 .WillOnce(SendFast(kBlankPngResponse[0], 205 std::string(kBlankPngResponse[1], 206 kBlankPngFileLength))); 207 208 // top.html contains a form. Fill in the username field and submit, causing 209 // the value to be stored in Chrome's form data DB. 210 EXPECT_CALL(load_helper, OnLoadComplete()) 211 .WillOnce(testing::DoAll( 212 AccLeftClickInRenderer(&ie_mock_, AccObjectMatcher(L"username")), 213 PostKeyMessagesToRenderer(&ie_mock_, WideToASCII(kFormFieldValue)), 214 AccLeftClickInRenderer(&ie_mock_, AccObjectMatcher(L"Submit")))); 215 216 EXPECT_CALL(server_mock_, Post(_, testing::StrEq(L"/form"), _)) 217 .WillOnce(SendFast(kHtmlHttpHeaders, kFormResultHtml)); 218 219 // OnLoad of the result page from form submission. Now close the browser. 220 EXPECT_CALL(load_helper, OnLoadComplete()) 221 .WillOnce(testing::DoAll( 222 WatchRendererProcess(&ie_process_exit_watcher_mock_, &ie_mock_), 223 CloseBrowserMock(&ie_mock_))); 224 225 EXPECT_CALL(ie_mock_, OnQuit()); 226 227 // Wait until the process is gone, so that the Chrome databases are unlocked. 228 // Verify that the submitted username is in the database, then launch a new 229 // IE instance. 230 EXPECT_CALL(ie_process_exit_watcher_mock_, OnObjectSignaled(_)) 231 .WillOnce(testing::DoAll( 232 ExpectFormValuesForElementNameMatch( 233 kFormFieldName, testing::Contains(kFormFieldValue)), 234 LaunchThisIEAndNavigate(&ie_mock2_, top_url))); 235 236 // Second launch won't load the image due to the cache. 237 238 // We do the delete private data twice, each time toggling the state of the 239 // 'Delete form data' and 'Delete temporary files' options. 240 // That's because we have no way to know their initial states. Using this, 241 // trick we are guaranteed to run it exactly once with each option turned on. 242 // Running it once with the option turned off is harmless. 243 244 // Proceed to open up the "Safety" menu for the first time through the loop. 245 EXPECT_CALL(load_helper2, OnLoadComplete()) 246 .WillOnce(AccDoDefaultActionInBrowser(&ie_mock2_, 247 AccObjectMatcher(L"Safety"))); 248 249 // Store the dialog and progress_bar HWNDs for each iteration 250 // in order to distinguish between the OnClose of each. 251 HWND dialog[] = {NULL, NULL}; 252 HWND progress_bar[] = {NULL, NULL}; 253 254 for (int i = 0; i < 2; ++i) { 255 // Watch for the popup menu, click 'Delete Browsing History...' 256 EXPECT_CALL(acc_observer_, OnMenuPopup(_)) 257 .WillOnce( 258 AccLeftClick(AccObjectMatcher(L"Delete Browsing History...*"))); 259 260 // When it shows up, toggle the options and click "Delete". 261 EXPECT_CALL(delete_browsing_history_window_observer_mock_, OnWindowOpen(_)) 262 .WillOnce(testing::DoAll( 263 testing::SaveArg<0>(&dialog[i]), 264 AccLeftClick(AccObjectMatcher(L"Temporary Internet files")), 265 AccLeftClick(AccObjectMatcher(L"Form data")), 266 AccLeftClick(AccObjectMatcher(L"Delete")))); 267 268 // The configuration dialog closes. 269 // This is not reliably ordered with respect to the following OnWindowOpen. 270 // Specifying 'AnyNumber' of times allows us to disregard it, although we 271 // can't avoid receiving the call. 272 EXPECT_CALL(delete_browsing_history_window_observer_mock_, 273 OnWindowClose(testing::Eq(testing::ByRef(dialog[i])))) 274 .Times(testing::AnyNumber()); 275 276 // The progress dialog that pops up has the same caption. 277 EXPECT_CALL(delete_browsing_history_window_observer_mock_, 278 OnWindowOpen(_)).WillOnce(testing::SaveArg<0>(&progress_bar[i])); 279 280 // Watch for it to go away, then either do the "Delete History" again or 281 // close the browser. 282 // In either case, validate the contents of the renderer to ensure that 283 // we didn't cause Chrome to crash. 284 if (i == 0) { 285 EXPECT_CALL(delete_browsing_history_window_observer_mock_, 286 OnWindowClose(testing::Eq(testing::ByRef(progress_bar[i])))) 287 .WillOnce(testing::DoAll( 288 AccExpectInRenderer(&ie_mock2_, 289 AccObjectMatcher(L"Blank image.")), 290 AccDoDefaultActionInBrowser(&ie_mock2_, 291 AccObjectMatcher(L"Safety")))); 292 } else { 293 EXPECT_CALL(delete_browsing_history_window_observer_mock_, 294 OnWindowClose(testing::Eq(testing::ByRef(progress_bar[i])))) 295 .WillOnce(testing::DoAll( 296 AccExpectInRenderer(&ie_mock2_, 297 AccObjectMatcher(L"Blank image.")), 298 WatchRendererProcess(&ie_process_exit_watcher_mock_, 299 &ie_mock2_), 300 CloseBrowserMock(&ie_mock2_))); 301 } 302 } 303 304 EXPECT_CALL(ie_mock2_, OnQuit()); 305 306 // When the process is actually exited, and the DB has been released, verify 307 // that the remembered form data is not in the form data DB. 308 EXPECT_CALL(ie_process_exit_watcher_mock_, OnObjectSignaled(_)) 309 .WillOnce(testing::DoAll( 310 ExpectFormValuesForElementNameMatch( 311 kFormFieldName, testing::Not(testing::Contains(kFormFieldValue))), 312 LaunchThisIEAndNavigate(&ie_mock3_, top_url))); 313 314 // Now that the cache is cleared, final session should load the image from the 315 // server. 316 EXPECT_CALL(server_mock_, Get(_, testing::StrEq(image_path_), _)) 317 .WillOnce( 318 SendFast(kBlankPngResponse[0], std::string(kBlankPngResponse[1], 319 kBlankPngFileLength))); 320 321 EXPECT_CALL(load_helper3, OnLoadComplete()) 322 .WillOnce(CloseBrowserMock(&ie_mock3_)); 323 324 EXPECT_CALL(ie_mock3_, OnQuit()) 325 .WillOnce(QUIT_LOOP(loop_)); 326 327 // Start it up. Everything else is triggered as mock actions. 328 ASSERT_HRESULT_SUCCEEDED( 329 ie_mock_.event_sink()->LaunchIEAndNavigate(top_url, &ie_mock_)); 330 331 // 3 navigations + 2 invocations of delete browser history == 5 332 loop_.RunFor(kChromeFrameLongNavigationTimeout * 5); 333 } 334 335 } // namespace chrome_frame_test 336