chromium/chrome/browser/webshare/share_service_browsertest.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/webshare/share_service_impl.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/safe_browsing/content/common/file_type_policies_test_util.h"
#include "components/safe_browsing/core/browser/db/fake_database_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/prerender_test_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/sharesheet/sharesheet_types.h"
#include "chrome/browser/webshare/chromeos/sharesheet_client.h"
#include "chromeos/components/sharesheet/constants.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "chrome/browser/webshare/win/scoped_share_operation_fake_components.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "chrome/browser/webshare/mac/sharing_service_operation.h"
#include "third_party/blink/public/mojom/webshare/webshare.mojom.h"
#endif

class ShareServiceBrowserTest : public InProcessBrowserTest {
 public:
  ShareServiceBrowserTest() {
    feature_list_.InitAndEnableFeature(features::kWebShare);
  }

  void SetUpOnMainThread() override {
    InProcessBrowserTest::SetUpOnMainThread();
#if BUILDFLAG(IS_CHROMEOS)
    webshare::SharesheetClient::SetSharesheetCallbackForTesting(
        base::BindRepeating(&ShareServiceBrowserTest::AcceptShareRequest));
#endif
#if BUILDFLAG(IS_WIN)
    ASSERT_NO_FATAL_FAILURE(scoped_fake_components_.SetUp());
#endif
#if BUILDFLAG(IS_MAC)
    webshare::SharingServiceOperation::SetSharePickerCallbackForTesting(
        base::BindRepeating(&ShareServiceBrowserTest::AcceptShareRequest));
#endif
  }

#if BUILDFLAG(IS_CHROMEOS)
  static void AcceptShareRequest(
      content::WebContents* web_contents,
      const std::vector<base::FilePath>& file_paths,
      const std::vector<std::string>& content_types,
      const std::vector<uint64_t>& file_sizes,
      const std::string& text,
      const std::string& title,
      sharesheet::DeliveredCallback delivered_callback) {
    std::move(delivered_callback).Run(sharesheet::SharesheetResult::kSuccess);
  }
#endif

#if BUILDFLAG(IS_MAC)
  static void AcceptShareRequest(
      content::WebContents* web_contents,
      const std::vector<base::FilePath>& file_paths,
      const std::string& text,
      const std::string& title,
      const GURL& url,
      blink::mojom::ShareService::ShareCallback close_callback) {
    std::move(close_callback).Run(blink::mojom::ShareError::OK);
  }
#endif

 private:
  base::test::ScopedFeatureList feature_list_;
#if BUILDFLAG(IS_WIN)
  webshare::ScopedShareOperationFakeComponents scoped_fake_components_;
#endif
};

IN_PROC_BROWSER_TEST_F(ShareServiceBrowserTest, Text) {
  const int kRepeats = 4;

  base::HistogramTester histogram_tester;
  ASSERT_TRUE(embedded_test_server()->Start());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/webshare/index.html")));

  content::WebContents* const contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  const std::string script = "share_text('hello')";
  for (int index = 0; index < kRepeats; ++index) {
    const content::EvalJsResult result = content::EvalJs(contents, script);
    EXPECT_EQ("share succeeded", result);
  }

  histogram_tester.ExpectBucketCount(kWebShareApiCountMetric,
                                     WebShareMethod::kShare, kRepeats);
}

class SafeBrowsingShareServiceBrowserTest : public ShareServiceBrowserTest {
 public:
  SafeBrowsingShareServiceBrowserTest()
      : safe_browsing_factory_(
            std::make_unique<safe_browsing::TestSafeBrowsingServiceFactory>()) {
  }

 protected:
  void CreatedBrowserMainParts(
      content::BrowserMainParts* browser_main_parts) override {
    fake_safe_browsing_database_manager_ =
        base::MakeRefCounted<safe_browsing::FakeSafeBrowsingDatabaseManager>(
            content::GetUIThreadTaskRunner({}));
    safe_browsing_factory_->SetTestDatabaseManager(
        fake_safe_browsing_database_manager_.get());
    safe_browsing::SafeBrowsingService::RegisterFactory(
        safe_browsing_factory_.get());
    ShareServiceBrowserTest::CreatedBrowserMainParts(browser_main_parts);
  }

  void AddDangerousUrl(const GURL& dangerous_url) {
    fake_safe_browsing_database_manager_->AddDangerousUrl(
        dangerous_url,
        safe_browsing::SBThreatType::SB_THREAT_TYPE_URL_BINARY_MALWARE);
  }

  void TearDown() override {
    ShareServiceBrowserTest::TearDown();
    safe_browsing::SafeBrowsingService::RegisterFactory(nullptr);
  }

 private:
  scoped_refptr<safe_browsing::FakeSafeBrowsingDatabaseManager>
      fake_safe_browsing_database_manager_;
  std::unique_ptr<safe_browsing::TestSafeBrowsingServiceFactory>
      safe_browsing_factory_;
};

IN_PROC_BROWSER_TEST_F(SafeBrowsingShareServiceBrowserTest,
                       PortableDocumentFile) {
  safe_browsing::FileTypePoliciesTestOverlay policies;
  std::unique_ptr<safe_browsing::DownloadFileTypeConfig> file_type_config =
      std::make_unique<safe_browsing::DownloadFileTypeConfig>();
  auto* file_type = file_type_config->mutable_default_file_type();
  file_type->set_uma_value(-1);
  file_type->set_ping_setting(safe_browsing::DownloadFileType::FULL_PING);
  auto* platform_settings = file_type->add_platform_settings();
  platform_settings->set_danger_level(
      safe_browsing::DownloadFileType::NOT_DANGEROUS);
  platform_settings->set_auto_open_hint(
      safe_browsing::DownloadFileType::ALLOW_AUTO_OPEN);
  policies.SwapConfig(file_type_config);

  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url(embedded_test_server()->GetURL("/webshare/index.html"));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  content::WebContents* const contents =
      browser()->tab_strip_model()->GetActiveWebContents();

  EXPECT_EQ("share succeeded", content::EvalJs(contents, "share_pdf_file()"));

  AddDangerousUrl(url);
  EXPECT_EQ("share failed: NotAllowedError: Permission denied",
            content::EvalJs(contents, "share_pdf_file()"));
}

class ShareServicePrerenderBrowserTest : public ShareServiceBrowserTest {
 public:
  ShareServicePrerenderBrowserTest()
      : prerender_helper_(
            base::BindRepeating(&ShareServicePrerenderBrowserTest::web_contents,
                                base::Unretained(this))) {}
  ~ShareServicePrerenderBrowserTest() override = default;

 protected:
  content::WebContents* web_contents() {
    return browser()->tab_strip_model()->GetActiveWebContents();
  }

  content::test::PrerenderTestHelper prerender_helper_;
};

IN_PROC_BROWSER_TEST_F(ShareServicePrerenderBrowserTest, Text) {
  base::HistogramTester histogram_tester;
  ASSERT_TRUE(embedded_test_server()->Start());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/empty.html")));

  content::WebContents* const contents =
      browser()->tab_strip_model()->GetActiveWebContents();

  // Start a prerender.
  const GURL kPrerenderUrl =
      embedded_test_server()->GetURL("/webshare/index.html");
  const int kPrerenderHostId = prerender_helper_.AddPrerender((kPrerenderUrl));
  ASSERT_EQ(prerender_helper_.GetHostForUrl(kPrerenderUrl), kPrerenderHostId);

  content::RenderFrameHost* prerender_rfh =
      prerender_helper_.GetPrerenderedMainFrameHost(kPrerenderHostId);
  EXPECT_EQ(prerender_rfh->GetLifecycleState(),
            content::RenderFrameHost::LifecycleState::kPrerendering);
  const std::string script = "share_text('hello')";
  const content::EvalJsResult prerendered_result =
      content::EvalJs(prerender_rfh, script);
  EXPECT_EQ(
      "share failed: NotAllowedError: Failed to execute 'share' on "
      "'Navigator': Must be handling a user gesture to perform a share "
      "request.",
      prerendered_result);
  histogram_tester.ExpectBucketCount(kWebShareApiCountMetric,
                                     WebShareMethod::kShare, 0);

  // Activate the prerendered page.
  prerender_helper_.NavigatePrimaryPage(kPrerenderUrl);
  EXPECT_EQ(prerender_rfh->GetLifecycleState(),
            content::RenderFrameHost::LifecycleState::kActive);
  ASSERT_EQ(kPrerenderUrl, contents->GetLastCommittedURL());
  const content::EvalJsResult activated_result =
      content::EvalJs(prerender_rfh, script);
  EXPECT_EQ("share succeeded", activated_result);
  histogram_tester.ExpectBucketCount(kWebShareApiCountMetric,
                                     WebShareMethod::kShare, 1);
}