blob: ee4cfcf5b253a8f862a7394c43cbffd0986c176e [file] [log] [blame]
// Copyright (c) 2012 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 <string>
#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 =
"<html><head>"
"<meta http-equiv=\"x-ua-compatible\" content=\"chrome=1\" />"
"</head>"
"<body>"
"<form method=\"POST\" action=\"/form\">"
"<input title=\"username\" type=\"text\" name=\"username\" />"
"<input type=\"submit\" title=\"Submit\" name=\"Submit\" />"
"</form>"
"<img alt=\"Blank image.\" src=\"" + WideToASCII(image_path_) + "\" />"
"This is some text.</body></html>";
}
protected:
std::wstring image_path_;
std::string topHtml;
testing::NiceMock<MockAccEventObserver> acc_observer_;
MockWindowObserver delete_browsing_history_window_observer_mock_;
MockObjectWatcherDelegate ie_process_exit_watcher_mock_;
testing::StrictMock<MockIEEventSink> ie_mock2_;
testing::StrictMock<MockIEEventSink> ie_mock3_;
// Returns a string of |count| lowercase random characters.
static std::wstring RandomChars(int count) {
srand(static_cast<unsigned int>(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 =
"<html><head><meta http-equiv=\"x-ua-compatible\" content=\"chrome=1\" />"
"</head><body>Nice work.</body></html>";
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 root_path;
GetChromeFrameProfilePath(kIexploreProfileName, &root_path);
FilePath profile_path(
root_path.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<string16> 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<MockIEEventSink>* 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<HWND>* signaled_hwnd_set,
std::set<HWND>* other_hwnd_set) {
if (other_hwnd_set->erase(signaled_hwnd) != 0) {
OnLoadComplete();
} else {
signaled_hwnd_set->insert(signaled_hwnd);
}
}
std::set<HWND> acc_loaded_hwnds_;
std::set<HWND> on_loaded_hwnds_;
bool received_acc_load_;
bool received_on_load_;
testing::StrictMock<MockIEEventSink>* ie_mock_;
testing::NiceMock<MockAccEventObserver> 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(kChromeFrameLongNavigationTimeout * 5);
}
} // namespace chrome_frame_test