| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <array> |
| #include <memory> |
| #include <set> |
| #include <sstream> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/base64.h" |
| #include "base/command_line.h" |
| #include "base/feature_list.h" |
| #include "base/files/file.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/path_service.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/system/sys_info.h" |
| #include "base/test/bind.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/test_file_util.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/uuid.h" |
| #include "build/build_config.h" |
| #include "chrome/app/chrome_command_ids.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/download/chrome_download_manager_delegate.h" |
| #include "chrome/browser/download/download_browsertest_utils.h" |
| #include "chrome/browser/download/download_commands.h" |
| #include "chrome/browser/download/download_core_service.h" |
| #include "chrome/browser/download/download_core_service_factory.h" |
| #include "chrome/browser/download/download_crx_util.h" |
| #include "chrome/browser/download/download_history.h" |
| #include "chrome/browser/download/download_item_model.h" |
| #include "chrome/browser/download/download_item_web_app_data.h" |
| #include "chrome/browser/download/download_manager_utils.h" |
| #include "chrome/browser/download/download_prefs.h" |
| #include "chrome/browser/download/download_request_limiter.h" |
| #include "chrome/browser/download/download_target_determiner.h" |
| #include "chrome/browser/download/download_test_file_activity_observer.h" |
| #include "chrome/browser/download/simple_download_manager_coordinator_factory.h" |
| #include "chrome/browser/extensions/install_verifier.h" |
| #include "chrome/browser/extensions/scoped_test_mv2_enabler.h" |
| #include "chrome/browser/history/history_service_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_key.h" |
| #include "chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h" |
| #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h" |
| #include "chrome/browser/safe_browsing/download_protection/download_protection_util.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_commands.h" |
| #include "chrome/browser/ui/browser_finder.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/browser_tabstrip.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/chrome_pages.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h" |
| #include "chrome/browser/web_applications/test/web_app_install_test_utils.h" |
| #include "chrome/common/chrome_features.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/grit/generated_resources.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/download/public/common/download_danger_type.h" |
| #include "components/download/public/common/download_features.h" |
| #include "components/download/public/common/download_interrupt_reasons.h" |
| #include "components/download/public/common/download_item.h" |
| #include "components/download/public/common/in_progress_download_manager.h" |
| #include "components/history/content/browser/download_conversions.h" |
| #include "components/history/core/browser/download_constants.h" |
| #include "components/history/core/browser/download_row.h" |
| #include "components/history/core/browser/history_service.h" |
| #include "components/infobars/content/content_infobar_manager.h" |
| #include "components/infobars/core/confirm_infobar_delegate.h" |
| #include "components/infobars/core/infobar.h" |
| #include "components/lookalikes/core/safety_tip_test_utils.h" |
| #include "components/metrics/content/subprocess_metrics_provider.h" |
| #include "components/permissions/permission_request_manager.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/safe_browsing/buildflags.h" |
| #include "components/safe_browsing/content/browser/safe_browsing_service_interface.h" |
| #include "components/safe_browsing/content/common/file_type_policies_test_util.h" |
| #include "components/safe_browsing/content/common/proto/download_file_types.pb.h" |
| #include "components/safe_browsing/core/common/proto/csd.pb.h" |
| #include "components/safe_browsing/core/common/safe_browsing_prefs.h" |
| #include "components/security_state/core/security_state.h" |
| #include "components/services/quarantine/test_support.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/context_menu_params.h" |
| #include "content/public/browser/device_service.h" |
| #include "content/public/browser/download_manager.h" |
| #include "content/public/browser/download_request_utils.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/render_widget_host.h" |
| #include "content/public/browser/resource_context.h" |
| #include "content/public/browser/storage_partition_config.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/common/isolated_world_ids.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/download_test_observer.h" |
| #include "content/public/test/fenced_frame_test_util.h" |
| #include "content/public/test/hit_test_region_observer.h" |
| #include "content/public/test/prerender_test_util.h" |
| #include "content/public/test/slow_download_http_response.h" |
| #include "content/public/test/test_download_http_response.h" |
| #include "content/public/test/test_file_error_injector.h" |
| #include "content/public/test/test_navigation_observer.h" |
| #include "content/public/test/url_loader_interceptor.h" |
| #include "extensions/browser/extension_dialog_auto_confirm.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/extension_system.h" |
| #include "extensions/browser/scoped_ignore_content_verifier_for_test.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "net/base/features.h" |
| #include "net/base/filename_util.h" |
| #include "net/base/mime_util.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/http_request.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| #include "net/test/embedded_test_server/request_handler_util.h" |
| #include "net/test/scoped_mutually_exclusive_feature_list.h" |
| #include "net/test/url_request/url_request_mock_http_job.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "pdf/buildflags.h" |
| #include "services/device/public/mojom/wake_lock.mojom.h" |
| #include "services/device/public/mojom/wake_lock_provider.mojom.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/blink/public/common/loader/network_utils.h" |
| #include "third_party/blink/public/common/loader/referrer_utils.h" |
| #include "third_party/blink/public/common/switches.h" |
| #include "third_party/blink/public/mojom/context_menu/context_menu.mojom.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/page_transition_types.h" |
| #include "ui/base/ui_base_features.h" |
| #include "ui/gfx/geometry/point_conversions.h" |
| |
| #if !BUILDFLAG(IS_CHROMEOS) |
| #include "chrome/browser/download/bubble/download_bubble_ui_controller.h" |
| #include "chrome/browser/download/bubble/download_display_controller.h" |
| #include "chrome/browser/ui/download/download_display.h" |
| #endif |
| |
| #if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION) |
| #include "chrome/browser/safe_browsing/download_protection/download_feedback_service.h" |
| #include "chrome/browser/safe_browsing/download_protection/download_protection_service.h" |
| #include "chrome/browser/safe_browsing/test_safe_browsing_service.h" |
| #endif |
| |
| #if BUILDFLAG(ENABLE_PDF) |
| #include "base/test/with_feature_override.h" |
| #include "chrome/browser/pdf/pdf_extension_test_util.h" |
| #include "chrome/browser/pdf/test_pdf_viewer_stream_manager.h" |
| #include "chrome/browser/ui/pdf/chrome_pdf_document_helper_client.h" |
| #include "components/pdf/browser/pdf_document_helper.h" |
| #include "pdf/pdf_features.h" |
| #endif |
| |
| using content::BrowserContext; |
| using content::BrowserThread; |
| using content::DownloadManager; |
| using content::URLLoaderInterceptor; |
| using content::WebContents; |
| using download::DownloadItem; |
| using download::DownloadUrlParameters; |
| using extensions::Extension; |
| using net::URLRequestMockHTTPJob; |
| using net::test_server::EmbeddedTestServer; |
| |
| namespace { |
| |
| class InnerWebContentsAttachedWaiter : public content::WebContentsObserver { |
| public: |
| // Observes navigation for the specified |web_contents|. |
| explicit InnerWebContentsAttachedWaiter(WebContents* web_contents) |
| : content::WebContentsObserver(web_contents) {} |
| |
| void InnerWebContentsAttached( |
| WebContents* inner_web_contents, |
| content::RenderFrameHost* render_frame_host) override { |
| run_loop_.Quit(); |
| } |
| |
| void Wait() { run_loop_.Run(); } |
| |
| private: |
| base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed}; |
| }; |
| |
| void VerifyNewDownloadId(uint32_t expected_download_id, uint32_t download_id) { |
| ASSERT_EQ(expected_download_id, download_id); |
| } |
| |
| class DownloadTestContentBrowserClient : public content::ContentBrowserClient { |
| public: |
| explicit DownloadTestContentBrowserClient(bool must_download) |
| : must_download_(must_download) {} |
| |
| bool ShouldForceDownloadResource(content::BrowserContext* browser_context, |
| const GURL& url, |
| const std::string& mime_type) override { |
| return must_download_; |
| } |
| |
| private: |
| const bool must_download_; |
| }; |
| |
| class CreatedObserver : public content::DownloadManager::Observer { |
| public: |
| explicit CreatedObserver(content::DownloadManager* manager) |
| : manager_(manager) { |
| manager->AddObserver(this); |
| } |
| |
| CreatedObserver(const CreatedObserver&) = delete; |
| CreatedObserver& operator=(const CreatedObserver&) = delete; |
| |
| ~CreatedObserver() override { |
| if (manager_) |
| manager_->RemoveObserver(this); |
| } |
| |
| void Wait() { |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> downloads; |
| manager_->GetAllDownloads(&downloads); |
| if (!downloads.empty()) |
| return; |
| waiting_ = true; |
| run_loop_.Run(); |
| waiting_ = false; |
| } |
| |
| private: |
| void OnDownloadCreated(content::DownloadManager* manager, |
| download::DownloadItem* item) override { |
| DCHECK_EQ(manager_, manager); |
| if (waiting_) |
| run_loop_.QuitWhenIdle(); |
| } |
| |
| raw_ptr<content::DownloadManager> manager_; |
| bool waiting_ = false; |
| base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed}; |
| }; |
| |
| class OnCanDownloadDecidedObserver { |
| public: |
| OnCanDownloadDecidedObserver() = default; |
| |
| OnCanDownloadDecidedObserver(const OnCanDownloadDecidedObserver&) = delete; |
| OnCanDownloadDecidedObserver& operator=(const OnCanDownloadDecidedObserver&) = |
| delete; |
| |
| void WaitForNumberOfDecisions(size_t expected_num_of_decisions) { |
| if (expected_num_of_decisions <= decisions_.size()) |
| return; |
| |
| expected_num_of_decisions_ = expected_num_of_decisions; |
| base::RunLoop run_loop; |
| completion_closure_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| |
| void OnCanDownloadDecided(bool allow) { |
| decisions_.push_back(allow); |
| if (decisions_.size() == expected_num_of_decisions_) { |
| DCHECK(!completion_closure_.is_null()); |
| std::move(completion_closure_).Run(); |
| } |
| } |
| |
| const std::vector<bool>& GetDecisions() { return decisions_; } |
| |
| void Reset() { |
| expected_num_of_decisions_ = 0; |
| decisions_.clear(); |
| completion_closure_.Reset(); |
| } |
| |
| private: |
| std::vector<bool> decisions_; |
| size_t expected_num_of_decisions_ = 0; |
| base::OnceClosure completion_closure_; |
| }; |
| |
| class PercentWaiter : public download::DownloadItem::Observer { |
| public: |
| explicit PercentWaiter(DownloadItem* item) : item_(item) { |
| item_->AddObserver(this); |
| } |
| |
| PercentWaiter(const PercentWaiter&) = delete; |
| PercentWaiter& operator=(const PercentWaiter&) = delete; |
| |
| ~PercentWaiter() override { |
| if (item_) |
| item_->RemoveObserver(this); |
| } |
| |
| bool WaitForFinished() { |
| if (item_->GetState() == DownloadItem::COMPLETE) { |
| return item_->PercentComplete() == 100; |
| } |
| waiting_ = true; |
| run_loop_.Run(); |
| waiting_ = false; |
| return !error_; |
| } |
| |
| private: |
| void OnDownloadUpdated(download::DownloadItem* item) override { |
| DCHECK_EQ(item_, item); |
| if (!error_ && |
| ((prev_percent_ > item_->PercentComplete()) || |
| (item_->GetState() == DownloadItem::COMPLETE && |
| (item_->PercentComplete() != 100)))) { |
| error_ = true; |
| if (waiting_) |
| run_loop_.QuitWhenIdle(); |
| } |
| if (item_->GetState() == DownloadItem::COMPLETE && waiting_) |
| run_loop_.QuitWhenIdle(); |
| } |
| |
| void OnDownloadDestroyed(download::DownloadItem* item) override { |
| DCHECK_EQ(item_, item); |
| item_->RemoveObserver(this); |
| item_ = nullptr; |
| } |
| |
| raw_ptr<download::DownloadItem> item_; |
| bool waiting_ = false; |
| bool error_ = false; |
| int prev_percent_ = -1; |
| base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed}; |
| }; |
| |
| // IDs and paths of CRX files used in tests. |
| const char kGoodCrxId[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf"; |
| const char kGoodCrxPath[] = "extensions/good.crx"; |
| |
| const char kLargeThemeCrxId[] = "ibcijncamhmjjdodjamgiipcgnnaeagd"; |
| const char kLargeThemePath[] = "extensions/theme2.crx"; |
| |
| // User script file used in tests. |
| const char kUserScriptPath[] = "extensions/user_script_basic.user.js"; |
| |
| // Get History Information. |
| class DownloadsHistoryDataCollector { |
| public: |
| explicit DownloadsHistoryDataCollector(Profile* profile) |
| : profile_(profile) {} |
| |
| DownloadsHistoryDataCollector(const DownloadsHistoryDataCollector&) = delete; |
| DownloadsHistoryDataCollector& operator=( |
| const DownloadsHistoryDataCollector&) = delete; |
| |
| std::vector<history::DownloadRow> WaitForDownloadInfo() { |
| std::vector<history::DownloadRow> results; |
| HistoryServiceFactory::GetForProfile(profile_, |
| ServiceAccessType::EXPLICIT_ACCESS) |
| ->QueryDownloads(base::BindLambdaForTesting( |
| [&](std::vector<history::DownloadRow> rows) { |
| results = std::move(rows); |
| run_loop_.QuitWhenIdle(); |
| })); |
| |
| run_loop_.Run(); |
| return results; |
| } |
| |
| private: |
| raw_ptr<Profile> profile_; |
| base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed}; |
| }; |
| |
| bool WasAutoOpened(DownloadItem* item) { |
| return item->GetAutoOpened(); |
| } |
| |
| bool IsDownloadExternallyRemoved(DownloadItem* item) { |
| return item->GetFileExternallyRemoved(); |
| } |
| |
| #if !BUILDFLAG(IS_CHROMEOS) |
| // Called when a download starts. Marks the download as hidden. |
| void SetHiddenDownloadCallback(DownloadItem* item, |
| download::DownloadInterruptReason reason) { |
| DownloadItemModel(item).SetShouldShowInUi(false); |
| } |
| #endif |
| |
| class SimpleDownloadManagerCoordinatorWaiter |
| : public download::SimpleDownloadManagerCoordinator::Observer { |
| public: |
| explicit SimpleDownloadManagerCoordinatorWaiter( |
| download::SimpleDownloadManagerCoordinator* coordinator) |
| : coordinator_(coordinator) { |
| coordinator_->AddObserver(this); |
| } |
| |
| ~SimpleDownloadManagerCoordinatorWaiter() override { |
| if (coordinator_) |
| coordinator_->RemoveObserver(this); |
| } |
| |
| void WaitForInitialization() { |
| if (coordinator_ && coordinator_->initialized()) |
| return; |
| base::RunLoop run_loop; |
| completion_closure_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| |
| // Wait for a particular number of download to be created. |
| void WaitForDownloadCreation(int num_download_created) { |
| if (num_download_created_ >= num_download_created) |
| return; |
| num_download_to_wait_ = num_download_created; |
| base::RunLoop run_loop; |
| download_creation_closure_ = run_loop.QuitClosure(); |
| run_loop.Run(); |
| } |
| |
| int num_download_created() const { return num_download_created_; } |
| |
| void reset_num_download_created() { num_download_created_ = 0; } |
| |
| private: |
| void OnDownloadsInitialized(bool active_downloads_only) override { |
| if (completion_closure_) |
| std::move(completion_closure_).Run(); |
| } |
| |
| void OnDownloadCreated(download::DownloadItem* item) override { |
| num_download_created_++; |
| if (download_creation_closure_ && |
| num_download_created_ >= num_download_to_wait_) { |
| std::move(download_creation_closure_).Run(); |
| } |
| } |
| |
| void OnManagerGoingDown( |
| download::SimpleDownloadManagerCoordinator* coordinator) override { |
| DCHECK_EQ(coordinator_, coordinator); |
| coordinator_->RemoveObserver(this); |
| coordinator_ = nullptr; |
| } |
| |
| raw_ptr<download::SimpleDownloadManagerCoordinator> coordinator_; |
| base::OnceClosure completion_closure_; |
| base::OnceClosure download_creation_closure_; |
| int num_download_created_ = 0; |
| int num_download_to_wait_ = 0; |
| }; |
| |
| void CreateCompletedDownload(content::DownloadManager* download_manager, |
| const std::string& guid, |
| const base::FilePath target_path, |
| std::vector<GURL> url_chain, |
| int64_t file_size) { |
| base::Time current_time = base::Time::Now(); |
| download_manager->CreateDownloadItem( |
| guid, 1 /* id */, target_path, target_path, url_chain, |
| GURL() /* referrer_url */, |
| content::StoragePartitionConfig() /* storage_partition_config */, |
| GURL() /* tab_url */, GURL() /* tab_referrer_url */, |
| url::Origin() /* request_initiator */, "" /* mime_type */, |
| "" /* original_mime_type */, current_time, current_time, "" /* etag */, |
| "" /* last_modified */, file_size, file_size, "" /* hash */, |
| download::DownloadItem::COMPLETE, |
| download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED, |
| download::DOWNLOAD_INTERRUPT_REASON_NONE, false /* opened */, |
| current_time, false /* transient */, |
| std::vector<download::DownloadItem::ReceivedSlice>()); |
| } |
| |
| #if !BUILDFLAG(IS_CHROMEOS) |
| // Whether download UI is visible at all (download toolbar button for download |
| // bubble). |
| bool IsDownloadUiVisible(BrowserWindow* window) { |
| return window->GetDownloadBubbleUIController() |
| ->GetDownloadDisplayController() |
| ->download_display_for_testing() |
| ->IsShowing(); |
| } |
| |
| // Whether download details are visible in the UI (partial view for download |
| // bubble). |
| bool IsDownloadDetailedUiVisible(BrowserWindow* window) { |
| return window->GetDownloadBubbleUIController() |
| ->GetDownloadDisplayController() |
| ->download_display_for_testing() |
| ->IsShowingDetails(); |
| } |
| #endif |
| |
| } // namespace |
| |
| class HistoryObserver : public DownloadHistory::Observer { |
| public: |
| explicit HistoryObserver(Profile* profile) : profile_(profile) { |
| DownloadCoreServiceFactory::GetForBrowserContext(profile_) |
| ->GetDownloadHistory() |
| ->AddObserver(this); |
| } |
| |
| HistoryObserver(const HistoryObserver&) = delete; |
| HistoryObserver& operator=(const HistoryObserver&) = delete; |
| |
| ~HistoryObserver() override { |
| DownloadCoreService* service = |
| DownloadCoreServiceFactory::GetForBrowserContext(profile_); |
| if (service && service->GetDownloadHistory()) |
| service->GetDownloadHistory()->RemoveObserver(this); |
| } |
| |
| void OnDownloadStored(download::DownloadItem* item, |
| const history::DownloadRow& info) override { |
| seen_stored_ = true; |
| if (waiting_) |
| run_loop_.QuitWhenIdle(); |
| } |
| |
| void OnDownloadHistoryDestroyed() override { |
| DownloadCoreServiceFactory::GetForBrowserContext(profile_) |
| ->GetDownloadHistory() |
| ->RemoveObserver(this); |
| } |
| |
| void WaitForStored() { |
| if (seen_stored_) |
| return; |
| waiting_ = true; |
| run_loop_.Run(); |
| waiting_ = false; |
| } |
| |
| private: |
| raw_ptr<Profile> profile_; |
| bool waiting_ = false; |
| bool seen_stored_ = false; |
| base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed}; |
| }; |
| |
| class DownloadReferrerPolicyTest |
| : public DownloadTestBase, |
| public ::testing::WithParamInterface<network::mojom::ReferrerPolicy> { |
| public: |
| DownloadReferrerPolicyTest() { |
| // Link Preview hides alt+click. Disables it not to do so. |
| feature_list_.InitAndDisableFeature(blink::features::kLinkPreview); |
| } |
| |
| void SetUpOnMainThread() override { |
| referrer_policy_ = GetParam(); |
| DownloadTestBase::SetUpOnMainThread(); |
| } |
| |
| protected: |
| const network::mojom::ReferrerPolicy& referrer_policy() const { |
| return referrer_policy_; |
| } |
| |
| private: |
| network::mojom::ReferrerPolicy referrer_policy_; |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| DownloadReferrerPolicyTest, |
| ::testing::Values( |
| network::mojom::ReferrerPolicy::kAlways, |
| network::mojom::ReferrerPolicy::kDefault, |
| network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade, |
| network::mojom::ReferrerPolicy::kNever, |
| network::mojom::ReferrerPolicy::kOrigin, |
| network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin, |
| network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin, |
| network::mojom::ReferrerPolicy::kSameOrigin, |
| network::mojom::ReferrerPolicy::kStrictOrigin)); |
| |
| class MPArchDownloadTest : public DownloadTestBase { |
| public: |
| MPArchDownloadTest() = default; |
| ~MPArchDownloadTest() override = default; |
| |
| void SetUpOnMainThread() override { |
| DownloadTestBase::SetUpOnMainThread(); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| } |
| |
| content::WebContents* GetWebContents() { |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| }; |
| |
| class PrerenderDownloadTest : public MPArchDownloadTest { |
| public: |
| PrerenderDownloadTest() |
| : prerender_helper_( |
| base::BindRepeating(&PrerenderDownloadTest::GetWebContents, |
| base::Unretained(this))) {} |
| ~PrerenderDownloadTest() override = default; |
| |
| void SetUp() override { |
| prerender_helper_.RegisterServerRequestMonitor(embedded_test_server()); |
| MPArchDownloadTest::SetUp(); |
| } |
| |
| content::test::PrerenderTestHelper* prerender_helper() { |
| return &prerender_helper_; |
| } |
| |
| private: |
| content::test::PrerenderTestHelper prerender_helper_; |
| }; |
| |
| namespace { |
| |
| class FakeDownloadProtectionService |
| : public safe_browsing::DownloadProtectionService { |
| public: |
| FakeDownloadProtectionService() |
| : safe_browsing::DownloadProtectionService(nullptr) {} |
| |
| void CheckClientDownload( |
| DownloadItem* download_item, |
| safe_browsing::CheckDownloadRepeatingCallback callback, |
| base::optional_ref<const std::string> password) override { |
| safe_browsing::ClientDownloadResponse::Verdict verdict = |
| fake_verdict_.value_or(safe_browsing::ClientDownloadResponse::UNCOMMON); |
| DownloadProtectionService::SetDownloadProtectionData( |
| download_item, "token", verdict, |
| safe_browsing::ClientDownloadResponse::TailoredVerdict()); |
| |
| safe_browsing::DownloadCheckResult result = |
| fake_result_.value_or(safe_browsing::DownloadCheckResult::UNCOMMON); |
| std::move(callback).Run(result); |
| } |
| |
| void SetFakeResponse(safe_browsing::DownloadCheckResult result, |
| safe_browsing::ClientDownloadResponse::Verdict verdict) { |
| fake_result_ = result; |
| fake_verdict_ = verdict; |
| } |
| |
| private: |
| std::optional<safe_browsing::DownloadCheckResult> fake_result_; |
| std::optional<safe_browsing::ClientDownloadResponse::Verdict> fake_verdict_; |
| }; |
| |
| class FakeSafeBrowsingService : public safe_browsing::TestSafeBrowsingService { |
| public: |
| FakeSafeBrowsingService() : TestSafeBrowsingService() {} |
| |
| FakeSafeBrowsingService(const FakeSafeBrowsingService&) = delete; |
| FakeSafeBrowsingService& operator=(const FakeSafeBrowsingService&) = delete; |
| |
| protected: |
| ~FakeSafeBrowsingService() override = default; |
| |
| // ServicesDelegate::ServicesCreator: |
| bool CanCreateDownloadProtectionService() override { return true; } |
| safe_browsing::DownloadProtectionService* CreateDownloadProtectionService() |
| override { |
| return new FakeDownloadProtectionService(); |
| } |
| }; |
| |
| // Factory that creates FakeSafeBrowsingService instances. |
| class TestSafeBrowsingServiceFactory |
| : public safe_browsing::SafeBrowsingServiceFactory { |
| public: |
| TestSafeBrowsingServiceFactory() = default; |
| ~TestSafeBrowsingServiceFactory() override = default; |
| |
| safe_browsing::SafeBrowsingServiceInterface* CreateSafeBrowsingService() |
| override { |
| DCHECK(!fake_safe_browsing_service_); |
| fake_safe_browsing_service_ = new FakeSafeBrowsingService(); |
| return fake_safe_browsing_service_.get(); |
| } |
| |
| scoped_refptr<FakeSafeBrowsingService> fake_safe_browsing_service() { |
| return fake_safe_browsing_service_; |
| } |
| |
| private: |
| scoped_refptr<FakeSafeBrowsingService> fake_safe_browsing_service_; |
| }; |
| |
| class DownloadTestWithFakeSafeBrowsing : public DownloadTestBase { |
| public: |
| DownloadTestWithFakeSafeBrowsing() |
| : test_safe_browsing_factory_(new TestSafeBrowsingServiceFactory()) {} |
| |
| void SetUp() override { |
| safe_browsing::SafeBrowsingServiceInterface::RegisterFactory( |
| test_safe_browsing_factory_.get()); |
| DownloadTestBase::SetUp(); |
| } |
| |
| void TearDown() override { |
| safe_browsing::SafeBrowsingServiceInterface::RegisterFactory(nullptr); |
| DownloadTestBase::TearDown(); |
| } |
| |
| protected: |
| std::unique_ptr<TestSafeBrowsingServiceFactory> test_safe_browsing_factory_; |
| }; |
| |
| class DownloadWakeLockTest : public DownloadTestBase { |
| public: |
| DownloadWakeLockTest() = default; |
| |
| DownloadWakeLockTest(const DownloadWakeLockTest&) = delete; |
| DownloadWakeLockTest& operator=(const DownloadWakeLockTest&) = delete; |
| |
| void Initialize() { |
| content::GetDeviceService().BindWakeLockProvider( |
| wake_lock_provider_.BindNewPipeAndPassReceiver()); |
| } |
| |
| // Returns the number of active wake locks of type |type|. |
| int GetActiveWakeLocks(device::mojom::WakeLockType type) { |
| base::RunLoop run_loop; |
| int result_count = 0; |
| wake_lock_provider_->GetActiveWakeLocksForTests( |
| type, |
| base::BindOnce( |
| [](base::RunLoop* run_loop, int* result_count, int32_t count) { |
| *result_count = count; |
| run_loop->Quit(); |
| }, |
| &run_loop, &result_count)); |
| run_loop.Run(); |
| return result_count; |
| } |
| |
| protected: |
| mojo::Remote<device::mojom::WakeLockProvider> wake_lock_provider_; |
| }; |
| |
| } // namespace |
| |
| using DownloadTest = DownloadTestBase; |
| |
| // NOTES: |
| // |
| // Files for these tests are found in DIR_TEST_DATA (currently |
| // "chrome\test\data\", see chrome_paths.cc). |
| // Mock responses have extension .mock-http-headers appended to the file name. |
| |
| // Download a file due to the associated MIME type. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadMimeType) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| // Download the file and wait. We do not expect the Select File dialog. |
| DownloadAndWait(browser(), url); |
| |
| // Check state. |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| CheckDownload(browser(), file, file); |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| // Download a file and confirm that the file is correctly quarantined. |
| // |
| // TODO(asanka): We should enable the test on Mac as well, but currently |
| // |browser_tests| aren't run from a process that has LSFileQuarantineEnabled |
| // bit set. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, Quarantine_DependsOnLocalConfig) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| // Download the file and wait. We do not expect the Select File dialog. |
| DownloadAndWait(browser(), url); |
| |
| // Check state. Special file state must be checked before CheckDownload, |
| // as CheckDownload will delete the output file. |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| base::FilePath downloaded_file(DestinationFile(browser(), file)); |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| EXPECT_TRUE(quarantine::IsFileQuarantined(downloaded_file, url, GURL())); |
| CheckDownload(browser(), file, file); |
| } |
| |
| // A couple of Windows specific tests to make sure we respect OS specific |
| // restrictions on Mark-Of-The-Web can be applied. While Chrome doesn't directly |
| // apply these policies, Chrome still needs to make sure the correct APIs are |
| // invoked during the download process that result in the expected MOTW |
| // behavior. |
| |
| // Downloading a file from the local host shouldn't cause the application of a |
| // zone identifier. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, CheckLocalhostZone_DependsOnLocalConfig) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // Assumes that localhost maps to 127.0.0.1. Otherwise the test will fail |
| // since EmbeddedTestServer is listening on that address. |
| GURL url = |
| embedded_test_server()->GetURL("localhost", "/downloads/a_zip_file.zip"); |
| DownloadAndWait(browser(), url); |
| base::FilePath file(FILE_PATH_LITERAL("a_zip_file.zip")); |
| base::FilePath downloaded_file(DestinationFile(browser(), file)); |
| EXPECT_FALSE(quarantine::IsFileQuarantined(downloaded_file, GURL(), GURL())); |
| } |
| |
| // Same as the test above, but uses a file:// URL to a local file. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, CheckLocalFileZone_DependsOnLocalConfig) { |
| base::FilePath source_file = GetTestDataDirectory() |
| .AppendASCII("downloads") |
| .AppendASCII("a_zip_file.zip"); |
| |
| GURL url = net::FilePathToFileURL(source_file); |
| DownloadAndWait(browser(), url); |
| base::FilePath file(FILE_PATH_LITERAL("a_zip_file.zip")); |
| base::FilePath downloaded_file(DestinationFile(browser(), file)); |
| EXPECT_FALSE(quarantine::IsFileQuarantined(downloaded_file, GURL(), GURL())); |
| } |
| #endif |
| |
| // Put up a Select File dialog when the file is downloaded, due to |
| // downloads preferences settings. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadMimeTypeSelect) { |
| // Re-enable prompting. |
| SetPromptForDownload(browser(), true); |
| |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| EnableFileChooser(true); |
| |
| // Download the file and wait. We expect the Select File dialog to appear |
| // due to the MIME type, but we still wait until the download completes. |
| std::unique_ptr<content::DownloadTestObserver> observer( |
| new content::DownloadTestObserverTerminal( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| observer->WaitForFinished(); |
| EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| EXPECT_TRUE(DidShowFileChooser()); |
| |
| // Check state. |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| CheckDownload(browser(), file, file); |
| } |
| |
| // Access a file with a viewable mime-type, verify that a download |
| // did not initiate. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, NoDownload) { |
| base::FilePath file(FILE_PATH_LITERAL("download-test2.html")); |
| |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/download-test2.html"); |
| base::FilePath file_path(DestinationFile(browser(), file)); |
| |
| // Open a web page and wait. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // Check that we did not download the web page. |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| EXPECT_FALSE(base::PathExists(file_path)); |
| |
| // Check state. |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| EXPECT_TRUE(VerifyNoDownloads()); |
| } |
| |
| // EmbeddedTestServer::HandleRequestCallback function that returns the relative |
| // URL as the MIME type. |
| // E.g.: |
| // C -> S: GET /foo/bar => |
| // S -> C: HTTP/1.1 200 OK |
| // Content-Type: foo/bar |
| // ... |
| static std::unique_ptr<net::test_server::HttpResponse> |
| RespondWithContentTypeHandler(const net::test_server::HttpRequest& request) { |
| std::unique_ptr<net::test_server::BasicHttpResponse> response( |
| new net::test_server::BasicHttpResponse()); |
| response->set_content_type(request.relative_url.substr(1)); |
| response->set_code(net::HTTP_OK); |
| response->set_content("ooogaboogaboogabooga"); |
| return std::move(response); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, MimeTypesToShowNotDownload) { |
| embedded_test_server()->RegisterRequestHandler( |
| base::BindRepeating(&RespondWithContentTypeHandler)); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // These files should all be displayed in the browser. |
| auto mime_types = std::to_array<const char*>({ |
| // It is unclear whether to display text/css or download it. |
| // Firefox 3: Display |
| // Internet Explorer 7: Download |
| // Safari 3.2: Download |
| // We choose to match Firefox due to the lot of complains |
| // from the users if css files are downloaded: |
| // http://code.google.com/p/chromium/issues/detail?id=7192 |
| "text/css", |
| "text/javascript", |
| "text/plain", |
| "application/x-javascript", |
| "text/html", |
| "text/xml", |
| "text/xsl", |
| "application/xhtml+xml", |
| "image/png", |
| "image/gif", |
| "image/jpeg", |
| "image/bmp", |
| }); |
| for (size_t i = 0; i < std::size(mime_types); ++i) { |
| const char* mime_type = mime_types[i]; |
| GURL url( |
| embedded_test_server()->GetURL(std::string("/").append(mime_type))); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // Check state. |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| EXPECT_TRUE(VerifyNoDownloads()); |
| } |
| } |
| |
| // Verify that when the DownloadResourceThrottle cancels a download, the |
| // download never makes it to the downloads system. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadResourceThrottleCancels) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| // Navigate to a page with the same domain as the file to download. We can't |
| // navigate directly to the file we don't want to download because cross-site |
| // navigations reset the TabDownloadState. |
| GURL same_site_url = embedded_test_server()->GetURL("/download_script.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), same_site_url)); |
| |
| // Make sure the initial navigation didn't trigger a download. |
| EXPECT_TRUE(VerifyNoDownloads()); |
| |
| // Disable downloads for the tab. |
| WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| DownloadRequestLimiter::TabDownloadState* tab_download_state = |
| g_browser_process->download_request_limiter()->GetDownloadState( |
| web_contents, true); |
| ASSERT_TRUE(tab_download_state); |
| tab_download_state->set_download_seen(); |
| tab_download_state->SetDownloadStatusAndNotify( |
| url::Origin::Create(same_site_url), |
| DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED); |
| |
| // Try to start the download via Javascript and wait for the corresponding |
| // load stop event. |
| content::TestNavigationObserver observer(web_contents); |
| ASSERT_EQ(true, content::EvalJs( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| "startDownload();")); |
| observer.Wait(); |
| |
| // Check that we did not download the file. |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| base::FilePath file_path(DestinationFile(browser(), file)); |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| EXPECT_FALSE(base::PathExists(file_path)); |
| |
| // Check state. |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| |
| // Verify that there's no pending download. The resource throttle |
| // should have deleted it before it created a download item, so it |
| // shouldn't be available as a cancelled download either. |
| EXPECT_TRUE(VerifyNoDownloads()); |
| } |
| |
| // Test to make sure 'download' attribute in anchor tag doesn't trigger a |
| // download if DownloadRequestLimiter disallows it. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, |
| DownloadRequestLimiterDisallowsAnchorDownloadTag) { |
| OnCanDownloadDecidedObserver can_download_observer; |
| g_browser_process->download_request_limiter() |
| ->SetOnCanDownloadDecidedCallbackForTesting(base::BindRepeating( |
| &OnCanDownloadDecidedObserver::OnCanDownloadDecided, |
| base::Unretained(&can_download_observer))); |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url(embedded_test_server()->GetURL("/download-anchor-script.html")); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| // Make sure the initial navigation didn't trigger a download. |
| EXPECT_TRUE(VerifyNoDownloads()); |
| |
| WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| DownloadRequestLimiter::TabDownloadState* tab_download_state = |
| g_browser_process->download_request_limiter()->GetDownloadState( |
| web_contents, true); |
| ASSERT_TRUE(tab_download_state); |
| // Let the first download to fail. |
| tab_download_state->set_download_seen(); |
| tab_download_state->SetDownloadStatusAndNotify( |
| url::Origin::Create(url), DownloadRequestLimiter::DOWNLOADS_NOT_ALLOWED); |
| ASSERT_EQ(true, content::EvalJs( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| "startDownload1();")); |
| can_download_observer.WaitForNumberOfDecisions(1); |
| EXPECT_FALSE(can_download_observer.GetDecisions().front()); |
| can_download_observer.Reset(); |
| |
| // Let the 2nd download to succeed. |
| std::unique_ptr<content::DownloadTestObserver> observer( |
| CreateWaiter(browser(), 1)); |
| tab_download_state->SetDownloadStatusAndNotify( |
| url::Origin::Create(url), DownloadRequestLimiter::ALLOW_ALL_DOWNLOADS); |
| ASSERT_EQ(true, content::EvalJs( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| "startDownload2();")); |
| can_download_observer.WaitForNumberOfDecisions(1); |
| EXPECT_TRUE(can_download_observer.GetDecisions().front()); |
| |
| // Waits for the 2nd download to complete. |
| observer->WaitForFinished(); |
| |
| // Check that only the 2nd file is downloaded. |
| base::FilePath file1(FILE_PATH_LITERAL("red_dot1.png")); |
| base::FilePath file_path1(DestinationFile(browser(), file1)); |
| base::FilePath file2(FILE_PATH_LITERAL("red_dot2.png")); |
| base::FilePath file_path2(DestinationFile(browser(), file2)); |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| EXPECT_FALSE(base::PathExists(file_path1)); |
| EXPECT_TRUE(base::PathExists(file_path2)); |
| } |
| |
| // Verify that non-active main frame downloads (e.g. prerendering) don't affect |
| // the DownloadRequestLimiter state of the WebContents. |
| IN_PROC_BROWSER_TEST_F(PrerenderDownloadTest, |
| DownloadRequestLimiterIsUnaffectedByPrerendering) { |
| const GURL kInitialUrl = |
| embedded_test_server()->GetURL("/download_script.html"); |
| const GURL kPrerenderingUrl = embedded_test_server()->GetURL("/empty.html"); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kInitialUrl)); |
| |
| // Set the initial DownloadRequestLimiter state to prompt for downloads and |
| // deny all requests. This allows to check whether a prerender resets the |
| // state, since PROMPT_BEFORE_DOWNLOAD is reset by any navigation, while |
| // DOWNLOADS_NOT_ALLOWED require a cross-site navigation to be reset and |
| // those cannot be done in prerendering. |
| auto* web_contents = GetWebContents(); |
| DownloadRequestLimiter::TabDownloadState* tab_download_state = |
| g_browser_process->download_request_limiter()->GetDownloadState( |
| web_contents, true); |
| ASSERT_TRUE(tab_download_state); |
| tab_download_state->SetDownloadStatusAndNotify( |
| url::Origin::Create(kInitialUrl), |
| DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD); |
| permissions::PermissionRequestManager::FromWebContents(web_contents) |
| ->set_auto_response_for_test( |
| permissions::PermissionRequestManager::DENY_ALL); |
| |
| // Launch a prerendering page. |
| const content::FrameTreeNodeId host_id = |
| prerender_helper()->AddPrerender(kPrerenderingUrl); |
| ASSERT_TRUE(host_id); |
| content::test::PrerenderHostObserver host_observer(*web_contents, host_id); |
| |
| // Check that the tab download state wasn't reset by the initial prerender |
| // navigation (a primary main frame navigation would have reset it as seen in |
| // the test DownloadRequestLimiterTest.ResetOnNavigation). |
| ASSERT_EQ(tab_download_state, |
| g_browser_process->download_request_limiter()->GetDownloadState( |
| web_contents, false)); |
| ASSERT_EQ(tab_download_state->download_status(), |
| DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD); |
| |
| // Attempt a download. |
| OnCanDownloadDecidedObserver can_download_observer; |
| g_browser_process->download_request_limiter() |
| ->SetOnCanDownloadDecidedCallbackForTesting(base::BindRepeating( |
| &OnCanDownloadDecidedObserver::OnCanDownloadDecided, |
| base::Unretained(&can_download_observer))); |
| ASSERT_EQ(true, content::EvalJs(web_contents, "startDownload();")); |
| can_download_observer.WaitForNumberOfDecisions(1); |
| EXPECT_FALSE(can_download_observer.GetDecisions().front()); |
| |
| // Check that the download didn't succeed. |
| const base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| const base::FilePath file_path(DestinationFile(browser(), file)); |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| EXPECT_FALSE(base::PathExists(file_path)); |
| |
| EXPECT_TRUE(VerifyNoDownloads()); |
| } |
| |
| class FencedFrameDownloadTest : public MPArchDownloadTest { |
| public: |
| FencedFrameDownloadTest() = default; |
| ~FencedFrameDownloadTest() override = default; |
| FencedFrameDownloadTest(const FencedFrameDownloadTest&) = delete; |
| |
| FencedFrameDownloadTest& operator=(const FencedFrameDownloadTest&) = delete; |
| |
| void SetUpOnMainThread() override { |
| MPArchDownloadTest::SetUpOnMainThread(); |
| https_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| } |
| |
| content::test::FencedFrameTestHelper& fenced_frame_test_helper() { |
| return fenced_frame_helper_; |
| } |
| |
| private: |
| content::test::FencedFrameTestHelper fenced_frame_helper_; |
| }; |
| |
| // Verify that fenced frame downloads don't affect the DownloadRequestLimiter |
| // state of the WebContents. |
| IN_PROC_BROWSER_TEST_F(FencedFrameDownloadTest, |
| DownloadRequestLimiterIsUnaffectedByFencedFrame) { |
| const GURL kInitialUrl = |
| embedded_test_server()->GetURL("/download_script.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kInitialUrl)); |
| |
| // Set the initial DownloadRequestLimiter state to prompt for downloads and |
| // deny all requests. This allows to check whether a fenced frame resets the |
| // state, since PROMPT_BEFORE_DOWNLOAD is reset by any navigation, while |
| // DOWNLOADS_NOT_ALLOWED require a cross-site navigation to be reset and |
| // those cannot be done in a fenced frame. |
| auto* web_contents = GetWebContents(); |
| DownloadRequestLimiter::TabDownloadState* tab_download_state = |
| g_browser_process->download_request_limiter()->GetDownloadState( |
| web_contents, true); |
| ASSERT_TRUE(tab_download_state); |
| tab_download_state->SetDownloadStatusAndNotify( |
| url::Origin::Create(kInitialUrl), |
| DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD); |
| permissions::PermissionRequestManager::FromWebContents(web_contents) |
| ->set_auto_response_for_test( |
| permissions::PermissionRequestManager::DENY_ALL); |
| |
| // Create a fenced frame and load a URL. |
| const GURL kFencedFrameUrl = |
| embedded_test_server()->GetURL("/fenced_frames/title1.html"); |
| content::RenderFrameHost* fenced_frame_host = |
| fenced_frame_test_helper().CreateFencedFrame( |
| GetWebContents()->GetPrimaryMainFrame(), kFencedFrameUrl); |
| EXPECT_NE(nullptr, fenced_frame_host); |
| |
| // Check that the tab download state wasn't reset by the navigation on the |
| // fenced frame (a primary main frame navigation would have reset it as seen |
| // in the test DownloadRequestLimiterTest.ResetOnNavigation). |
| ASSERT_EQ(tab_download_state, |
| g_browser_process->download_request_limiter()->GetDownloadState( |
| web_contents, false)); |
| ASSERT_EQ(tab_download_state->download_status(), |
| DownloadRequestLimiter::PROMPT_BEFORE_DOWNLOAD); |
| |
| // Attempt a download. |
| OnCanDownloadDecidedObserver can_download_observer; |
| g_browser_process->download_request_limiter() |
| ->SetOnCanDownloadDecidedCallbackForTesting(base::BindRepeating( |
| &OnCanDownloadDecidedObserver::OnCanDownloadDecided, |
| base::Unretained(&can_download_observer))); |
| ASSERT_EQ(true, content::EvalJs(web_contents, "startDownload();")); |
| can_download_observer.WaitForNumberOfDecisions(1); |
| EXPECT_FALSE(can_download_observer.GetDecisions().front()); |
| |
| // Check that the download didn't succeed. |
| const base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| const base::FilePath file_path(DestinationFile(browser(), file)); |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| EXPECT_FALSE(base::PathExists(file_path)); |
| |
| EXPECT_TRUE(VerifyNoDownloads()); |
| } |
| |
| // Fenced frame forces download sandbox flag, which should prevent downloads |
| // from fenced frames. |
| IN_PROC_BROWSER_TEST_F(FencedFrameDownloadTest, |
| FencedFrameSandboxFlagBlockDownload) { |
| ASSERT_TRUE(https_test_server()->Start()); |
| const GURL main_url = https_test_server()->GetURL("a.test", "/title1.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url)); |
| |
| const GURL fenced_frame_url = |
| https_test_server()->GetURL("a.test", "/fenced_frames/title1.html"); |
| content::RenderFrameHost* fenced_frame_rfh = |
| fenced_frame_test_helper().CreateFencedFrame( |
| GetWebContents()->GetPrimaryMainFrame(), fenced_frame_url); |
| |
| content::WebContentsConsoleObserver console_observer(GetWebContents()); |
| console_observer.SetPattern("*Download is disallowed*"); |
| |
| // Attempt a download by clicking the anchor element with download attribute |
| // within the fenced frame. |
| constexpr char kADownloadScript[] = R"( |
| var a = document.createElement('a'); |
| a.setAttribute('href', 'foo.zip'); |
| a.download = ''; |
| document.body.appendChild(a); |
| a.click(); |
| )"; |
| |
| EXPECT_TRUE(ExecJs(fenced_frame_rfh, kADownloadScript)); |
| EXPECT_TRUE(console_observer.Wait()); |
| ASSERT_FALSE(console_observer.messages().empty()); |
| EXPECT_EQ(console_observer.GetMessageAt(0), |
| "Download is disallowed. The frame initiating or instantiating the " |
| "download is sandboxed, but the flag ‘allow-downloads’ is not set. " |
| "See https://www.chromestatus.com/feature/5706745674465280 for " |
| "more details."); |
| EXPECT_TRUE(VerifyNoDownloads()); |
| } |
| |
| // Download a 0-size file with a content-disposition header, verify that the |
| // download tab opened and the file exists as the filename specified in the |
| // header. This also ensures we properly handle empty file downloads. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, ContentDisposition) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/download-test3.gif"); |
| |
| base::FilePath download_file( |
| FILE_PATH_LITERAL("download-test3-attachment.gif")); |
| |
| // Download a file and wait. |
| DownloadAndWait(browser(), url); |
| |
| base::FilePath file(FILE_PATH_LITERAL("download-test3.gif")); |
| CheckDownload(browser(), download_file, file); |
| |
| // Check state. |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| } |
| |
| // UnknownSize and KnownSize are tests which depend on |
| // SlowDownloadHttpResponse to serve content in a certain way. Data will be |
| // sent in two chunks where the first chunk is 35K and the second chunk is 10K. |
| // The test will first attempt to download a file; but the server will "pause" |
| // in the middle until the server receives a second request for |
| // "download-finish". At that time, the download will finish. |
| // These tests don't currently test much due to holes in |RunSizeTest()|. See |
| // comments in that routine for details. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, UnknownSize) { |
| ASSERT_TRUE(RunSizeTest(browser(), SIZE_TEST_TYPE_UNKNOWN, |
| "32.0 KB - ", "100% - ")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, KnownSize) { |
| ASSERT_TRUE(RunSizeTest(browser(), SIZE_TEST_TYPE_KNOWN, |
| "71% - ", "100% - ")); |
| } |
| |
| // Test that when downloading an item in Incognito mode, we don't crash when |
| // closing the last Incognito window (http://crbug.com/13983). |
| IN_PROC_BROWSER_TEST_F(DownloadTest, IncognitoDownload) { |
| Browser* incognito = CreateIncognitoBrowser(); |
| ASSERT_TRUE(incognito); |
| int window_count = chrome::GetTotalBrowserCount(); |
| EXPECT_EQ(2, window_count); |
| |
| // Download a file in the Incognito window and wait. |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| // Since |incognito| is a separate browser, we have to set it up explicitly. |
| incognito->profile()->GetPrefs()->SetBoolean(prefs::kPromptForDownload, |
| false); |
| DownloadAndWait(incognito, url); |
| |
| // We should still have 2 windows. |
| ExpectWindowCountAfterDownload(2); |
| |
| // Close the Incognito window and don't crash. |
| chrome::CloseWindow(incognito); |
| |
| ui_test_utils::WaitForBrowserToClose(incognito); |
| ExpectWindowCountAfterDownload(1); |
| |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| CheckDownload(browser(), file, file); |
| } |
| |
| // Download one file on-record, then download the same file off-record, and test |
| // that the filename is deduplicated. The previous test tests for a specific |
| // bug; this next test tests that filename deduplication happens independently |
| // of DownloadManager/CDMD. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadTest_IncognitoRegular) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/downloads/a_zip_file.zip"); |
| |
| // Read the origin file now so that we can compare the downloaded files to it |
| // later. |
| base::FilePath origin(OriginFile(base::FilePath(FILE_PATH_LITERAL( |
| "downloads/a_zip_file.zip")))); |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| std::optional<int64_t> origin_file_size = base::GetFileSize(origin); |
| ASSERT_TRUE(origin_file_size.has_value()); |
| std::string original_contents; |
| EXPECT_TRUE(base::ReadFileToString(origin, &original_contents)); |
| |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> download_items; |
| GetDownloads(browser(), &download_items); |
| ASSERT_TRUE(download_items.empty()); |
| |
| // Download a file in the on-record browser and check that it was downloaded |
| // correctly. |
| DownloadAndWaitWithDisposition(browser(), url, |
| WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NO_WAIT); |
| GetDownloads(browser(), &download_items); |
| ASSERT_EQ(1UL, download_items.size()); |
| ASSERT_EQ(base::FilePath(FILE_PATH_LITERAL("a_zip_file.zip")), |
| download_items[0]->GetTargetFilePath().BaseName()); |
| ASSERT_TRUE(base::PathExists(download_items[0]->GetTargetFilePath())); |
| EXPECT_TRUE(VerifyFile(download_items[0]->GetTargetFilePath(), |
| original_contents, origin_file_size.value())); |
| uint32_t download_id = download_items[0]->GetId(); |
| // Verify that manager will increment the download ID when a new download is |
| // requested. |
| DownloadManagerForBrowser(browser())->GetNextId( |
| base::BindOnce(&VerifyNewDownloadId, download_id + 1)); |
| |
| // Setup an incognito window. |
| Browser* incognito = CreateIncognitoBrowser(); |
| ASSERT_TRUE(incognito); |
| int window_count = BrowserList::GetInstance()->size(); |
| EXPECT_EQ(2, window_count); |
| |
| download_items.clear(); |
| GetDownloads(incognito, &download_items); |
| ASSERT_TRUE(download_items.empty()); |
| |
| // Download a file in the incognito browser and check that it was downloaded |
| // correctly. |
| DownloadAndWaitWithDisposition(incognito, url, |
| WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NO_WAIT); |
| GetDownloads(incognito, &download_items); |
| ASSERT_EQ(1UL, download_items.size()); |
| ASSERT_EQ(base::FilePath(FILE_PATH_LITERAL("a_zip_file (1).zip")), |
| download_items[0]->GetTargetFilePath().BaseName()); |
| ASSERT_TRUE(base::PathExists(download_items[0]->GetTargetFilePath())); |
| EXPECT_TRUE(VerifyFile(download_items[0]->GetTargetFilePath(), |
| original_contents, origin_file_size.value())); |
| // The incognito download should increment the download ID again. |
| ASSERT_EQ(download_id + 2, download_items[0]->GetId()); |
| } |
| |
| // Navigate to a new background page, but don't download. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DontCloseNewTab1) { |
| // Because it's an HTML link, it should open a web page rather than |
| // downloading. |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/download-test2.html"); |
| |
| // Open a web page and wait. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::NEW_BACKGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // We should have two tabs now. |
| EXPECT_EQ(2, browser()->tab_strip_model()->count()); |
| EXPECT_TRUE(VerifyNoDownloads()); |
| } |
| |
| // Download a file in a background tab. Verify that the tab is closed |
| // automatically. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, CloseNewTab1) { |
| // Download a file in a new background tab and wait. The tab is automatically |
| // closed when the download begins. |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| DownloadAndWaitWithDisposition(browser(), url, |
| WindowOpenDisposition::NEW_BACKGROUND_TAB, 0); |
| |
| // When the download finishes, we should still have one tab. |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| CheckDownload(browser(), file, file); |
| } |
| |
| // Open a web page in the current tab, then download a file in another tab via |
| // a Javascript call. |
| // Verify that we have 2 tabs. |
| // |
| // The download_page1.html page contains an openNew() function that opens a |
| // tab and then downloads download-test1.lib. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DontCloseNewTab2) { |
| // Because it's an HTML link, it should open a web page rather than |
| // downloading. |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/download_page1.html"); |
| |
| // Open a web page and wait. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // Download a file in a new tab and wait (via Javascript). |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| DownloadAndWaitWithDisposition(browser(), GURL("javascript:openNew()"), |
| WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); |
| |
| // When the download finishes, we should have two tabs. |
| EXPECT_EQ(2, browser()->tab_strip_model()->count()); |
| |
| CheckDownload(browser(), file, file); |
| } |
| |
| // Open a web page in the current tab, open another tab via a Javascript call, |
| // then download a file in the new tab. |
| // Verify that we have 2 tabs. |
| // |
| // The download_page2.html page contains an openNew() function that opens a |
| // tab. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DontCloseNewTab3) { |
| // Because it's an HTML link, it should open a web page rather than |
| // downloading. |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url1 = embedded_test_server()->GetURL("/download_page2.html"); |
| |
| // Open a web page and wait. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url1)); |
| |
| // Open a new tab and wait. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL("javascript:openNew()"), |
| WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); |
| |
| EXPECT_EQ(2, browser()->tab_strip_model()->count()); |
| |
| // Download a file and wait. |
| GURL url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| DownloadAndWaitWithDisposition(browser(), url, |
| WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NO_WAIT); |
| |
| // When the download finishes, we should have two tabs. |
| EXPECT_EQ(2, browser()->tab_strip_model()->count()); |
| |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| CheckDownload(browser(), file, file); |
| } |
| |
| // Open a web page in the current tab, then download a file via Javascript, |
| // which will do so in a temporary tab. Verify that we have 1 tab. |
| // |
| // The download_page3.html page contains an openNew() function that opens a |
| // tab with download-test1.lib in the URL. When the URL is determined to be |
| // a download, the tab is closed automatically. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, CloseNewTab2) { |
| // Because it's an HTML link, it should open a web page rather than |
| // downloading. |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/download_page3.html"); |
| |
| // Open a web page and wait. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // Download a file and wait. |
| // The file to download is "download-test1.lib". |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| DownloadAndWaitWithDisposition(browser(), GURL("javascript:openNew()"), |
| WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); |
| |
| // When the download finishes, we should still have one tab. |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| |
| CheckDownload(browser(), file, file); |
| } |
| |
| // Open a web page in the current tab, then call Javascript via a button to |
| // download a file in a new tab, which is closed automatically when the |
| // download begins. |
| // Verify that we have 1 tab. |
| // |
| // The download_page4.html page contains a form with download-test1.lib as the |
| // action. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, CloseNewTab3) { |
| // Because it's an HTML link, it should open a web page rather than |
| // downloading. |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/download_page4.html"); |
| |
| // Open a web page and wait. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // Download a file in a new tab and wait. The tab will automatically close |
| // when the download begins. |
| // The file to download is "download-test1.lib". |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| DownloadAndWaitWithDisposition( |
| browser(), GURL("javascript:document.getElementById('form').submit()"), |
| WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); |
| |
| // When the download finishes, we should still have one tab. |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| |
| CheckDownload(browser(), file, file); |
| } |
| |
| // Open a second tab, then download a file in that tab. However, have the |
| // download be canceled by having the file picker act like the user canceled |
| // the download. The 2nd tab should be closed automatically. |
| // TODO(xingliu): Figure out why this is working for network service. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, CloseNewTab4) { |
| std::unique_ptr<content::DownloadTestObserver> observer( |
| CreateWaiter(browser(), 1)); |
| DownloadManager* manager = DownloadManagerForBrowser(browser()); |
| EXPECT_EQ(0, manager->InProgressCount()); |
| EnableFileChooser(false); |
| |
| // Get the download URL |
| embedded_test_server()->RegisterRequestHandler(base::BindRepeating( |
| &content::SlowDownloadHttpResponse::HandleSlowDownloadRequest)); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| GURL slow_download_url = embedded_test_server()->GetURL( |
| content::SlowDownloadHttpResponse::kUnknownSizeUrl); |
| |
| // Open a new tab for the download |
| content::WebContents* tab = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| std::unique_ptr<content::WebContents> new_tab = content::WebContents::Create( |
| content::WebContents::CreateParams(tab->GetBrowserContext())); |
| content::WebContents* raw_new_tab = new_tab.get(); |
| ASSERT_TRUE(raw_new_tab); |
| ASSERT_TRUE(raw_new_tab->GetController().IsInitialNavigation()); |
| browser()->tab_strip_model()->AppendWebContents(std::move(new_tab), true); |
| EXPECT_EQ(2, browser()->tab_strip_model()->count()); |
| |
| // Download a file in that new tab, having it open a file picker |
| std::unique_ptr<DownloadUrlParameters> params( |
| content::DownloadRequestUtils::CreateDownloadForWebContentsMainFrame( |
| raw_new_tab, slow_download_url, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| params->set_prompt(true); |
| manager->DownloadUrl(std::move(params)); |
| observer->WaitForFinished(); |
| |
| DownloadManager::DownloadVector items; |
| manager->GetAllDownloads(&items); |
| ASSERT_NE(0u, items.size()); |
| DownloadItem* item = items[0]; |
| ASSERT_TRUE(item); |
| |
| // When the download is canceled, the second tab should close. |
| EXPECT_EQ(item->GetState(), DownloadItem::CANCELLED); |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| } |
| |
| // EmbeddedTestServer::HandleRequestCallback function that responds with a |
| // redirect to the URL specified via a query string. |
| // E.g.: |
| // C -> S: GET /redirect?http://example.com |
| // S -> C: HTTP/1.1 301 Moved Permanently |
| // Location: http://example.com |
| // ... |
| static std::unique_ptr<net::test_server::HttpResponse> |
| ServerRedirectRequestHandler(const net::test_server::HttpRequest& request) { |
| if (!base::StartsWith(request.relative_url, "/redirect", |
| base::CompareCase::SENSITIVE)) { |
| return nullptr; |
| } |
| |
| std::unique_ptr<net::test_server::BasicHttpResponse> response( |
| new net::test_server::BasicHttpResponse()); |
| size_t query_position = request.relative_url.find('?'); |
| |
| if (query_position == std::string::npos) { |
| response->set_code(net::HTTP_PERMANENT_REDIRECT); |
| response->AddCustomHeader("Location", |
| "https://request-had-no-query-string"); |
| response->set_content_type("text/plain"); |
| response->set_content("Error"); |
| return std::move(response); |
| } |
| |
| response->set_code(net::HTTP_PERMANENT_REDIRECT); |
| response->AddCustomHeader("Location", |
| request.relative_url.substr(query_position + 1)); |
| response->set_content_type("text/plain"); |
| response->set_content("It's gone!"); |
| return std::move(response); |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| // https://crbug.com/788160 |
| #define MAYBE_DownloadHistoryCheck DISABLED_DownloadHistoryCheck |
| #else |
| #define MAYBE_DownloadHistoryCheck DownloadHistoryCheck |
| #endif |
| IN_PROC_BROWSER_TEST_F(DownloadTest, MAYBE_DownloadHistoryCheck) { |
| // Rediret to the actual download URL. |
| embedded_test_server()->RegisterRequestHandler( |
| base::BindRepeating(&ServerRedirectRequestHandler)); |
| embedded_test_server()->RegisterRequestHandler(base::BindRepeating( |
| &content::SlowDownloadHttpResponse::HandleSlowDownloadRequest)); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| GURL download_url = embedded_test_server()->GetURL( |
| content::SlowDownloadHttpResponse::kKnownSizeUrl); |
| GURL redirect_url = |
| embedded_test_server()->GetURL("/redirect?" + download_url.spec()); |
| |
| // Inject an error. |
| using TestFileErrorInjector = content::TestFileErrorInjector; |
| scoped_refptr<TestFileErrorInjector> injector( |
| TestFileErrorInjector::Create(DownloadManagerForBrowser(browser()))); |
| TestFileErrorInjector::FileErrorInfo error_info = { |
| TestFileErrorInjector::FILE_OPERATION_STREAM_COMPLETE, 0, |
| download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT}; |
| error_info.stream_offset = 0; |
| error_info.stream_bytes_written = 1024; |
| injector->InjectError(error_info); |
| |
| base::FilePath file(net::GenerateFileName(download_url, |
| std::string(), |
| std::string(), |
| std::string(), |
| std::string(), |
| std::string())); |
| |
| // Download the url and wait until the object has been stored. |
| base::Time start(base::Time::Now()); |
| HistoryObserver observer(browser()->profile()); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), redirect_url)); |
| |
| // Finish the download. We're ok relying on the history to be flushed |
| // at this point as our queries will be behind the history updates |
| // invoked by completion. |
| std::unique_ptr<content::DownloadTestObserver> download_observer( |
| new content::DownloadTestObserverInterrupted( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| |
| // Finsih the download. |
| GURL finish_url = embedded_test_server()->GetURL( |
| content::SlowDownloadHttpResponse::kFinishSlowResponseUrl); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), finish_url)); |
| |
| download_observer->WaitForFinished(); |
| EXPECT_EQ(1u, download_observer->NumDownloadsSeenInState( |
| DownloadItem::INTERRUPTED)); |
| base::Time end(base::Time::Now()); |
| |
| // Get what was stored in the history. |
| observer.WaitForStored(); |
| // Get the details on what was stored into the history. |
| std::vector<history::DownloadRow> downloads_in_database = |
| DownloadsHistoryDataCollector(browser()->profile()).WaitForDownloadInfo(); |
| ASSERT_EQ(1u, downloads_in_database.size()); |
| |
| // Confirm history storage is what you expect for an interrupted slow download |
| // job. The download isn't continuable, so there's no intermediate file. |
| history::DownloadRow& row1(downloads_in_database[0]); |
| EXPECT_EQ(DestinationFile(browser(), file), row1.target_path); |
| EXPECT_TRUE(row1.current_path.empty()); |
| ASSERT_EQ(2u, row1.url_chain.size()); |
| EXPECT_EQ(redirect_url.spec(), row1.url_chain[0].spec()); |
| EXPECT_EQ(download_url.spec(), row1.url_chain[1].spec()); |
| EXPECT_EQ(history::DownloadDangerType::MAYBE_DANGEROUS_CONTENT, |
| row1.danger_type); |
| EXPECT_LE(start, row1.start_time); |
| EXPECT_GE(end, row1.end_time); |
| EXPECT_EQ(0, row1.received_bytes); // There's no ETag. So the intermediate |
| // state is discarded. |
| EXPECT_EQ(content::SlowDownloadHttpResponse::kFirstResponsePartSize + |
| content::SlowDownloadHttpResponse::kSecondResponsePartSize, |
| row1.total_bytes); |
| EXPECT_EQ(history::DownloadState::INTERRUPTED, row1.state); |
| EXPECT_EQ(history::ToHistoryDownloadInterruptReason( |
| download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT), |
| row1.interrupt_reason); |
| EXPECT_FALSE(row1.opened); |
| } |
| |
| // Make sure a dangerous file shows up properly in the history. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadHistoryDangerCheck) { |
| // Disable SafeBrowsing so that danger will be determined by downloads system. |
| browser()->profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, |
| false); |
| |
| // .swf file so that it's dangerous on all platforms (including CrOS). |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL download_url = |
| embedded_test_server()->GetURL("/downloads/dangerous/dangerous.swf"); |
| |
| // Download the url and wait until the object has been stored. |
| auto completion_observer = |
| std::make_unique<content::DownloadTestObserverTerminal>( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_IGNORE); |
| auto dangerous_observer = |
| std::make_unique<content::DownloadTestObserverTerminal>( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_QUIT); |
| base::Time start(base::Time::Now()); |
| HistoryObserver observer(browser()->profile()); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), download_url)); |
| |
| // Validate the download and wait for it to finish. |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> downloads; |
| dangerous_observer->WaitForFinished(); |
| DownloadManagerForBrowser(browser())->GetAllDownloads(&downloads); |
| ASSERT_EQ(1u, downloads.size()); |
| downloads[0]->ValidateDangerousDownload(); |
| completion_observer->WaitForFinished(); |
| EXPECT_EQ(1u, completion_observer->NumDangerousDownloadsSeen()); |
| |
| // Get history details and confirm it's what you expect. |
| observer.WaitForStored(); |
| std::vector<history::DownloadRow> downloads_in_database = |
| DownloadsHistoryDataCollector(browser()->profile()).WaitForDownloadInfo(); |
| ASSERT_EQ(1u, downloads_in_database.size()); |
| history::DownloadRow& row1(downloads_in_database[0]); |
| base::FilePath file(FILE_PATH_LITERAL("downloads/dangerous/dangerous.swf")); |
| EXPECT_EQ(DestinationFile(browser(), file), row1.target_path); |
| EXPECT_EQ(DestinationFile(browser(), file), row1.current_path); |
| EXPECT_EQ(history::DownloadDangerType::USER_VALIDATED, row1.danger_type); |
| EXPECT_LE(start, row1.start_time); |
| EXPECT_EQ(history::DownloadState::COMPLETE, row1.state); |
| EXPECT_FALSE(row1.opened); |
| // Not checking file size--not relevant to the point of the test, and |
| // the file size is actually different on Windows and other platforms, |
| // because for source control simplicity it's actually a text file, and |
| // there are CRLF transformations for those files. |
| } |
| |
| // Test for crbug.com/14505. This tests that chrome:// urls are still functional |
| // after download of a file while viewing another chrome://. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, ChromeURLAfterDownload) { |
| GURL flags_url(chrome::kChromeUIFlagsURL); |
| GURL extensions_url(chrome::kChromeUIExtensionsURL); |
| |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL download_url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), flags_url)); |
| DownloadAndWait(browser(), download_url); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), extensions_url)); |
| WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(contents); |
| EXPECT_EQ(true, content::EvalJs(contents, |
| R"( |
| new Promise(resolve => { |
| chrome.developerPrivate.getExtensionsInfo(function(info) { |
| resolve(!!info && !chrome.runtime.lastError); |
| }); |
| }); |
| )")); |
| } |
| |
| // Test for crbug.com/12745. This tests that if a download is initiated from |
| // a chrome:// page that has registered and onunload handler, the browser |
| // will be able to close. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, BrowserCloseAfterDownload) { |
| GURL downloads_url(chrome::kChromeUIFlagsURL); |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL download_url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), downloads_url)); |
| WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(contents); |
| EXPECT_EQ(true, content::EvalJs( |
| contents, |
| "window.onunload = function() { var do_nothing = 0; }; " |
| "true;")); |
| |
| DownloadAndWait(browser(), download_url); |
| |
| CloseBrowserSynchronously(browser()); |
| } |
| |
| // Test to make sure the 'download' attribute in anchor tag is respected. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, AnchorDownloadTag) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/download-anchor-attrib.html"); |
| |
| // Create a download, wait until it's complete, and confirm |
| // we're in the expected state. |
| std::unique_ptr<content::DownloadTestObserver> observer( |
| CreateWaiter(browser(), 1)); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| observer->WaitForFinished(); |
| EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| |
| // Confirm the downloaded data exists. |
| base::FilePath downloaded_file = GetDownloadDirectory(browser()); |
| downloaded_file = downloaded_file.Append(FILE_PATH_LITERAL("a_red_dot.png")); |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| EXPECT_TRUE(base::PathExists(downloaded_file)); |
| } |
| |
| // Test that navigating to a user script URL will result in a download. |
| // TODO(crbug.com/380333248): Re-enable this test |
| #if BUILDFLAG(IS_LINUX) || \ |
| (BUILDFLAG(IS_CHROMEOS) && defined(ADDRESS_SANITIZER)) |
| #define MAYBE_UserScriptDownload DISABLED_UserScriptDownload |
| #else |
| #define MAYBE_UserScriptDownload UserScriptDownload |
| #endif |
| IN_PROC_BROWSER_TEST_F(DownloadTest, MAYBE_UserScriptDownload) { |
| DownloadTestContentBrowserClient new_client(true); |
| content::ContentBrowserClient* old_client = |
| SetBrowserClientForTesting(&new_client); |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/" + std::string(kUserScriptPath)); |
| |
| // Navigate to the user script URL and wait for the download to complete. |
| std::unique_ptr<content::DownloadTestObserver> observer( |
| DangerousDownloadWaiter( |
| browser(), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT)); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| observer->WaitForFinished(); |
| EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| SetBrowserClientForTesting(old_client); |
| } |
| |
| // Test to make sure auto-open works. |
| // High flake rate; https://crbug.com/1247392. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DISABLED_AutoOpenByUser) { |
| base::FilePath file(FILE_PATH_LITERAL("download-autoopen.txt")); |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/download-autoopen.txt"); |
| |
| ASSERT_TRUE( |
| GetDownloadPrefs(browser())->EnableAutoOpenByUserBasedOnExtension(file)); |
| |
| DownloadAndWait(browser(), url); |
| |
| // Find the download and confirm it was opened. |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> downloads; |
| DownloadManagerForBrowser(browser())->GetAllDownloads(&downloads); |
| ASSERT_EQ(1u, downloads.size()); |
| EXPECT_EQ(DownloadItem::COMPLETE, downloads[0]->GetState()); |
| |
| // Unfortunately, this will block forever, causing a timeout, if |
| // the download is never opened. |
| content::DownloadUpdatedObserver(downloads[0], |
| base::BindRepeating(&WasAutoOpened)) |
| .WaitForEvent(); |
| EXPECT_TRUE(downloads[0]->GetOpened()); // Confirm it anyway. |
| |
| // As long as we're here, confirmed everything else is good. |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| CheckDownload(browser(), file, file); |
| } |
| |
| // Download an extension. Expect a dangerous download warning. |
| // Deny the download. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, CrxDenyInstall) { |
| std::unique_ptr<base::AutoReset<bool>> allow_offstore_install = |
| download_crx_util::OverrideOffstoreInstallAllowedForTesting(true); |
| |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL extension_url = |
| embedded_test_server()->GetURL("/" + std::string(kGoodCrxPath)); |
| |
| std::unique_ptr<content::DownloadTestObserver> observer( |
| DangerousDownloadWaiter( |
| browser(), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_DENY)); |
| |
| NavigateParams params(browser(), extension_url, ui::PAGE_TRANSITION_TYPED); |
| params.user_gesture = false; |
| ui_test_utils::NavigateToURL(¶ms); |
| |
| observer->WaitForFinished(); |
| EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::CANCELLED)); |
| EXPECT_EQ(1u, observer->NumDangerousDownloadsSeen()); |
| EXPECT_TRUE(VerifyNoDownloads()); |
| |
| // Check that the CRX is not installed. |
| extensions::ExtensionRegistry* extension_registry = |
| extensions::ExtensionRegistry::Get(browser()->profile()); |
| ASSERT_FALSE(extension_registry->enabled_extensions().Contains(kGoodCrxId)); |
| } |
| |
| // Download an extension. Expect a dangerous download warning. |
| // Allow the download, deny the install. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, CrxInstallDenysPermissions) { |
| std::unique_ptr<base::AutoReset<bool>> allow_offstore_install = |
| download_crx_util::OverrideOffstoreInstallAllowedForTesting(true); |
| extensions::ScopedTestDialogAutoConfirm auto_confirm_install_prompt( |
| extensions::ScopedTestDialogAutoConfirm::CANCEL); |
| |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL extension_url = |
| embedded_test_server()->GetURL("/" + std::string(kGoodCrxPath)); |
| |
| std::unique_ptr<content::DownloadTestObserver> observer( |
| DangerousDownloadWaiter( |
| browser(), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT)); |
| NavigateParams params(browser(), extension_url, ui::PAGE_TRANSITION_TYPED); |
| params.user_gesture = false; |
| ui_test_utils::NavigateToURL(¶ms); |
| |
| observer->WaitForFinished(); |
| EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| EXPECT_EQ(1u, observer->NumDangerousDownloadsSeen()); |
| |
| content::DownloadManager::DownloadVector downloads; |
| DownloadManagerForBrowser(browser())->GetAllDownloads(&downloads); |
| ASSERT_EQ(1u, downloads.size()); |
| content::DownloadUpdatedObserver(downloads[0], |
| base::BindRepeating(&WasAutoOpened)) |
| .WaitForEvent(); |
| |
| // Check that the extension was not installed. |
| extensions::ExtensionRegistry* extension_registry = |
| extensions::ExtensionRegistry::Get(browser()->profile()); |
| ASSERT_FALSE(extension_registry->enabled_extensions().Contains(kGoodCrxId)); |
| } |
| |
| // Download an extension. Expect a dangerous download warning. |
| // Allow the download, and the install. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, CrxInstallAcceptPermissions) { |
| // TODO(https://crbug.com/40804030): Remove this when updated to use MV3. |
| extensions::ScopedTestMV2Enabler mv2_enabler; |
| |
| std::unique_ptr<base::AutoReset<bool>> allow_offstore_install = |
| download_crx_util::OverrideOffstoreInstallAllowedForTesting(true); |
| |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL extension_url = |
| embedded_test_server()->GetURL("/" + std::string(kGoodCrxPath)); |
| |
| // Simulate the user allowing permission to finish the install. |
| extensions::ScopedTestDialogAutoConfirm auto_confirm_install_prompt( |
| extensions::ScopedTestDialogAutoConfirm::ACCEPT); |
| |
| std::unique_ptr<content::DownloadTestObserver> observer( |
| DangerousDownloadWaiter( |
| browser(), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT)); |
| NavigateParams params(browser(), extension_url, ui::PAGE_TRANSITION_TYPED); |
| params.user_gesture = false; |
| ui_test_utils::NavigateToURL(¶ms); |
| |
| observer->WaitForFinished(); |
| EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| EXPECT_EQ(1u, observer->NumDangerousDownloadsSeen()); |
| |
| // Download shelf should close from auto-open. |
| content::DownloadManager::DownloadVector downloads; |
| DownloadManagerForBrowser(browser())->GetAllDownloads(&downloads); |
| ASSERT_EQ(1u, downloads.size()); |
| content::DownloadUpdatedObserver(downloads[0], |
| base::BindRepeating(&WasAutoOpened)) |
| .WaitForEvent(); |
| |
| // Check that the extension was installed. |
| extensions::ExtensionRegistry* extension_registry = |
| extensions::ExtensionRegistry::Get(browser()->profile()); |
| ASSERT_TRUE(extension_registry->enabled_extensions().Contains(kGoodCrxId)); |
| } |
| |
| // Test installing a CRX that fails integrity checks. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, CrxInvalid) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL extension_url = |
| embedded_test_server()->GetURL("/extensions/bad_signature.crx"); |
| |
| // Simulate the user allowing permission to finish the install. |
| extensions::ScopedTestDialogAutoConfirm auto_confirm_install_prompt( |
| extensions::ScopedTestDialogAutoConfirm::ACCEPT); |
| |
| std::unique_ptr<content::DownloadTestObserver> observer( |
| DangerousDownloadWaiter( |
| browser(), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT)); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), extension_url)); |
| |
| observer->WaitForFinished(); |
| EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| |
| // Check that the extension was not installed. |
| extensions::ExtensionRegistry* extension_registry = |
| extensions::ExtensionRegistry::Get(browser()->profile()); |
| ASSERT_FALSE(extension_registry->enabled_extensions().Contains(kGoodCrxId)); |
| } |
| |
| // Install a large (100kb) theme. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, CrxLargeTheme) { |
| std::unique_ptr<base::AutoReset<bool>> allow_offstore_install = |
| download_crx_util::OverrideOffstoreInstallAllowedForTesting(true); |
| |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL extension_url = |
| embedded_test_server()->GetURL("/" + std::string(kLargeThemePath)); |
| |
| // Simulate the user allowing permission to finish the install. |
| extensions::ScopedTestDialogAutoConfirm auto_confirm_install_prompt( |
| extensions::ScopedTestDialogAutoConfirm::ACCEPT); |
| |
| std::unique_ptr<content::DownloadTestObserver> observer( |
| DangerousDownloadWaiter( |
| browser(), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT)); |
| NavigateParams params(browser(), extension_url, ui::PAGE_TRANSITION_TYPED); |
| params.user_gesture = false; |
| ui_test_utils::NavigateToURL(¶ms); |
| |
| observer->WaitForFinished(); |
| EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| EXPECT_EQ(1u, observer->NumDangerousDownloadsSeen()); |
| |
| // Download shelf should close from auto-open. |
| content::DownloadManager::DownloadVector downloads; |
| DownloadManagerForBrowser(browser())->GetAllDownloads(&downloads); |
| ASSERT_EQ(1u, downloads.size()); |
| content::DownloadUpdatedObserver(downloads[0], |
| base::BindRepeating(&WasAutoOpened)) |
| .WaitForEvent(); |
| |
| // Check that the extension was installed. |
| extensions::ExtensionRegistry* extension_registry = |
| extensions::ExtensionRegistry::Get(browser()->profile()); |
| ASSERT_TRUE( |
| extension_registry->enabled_extensions().Contains(kLargeThemeCrxId)); |
| } |
| |
| // Tests for download initiation functions. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadUrl) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| // DownloadUrl always prompts; return acceptance of whatever it prompts. |
| EnableFileChooser(true); |
| |
| WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| |
| content::DownloadTestObserver* observer( |
| new content::DownloadTestObserverTerminal( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| std::unique_ptr<DownloadUrlParameters> params( |
| content::DownloadRequestUtils::CreateDownloadForWebContentsMainFrame( |
| web_contents, url, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| params->set_prompt(true); |
| DownloadManagerForBrowser(browser())->DownloadUrl(std::move(params)); |
| observer->WaitForFinished(); |
| EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| EXPECT_TRUE(DidShowFileChooser()); |
| |
| // Check state. |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| ASSERT_TRUE(CheckDownload(browser(), file, file)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadUrlToPath) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| base::ScopedTempDir other_directory; |
| ASSERT_TRUE(other_directory.CreateUniqueTempDir()); |
| base::FilePath target_file_full_path = |
| other_directory.GetPath().Append(file.BaseName()); |
| content::DownloadTestObserver* observer(CreateWaiter(browser(), 1)); |
| std::unique_ptr<DownloadUrlParameters> params( |
| content::DownloadRequestUtils::CreateDownloadForWebContentsMainFrame( |
| web_contents, url, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| params->set_file_path(target_file_full_path); |
| DownloadManagerForBrowser(browser())->DownloadUrl(std::move(params)); |
| observer->WaitForFinished(); |
| EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| |
| // Check state. |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| ASSERT_TRUE(CheckDownloadFullPaths(browser(), |
| target_file_full_path, |
| OriginFile(file))); |
| |
| // Temporary are treated as auto-opened, and after that open won't be |
| // visible; wait for auto-open and confirm not visible. |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> downloads; |
| DownloadManagerForBrowser(browser())->GetAllDownloads(&downloads); |
| ASSERT_EQ(1u, downloads.size()); |
| content::DownloadUpdatedObserver(downloads[0], |
| base::BindRepeating(&WasAutoOpened)) |
| .WaitForEvent(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, TransientDownload) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| base::ScopedTempDir other_directory; |
| ASSERT_TRUE(other_directory.CreateUniqueTempDir()); |
| base::FilePath target_file_full_path = |
| other_directory.GetPath().Append(file.BaseName()); |
| content::DownloadTestObserver* observer(CreateWaiter(browser(), 1)); |
| std::unique_ptr<DownloadUrlParameters> params( |
| content::DownloadRequestUtils::CreateDownloadForWebContentsMainFrame( |
| web_contents, url, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| params->set_file_path(target_file_full_path); |
| params->set_transient(true); |
| DownloadManagerForBrowser(browser())->DownloadUrl(std::move(params)); |
| observer->WaitForFinished(); |
| EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| |
| // Check state. |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| ASSERT_TRUE(CheckDownloadFullPaths(browser(), target_file_full_path, |
| OriginFile(file))); |
| |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> downloads; |
| DownloadManagerForBrowser(browser())->GetAllDownloads(&downloads); |
| ASSERT_EQ(1u, downloads.size()); |
| ASSERT_TRUE(downloads[0]->IsTransient()); |
| ASSERT_FALSE(downloads[0]->IsTemporary()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, NullInitiator) { |
| GURL extensions_url("chrome-extension://fakeextension/resources"); |
| |
| WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| base::FilePath target_file_full_path = |
| temp_dir.GetPath().Append(file.BaseName()); |
| content::DownloadTestObserver* observer(CreateWaiter(browser(), 1)); |
| std::unique_ptr<DownloadUrlParameters> params( |
| content::DownloadRequestUtils::CreateDownloadForWebContentsMainFrame( |
| web_contents, extensions_url, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| |
| params->set_file_path(target_file_full_path); |
| params->set_transient(true); |
| DownloadManagerForBrowser(browser())->DownloadUrl(std::move(params)); |
| observer->WaitForFinished(); |
| EXPECT_EQ(0u, observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| } |
| |
| class DownloadTestSplitCacheEnabledBase : public DownloadTest { |
| public: |
| DownloadTestSplitCacheEnabledBase() { |
| feature_list_.InitAndEnableFeature( |
| net::features::kSplitCacheByNetworkIsolationKey); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| enum class SplitCacheTestCase { |
| kEnabledTripleKeyed, |
| kEnabledTriplePlusCrossSiteMainFrameNavBool, |
| }; |
| |
| const struct { |
| const SplitCacheTestCase test_case; |
| base::test::FeatureRef feature; |
| } kTestCaseToFeatureMapping[] = { |
| {SplitCacheTestCase::kEnabledTriplePlusCrossSiteMainFrameNavBool, |
| net::features::kSplitCacheByCrossSiteMainFrameNavigationBoolean}, |
| }; |
| |
| std::string GetSplitCacheTestName(SplitCacheTestCase test_case) { |
| switch (test_case) { |
| case (SplitCacheTestCase::kEnabledTripleKeyed): |
| return "TripleKeyed"; |
| case (SplitCacheTestCase::kEnabledTriplePlusCrossSiteMainFrameNavBool): |
| return "TriplePlusCrossSiteMainFrameNavigationBool"; |
| } |
| } |
| |
| class DownloadTestSplitCacheEnabled |
| : public DownloadTestSplitCacheEnabledBase, |
| public testing::WithParamInterface<SplitCacheTestCase> { |
| public: |
| DownloadTestSplitCacheEnabled() |
| : split_cache_experiment_feature_list_(GetParam(), |
| kTestCaseToFeatureMapping) {} |
| |
| private: |
| net::test::ScopedMutuallyExclusiveFeatureList |
| split_cache_experiment_feature_list_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| DownloadTestSplitCacheEnabled, |
| testing::ValuesIn( |
| {SplitCacheTestCase::kEnabledTripleKeyed, |
| SplitCacheTestCase::kEnabledTriplePlusCrossSiteMainFrameNavBool}), |
| [](const testing::TestParamInfo<SplitCacheTestCase>& info) { |
| return GetSplitCacheTestName(info.param); |
| }); |
| |
| #if BUILDFLAG(ENABLE_PDF) |
| class PdfDownloadTestSplitCacheEnabled |
| : public DownloadTestSplitCacheEnabledBase, |
| public testing::WithParamInterface<std::tuple<bool, SplitCacheTestCase>> { |
| public: |
| PdfDownloadTestSplitCacheEnabled() |
| : split_cache_experiment_feature_list_(GetSplitCacheTestCase(), |
| kTestCaseToFeatureMapping) { |
| if (UseOopif()) { |
| oopif_feature_list_.InitAndEnableFeature(chrome_pdf::features::kPdfOopif); |
| } else { |
| oopif_feature_list_.InitAndDisableFeature( |
| chrome_pdf::features::kPdfOopif); |
| } |
| } |
| |
| bool UseOopif() const { return std::get<0>(GetParam()); } |
| |
| SplitCacheTestCase GetSplitCacheTestCase() const { |
| return std::get<1>(GetParam()); |
| } |
| |
| pdf::TestPdfViewerStreamManager* GetTestPdfViewerStreamManager() { |
| return factory_.GetTestPdfViewerStreamManager( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| } |
| |
| void TestSaveMainFramePdfFromTargetFrameContextMenu( |
| content::RenderFrameHost* target_host, |
| const GURL& url, |
| std::optional<url::Origin> expected_initiator = std::nullopt) { |
| auto origin = |
| url::Origin::Create(https_test_server()->GetURL("a.test", "/")); |
| net::SiteForCookies expected_site_for_cookies = |
| net::SiteForCookies::FromOrigin(origin); |
| |
| net::IsolationInfo expected_isolation_info = |
| net::IsolationInfo::Create(net::IsolationInfo::RequestType::kMainFrame, |
| origin, origin, expected_site_for_cookies); |
| |
| // Stop the server. This makes sure we really are pulling from the cache for |
| // the download request. |
| ASSERT_TRUE(https_test_server()->ShutdownAndWaitUntilComplete()); |
| |
| std::optional<network::ResourceRequest::TrustedParams> trusted_params; |
| net::SiteForCookies site_for_cookies; |
| std::optional<url::Origin> request_initiator; |
| |
| base::RunLoop request_waiter; |
| URLLoaderInterceptor request_listener(base::BindLambdaForTesting( |
| [&](URLLoaderInterceptor::RequestParams* params) { |
| if (params->url_request.url == url) { |
| trusted_params = params->url_request.trusted_params; |
| site_for_cookies = params->url_request.site_for_cookies; |
| request_initiator = params->url_request.request_initiator; |
| request_waiter.Quit(); |
| } |
| return false; |
| })); |
| |
| std::unique_ptr<content::DownloadTestObserver> download_waiter( |
| CreateWaiter(browser(), 1)); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Simulate saving the PDF from the context menu "Save As...". |
| content::ContextMenuParams context_menu_params; |
| context_menu_params.media_type = |
| blink::mojom::ContextMenuDataMediaType::kPlugin; |
| context_menu_params.src_url = url; |
| context_menu_params.page_url = web_contents->GetLastCommittedURL(); |
| TestRenderViewContextMenu menu(*target_host, context_menu_params); |
| menu.Init(); |
| menu.ExecuteCommand(IDC_SAVE_PAGE, 0); |
| |
| request_waiter.Run(); |
| |
| ASSERT_TRUE(trusted_params.has_value()); |
| EXPECT_TRUE(trusted_params->isolation_info.IsEqualForTesting( |
| expected_isolation_info)); |
| EXPECT_TRUE(site_for_cookies.IsEquivalent(expected_site_for_cookies)); |
| EXPECT_EQ(request_initiator, expected_initiator); |
| |
| download_waiter->WaitForFinished(); |
| |
| EXPECT_EQ(1u, |
| download_waiter->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| } |
| |
| private: |
| net::test::ScopedMutuallyExclusiveFeatureList |
| split_cache_experiment_feature_list_; |
| base::test::ScopedFeatureList oopif_feature_list_; |
| pdf::TestPdfViewerStreamManagerFactory factory_; |
| }; |
| |
| // Test that the PDF can be saved from the primary frame's context menu. |
| IN_PROC_BROWSER_TEST_P( |
| PdfDownloadTestSplitCacheEnabled, |
| SaveMainFramePdfFromPrimaryFrameContextMenuBrowserInitiatedNavigation) { |
| https_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(https_test_server()->Start()); |
| EnableFileChooser(true); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Set up a PDF page. |
| GURL url = https_test_server()->GetURL("a.test", "/pdf/test.pdf"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| ASSERT_TRUE(pdf_extension_test_util::EnsurePDFHasLoaded(web_contents)); |
| |
| TestSaveMainFramePdfFromTargetFrameContextMenu( |
| web_contents->GetPrimaryMainFrame(), url); |
| } |
| |
| // Same as above but using a renderer-initiated navigation from a cross-origin |
| // document. |
| IN_PROC_BROWSER_TEST_P( |
| PdfDownloadTestSplitCacheEnabled, |
| SaveMainFramePdfFromPrimaryFrameContextMenuRendererInitiatedNavigation) { |
| https_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(https_test_server()->Start()); |
| EnableFileChooser(true); |
| |
| GURL initial_url = https_test_server()->GetURL("b.test", "/empty.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Set up a PDF page. |
| GURL url = https_test_server()->GetURL("a.test", "/pdf/test.pdf"); |
| ASSERT_TRUE(content::NavigateToURLFromRenderer(web_contents, url)); |
| ASSERT_TRUE(pdf_extension_test_util::EnsurePDFHasLoaded(web_contents)); |
| |
| TestSaveMainFramePdfFromTargetFrameContextMenu( |
| web_contents->GetPrimaryMainFrame(), url, |
| url::Origin::Create(initial_url)); |
| } |
| |
| // Test that the PDF can be saved from the PDf extension frame's context menu. |
| IN_PROC_BROWSER_TEST_P( |
| PdfDownloadTestSplitCacheEnabled, |
| SaveMainFramePdfFromExtensionFrameContextMenuBrowserInitiatedNavigation) { |
| https_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(https_test_server()->Start()); |
| EnableFileChooser(true); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Set up a PDF page. |
| GURL url = https_test_server()->GetURL("a.test", "/pdf/test.pdf"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| ASSERT_TRUE(pdf_extension_test_util::EnsurePDFHasLoaded(web_contents)); |
| |
| content::RenderFrameHost* extension_host = |
| pdf_extension_test_util::GetOnlyPdfExtensionHost(web_contents); |
| ASSERT_TRUE(extension_host); |
| |
| TestSaveMainFramePdfFromTargetFrameContextMenu(extension_host, url); |
| } |
| // Same as above but using a renderer-initiated navigation from a cross-origin |
| // document. |
| IN_PROC_BROWSER_TEST_P( |
| PdfDownloadTestSplitCacheEnabled, |
| SaveMainFramePdfFromExtensionFrameContextMenuRendererInitiatedNavigation) { |
| https_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(https_test_server()->Start()); |
| EnableFileChooser(true); |
| |
| GURL initial_url = https_test_server()->GetURL("b.test", "/empty.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Set up a PDF page. |
| GURL url = https_test_server()->GetURL("a.test", "/pdf/test.pdf"); |
| ASSERT_TRUE(content::NavigateToURLFromRenderer(web_contents, url)); |
| ASSERT_TRUE(pdf_extension_test_util::EnsurePDFHasLoaded(web_contents)); |
| |
| content::RenderFrameHost* extension_host = |
| pdf_extension_test_util::GetOnlyPdfExtensionHost(web_contents); |
| ASSERT_TRUE(extension_host); |
| |
| TestSaveMainFramePdfFromTargetFrameContextMenu( |
| extension_host, url, url::Origin::Create(initial_url)); |
| } |
| |
| // Test that the PDF can be saved from the PDF content frame's context menu. |
| IN_PROC_BROWSER_TEST_P( |
| PdfDownloadTestSplitCacheEnabled, |
| SaveMainFramePdfFromContentFrameContextMenuBrowserInitiatedNavigation) { |
| https_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(https_test_server()->Start()); |
| EnableFileChooser(true); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Set up a PDF page. |
| GURL url = https_test_server()->GetURL("a.test", "/pdf/test.pdf"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| ASSERT_TRUE(pdf_extension_test_util::EnsurePDFHasLoaded(web_contents)); |
| |
| content::RenderFrameHost* content_host = |
| pdf_extension_test_util::GetOnlyPdfPluginFrame(web_contents); |
| ASSERT_TRUE(content_host); |
| |
| TestSaveMainFramePdfFromTargetFrameContextMenu(content_host, url); |
| } |
| |
| // Same as above but using a renderer-initiated navigation from a cross-origin |
| // document. |
| IN_PROC_BROWSER_TEST_P( |
| PdfDownloadTestSplitCacheEnabled, |
| SaveMainFramePdfFromContentFrameContextMenuRendererInitiatedNavigation) { |
| https_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(https_test_server()->Start()); |
| EnableFileChooser(true); |
| |
| GURL initial_url = https_test_server()->GetURL("b.test", "/empty.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Set up a PDF page. |
| GURL url = https_test_server()->GetURL("a.test", "/pdf/test.pdf"); |
| ASSERT_TRUE(content::NavigateToURLFromRenderer(web_contents, url)); |
| ASSERT_TRUE(pdf_extension_test_util::EnsurePDFHasLoaded(web_contents)); |
| |
| content::RenderFrameHost* content_host = |
| pdf_extension_test_util::GetOnlyPdfPluginFrame(web_contents); |
| ASSERT_TRUE(content_host); |
| |
| TestSaveMainFramePdfFromTargetFrameContextMenu( |
| content_host, url, url::Origin::Create(initial_url)); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(PdfDownloadTestSplitCacheEnabled, |
| SaveSubframePdfFromPdfUIIsolationInfo) { |
| https_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(https_test_server()->Start()); |
| EnableFileChooser(true); |
| |
| auto top_frame_origin = |
| url::Origin::Create(https_test_server()->GetURL("a.test", "/")); |
| net::SiteForCookies expected_site_for_cookies = |
| net::SiteForCookies::FromOrigin(top_frame_origin); |
| |
| net::IsolationInfo expected_isolation_info = net::IsolationInfo::Create( |
| net::IsolationInfo::RequestType::kSubFrame, top_frame_origin, |
| url::Origin::Create(https_test_server()->GetURL("b.test", "/")), |
| expected_site_for_cookies, /*nonce=*/std::nullopt, |
| net::NetworkIsolationPartition::kGeneral, |
| net::IsolationInfo::FrameAncestorRelation::kSameOrigin); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Set up a page with a cross-origin iframe hosting a PDF. |
| GURL url = https_test_server()->GetURL("a.test", "/iframe.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| GURL subframe_url(https_test_server()->GetURL("b.test", "/pdf/test.pdf")); |
| |
| // Navigate the subframe and get the `RenderFrameHost` needed for |
| // `pdf::PDFDocumentHelper`. |
| content::RenderFrameHost* document_frame; |
| if (UseOopif()) { |
| content::NavigateIframeToURL(web_contents, |
| /*iframe_id=*/"test", subframe_url); |
| ASSERT_TRUE( |
| GetTestPdfViewerStreamManager()->WaitUntilPdfLoadedInFirstChild()); |
| |
| document_frame = |
| pdf_extension_test_util::GetOnlyPdfPluginFrame(web_contents); |
| ASSERT_TRUE(document_frame); |
| } else { |
| InnerWebContentsAttachedWaiter waiter(web_contents); |
| |
| content::BeginNavigateIframeToURL(web_contents, |
| /*iframe_id=*/"test", subframe_url); |
| waiter.Wait(); |
| |
| std::vector<content::WebContents*> inner_web_contents_vector = |
| web_contents->GetInnerWebContents(); |
| ASSERT_EQ(1u, inner_web_contents_vector.size()); |
| content::WebContents* inner_web_contents = |
| inner_web_contents_vector.front(); |
| |
| // Wait for the page to finish loading. |
| if (inner_web_contents->IsLoading()) { |
| content::TestNavigationObserver inner_navigation_waiter( |
| inner_web_contents); |
| inner_navigation_waiter.Wait(); |
| ASSERT_TRUE(!inner_web_contents->IsLoading()); |
| } |
| |
| document_frame = inner_web_contents->GetPrimaryMainFrame(); |
| } |
| |
| // Stop the server. This makes sure we really are pulling from the cache for |
| // the download request. |
| ASSERT_TRUE(https_test_server()->ShutdownAndWaitUntilComplete()); |
| |
| std::optional<network::ResourceRequest::TrustedParams> trusted_params; |
| net::SiteForCookies site_for_cookies; |
| std::optional<url::Origin> request_initiator; |
| |
| base::RunLoop request_waiter; |
| URLLoaderInterceptor request_listener(base::BindLambdaForTesting( |
| [&](URLLoaderInterceptor::RequestParams* params) { |
| if (params->url_request.url == subframe_url) { |
| trusted_params = params->url_request.trusted_params; |
| site_for_cookies = params->url_request.site_for_cookies; |
| request_initiator = params->url_request.request_initiator; |
| request_waiter.Quit(); |
| } |
| return false; |
| })); |
| |
| std::unique_ptr<content::DownloadTestObserver> download_waiter( |
| CreateWaiter(browser(), 1)); |
| |
| // Simulate saving the PDF from the UI. |
| pdf::PDFDocumentHelper::CreateForCurrentDocument( |
| document_frame, std::make_unique<ChromePDFDocumentHelperClient>()); |
| pdf::PDFDocumentHelper* pdf_helper = |
| pdf::PDFDocumentHelper::GetForCurrentDocument(document_frame); |
| pdf_helper->SaveUrlAs( |
| subframe_url, |
| network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin); |
| |
| request_waiter.Run(); |
| |
| ASSERT_TRUE(trusted_params.has_value()); |
| EXPECT_TRUE(trusted_params->isolation_info.IsEqualForTesting( |
| expected_isolation_info)); |
| EXPECT_TRUE(site_for_cookies.IsEquivalent(expected_site_for_cookies)); |
| ASSERT_TRUE(request_initiator.has_value()); |
| EXPECT_EQ(*request_initiator, top_frame_origin); |
| |
| download_waiter->WaitForFinished(); |
| EXPECT_EQ(1u, |
| download_waiter->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| } |
| #endif // BUILDFLAG(ENABLE_PDF) |
| |
| IN_PROC_BROWSER_TEST_P(DownloadTestSplitCacheEnabled, |
| SaveSubframeImageFromContextMenuIsolationInfo) { |
| https_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(https_test_server()->Start()); |
| EnableFileChooser(true); |
| |
| auto top_frame_origin = |
| url::Origin::Create(https_test_server()->GetURL("a.test", "/")); |
| net::SiteForCookies expected_site_for_cookies = |
| net::SiteForCookies::FromOrigin(top_frame_origin); |
| |
| net::IsolationInfo expected_isolation_info = net::IsolationInfo::Create( |
| net::IsolationInfo::RequestType::kSubFrame, top_frame_origin, |
| url::Origin::Create(https_test_server()->GetURL("b.test", "/")), |
| expected_site_for_cookies, /*nonce=*/std::nullopt, |
| net::NetworkIsolationPartition::kGeneral, |
| net::IsolationInfo::FrameAncestorRelation::kSameOrigin); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Set up a page with a cross-origin iframe hosting a PDF. |
| GURL url = https_test_server()->GetURL("a.test", "/iframe.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| GURL subframe_url( |
| https_test_server()->GetURL("b.test", "/downloads/image.jpg")); |
| content::NavigateIframeToURL(web_contents, |
| /*iframe_id=*/"test", subframe_url); |
| |
| // Stop the server. This makes sure we really are pulling from the cache for |
| // the download request. |
| ASSERT_TRUE(https_test_server()->ShutdownAndWaitUntilComplete()); |
| |
| std::optional<network::ResourceRequest::TrustedParams> trusted_params; |
| net::SiteForCookies site_for_cookies; |
| std::optional<url::Origin> request_initiator; |
| |
| base::RunLoop request_waiter; |
| URLLoaderInterceptor request_listener(base::BindLambdaForTesting( |
| [&](URLLoaderInterceptor::RequestParams* params) { |
| if (params->url_request.url == subframe_url) { |
| trusted_params = params->url_request.trusted_params; |
| site_for_cookies = params->url_request.site_for_cookies; |
| request_initiator = params->url_request.request_initiator; |
| request_waiter.Quit(); |
| } |
| return false; |
| })); |
| |
| std::unique_ptr<content::DownloadTestObserver> download_waiter( |
| CreateWaiter(browser(), 1)); |
| |
| // Simulate saving the image from the context menu "Save As..." |
| content::ContextMenuParams context_menu_params; |
| context_menu_params.media_type = |
| blink::mojom::ContextMenuDataMediaType::kImage; |
| context_menu_params.src_url = subframe_url; |
| context_menu_params.page_url = |
| content::ChildFrameAt(web_contents->GetPrimaryMainFrame(), 0) |
| ->GetLastCommittedURL(); |
| content::RenderFrameHost* frame = |
| content::ChildFrameAt(web_contents->GetPrimaryMainFrame(), 0); |
| ASSERT_TRUE(frame); |
| TestRenderViewContextMenu menu(*frame, context_menu_params); |
| menu.Init(); |
| menu.ExecuteCommand(IDC_CONTENT_CONTEXT_SAVEIMAGEAS, 0); |
| |
| request_waiter.Run(); |
| |
| ASSERT_TRUE(trusted_params.has_value()); |
| EXPECT_TRUE(trusted_params->isolation_info.IsEqualForTesting( |
| expected_isolation_info)); |
| EXPECT_TRUE(site_for_cookies.IsEquivalent(expected_site_for_cookies)); |
| ASSERT_TRUE(request_initiator.has_value()); |
| EXPECT_EQ(*request_initiator, top_frame_origin); |
| |
| download_waiter->WaitForFinished(); |
| |
| EXPECT_EQ(1u, |
| download_waiter->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| } |
| |
| #if BUILDFLAG(ENABLE_PDF) |
| IN_PROC_BROWSER_TEST_P(PdfDownloadTestSplitCacheEnabled, |
| SaveSubframePdfFromContextMenuIsolationInfo) { |
| https_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(https_test_server()->Start()); |
| EnableFileChooser(true); |
| |
| auto top_frame_origin = |
| url::Origin::Create(https_test_server()->GetURL("a.test", "/")); |
| net::SiteForCookies expected_site_for_cookies = |
| net::SiteForCookies::FromOrigin(top_frame_origin); |
| |
| net::IsolationInfo expected_isolation_info = net::IsolationInfo::Create( |
| net::IsolationInfo::RequestType::kSubFrame, top_frame_origin, |
| url::Origin::Create(https_test_server()->GetURL("b.test", "/")), |
| expected_site_for_cookies, /*nonce=*/std::nullopt, |
| net::NetworkIsolationPartition::kGeneral, |
| net::IsolationInfo::FrameAncestorRelation::kSameOrigin); |
| |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Set up a page with a cross-origin iframe hosting a PDF. |
| GURL url = https_test_server()->GetURL("a.test", "/iframe.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| GURL subframe_url(https_test_server()->GetURL("b.test", "/pdf/test.pdf")); |
| |
| // Get the `RenderFrameHost` intended to handle the save. For OOPIF PDF |
| // viewer, this will be the PDF content `RenderFrameHost`. For GuestView PDF |
| // viewer, this will be the PDF extension `RenderFrameHost`. |
| content::RenderFrameHost* target_frame; |
| if (UseOopif()) { |
| content::NavigateIframeToURL(web_contents, |
| /*iframe_id=*/"test", subframe_url); |
| ASSERT_TRUE( |
| GetTestPdfViewerStreamManager()->WaitUntilPdfLoadedInFirstChild()); |
| |
| target_frame = pdf_extension_test_util::GetOnlyPdfPluginFrame(web_contents); |
| ASSERT_TRUE(target_frame); |
| } else { |
| InnerWebContentsAttachedWaiter waiter(web_contents); |
| |
| content::BeginNavigateIframeToURL(web_contents, |
| /*iframe_id=*/"test", subframe_url); |
| waiter.Wait(); |
| |
| std::vector<content::WebContents*> inner_web_contents_vector = |
| web_contents->GetInnerWebContents(); |
| ASSERT_EQ(1u, inner_web_contents_vector.size()); |
| content::WebContents* inner_web_contents = |
| inner_web_contents_vector.front(); |
| |
| // Wait for the page to finish loading. |
| if (inner_web_contents->IsLoading()) { |
| content::TestNavigationObserver inner_navigation_waiter( |
| inner_web_contents); |
| inner_navigation_waiter.Wait(); |
| ASSERT_TRUE(!inner_web_contents->IsLoading()); |
| } |
| |
| target_frame = inner_web_contents->GetPrimaryMainFrame(); |
| } |
| |
| // Stop the server. This makes sure we really are pulling from the cache for |
| // the download request. |
| ASSERT_TRUE(https_test_server()->ShutdownAndWaitUntilComplete()); |
| |
| std::optional<network::ResourceRequest::TrustedParams> trusted_params; |
| net::SiteForCookies site_for_cookies; |
| std::optional<url::Origin> request_initiator; |
| |
| base::RunLoop request_waiter; |
| URLLoaderInterceptor request_listener(base::BindLambdaForTesting( |
| [&](URLLoaderInterceptor::RequestParams* params) { |
| if (params->url_request.url == subframe_url) { |
| trusted_params = params->url_request.trusted_params; |
| site_for_cookies = params->url_request.site_for_cookies; |
| request_initiator = params->url_request.request_initiator; |
| request_waiter.Quit(); |
| } |
| return false; |
| })); |
| |
| std::unique_ptr<content::DownloadTestObserver> download_waiter( |
| CreateWaiter(browser(), 1)); |
| |
| // Simulate saving the PDF from the context menu "Save As..." |
| content::ContextMenuParams context_menu_params; |
| context_menu_params.media_type = |
| blink::mojom::ContextMenuDataMediaType::kPlugin; |
| const GURL kExtensionUrl( |
| "chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html"); |
| context_menu_params.src_url = kExtensionUrl; |
| context_menu_params.page_url = web_contents->GetLastCommittedURL(); |
| TestRenderViewContextMenu menu(*target_frame, context_menu_params); |
| menu.Init(); |
| menu.ExecuteCommand(IDC_CONTENT_CONTEXT_SAVEPLUGINAS, 0); |
| |
| request_waiter.Run(); |
| |
| ASSERT_TRUE(trusted_params.has_value()); |
| EXPECT_TRUE(trusted_params->isolation_info.IsEqualForTesting( |
| expected_isolation_info)); |
| EXPECT_TRUE(site_for_cookies.IsEquivalent(expected_site_for_cookies)); |
| ASSERT_TRUE(request_initiator.has_value()); |
| EXPECT_EQ(*request_initiator, top_frame_origin); |
| |
| download_waiter->WaitForFinished(); |
| |
| EXPECT_EQ(1u, |
| download_waiter->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| } |
| |
| // Tests that opening a download in the browser always opens it in the most |
| // recent browser window for that profile. This test is in the PDF download test |
| // suite because this behavior requires that the file type is both downloadable |
| // and openable by the browser, and PDFs fit the bill. |
| #if (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)) && \ |
| BUILDFLAG(ENABLE_PDF) |
| IN_PROC_BROWSER_TEST_P(PdfDownloadTestSplitCacheEnabled, |
| OpenDownloadInMostRecentBrowser) { |
| https_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(https_test_server()->Start()); |
| EnableFileChooser(true); |
| SetAllowOpenDownload(true); |
| |
| Browser* download_browser = browser(); |
| content::WebContents* web_contents = |
| download_browser->tab_strip_model()->GetActiveWebContents(); |
| |
| // Navigate to a PDF. |
| GURL url = https_test_server()->GetURL("a.test", "/pdf/test.pdf"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(download_browser, url)); |
| ASSERT_TRUE(pdf_extension_test_util::EnsurePDFHasLoaded(web_contents)); |
| // Force the downloaded PDF to open in the browser. |
| GetDownloadPrefs(browser())->SetShouldOpenPdfInSystemReader(false); |
| // Download the PDF. |
| TestSaveMainFramePdfFromTargetFrameContextMenu( |
| web_contents->GetPrimaryMainFrame(), url); |
| |
| // Open a newer browser window that the download should be opened in. |
| Browser* latest_tabbed_browser = |
| ui_test_utils::OpenNewEmptyWindowAndWaitUntilActivated( |
| browser()->profile()); |
| ASSERT_EQ(2u, chrome::GetTotalBrowserCount()); |
| |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> download_items; |
| DownloadManagerForBrowser(download_browser)->GetAllDownloads(&download_items); |
| ASSERT_EQ(download_items.size(), 1u); |
| download::DownloadItem* item = download_items[0]; |
| ASSERT_TRUE(item->CanOpenDownload()); |
| |
| // Open the download. |
| ui_test_utils::AllBrowserTabAddedWaiter waiter; |
| item->OpenDownload(); |
| content::WebContents* new_tab = waiter.Wait(); |
| EXPECT_TRUE(new_tab->GetVisibleURL().SchemeIsFile()); |
| |
| // The download was opened in the most recently active browser, not the |
| // original browser it was downloaded in. |
| EXPECT_EQ(download_browser->tab_strip_model()->GetIndexOfWebContents(new_tab), |
| TabStripModel::kNoTab); |
| EXPECT_NE( |
| latest_tabbed_browser->tab_strip_model()->GetIndexOfWebContents(new_tab), |
| TabStripModel::kNoTab); |
| } |
| #endif |
| |
| // TODO(crbug.com/40268279): Stop testing both modes after OOPIF PDF viewer |
| // launches. |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| PdfDownloadTestSplitCacheEnabled, |
| testing::Combine( |
| testing::Bool(), |
| testing::ValuesIn( |
| {SplitCacheTestCase::kEnabledTripleKeyed, |
| SplitCacheTestCase::kEnabledTriplePlusCrossSiteMainFrameNavBool})), |
| [](const testing::TestParamInfo<std::tuple<bool, SplitCacheTestCase>>& |
| info) { |
| std::string test_prefix = |
| std::get<0>(info.param) ? "PdfOopifEnabled" : "PdfOopifDisabled"; |
| return base::StrCat( |
| {test_prefix, "_", GetSplitCacheTestName(std::get<1>(info.param))}); |
| }); |
| #endif // BUILDFLAG(ENABLE_PDF) |
| |
| class DownloadTestWithHistogramTester : public DownloadTest { |
| public: |
| void SetUp() override { |
| // Drop the request for https://accounts.google.com/ListAccounts.... Whether |
| // this request exist can be platform-specific, so drop it for consistency |
| // in a histogram recording result. |
| url_loader_interceptor_ = |
| std::make_unique<URLLoaderInterceptor>(base::BindLambdaForTesting( |
| [&](URLLoaderInterceptor::RequestParams* params) { |
| return params->url_request.url.spec().find( |
| "accounts.google.com") != std::string::npos; |
| })); |
| DownloadTest::SetUp(); |
| } |
| |
| void ResetURLLoaderInterceptor() { url_loader_interceptor_.reset(); } |
| |
| base::HistogramTester& histogram_tester() { return histogram_tester_; } |
| |
| private: |
| base::HistogramTester histogram_tester_; |
| std::unique_ptr<URLLoaderInterceptor> url_loader_interceptor_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTestWithHistogramTester, |
| DISABLED_SavePageNonHTMLViaGet) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| EnableFileChooser(true); |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> download_items; |
| GetDownloads(browser(), &download_items); |
| ASSERT_TRUE(download_items.empty()); |
| |
| // Navigate to a non-HTML resource. The resource also has |
| // Cache-Control: no-cache set, which normally requires revalidation |
| // each time. |
| GURL url = embedded_test_server()->GetURL("/downloads/image.jpg"); |
| ASSERT_TRUE(url.is_valid()); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // Stop the test server, and then try to save the page. If cache validation |
| // is not bypassed then this will fail since the server is no longer |
| // reachable. |
| ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete()); |
| |
| std::unique_ptr<content::DownloadTestObserver> waiter( |
| new content::DownloadTestObserverTerminal( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| chrome::SavePage(browser()); |
| waiter->WaitForFinished(); |
| EXPECT_EQ(1u, waiter->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| |
| // Validate that the correct file was downloaded. |
| GetDownloads(browser(), &download_items); |
| EXPECT_TRUE(DidShowFileChooser()); |
| ASSERT_EQ(1u, download_items.size()); |
| ASSERT_EQ(url, download_items[0]->GetOriginalUrl()); |
| |
| // Try to download it via a context menu. |
| std::unique_ptr<content::DownloadTestObserver> waiter_context_menu( |
| new content::DownloadTestObserverTerminal( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| content::ContextMenuParams context_menu_params; |
| context_menu_params.media_type = |
| blink::mojom::ContextMenuDataMediaType::kImage; |
| context_menu_params.src_url = url; |
| context_menu_params.page_url = url; |
| TestRenderViewContextMenu menu(*browser() |
| ->tab_strip_model() |
| ->GetActiveWebContents() |
| ->GetPrimaryMainFrame(), |
| context_menu_params); |
| menu.Init(); |
| menu.ExecuteCommand(IDC_CONTENT_CONTEXT_SAVEIMAGEAS, 0); |
| waiter_context_menu->WaitForFinished(); |
| EXPECT_EQ( |
| 1u, waiter_context_menu->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(2, DownloadItem::COMPLETE); |
| |
| // Validate that the correct file was downloaded via the context menu. |
| download_items.clear(); |
| GetDownloads(browser(), &download_items); |
| EXPECT_TRUE(DidShowFileChooser()); |
| ASSERT_EQ(2u, download_items.size()); |
| ASSERT_EQ(url, download_items[0]->GetOriginalUrl()); |
| ASSERT_EQ(url, download_items[1]->GetOriginalUrl()); |
| |
| ResetURLLoaderInterceptor(); |
| } |
| |
| // Times out often on debug ChromeOS because test is slow. |
| #if BUILDFLAG(IS_CHROMEOS) && (!defined(NDEBUG) || defined(MEMORY_SANITIZER)) |
| #define MAYBE_SaveLargeImage DISABLED_SaveLargeImage |
| #elif BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ |
| BUILDFLAG(IS_CHROMEOS) |
| // Flaking on Windows, macOS, Linux, ChromeOS. https://crbug.com/1141263 |
| #define MAYBE_SaveLargeImage DISABLED_SaveLargeImage |
| #else |
| #define MAYBE_SaveLargeImage SaveLargeImage |
| #endif |
| // Tests saving an image from a data URL that's bigger than url::kMaxURLChars. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, MAYBE_SaveLargeImage) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| EnableFileChooser(true); |
| |
| GURL url = embedded_test_server()->GetURL("/empty.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| base::FilePath data_file = ui_test_utils::GetTestFilePath( |
| base::FilePath().AppendASCII("downloads"), |
| base::FilePath().AppendASCII("large_image.png")); |
| std::string png_data; |
| { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| CHECK(base::ReadFileToString(data_file, &png_data)); |
| } |
| |
| std::string data_url = base::Base64Encode(png_data); |
| data_url.insert(0, "data:image/png;base64,"); |
| |
| ASSERT_GE(data_url.size(), url::kMaxURLChars); |
| |
| // Try to download a large image via a context menu. |
| std::unique_ptr<content::DownloadTestObserver> waiter_context_menu( |
| new content::DownloadTestObserverTerminal( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| content::ContextMenuParams context_menu_params; |
| context_menu_params.media_type = |
| blink::mojom::ContextMenuDataMediaType::kImage; |
| context_menu_params.src_url = GURL(data_url); |
| context_menu_params.page_url = url; |
| TestRenderViewContextMenu menu(*browser() |
| ->tab_strip_model() |
| ->GetActiveWebContents() |
| ->GetPrimaryMainFrame(), |
| context_menu_params); |
| menu.Init(); |
| menu.ExecuteCommand(IDC_CONTENT_CONTEXT_SAVEIMAGEAS, 0); |
| waiter_context_menu->WaitForFinished(); |
| EXPECT_EQ( |
| 1u, waiter_context_menu->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| |
| // Validate that the correct file was downloaded via the context menu. |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> download_items; |
| GetDownloads(browser(), &download_items); |
| EXPECT_TRUE(DidShowFileChooser()); |
| ASSERT_EQ(1u, download_items.size()); |
| |
| std::string downloaded_data; |
| { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| CHECK(base::ReadFileToString(download_items[0]->GetFullPath(), |
| &downloaded_data)); |
| } |
| ASSERT_EQ(downloaded_data, png_data); |
| } |
| |
| // A EmbeddedTestServer::HandleRequestCallback function that checks for requests |
| // with query string ?allow-post-only, and returns a 404 response if the method |
| // is not POST. Similar for ?allow-get-only. |
| static std::unique_ptr<net::test_server::HttpResponse> |
| FilterMethodSpecificURLsHandler(const net::test_server::HttpRequest& request) { |
| std::unique_ptr<net::test_server::BasicHttpResponse> response; |
| if ((request.relative_url.find("?allow-post-only") != std::string::npos && |
| request.method != net::test_server::METHOD_POST) || |
| (request.relative_url.find("?allow-get-only") != std::string::npos && |
| request.method != net::test_server::METHOD_GET)) { |
| response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| response->set_code(net::HTTP_NOT_FOUND); |
| } |
| return std::move(response); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, SavePageNonHTMLViaPost) { |
| embedded_test_server()->RegisterRequestHandler( |
| base::BindRepeating(&FilterMethodSpecificURLsHandler)); |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| EnableFileChooser(true); |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> download_items; |
| GetDownloads(browser(), &download_items); |
| ASSERT_TRUE(download_items.empty()); |
| |
| // Navigate to a form page. |
| GURL form_url = |
| embedded_test_server()->GetURL("/downloads/form_page_to_post.html"); |
| ASSERT_TRUE(form_url.is_valid()); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), form_url)); |
| |
| // Submit the form. This will send a POST reqeuest, and the response is a |
| // JPEG image. The resource also has Cache-Control: no-cache set, |
| // which normally requires revalidation each time. |
| GURL jpeg_url = |
| embedded_test_server()->GetURL("/downloads/image.jpg?allow-post-only"); |
| ASSERT_TRUE(jpeg_url.is_valid()); |
| WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| content::RenderFrameHost* render_frame_host = |
| web_contents->GetPrimaryMainFrame(); |
| ASSERT_TRUE(render_frame_host); |
| content::TestNavigationObserver navigation_observer(web_contents, 1); |
| EXPECT_TRUE(content::ExecJs(render_frame_host, "SubmitForm()")); |
| navigation_observer.Wait(); |
| EXPECT_EQ(jpeg_url, web_contents->GetURL()); |
| |
| // Stop the test server, and then try to save the page. If cache validation |
| // is not bypassed then this will fail since the server is no longer |
| // reachable. This will also fail if it tries to be retrieved via "GET" |
| // rather than "POST". |
| ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete()); |
| std::unique_ptr<content::DownloadTestObserver> waiter( |
| new content::DownloadTestObserverTerminal( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| chrome::SavePage(browser()); |
| waiter->WaitForFinished(); |
| EXPECT_EQ(1u, waiter->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| |
| // Validate that the correct file was downloaded. |
| GetDownloads(browser(), &download_items); |
| EXPECT_TRUE(DidShowFileChooser()); |
| ASSERT_EQ(1u, download_items.size()); |
| ASSERT_EQ(jpeg_url, download_items[0]->GetOriginalUrl()); |
| |
| // Try to download it via a context menu. |
| std::unique_ptr<content::DownloadTestObserver> waiter_context_menu( |
| new content::DownloadTestObserverTerminal( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| content::ContextMenuParams context_menu_params; |
| context_menu_params.media_type = |
| blink::mojom::ContextMenuDataMediaType::kImage; |
| context_menu_params.src_url = jpeg_url; |
| context_menu_params.page_url = jpeg_url; |
| context_menu_params.is_image_media_plugin_document = true; |
| TestRenderViewContextMenu menu(*web_contents->GetPrimaryMainFrame(), |
| context_menu_params); |
| menu.Init(); |
| menu.ExecuteCommand(IDC_CONTENT_CONTEXT_SAVEIMAGEAS, 0); |
| waiter_context_menu->WaitForFinished(); |
| EXPECT_EQ( |
| 1u, waiter_context_menu->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(2, DownloadItem::COMPLETE); |
| |
| // Validate that the correct file was downloaded via the context menu. |
| download_items.clear(); |
| GetDownloads(browser(), &download_items); |
| EXPECT_TRUE(DidShowFileChooser()); |
| ASSERT_EQ(2u, download_items.size()); |
| ASSERT_EQ(jpeg_url, download_items[0]->GetOriginalUrl()); |
| ASSERT_EQ(jpeg_url, download_items[1]->GetOriginalUrl()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, SaveImageInPostPage) { |
| embedded_test_server()->RegisterRequestHandler( |
| base::BindRepeating(&FilterMethodSpecificURLsHandler)); |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| EnableFileChooser(true); |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> download_items; |
| GetDownloads(browser(), &download_items); |
| ASSERT_TRUE(download_items.empty()); |
| |
| // Navigate to a form page. |
| GURL form_url = |
| embedded_test_server()->GetURL("/downloads/page_with_image.html"); |
| GURL jpeg_url = |
| embedded_test_server()->GetURL("/downloads/image.jpg?allow-get-only"); |
| ASSERT_TRUE(form_url.is_valid()); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), form_url)); |
| |
| // Submit the form. |
| WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::TestNavigationObserver navigation_observer(web_contents, 1); |
| EXPECT_TRUE(content::ExecJs(web_contents, "document.forms[0].submit()")); |
| navigation_observer.Wait(); |
| EXPECT_EQ(form_url, web_contents->GetURL()); |
| |
| // Wait until the frame is ready to accept input events. |
| content::RenderFrameHost* render_frame_host = |
| web_contents->GetPrimaryMainFrame(); |
| content::WaitForHitTestData(render_frame_host); |
| |
| // Try to download the image via a context menu. |
| // The context menu is actually opened to check that it computes the right |
| // params, since the renderer is responsible for part of this check. |
| content::DownloadTestObserverTerminal waiter( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL); |
| ContextMenuWaiter context_menu_waiter(IDC_CONTENT_CONTEXT_SAVEIMAGEAS); |
| gfx::Point right_click_point = gfx::ToFlooredPoint( |
| content::GetCenterCoordinatesOfElementWithId(web_contents, "image")); |
| content::SimulateMouseClickAt( |
| web_contents, 0, blink::WebMouseEvent::Button::kRight, right_click_point); |
| context_menu_waiter.WaitForMenuOpenAndClose(); |
| waiter.WaitForFinished(); |
| EXPECT_EQ(1u, waiter.NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| |
| // Validate that the correct file was downloaded via the context menu. |
| download_items.clear(); |
| GetDownloads(browser(), &download_items); |
| EXPECT_TRUE(DidShowFileChooser()); |
| ASSERT_EQ(1u, download_items.size()); |
| ASSERT_EQ(jpeg_url, download_items[0]->GetOriginalUrl()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadErrorsServer) { |
| DownloadInfo download_info[] = { |
| {// Normal navigated download. |
| "a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, |
| download::DOWNLOAD_INTERRUPT_REASON_NONE, true, false}, |
| {// Normal direct download. |
| "a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, |
| download::DOWNLOAD_INTERRUPT_REASON_NONE, true, false}, |
| {// Direct download with 404 error. |
| "there_IS_no_spoon.zip", "there_IS_no_spoon.zip", DOWNLOAD_DIRECT, |
| download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, true, false}, |
| {// Navigated download with 404 error. |
| "there_IS_no_spoon.zip", "there_IS_no_spoon.zip", DOWNLOAD_NAVIGATE, |
| download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, false, false}, |
| {// Direct download with 400 error. |
| "zip_file_not_found.zip", "zip_file_not_found.zip", DOWNLOAD_DIRECT, |
| download::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED, true, false}, |
| {// Navigated download with 400 error. |
| "zip_file_not_found.zip", "", DOWNLOAD_NAVIGATE, |
| download::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED, false, false}, |
| {// Simulates clicking on <a href="http://..." download="">. The name does |
| // not resolve. But since this is an explicit download, the download |
| // should appear on the shelf and the error should be indicated. |
| "download-anchor-attrib-name-not-resolved.html", |
| "http://doesnotexist/shouldnotberesolved", DOWNLOAD_NAVIGATE, |
| download::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, false, false}, |
| {// Similar to the above, but the resulting response contains a status |
| // code of 400. |
| "download-anchor-attrib-400.html", "zip_file_not_found.zip", |
| DOWNLOAD_NAVIGATE, download::DOWNLOAD_INTERRUPT_REASON_SERVER_FAILED, |
| true, false}, |
| {// Direct download of a URL where the hostname doesn't resolve. |
| "http://doesnotexist/shouldnotdownloadsuccessfully", |
| "http://doesnotexist/shouldnotdownloadsuccessfully", DOWNLOAD_DIRECT, |
| download::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED, true, false}}; |
| |
| DownloadFilesCheckErrors(std::size(download_info), download_info); |
| } |
| |
| // TODO(crbug.com/40197726): Flaky on multiple platforms. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DISABLED_DownloadErrorsServerNavigate404) { |
| DownloadInfo download_info[] = { |
| {// Simulates clicking on <a href="http://..." download=""> where the URL |
| // leads to a 404 response. This is different from the previous test case |
| // in that the ResourceLoader issues a OnResponseStarted() callback since |
| // the headers are successfully received. |
| "download-anchor-attrib-404.html", "there_IS_no_spoon.zip", |
| DOWNLOAD_NAVIGATE, |
| download::DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT, true, false}}; |
| |
| DownloadFilesCheckErrors(std::size(download_info), download_info); |
| } |
| |
| #if BUILDFLAG(IS_MAC) |
| // https://crbug.com/739766 |
| #define MAYBE_DownloadErrorsFile DISABLED_DownloadErrorsFile |
| #else |
| #define MAYBE_DownloadErrorsFile DownloadErrorsFile |
| #endif |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, MAYBE_DownloadErrorsFile) { |
| FileErrorInjectInfo error_info[] = { |
| {// Navigated download with injected "Disk full" error in Initialize(). |
| {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, true, false}, |
| { |
| content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, |
| 0, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, |
| }}, |
| {// Direct download with injected "Disk full" error in Initialize(). |
| {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, true, false}, |
| { |
| content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, |
| 0, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, |
| }}, |
| {// Navigated download with injected "Disk full" error in Write(). |
| {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, true, false}, |
| { |
| content::TestFileErrorInjector::FILE_OPERATION_WRITE, |
| 0, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, |
| }}, |
| {// Direct download with injected "Disk full" error in Write(). |
| {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, true, false}, |
| { |
| content::TestFileErrorInjector::FILE_OPERATION_WRITE, |
| 0, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, |
| }}, |
| {// Navigated download with injected "Failed" error in Initialize(). |
| {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, true, false}, |
| { |
| content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, |
| 0, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, |
| }}, |
| {// Direct download with injected "Failed" error in Initialize(). |
| {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, true, false}, |
| { |
| content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, |
| 0, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, |
| }}, |
| {// Navigated download with injected "Failed" error in Write(). |
| {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, true, false}, |
| { |
| content::TestFileErrorInjector::FILE_OPERATION_WRITE, |
| 0, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, |
| }}, |
| {// Direct download with injected "Failed" error in Write(). |
| {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, true, false}, |
| { |
| content::TestFileErrorInjector::FILE_OPERATION_WRITE, |
| 0, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, |
| }}, |
| {// Navigated download with injected "Name too long" error in |
| // Initialize(). |
| {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, true, false}, |
| { |
| content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, |
| 0, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, |
| }}, |
| {// Direct download with injected "Name too long" error in Initialize(). |
| {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, true, false}, |
| { |
| content::TestFileErrorInjector::FILE_OPERATION_INITIALIZE, |
| 0, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG, |
| }}, |
| {// Navigated download with injected "Name too long" error in Write(). |
| {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, true, false}, |
| { |
| content::TestFileErrorInjector::FILE_OPERATION_WRITE, |
| 0, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, |
| }}, |
| {// Direct download with injected "Name too long" error in Write(). |
| {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, true, false}, |
| { |
| content::TestFileErrorInjector::FILE_OPERATION_WRITE, |
| 0, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, |
| }}, |
| {// Direct download with injected "Disk full" error in 2nd Write(). |
| {"large_image.png", "large_image.png", DOWNLOAD_DIRECT, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, true, false}, |
| { |
| content::TestFileErrorInjector::FILE_OPERATION_WRITE, |
| 1, |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, |
| }}}; |
| |
| DownloadInsertFilesErrorCheckErrors(std::size(error_info), error_info); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadErrorReadonlyFolder) { |
| DownloadInfo download_info[] = { |
| {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_DIRECT, |
| // This passes because we switch to the My Documents folder. |
| download::DOWNLOAD_INTERRUPT_REASON_NONE, true, true}, |
| {"a_zip_file.zip", "a_zip_file.zip", DOWNLOAD_NAVIGATE, |
| // This passes because we switch to the My Documents folder. |
| download::DOWNLOAD_INTERRUPT_REASON_NONE, true, true}}; |
| |
| DownloadFilesToReadonlyFolder(std::size(download_info), download_info); |
| } |
| |
| // Test that we show a dangerous downloads warning for a dangerous file |
| // downloaded through a blob: URL. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadDangerousBlobData) { |
| safe_browsing::FileTypePoliciesTestOverlay scoped_dangerous = |
| safe_browsing::ScopedMarkAllFilesDangerousForTesting(); |
| |
| // If SafeBrowsing is enabled, certain file types (.exe, .cab, |
| // .msi) will be handled by the DownloadProtectionService. However, if the URL |
| // is non-standard (e.g. blob:) then those files won't be handled by the |
| // DPS. We should be showing the dangerous download warning for any file |
| // considered dangerous and isn't handled by the DPS. |
| std::string path("downloads/download-dangerous-blob.html?filename=foo.evil"); |
| |
| // Need to use http urls because the blob js doesn't work on file urls for |
| // security reasons. |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/" + path); |
| |
| content::DownloadTestObserver* observer(DangerousDownloadWaiter( |
| browser(), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_ACCEPT)); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| observer->WaitForFinished(); |
| |
| EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| EXPECT_EQ(1u, observer->NumDangerousDownloadsSeen()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, OverrideLocalFileUrlAsNotDangerous) { |
| // We must override the mime type logic to a consistent, platform-independent |
| // value, so that the file can be downloaded instead of recognized as a |
| // supported mime type that is opened instead. |
| net::ScopedOverrideGetMimeTypeForTesting override_mime_type( |
| "application/x-some-type"); |
| |
| // Hack to strip the leading separator. |
| std::string file_path(DownloadTestBase::kDangerousMockFilePath); |
| file_path = file_path.substr(1); |
| |
| // The dangerous file, when downloaded from a local file:// URL, should show |
| // up as "not dangerous" due to an override to file type policies. |
| base::FilePath source_file = GetTestDataDirectory().AppendASCII(file_path); |
| GURL url = net::FilePathToFileURL(source_file); |
| |
| content::DownloadTestObserver* observer(DangerousDownloadWaiter( |
| browser(), 1, content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| DownloadAndWait(browser(), url); |
| observer->WaitForFinished(); |
| |
| EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| EXPECT_EQ(0u, observer->NumDangerousDownloadsSeen()); |
| } |
| |
| // A EmbeddedTestServer::HandleRequestCallback function that echoes the Referrer |
| // header as its contents. Only responds to the relative URL /echoreferrer |
| // E.g.: |
| // C -> S: GET /foo |
| // Referer: http://example.com/foo |
| // S -> C: HTTP/1.1 200 OK |
| // Content-Type: text/plain |
| // |
| // http://example.com/foo |
| static std::unique_ptr<net::test_server::HttpResponse> |
| EchoReferrerRequestHandler(const net::test_server::HttpRequest& request) { |
| const std::string kReferrerHeader = "Referer"; // SIC |
| |
| if (!base::StartsWith(request.relative_url, "/echoreferrer", |
| base::CompareCase::SENSITIVE)) { |
| return nullptr; |
| } |
| |
| std::unique_ptr<net::test_server::BasicHttpResponse> response( |
| new net::test_server::BasicHttpResponse()); |
| response->set_code(net::HTTP_OK); |
| response->set_content_type("text/plain"); |
| response->AddCustomHeader("Content-Disposition", "attachment"); |
| auto referrer_header = request.headers.find(kReferrerHeader); |
| if (referrer_header != request.headers.end()) |
| response->set_content(referrer_header->second); |
| return std::move(response); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(DownloadReferrerPolicyTest, |
| AltClickDownloadReferrerPolicy) { |
| embedded_test_server()->RegisterRequestHandler( |
| base::BindRepeating(&EchoReferrerRequestHandler)); |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| EnableFileChooser(true); |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> download_items; |
| GetDownloads(browser(), &download_items); |
| ASSERT_TRUE(download_items.empty()); |
| |
| // Navigate to a page with a referrer policy and a link on it. The link points |
| // to /echoreferrer. |
| GURL url = embedded_test_server()->GetURL( |
| base::StringPrintf( |
| "/referrer_policy/referrer-policy-start.html?policy=%s", |
| content::ReferrerPolicyToString(referrer_policy()).c_str()) + |
| "&redirect=" + embedded_test_server()->GetURL("/echoreferrer").spec() + |
| "&link=true&target="); |
| ASSERT_TRUE(url.is_valid()); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| std::unique_ptr<content::DownloadTestObserver> waiter( |
| new content::DownloadTestObserverTerminal( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| |
| // Click on the link with the alt key pressed. This will download the link |
| // target. |
| WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); |
| blink::WebMouseEvent mouse_event( |
| blink::WebInputEvent::Type::kMouseDown, blink::WebInputEvent::kAltKey, |
| blink::WebInputEvent::GetStaticTimeStampForTests()); |
| mouse_event.button = blink::WebMouseEvent::Button::kLeft; |
| mouse_event.SetPositionInWidget(15, 15); |
| mouse_event.click_count = 1; |
| tab->GetPrimaryMainFrame() |
| ->GetRenderViewHost() |
| ->GetWidget() |
| ->ForwardMouseEvent(mouse_event); |
| mouse_event.SetType(blink::WebInputEvent::Type::kMouseUp); |
| tab->GetPrimaryMainFrame() |
| ->GetRenderViewHost() |
| ->GetWidget() |
| ->ForwardMouseEvent(mouse_event); |
| |
| waiter->WaitForFinished(); |
| EXPECT_EQ(1u, waiter->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| |
| // Validate that the correct file was downloaded. |
| GetDownloads(browser(), &download_items); |
| ASSERT_EQ(1u, download_items.size()); |
| ASSERT_EQ(embedded_test_server()->GetURL("/echoreferrer"), |
| download_items[0]->GetOriginalUrl()); |
| |
| // Check that the file contains the expected referrer. |
| base::FilePath file(download_items[0]->GetTargetFilePath()); |
| GURL origin = url::Origin::Create(url).GetURL(); |
| switch (referrer_policy()) { |
| case network::mojom::ReferrerPolicy::kAlways: |
| case network::mojom::ReferrerPolicy::kDefault: |
| case network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade: |
| case network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin: |
| case network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin: |
| case network::mojom::ReferrerPolicy::kSameOrigin: |
| EXPECT_TRUE(VerifyFile(file, url.spec(), url.spec().length())); |
| break; |
| case network::mojom::ReferrerPolicy::kNever: |
| EXPECT_TRUE(VerifyFile(file, "", 0)); |
| break; |
| case network::mojom::ReferrerPolicy::kOrigin: |
| case network::mojom::ReferrerPolicy::kStrictOrigin: |
| EXPECT_TRUE(VerifyFile(file, origin.spec(), origin.spec().length())); |
| break; |
| } |
| } |
| |
| // This test ensures that the Referer header is properly sanitized when |
| // Save Link As is chosen from the context menu from a page with all possible |
| // referrer policies. |
| IN_PROC_BROWSER_TEST_P(DownloadReferrerPolicyTest, SaveLinkAsReferrerPolicy) { |
| embedded_test_server()->RegisterRequestHandler( |
| base::BindRepeating(&EchoReferrerRequestHandler)); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| EnableFileChooser(true); |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> download_items; |
| GetDownloads(browser(), &download_items); |
| ASSERT_TRUE(download_items.empty()); |
| |
| // Navigate to the initial page, where Save Link As will be executed. |
| GURL url = embedded_test_server()->GetURL( |
| base::StringPrintf( |
| "/referrer_policy/referrer-policy-start.html?policy=%s", |
| content::ReferrerPolicyToString(referrer_policy()).c_str()) + |
| "&redirect=" + embedded_test_server()->GetURL("/echoreferrer").spec() + |
| "&link=true&target="); |
| ASSERT_TRUE(url.is_valid()); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| std::unique_ptr<content::DownloadTestObserver> waiter( |
| new content::DownloadTestObserverTerminal( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| |
| // Right-click on the link and choose Save Link As. This will download the |
| // link target. |
| ContextMenuNotificationObserver context_menu_observer( |
| IDC_CONTENT_CONTEXT_SAVELINKAS); |
| |
| WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); |
| blink::WebMouseEvent mouse_event( |
| blink::WebInputEvent::Type::kMouseDown, |
| blink::WebInputEvent::kNoModifiers, |
| blink::WebInputEvent::GetStaticTimeStampForTests()); |
| mouse_event.button = blink::WebMouseEvent::Button::kRight; |
| mouse_event.SetPositionInWidget(15, 15); |
| mouse_event.click_count = 1; |
| tab->GetPrimaryMainFrame() |
| ->GetRenderViewHost() |
| ->GetWidget() |
| ->ForwardMouseEvent(mouse_event); |
| mouse_event.SetType(blink::WebInputEvent::Type::kMouseUp); |
| tab->GetPrimaryMainFrame() |
| ->GetRenderViewHost() |
| ->GetWidget() |
| ->ForwardMouseEvent(mouse_event); |
| |
| waiter->WaitForFinished(); |
| EXPECT_EQ(1u, waiter->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| |
| // Validate that the correct file was downloaded. |
| GetDownloads(browser(), &download_items); |
| EXPECT_EQ(1u, download_items.size()); |
| EXPECT_EQ(embedded_test_server()->GetURL("/echoreferrer"), |
| download_items[0]->GetOriginalUrl()); |
| |
| // Check that the file contains the expected referrer. |
| base::FilePath file(download_items[0]->GetTargetFilePath()); |
| GURL origin = url::Origin::Create(url).GetURL(); |
| switch (referrer_policy()) { |
| case network::mojom::ReferrerPolicy::kAlways: |
| case network::mojom::ReferrerPolicy::kDefault: |
| case network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade: |
| case network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin: |
| case network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin: |
| case network::mojom::ReferrerPolicy::kSameOrigin: |
| EXPECT_TRUE(VerifyFile(file, url.spec(), url.spec().length())); |
| break; |
| case network::mojom::ReferrerPolicy::kNever: |
| EXPECT_TRUE(VerifyFile(file, "", 0)); |
| break; |
| case network::mojom::ReferrerPolicy::kOrigin: |
| case network::mojom::ReferrerPolicy::kStrictOrigin: |
| EXPECT_TRUE(VerifyFile(file, origin.spec(), origin.spec().length())); |
| break; |
| } |
| } |
| |
| // This test ensures that Cross-Origin-Resource-Policy response header doesn't |
| // apply to download requests initiated via Save Link As context menu (such |
| // requests are considered browser-initiated). See also |
| // https://crbug.com/952834. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, SaveLinkAsVsCrossOriginResourcePolicy) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| EnableFileChooser(true); |
| |
| // Test's sanity check that initially there are no download items. |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> download_items; |
| GetDownloads(browser(), &download_items); |
| ASSERT_TRUE(download_items.empty()); |
| |
| // Read the origin file now so that we can compare the downloaded files to it |
| // later. |
| base::FilePath origin(OriginFile(base::FilePath(FILE_PATH_LITERAL( |
| "downloads/cross-origin-resource-policy-resource.txt")))); |
| |
| std::optional<int64_t> origin_file_size; |
| std::string original_contents; |
| { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| origin_file_size = base::GetFileSize(origin); |
| ASSERT_TRUE(origin_file_size.has_value()); |
| EXPECT_TRUE(base::ReadFileToString(origin, &original_contents)); |
| } |
| |
| // Navigate to the test page. |
| GURL url = embedded_test_server()->GetURL( |
| "a.test", "/downloads/cross-origin-resource-policy-test.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // Right-click on the link and choose Save Link As. This will download the |
| // link target. |
| std::unique_ptr<content::DownloadTestObserver> download_waiter( |
| new content::DownloadTestObserverTerminal( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| ContextMenuNotificationObserver context_menu_observer( |
| IDC_CONTENT_CONTEXT_SAVELINKAS); |
| WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); |
| blink::WebMouseEvent mouse_event( |
| blink::WebInputEvent::Type::kMouseDown, |
| blink::WebInputEvent::kNoModifiers, |
| blink::WebInputEvent::GetStaticTimeStampForTests()); |
| mouse_event.button = blink::WebMouseEvent::Button::kRight; |
| mouse_event.SetPositionInWidget(15, 15); |
| mouse_event.click_count = 1; |
| tab->GetPrimaryMainFrame() |
| ->GetRenderViewHost() |
| ->GetWidget() |
| ->ForwardMouseEvent(mouse_event); |
| mouse_event.SetType(blink::WebInputEvent::Type::kMouseUp); |
| tab->GetPrimaryMainFrame() |
| ->GetRenderViewHost() |
| ->GetWidget() |
| ->ForwardMouseEvent(mouse_event); |
| |
| download_waiter->WaitForFinished(); |
| EXPECT_EQ(1u, |
| download_waiter->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| |
| // Validate that the correct file was downloaded. |
| GetDownloads(browser(), &download_items); |
| ASSERT_EQ(1u, download_items.size()); |
| GURL expected_original_url = embedded_test_server()->GetURL( |
| "a.test", |
| "/cross-site/b.test/downloads/" |
| "cross-origin-resource-policy-resource.txt"); |
| EXPECT_EQ(expected_original_url, download_items[0]->GetOriginalUrl()); |
| EXPECT_TRUE(VerifyFile(download_items[0]->GetTargetFilePath(), |
| original_contents, origin_file_size.value())); |
| } |
| |
| // This test ensures that the Referer header is properly sanitized when |
| // Save Image As is chosen from the context menu. |
| IN_PROC_BROWSER_TEST_P(DownloadReferrerPolicyTest, |
| DISABLED_SaveImageAsReferrerPolicy) { |
| embedded_test_server()->RegisterRequestHandler( |
| base::BindRepeating(&EchoReferrerRequestHandler)); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| EnableFileChooser(true); |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> download_items; |
| GetDownloads(browser(), &download_items); |
| ASSERT_TRUE(download_items.empty()); |
| |
| // Navigate to site using secure HTTPS schema, which serves as referrer URL |
| // of the next request. |
| EmbeddedTestServer https_server(EmbeddedTestServer::TYPE_HTTPS); |
| https_server.ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(https_server.Start()); |
| GURL url = https_server.GetURL( |
| base::StringPrintf( |
| "/referrer_policy/referrer-policy-start.html?policy=%s", |
| content::ReferrerPolicyToString(referrer_policy()).c_str()) + |
| "&redirect=" + embedded_test_server()->GetURL("/echoreferrer").spec() + |
| "&link=true&target="); /* HTTPS */ |
| ASSERT_TRUE(url.is_valid()); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // Try to download an image via a context menu from the secure HTTPS site. |
| // The download request uses insecure HTTP. The referrer URL is downgraded, |
| // resulting in the referrer URL being sanitized from the download request. |
| GURL img_url = embedded_test_server()->GetURL("/echoreferrer"); /* HTTP */ |
| |
| std::unique_ptr<content::DownloadTestObserver> waiter_context_menu( |
| new content::DownloadTestObserverTerminal( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| content::ContextMenuParams context_menu_params; |
| context_menu_params.media_type = |
| blink::mojom::ContextMenuDataMediaType::kImage; |
| context_menu_params.page_url = url; |
| context_menu_params.src_url = img_url; |
| TestRenderViewContextMenu menu(*browser() |
| ->tab_strip_model() |
| ->GetActiveWebContents() |
| ->GetPrimaryMainFrame(), |
| context_menu_params); |
| menu.Init(); |
| menu.ExecuteCommand(IDC_CONTENT_CONTEXT_SAVEIMAGEAS, 0); |
| waiter_context_menu->WaitForFinished(); |
| EXPECT_EQ( |
| 1u, waiter_context_menu->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| |
| // Validate that the correct file was downloaded via the context menu. |
| download_items.clear(); |
| GetDownloads(browser(), &download_items); |
| EXPECT_TRUE(DidShowFileChooser()); |
| ASSERT_EQ(1u, download_items.size()); |
| ASSERT_EQ(img_url, download_items[0]->GetOriginalUrl()); |
| base::FilePath file = download_items[0]->GetTargetFilePath(); |
| // The contents of the file is the value of the Referer header if there was |
| // one. Since the URL is downgraded from HTTPS to HTTP, the referrer is |
| // removed. |
| GURL origin = url::Origin::Create(url).GetURL(); |
| switch (referrer_policy()) { |
| case network::mojom::ReferrerPolicy::kAlways: |
| EXPECT_TRUE(VerifyFile(file, url.spec(), url.spec().length())); |
| break; |
| case network::mojom::ReferrerPolicy::kDefault: |
| case network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade: |
| case network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin: |
| case network::mojom::ReferrerPolicy::kStrictOrigin: |
| case network::mojom::ReferrerPolicy::kSameOrigin: |
| case network::mojom::ReferrerPolicy::kNever: |
| EXPECT_TRUE(VerifyFile(file, "", 0)); |
| break; |
| case network::mojom::ReferrerPolicy::kOrigin: |
| case network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin: |
| EXPECT_TRUE(VerifyFile(file, origin.spec(), origin.spec().length())); |
| break; |
| } |
| } |
| |
| // This test ensures that a cross-domain download correctly sets the referrer |
| // according to the referrer policy. |
| IN_PROC_BROWSER_TEST_P(DownloadReferrerPolicyTest, |
| DownloadCrossDomainReferrerPolicy) { |
| https_test_server()->RegisterRequestHandler( |
| base::BindRepeating(&ServerRedirectRequestHandler)); |
| https_test_server()->RegisterRequestHandler( |
| base::BindRepeating(&EchoReferrerRequestHandler)); |
| https_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(https_test_server()->Start()); |
| EnableFileChooser(true); |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> download_items; |
| GetDownloads(browser(), &download_items); |
| ASSERT_TRUE(download_items.empty()); |
| |
| // Navigate to a page with a referrer policy and a link on it. The link points |
| // to /echoreferrer. |
| GURL url = https_test_server()->GetURL( |
| "www.b.test", |
| base::StringPrintf( |
| "/downloads/download_cross_referrer_policy.html?policy=%s", |
| content::ReferrerPolicyToString(referrer_policy()).c_str())); |
| ASSERT_TRUE(url.is_valid()); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| std::unique_ptr<content::DownloadTestObserver> waiter( |
| new content::DownloadTestObserverTerminal( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| |
| // Click on the link with the alt key pressed. This will download the link |
| // target. |
| WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); |
| blink::WebMouseEvent mouse_event( |
| blink::WebInputEvent::Type::kMouseDown, blink::WebInputEvent::kAltKey, |
| blink::WebInputEvent::GetStaticTimeStampForTests()); |
| mouse_event.button = blink::WebMouseEvent::Button::kLeft; |
| mouse_event.SetPositionInWidget(15, 15); |
| mouse_event.click_count = 1; |
| tab->GetPrimaryMainFrame() |
| ->GetRenderViewHost() |
| ->GetWidget() |
| ->ForwardMouseEvent(mouse_event); |
| mouse_event.SetType(blink::WebInputEvent::Type::kMouseUp); |
| tab->GetPrimaryMainFrame() |
| ->GetRenderViewHost() |
| ->GetWidget() |
| ->ForwardMouseEvent(mouse_event); |
| |
| waiter->WaitForFinished(); |
| EXPECT_EQ(1u, waiter->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| |
| // Validate that the correct file was downloaded. |
| GetDownloads(browser(), &download_items); |
| ASSERT_EQ(1u, download_items.size()); |
| ASSERT_EQ(https_test_server()->GetURL("www.a.test", "/echoreferrer"), |
| download_items[0]->GetURL()); |
| |
| // Check that the file contains the expected referrer. The referrer is |
| // expected to be sent for policies kAlways, kDefault, and |
| // kNoReferrerWhenDowngrade. The referrer should not be sent for policies |
| // kNever, kSameOrigin, and kStrictOriginWhenCrossOrigin. |
| base::FilePath file(download_items[0]->GetTargetFilePath()); |
| GURL origin = url::Origin::Create(url).GetURL(); |
| |
| // Since the default referrer policy can change based on configuration, |
| // resolve referrer_policy() into a concrete policy. |
| auto policy_for_comparison = referrer_policy(); |
| if (policy_for_comparison == network::mojom::ReferrerPolicy::kDefault) { |
| policy_for_comparison = blink::ReferrerUtils::NetToMojoReferrerPolicy( |
| blink::ReferrerUtils::GetDefaultNetReferrerPolicy()); |
| } |
| |
| switch (policy_for_comparison) { |
| case network::mojom::ReferrerPolicy::kAlways: |
| case network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade: |
| EXPECT_TRUE(VerifyFile(file, url.spec(), url.spec().length())); |
| break; |
| case network::mojom::ReferrerPolicy::kSameOrigin: |
| case network::mojom::ReferrerPolicy::kNever: |
| EXPECT_TRUE(VerifyFile(file, "", 0)); |
| break; |
| case network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin: |
| case network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin: |
| case network::mojom::ReferrerPolicy::kOrigin: |
| case network::mojom::ReferrerPolicy::kStrictOrigin: |
| EXPECT_TRUE(VerifyFile(file, origin.spec(), origin.spec().length())); |
| break; |
| default: |
| NOTREACHED() << "Unexpected policy."; |
| } |
| } |
| |
| // TODO(crbug.com/395491549): The test is flaky. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DISABLED_TestMultipleDownloadsRequests) { |
| // Create a downloads observer. |
| std::unique_ptr<content::DownloadTestObserver> downloads_observer( |
| CreateWaiter(browser(), 2)); |
| |
| permissions::PermissionRequestManager* permission_request_manager = |
| permissions::PermissionRequestManager::FromWebContents( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| permission_request_manager->set_auto_response_for_test( |
| permissions::PermissionRequestManager::ACCEPT_ALL); |
| |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/downloads/download-a_zip_file.html"); |
| |
| ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url, 1); |
| |
| // Waits for the download to complete. |
| downloads_observer->WaitForFinished(); |
| EXPECT_EQ(2u, downloads_observer->NumDownloadsSeenInState( |
| DownloadItem::COMPLETE)); |
| |
| browser()->tab_strip_model()->GetActiveWebContents()->Close(); |
| } |
| |
| // Test the scenario for 3 consecutive downloads, where each is triggered by |
| // creating an iframe with srcdoc to another iframe with src to a downloadable |
| // file. Only the 1st download is expected to happen. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, MultipleDownloadsFromIframeSrcdoc) { |
| std::unique_ptr<content::DownloadTestObserver> downloads_observer( |
| CreateWaiter(browser(), 1u)); |
| |
| OnCanDownloadDecidedObserver can_download_observer; |
| g_browser_process->download_request_limiter() |
| ->SetOnCanDownloadDecidedCallbackForTesting(base::BindRepeating( |
| &OnCanDownloadDecidedObserver::OnCanDownloadDecided, |
| base::Unretained(&can_download_observer))); |
| |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL( |
| "/downloads/multiple_download_from_iframe_srcdoc.html"); |
| |
| ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url, 1); |
| |
| // Only the 1st download should succeed. The following should fail. |
| can_download_observer.WaitForNumberOfDecisions(3); |
| std::vector<bool> expected_decisions{true, false, false}; |
| EXPECT_EQ(can_download_observer.GetDecisions(), expected_decisions); |
| |
| downloads_observer->WaitForFinished(); |
| |
| EXPECT_EQ( |
| 1u, downloads_observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| } |
| |
| // Test <a download> download that triggers a x-origin redirect to another |
| // download. The download should succeed. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, |
| CrossOriginRedirectDownloadFromAnchorDownload) { |
| std::unique_ptr<content::DownloadTestObserver> downloads_observer( |
| CreateWaiter(browser(), 1u)); |
| OnCanDownloadDecidedObserver can_download_observer; |
| g_browser_process->download_request_limiter() |
| ->SetOnCanDownloadDecidedCallbackForTesting(base::BindRepeating( |
| &OnCanDownloadDecidedObserver::OnCanDownloadDecided, |
| base::Unretained(&can_download_observer))); |
| |
| https_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(https_test_server()->Start()); |
| GURL url = https_test_server()->GetURL( |
| "www.b.test", |
| "/downloads/multiple_a_download_x_origin_redirect_to_download.html"); |
| |
| base::StringPairs port_replacement; |
| port_replacement.emplace_back( |
| "{{PORT}}", base::NumberToString(https_test_server()->port())); |
| std::string download_url = net::test_server::GetFilePathWithReplacements( |
| "redirect_x_origin_download.html", port_replacement); |
| |
| url = GURL(url.spec() + "?download_url=" + download_url + "&num_downloads=1"); |
| |
| // Navigate to a page that triggers a <a download> download attempt that |
| // triggers a x-origin redirect to another download. |
| ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url, 1); |
| |
| // The <a download> attempt and well as the redirected download should both |
| // pass the download limiter check. |
| can_download_observer.WaitForNumberOfDecisions(2); |
| std::vector<bool> expected_decisions{true, true}; |
| EXPECT_EQ(can_download_observer.GetDecisions(), expected_decisions); |
| |
| // Wait for the redirected download resulted from the download attempt to |
| // finish. |
| downloads_observer->WaitForFinished(); |
| } |
| |
| // Test the scenario for 3 consecutive <a download> download attempts that all |
| // trigger a x-origin redirect to another download. Only the redirected download |
| // resulted from the 1st <a download> attempt should succeed. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, |
| MultipleCrossOriginRedirectDownloadsFromAnchorDownload) { |
| std::unique_ptr<content::DownloadTestObserver> downloads_observer( |
| CreateWaiter(browser(), 1u)); |
| |
| OnCanDownloadDecidedObserver can_download_observer; |
| g_browser_process->download_request_limiter() |
| ->SetOnCanDownloadDecidedCallbackForTesting(base::BindRepeating( |
| &OnCanDownloadDecidedObserver::OnCanDownloadDecided, |
| base::Unretained(&can_download_observer))); |
| |
| https_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(https_test_server()->Start()); |
| GURL url = https_test_server()->GetURL( |
| "www.b.test", |
| "/downloads/multiple_a_download_x_origin_redirect_to_download.html"); |
| |
| base::StringPairs port_replacement; |
| port_replacement.emplace_back( |
| "{{PORT}}", base::NumberToString(https_test_server()->port())); |
| std::string download_url = net::test_server::GetFilePathWithReplacements( |
| "redirect_x_origin_download.html", port_replacement); |
| |
| url = GURL(url.spec() + "?download_url=" + download_url + "&num_downloads=3"); |
| |
| // Navigate to a page that triggers 3 consecutive <a download> download |
| // attempts that all trigger a x-origin redirect to another download. |
| ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url, 1); |
| |
| // The 1st <a download> attempt should pass the download limiter check, |
| // and prevent subsequent 2nd/3rd download attempts from passing the check. |
| // The download resulted from the x-origin redirect from the 1st download |
| // attempt will still pass the check, which could happen at any point |
| // before/between/after the 2nd and 3rd <a download> attempts. |
| can_download_observer.WaitForNumberOfDecisions(4); |
| const std::vector<bool>& decisions = can_download_observer.GetDecisions(); |
| EXPECT_EQ(decisions.size(), 4u); |
| EXPECT_TRUE(decisions.front()); |
| EXPECT_EQ(1, std::count(decisions.begin() + 1, decisions.end(), true)); |
| |
| // Wait for the redirected download resulted from the 1st download attempt to |
| // finish. |
| downloads_observer->WaitForFinished(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadTest_Renaming) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/downloads/a_zip_file.zip"); |
| |
| content::DownloadManager* manager = DownloadManagerForBrowser(browser()); |
| base::FilePath origin_file(OriginFile(base::FilePath(FILE_PATH_LITERAL( |
| "downloads/a_zip_file.zip")))); |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| ASSERT_TRUE(base::PathExists(origin_file)); |
| std::string origin_contents; |
| ASSERT_TRUE(base::ReadFileToString(origin_file, &origin_contents)); |
| |
| // Download the same url several times and expect that all downloaded files |
| // after the zero-th contain a deduplication counter. |
| for (int index = 0; index < 5; ++index) { |
| DownloadAndWait(browser(), url); |
| download::DownloadItem* item = |
| manager->GetDownload(download::DownloadItem::kInvalidId + 1 + index); |
| ASSERT_TRUE(item); |
| ASSERT_EQ(DownloadItem::COMPLETE, item->GetState()); |
| base::FilePath target_path(item->GetTargetFilePath()); |
| EXPECT_EQ(std::string("a_zip_file") + |
| (index == 0 ? std::string(".zip") : |
| base::StringPrintf(" (%d).zip", index)), |
| target_path.BaseName().AsUTF8Unsafe()); |
| ASSERT_TRUE(base::PathExists(target_path)); |
| ASSERT_TRUE(VerifyFile(target_path, origin_contents, |
| origin_contents.size())); |
| } |
| } |
| |
| // Test that the entire download pipeline handles unicode correctly. |
| // Disabled on Windows due to flaky timeouts: crbug.com/446695 |
| #if BUILDFLAG(IS_WIN) |
| #define MAYBE_DownloadTest_CrazyFilenames DISABLED_DownloadTest_CrazyFilenames |
| #else |
| #define MAYBE_DownloadTest_CrazyFilenames DownloadTest_CrazyFilenames |
| #endif |
| IN_PROC_BROWSER_TEST_F(DownloadTest, MAYBE_DownloadTest_CrazyFilenames) { |
| constexpr static const auto kCrazyFilenames = std::to_array<const wchar_t*>({ |
| L"a_file_name.zip", |
| L"\u89c6\u9891\u76f4\u64ad\u56fe\u7247.zip", // chinese chars |
| (L"\u0412\u043e " |
| L"\u0424\u043b\u043e\u0440\u0438\u0434\u0435\u043e\u0431\u044a" |
| L"\u044f\u0432\u043b\u0435\u043d\u0440\u0435\u0436\u0438\u043c \u0427" |
| L"\u041f \u0438\u0437-\u0437\u0430 \u0443\u0442\u0435\u0447\u043a\u0438 " |
| L"\u043d\u0435\u0444\u0442\u0438.zip"), // russian |
| L"Desocupa\xe7\xe3o est\xe1vel.zip", |
| // arabic: |
| (L"\u0638\u2026\u0638\u02c6\u0637\xa7\u0638\u201a\u0637\xb9 \u0638\u201e" |
| L"\u0638\u201e\u0637\xb2\u0638\u0679\u0637\xa7\u0637\xb1\u0637\xa9.zip"), |
| L"\u05d4\u05e2\u05d3\u05e4\u05d5\u05ea.zip", // hebrew |
| L"\u092d\u093e\u0930\u0924.zip", // hindi |
| L"d\xe9stabilis\xe9.zip", // french |
| // korean |
| L"\u97d3-\u4e2d \uc815\uc0c1, \ucc9c\uc548\ud568 \uc758\uacac.zip", |
| L"jiho....tiho...miho.zip", |
| L"jiho!@#$tiho$%^&-()_+=miho copy.zip", // special chars |
| L"Wohoo-to hoo+I.zip", |
| L"Picture 1.zip", |
| L"This is a very very long english sentence with spaces and , and +.zip", |
| }); |
| |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> download_items; |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| base::FilePath origin_directory = |
| GetDownloadDirectory(browser()).Append(FILE_PATH_LITERAL("origin")); |
| ASSERT_TRUE(base::CreateDirectory(origin_directory)); |
| |
| for (size_t index = 0; index < std::size(kCrazyFilenames); ++index) { |
| SCOPED_TRACE(testing::Message() << "Index " << index); |
| std::string crazy8; |
| const wchar_t* const crazy_w = kCrazyFilenames[index]; |
| ASSERT_TRUE(base::WideToUTF8(crazy_w, wcslen(crazy_w), &crazy8)); |
| base::FilePath file_path(origin_directory.Append( |
| #if BUILDFLAG(IS_WIN) |
| crazy_w |
| #elif BUILDFLAG(IS_POSIX) |
| crazy8 |
| #endif |
| )); |
| |
| // Create the file. |
| EXPECT_TRUE(base::WriteFile(file_path, crazy8)); |
| GURL file_url(net::FilePathToFileURL(file_path)); |
| |
| // Download the file and check that the filename is correct. |
| DownloadAndWait(browser(), file_url); |
| GetDownloads(browser(), &download_items); |
| ASSERT_EQ(1UL, download_items.size()); |
| base::FilePath downloaded(download_items[0]->GetTargetFilePath()); |
| download_items[0]->Remove(); |
| download_items.clear(); |
| ASSERT_TRUE(CheckDownloadFullPaths( |
| browser(), |
| downloaded, |
| file_path)); |
| } |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadTest_Remove) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/downloads/a_zip_file.zip"); |
| |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> download_items; |
| GetDownloads(browser(), &download_items); |
| ASSERT_TRUE(download_items.empty()); |
| |
| // Download a file. |
| DownloadAndWaitWithDisposition(browser(), url, |
| WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NO_WAIT); |
| GetDownloads(browser(), &download_items); |
| ASSERT_EQ(1UL, download_items.size()); |
| base::FilePath downloaded(download_items[0]->GetTargetFilePath()); |
| |
| // Remove the DownloadItem but not the file, then check that the file still |
| // exists. |
| download_items[0]->Remove(); |
| download_items.clear(); |
| GetDownloads(browser(), &download_items); |
| ASSERT_EQ(0UL, download_items.size()); |
| ASSERT_TRUE(CheckDownloadFullPaths( |
| browser(), downloaded, OriginFile(base::FilePath( |
| FILE_PATH_LITERAL("downloads/a_zip_file.zip"))))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadTest_PauseResumeCancel) { |
| DownloadItem* download_item = CreateSlowTestDownload(); |
| ASSERT_TRUE(download_item); |
| ASSERT_FALSE(download_item->GetTargetFilePath().empty()); |
| EXPECT_FALSE(download_item->IsPaused()); |
| EXPECT_NE(DownloadItem::CANCELLED, download_item->GetState()); |
| download_item->Pause(); |
| EXPECT_TRUE(download_item->IsPaused()); |
| download_item->Resume(false); |
| EXPECT_FALSE(download_item->IsPaused()); |
| EXPECT_NE(DownloadItem::CANCELLED, download_item->GetState()); |
| download_item->Cancel(true); |
| EXPECT_EQ(DownloadItem::CANCELLED, download_item->GetState()); |
| } |
| |
| // The Mac downloaded files quarantine feature is implemented by the |
| // Contents/Info.plist file in cocoa apps. browser_tests cannot test |
| // quarantining files on Mac because it is not a cocoa app. |
| // TODO(benjhayden) test the equivalents on other platforms. |
| |
| #if BUILDFLAG(IS_LINUX) && defined(ARCH_CPU_ARM_FAMILY) |
| // Timing out on ARM linux: http://crbug.com/238459 |
| #define MAYBE_DownloadTest_PercentComplete DISABLED_DownloadTest_PercentComplete |
| #elif BUILDFLAG(IS_WIN) && defined(ADDRESS_SANITIZER) |
| // Stack overflow on Win/ASan: http://crbug.com/367746304 |
| #define MAYBE_DownloadTest_PercentComplete DISABLED_DownloadTest_PercentComplete |
| #else |
| #define MAYBE_DownloadTest_PercentComplete DownloadTest_PercentComplete |
| #endif |
| IN_PROC_BROWSER_TEST_F(DownloadTest, MAYBE_DownloadTest_PercentComplete) { |
| // Write a huge file. Make sure the test harness can supply "Content-Length" |
| // header to indicate the file size, or the download will not have valid |
| // percentage progression. |
| test_response_handler()->RegisterToTestServer(embedded_test_server()); |
| EXPECT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/large_file"); |
| |
| content::TestDownloadHttpResponse::Parameters parameters; |
| parameters.size = 1024 * 1024 * 32; /* 32MB file. */ |
| content::TestDownloadHttpResponse::StartServing(parameters, url); |
| |
| // Ensure that we have enough disk space to download the large file. |
| { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| int64_t free_space = |
| base::SysInfo::AmountOfFreeDiskSpace(GetDownloadDirectory(browser())); |
| ASSERT_LE(parameters.size, free_space) |
| << "Not enough disk space to download. Got " << free_space; |
| } |
| |
| std::unique_ptr<content::DownloadTestObserver> progress_waiter( |
| CreateInProgressWaiter(browser(), 1)); |
| |
| // Start downloading a file, wait for it to be created. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NO_WAIT); |
| progress_waiter->WaitForFinished(); |
| EXPECT_EQ(1u, progress_waiter->NumDownloadsSeenInState( |
| DownloadItem::IN_PROGRESS)); |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> download_items; |
| GetDownloads(browser(), &download_items); |
| ASSERT_EQ(1u, download_items.size()); |
| |
| // Wait for the download to complete, checking along the way that the |
| // PercentComplete() never regresses. |
| PercentWaiter waiter(download_items[0]); |
| EXPECT_TRUE(waiter.WaitForFinished()); |
| EXPECT_EQ(DownloadItem::COMPLETE, download_items[0]->GetState()); |
| ASSERT_EQ(100, download_items[0]->PercentComplete()); |
| |
| // Check that the file downloaded correctly. |
| ASSERT_EQ(parameters.size, download_items[0]->GetReceivedBytes()); |
| ASSERT_EQ(parameters.size, download_items[0]->GetTotalBytes()); |
| |
| // Delete the file. |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| ASSERT_TRUE(base::DieFileDie(download_items[0]->GetTargetFilePath(), false)); |
| } |
| |
| // A download that is interrupted due to a file error should be able to be |
| // resumed. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, Resumption_NoPrompt) { |
| scoped_refptr<content::TestFileErrorInjector> error_injector( |
| content::TestFileErrorInjector::Create( |
| DownloadManagerForBrowser(browser()))); |
| std::unique_ptr<content::DownloadTestObserver> completion_observer( |
| CreateWaiter(browser(), 1)); |
| EnableFileChooser(true); |
| |
| DownloadItem* download = StartMockDownloadAndInjectError( |
| error_injector.get(), download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); |
| ASSERT_TRUE(download); |
| |
| download->Resume(false); |
| completion_observer->WaitForFinished(); |
| |
| EXPECT_EQ( |
| 1u, completion_observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| EXPECT_FALSE(DidShowFileChooser()); |
| } |
| |
| // A download that's interrupted due to a reason that indicates that the target |
| // path is invalid or unusable should cause a prompt to be displayed on |
| // resumption. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, Resumption_WithPrompt) { |
| scoped_refptr<content::TestFileErrorInjector> error_injector( |
| content::TestFileErrorInjector::Create( |
| DownloadManagerForBrowser(browser()))); |
| std::unique_ptr<content::DownloadTestObserver> completion_observer( |
| CreateWaiter(browser(), 1)); |
| EnableFileChooser(true); |
| |
| DownloadItem* download = StartMockDownloadAndInjectError( |
| error_injector.get(), download::DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE); |
| ASSERT_TRUE(download); |
| |
| download->Resume(true); |
| completion_observer->WaitForFinished(); |
| |
| EXPECT_EQ( |
| 1u, completion_observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| EXPECT_TRUE(DidShowFileChooser()); |
| } |
| |
| // The user shouldn't be prompted on a resumed download unless a prompt is |
| // necessary due to the interrupt reason. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, Resumption_WithPromptAlways) { |
| browser()->profile()->GetPrefs()->SetBoolean( |
| prefs::kPromptForDownload, true); |
| scoped_refptr<content::TestFileErrorInjector> error_injector( |
| content::TestFileErrorInjector::Create( |
| DownloadManagerForBrowser(browser()))); |
| std::unique_ptr<content::DownloadTestObserver> completion_observer( |
| CreateWaiter(browser(), 1)); |
| EnableFileChooser(true); |
| |
| DownloadItem* download = StartMockDownloadAndInjectError( |
| error_injector.get(), download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); |
| ASSERT_TRUE(download); |
| |
| // Prompts the user initially because of the kPromptForDownload preference. |
| EXPECT_TRUE(DidShowFileChooser()); |
| |
| download->Resume(true); |
| completion_observer->WaitForFinished(); |
| |
| EXPECT_EQ( |
| 1u, completion_observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| // Shouldn't prompt for resumption. |
| EXPECT_FALSE(DidShowFileChooser()); |
| } |
| |
| // A download that is interrupted due to a transient error should be resumed |
| // automatically. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, Resumption_Automatic) { |
| scoped_refptr<content::TestFileErrorInjector> error_injector( |
| content::TestFileErrorInjector::Create( |
| DownloadManagerForBrowser(browser()))); |
| |
| DownloadItem* download = StartMockDownloadAndInjectError( |
| error_injector.get(), |
| download::DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR); |
| ASSERT_TRUE(download); |
| |
| // The number of times this the download is resumed automatically is defined |
| // in DownloadItemImpl::kMaxAutoResumeAttempts. The number of DownloadFiles |
| // created should be that number + 1 (for the original download request). We |
| // only care that it is greater than 1. |
| EXPECT_GT(1u, error_injector->TotalFileCount()); |
| |
| std::unique_ptr<content::DownloadTestObserver> completion_observer( |
| CreateWaiter(browser(), 1)); |
| download->Resume(true); |
| completion_observer->WaitForFinished(); |
| |
| // Automatic resumption causes download target determination to be run |
| // multiple times. Make sure we end up with the correct filename at the end. |
| EXPECT_STREQ(kDownloadTest1Path, |
| download->GetTargetFilePath().BaseName().AsUTF8Unsafe().c_str()); |
| } |
| |
| // An interrupting download should be resumable multiple times. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, Resumption_MultipleAttempts) { |
| scoped_refptr<content::TestFileErrorInjector> error_injector( |
| content::TestFileErrorInjector::Create( |
| DownloadManagerForBrowser(browser()))); |
| std::unique_ptr<DownloadTestObserverNotInProgress> completion_observer( |
| new DownloadTestObserverNotInProgress( |
| DownloadManagerForBrowser(browser()), 1)); |
| // Wait for two transitions to a resumable state |
| std::unique_ptr<content::DownloadTestObserver> resumable_observer( |
| new DownloadTestObserverResumable(DownloadManagerForBrowser(browser()), |
| 2)); |
| |
| EnableFileChooser(true); |
| DownloadItem* download = StartMockDownloadAndInjectError( |
| error_injector.get(), download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED); |
| ASSERT_TRUE(download); |
| |
| content::TestFileErrorInjector::FileErrorInfo error_info; |
| error_info.code = content::TestFileErrorInjector::FILE_OPERATION_WRITE; |
| error_info.operation_instance = 0; |
| error_info.error = download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; |
| error_injector->InjectError(error_info); |
| |
| // Resuming should cause the download to be interrupted again due to the |
| // errors we are injecting. |
| download->Resume(false); |
| resumable_observer->WaitForFinished(); |
| ASSERT_EQ(DownloadItem::INTERRUPTED, download->GetState()); |
| ASSERT_EQ(download::DOWNLOAD_INTERRUPT_REASON_FILE_FAILED, |
| download->GetLastReason()); |
| |
| error_injector->ClearError(); |
| |
| // No errors this time. The download should complete successfully. |
| EXPECT_FALSE(completion_observer->IsFinished()); |
| completion_observer->StartObserving(); |
| download->Resume(false); |
| completion_observer->WaitForFinished(); |
| EXPECT_EQ(DownloadItem::COMPLETE, download->GetState()); |
| |
| EXPECT_FALSE(DidShowFileChooser()); |
| } |
| |
| // The file empty.bin is served with a MIME type of application/octet-stream. |
| // The content body is empty. Make sure this case is handled properly and we |
| // don't regress on http://crbug.com/320394. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadTest_GZipWithNoContent) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/downloads/empty.bin"); |
| |
| // Downloading the same URL twice causes the second request to be served from |
| // cached (with a high probability). This test verifies that that doesn't |
| // happen regardless of whether the request is served via the cache or from |
| // the network. |
| DownloadAndWait(browser(), url); |
| DownloadAndWait(browser(), url); |
| } |
| |
| // Test that the SecurityLevel of the initiating page is used for the histogram |
| // rather than the SecurityLevel of the download URL, and that downloads in new |
| // tabs are not tracked. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, SecurityLevels) { |
| base::HistogramTester histogram_tester; |
| net::EmbeddedTestServer http_server(net::EmbeddedTestServer::TYPE_HTTP); |
| net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); |
| http_server.ServeFilesFromDirectory(GetTestDataDirectory()); |
| https_server.ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(http_server.Start()); |
| ASSERT_TRUE(https_server.Start()); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), |
| http_server.GetURL("/simple.html"))); |
| DownloadAndWait(browser(), https_server.GetURL("/downloads/a_zip_file.zip")); |
| histogram_tester.ExpectBucketCount("Security.SecurityLevel.DownloadStarted", |
| security_state::NONE, 1); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), https_server.GetURL("/simple.html"))); |
| DownloadAndWait(browser(), http_server.GetURL("/downloads/a_zip_file.zip")); |
| histogram_tester.ExpectBucketCount("Security.SecurityLevel.DownloadStarted", |
| security_state::SECURE, 1); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), |
| http_server.GetURL("/simple.html"))); |
| DownloadAndWaitWithDisposition( |
| browser(), https_server.GetURL("/downloads/a_zip_file.zip"), |
| WindowOpenDisposition::NEW_BACKGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); |
| histogram_tester.ExpectTotalCount("Security.SecurityLevel.DownloadStarted", |
| 2); |
| } |
| |
| // Tests that opening the downloads page will cause file existence check. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, FileExistenceCheckOpeningDownloadsPage) { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| // Download the file and wait. We do not expect the Select File dialog. |
| DownloadAndWait(browser(), url); |
| |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> downloads; |
| DownloadManagerForBrowser(browser())->GetAllDownloads(&downloads); |
| ASSERT_EQ(1u, downloads.size()); |
| DownloadItem* item = downloads[0]; |
| base::DeleteFile(item->GetTargetFilePath()); |
| ASSERT_FALSE(item->GetFileExternallyRemoved()); |
| |
| // Open the downloads tab. |
| chrome::ShowDownloads(browser()); |
| // Check file removal update will eventually come. |
| content::DownloadUpdatedObserver( |
| item, base::BindRepeating(&IsDownloadExternallyRemoved)) |
| .WaitForEvent(); |
| } |
| |
| // Checks that the navigation resulting from a cross origin download navigates |
| // the correct iframe. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, CrossOriginDownloadNavigatesIframe) { |
| EmbeddedTestServer origin_one; |
| EmbeddedTestServer origin_two; |
| EmbeddedTestServer origin_three; |
| |
| origin_one.ServeFilesFromDirectory(GetTestDataDirectory()); |
| origin_two.ServeFilesFromDirectory(GetTestDataDirectory()); |
| origin_three.ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(origin_one.InitializeAndListen()); |
| ASSERT_TRUE(origin_two.InitializeAndListen()); |
| ASSERT_TRUE(origin_three.InitializeAndListen()); |
| |
| // We load a page on origin_one which iframes a page from origin_two which |
| // downloads a file that redirects to origin_three. |
| GURL download_url = |
| origin_two.GetURL(std::string("/redirect?") + |
| origin_three.GetURL("/downloads/message.html").spec()); |
| GURL referrer_url = origin_two.GetURL( |
| std::string("/downloads/download-attribute.html?target=") + |
| download_url.spec()); |
| GURL main_url = |
| origin_one.GetURL(std::string("/downloads/page-with-frame.html?url=") + |
| referrer_url.spec()); |
| |
| origin_two.RegisterRequestHandler( |
| base::BindRepeating(&ServerRedirectRequestHandler)); |
| |
| origin_one.StartAcceptingConnections(); |
| origin_two.StartAcceptingConnections(); |
| origin_three.StartAcceptingConnections(); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url)); |
| |
| WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(web_contents); |
| content::RenderFrameHost* render_frame_host = |
| web_contents->GetPrimaryMainFrame(); |
| ASSERT_TRUE(render_frame_host); |
| |
| // Clicking the <a download> in the iframe should navigate the iframe, |
| // not the main frame. |
| std::u16string expected_title(u"Loaded as iframe"); |
| std::u16string failed_title(u"Loaded as main frame"); |
| content::TitleWatcher title_watcher(web_contents, expected_title); |
| title_watcher.AlsoWaitForTitle(failed_title); |
| render_frame_host->ExecuteJavaScriptForTests( |
| u"runTest();", base::NullCallback(), content::ISOLATED_WORLD_ID_GLOBAL); |
| ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle()); |
| |
| // Also verify that there's no download. |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> downloads; |
| DownloadManagerForBrowser(browser())->GetAllDownloads(&downloads); |
| ASSERT_EQ(0u, downloads.size()); |
| |
| ASSERT_TRUE(origin_one.ShutdownAndWaitUntilComplete()); |
| ASSERT_TRUE(origin_two.ShutdownAndWaitUntilComplete()); |
| ASSERT_TRUE(origin_three.ShutdownAndWaitUntilComplete()); |
| } |
| |
| // Test is flaky on multiple platforms. |
| // https://crbug.com/1064435 |
| IN_PROC_BROWSER_TEST_F(DownloadWakeLockTest, |
| DISABLED_WakeLockAcquireAndCancel) { |
| Initialize(); |
| EXPECT_EQ(0, GetActiveWakeLocks( |
| device::mojom::WakeLockType::kPreventAppSuspension)); |
| DownloadItem* download_item = CreateSlowTestDownload(); |
| ASSERT_TRUE(download_item); |
| EXPECT_EQ(1, GetActiveWakeLocks( |
| device::mojom::WakeLockType::kPreventAppSuspension)); |
| download_item->Cancel(true); |
| EXPECT_EQ(DownloadItem::CANCELLED, download_item->GetState()); |
| EXPECT_EQ(0, GetActiveWakeLocks( |
| device::mojom::WakeLockType::kPreventAppSuspension)); |
| } |
| |
| // Downloading a data URL that's bigger than url::kMaxURLChars should work. |
| // Flaky: https://crbug.com/1141278 |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DISABLED_DownloadLargeDataURL) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| auto completion_observer = |
| std::make_unique<content::DownloadTestObserverTerminal>( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_IGNORE); |
| |
| // Navigating to large_data_url.html will trigger a download of a data URL |
| // that is larger than 2MB. |
| GURL url = embedded_test_server()->GetURL("/downloads/large_data_url.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| base::FilePath data_file = ui_test_utils::GetTestFilePath( |
| base::FilePath().AppendASCII("downloads"), |
| base::FilePath().AppendASCII("large_image.png")); |
| std::string png_data; |
| { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| CHECK(base::ReadFileToString(data_file, &png_data)); |
| } |
| |
| completion_observer->WaitForFinished(); |
| EXPECT_EQ( |
| 1u, completion_observer->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| |
| // Validate that the correct file was downloaded via the context menu. |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> download_items; |
| GetDownloads(browser(), &download_items); |
| ASSERT_EQ(base::FilePath(FILE_PATH_LITERAL("large.png")), |
| download_items[0]->GetFileNameToReportUser()); |
| |
| std::string downloaded_data; |
| { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| CHECK(base::ReadFileToString(download_items[0]->GetFullPath(), |
| &downloaded_data)); |
| } |
| ASSERT_EQ(downloaded_data, png_data); |
| } |
| |
| // Testing the behavior of resuming with only in-progress download manager. |
| class InProgressDownloadTest : public DownloadTest { |
| public: |
| InProgressDownloadTest() { |
| feature_list_.InitWithFeatures( |
| {download::features::kUseInProgressDownloadManagerForDownloadService}, |
| {}); |
| |
| // The in progress download manager will be released from |
| // `DownloadManagerUtils` during creation of the `DownloadManagerImpl`. As |
| // the `DownloadManagerImpl` may be created before test bodies can run, |
| // register a callback to cache a pointer before release occurs. |
| DownloadManagerUtils:: |
| SetRetrieveInProgressDownloadManagerCallbackForTesting( |
| base::BindRepeating( |
| &InProgressDownloadTest::set_in_progress_manager, |
| base::Unretained(this))); |
| } |
| |
| // DownloadTest: |
| void SetUpOnMainThread() override { |
| EXPECT_TRUE(CheckTestDir()); |
| |
| if (!in_progress_manager_) { |
| // This will only occur if `DownloadManagerImpl` has not already been |
| // created in which case the in progress download manager has not yet been |
| // released from `DownloadManagerUtils`. |
| set_in_progress_manager( |
| DownloadManagerUtils::GetInProgressDownloadManager( |
| browser()->profile()->GetProfileKey())); |
| } |
| |
| // As a pointer to the in progress download manager has now been cached, |
| // watching for release from `DownloadManagerUtils` (if it has not already |
| // occurred) is no longer necessary. |
| DownloadManagerUtils:: |
| SetRetrieveInProgressDownloadManagerCallbackForTesting( |
| base::NullCallback()); |
| } |
| |
| download::InProgressDownloadManager* in_progress_manager() { |
| return in_progress_manager_; |
| } |
| |
| void set_in_progress_manager( |
| download::InProgressDownloadManager* in_progress_manager) { |
| in_progress_manager_ = in_progress_manager; |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| raw_ptr<download::InProgressDownloadManager, DanglingUntriaged> |
| in_progress_manager_ = nullptr; |
| }; |
| |
| // Check that if a download exists in both in-progress and history DB, |
| // resuming the download after loading the in-progress DB and before |
| // history initialization will continue downloading the item even if it |
| // is in a terminal state in history DB. |
| IN_PROC_BROWSER_TEST_F(InProgressDownloadTest, |
| ResumeInProgressDownloadBeforeLoadingHistory) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/downloads/a_zip_file.zip"); |
| base::FilePath origin(OriginFile( |
| base::FilePath(FILE_PATH_LITERAL("downloads/a_zip_file.zip")))); |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| ASSERT_TRUE(base::PathExists(origin)); |
| // Gets the file size. |
| std::optional<int64_t> origin_file_size = base::GetFileSize(origin); |
| ASSERT_TRUE(origin_file_size.has_value()); |
| std::string guid = base::Uuid::GenerateRandomV4().AsLowercaseString(); |
| |
| // Wait for in-progress download manager to initialize. |
| download::SimpleDownloadManagerCoordinator* coordinator = |
| SimpleDownloadManagerCoordinatorFactory::GetForKey( |
| browser()->profile()->GetProfileKey()); |
| SimpleDownloadManagerCoordinatorWaiter coordinator_waiter(coordinator); |
| coordinator_waiter.WaitForInitialization(); |
| |
| base::FilePath target_path; |
| ASSERT_TRUE( |
| base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &target_path)); |
| target_path = |
| target_path.Append(base::FilePath(FILE_PATH_LITERAL("a_zip_file.zip"))); |
| std::vector<GURL> url_chain; |
| url_chain.emplace_back(url); |
| base::Time current_time = base::Time::Now(); |
| in_progress_manager()->AddInProgressDownloadForTest( |
| std::make_unique<download::DownloadItemImpl>( |
| in_progress_manager(), guid, 1 /* id */, |
| target_path.AddExtensionASCII("crdownload"), target_path, url_chain, |
| GURL() /* referrer_url */, |
| std::string() /* serialized_embedder_data */, GURL() /* tab_url */, |
| GURL() /* tab_referrer_url */, url::Origin() /* request_initiator */, |
| "" /* mime_type */, "" /* original_mime_type */, current_time, |
| current_time, "" /* etag */, "" /* last_modified */, |
| 0 /* received_bytes */, origin_file_size.value(), |
| 0 /* auto_resume_count */, "" /* hash */, |
| download::DownloadItem::INTERRUPTED, |
| download::DOWNLOAD_DANGER_TYPE_USER_VALIDATED, |
| download::DOWNLOAD_INTERRUPT_REASON_CRASH, false /* paused */, |
| false /* allow_metered */, false /* opened */, current_time, |
| false /* transient */, |
| std::vector<download::DownloadItem::ReceivedSlice>(), |
| download::kInvalidRange, download::kInvalidRange, |
| nullptr /* download_entry */)); |
| |
| download::DownloadItem* download = coordinator->GetDownloadByGuid(guid); |
| content::DownloadManager* manager = DownloadManagerForBrowser(browser()); |
| DownloadCoreService* service = |
| DownloadCoreServiceFactory::GetForBrowserContext(browser()->profile()); |
| service->SetDownloadHistoryForTesting(nullptr); |
| |
| ASSERT_TRUE(download); |
| PercentWaiter waiter(download); |
| // Resume the download first, before download history loads. |
| download->Resume(true); |
| // Now simulate that history DB is loaded. |
| manager->OnHistoryQueryComplete(base::BindOnce( |
| CreateCompletedDownload, base::Unretained(manager), guid, target_path, |
| std::move(url_chain), origin_file_size.value())); |
| // Download should continue and complete. |
| ASSERT_TRUE(waiter.WaitForFinished()); |
| download::DownloadItem* history_download = manager->GetDownloadByGuid(guid); |
| CHECK_EQ(download, history_download); |
| } |
| |
| // Check that InProgressDownloadManager can handle transient downloads with the |
| // same GUID. |
| IN_PROC_BROWSER_TEST_F(InProgressDownloadTest, |
| DownloadURLWithInProgressManager) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/downloads/a_zip_file.zip"); |
| base::FilePath origin(OriginFile( |
| base::FilePath(FILE_PATH_LITERAL("downloads/a_zip_file.zip")))); |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| ASSERT_TRUE(base::PathExists(origin)); |
| std::string guid = base::Uuid::GenerateRandomV4().AsLowercaseString(); |
| |
| // Wait for in-progress download manager to initialize. |
| download::SimpleDownloadManagerCoordinator* coordinator = |
| SimpleDownloadManagerCoordinatorFactory::GetForKey( |
| browser()->profile()->GetProfileKey()); |
| SimpleDownloadManagerCoordinatorWaiter coordinator_waiter(coordinator); |
| coordinator_waiter.WaitForInitialization(); |
| |
| base::FilePath target_path; |
| ASSERT_TRUE( |
| base::PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &target_path)); |
| target_path = |
| target_path.Append(base::FilePath(FILE_PATH_LITERAL("a_zip_file.zip"))); |
| std::vector<GURL> url_chain; |
| url_chain.emplace_back(url); |
| // Kick off 2 download with the same GUID |
| auto params = std::make_unique<DownloadUrlParameters>( |
| url, TRAFFIC_ANNOTATION_FOR_TESTS); |
| params->set_guid(guid); |
| params->set_file_path(target_path); |
| params->set_transient(true); |
| params->set_require_safety_checks(false); |
| in_progress_manager()->DownloadUrl(std::move(params)); |
| auto params2 = std::make_unique<DownloadUrlParameters>( |
| url, TRAFFIC_ANNOTATION_FOR_TESTS); |
| params2->set_guid(guid); |
| params2->set_file_path(target_path); |
| params2->set_transient(true); |
| params2->set_require_safety_checks(false); |
| in_progress_manager()->DownloadUrl(std::move(params2)); |
| coordinator_waiter.WaitForDownloadCreation(1); |
| download::DownloadItem* download = coordinator->GetDownloadByGuid(guid); |
| ASSERT_TRUE(download); |
| |
| PercentWaiter waiter(download); |
| // Download should continue and complete. |
| ASSERT_TRUE(waiter.WaitForFinished()); |
| |
| // Only 1 download is created above, no more new downloads are created. |
| ASSERT_EQ(coordinator_waiter.num_download_created(), 1); |
| } |
| |
| // Tests that download a canvas image will show the file chooser. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, SaveCanvasImage) { |
| EnableFileChooser(true); |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/downloads/page_with_canvas_image.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| // Try to download a canvas image via a context menu. |
| std::unique_ptr<content::DownloadTestObserver> waiter( |
| new content::DownloadTestObserverTerminal( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| |
| // Right-click on the link and choose Save Image As. This will download the |
| // canvas image. |
| ContextMenuNotificationObserver context_menu_observer( |
| IDC_CONTENT_CONTEXT_SAVEIMAGEAS); |
| |
| WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); |
| blink::WebMouseEvent mouse_event( |
| blink::WebInputEvent::Type::kMouseDown, |
| blink::WebInputEvent::kNoModifiers, |
| blink::WebInputEvent::GetStaticTimeStampForTests()); |
| mouse_event.button = blink::WebMouseEvent::Button::kRight; |
| mouse_event.SetPositionInWidget(15, 15); |
| mouse_event.click_count = 1; |
| tab->GetPrimaryMainFrame() |
| ->GetRenderViewHost() |
| ->GetWidget() |
| ->ForwardMouseEvent(mouse_event); |
| mouse_event.SetType(blink::WebInputEvent::Type::kMouseUp); |
| tab->GetPrimaryMainFrame() |
| ->GetRenderViewHost() |
| ->GetWidget() |
| ->ForwardMouseEvent(mouse_event); |
| waiter->WaitForFinished(); |
| EXPECT_EQ(1u, waiter->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| EXPECT_TRUE(DidShowFileChooser()); |
| } |
| |
| // Tests that accept header is correctly set when using context menu to download |
| // an image. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, ContextMenuSaveImageWithAcceptHeader) { |
| EnableFileChooser(true); |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/downloads/large_image.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| |
| GURL download_url = |
| embedded_test_server()->GetURL("/downloads/large_image.png"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| // Try to download a canvas image via a context menu. |
| std::unique_ptr<content::DownloadTestObserver> waiter( |
| new content::DownloadTestObserverTerminal( |
| DownloadManagerForBrowser(browser()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| |
| net::HttpRequestHeaders headers; |
| base::RunLoop request_waiter; |
| URLLoaderInterceptor request_listener(base::BindLambdaForTesting( |
| [&](URLLoaderInterceptor::RequestParams* params) { |
| if (params->url_request.url == download_url) { |
| headers = params->url_request.headers; |
| request_waiter.Quit(); |
| } |
| return false; |
| })); |
| |
| // Right-click on the link and choose Save Image As. This will download the |
| // image. |
| ContextMenuNotificationObserver context_menu_observer( |
| IDC_CONTENT_CONTEXT_SAVEIMAGEAS); |
| |
| WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); |
| blink::WebMouseEvent mouse_event( |
| blink::WebInputEvent::Type::kMouseDown, |
| blink::WebInputEvent::kNoModifiers, |
| blink::WebInputEvent::GetStaticTimeStampForTests()); |
| mouse_event.button = blink::WebMouseEvent::Button::kRight; |
| mouse_event.SetPositionInWidget(15, 15); |
| mouse_event.click_count = 1; |
| tab->GetPrimaryMainFrame() |
| ->GetRenderViewHost() |
| ->GetWidget() |
| ->ForwardMouseEvent(mouse_event); |
| mouse_event.SetType(blink::WebInputEvent::Type::kMouseUp); |
| tab->GetPrimaryMainFrame() |
| ->GetRenderViewHost() |
| ->GetWidget() |
| ->ForwardMouseEvent(mouse_event); |
| waiter->WaitForFinished(); |
| EXPECT_EQ(headers.GetHeader(net::HttpRequestHeaders::kAccept) |
| .value_or(std::string()), |
| blink::network_utils::ImageAcceptHeader()); |
| EXPECT_EQ(1u, waiter->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| CheckDownloadStates(1, DownloadItem::COMPLETE); |
| } |
| |
| #if BUILDFLAG(SAFE_BROWSING_DOWNLOAD_PROTECTION) |
| |
| namespace { |
| |
| // This is a custom DownloadTestObserver for |
| // DangerousFileWithSBDisabledBeforeCompletion test that disables the |
| // SafeBrowsing service when a single download is IN_PROGRESS and has a target |
| // path assigned. DownloadItemImpl is expected to call MaybeCompleteDownload |
| // soon afterwards and we want to disable the service before then. |
| class DisableSafeBrowsingOnInProgressDownload |
| : public content::DownloadTestObserver { |
| public: |
| explicit DisableSafeBrowsingOnInProgressDownload(Browser* browser) |
| : DownloadTestObserver(DownloadManagerForBrowser(browser), |
| 1, |
| ON_DANGEROUS_DOWNLOAD_QUIT), |
| browser_(browser), |
| final_state_seen_(false) { |
| Init(); |
| } |
| ~DisableSafeBrowsingOnInProgressDownload() override = default; |
| |
| bool IsDownloadInFinalState(DownloadItem* download) override { |
| if (download->GetState() != DownloadItem::IN_PROGRESS || |
| download->GetTargetFilePath().empty()) |
| return false; |
| |
| if (final_state_seen_) |
| return true; |
| |
| final_state_seen_ = true; |
| browser_->profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, |
| false); |
| EXPECT_EQ(download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT, |
| download->GetDangerType()); |
| EXPECT_FALSE(download->IsDangerous()); |
| EXPECT_NE(safe_browsing::DownloadFileType::NOT_DANGEROUS, |
| DownloadItemModel(download).GetDangerLevel()); |
| return true; |
| } |
| |
| private: |
| raw_ptr<Browser> browser_; |
| bool final_state_seen_; |
| }; |
| |
| } // namespace |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, |
| DangerousFileWithSBDisabledBeforeCompletion) { |
| browser()->profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, |
| true); |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL download_url = |
| embedded_test_server()->GetURL(DownloadTestBase::kDangerousMockFilePath); |
| |
| std::unique_ptr<content::DownloadTestObserver> dangerous_observer( |
| DangerousDownloadWaiter( |
| browser(), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_QUIT)); |
| std::unique_ptr<content::DownloadTestObserver> in_progress_observer( |
| new DisableSafeBrowsingOnInProgressDownload(browser())); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), download_url, WindowOpenDisposition::NEW_BACKGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_NO_WAIT); |
| in_progress_observer->WaitForFinished(); |
| |
| // SafeBrowsing should have been disabled by our observer. |
| ASSERT_FALSE(browser()->profile()->GetPrefs()->GetBoolean( |
| prefs::kSafeBrowsingEnabled)); |
| |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> downloads; |
| DownloadManagerForBrowser(browser())->GetAllDownloads(&downloads); |
| ASSERT_EQ(1u, downloads.size()); |
| DownloadItem* download = downloads[0]; |
| |
| dangerous_observer->WaitForFinished(); |
| |
| EXPECT_TRUE(download->IsDangerous()); |
| EXPECT_EQ(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE, |
| download->GetDangerType()); |
| download->Cancel(true); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DangerousFileWithSBDisabledBeforeStart) { |
| // Disable SafeBrowsing |
| browser()->profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, |
| false); |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL download_url = |
| embedded_test_server()->GetURL(DownloadTestBase::kDangerousMockFilePath); |
| |
| std::unique_ptr<content::DownloadTestObserver> dangerous_observer( |
| DangerousDownloadWaiter( |
| browser(), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_QUIT)); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), download_url, WindowOpenDisposition::NEW_BACKGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_NO_WAIT); |
| dangerous_observer->WaitForFinished(); |
| |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> downloads; |
| DownloadManagerForBrowser(browser())->GetAllDownloads(&downloads); |
| ASSERT_EQ(1u, downloads.size()); |
| |
| DownloadItem* download = downloads[0]; |
| EXPECT_TRUE(download->IsDangerous()); |
| EXPECT_EQ(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE, |
| download->GetDangerType()); |
| |
| download->Cancel(true); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, SafeSupportedFile) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL download_url = |
| embedded_test_server()->GetURL("/downloads/a_zip_file.zip"); |
| |
| DownloadAndWait(browser(), download_url); |
| |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> downloads; |
| DownloadManagerForBrowser(browser())->GetAllDownloads(&downloads); |
| ASSERT_EQ(1u, downloads.size()); |
| |
| DownloadItem* download = downloads[0]; |
| EXPECT_FALSE(download->IsDangerous()); |
| EXPECT_EQ(download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, |
| download->GetDangerType()); |
| |
| download->Cancel(true); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTestWithFakeSafeBrowsing, |
| SendUncommonDownloadReportIfUserProceed) { |
| browser()->profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, |
| true); |
| // Make a dangerous file. |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL download_url = |
| embedded_test_server()->GetURL(DownloadTestBase::kDangerousMockFilePath); |
| |
| std::unique_ptr<content::DownloadTestObserver> dangerous_observer( |
| DangerousDownloadWaiter( |
| browser(), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_QUIT)); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), download_url)); |
| dangerous_observer->WaitForFinished(); |
| |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> downloads; |
| DownloadManagerForBrowser(browser())->GetAllDownloads(&downloads); |
| ASSERT_EQ(1u, downloads.size()); |
| DownloadItem* download = downloads[0]; |
| DownloadItemModel model(download); |
| DownloadCommands(model.GetWeakPtr()).ExecuteCommand(DownloadCommands::KEEP); |
| |
| safe_browsing::ClientSafeBrowsingReportRequest actual_report; |
| actual_report.ParseFromString( |
| test_safe_browsing_factory_->fake_safe_browsing_service() |
| ->serialized_download_report()); |
| EXPECT_EQ(safe_browsing::ClientSafeBrowsingReportRequest:: |
| DANGEROUS_DOWNLOAD_WARNING, |
| actual_report.type()); |
| EXPECT_EQ(safe_browsing::ClientDownloadResponse::UNCOMMON, |
| actual_report.download_verdict()); |
| EXPECT_EQ(download_url.spec(), actual_report.url()); |
| EXPECT_TRUE(actual_report.did_proceed()); |
| |
| download->Cancel(true); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTestWithFakeSafeBrowsing, |
| SendDownloadReportIfUserProceedsDeepScanning) { |
| browser()->profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, |
| true); |
| // Make a dangerous file. |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL download_url = |
| embedded_test_server()->GetURL(DownloadTestBase::kDangerousMockFilePath); |
| auto* download_protection_service = |
| static_cast<FakeDownloadProtectionService*>( |
| g_browser_process->safe_browsing_service() |
| ->download_protection_service()); |
| download_protection_service->SetFakeResponse( |
| safe_browsing::DownloadCheckResult::PROMPT_FOR_SCANNING, |
| safe_browsing::ClientDownloadResponse::UNCOMMON); |
| std::unique_ptr<content::DownloadTestObserver> dangerous_observer( |
| DangerousDownloadWaiter( |
| browser(), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_QUIT)); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), download_url)); |
| dangerous_observer->WaitForFinished(); |
| |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> downloads; |
| DownloadManagerForBrowser(browser())->GetAllDownloads(&downloads); |
| ASSERT_EQ(1u, downloads.size()); |
| DownloadItem* download = downloads[0]; |
| DownloadItemModel model(download); |
| DownloadCommands(model.GetWeakPtr()) |
| .ExecuteCommand(DownloadCommands::BYPASS_DEEP_SCANNING); |
| |
| safe_browsing::ClientSafeBrowsingReportRequest actual_report; |
| actual_report.ParseFromString( |
| test_safe_browsing_factory_->fake_safe_browsing_service() |
| ->serialized_download_report()); |
| EXPECT_EQ(safe_browsing::ClientSafeBrowsingReportRequest:: |
| DANGEROUS_DOWNLOAD_WARNING, |
| actual_report.type()); |
| EXPECT_EQ(safe_browsing::ClientDownloadResponse::UNCOMMON, |
| actual_report.download_verdict()); |
| EXPECT_EQ(download_url.spec(), actual_report.url()); |
| EXPECT_TRUE(actual_report.did_proceed()); |
| |
| // Trying to quit when the download hasn't completed will show a "Continue |
| // downloading?" prompt, and the test will timeout trying to quit. Instead |
| // wait for the download to complete before quitting. |
| std::unique_ptr<content::DownloadTestObserver> completed_observer( |
| CreateWaiter(browser(), 1)); |
| completed_observer->WaitForFinished(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTestWithFakeSafeBrowsing, |
| SendUncommonDownloadReportIfUserDiscard) { |
| browser()->profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, |
| true); |
| // Make a dangerous file. |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL download_url = |
| embedded_test_server()->GetURL(DownloadTestBase::kDangerousMockFilePath); |
| std::unique_ptr<content::DownloadTestObserver> dangerous_observer( |
| DangerousDownloadWaiter( |
| browser(), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_QUIT)); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), download_url)); |
| dangerous_observer->WaitForFinished(); |
| |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> downloads; |
| DownloadManagerForBrowser(browser())->GetAllDownloads(&downloads); |
| ASSERT_EQ(1u, downloads.size()); |
| DownloadItem* download = downloads[0]; |
| DownloadItemModel model(download); |
| DownloadCommands(model.GetWeakPtr()) |
| .ExecuteCommand(DownloadCommands::DISCARD); |
| |
| safe_browsing::ClientSafeBrowsingReportRequest actual_report; |
| actual_report.ParseFromString( |
| test_safe_browsing_factory_->fake_safe_browsing_service() |
| ->serialized_download_report()); |
| EXPECT_EQ(safe_browsing::ClientSafeBrowsingReportRequest:: |
| DANGEROUS_DOWNLOAD_WARNING, |
| actual_report.type()); |
| EXPECT_EQ(safe_browsing::ClientDownloadResponse::UNCOMMON, |
| actual_report.download_verdict()); |
| EXPECT_EQ(download_url.spec(), actual_report.url()); |
| EXPECT_FALSE(actual_report.did_proceed()); |
| } |
| |
| #endif // SAFE_BROWSING_DOWNLOAD_PROTECTION |
| |
| // The rest of these tests rely on the download surface, which ChromeOS doesn't |
| // use (crbug.com/1323505 is tracking Download Bubble on ChromeOS). |
| #if !BUILDFLAG(IS_CHROMEOS) |
| // Test that the download surface is shown by starting a download. |
| // |
| // TODO(crbug.com/40266279): This test is flaky. Perhaps because it depends on |
| // focus, in which case it should be an interactive ui test instead of a |
| // browser test? |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DISABLED_DownloadAndWait) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/downloads/a_zip_file.zip"); |
| |
| DownloadAndWait(browser(), url); |
| |
| // The download surface should be visible. |
| EXPECT_TRUE(IsDownloadDetailedUiVisible(browser()->window())); |
| } |
| |
| // Flaky. crbug.com/1383009 |
| // Test that when downloading an item in Incognito mode, the download surface is |
| // not visible after closing the Incognito window. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, |
| DISABLED_IncognitoDownloadSurfaceVisibility) { |
| Browser* incognito = CreateIncognitoBrowser(); |
| ASSERT_TRUE(incognito); |
| |
| // Download a file in the Incognito window and wait. |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| // Since |incognito| is a separate browser, we have to set it up explicitly. |
| incognito->profile()->GetPrefs()->SetBoolean(prefs::kPromptForDownload, |
| false); |
| DownloadAndWait(incognito, url); |
| |
| // Verify that the download surface is showing for the Incognito window. |
| EXPECT_TRUE(IsDownloadDetailedUiVisible(incognito->window())); |
| |
| // Verify that the regular window does not have a download surface. |
| EXPECT_FALSE(IsDownloadDetailedUiVisible(browser()->window())); |
| } |
| |
| // Download a file in a new window. |
| // Verify that we have 2 windows, and the download surface is not visible in the |
| // first window, but is visible in the second window. |
| // Close the new window. |
| // Verify that we have 1 window, and the download surface is not visible. |
| // |
| // Regression test for http://crbug.com/44454 |
| // TODO(crbug.com/40262026): Flaky on Linux. |
| #if BUILDFLAG(IS_LINUX) |
| #define MAYBE_NewWindow DISABLED_NewWindow |
| #else |
| #define MAYBE_NewWindow NewWindow |
| #endif |
| IN_PROC_BROWSER_TEST_F(DownloadTest, MAYBE_NewWindow) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| const Browser* first_browser = browser(); |
| |
| // Download a file in a new window and wait. |
| DownloadAndWaitWithDisposition(browser(), url, |
| WindowOpenDisposition::NEW_WINDOW, |
| ui_test_utils::BROWSER_TEST_NO_WAIT); |
| |
| // When the download finishes, the download surface SHOULD NOT be visible in |
| // the first window. |
| ExpectWindowCountAfterDownload(2); |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| // Download surface should close. |
| EXPECT_FALSE(IsDownloadDetailedUiVisible(browser()->window())); |
| |
| // The download surface SHOULD be visible in the second window. |
| std::set<Browser*> original_browsers; |
| original_browsers.insert(browser()); |
| Browser* download_browser = |
| ui_test_utils::GetBrowserNotInSet(original_browsers); |
| ASSERT_TRUE(download_browser); |
| EXPECT_NE(download_browser, browser()); |
| EXPECT_EQ(1, download_browser->tab_strip_model()->count()); |
| EXPECT_TRUE(IsDownloadDetailedUiVisible(download_browser->window())); |
| |
| // Close the new window. |
| chrome::CloseWindow(download_browser); |
| |
| ui_test_utils::WaitForBrowserToClose(download_browser); |
| EXPECT_EQ(first_browser, browser()); |
| ExpectWindowCountAfterDownload(1); |
| |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| // Download surface should close. |
| EXPECT_FALSE(IsDownloadDetailedUiVisible(browser()->window())); |
| |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| CheckDownload(browser(), file, file); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, PRE_DownloadTest_History) { |
| // Download a file and wait for it to be stored. |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL download_url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| HistoryObserver observer(browser()->profile()); |
| DownloadAndWait(browser(), download_url); |
| observer.WaitForStored(); |
| base::RunLoop run_loop; |
| HistoryServiceFactory::GetForProfile(browser()->profile(), |
| ServiceAccessType::IMPLICIT_ACCESS) |
| ->FlushForTest(run_loop.QuitWhenIdleClosure()); |
| run_loop.Run(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadTest_History) { |
| // This starts up right after PRE_DownloadTest_History and shares the same |
| // profile directory. |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL download_url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> downloads; |
| content::DownloadManager* manager = DownloadManagerForBrowser(browser()); |
| |
| // Wait for the history to be loaded with a single DownloadItem. Check that |
| // it's the file that was downloaded in PRE_DownloadTest_History. |
| base::FilePath file(FILE_PATH_LITERAL("download-test1.lib")); |
| CreatedObserver created_observer(manager); |
| created_observer.Wait(); |
| manager->GetAllDownloads(&downloads); |
| ASSERT_EQ(1UL, downloads.size()); |
| DownloadItem* item = downloads[0]; |
| EXPECT_EQ(file.value(), item->GetFullPath().BaseName().value()); |
| EXPECT_EQ(file.value(), item->GetTargetFilePath().BaseName().value()); |
| // Only compare the host name, port will be different for each embedded test |
| // server session. |
| EXPECT_EQ(download_url.host(), item->GetURL().host()); |
| // The following are set by download-test1.lib.mock-http-headers. |
| std::string etag = item->GetETag(); |
| base::TrimWhitespaceASCII(etag, base::TRIM_ALL, &etag); |
| EXPECT_EQ("abracadabra", etag); |
| |
| std::string last_modified = item->GetLastModifiedTime(); |
| base::TrimWhitespaceASCII(last_modified, base::TRIM_ALL, &last_modified); |
| EXPECT_EQ("Mon, 13 Nov 2006 20:31:09 GMT", last_modified); |
| |
| // Downloads that were restored from history shouldn't cause the download |
| // surface to be displayed. |
| EXPECT_FALSE(IsDownloadDetailedUiVisible(browser()->window())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, HiddenDownload) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/" + std::string(kDownloadTest1Path)); |
| |
| DownloadManager* download_manager = DownloadManagerForBrowser(browser()); |
| std::unique_ptr<content::DownloadTestObserver> observer( |
| new content::DownloadTestObserverTerminal( |
| download_manager, 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL)); |
| |
| // Download and set IsHiddenDownload to true. |
| WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| std::unique_ptr<DownloadUrlParameters> params( |
| content::DownloadRequestUtils::CreateDownloadForWebContentsMainFrame( |
| web_contents, url, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| params->set_callback(base::BindOnce(&SetHiddenDownloadCallback)); |
| download_manager->DownloadUrl(std::move(params)); |
| observer->WaitForFinished(); |
| |
| // Verify that download surface is not shown. |
| EXPECT_FALSE(IsDownloadDetailedUiVisible(browser()->window())); |
| } |
| |
| // High flake rate; https://crbug.com/1247392. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DISABLED_AutoOpenClosesSurface) { |
| base::FilePath file(FILE_PATH_LITERAL("download-autoopen.txt")); |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/download-autoopen.txt"); |
| |
| ASSERT_TRUE( |
| GetDownloadPrefs(browser())->EnableAutoOpenByUserBasedOnExtension(file)); |
| |
| DownloadAndWait(browser(), url); |
| |
| // Download surface should close. |
| EXPECT_FALSE(IsDownloadDetailedUiVisible(browser()->window())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DownloadTest, CrxDenyInstallClosesSurface) { |
| std::unique_ptr<base::AutoReset<bool>> allow_offstore_install = |
| download_crx_util::OverrideOffstoreInstallAllowedForTesting(true); |
| |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL extension_url = |
| embedded_test_server()->GetURL("/" + std::string(kGoodCrxPath)); |
| |
| std::unique_ptr<content::DownloadTestObserver> observer( |
| DangerousDownloadWaiter( |
| browser(), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_DENY)); |
| NavigateParams params(browser(), extension_url, ui::PAGE_TRANSITION_TYPED); |
| params.user_gesture = false; |
| ui_test_utils::NavigateToURL(¶ms); |
| |
| observer->WaitForFinished(); |
| |
| // Download surface should close. |
| EXPECT_FALSE(IsDownloadDetailedUiVisible(browser()->window())); |
| } |
| |
| // Test that the download UI surface only shows on the appropriate window for a |
| // web app. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, WebAppDownloadOnlyShowsUiInWebAppWindow) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/downloads/a_zip_file.zip"); |
| |
| // Load an app. |
| webapps::AppId app_id = web_app::test::InstallDummyWebApp( |
| browser()->profile(), "testapp", embedded_test_server()->GetURL("/")); |
| Browser* app_browser = |
| web_app::LaunchWebAppBrowserAndWait(browser()->profile(), app_id); |
| |
| DownloadAndWait(app_browser, url); |
| |
| EXPECT_FALSE(IsDownloadUiVisible(browser()->window())); |
| EXPECT_TRUE(IsDownloadUiVisible(app_browser->window())); |
| } |
| |
| // Test that the download UI surface only does not show in a web app window |
| // for a regular Chrome window's downloads, even if it is the same domain. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, |
| RegularBrowserDownloadDoesNotShowInWebAppWindow) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/downloads/a_zip_file.zip"); |
| |
| // Load an app. |
| webapps::AppId app_id = web_app::test::InstallDummyWebApp( |
| browser()->profile(), "testapp", embedded_test_server()->GetURL("/")); |
| Browser* app_browser = |
| web_app::LaunchWebAppBrowserAndWait(browser()->profile(), app_id); |
| |
| DownloadAndWait(browser(), url); |
| |
| EXPECT_TRUE(IsDownloadUiVisible(browser()->window())); |
| EXPECT_FALSE(IsDownloadUiVisible(app_browser->window())); |
| } |
| #endif // !BUILDFLAG(IS_CHROMEOS) |
| |
| // Test that web app info is properly attached to the download. |
| IN_PROC_BROWSER_TEST_F(DownloadTest, DownloadFromWebApp) { |
| embedded_test_server()->ServeFilesFromDirectory(GetTestDataDirectory()); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL("/downloads/a_zip_file.zip"); |
| |
| // Load an app. |
| webapps::AppId app_id = web_app::test::InstallDummyWebApp( |
| browser()->profile(), "testapp", embedded_test_server()->GetURL("/")); |
| Browser* app_browser = |
| web_app::LaunchWebAppBrowserAndWait(browser()->profile(), app_id); |
| |
| DownloadAndWait(app_browser, url); |
| |
| DownloadManager* manager = DownloadManagerForBrowser(app_browser); |
| std::vector<raw_ptr<DownloadItem, VectorExperimental>> all_downloads; |
| manager->GetAllDownloads(&all_downloads); |
| ASSERT_EQ(all_downloads.size(), 1u); |
| auto* web_app_data = DownloadItemWebAppData::Get(all_downloads[0]); |
| EXPECT_NE(web_app_data, nullptr); |
| EXPECT_EQ(web_app_data->id(), app_id); |
| } |