blob: 52b6b6a8e420ca6da0da60311d14fd2a98286f1b [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 <stddef.h>
#include <stdint.h>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_file_util.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_core_service.h"
#include "chrome/browser/download/download_core_service_factory.h"
#include "chrome/browser/download/download_history.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/save_package_file_picker.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/download/public/common/download_item.h"
#include "components/history/core/browser/download_constants.h"
#include "components/history/core/browser/download_row.h"
#include "components/prefs/pref_member.h"
#include "components/prefs/pref_service.h"
#include "components/security_state/core/security_state.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/download_test_observer.h"
#include "content/public/test/test_utils.h"
#include "net/base/filename_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using content::BrowserContext;
using content::BrowserThread;
using content::DownloadManager;
using content::RenderFrameHost;
using content::RenderProcessHost;
using content::WebContents;
using download::DownloadItem;
using testing::ContainsRegex;
using testing::HasSubstr;
namespace {
// Returns file contents with each continuous run of whitespace replaced by a
// single space.
std::string ReadFileAndCollapseWhitespace(const base::FilePath& file_path) {
std::string file_contents;
if (!base::ReadFileToString(file_path, &file_contents)) {
ADD_FAILURE() << "Failed to read \"" << file_path.value() << "\" file.";
return std::string();
}
return base::CollapseWhitespaceASCII(file_contents, false);
}
// Takes a string with "url=(%04d)%s", and replaces that with the length and
// contents of the path the response was saved from, |url|, to match output by
// the SavePageAs logic.
std::string WriteSavedFromPath(const std::string& file_contents,
const GURL& url) {
return base::StringPrintf(file_contents.c_str(), url.spec().length(),
url.spec().c_str());
}
// Waits for an item record in the downloads database to match |filter|. See
// DownloadStoredProperly() below for an example filter.
class DownloadPersistedObserver : public DownloadHistory::Observer {
public:
typedef base::Callback<bool(
DownloadItem* item,
const history::DownloadRow&)> PersistedFilter;
DownloadPersistedObserver(Profile* profile, const PersistedFilter& filter)
: profile_(profile),
filter_(filter),
persisted_(false) {
DownloadCoreServiceFactory::GetForBrowserContext(profile_)
->GetDownloadHistory()
->AddObserver(this);
}
~DownloadPersistedObserver() override {
DownloadCoreService* service =
DownloadCoreServiceFactory::GetForBrowserContext(profile_);
if (service && service->GetDownloadHistory())
service->GetDownloadHistory()->RemoveObserver(this);
}
bool WaitForPersisted() {
if (persisted_)
return true;
base::RunLoop run_loop;
quit_waiting_callback_ = run_loop.QuitClosure();
run_loop.Run();
quit_waiting_callback_ = base::Closure();
return persisted_;
}
void OnDownloadStored(DownloadItem* item,
const history::DownloadRow& info) override {
persisted_ = persisted_ || filter_.Run(item, info);
if (persisted_ && !quit_waiting_callback_.is_null())
quit_waiting_callback_.Run();
}
private:
Profile* profile_;
PersistedFilter filter_;
base::Closure quit_waiting_callback_;
bool persisted_;
DISALLOW_COPY_AND_ASSIGN(DownloadPersistedObserver);
};
// Waits for an item record to be removed from the downloads database.
class DownloadRemovedObserver : public DownloadPersistedObserver {
public:
DownloadRemovedObserver(Profile* profile, int32_t download_id)
: DownloadPersistedObserver(profile, PersistedFilter()),
removed_(false),
download_id_(download_id) {}
~DownloadRemovedObserver() override {}
bool WaitForRemoved() {
if (removed_)
return true;
base::RunLoop run_loop;
quit_waiting_callback_ = run_loop.QuitClosure();
run_loop.Run();
quit_waiting_callback_ = base::Closure();
return removed_;
}
void OnDownloadStored(DownloadItem* item,
const history::DownloadRow& info) override {}
void OnDownloadsRemoved(const DownloadHistory::IdSet& ids) override {
removed_ = ids.find(download_id_) != ids.end();
if (removed_ && !quit_waiting_callback_.is_null())
quit_waiting_callback_.Run();
}
private:
bool removed_;
base::Closure quit_waiting_callback_;
int32_t download_id_;
DISALLOW_COPY_AND_ASSIGN(DownloadRemovedObserver);
};
bool DownloadStoredProperly(const GURL& expected_url,
const base::FilePath& expected_path,
int64_t num_files,
history::DownloadState expected_state,
DownloadItem* item,
const history::DownloadRow& info) {
// This function may be called multiple times for a given test. Returning
// false doesn't necessarily mean that the test has failed or will fail, it
// might just mean that the test hasn't passed yet.
if (!expected_path.empty() && info.target_path != expected_path) {
DVLOG(20) << __FUNCTION__ << " " << info.target_path.value()
<< " != " << expected_path.value();
return false;
}
if (info.url_chain.size() != 1u) {
DVLOG(20) << __FUNCTION__ << " " << info.url_chain.size() << " != 1";
return false;
}
if (info.url_chain[0] != expected_url) {
DVLOG(20) << __FUNCTION__ << " " << info.url_chain[0].spec()
<< " != " << expected_url.spec();
return false;
}
if ((num_files >= 0) && (info.received_bytes != num_files)) {
DVLOG(20) << __FUNCTION__ << " " << num_files
<< " != " << info.received_bytes;
return false;
}
if (info.state != expected_state) {
DVLOG(20) << __FUNCTION__ << " " << info.state << " != " << expected_state;
return false;
}
return true;
}
static const char kAppendedExtension[] = ".html";
// Loosely based on logic in DownloadTestObserver.
class DownloadItemCreatedObserver : public DownloadManager::Observer {
public:
explicit DownloadItemCreatedObserver(DownloadManager* manager)
: manager_(manager) {
manager->AddObserver(this);
}
~DownloadItemCreatedObserver() override {
if (manager_)
manager_->RemoveObserver(this);
}
// Wait for the first download item created after object creation.
// Note that this class provides no protection against the download
// being destroyed between creation and return of WaitForNewDownloadItem();
// the caller must guarantee that in some other fashion.
void WaitForDownloadItem(std::vector<DownloadItem*>* items_seen) {
if (!manager_) {
// The manager went away before we were asked to wait; return
// what we have, even if it's null.
*items_seen = items_seen_;
return;
}
if (items_seen_.empty()) {
base::RunLoop run_loop;
quit_waiting_callback_ = run_loop.QuitClosure();
run_loop.Run();
quit_waiting_callback_ = base::Closure();
}
*items_seen = items_seen_;
return;
}
private:
// DownloadManager::Observer
void OnDownloadCreated(DownloadManager* manager,
DownloadItem* item) override {
DCHECK_EQ(manager, manager_);
items_seen_.push_back(item);
if (!quit_waiting_callback_.is_null())
quit_waiting_callback_.Run();
}
void ManagerGoingDown(DownloadManager* manager) override {
manager_->RemoveObserver(this);
manager_ = NULL;
if (!quit_waiting_callback_.is_null())
quit_waiting_callback_.Run();
}
base::Closure quit_waiting_callback_;
DownloadManager* manager_;
std::vector<DownloadItem*> items_seen_;
DISALLOW_COPY_AND_ASSIGN(DownloadItemCreatedObserver);
};
class SavePageBrowserTest : public InProcessBrowserTest {
public:
SavePageBrowserTest() {}
protected:
void SetUp() override {
ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
content::SetupCrossSiteRedirector(embedded_test_server());
embedded_test_server()->StartAcceptingConnections();
ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_dir_));
InProcessBrowserTest::SetUp();
}
GURL NavigateToMockURL(const std::string& prefix) {
GURL url = embedded_test_server()->GetURL("/save_page/" + prefix + ".htm");
ui_test_utils::NavigateToURL(browser(), url);
return url;
}
// Returns full paths of destination file and directory.
void GetDestinationPaths(const std::string& prefix,
base::FilePath* full_file_name,
base::FilePath* dir,
content::SavePageType save_page_type =
content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML) {
std::string extension =
(save_page_type == content::SAVE_PAGE_TYPE_AS_MHTML) ? ".mht" : ".htm";
*full_file_name = GetSaveDir().AppendASCII(prefix + extension);
*dir = GetSaveDir().AppendASCII(prefix + "_files");
}
WebContents* GetCurrentTab(Browser* browser) const {
WebContents* current_tab =
browser->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(current_tab);
return current_tab;
}
// Returns true if and when there was a single download created, and its url
// is |expected_url|.
bool VerifySavePackageExpectations(
Browser* browser,
const GURL& expected_url) const {
// Generally, there should only be one download item created
// in all of these tests. If it's already here, grab it; if not,
// wait for it to show up.
std::vector<DownloadItem*> items;
DownloadManager* manager =
BrowserContext::GetDownloadManager(browser->profile());
manager->GetAllDownloads(&items);
if (items.empty())
DownloadItemCreatedObserver(manager).WaitForDownloadItem(&items);
EXPECT_EQ(1u, items.size());
if (1u != items.size())
return false;
DownloadItem* download_item(items[0]);
return (expected_url == download_item->GetOriginalUrl());
}
void SaveCurrentTab(const GURL& url,
content::SavePageType save_page_type,
const std::string& prefix_for_output_files,
int expected_number_of_files,
base::FilePath* output_dir,
base::FilePath* main_file_name) {
GetDestinationPaths(prefix_for_output_files, main_file_name, output_dir,
save_page_type);
DownloadPersistedObserver persisted(
browser()->profile(),
base::Bind(&DownloadStoredProperly, url, *main_file_name,
expected_number_of_files, history::DownloadState::COMPLETE));
base::RunLoop run_loop;
content::SavePackageFinishedObserver observer(
content::BrowserContext::GetDownloadManager(browser()->profile()),
run_loop.QuitClosure());
ASSERT_TRUE(GetCurrentTab(browser())
->SavePage(*main_file_name, *output_dir, save_page_type));
run_loop.Run();
ASSERT_TRUE(VerifySavePackageExpectations(browser(), url));
persisted.WaitForPersisted();
}
// Note on synchronization:
//
// For each Save Page As operation, we create a corresponding shell
// DownloadItem to display progress to the user. That DownloadItem goes
// through its own state transitions, including being persisted out to the
// history database, and the download shelf is not shown until after the
// persistence occurs. Save Package completion (and marking the DownloadItem
// as completed) occurs asynchronously from persistence. Thus if we want to
// examine either UI state or DB state, we need to wait until both the save
// package operation is complete and the relevant download item has been
// persisted.
DownloadManager* GetDownloadManager() const {
DownloadManager* download_manager =
BrowserContext::GetDownloadManager(browser()->profile());
EXPECT_TRUE(download_manager);
return download_manager;
}
// Returns full path to a file in chrome/test/data/save_page directory.
base::FilePath GetTestDirFile(const std::string& file_name) {
const base::FilePath::CharType kTestDir[] = FILE_PATH_LITERAL("save_page");
return test_dir_.Append(base::FilePath(kTestDir)).AppendASCII(file_name);
}
base::FilePath GetSaveDir() {
return DownloadPrefs(browser()->profile()).DownloadPath();
}
// Path to directory containing test data.
base::FilePath test_dir_;
private:
DISALLOW_COPY_AND_ASSIGN(SavePageBrowserTest);
};
IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, SaveHTMLOnly) {
GURL url = NavigateToMockURL("a");
base::FilePath full_file_name, dir;
SaveCurrentTab(url, content::SAVE_PAGE_TYPE_AS_ONLY_HTML, "a", 1, &dir,
&full_file_name);
ASSERT_FALSE(HasFailure());
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::PathExists(full_file_name));
EXPECT_FALSE(base::PathExists(dir));
EXPECT_TRUE(base::ContentsEqual(GetTestDirFile("a.htm"), full_file_name));
}
IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, SaveHTMLOnlyCancel) {
GURL url = NavigateToMockURL("a");
DownloadManager* manager = GetDownloadManager();
std::vector<DownloadItem*> downloads;
manager->GetAllDownloads(&downloads);
ASSERT_EQ(0u, downloads.size());
base::FilePath full_file_name, dir;
GetDestinationPaths("a", &full_file_name, &dir);
DownloadItemCreatedObserver creation_observer(manager);
DownloadPersistedObserver persisted(browser()->profile(), base::Bind(
&DownloadStoredProperly, url, full_file_name, -1,
history::DownloadState::CANCELLED));
// -1 to disable number of files check; we don't update after cancel, and
// we don't know when the single file completed in relationship to
// the cancel.
ASSERT_TRUE(GetCurrentTab(browser())->SavePage(full_file_name, dir,
content::SAVE_PAGE_TYPE_AS_ONLY_HTML));
std::vector<DownloadItem*> items;
creation_observer.WaitForDownloadItem(&items);
ASSERT_EQ(1UL, items.size());
ASSERT_EQ(url.spec(), items[0]->GetOriginalUrl().spec());
items[0]->Cancel(true);
// TODO(rdsmith): Fix DII::Cancel() to actually cancel the save package.
// Currently it's ignored.
persisted.WaitForPersisted();
// TODO(benjhayden): Figure out how to safely wait for SavePackage's finished
// notification, then expect the contents of the downloaded file.
}
class DelayingDownloadManagerDelegate : public ChromeDownloadManagerDelegate {
public:
explicit DelayingDownloadManagerDelegate(Profile* profile)
: ChromeDownloadManagerDelegate(profile) {
}
~DelayingDownloadManagerDelegate() override {}
bool ShouldCompleteDownload(
download::DownloadItem* item,
const base::Closure& user_complete_callback) override {
return false;
}
private:
DISALLOW_COPY_AND_ASSIGN(DelayingDownloadManagerDelegate);
};
// Disabled on ChromeOS due to flakiness. crbug.com/580766
#if defined(OS_CHROMEOS)
#define MAYBE_SaveHTMLOnlyTabDestroy DISABLED_SaveHTMLOnlyTabDestroy
#else
#define MAYBE_SaveHTMLOnlyTabDestroy SaveHTMLOnlyTabDestroy
#endif
IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, MAYBE_SaveHTMLOnlyTabDestroy) {
GURL url = NavigateToMockURL("a");
std::unique_ptr<DelayingDownloadManagerDelegate> delaying_delegate(
new DelayingDownloadManagerDelegate(browser()->profile()));
delaying_delegate->GetDownloadIdReceiverCallback().Run(
download::DownloadItem::kInvalidId + 1);
DownloadCoreServiceFactory::GetForBrowserContext(browser()->profile())
->SetDownloadManagerDelegateForTesting(std::move(delaying_delegate));
DownloadManager* manager = GetDownloadManager();
std::vector<DownloadItem*> downloads;
manager->GetAllDownloads(&downloads);
ASSERT_EQ(0u, downloads.size());
base::FilePath full_file_name, dir;
GetDestinationPaths("a", &full_file_name, &dir);
DownloadItemCreatedObserver creation_observer(manager);
ASSERT_TRUE(GetCurrentTab(browser())->SavePage(full_file_name, dir,
content::SAVE_PAGE_TYPE_AS_ONLY_HTML));
std::vector<DownloadItem*> items;
creation_observer.WaitForDownloadItem(&items);
ASSERT_TRUE(items.size() == 1);
// Close the tab; does this cancel the download?
GetCurrentTab(browser())->Close();
EXPECT_EQ(DownloadItem::CANCELLED, items[0]->GetState());
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_FALSE(base::PathExists(full_file_name));
EXPECT_FALSE(base::PathExists(dir));
}
IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, SaveViewSourceHTMLOnly) {
GURL mock_url = embedded_test_server()->GetURL("/save_page/a.htm");
GURL view_source_url =
GURL(content::kViewSourceScheme + std::string(":") + mock_url.spec());
GURL actual_page_url = embedded_test_server()->GetURL("/save_page/a.htm");
ui_test_utils::NavigateToURL(browser(), view_source_url);
base::FilePath full_file_name, dir;
SaveCurrentTab(actual_page_url, content::SAVE_PAGE_TYPE_AS_ONLY_HTML, "a", 1,
&dir, &full_file_name);
ASSERT_FALSE(HasFailure());
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::PathExists(full_file_name));
EXPECT_FALSE(base::PathExists(dir));
EXPECT_TRUE(base::ContentsEqual(GetTestDirFile("a.htm"), full_file_name));
}
IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, SaveCompleteHTML) {
GURL url = NavigateToMockURL("b");
base::FilePath full_file_name, dir;
SaveCurrentTab(url, content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML, "b", 3, &dir,
&full_file_name);
ASSERT_FALSE(HasFailure());
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::PathExists(full_file_name));
EXPECT_TRUE(base::PathExists(dir));
EXPECT_EQ(
ReadFileAndCollapseWhitespace(full_file_name),
WriteSavedFromPath(
ReadFileAndCollapseWhitespace(GetTestDirFile("b.saved1.htm")), url));
EXPECT_TRUE(
base::ContentsEqual(GetTestDirFile("1.png"), dir.AppendASCII("1.png")));
EXPECT_EQ(ReadFileAndCollapseWhitespace(dir.AppendASCII("1.css")),
ReadFileAndCollapseWhitespace(GetTestDirFile("1.css")));
}
IN_PROC_BROWSER_TEST_F(SavePageBrowserTest,
SaveDuringInitialNavigationIncognito) {
// Open an Incognito window.
Browser* incognito = CreateIncognitoBrowser(); // Waits.
ASSERT_TRUE(incognito);
// Create a download item creation waiter on that window.
DownloadItemCreatedObserver creation_observer(
BrowserContext::GetDownloadManager(incognito->profile()));
// Navigate, unblocking with new tab.
GURL url = embedded_test_server()->GetURL("/save_page/b.htm");
NavigateToURLWithDisposition(incognito, url,
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
// Save the page before completion.
base::FilePath full_file_name, dir;
GetDestinationPaths("b", &full_file_name, &dir);
scoped_refptr<content::MessageLoopRunner> loop_runner(
new content::MessageLoopRunner);
content::SavePackageFinishedObserver observer(
content::BrowserContext::GetDownloadManager(incognito->profile()),
loop_runner->QuitClosure());
ASSERT_TRUE(GetCurrentTab(incognito)->SavePage(
full_file_name, dir, content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML));
loop_runner->Run();
ASSERT_TRUE(VerifySavePackageExpectations(incognito, url));
// We can't check more than this because SavePackage is racing with
// the page load. If the page load won the race, then SavePackage
// might have completed. If the page load lost the race, then
// SavePackage will cancel because there aren't any resources to
// save.
}
IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, NoSave) {
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
EXPECT_FALSE(chrome::CanSavePage(browser()));
}
IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, FileNameFromPageTitle) {
GURL url = NavigateToMockURL("b");
base::FilePath full_file_name = GetSaveDir().AppendASCII(
std::string("Test page for saving page feature") + kAppendedExtension);
base::FilePath dir =
GetSaveDir().AppendASCII("Test page for saving page feature_files");
DownloadPersistedObserver persisted(browser()->profile(), base::Bind(
&DownloadStoredProperly, url, full_file_name, 3,
history::DownloadState::COMPLETE));
scoped_refptr<content::MessageLoopRunner> loop_runner(
new content::MessageLoopRunner);
content::SavePackageFinishedObserver observer(
content::BrowserContext::GetDownloadManager(browser()->profile()),
loop_runner->QuitClosure());
ASSERT_TRUE(GetCurrentTab(browser())->SavePage(
full_file_name, dir, content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML));
loop_runner->Run();
ASSERT_TRUE(VerifySavePackageExpectations(browser(), url));
persisted.WaitForPersisted();
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::PathExists(full_file_name));
EXPECT_TRUE(base::PathExists(dir));
EXPECT_EQ(
ReadFileAndCollapseWhitespace(full_file_name),
WriteSavedFromPath(
ReadFileAndCollapseWhitespace(GetTestDirFile("b.saved2.htm")), url));
EXPECT_TRUE(
base::ContentsEqual(GetTestDirFile("1.png"), dir.AppendASCII("1.png")));
EXPECT_EQ(ReadFileAndCollapseWhitespace(dir.AppendASCII("1.css")),
ReadFileAndCollapseWhitespace(GetTestDirFile("1.css")));
}
IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, RemoveFromList) {
GURL url = NavigateToMockURL("a");
base::FilePath full_file_name, dir;
SaveCurrentTab(url, content::SAVE_PAGE_TYPE_AS_ONLY_HTML, "a", 1, &dir,
&full_file_name);
ASSERT_FALSE(HasFailure());
DownloadManager* manager = GetDownloadManager();
std::vector<DownloadItem*> downloads;
manager->GetAllDownloads(&downloads);
ASSERT_EQ(1UL, downloads.size());
DownloadRemovedObserver removed(browser()->profile(), downloads[0]->GetId());
downloads[0]->Remove();
removed.WaitForRemoved();
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::PathExists(full_file_name));
EXPECT_FALSE(base::PathExists(dir));
EXPECT_TRUE(base::ContentsEqual(GetTestDirFile("a.htm"), full_file_name));
}
// This tests that a webpage with the title "test.exe" is saved as
// "test.exe.htm".
// We probably don't care to handle this on Linux or Mac.
#if defined(OS_WIN)
IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, CleanFilenameFromPageTitle) {
base::FilePath download_dir =
DownloadPrefs::FromDownloadManager(GetDownloadManager())->
DownloadPath();
base::FilePath full_file_name =
download_dir.AppendASCII(std::string("test.exe") + kAppendedExtension);
base::FilePath dir = download_dir.AppendASCII("test.exe_files");
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_FALSE(base::PathExists(full_file_name));
GURL url = embedded_test_server()->GetURL("/save_page/c.htm");
ui_test_utils::NavigateToURL(browser(), url);
SavePackageFilePicker::SetShouldPromptUser(false);
scoped_refptr<content::MessageLoopRunner> loop_runner(
new content::MessageLoopRunner);
content::SavePackageFinishedObserver observer(
content::BrowserContext::GetDownloadManager(browser()->profile()),
loop_runner->QuitClosure());
chrome::SavePage(browser());
loop_runner->Run();
EXPECT_TRUE(base::PathExists(full_file_name));
EXPECT_TRUE(base::DieFileDie(full_file_name, false));
EXPECT_TRUE(base::DieFileDie(dir, true));
}
#endif
// Tests that the SecurityLevel histograms are logged for save page downloads.
IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, SecurityLevelHistogram) {
base::HistogramTester histogram_tester;
GURL url = NavigateToMockURL("a");
base::FilePath full_file_name, dir;
SaveCurrentTab(url, content::SAVE_PAGE_TYPE_AS_ONLY_HTML, "a", 1, &dir,
&full_file_name);
histogram_tester.ExpectUniqueSample("Security.SecurityLevel.DownloadStarted",
security_state::NONE, 1);
}
class SavePageAsMHTMLBrowserTest : public SavePageBrowserTest {
public:
SavePageAsMHTMLBrowserTest() {}
~SavePageAsMHTMLBrowserTest() override;
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kSavePageAsMHTML);
}
private:
DISALLOW_COPY_AND_ASSIGN(SavePageAsMHTMLBrowserTest);
};
SavePageAsMHTMLBrowserTest::~SavePageAsMHTMLBrowserTest() {
}
IN_PROC_BROWSER_TEST_F(SavePageAsMHTMLBrowserTest, SavePageAsMHTML) {
static const int64_t kFileSizeMin = 2758;
GURL url = NavigateToMockURL("b");
base::FilePath download_dir = DownloadPrefs::FromDownloadManager(
GetDownloadManager())->DownloadPath();
base::FilePath full_file_name = download_dir.AppendASCII(std::string(
"Test page for saving page feature.mhtml"));
SavePackageFilePicker::SetShouldPromptUser(false);
DownloadPersistedObserver persisted(browser()->profile(), base::Bind(
&DownloadStoredProperly, url, full_file_name, -1,
history::DownloadState::COMPLETE));
scoped_refptr<content::MessageLoopRunner> loop_runner(
new content::MessageLoopRunner);
content::SavePackageFinishedObserver observer(
content::BrowserContext::GetDownloadManager(browser()->profile()),
loop_runner->QuitClosure());
chrome::SavePage(browser());
loop_runner->Run();
ASSERT_TRUE(VerifySavePackageExpectations(browser(), url));
persisted.WaitForPersisted();
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_TRUE(base::PathExists(full_file_name));
int64_t actual_file_size = -1;
EXPECT_TRUE(base::GetFileSize(full_file_name, &actual_file_size));
EXPECT_LE(kFileSizeMin, actual_file_size);
std::string contents;
EXPECT_TRUE(base::ReadFileToString(full_file_name, &contents));
// Test for a CSS encoded character. This used to use HTML encoding.
EXPECT_THAT(contents, HasSubstr("content: \"\\e003 \\e004 b\""));
}
IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, SavePageBrowserTest_NonMHTML) {
SavePackageFilePicker::SetShouldPromptUser(false);
GURL url("data:text/plain,foo");
ui_test_utils::NavigateToURL(browser(), url);
scoped_refptr<content::MessageLoopRunner> loop_runner(
new content::MessageLoopRunner);
content::SavePackageFinishedObserver observer(
content::BrowserContext::GetDownloadManager(browser()->profile()),
loop_runner->QuitClosure());
chrome::SavePage(browser());
loop_runner->Run();
base::FilePath download_dir = DownloadPrefs::FromDownloadManager(
GetDownloadManager())->DownloadPath();
base::FilePath filename = download_dir.AppendASCII("dataurl.txt");
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_TRUE(base::PathExists(filename));
std::string contents;
EXPECT_TRUE(base::ReadFileToString(filename, &contents));
EXPECT_EQ("foo", contents);
}
// If a save-page-complete operation results in creating subresources that would
// otherwise be considered dangerous, such files should get a .download
// extension appended so that they won't be accidentally executed by the user.
IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, DangerousSubresources) {
GURL url =
embedded_test_server()->GetURL("/save_page/dubious-subresources.html");
ui_test_utils::NavigateToURL(browser(), url);
base::FilePath full_file_name, dir;
SaveCurrentTab(url, content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML,
"dubious-subresources", 2, &dir, &full_file_name);
ASSERT_FALSE(HasFailure());
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::PathExists(full_file_name));
EXPECT_TRUE(base::PathExists(dir.AppendASCII("not-a-crx.crx.download")));
}
// Test that we don't crash when the page contains an iframe that
// was handled as a download (http://crbug.com/42212).
IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, SaveDownloadableIFrame) {
GURL url =
embedded_test_server()->GetURL("/downloads/iframe-src-is-a-download.htm");
// Wait for and then dismiss the non-save-page-as-related download item
// (the one associated with downloading of "thisdayinhistory.xls" file).
{
GURL download_url =
embedded_test_server()->GetURL("/downloads/thisdayinhistory.xls");
DownloadPersistedObserver persisted(
browser()->profile(),
base::Bind(&DownloadStoredProperly, download_url, base::FilePath(), -1,
history::DownloadState::COMPLETE));
ui_test_utils::NavigateToURL(browser(), url);
ASSERT_TRUE(VerifySavePackageExpectations(browser(), download_url));
persisted.WaitForPersisted();
std::vector<download::DownloadItem*> downloads;
GetDownloadManager()->GetAllDownloads(&downloads);
for (auto* download : downloads)
download->Remove();
}
base::FilePath full_file_name, dir;
SaveCurrentTab(url, content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML,
"iframe-src-is-a-download", 3, &dir, &full_file_name);
ASSERT_FALSE(HasFailure());
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::PathExists(full_file_name));
EXPECT_TRUE(base::PathExists(dir.AppendASCII("thisdayinhistory.html")));
EXPECT_TRUE(base::PathExists(dir.AppendASCII("no-such-file.html")));
}
// Test that file: URI won't be saved when referred to from an HTTP page.
// See also https://crbug.com/616429.
IN_PROC_BROWSER_TEST_F(SavePageBrowserTest, SaveUnauthorizedResource) {
GURL url = NavigateToMockURL("unauthorized-access");
// Create a test file (that the web page should not have access to).
base::ScopedAllowBlockingForTesting allow_blocking;
base::ScopedTempDir temp_dir2;
ASSERT_TRUE(temp_dir2.CreateUniqueTempDir());
base::FilePath file_path =
temp_dir2.GetPath().Append(FILE_PATH_LITERAL("should-not-save.jpg"));
std::string file_content("fake-jpg");
ASSERT_LT(
0, base::WriteFile(file_path, file_content.data(), file_content.size()));
// Refer to the test file from the test page.
GURL file_url = net::FilePathToFileURL(file_path);
ASSERT_TRUE(ExecuteScript(
browser()->tab_strip_model()->GetWebContentsAt(0),
base::StringPrintf("document.getElementById('resource1').src = '%s';",
file_url.spec().data())));
// Save the current page.
base::FilePath full_file_name, dir;
SaveCurrentTab(url, content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML,
"unauthorized-access", 2, &dir, &full_file_name);
// We should not save resource that the web page didn't have access to.
// (because executing a resource request can have side effects - for example
// after https://crbug.com/590714 a website from the internet should not be
// able to issue a resource request to an intranet website and trigger
// server-side actions in the internet; this test uses a file: URI as a
// canary for detecting whether a website can access restricted resources).
EXPECT_FALSE(base::PathExists(dir.AppendASCII("should-not-save.jpg")));
}
// Test suite that allows testing --site-per-process against cross-site frames.
// See http://dev.chromium.org/developers/design-documents/site-isolation.
class SavePageSitePerProcessBrowserTest : public SavePageBrowserTest {
public:
SavePageSitePerProcessBrowserTest() {}
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
SavePageBrowserTest::SetUpCommandLine(command_line);
// Append --site-per-process flag.
content::IsolateAllSitesForTesting(command_line);
}
void SetUpOnMainThread() override {
SavePageBrowserTest::SetUpOnMainThread();
// Used by the BrokenImage test which depends on *.no.such.host not
// resolving to 127.0.0.1
host_resolver()->AddRule("no.such.host", "128.0.0.1");
host_resolver()->AddRule("*", "127.0.0.1");
}
private:
DISALLOW_COPY_AND_ASSIGN(SavePageSitePerProcessBrowserTest);
};
// Test for crbug.com/526786.
IN_PROC_BROWSER_TEST_F(SavePageSitePerProcessBrowserTest, SaveAsCompleteHtml) {
GURL url(
embedded_test_server()->GetURL("a.com", "/save_page/frames-xsite.htm"));
ui_test_utils::NavigateToURL(browser(), url);
base::FilePath full_file_name, dir;
SaveCurrentTab(url, content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML,
"frames-xsite-complete-html", 5, &dir, &full_file_name);
ASSERT_FALSE(HasFailure());
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::DirectoryExists(dir));
base::FilePath expected_files[] = {
full_file_name,
dir.AppendASCII("a.html"), // From iframes.htm
dir.AppendASCII("b.html"), // From iframes.htm
dir.AppendASCII("1.css"), // From b.htm
dir.AppendASCII("1.png"), // Deduplicated from iframes.htm and b.htm.
};
for (auto file_path : expected_files) {
EXPECT_TRUE(base::PathExists(file_path)) << "Does " << file_path.value()
<< " exist?";
int64_t actual_file_size = 0;
EXPECT_TRUE(base::GetFileSize(file_path, &actual_file_size));
EXPECT_NE(0, actual_file_size) << "Is " << file_path.value()
<< " non-empty?";
}
// Verify that local links got correctly replaced with local paths
// (most importantly for iframe elements, which are only exercised
// by this particular test).
std::string main_contents;
ASSERT_TRUE(base::ReadFileToString(full_file_name, &main_contents));
EXPECT_THAT(
main_contents,
HasSubstr("<iframe "
"src=\"./frames-xsite-complete-html_files/a.html\"></iframe>"));
EXPECT_THAT(
main_contents,
HasSubstr("<iframe "
"src=\"./frames-xsite-complete-html_files/b.html\"></iframe>"));
EXPECT_THAT(
main_contents,
HasSubstr("<img src=\"./frames-xsite-complete-html_files/1.png\">"));
// Verification of html contents.
EXPECT_THAT(
main_contents,
HasSubstr("frames-xsite.htm: 896fd88d-a77a-4f46-afd8-24db7d5af9c2"));
std::string a_contents;
ASSERT_TRUE(base::ReadFileToString(dir.AppendASCII("a.html"), &a_contents));
EXPECT_THAT(a_contents,
HasSubstr("a.htm: 1b8aae2b-e164-462f-bd5b-98aa366205f2"));
std::string b_contents;
ASSERT_TRUE(base::ReadFileToString(dir.AppendASCII("b.html"), &b_contents));
EXPECT_THAT(b_contents,
HasSubstr("b.htm: 3a35f7fa-96a9-4487-9f18-4470263907fa"));
}
// Test for crbug.com/538766.
IN_PROC_BROWSER_TEST_F(SavePageSitePerProcessBrowserTest, SaveAsMHTML) {
GURL url(
embedded_test_server()->GetURL("a.com", "/save_page/frames-xsite.htm"));
ui_test_utils::NavigateToURL(browser(), url);
base::FilePath full_file_name, dir;
SaveCurrentTab(url, content::SAVE_PAGE_TYPE_AS_MHTML, "frames-xsite-mhtml",
-1, &dir, &full_file_name);
ASSERT_FALSE(HasFailure());
std::string mhtml;
{
base::ScopedAllowBlockingForTesting allow_blocking;
ASSERT_TRUE(base::ReadFileToString(full_file_name, &mhtml));
}
// Verify content of main frame, subframes and some savable resources.
EXPECT_THAT(
mhtml,
HasSubstr("frames-xsite.htm: 896fd88d-a77a-4f46-afd8-24db7d5af9c2"));
EXPECT_THAT(mhtml, HasSubstr("a.htm: 1b8aae2b-e164-462f-bd5b-98aa366205f2"));
EXPECT_THAT(mhtml, HasSubstr("b.htm: 3a35f7fa-96a9-4487-9f18-4470263907fa"));
EXPECT_THAT(mhtml, HasSubstr("font-size: 20px;"))
<< "Verifying if content from 1.css is present";
// Verify presence of URLs associated with main frame, subframes and some
// savable resources.
// (note that these are single-line regexes).
EXPECT_THAT(mhtml,
ContainsRegex("Content-Location.*/save_page/frames-xsite.htm"));
EXPECT_THAT(mhtml, ContainsRegex("Content-Location.*/save_page/a.htm"));
EXPECT_THAT(mhtml, ContainsRegex("Content-Location.*/save_page/b.htm"));
EXPECT_THAT(mhtml, ContainsRegex("Content-Location.*/save_page/1.css"));
EXPECT_THAT(mhtml, ContainsRegex("Content-Location.*/save_page/1.png"));
// Verify that 1.png appears in the output only once (despite being referred
// to twice - from iframes.htm and from b.htm).
int count = 0;
size_t pos = 0;
for (;;) {
pos = mhtml.find("Content-Type: image/png", pos);
if (pos == std::string::npos)
break;
count++;
pos++;
}
EXPECT_EQ(1, count) << "Verify number of image/png parts in the mhtml output";
}
// Test for crbug.com/541342 - handling of dead renderer processes.
IN_PROC_BROWSER_TEST_F(SavePageSitePerProcessBrowserTest,
CompleteHtmlWhenRendererIsDead) {
GURL url(
embedded_test_server()->GetURL("a.com", "/save_page/frames-xsite.htm"));
ui_test_utils::NavigateToURL(browser(), url);
// Kill one of renderer processes (this is the essence of this test).
WebContents* web_contents = GetCurrentTab(browser());
bool did_kill_a_process = false;
for (RenderFrameHost* frame : web_contents->GetAllFrames()) {
if (frame->GetLastCommittedURL().host() == "bar.com") {
RenderProcessHost* process_to_kill = frame->GetProcess();
EXPECT_NE(
web_contents->GetMainFrame()->GetProcess()->GetID(),
process_to_kill->GetID())
<< "a.com and bar.com should be in different processes.";
EXPECT_TRUE(process_to_kill->FastShutdownIfPossible());
EXPECT_FALSE(process_to_kill->IsInitializedAndNotDead());
did_kill_a_process = true;
break;
}
}
EXPECT_TRUE(did_kill_a_process);
// Main verification is that we don't hang and time out when saving.
base::FilePath full_file_name, dir;
SaveCurrentTab(url, content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML,
"frames-xsite-complete-html", 5, &dir, &full_file_name);
ASSERT_FALSE(HasFailure());
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::DirectoryExists(dir));
EXPECT_TRUE(base::PathExists(full_file_name));
}
// Test suite that verifies that the frame tree "looks" the same before
// and after a save-page-as.
class SavePageOriginalVsSavedComparisonTest
: public SavePageSitePerProcessBrowserTest,
public ::testing::WithParamInterface<content::SavePageType> {
protected:
void TestOriginalVsSavedPage(
content::SavePageType save_page_type,
const GURL& url,
int expected_number_of_frames_in_original_page,
int expected_number_of_frames_in_mhtml_page,
const std::vector<std::string>& expected_substrings) {
// Navigate to the test page and verify if test expectations
// are met (this is mostly a sanity check - a failure to meet
// expectations would probably mean that there is a test bug
// (i.e. that we got called with wrong expected_foo argument).
ui_test_utils::NavigateToURL(browser(), url);
DLOG(INFO) << "Verifying test expectations for original page... : "
<< GetCurrentTab(browser())->GetLastCommittedURL();
AssertExpectationsAboutCurrentTab(
expected_number_of_frames_in_original_page, expected_substrings);
// Save the page.
base::FilePath full_file_name, dir;
SaveCurrentTab(url, save_page_type, "save_result", -1, &dir,
&full_file_name);
ASSERT_FALSE(HasFailure());
// Stop the test server (to make sure the locally saved page
// is self-contained / won't try to open original resources).
ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
// Open the saved page and verify if test expectations are
// met (i.e. if the same expectations are met for "after"
// [saved version of the page] as for the "before"
// [the original version of the page].
ui_test_utils::NavigateToURL(browser(),
GURL(net::FilePathToFileURL(full_file_name)));
DLOG(INFO) << "Verifying test expectations for saved page... : "
<< GetCurrentTab(browser())->GetLastCommittedURL();
// Hidden elements, i.e., hidden frames, will be removed only from MHTML
// page. They're still kept in other types of serialization, like saving
// as a complete html page.
int expected_number_of_frames_in_saved_page =
(save_page_type == content::SAVE_PAGE_TYPE_AS_MHTML) ?
expected_number_of_frames_in_mhtml_page :
expected_number_of_frames_in_original_page;
AssertExpectationsAboutCurrentTab(expected_number_of_frames_in_saved_page,
expected_substrings);
if (GetParam() == content::SAVE_PAGE_TYPE_AS_MHTML) {
std::set<url::Origin> origins;
GetCurrentTab(browser())->ForEachFrame(
base::BindRepeating(&CheckFrameForMHTML, base::Unretained(&origins)));
int unique_origins = origins.size();
EXPECT_EQ(expected_number_of_frames_in_saved_page, unique_origins)
<< "All origins should be unique";
}
}
// Helper method to deduplicate some code across 2 tests.
void RunObjectElementsTest(GURL url) {
content::SavePageType save_page_type = GetParam();
// 7 comes from:
// - main frame (frames-objects.htm)
// - object with frame-nested.htm + 2 subframes (frames-nested2.htm + b.htm)
// - iframe with a.htm
// - object with svg.svg
// - object with text.txt
// (pdf and png objects do not get a separate frame)
int expected_number_of_frames = 7;
std::vector<std::string> expected_substrings = {
"frames-objects.htm: 8da13db4-a512-4d9b-b1c5-dc1c134234b9",
"a.htm: 1b8aae2b-e164-462f-bd5b-98aa366205f2",
"b.htm: 3a35f7fa-96a9-4487-9f18-4470263907fa",
"frames-nested.htm: 4388232f-8d45-4d2e-9807-721b381be153",
"frames-nested2.htm: 6d23dc47-f283-4977-96ec-66bcf72301a4",
"text-object.txt: ae52dd09-9746-4b7e-86a6-6ada5e2680c2",
"svg: 0875fd06-131d-4708-95e1-861853c6b8dc",
};
// TODO(lukasza): crbug.com/553478: Enable <object> testing of MHTML.
if (save_page_type == content::SAVE_PAGE_TYPE_AS_MHTML)
return;
TestOriginalVsSavedPage(save_page_type, url, expected_number_of_frames,
expected_number_of_frames, expected_substrings);
}
private:
void AssertExpectationsAboutCurrentTab(
int expected_number_of_frames,
const std::vector<std::string>& expected_substrings) {
int actual_number_of_frames = 0;
GetCurrentTab(browser())->ForEachFrame(base::BindRepeating(
&IncrementInteger, base::Unretained(&actual_number_of_frames)));
EXPECT_EQ(expected_number_of_frames, actual_number_of_frames);
for (const auto& expected_substring : expected_substrings) {
int actual_number_of_matches = ui_test_utils::FindInPage(
GetCurrentTab(browser()), base::UTF8ToUTF16(expected_substring),
true, // |forward|
true, // |case_sensitive|
nullptr, nullptr);
EXPECT_EQ(1, actual_number_of_matches)
<< "Verifying that \"" << expected_substring << "\" appears "
<< "exactly once in the text of web contents";
}
std::string forbidden_substrings[] = {
"head", // Html markup should not be visible.
"err", // "err" is a prefix of error messages + is strategically
// included in some tests in contents that should not render
// (i.e. inside of an object element and/or inside of a frame
// that should be hidden).
};
for (const auto& forbidden_substring : forbidden_substrings) {
int actual_number_of_matches = ui_test_utils::FindInPage(
GetCurrentTab(browser()), base::UTF8ToUTF16(forbidden_substring),
true, // |forward|
false, // |case_sensitive|
nullptr, nullptr);
EXPECT_EQ(0, actual_number_of_matches)
<< "Verifying that \"" << forbidden_substring << "\" doesn't "
<< "appear in the text of web contents";
}
}
static void IncrementInteger(int* i, content::RenderFrameHost* /* unused */) {
(*i)++;
}
static void CheckFrameForMHTML(std::set<url::Origin>* origins,
content::RenderFrameHost* host) {
// See RFC n°2557, section-8.3: "Use of the Content-ID header and CID URLs".
const char kContentIdScheme[] = "cid";
origins->insert(host->GetLastCommittedOrigin());
EXPECT_TRUE(host->GetLastCommittedOrigin().opaque());
if (!host->GetParent())
EXPECT_TRUE(host->GetLastCommittedURL().SchemeIsFile());
else
EXPECT_TRUE(host->GetLastCommittedURL().SchemeIs(kContentIdScheme));
}
};
// Test coverage for:
// - crbug.com/526786: OOPIFs support for CompleteHtml
// - crbug.com/538766: OOPIFs support for MHTML
// - crbug.com/539936: Subframe gets redirected.
// Test compares original-vs-saved for a page with cross-site frames
// (subframes get redirected to a different domain - see frames-xsite.htm).
IN_PROC_BROWSER_TEST_P(SavePageOriginalVsSavedComparisonTest, CrossSite) {
content::SavePageType save_page_type = GetParam();
std::vector<std::string> expected_substrings = {
"frames-xsite.htm: 896fd88d-a77a-4f46-afd8-24db7d5af9c2",
"a.htm: 1b8aae2b-e164-462f-bd5b-98aa366205f2",
"b.htm: 3a35f7fa-96a9-4487-9f18-4470263907fa",
};
GURL url(
embedded_test_server()->GetURL("a.com", "/save_page/frames-xsite.htm"));
TestOriginalVsSavedPage(save_page_type, url, 3, 3, expected_substrings);
}
// Test compares original-vs-saved for a page with <object> elements.
// (see crbug.com/553478).
IN_PROC_BROWSER_TEST_P(SavePageOriginalVsSavedComparisonTest,
ObjectElementsViaHttp) {
GURL url(
embedded_test_server()->GetURL("a.com", "/save_page/frames-objects.htm"));
RunObjectElementsTest(url);
}
// Tests that saving a page from file: URI works.
// TODO(https://crbug.com/840063): Deflake and reenable.
IN_PROC_BROWSER_TEST_P(SavePageOriginalVsSavedComparisonTest,
DISABLED_ObjectElementsViaFile) {
base::FilePath test_data_dir;
ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
GURL url(net::FilePathToFileURL(
test_data_dir.Append(FILE_PATH_LITERAL("save_page/frames-objects.htm"))));
EXPECT_TRUE(url.SchemeIsFile());
RunObjectElementsTest(url);
}
// Test compares original-vs-saved for a page with frames at about:blank uri.
// This tests handling of iframe elements without src attribute (only with
// srcdoc attribute) and how they get saved / cross-referenced.
IN_PROC_BROWSER_TEST_P(SavePageOriginalVsSavedComparisonTest, AboutBlank) {
content::SavePageType save_page_type = GetParam();
std::vector<std::string> expected_substrings = {
"main: acb0609d-eb10-4c26-83e2-ad8afb7b0ff3",
"sub1: b124df3a-d39f-47a1-ae04-5bb5d0bf549e",
"sub2: 07014068-604d-45ae-884f-a068cfe7bc0a",
"sub3: 06cc8fcc-c692-4a1a-a10f-1645b746e8f4",
};
GURL url(embedded_test_server()->GetURL("a.com",
"/save_page/frames-about-blank.htm"));
TestOriginalVsSavedPage(save_page_type, url, 4, 4, expected_substrings);
}
// Test compares original-vs-saved for a page with nested frames.
// Two levels of nesting are especially good for verifying correct
// link rewriting for subframes-vs-main-frame (see crbug.com/554666).
IN_PROC_BROWSER_TEST_P(SavePageOriginalVsSavedComparisonTest, NestedFrames) {
content::SavePageType save_page_type = GetParam();
std::vector<std::string> expected_substrings = {
"frames-nested.htm: 4388232f-8d45-4d2e-9807-721b381be153",
"frames-nested2.htm: 6d23dc47-f283-4977-96ec-66bcf72301a4",
"b.htm: 3a35f7fa-96a9-4487-9f18-4470263907fa",
};
GURL url(
embedded_test_server()->GetURL("a.com", "/save_page/frames-nested.htm"));
TestOriginalVsSavedPage(save_page_type, url, 3, 3, expected_substrings);
}
// Test for crbug.com/106364 and crbug.com/538188.
// Test frames have the same uri ...
// subframe1 and subframe2 - both have src=b.htm
// subframe3 and subframe4 - about:blank (no src, only srcdoc attribute).
// ... but different content (generated by main frame's javascript).
IN_PROC_BROWSER_TEST_P(SavePageOriginalVsSavedComparisonTest, RuntimeChanges) {
content::SavePageType save_page_type = GetParam();
std::vector<std::string> expected_substrings = {
"frames-runtime-changes.htm: 4388232f-8d45-4d2e-9807-721b381be153",
"subframe1: 21595339-61fc-4854-b6df-0668328ea263",
"subframe2: adf55719-15e7-45be-9eda-d12fe782a1bd",
"subframe3: 50e294bf-3a5b-499d-8772-651ead26952f",
"subframe4: e0ea9289-7467-4d32-ba5c-c604e8d84cb7",
};
GURL url(embedded_test_server()->GetURL(
"a.com", "/save_page/frames-runtime-changes.htm?do_runtime_changes=1"));
TestOriginalVsSavedPage(save_page_type, url, 5, 5, expected_substrings);
}
// Test for saving frames with various encodings:
// - iso-8859-2: encoding declared via <meta> element
// - utf16-le-bom.htm, utf16-be-bom.htm: encoding detected via BOM
// - utf16-le-nobom.htm, utf16-le-nobom.htm - encoding declared via
// mocked http headers
IN_PROC_BROWSER_TEST_P(SavePageOriginalVsSavedComparisonTest, Encoding) {
content::SavePageType save_page_type = GetParam();
std::vector<std::string> expected_substrings = {
"frames-encodings.htm: f53295dd-a95b-4b32-85f5-b6e15377fb20",
"iso-8859-2.htm: Za\xc5\xbc\xc3\xb3\xc5\x82\xc4\x87 "
"g\xc4\x99\xc5\x9bl\xc4\x85 ja\xc5\xba\xc5\x84",
"utf16-le-nobom.htm: Za\xc5\xbc\xc3\xb3\xc5\x82\xc4\x87 "
"g\xc4\x99\xc5\x9bl\xc4\x85 ja\xc5\xba\xc5\x84",
"utf16-le-bom.htm: Za\xc5\xbc\xc3\xb3\xc5\x82\xc4\x87 "
"g\xc4\x99\xc5\x9bl\xc4\x85 ja\xc5\xba\xc5\x84",
"utf16-be-nobom.htm: Za\xc5\xbc\xc3\xb3\xc5\x82\xc4\x87 "
"g\xc4\x99\xc5\x9bl\xc4\x85 ja\xc5\xba\xc5\x84",
"utf16-be-bom.htm: Za\xc5\xbc\xc3\xb3\xc5\x82\xc4\x87 "
"g\xc4\x99\xc5\x9bl\xc4\x85 ja\xc5\xba\xc5\x84"};
GURL url(embedded_test_server()->GetURL("a.com",
"/save_page/frames-encodings.htm"));
// TODO(lukasza): crbug.com/541699: MHTML needs to handle multi-byte encodings
// by either:
// 1. Continuing to preserve the original encoding, but starting to round-trip
// the encoding declaration (in Content-Type MIME/MHTML header?)
// 2. Saving html docs in UTF8.
// 3. Saving the BOM (not sure if this will help for all cases though).
if (save_page_type == content::SAVE_PAGE_TYPE_AS_MHTML)
return;
TestOriginalVsSavedPage(save_page_type, url, 6, 6, expected_substrings);
}
// Test for saving style element and attribute (see also crbug.com/568293).
IN_PROC_BROWSER_TEST_P(SavePageOriginalVsSavedComparisonTest, Style) {
content::SavePageType save_page_type = GetParam();
std::vector<std::string> expected_substrings = {
"style.htm: af84c3ca-0fc6-4b0d-bf7a-5ac18a4dab62",
"frameF: c9539ccd-47b0-47cf-a03b-734614865872",
};
GURL url(embedded_test_server()->GetURL("a.com", "/save_page/style.htm"));
// The original page has 7 iframes. One of them that contains hidden attribute
// will be excluded from the saved page.
TestOriginalVsSavedPage(save_page_type, url, 7, 6, expected_substrings);
}
// Test for saving a page with broken subresources:
// - Broken, undecodable image (see also https://crbug.com/586680)
// - Broken link, to unresolvable host (see also https://crbug.com/594219)
IN_PROC_BROWSER_TEST_P(SavePageOriginalVsSavedComparisonTest, BrokenImage) {
content::SavePageType save_page_type = GetParam();
std::vector<std::string> expected_substrings = {
"broken-image.htm: 1e846775-b3ed-4d9c-a124-029554a1eb9d",
};
GURL url(embedded_test_server()->GetURL("127.0.0.1",
"/save_page/broken-image.htm"));
TestOriginalVsSavedPage(save_page_type, url, 1, 1, expected_substrings);
}
// Test for saving a page with a cross-site <object> element.
IN_PROC_BROWSER_TEST_P(SavePageOriginalVsSavedComparisonTest, CrossSiteObject) {
content::SavePageType save_page_type = GetParam();
std::vector<std::string> expected_substrings = {
"cross-site-object.htm: f727dd87-2048-44cf-beee-19fa9863f046",
"a.htm: 1b8aae2b-e164-462f-bd5b-98aa366205f2",
"svg: 0875fd06-131d-4708-95e1-861853c6b8dc",
};
GURL url(embedded_test_server()->GetURL("a.com",
"/save_page/cross-site-object.htm"));
TestOriginalVsSavedPage(save_page_type, url, 4, 4, expected_substrings);
}
INSTANTIATE_TEST_SUITE_P(
SaveType,
SavePageOriginalVsSavedComparisonTest,
::testing::Values(content::SAVE_PAGE_TYPE_AS_COMPLETE_HTML,
content::SAVE_PAGE_TYPE_AS_MHTML));
} // namespace