910e62b5创建于 1月15日历史提交
// 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 "base/command_line.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_destroyer.h"
#include "components/content_settings/core/common/content_settings.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/test/extension_test_message_listener.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "services/network/public/mojom/network_context.mojom.h"

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

namespace extensions {

using ContextType = extensions::browser_test_util::ContextType;

namespace {

enum class SameSiteCookieSemantics {
  kModern,
  kLegacy,
};

}  // namespace

#if !BUILDFLAG(IS_ANDROID)
// This test cannot be run by a Service Worked-based extension
// because it uses the Document object.
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ReadFromDocument) {
  ASSERT_TRUE(RunExtensionTest("cookies/read_from_doc")) << message_;
}
#endif

class CookiesApiTest : public ExtensionApiTest,
                       public testing::WithParamInterface<
                           std::tuple<ContextType, SameSiteCookieSemantics>> {
 public:
  CookiesApiTest() : ExtensionApiTest(std::get<0>(GetParam())) {}
  ~CookiesApiTest() override = default;
  CookiesApiTest(const CookiesApiTest&) = delete;
  CookiesApiTest& operator=(const CookiesApiTest&) = delete;

  void SetUpOnMainThread() override {
    ExtensionApiTest::SetUpOnMainThread();

    // If SameSite access semantics is "legacy", add content settings to allow
    // legacy access for all sites.
    if (!AreSameSiteCookieSemanticsModern()) {
      profile()
          ->GetDefaultStoragePartition()
          ->GetNetworkContext()
          ->GetCookieManager(
              cookie_manager_remote_.BindNewPipeAndPassReceiver());
      cookie_manager_remote_->SetContentSettings(
          ContentSettingsType::LEGACY_COOKIE_ACCESS,
          {ContentSettingPatternSource(
              ContentSettingsPattern::Wildcard(),
              ContentSettingsPattern::Wildcard(),
              base::Value(ContentSetting::CONTENT_SETTING_ALLOW),
              content_settings::ProviderType::kNone, false /* incognito */)},
          base::NullCallback());
      cookie_manager_remote_.FlushForTesting();
    }

    net::test_server::RegisterDefaultHandlers(embedded_test_server());
    host_resolver()->AddRule("*", "127.0.0.1");

    ASSERT_TRUE(StartEmbeddedTestServer());
  }

 protected:
  bool RunTest(const char* extension_name,
               bool allow_in_incognito = false,
               const char* custom_arg = nullptr) {
    return RunExtensionTest(extension_name, {.custom_arg = custom_arg},
                            {.allow_in_incognito = allow_in_incognito});
  }

  ContextType GetContextType() { return std::get<0>(GetParam()); }

  bool AreSameSiteCookieSemanticsModern() {
    return std::get<1>(GetParam()) == SameSiteCookieSemantics::kModern;
  }

 private:
  mojo::Remote<network::mojom::CookieManager> cookie_manager_remote_;
};

// Android extension API tests only support service worker.
#if !BUILDFLAG(IS_ANDROID)
INSTANTIATE_TEST_SUITE_P(
    EventPage,
    CookiesApiTest,
    ::testing::Combine(::testing::Values(ContextType::kEventPage),
                       ::testing::Values(SameSiteCookieSemantics::kLegacy,
                                         SameSiteCookieSemantics::kModern)));
#endif

INSTANTIATE_TEST_SUITE_P(
    ServiceWorker,
    CookiesApiTest,
    ::testing::Combine(::testing::Values(ContextType::kServiceWorker),
                       ::testing::Values(SameSiteCookieSemantics::kLegacy,
                                         SameSiteCookieSemantics::kModern)));

// A test suite that only runs with MV3 extensions.
using CookiesApiMV3Test = CookiesApiTest;
INSTANTIATE_TEST_SUITE_P(
    ServiceWorker,
    CookiesApiMV3Test,
    ::testing::Combine(::testing::Values(ContextType::kServiceWorker),
                       ::testing::Values(SameSiteCookieSemantics::kLegacy,
                                         SameSiteCookieSemantics::kModern)));

// TODO(crbug.com/40839864): Flaky on Windows.
// TODO(crbug.com/371423073): Flaky on desktop Android.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
#define MAYBE_Cookies DISABLED_Cookies
#else
#define MAYBE_Cookies Cookies
#endif
IN_PROC_BROWSER_TEST_P(CookiesApiTest, MAYBE_Cookies) {
  ASSERT_TRUE(RunTest("cookies/api", /*allow_in_incognito=*/false,
                      AreSameSiteCookieSemanticsModern() ? "true" : "false"))
      << message_;
}

IN_PROC_BROWSER_TEST_P(CookiesApiTest, CookiesEvents) {
  ASSERT_TRUE(RunTest("cookies/events")) << message_;
}

IN_PROC_BROWSER_TEST_P(CookiesApiTest, CookiesNoPermission) {
  ASSERT_TRUE(RunTest("cookies/no_permission")) << message_;
}

IN_PROC_BROWSER_TEST_P(CookiesApiTest, CookiesEventsSpanningAsync) {
  // This version of the test creates the OTR page *after* the JavaScript test
  // code has registered the cookie listener. This tests the cookie API code
  // that listens for the new profile creation.
  //
  // The test sends us message with the string "listening" once it's registered
  // its listener. We force a reply to synchronize with the JS so the test
  // always runs the same way.
  ExtensionTestMessageListener listener("listening", ReplyBehavior::kWillReply);
  listener.SetOnSatisfied(
      base::BindLambdaForTesting([this, &listener](const std::string&) {
        PlatformOpenURLOffTheRecord(profile(), GURL("chrome://newtab/"));
        listener.Reply("ok");
      }));

  ASSERT_TRUE(RunTest("cookies/events_spanning",
                      /*allow_in_incognito=*/true))
      << message_;
}

IN_PROC_BROWSER_TEST_P(CookiesApiTest, CookiesEventsObservePrimaryOTROnly) {
  // In addition to above, this test makes sure that CookiesEventRouter
  // does not observe a non-primary OTR profile, which leads to CHECK
  // failure (crbug.com/6527130).
  ExtensionTestMessageListener listener("listening", ReplyBehavior::kWillReply);
  listener.SetOnSatisfied(
      base::BindLambdaForTesting([this, &listener](const std::string&) {
        PlatformOpenURLOffTheRecord(profile(), GURL("chrome://newtab/"));
        listener.Reply("ok");
      }));

  ASSERT_TRUE(RunTest("cookies/events_spanning",
                      /*allow_in_incognito=*/true))
      << message_;
  {
    auto* second_profile = profile()->GetOffTheRecordProfile(
        Profile::OTRProfileID::CreateUniqueForTesting(),
        /*create_if_needed=*/true);
    ProfileDestroyer::DestroyOTRProfileWhenAppropriate(second_profile);
  }
}

IN_PROC_BROWSER_TEST_P(CookiesApiTest, CookiesEventsSpanning) {
  // We need to initialize an incognito mode window in order have an initialized
  // incognito cookie store. Otherwise, the chrome.cookies.set operation is just
  // ignored and we won't be notified about a newly set cookie for which we want
  // to test whether the storeId is set correctly.
  PlatformOpenURLOffTheRecord(profile(), GURL("chrome://newtab/"));
  ASSERT_TRUE(RunTest("cookies/events_spanning",
                      /*allow_in_incognito=*/true))
      << message_;
}

IN_PROC_BROWSER_TEST_P(CookiesApiMV3Test, TestGetPartitionKey) {
  // Before running test, set up a top-level site (a.com) that embeds a
  // cross-site (b.com). To test the cookies.getPartitionKey() api.
  content::WebContents* contents = GetActiveWebContents();
  const std::string default_response = "/defaultresponse";
  ASSERT_TRUE(NavigateToURL(
      contents, embedded_test_server()->GetURL("a.com", default_response)));

  // Inject two iframes and navigate one to a cross-site with host permissions
  // (b.com) and the other to a cross-site (c.com) with no host permissions.
  const GURL cross_site_url =
      embedded_test_server()->GetURL("b.com", default_response);
  const GURL no_host_permissions_url =
      embedded_test_server()->GetURL("c.com", default_response);

  std::string script =
      "var f = document.createElement('iframe');\n"
      "f.src = '" +
      cross_site_url.spec() +
      "';\n"
      "document.body.appendChild(f);\n"
      "var noHostFrame = document.createElement('iframe');\n"
      "noHostFrame.src = '" +
      no_host_permissions_url.spec() +
      "';\n"
      "document.body.appendChild(noHostFrame);\n";

  EXPECT_TRUE(ExecJs(contents, script));
  EXPECT_TRUE(WaitForLoadStop(contents));
  ASSERT_TRUE(RunTest("cookies/get_partition_key")) << message_;
}

}  // namespace extensions