// Copyright (c) 2011 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 #include "base/rand_util.h" #include "chrome/browser/webdata/autofill_table.h" #include "chrome/browser/webdata/web_database.h" #include "chrome/common/url_constants.h" #include "chrome/common/chrome_constants.h" #include "chrome_frame/test/mock_ie_event_sink_actions.h" #include "chrome_frame/test/mock_ie_event_sink_test.h" using testing::_; namespace chrome_frame_test { class DeleteBrowsingHistoryTest : public MockIEEventSinkTest, public testing::Test { public: DeleteBrowsingHistoryTest() {} virtual void SetUp() { // We will use the OnAccLoad event to monitor page loads, so we ignore // these. ie_mock_.ExpectAnyNavigations(); ie_mock2_.ExpectAnyNavigations(); ie_mock3_.ExpectAnyNavigations(); EXPECT_CALL(acc_observer_, OnAccDocLoad(_)).Times(testing::AnyNumber()); // Use a random image_path to ensure that a prior run does not // interfere with our expectations about caching. image_path_ = L"/" + RandomChars(32); topHtml = "" "" "" "" "
" "" "" "
" "\"Blank" "This is some text."; } protected: std::wstring image_path_; std::string topHtml; testing::NiceMock acc_observer_; MockWindowObserver delete_browsing_history_window_observer_mock_; MockObjectWatcherDelegate ie_process_exit_watcher_mock_; testing::StrictMock ie_mock2_; testing::StrictMock ie_mock3_; // Returns a string of |count| lowercase random characters. static std::wstring RandomChars(int count) { srand(static_cast(time(NULL))); std::wstring str; for (int i = 0; i < count; ++i) str += L'a' + base::RandInt(0, 25); return str; } }; namespace { const wchar_t* kFormFieldName = L"username"; const wchar_t* kFormFieldValue = L"test_username"; const char* kHtmlHttpHeaders = "HTTP/1.1 200 OK\r\n" "Connection: close\r\n" "Content-Type: text/html\r\n"; const char* kFormResultHtml = "" "Nice work."; const char* kBlankPngResponse[] = { "HTTP/1.1 200 OK\r\n" "Connection: close\r\n" "Content-Type: image/png\r\n" "Cache-Control: max-age=3600, must-revalidate\r\n", "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52" "\x00\x00\x00\x01\x00\x00\x00\x01\x01\x03\x00\x00\x00\x25\xdb\x56" "\xca\x00\x00\x00\x03\x50\x4c\x54\x45\x00\x00\x00\xa7\x7a\x3d\xda" "\x00\x00\x00\x01\x74\x52\x4e\x53\x00\x40\xe6\xd8\x66\x00\x00\x00" "\x0a\x49\x44\x41\x54\x08\xd7\x63\x60\x00\x00\x00\x02\x00\x01\xe2" "\x21\xbc\x33\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82"}; const size_t kBlankPngFileLength = 95; } // anonymous namespace // Looks up |element_name| in the Chrome form data DB and ensures that the // results match |matcher|. ACTION_P2(ExpectFormValuesForElementNameMatch, element_name, matcher) { FilePath profile_path( chrome_frame_test::GetProfilePath(kIexploreProfileName) .Append(L"Default").Append(chrome::kWebDataFilename)); WebDatabase web_database; sql::InitStatus init_status = web_database.Init(profile_path); EXPECT_EQ(sql::INIT_OK, init_status); if (init_status == sql::INIT_OK) { std::vector values; web_database.GetAutofillTable()->GetFormValuesForElementName( element_name, L"", &values, 9999); EXPECT_THAT(values, matcher); } } // Launch |ie_mock| and navigate it to |url|. ACTION_P2(LaunchThisIEAndNavigate, ie_mock, url) { EXPECT_HRESULT_SUCCEEDED(ie_mock->event_sink()->LaunchIEAndNavigate(url, ie_mock)); } // Listens for OnAccLoad and OnLoad events for an IE instance and // sends a single signal once both have been received. // // Allows tests to wait for both events to occur irrespective of their relative // ordering. class PageLoadHelper { public: explicit PageLoadHelper(testing::StrictMock* ie_mock) : received_acc_load_(false), received_on_load_(false), ie_mock_(ie_mock) { EXPECT_CALL(*ie_mock_, OnLoad(_, _)) .Times(testing::AnyNumber()) .WillRepeatedly(testing::InvokeWithoutArgs( this, &PageLoadHelper::HandleOnLoad)); EXPECT_CALL(acc_observer_, OnAccDocLoad(_)) .Times(testing::AnyNumber()) .WillRepeatedly(testing::Invoke(this, &PageLoadHelper::HandleAccLoad)); } void HandleAccLoad(HWND hwnd) { ReconcileHwnds(hwnd, &acc_loaded_hwnds_, &on_loaded_hwnds_); } void HandleOnLoad() { HWND hwnd = ie_mock_->event_sink()->GetRendererWindow(); ReconcileHwnds(hwnd, &on_loaded_hwnds_, &acc_loaded_hwnds_); } MOCK_METHOD0(OnLoadComplete, void()); private: void ReconcileHwnds(HWND signaled_hwnd, std::set* signaled_hwnd_set, std::set* other_hwnd_set) { if (other_hwnd_set->erase(signaled_hwnd) != 0) { OnLoadComplete(); } else { signaled_hwnd_set->insert(signaled_hwnd); } } std::set acc_loaded_hwnds_; std::set on_loaded_hwnds_; bool received_acc_load_; bool received_on_load_; testing::StrictMock* ie_mock_; testing::NiceMock acc_observer_; }; TEST_F(DeleteBrowsingHistoryTest, DISABLED_CFDeleteBrowsingHistory) { if (GetInstalledIEVersion() < IE_8) { LOG(ERROR) << "Test does not apply to IE versions < 8."; return; } PageLoadHelper load_helper(&ie_mock_); PageLoadHelper load_helper2(&ie_mock2_); PageLoadHelper load_helper3(&ie_mock3_); delete_browsing_history_window_observer_mock_.WatchWindow( "Delete Browsing History", ""); // For some reason, this page is occasionally being cached, so we randomize // its name to ensure that, at least the first time we request it, it is // retrieved. std::wstring top_name = RandomChars(32); std::wstring top_url = server_mock_.Resolve(top_name); std::wstring top_path = L"/" + top_name; // Even still, it might not be hit the second or third time, so let's just // not worry about how often or whether it's called EXPECT_CALL(server_mock_, Get(_, testing::StrEq(top_path), _)) .Times(testing::AnyNumber()) .WillRepeatedly(SendFast(kHtmlHttpHeaders, topHtml)); testing::InSequence expect_in_sequence_for_scope; // First launch will hit the server, requesting top.html and then image_path_ EXPECT_CALL(server_mock_, Get(_, testing::StrEq(image_path_), _)) .WillOnce(SendFast(kBlankPngResponse[0], std::string(kBlankPngResponse[1], kBlankPngFileLength))); // top.html contains a form. Fill in the username field and submit, causing // the value to be stored in Chrome's form data DB. EXPECT_CALL(load_helper, OnLoadComplete()) .WillOnce(testing::DoAll( AccLeftClickInRenderer(&ie_mock_, AccObjectMatcher(L"username")), PostCharMessagesToRenderer(&ie_mock_, WideToASCII(kFormFieldValue)), AccLeftClickInRenderer(&ie_mock_, AccObjectMatcher(L"Submit")))); EXPECT_CALL(server_mock_, Post(_, testing::StrEq(L"/form"), _)) .WillOnce(SendFast(kHtmlHttpHeaders, kFormResultHtml)); // OnLoad of the result page from form submission. Now close the browser. EXPECT_CALL(load_helper, OnLoadComplete()) .WillOnce(testing::DoAll( WatchRendererProcess(&ie_process_exit_watcher_mock_, &ie_mock_), CloseBrowserMock(&ie_mock_))); EXPECT_CALL(ie_mock_, OnQuit()); // Wait until the process is gone, so that the Chrome databases are unlocked. // Verify that the submitted username is in the database, then launch a new // IE instance. EXPECT_CALL(ie_process_exit_watcher_mock_, OnObjectSignaled(_)) .WillOnce(testing::DoAll( ExpectFormValuesForElementNameMatch( kFormFieldName, testing::Contains(kFormFieldValue)), LaunchThisIEAndNavigate(&ie_mock2_, top_url))); // Second launch won't load the image due to the cache. // We do the delete private data twice, each time toggling the state of the // 'Delete form data' and 'Delete temporary files' options. // That's because we have no way to know their initial states. Using this, // trick we are guaranteed to run it exactly once with each option turned on. // Running it once with the option turned off is harmless. // Proceed to open up the "Safety" menu for the first time through the loop. EXPECT_CALL(load_helper2, OnLoadComplete()) .WillOnce(AccDoDefaultActionInBrowser(&ie_mock2_, AccObjectMatcher(L"Safety"))); // Store the dialog and progress_bar HWNDs for each iteration // in order to distinguish between the OnClose of each. HWND dialog[] = {NULL, NULL}; HWND progress_bar[] = {NULL, NULL}; for (int i = 0; i < 2; ++i) { // Watch for the popup menu, click 'Delete Browsing History...' EXPECT_CALL(acc_observer_, OnMenuPopup(_)) .WillOnce( AccLeftClick(AccObjectMatcher(L"Delete Browsing History...*"))); // When it shows up, toggle the options and click "Delete". EXPECT_CALL(delete_browsing_history_window_observer_mock_, OnWindowOpen(_)) .WillOnce(testing::DoAll( testing::SaveArg<0>(&dialog[i]), AccLeftClick(AccObjectMatcher(L"Temporary Internet files")), AccLeftClick(AccObjectMatcher(L"Form data")), AccLeftClick(AccObjectMatcher(L"Delete")))); // The configuration dialog closes. // This is not reliably ordered with respect to the following OnWindowOpen. // Specifying 'AnyNumber' of times allows us to disregard it, although we // can't avoid receiving the call. EXPECT_CALL(delete_browsing_history_window_observer_mock_, OnWindowClose(testing::Eq(testing::ByRef(dialog[i])))) .Times(testing::AnyNumber()); // The progress dialog that pops up has the same caption. EXPECT_CALL(delete_browsing_history_window_observer_mock_, OnWindowOpen(_)).WillOnce(testing::SaveArg<0>(&progress_bar[i])); // Watch for it to go away, then either do the "Delete History" again or // close the browser. // In either case, validate the contents of the renderer to ensure that // we didn't cause Chrome to crash. if (i == 0) { EXPECT_CALL(delete_browsing_history_window_observer_mock_, OnWindowClose(testing::Eq(testing::ByRef(progress_bar[i])))) .WillOnce(testing::DoAll( AccExpectInRenderer(&ie_mock2_, AccObjectMatcher(L"Blank image.")), AccDoDefaultActionInBrowser(&ie_mock2_, AccObjectMatcher(L"Safety")))); } else { EXPECT_CALL(delete_browsing_history_window_observer_mock_, OnWindowClose(testing::Eq(testing::ByRef(progress_bar[i])))) .WillOnce(testing::DoAll( AccExpectInRenderer(&ie_mock2_, AccObjectMatcher(L"Blank image.")), WatchRendererProcess(&ie_process_exit_watcher_mock_, &ie_mock2_), CloseBrowserMock(&ie_mock2_))); } } EXPECT_CALL(ie_mock2_, OnQuit()); // When the process is actually exited, and the DB has been released, verify // that the remembered form data is not in the form data DB. EXPECT_CALL(ie_process_exit_watcher_mock_, OnObjectSignaled(_)) .WillOnce(testing::DoAll( ExpectFormValuesForElementNameMatch( kFormFieldName, testing::Not(testing::Contains(kFormFieldValue))), LaunchThisIEAndNavigate(&ie_mock3_, top_url))); // Now that the cache is cleared, final session should load the image from the // server. EXPECT_CALL(server_mock_, Get(_, testing::StrEq(image_path_), _)) .WillOnce( SendFast(kBlankPngResponse[0], std::string(kBlankPngResponse[1], kBlankPngFileLength))); EXPECT_CALL(load_helper3, OnLoadComplete()) .WillOnce(CloseBrowserMock(&ie_mock3_)); EXPECT_CALL(ie_mock3_, OnQuit()) .WillOnce(QUIT_LOOP(loop_)); // Start it up. Everything else is triggered as mock actions. ASSERT_HRESULT_SUCCEEDED( ie_mock_.event_sink()->LaunchIEAndNavigate(top_url, &ie_mock_)); // 3 navigations + 2 invocations of delete browser history == 5 loop_.RunFor(kChromeFrameLongNavigationTimeoutInSeconds * 5); } } // namespace chrome_frame_test