chromium/chrome/browser/chrome_service_worker_browsertest.cc

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

// This file tests that Service Workers (a Content feature) work in the Chromium
// embedder.

#include <optional>
#include <string_view>

#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/json/json_reader.h"
#include "base/numerics/safe_conversions.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/favicon/content/content_favicon_driver.h"
#include "components/favicon/core/favicon_driver_observer.h"
#include "components/metrics/content/subprocess_metrics_provider.h"
#include "components/nacl/common/buildflags.h"
#include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/page.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/service_worker_context.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/webui_config.h"
#include "content/public/browser/webui_config_map.h"
#include "content/public/common/content_features.h"
#include "content/public/common/isolated_world_ids.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_frame_navigation_observer.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 "ppapi/shared_impl/ppapi_switches.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "third_party/blink/public/common/messaging/string_message_codec.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_registration_options.mojom.h"
#include "url/origin.h"

PageLoadMetricsTestWaiter;

namespace chrome_service_worker_browser_test {

const char kInstallAndWaitForActivatedPage[] =;

const char kInstallAndWaitForActivatedPageWithModuleScript[] =;

template <typename T>
static void ExpectResultAndRun(T expected,
                               base::OnceClosure continuation,
                               T actual) {}

class ChromeServiceWorkerTest : public InProcessBrowserTest {};

// http://crbug.com/368570
IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest,
                       CanShutDownWithRegisteredServiceWorker) {}

// http://crbug.com/419290
IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest,
                       CanCloseIncognitoWindowWithServiceWorkerController) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest,
                       FailRegisterServiceWorkerWhenJSDisabled) {}

IN_PROC_BROWSER_TEST_F(
    ChromeServiceWorkerTest,
    FallbackMainResourceRequestWhenJSDisabledForClassicServiceWorker) {}

IN_PROC_BROWSER_TEST_F(
    ChromeServiceWorkerTest,
    FallbackMainResourceRequestWhenJSDisabledForModuleServiceWorker) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest,
                       StartServiceWorkerAndDispatchMessage) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest,
                       StartServiceWorkerWithModuleScriptAndDispatchMessage) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest, SubresourceCountUKM) {}

// TODO(crbug.com/355104619): The test is flaky. Re-enable it.
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
#define MAYBE_StaticRoutingAPISubresourceHistogramTest
#else
#define MAYBE_StaticRoutingAPISubresourceHistogramTest
#endif
IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest,
                       MAYBE_StaticRoutingAPISubresourceHistogramTest) {}

// TODO(crbug.com/360158408): The test is flaky on mac bots.
#if BUILDFLAG(IS_MAC)
#define MAYBE_SubresourceCountUMA
#else
#define MAYBE_SubresourceCountUMA
#endif
IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest, MAYBE_SubresourceCountUMA) {}

class ChromeServiceWorkerFetchTest : public ChromeServiceWorkerTest {};

class FaviconUpdateWaiter : public favicon::FaviconDriverObserver {};

class ChromeServiceWorkerLinkFetchTest : public ChromeServiceWorkerFetchTest {};

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerLinkFetchTest, ManifestSameOrigin) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerLinkFetchTest,
                       ManifestSameOriginUseCredentials) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerLinkFetchTest, ManifestOtherOrigin) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerLinkFetchTest,
                       ManifestOtherOriginUseCredentials) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerLinkFetchTest, FaviconSameOrigin) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerLinkFetchTest, FaviconOtherOrigin) {}

#if BUILDFLAG(ENABLE_NACL)
// This test registers a service worker and then loads a controlled iframe that
// creates a PNaCl plugin in an <embed> element. Once loaded, the PNaCl plugin
// is ordered to do a resource request for "/echo". The service worker records
// all the fetch events it sees. Since requests for plug-ins and requests
// initiated by plug-ins should not be interecepted by service workers, we
// expect that the the service worker only see the navigation request for the
// iframe.
class ChromeServiceWorkerFetchPPAPITest : public ChromeServiceWorkerFetchTest {
 public:
  ChromeServiceWorkerFetchPPAPITest(const ChromeServiceWorkerFetchPPAPITest&) =
      delete;
  ChromeServiceWorkerFetchPPAPITest& operator=(
      const ChromeServiceWorkerFetchPPAPITest&) = delete;

 protected:
  ChromeServiceWorkerFetchPPAPITest() {}
  ~ChromeServiceWorkerFetchPPAPITest() override {}

  void SetUpCommandLine(base::CommandLine* command_line) override {
    ChromeServiceWorkerFetchTest::SetUpCommandLine(command_line);
    // Use --enable-nacl flag to ensure the PNaCl module can load (without
    // needing to use an OT token)
    command_line->AppendSwitch(switches::kEnableNaCl);
  }

  void SetUpOnMainThread() override {
    base::FilePath document_root;
    ASSERT_TRUE(ui_test_utils::GetRelativeBuildDirectory(&document_root));
    embedded_test_server()->AddDefaultHandlers(
        document_root.Append(FILE_PATH_LITERAL("nacl_test_data"))
            .Append(FILE_PATH_LITERAL("pnacl")));
    ChromeServiceWorkerFetchTest::SetUpOnMainThread();
    test_page_url_ = GetURL("/pnacl_url_loader.html");
  }

  std::string GetNavigationRequestString(const std::string& fragment) const {
    return RequestString(test_page_url_ + fragment, "navigate", "include", "");
  }

  std::string ExecutePNACLUrlLoaderTest(const std::string& mode) {
    content::DOMMessageQueue message_queue;
    EXPECT_TRUE(content::ExecJs(
        browser()->tab_strip_model()->GetActiveWebContents(),
        base::StringPrintf("reportOnFetch = false;"
                           "var iframe = document.createElement('iframe');"
                           "iframe.src='%s#%s';"
                           "document.body.appendChild(iframe);",
                           test_page_url_.c_str(), mode.c_str())));

    std::string json;
    EXPECT_TRUE(message_queue.WaitForMessage(&json));

    base::Value result =
        base::JSONReader::Read(json, base::JSON_ALLOW_TRAILING_COMMAS).value();

    EXPECT_TRUE(result.is_string());
    EXPECT_EQ(base::StringPrintf("OnOpen%s", mode.c_str()), result.GetString());
    return EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
                  "reportRequests();")
        .ExtractString();
  }

 private:
  std::string test_page_url_;
};

// Flaky on Windows and Linux ASan. https://crbug.com/1113802
IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerFetchPPAPITest,
                       DISABLED_NotInterceptedByServiceWorker) {
  // Only the navigation to the iframe should be intercepted by the service
  // worker. The request for the PNaCl manifest ("/pnacl_url_loader.nmf"),
  // the request for the compiled code ("/pnacl_url_loader_newlib_pnacl.pexe"),
  // and any other requests initiated by the plug-in ("/echo") should not be
  // seen by the service worker.
  const std::string fragment =
      "NotIntercepted";  // this string is not important.
  EXPECT_EQ(GetNavigationRequestString("#" + fragment),
            ExecutePNACLUrlLoaderTest(fragment));
}
#endif  // BUILDFLAG(ENABLE_NACL)

class ChromeServiceWorkerNavigationHintTest : public ChromeServiceWorkerTest {};

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationHintTest, Started) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationHintTest,
                       StartedModuleScript) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationHintTest, AlreadyRunning) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationHintTest,
                       AlreadyRunningModuleScript) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationHintTest,
                       NoServiceWorkerRegistration) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationHintTest,
                       NoActiveServiceWorkerVersion) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationHintTest, NoFetchHandler) {}

IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationHintTest,
                       NoFetchHandlerModuleScript) {}

// URLDataSource that serves an empty page for all URLs except source/sw.js
// for which it serves valid service worker code.
class StaticURLDataSource : public content::URLDataSource {};

class StaticWebUIController : public content::WebUIController {};

class TestWebUIConfig : public content::WebUIConfig {};

class ChromeWebUIServiceWorkerTest : public ChromeServiceWorkerTest {};

// Tests that registering a service worker in JavaScript with a chrome:// URL
// fails.
IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerTest,
                       DisallowChromeSchemeInJavaScript) {}

// Tests that registering a service worker with a chrome:// URL fails.
IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerTest, DisallowChromeScheme) {}

// Tests that registering a service worker in JavaScript with a
// chrome-untrusted:// URL fails.
IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerTest,
                       DisallowChromeUntrustedSchemeInJavaScript) {}

// Tests that registering a service worker with a chrome-untrusted:// URL fails
// if the flag is not enabled.
IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerTest,
                       DisllowChromeUntrustedScheme) {}

class ChromeWebUIServiceWorkerFlagTest : public ChromeWebUIServiceWorkerTest {};

// Tests that registering a service worker in JavaScript with a
// chrome:// URL fails even if the flag is enabled.
IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerFlagTest,
                       DisallowChromeSchemeInJavaScript) {}

// Tests that registering a service worker with a chrome-untrusted:// URL fails
// even if the flag is enabled.
IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerFlagTest,
                       DisallowChromeUntrustedScheme) {}

// Tests that registering a service worker with a chrome:// URL works
// if the flag is enabled.
IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerFlagTest, AllowChromeScheme) {}

// Tests that registering a service worker in JavaScript with a
// chrome-untrusted:// URL fails.
IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerFlagTest,
                       DisallowChromeUntrustedSchemeInJavaScript) {}

class ChromeWebUIServiceWorkerUntrustedFlagTest
    : public ChromeWebUIServiceWorkerTest {};

// Tests that registering a service worker in JavaScript with a chrome:// URL
// fails even if the untrusted flag is enabled.
IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerUntrustedFlagTest,
                       DisallowChromeSchemeInJavaScript) {}

// Tests that registering a service worker with a chrome:// URL fails even
// if the untrusted flag is enabled.
IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerUntrustedFlagTest,
                       DisallowChromeScheme) {}

// Tests that registering a service worker with a chrome-untrusted:// URL works
// if the flag is enabled.
IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerUntrustedFlagTest,
                       AllowChromeUntrustedScheme) {}

// Tests that registering a service worker in JavaScript with a
// chrome-untrusted:// URL fails.
IN_PROC_BROWSER_TEST_F(ChromeWebUIServiceWorkerUntrustedFlagTest,
                       DisallowChromeUntrustedSchemeInJavaScript) {}

enum class ServicifiedFeatures {};

// A simple fixture used for navigation preload tests so far. The fixture
// stashes the HttpRequest to a certain URL, useful for inspecting the headers
// to see if it was a navigation preload request and if it contained cookies.
//
// This is in //chrome instead of //content since the tests exercise the
// kBlockThirdPartyCookies preference which is not a //content concept.
class ChromeServiceWorkerNavigationPreloadTest : public InProcessBrowserTest {};

// Tests navigation preload during a navigation in the top-level frame
// when third-party cookies are blocked. The navigation preload request
// should be sent with cookies as normal. Regression test for
// https://crbug.com/913220.
IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationPreloadTest,
                       TopFrameWithThirdPartyBlocking) {}

// Tests navigation preload during a navigation in a third-party iframe
// when third-party cookies are blocked. This blocks service worker as well,
// so the navigation preload request should not be sent. And the navigation
// request should not include cookies.
IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationPreloadTest,
                       SubFrameWithThirdPartyBlocking) {}

}  // namespace chrome_service_worker_browser_test