text:
using content::BrowserThread;
using content_settings::PageSpecificContentSettings;
using net::URLRequestMockHTTPJob;

namespace {

browsing_data::CannedCookieHelper* GetSiteSettingsCookieContainer(
    Browser* browser) {
  PageSpecificContentSettings* settings =
      PageSpecificContentSettings::GetForFrame(browser->tab_strip_model()
                                                   ->GetActiveWebContents()
                                                   ->GetPrimaryMainFrame());
  return settings->allowed_local_shared_objects().cookies();
}

browsing_data::CannedCookieHelper* GetSiteSettingsBlockedCookieContainer(
    Browser* browser) {
  PageSpecificContentSettings* settings =
      PageSpecificContentSettings::GetForFrame(browser->tab_strip_model()
                                                   ->GetActiveWebContents()
                                                   ->GetPrimaryMainFrame());
  return settings->blocked_local_shared_objects().cookies();
}

net::CookieList ExtractCookies(browsing_data::CannedCookieHelper* container) {
  bool got_result = false;
  net::CookieList result;
  container->StartFetching(
      base::BindLambdaForTesting([&](const net::CookieList& list) {
        result = list;
        got_result = true;
      }));
  CHECK(got_result);
  return result;
}

size_t GetRenderFrameHostCount(content::RenderFrameHost* starting_frame) {
  size_t count = 0;
  starting_frame->ForEachRenderFrameHost(
      [&](content::RenderFrameHost*) { ++count; });
  return count;
}

class CookieChangeObserver : public content::WebContentsObserver {
 public:
  explicit CookieChangeObserver(content::WebContents* web_contents)
      : content::WebContentsObserver(web_contents) {}
  ~CookieChangeObserver() override = default;

  void Wait() { run_loop_.Run(); }

  void OnCookiesAccessed(content::RenderFrameHost* render_frame_host,
                         const content::CookieAccessDetails& details) override {
    run_loop_.Quit();
  }

  void OnCookiesAccessed(content::NavigationHandle* navigation,
                         const content::CookieAccessDetails& details) override {
    run_loop_.Quit();
  }

 private:
  base::RunLoop run_loop_;
};

class MockWebContentsLoadFailObserver : public content::WebContentsObserver {
 public:
  explicit MockWebContentsLoadFailObserver(content::WebContents* web_contents)
      : content::WebContentsObserver(web_contents) {}
  ~MockWebContentsLoadFailObserver() override = default;

  MOCK_METHOD1(DidFinishNavigation,
               void(content::NavigationHandle* navigation_handle));
};

MATCHER(IsErrorTooManyRedirects, "") {
  return arg->GetNetErrorCode() == net::ERR_TOO_MANY_REDIRECTS;
}

}  // namespace

class ContentSettingsTest : public InProcessBrowserTest {
 public:
  ContentSettingsTest() {
    https_server_.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
  }

  void SetUpOnMainThread() override {
    InProcessBrowserTest::SetUpOnMainThread();
    host_resolver()->AddRule("*", "127.0.0.1");
  }

  net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
};

// Test the combination of different ways of accessing cookies --- JS, HTML,
// or the new async cookie-store API.
enum class CookieMode {
  kDocumentCookieJS,
  kHttp,
  kCookieStoreJS,
};

class CookieSettingsTest
    : public ContentSettingsTest,
      public testing::WithParamInterface<std::pair<CookieMode, CookieMode>> {
 public:
  CookieMode ReadMode() const { return GetParam().first; }
  CookieMode WriteMode() const { return GetParam().second; }

  void SetUpOnMainThread() override {
    RegisterDefaultHandlers(embedded_test_server());
    embedded_test_server()->RegisterRequestMonitor(base::BindRepeating(
        &CookieSettingsTest::MonitorRequest, base::Unretained(this)));
    RegisterDefaultHandlers(&https_server_);
    https_server_.RegisterRequestMonitor(base::BindRepeating(
        &CookieSettingsTest::MonitorRequest, base::Unretained(this)));
    ASSERT_TRUE(embedded_test_server()->Start());
    ASSERT_TRUE(https_server_.Start());

    host_resolver()->AddRule("*", "127.0.0.1");

    // CookieStore API is for https only.
    if (ReadMode() == CookieMode::kCookieStoreJS ||
        WriteMode() == CookieMode::kCookieStoreJS)
      set_secure_scheme();
  }

  void SetUpCommandLine(base::CommandLine* command_line) override {
    // TODO(fivedots): Remove this switch once Storage Foundation is enabled
    // by default.
    command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
                                    "StorageFoundationAPI");
    ContentSettingsTest::SetUpCommandLine(command_line);
  }

  void set_secure_scheme() { secure_scheme_ = true; }

  std::string ReadCookie(Browser* browser) {
    switch (ReadMode()) {
      case CookieMode::kDocumentCookieJS:
        return JSReadCookie(browser);
      case CookieMode::kHttp:
        return HttpReadCookie(browser);
      case CookieMode::kCookieStoreJS:
        return JSAsyncReadCookie(browser);
    }
  }

  void WriteCookie(Browser* browser) {
    switch (WriteMode()) {
      case CookieMode::kDocumentCookieJS:
        return JSWriteCookie(browser);
      case CookieMode::kHttp:
        return HttpWriteCookie(browser);
      case CookieMode::kCookieStoreJS:
        return JSAsyncWriteCookie(browser);
    }
  }

  // Check the cookie in an incognito window.
  void CookieCheckIncognitoWindow(bool cookies_enabled) {
    ASSERT_TRUE(ReadCookie(browser()).empty());

    Browser* incognito = CreateIncognitoBrowser();
    ASSERT_TRUE(ui_test_utils::NavigateToURL(incognito, GetPageURL()));
    ASSERT_TRUE(ReadCookie(incognito).empty());
    WriteCookie(incognito);
    ASSERT_EQ(cookies_enabled, !ReadCookie(incognito).empty());

    // Ensure incognito cookies don't leak to regular profile.
    ASSERT_TRUE(ReadCookie(browser()).empty());

    // Ensure cookies get wiped after last incognito window closes.
    CloseBrowserSynchronously(incognito);

    incognito = CreateIncognitoBrowser();
    ASSERT_TRUE(ui_test_utils::NavigateToURL(incognito, GetPageURL()));
    ASSERT_TRUE(ReadCookie(incognito).empty());
    CloseBrowserSynchronously(incognito);
  }

  void PreBasic() {
    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetPageURL()));
    ASSERT_TRUE(ReadCookie(browser()).empty());

    CookieCheckIncognitoWindow(true);

    WriteCookie(browser());
    ASSERT_FALSE(ReadCookie(browser()).empty());
  }

  void Basic() {
    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetPageURL()));
    ASSERT_FALSE(ReadCookie(browser()).empty());
  }

  GURL GetPageURL() { return GetServer()->GetURL("/simple.html"); }

  GURL GetSetCookieURL() {
    return GetServer()->GetURL("/set_cookie_header.html");
  }

  net::EmbeddedTestServer* GetOtherServer() {
    return secure_scheme_ ? embedded_test_server() : &https_server_;
  }

  net::EmbeddedTestServer* GetServer() {
    return secure_scheme_ ? &https_server_ : embedded_test_server();
  }

  // Read a cookie by fetching a url and checking what Cookie header (if any) it
  // saw.
  std::string HttpReadCookieWithURL(Browser* browser, const GURL& url) {
    {
      base::AutoLock auto_lock(cookies_seen_lock_);
      cookies_seen_.clear();
    }

    auto* network_context =
        browser->profile()->GetDefaultStoragePartition()->GetNetworkContext();
    content::LoadBasicRequest(network_context, url);

    {
      base::AutoLock auto_lock(cookies_seen_lock_);
      return cookies_seen_[url];
    }
  }

  // Set a cookie by visiting a page that has a Set-Cookie header.
  void HttpWriteCookieWithURL(Browser* browser, const GURL& url) {
    auto* frame = browser->tab_strip_model()
                      ->GetActiveWebContents()
                      ->GetPrimaryMainFrame();
    // Need to load via |frame| for the accessed/blocked cookies lists to be
    // updated properly.
    content::LoadBasicRequest(frame, url);
  }

 private:
  // Read a cookie via JavaScript.
  std::string JSReadCookie(Browser* browser) {
    std::string cookies;
    bool rv = content::ExecuteScriptAndExtractString(
        browser->tab_strip_model()->GetActiveWebContents(),
        "window.domAutomationController.send(document.cookie)", &cookies);
    CHECK(rv);
    return cookies;
  }

  // Read a cookie with JavaScript cookie-store API
  std::string JSAsyncReadCookie(Browser* browser) {
    return content::EvalJs(
               browser->tab_strip_model()->GetActiveWebContents(),
               "async function doGet() {"
               "  const cookies = await window.cookieStore.getAll();"
               "  let cookie_str = '';"
               "  for (const cookie of cookies)"
               "    cookie_str += `${cookie.name}=${cookie.value};`;"
               "  window.domAutomationController.send(cookie_str);"
               "}"
               "doGet()",
               content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
        .ExtractString();
  }

  // Read a cookie by fetching the page url (which we should have just navigated
  // to) and checking what Cookie header (if any) it saw.
  std::string HttpReadCookie(Browser* browser) {
    GURL url = browser->tab_strip_model()
                   ->GetActiveWebContents()
                   ->GetLastCommittedURL();
    EXPECT_EQ(GetPageURL(), url);
    return HttpReadCookieWithURL(browser, url);
  }

  // Set a cookie with JavaScript.
  void JSWriteCookie(Browser* browser) {
    bool rv = content::ExecuteScript(
        browser->tab_strip_model()->GetActiveWebContents(),
        "document.cookie = 'name=Good;Max-Age=3600'");
    CHECK(rv);
  }

  // Set a cookie with JavaScript cookie-store api.
  void JSAsyncWriteCookie(Browser* browser) {
    content::EvalJsResult result =
        content::EvalJs(browser->tab_strip_model()->GetActiveWebContents(),
                        "async function doSet() {"
                        "  await window.cookieStore.set("
                        "       { name: 'name',"
                        "         value: 'Good',"
                        "         expires: Date.now() + 3600*1000,"
                        "         sameSite: 'none' });"
                        "  window.domAutomationController.send(true);"
                        "}"
                        "doSet()",
                        content::EXECUTE_SCRIPT_USE_MANUAL_REPLY);
    // Failure ignored here since some tests purposefully try to set disallowed
    // cookies.
  }

  // Set a cookie by visiting a page that has a Set-Cookie header.
  void HttpWriteCookie(Browser* browser) {
    HttpWriteCookieWithURL(browser, GetSetCookieURL());
  }

  void MonitorRequest(const net::test_server::HttpRequest& request) {
    base::AutoLock auto_lock(cookies_seen_lock_);
    auto it = request.headers.find("Cookie");
    if (it != request.headers.end())
      cookies_seen_[request.GetURL()] = it->second;
  }

  bool secure_scheme_ = false;
  base::Lock cookies_seen_lock_;
  std::map<GURL, std::string> cookies_seen_;
};

// Sanity check on cookies before we do other tests. While these can be written
// in content_browsertests, we want to verify Chrome's cookie storage and how it
// handles incognito windows.
IN_PROC_BROWSER_TEST_P(CookieSettingsTest, PRE_BasicCookies) {
  PreBasic();
}

IN_PROC_BROWSER_TEST_P(CookieSettingsTest, BasicCookies) {
  Basic();
}

IN_PROC_BROWSER_TEST_P(CookieSettingsTest, PRE_BasicCookiesHttps) {
  set_secure_scheme();
  PreBasic();
}

IN_PROC_BROWSER_TEST_P(CookieSettingsTest, BasicCookiesHttps) {
  set_secure_scheme();
  Basic();
}

// Verify that cookies are being blocked.
IN_PROC_BROWSER_TEST_P(CookieSettingsTest, PRE_BlockCookies) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetPageURL()));
  CookieSettingsFactory::GetForProfile(browser()->profile())
      ->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);
  WriteCookie(browser());
  ASSERT_TRUE(ReadCookie(browser()).empty());
  CookieCheckIncognitoWindow(false);
}

// Ensure that the setting persists.
IN_PROC_BROWSER_TEST_P(CookieSettingsTest, BlockCookies) {
  ASSERT_EQ(CONTENT_SETTING_BLOCK,
            CookieSettingsFactory::GetForProfile(browser()->profile())
                ->GetDefaultCookieSetting(nullptr));
}

// Verify that cookies can be allowed and set using exceptions for particular
// website(s) when all others are blocked.
// Flaky on Mac (crbug.com/1155077) and Linux (crbug.com/1242410).
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
#define MAYBE_AllowCookiesUsingExceptions DISABLED_AllowCookiesUsingExceptions
#else
#define MAYBE_AllowCookiesUsingExceptions AllowCookiesUsingExceptions
#endif
IN_PROC_BROWSER_TEST_P(CookieSettingsTest, MAYBE_AllowCookiesUsingExceptions) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetPageURL()));
  content_settings::CookieSettings* settings =
      CookieSettingsFactory::GetForProfile(browser()->profile()).get();
  settings->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);

  WriteCookie(browser());
  ASSERT_TRUE(ReadCookie(browser()).empty());

  browsing_data::CannedCookieHelper* accepted =
      GetSiteSettingsCookieContainer(browser());
  browsing_data::CannedCookieHelper* blocked =
      GetSiteSettingsBlockedCookieContainer(browser());
  EXPECT_TRUE(accepted->empty());
  ASSERT_EQ(1u, blocked->GetCookieCount());
  net::CookieList blocked_cookies = ExtractCookies(blocked);
  EXPECT_THAT(blocked_cookies, net::MatchesCookieLine("name=Good"));

  settings->SetCookieSetting(GetPageURL(), CONTENT_SETTING_ALLOW);

  WriteCookie(browser());
  ASSERT_FALSE(ReadCookie(browser()).empty());
  accepted = GetSiteSettingsCookieContainer(browser());
  blocked = GetSiteSettingsBlockedCookieContainer(browser());

  ASSERT_EQ(1u, accepted->GetCookieCount());
  net::CookieList accepted_cookies = ExtractCookies(accepted);
  EXPECT_THAT(accepted_cookies, net::MatchesCookieLine("name=Good"));

  // No navigation, so there should still be one blocked cookie.
  EXPECT_EQ(1u, blocked->GetCookieCount());
}

// Verify that cookies can be blocked for a specific website using exceptions.
//
// TODO(https://crbug.com/931080): Re-enable test once flakiness is fixed.
IN_PROC_BROWSER_TEST_P(CookieSettingsTest,
                       DISABLED_BlockCookiesUsingExceptions) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetPageURL()));
  content_settings::CookieSettings* settings =
      CookieSettingsFactory::GetForProfile(browser()->profile()).get();
  settings->SetCookieSetting(GetPageURL(), CONTENT_SETTING_BLOCK);

  WriteCookie(browser());
  ASSERT_TRUE(ReadCookie(browser()).empty());
  browsing_data::CannedCookieHelper* accepted =
      GetSiteSettingsCookieContainer(browser());
  browsing_data::CannedCookieHelper* blocked =
      GetSiteSettingsBlockedCookieContainer(browser());
  EXPECT_TRUE(accepted->empty());
  ASSERT_EQ(1u, blocked->GetCookieCount());
  net::CookieList blocked_cookies = ExtractCookies(blocked);
  EXPECT_THAT(blocked_cookies, net::MatchesCookieLine("name=Good"));

  GURL unblocked_url = GetOtherServer()->GetURL("/cookie1.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), unblocked_url));
  ASSERT_FALSE(GetCookies(browser()->profile(), unblocked_url).empty());
  accepted = GetSiteSettingsCookieContainer(browser());
  blocked = GetSiteSettingsBlockedCookieContainer(browser());

  ASSERT_EQ(1u, accepted->GetCookieCount());
  net::CookieList accepted_cookies = ExtractCookies(accepted);
  EXPECT_THAT(accepted_cookies, net::MatchesCookieLine("foo=baz"));
  EXPECT_TRUE(blocked->empty());
}

// Test that cookies that are considered "blocked" are excluded only due to the
// content settings blocking (i.e. not for other reasons like domain or path not
// matching). See https://crbug.com/1104451.
IN_PROC_BROWSER_TEST_P(CookieSettingsTest,
                       BlockedCookiesOnlyExcludedDueToBlocking) {
  // This test only runs in HTTP mode, not with the full parameterized test
  // suite.
  if (ReadMode() != CookieMode::kHttp || WriteMode() != CookieMode::kHttp)
    return;

  // URLs to get cookies from:
  GURL cookies_present_url(GetServer()->GetURL("a.test", "/simple.html"));
  GURL cookies_blocked_url(
      GetServer()->GetURL("sub.a.test", "/echoheader?Cookie"));

  // URLs to set cookies on:
  GURL set_host_cookie_url(
      GetServer()->GetURL("a.test", "/set-cookie?host_cookie=1"));
  GURL set_path_cookie_url(GetServer()->GetURL(
      "sub.a.test",
      "/set-cookie?path_cookie=1;domain=a.test;path=/simple.html"));
  GURL set_included_cookie_url(GetServer()->GetURL(
      "sub.a.test", "/set-cookie?included_cookie=1;domain=a.test"));

  // No cookies are present prior to setting them.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), cookies_present_url));
  ASSERT_TRUE(HttpReadCookieWithURL(browser(), cookies_present_url).empty());
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), cookies_blocked_url));
  ASSERT_TRUE(HttpReadCookieWithURL(browser(), cookies_blocked_url).empty());

  // Set the cookies.
  HttpWriteCookieWithURL(browser(), set_host_cookie_url);
  HttpWriteCookieWithURL(browser(), set_path_cookie_url);
  HttpWriteCookieWithURL(browser(), set_included_cookie_url);

  // Verify all cookies are present on |cookies_present_url|.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), cookies_present_url));
  HttpReadCookieWithURL(browser(), cookies_present_url);
  browsing_data::CannedCookieHelper* accepted =
      GetSiteSettingsCookieContainer(browser());
  browsing_data::CannedCookieHelper* blocked =
      GetSiteSettingsBlockedCookieContainer(browser());
  EXPECT_EQ(3u, accepted->GetCookieCount());
  EXPECT_TRUE(blocked->empty());
  net::CookieList accepted_cookies = ExtractCookies(accepted);
  EXPECT_THAT(accepted_cookies,
              net::MatchesCookieLine(
                  "included_cookie=1; host_cookie=1; path_cookie=1"));

  // Verify there is only one included cookie for |cookies_blocked_url|.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), cookies_blocked_url));
  HttpReadCookieWithURL(browser(), cookies_blocked_url);
  accepted = GetSiteSettingsCookieContainer(browser());
  blocked = GetSiteSettingsBlockedCookieContainer(browser());
  EXPECT_EQ(1u, accepted->GetCookieCount());
  EXPECT_TRUE(blocked->empty());
  accepted_cookies = ExtractCookies(accepted);
  EXPECT_THAT(accepted_cookies, net::MatchesCookieLine("included_cookie=1"));

  // Set content settings to block cookies for |cookies_blocked_url|.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), cookies_blocked_url));
  content_settings::CookieSettings* settings =
      CookieSettingsFactory::GetForProfile(browser()->profile()).get();
  settings->SetCookieSetting(cookies_blocked_url, CONTENT_SETTING_BLOCK);

  // Verify all cookies are still present on |cookies_present_url|.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), cookies_present_url));
  HttpReadCookieWithURL(browser(), cookies_present_url);
  accepted = GetSiteSettingsCookieContainer(browser());
  blocked = GetSiteSettingsBlockedCookieContainer(browser());
  EXPECT_EQ(3u, accepted->GetCookieCount());
  EXPECT_TRUE(blocked->empty());
  accepted_cookies = ExtractCookies(accepted);
  EXPECT_THAT(accepted_cookies,
              net::MatchesCookieLine(
                  "included_cookie=1; host_cookie=1; path_cookie=1"));

  // Verify there is only one blocked cookie on |cookies_blocked_url|.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), cookies_blocked_url));
  HttpReadCookieWithURL(browser(), cookies_blocked_url);
  accepted = GetSiteSettingsCookieContainer(browser());
  blocked = GetSiteSettingsBlockedCookieContainer(browser());
  EXPECT_TRUE(accepted->empty());
  EXPECT_EQ(1u, blocked->GetCookieCount());
  net::CookieList blocked_cookies = ExtractCookies(blocked);
  EXPECT_THAT(blocked_cookies, net::MatchesCookieLine("included_cookie=1"));
}

IN_PROC_BROWSER_TEST_P(CookieSettingsTest, BlockCookiesAlsoBlocksCacheStorage) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetPageURL()));
  content_settings::CookieSettings* settings =
      CookieSettingsFactory::GetForProfile(browser()->profile()).get();
  settings->SetCookieSetting(GetPageURL(), CONTENT_SETTING_BLOCK);

  const char kBaseExpected[] =
      "%s - SecurityError: An attempt was made to break through the security "
      "policy of the user agent.";

  const char kBaseScript[] =
      "(async function() {"
      "  const name = `%s`;"
      "  try {"
      "    await %s;"
      "  } catch(e) {"
      "    return `${name} - ${e.toString()}`;"
      "  }"
      "  return `${name} - success`;"
      "}())";

  const std::vector<std::string> kTestOps({
      "caches.open('foo')",
      "caches.has('foo')",
      "caches.keys()",
      "caches.delete('foo')",
      "caches.match('/')",
  });

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

  for (auto& op : kTestOps) {
    EXPECT_EQ(
        base::StringPrintf(kBaseExpected, op.data()),
        EvalJs(tab, base::StringPrintf(kBaseScript, op.data(), op.data())));
  }
}

IN_PROC_BROWSER_TEST_P(CookieSettingsTest, BlockCookiesAlsoBlocksIndexedDB) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetPageURL()));
  content_settings::CookieSettings* settings =
      CookieSettingsFactory::GetForProfile(browser()->profile()).get();
  settings->SetCookieSetting(GetPageURL(), CONTENT_SETTING_BLOCK);

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

  const char kBaseScript[] =
      "(async function() {"
      "  const name = `%s`;"
      "  function wrap(req) {"
      "    return new Promise((resolve, reject) => {"
      "      req.onerror = function() { reject(req.error); };"
      "      req.onsuccess = function() { resolve(); };"
      "    });"
      "  }"
      "  try {"
      "    let promiselike = indexedDB.%s%s;"
      "    if (typeof promiselike.then !== 'undefined') {"
      "      await promiselike;"
      "    }"
      "    await wrap(promiselike);"
      "  } catch(e) {"
      "    return `${name} - ${e.toString()}`;"
      "  }"
      "  return `${name} - success`;"
      "}())";

  struct TestOp {
    const char* cmd;
    const char* args;
  };

  const TestOp kTestOps[] = {
      {.cmd = "open", .args = "('foo', 1)"},
      {.cmd = "deleteDatabase", .args = "('foo')"},
  };

  const char kBaseExpected[] =
      "%s - UnknownError: The user denied permission to access the database.";

  for (auto& op : kTestOps) {
    EXPECT_EQ(
        base::StringPrintf(kBaseExpected, op.cmd),
        EvalJs(tab, base::StringPrintf(kBaseScript, op.cmd, op.cmd, op.args)));
  }

  const TestOp kPromiseTestOps[] = {
      {.cmd = "databases", .args = "()"},
  };

  const char kPromiseBaseExpected[] =
      "%s - UnknownError: Failed to execute '%s' on 'IDBFactory': The user "
      "denied permission to access the database.";

  for (auto& op : kPromiseTestOps) {
    EXPECT_EQ(
        base::StringPrintf(kPromiseBaseExpected, op.cmd, op.cmd),
        EvalJs(tab, base::StringPrintf(kBaseScript, op.cmd, op.cmd, op.args)));
  }
}

IN_PROC_BROWSER_TEST_P(CookieSettingsTest, BlockCookiesAlsoBlocksFileSystem) {
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetPageURL()));
  content_settings::CookieSettings* settings =
      CookieSettingsFactory::GetForProfile(browser()->profile()).get();
  settings->SetCookieSetting(GetPageURL(), CONTENT_SETTING_BLOCK);

  const char kBaseExpected[] = "%s - %s";

  const char kBaseScript[] = R"(
      (async function() {
        const name = `%s`;
        try {
          await %s;
        } catch(e) {
          return `${name} - ${e.toString()}`;
        }
        return `${name} - success`;
      }())
  )";

  struct TestOp {
    const char* name;
    const char* code;
    const char* error;
  };

  const TestOp kTestOps[] = {
      {.name = "navigator.storage.getDirectory()",
       .code = "navigator.storage.getDirectory()",
       .error = "SecurityError: Storage directory access is denied."},
      {.name = "window.webkitRequestFileSystem()",
       .code = "new Promise((resolve, reject) => { "
               " window.webkitRequestFileSystem(window.TEMPORARY,"
               " 5*1024, () => resolve(),"
               " (e) => reject(e));"
               "});",
       .error = "AbortError: An ongoing operation was aborted, typically with "
                "a call to abort()."},
  };

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

  for (auto& op : kTestOps) {
    EXPECT_EQ(base::StringPrintf(kBaseExpected, op.name, op.error),
              EvalJs(tab, base::StringPrintf(kBaseScript, op.name, op.code)));
  }
}

IN_PROC_BROWSER_TEST_P(CookieSettingsTest,
                       BlockCookiesAlsoBlocksStorageFoundation) {
  set_secure_scheme();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetPageURL()));
  content_settings::CookieSettings* settings =
      CookieSettingsFactory::GetForProfile(browser()->profile()).get();
  settings->SetCookieSetting(GetPageURL(), CONTENT_SETTING_BLOCK);

  const char kBaseExpected[] = "%s - Storage access is denied";

  const char kBaseScript[] = R"(
      (async function() {
        const name = `%s`;
        try {
          await %s;
        } catch(e) {
          const error = e.toString();
          const n = error.lastIndexOf(`: `);
          const message = error.substring(n + 2)
          return `${name} - ${message}`;
        }
        return `${name} - success`;
      }())
  )";

  struct TestOp {
    const char* name;
    const char* code;
  };

  const TestOp kTestOps[] = {
      {.name = "storageFoundation.open()",
       .code = "storageFoundation.open('foo')"},
      {.name = "storageFoundation.delete()",
       .code = "storageFoundation.delete('foo')"},
      {.name = "storageFoundation.rename()",
       .code = "storageFoundation.rename('foo', 'bar')"},
      {.name = "storageFoundation.getAll()",
       .code = "storageFoundation.getAll()"},
      {.name = "storageFoundation.requestCapacity()",
       .code = "storageFoundation.requestCapacity(10)"},
      {.name = "storageFoundation.releaseCapacity()",
       .code = "storageFoundation.releaseCapacity(10)"},
      {.name = "storageFoundation.getRemainingCapacity()",
       .code = "storageFoundation.getRemainingCapacity()"},
  };

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

  for (auto& op : kTestOps) {
    EXPECT_EQ(base::StringPrintf(kBaseExpected, op.name),
              EvalJs(tab, base::StringPrintf(kBaseScript, op.name, op.code)));
  }
}

IN_PROC_BROWSER_TEST_P(CookieSettingsTest,
                       BlockCookiesAlsoBlocksSyncStorageFoundation) {
  set_secure_scheme();
  GURL url = GetServer()->GetURL("/sync_storage_foundation.html");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  content_settings::CookieSettings* settings =
      CookieSettingsFactory::GetForProfile(browser()->profile()).get();
  settings->SetCookieSetting(GetPageURL(), CONTENT_SETTING_BLOCK);

  const char kBaseExpected[] = "%s - Storage access is denied";
  const char kBaseUnexpected[] = "%s - Success";
  const char kBaseCall[] = "run('%s')";

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

  const char* kTestOps[] = {
      "openSync",
      "deleteSync",
      "renameSync",
      "getAllSync",
      "requestCapacitySync",
      "releaseCapacitySync",
      "getRemainingCapacitySync",
  };

  for (auto* op : kTestOps) {
    EXPECT_TRUE(ExecJs(tab, base::StringPrintf(kBaseCall, op)));

    std::u16string expected_title(
        base::ASCIIToUTF16(base::StringPrintf(kBaseExpected, op)));
    content::TitleWatcher title_watcher(tab, expected_title);

    std::u16string unexpected_title(
        base::ASCIIToUTF16(base::StringPrintf(kBaseUnexpected, op)));
    title_watcher.AlsoWaitForTitle(unexpected_title);

    EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
  }
}

INSTANTIATE_TEST_SUITE_P(
    All,
    CookieSettingsTest,
    ::testing::Values(
        std::make_pair(CookieMode::kDocumentCookieJS,
                       CookieMode::kDocumentCookieJS),
        std::make_pair(CookieMode::kDocumentCookieJS, CookieMode::kHttp),
        std::make_pair(CookieMode::kHttp, CookieMode::kDocumentCookieJS),
        std::make_pair(CookieMode::kHttp, CookieMode::kHttp),
        std::make_pair(CookieMode::kHttp, CookieMode::kCookieStoreJS),
        std::make_pair(CookieMode::kCookieStoreJS,
                       CookieMode::kDocumentCookieJS)));

// This fails on ChromeOS because kRestoreOnStartup is ignored and the startup
// preference is always "continue where I left off.
#if !BUILDFLAG(IS_CHROMEOS_ASH)

// Verify that cookies can be allowed and set using exceptions for particular
// website(s) only for a session when all others are blocked.
IN_PROC_BROWSER_TEST_F(ContentSettingsTest,
                       PRE_AllowCookiesForASessionUsingExceptions) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url = embedded_test_server()->GetURL("/setcookie.html");
  content_settings::CookieSettings* settings =
      CookieSettingsFactory::GetForProfile(browser()->profile()).get();
  settings->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  ASSERT_TRUE(GetCookies(browser()->profile(), url).empty());

  settings->SetCookieSetting(url, CONTENT_SETTING_SESSION_ONLY);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  ASSERT_FALSE(GetCookies(browser()->profile(), url).empty());
}

IN_PROC_BROWSER_TEST_F(ContentSettingsTest,
                       AllowCookiesForASessionUsingExceptions) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url = embedded_test_server()->GetURL("/setcookie.html");
  // Cookies are shared between ports, so this will get cookies set in PRE.
  ASSERT_TRUE(GetCookies(browser()->profile(), url).empty());
}

#endif  // !CHROME_OS

// Regression test for http://crbug.com/63649.
IN_PROC_BROWSER_TEST_F(ContentSettingsTest, RedirectLoopCookies) {
  ASSERT_TRUE(embedded_test_server()->Start());

  GURL test_url = embedded_test_server()->GetURL("/redirect-loop.html");

  CookieSettingsFactory::GetForProfile(browser()->profile())
      ->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  MockWebContentsLoadFailObserver observer(web_contents);
  EXPECT_CALL(observer, DidFinishNavigation(IsErrorTooManyRedirects()));

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url));

  ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(&observer));

  EXPECT_TRUE(PageSpecificContentSettings::GetForFrame(
                  web_contents->GetPrimaryMainFrame())
                  ->IsContentBlocked(ContentSettingsType::COOKIES));
}

// Any cookie access during a navigation does not end up in a new document (e.g.
// due to the request returning HTTP 204) should not be tracked by the
// PageSpecificContentSettings.
IN_PROC_BROWSER_TEST_F(ContentSettingsTest, CookiesIgnoredFor204) {
  ASSERT_TRUE(embedded_test_server()->Start());

  GURL test_url =
      embedded_test_server()->GetURL("/server-redirect-with-cookie?/nocontent");

  CookieSettingsFactory::GetForProfile(browser()->profile())
      ->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);

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

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url));

  EXPECT_FALSE(PageSpecificContentSettings::GetForFrame(
                   web_contents->GetPrimaryMainFrame())
                   ->IsContentBlocked(ContentSettingsType::COOKIES));
}

class ContentSettingsBackForwardCacheBrowserTest : public ContentSettingsTest {
 public:
  void SetUpOnMainThread() override {
    host_resolver()->AddRule("*", "127.0.0.1");
    ContentSettingsTest ::SetUpOnMainThread();
  }

  void SetUpCommandLine(base::CommandLine* command_line) override {
    scoped_feature_list_.InitWithFeaturesAndParameters(
        {{features::kBackForwardCache,
          {// Set a very long TTL before expiration (longer than the test
           // timeout) so tests that are expecting deletion don't pass when
           // they shouldn't.
           {"TimeToLiveInBackForwardCacheInSeconds", "3600"},
           {"ignore_outstanding_network_request_for_testing", "true"}}}},
        // Allow BackForwardCache for all devices regardless of their memory.
        {features::kBackForwardCacheMemoryControls});
    ContentSettingsTest::SetUpCommandLine(command_line);
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_F(ContentSettingsBackForwardCacheBrowserTest,
                       StateRestoredWhenNavigatingBack) {
  ASSERT_TRUE(embedded_test_server()->Start());

  GURL test_url = embedded_test_server()->GetURL("a.com", "/setcookie.html");
  GURL other_url = embedded_test_server()->GetURL("b.com", "/title1.html");

  CookieSettingsFactory::GetForProfile(browser()->profile())
      ->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url));

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  content::RenderFrameHost* main_frame = web_contents->GetPrimaryMainFrame();

  EXPECT_TRUE(PageSpecificContentSettings::GetForFrame(
                  web_contents->GetPrimaryMainFrame())
                  ->IsContentBlocked(ContentSettingsType::COOKIES));

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), other_url));
  EXPECT_EQ(main_frame->GetLifecycleState(),
            content::RenderFrameHost::LifecycleState::kInBackForwardCache);
  EXPECT_FALSE(PageSpecificContentSettings::GetForFrame(
                   web_contents->GetPrimaryMainFrame())
                   ->IsContentBlocked(ContentSettingsType::COOKIES));

  web_contents->GetController().GoBack();
  EXPECT_TRUE(WaitForLoadStop(web_contents));
  EXPECT_EQ(main_frame, web_contents->GetPrimaryMainFrame());
  EXPECT_TRUE(PageSpecificContentSettings::GetForFrame(
                  web_contents->GetPrimaryMainFrame())
                  ->IsContentBlocked(ContentSettingsType::COOKIES));
}

IN_PROC_BROWSER_TEST_F(ContentSettingsBackForwardCacheBrowserTest,
                       SettingsUpdateWhileInCacheShouldBeProcessed) {
  ASSERT_TRUE(embedded_test_server()->Start());

  GURL test_url = embedded_test_server()->GetURL("a.com", "/setcookie.html");
  GURL other_url = embedded_test_server()->GetURL("b.com", "/title1.html");
  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();

  CookieSettingsFactory::GetForProfile(browser()->profile())
      ->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url));
  EXPECT_TRUE(PageSpecificContentSettings::GetForFrame(
                  web_contents->GetPrimaryMainFrame())
                  ->IsContentBlocked(ContentSettingsType::COOKIES));

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), other_url));
  EXPECT_FALSE(PageSpecificContentSettings::GetForFrame(
                   web_contents->GetPrimaryMainFrame())
                   ->IsContentBlocked(ContentSettingsType::COOKIES));

  // This triggers a OnContentSettingChanged notification that should be
  // processed by the page in the cache.
  CookieSettingsFactory::GetForProfile(browser()->profile())
      ->SetDefaultCookieSetting(CONTENT_SETTING_ALLOW);

  web_contents->GetController().GoBack();
  EXPECT_TRUE(WaitForLoadStop(web_contents));
  EXPECT_FALSE(PageSpecificContentSettings::GetForFrame(
                   web_contents->GetPrimaryMainFrame())
                   ->IsContentBlocked(ContentSettingsType::COOKIES));
}

// This test verifies that the site settings accurately reflect that an attempt
// to create a secure cookie by an insecure origin fails.
IN_PROC_BROWSER_TEST_F(ContentSettingsTest, SecureCookies) {
  ASSERT_TRUE(embedded_test_server()->Start());

  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
  https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
  https_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
  ASSERT_TRUE(https_server.Start());

  GURL http_url =
      embedded_test_server()->GetURL("a.test", "/setsecurecookie.html");
  GURL https_url = https_server.GetURL("a.test", "/setsecurecookie.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), http_url));
  EXPECT_TRUE(GetSiteSettingsCookieContainer(browser())->empty());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), https_url));
  EXPECT_FALSE(GetSiteSettingsCookieContainer(browser())->empty());
}

IN_PROC_BROWSER_TEST_F(ContentSettingsTest, ContentSettingsBlockDataURLs) {
  GURL url("data:text/html,<title>Data URL</title><script>alert(1)</script>");

  HostContentSettingsMapFactory::GetForProfile(browser()->profile())
      ->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT,
                                 CONTENT_SETTING_BLOCK);

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  ASSERT_EQ(u"Data URL", web_contents->GetTitle());

  EXPECT_TRUE(PageSpecificContentSettings::GetForFrame(
                  web_contents->GetPrimaryMainFrame())
                  ->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
}

// Tests that if redirect across origins occurs, the new process still gets the
// content settings before the resource headers.
IN_PROC_BROWSER_TEST_F(ContentSettingsTest, RedirectCrossOrigin) {
  ASSERT_TRUE(embedded_test_server()->Start());

  net::HostPortPair host_port = embedded_test_server()->host_port_pair();
  DCHECK_EQ(host_port.host(), std::string("127.0.0.1"));

  std::string redirect(base::StringPrintf(
      "http://localhost:%d/redirect-cross-origin.html", host_port.port()));
  GURL test_url =
      embedded_test_server()->GetURL("/server-redirect?" + redirect);

  CookieSettingsFactory::GetForProfile(browser()->profile())
      ->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url));

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

  EXPECT_TRUE(PageSpecificContentSettings::GetForFrame(
                  web_contents->GetPrimaryMainFrame())
                  ->IsContentBlocked(ContentSettingsType::COOKIES));
}

IN_PROC_BROWSER_TEST_F(ContentSettingsTest, SendRendererContentRules) {
  ASSERT_TRUE(embedded_test_server()->Start());
  const GURL url_1 = embedded_test_server()->GetURL("a.com", "/title1.html");
  const GURL url_2 =
      embedded_test_server()->GetURL("b.com", "/javaScriptTitle.html");
  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_1));
  HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(
      Profile::FromBrowserContext(web_contents->GetBrowserContext()));
  EXPECT_NE(map, nullptr);
  EXPECT_FALSE(PageSpecificContentSettings::GetForFrame(
                   web_contents->GetPrimaryMainFrame())
                   ->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
  map->SetContentSettingDefaultScope(url_2, url_2,
                                     ContentSettingsType::JAVASCRIPT,
                                     ContentSetting::CONTENT_SETTING_BLOCK);
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_2));
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(PageSpecificContentSettings::GetForFrame(
                  web_contents->GetPrimaryMainFrame())
                  ->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
}

IN_PROC_BROWSER_TEST_F(ContentSettingsTest,
                       SpareRenderProcessHostRulesAreUpdated) {
  // Make sure a spare RenderProcessHost exists during the test.
  content::RenderProcessHost::WarmupSpareRenderProcessHost(
      browser()->profile());

  ASSERT_TRUE(embedded_test_server()->Start());
  // URL to a page that loads a cross-site iframe which creates another iframe
  // via JavaScript. We will count iframes to test if JavaScript is blocked or
  // not.
  const GURL url = embedded_test_server()->GetURL(
      "a.test", "/iframe_cross_site_with_script.html");

  // Disable JavaScript. A warmed-up spare renderer should get ContentSettings
  // updates and disable JavaScript.
  HostContentSettingsMapFactory::GetForProfile(browser()->profile())
      ->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT,
                                 CONTENT_SETTING_BLOCK);
  // Navigate to the page.
  content::RenderFrameHost* main_frame =
      ui_test_utils::NavigateToURL(browser(), url);
  ASSERT_TRUE(main_frame);
  // Ensure 2 frames exist after the load (main frame and 'b.test' frame).
  EXPECT_EQ(2u, GetRenderFrameHostCount(main_frame));
}

IN_PROC_BROWSER_TEST_F(ContentSettingsTest, NonMainFrameRulesAreUpdated) {
  ASSERT_TRUE(embedded_test_server()->Start());
  // URL to a page that loads a cross-site iframe which creates another iframe
  // via JavaScript. We will count iframes to test if JavaScript is blocked or
  // not.
  const GURL url = embedded_test_server()->GetURL(
      "a.test", "/iframe_cross_site_with_script.html");

  // Disable JavaScript.
  HostContentSettingsMapFactory::GetForProfile(browser()->profile())
      ->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT,
                                 CONTENT_SETTING_BLOCK);
  // Navigate to the page.
  content::RenderFrameHost* main_frame =
      ui_test_utils::NavigateToURL(browser(), url);
  ASSERT_TRUE(main_frame);
  // Ensure 2 frames exist after the load (main frame and 'b.test' frame).
  EXPECT_EQ(2u, GetRenderFrameHostCount(main_frame));

  // Enable JavaScript and load the same page.
  HostContentSettingsMapFactory::GetForProfile(browser()->profile())
      ->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT,
                                 CONTENT_SETTING_DEFAULT);
  main_frame = ui_test_utils::NavigateToURLWithDisposition(
      browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
  ASSERT_TRUE(main_frame);
  // Ensure 3 frames exist after the load (main frame, 'b.test' frame and
  // JavaScript-created 'b.test' nested frame).
  EXPECT_EQ(3u, GetRenderFrameHostCount(main_frame));

  // Disable JavaScript and reload the iframe which contains JavaScript.
  HostContentSettingsMapFactory::GetForProfile(browser()->profile())
      ->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT,
                                 CONTENT_SETTING_BLOCK);
  content::RenderFrameHost* iframe_to_reload =
      content::ChildFrameAt(main_frame, 0);
  content::TestFrameNavigationObserver iframe_nav_observer(iframe_to_reload);
  iframe_to_reload->Reload();
  iframe_nav_observer.Wait();

  // Ensure 2 frames exist after iframe reload (main frame and 'b.test' frame).
  EXPECT_EQ(2u, GetRenderFrameHostCount(main_frame));
}

// Simulates script being blocked in the renderer and notifying the browser
// before DidCommitNavigation is sent to the browser (i.e. while the RFH is
// still pending commit).
IN_PROC_BROWSER_TEST_F(ContentSettingsTest, RendererUpdateWhilePendingCommit) {
  ASSERT_TRUE(embedded_test_server()->Start());

  const GURL initial_url =
      embedded_test_server()->GetURL("a.test", "/title1.html");
  const GURL second_url =
      embedded_test_server()->GetURL("b.test", "/title1.html");

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));

  content::CommitMessageDelayer delayer(
      web_contents, second_url,
      base::BindOnce([](content::RenderFrameHost* rfh) {
        auto global_id = rfh->GetGlobalId();
        // Call ContentBlocked while the RFH is pending commit.
        PageSpecificContentSettings::ContentBlocked(
            global_id.child_id, global_id.frame_routing_id,
            ContentSettingsType::JAVASCRIPT);
      }));
  ui_test_utils::NavigateToURLWithDisposition(
      browser(), second_url, WindowOpenDisposition::CURRENT_TAB,
      ui_test_utils::BROWSER_TEST_NONE);
  delayer.Wait();

  EXPECT_TRUE(PageSpecificContentSettings::GetForFrame(
                  web_contents->GetPrimaryMainFrame())
                  ->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
}

class ContentSettingsWorkerModulesBrowserTest : public ContentSettingsTest {
 public:
  ContentSettingsWorkerModulesBrowserTest() = default;

  ContentSettingsWorkerModulesBrowserTest(
      const ContentSettingsWorkerModulesBrowserTest&) = delete;
  ContentSettingsWorkerModulesBrowserTest& operator=(
      const ContentSettingsWorkerModulesBrowserTest&) = delete;

  ~ContentSettingsWorkerModulesBrowserTest() override = default;

 protected:
  void RegisterStaticFile(net::EmbeddedTestServer* server,
                          const std::string& relative_url,
                          const std::string& content,
                          const std::string& content_type) {
    server->RegisterRequestHandler(base::BindRepeating(
        &ContentSettingsWorkerModulesBrowserTest::StaticRequestHandler,
        base::Unretained(this), relative_url, content, content_type));
  }

 private:
  std::unique_ptr<net::test_server::HttpResponse> StaticRequestHandler(
      const std::string& relative_url,
      const std::string& content,
      const std::string& content_type,
      const net::test_server::HttpRequest& request) const {
    if (request.relative_url != relative_url)
      return nullptr;
    std::unique_ptr<net::test_server::BasicHttpResponse> http_response(
        std::make_unique<net::test_server::BasicHttpResponse>());
    http_response->set_code(net::HTTP_OK);
    http_response->set_content(content);
    http_response->set_content_type(content_type);
    return std::move(http_response);
  }
};

IN_PROC_BROWSER_TEST_F(ContentSettingsWorkerModulesBrowserTest,
                       WorkerImportModule) {
  // This test uses 2 servers, |https_server_| and |embedded_test_server|.
  // These 3 files are served from them:
  //   - "worker_import_module.html" from |embedded_test_server|.
  //   - "worker_import_module_worker.js" from |embedded_test_server|.
  //   - "worker_import_module_imported.js" from |https_server_|.
  // 1. worker_import_module.html starts a dedicated worker which type is
  //    'module' using worker_import_module_worker.js.
  //      new Worker('worker_import_module_worker.js', { type: 'module' })
  // 2. worker_import_module_worker.js imports worker_import_module_imported.js.
  //    - If succeeded to import, calls postMessage() with the exported |msg|
  //      constant value which is 'Imported'.
  //    - If failed, calls postMessage('Failed').
  // 3. When the page receives the message from the worker, change the title
  //    to the message string.
  //      worker.onmessage = (event) => { document.title = event.data; };
  ASSERT_TRUE(https_server_.Start());
  GURL module_url = https_server_.GetURL("/worker_import_module_imported.js");

  const std::string script = base::StringPrintf(
      "import('%s')\n"
      "  .then(module => postMessage(module.msg), _ => postMessage('Failed'));",
      module_url.spec().c_str());
  RegisterStaticFile(embedded_test_server(), "/worker_import_module_worker.js",
                     script, "text/javascript");

  ASSERT_TRUE(embedded_test_server()->Start());
  GURL http_url = embedded_test_server()->GetURL("/worker_import_module.html");

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  std::u16string expected_title(u"Imported");
  content::TitleWatcher title_watcher(web_contents, expected_title);
  title_watcher.AlsoWaitForTitle(u"Failed");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), http_url));

  // The import must be executed successfully.
  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}

IN_PROC_BROWSER_TEST_F(ContentSettingsWorkerModulesBrowserTest,
                       WorkerImportModuleBlocked) {
  // This test uses 2 servers, |https_server_| and |embedded_test_server|.
  // These 3 files are served from them:
  //   - "worker_import_module.html" from |embedded_test_server|.
  //   - "worker_import_module_worker.js" from |embedded_test_server|.
  //   - "worker_import_module_imported.js" from |https_server_|.
  // 1. worker_import_module.html starts a dedicated worker which type is
  //    'module' using worker_import_module_worker.js.
  //      new Worker('worker_import_module_worker.js', { type: 'module' })
  // 2. worker_import_module_worker.js imports worker_import_module_imported.js.
  //    - If succeeded to import, calls postMessage() with the exported |msg|
  //      constant value which is 'Imported'.
  //    - If failed, calls postMessage('Failed').
  // 3. When the page receives the message from the worker, change the title
  //    to the message string.
  //      worker.onmessage = (event) => { document.title = event.data; };
  ASSERT_TRUE(https_server_.Start());
  GURL module_url = https_server_.GetURL("/worker_import_module_imported.js");

  const std::string script = base::StringPrintf(
      "import('%s')\n"
      "  .then(module => postMessage(module.msg), _ => postMessage('Failed'));",
      module_url.spec().c_str());
  RegisterStaticFile(embedded_test_server(), "/worker_import_module_worker.js",
                     script, "text/javascript");

  ASSERT_TRUE(embedded_test_server()->Start());
  GURL http_url = embedded_test_server()->GetURL("/worker_import_module.html");

  // Change the settings to blocks the script loading of
  // worker_import_module_imported.js from worker_import_module.html.
  HostContentSettingsMap* content_settings_map =
      HostContentSettingsMapFactory::GetForProfile(browser()->profile());
  content_settings_map->SetWebsiteSettingCustomScope(
      ContentSettingsPattern::FromURLNoWildcard(http_url),
      ContentSettingsPattern::FromURLNoWildcard(module_url),
      ContentSettingsType::JAVASCRIPT, base::Value(CONTENT_SETTING_BLOCK));

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

  std::u16string expected_title(u"Failed");
  content::TitleWatcher title_watcher(web_contents, expected_title);
  title_watcher.AlsoWaitForTitle(u"Imported");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), http_url));

  // The import must be blocked.
  ui_test_utils::WaitForViewVisibility(
      browser(), VIEW_ID_CONTENT_SETTING_JAVASCRIPT, true);
  EXPECT_TRUE(PageSpecificContentSettings::GetForFrame(
                  web_contents->GetPrimaryMainFrame())
                  ->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}

IN_PROC_BROWSER_TEST_F(ContentSettingsWorkerModulesBrowserTest, CookieStore) {
  const char kWorkerScript[] = R"(
      async function cookieHandler(e) {
        try {
          await cookieStore.set(
              { name: e.data,
                value: 'value',
                expires: Date.now() + 3600*1000,
                sameSite: 'none' });
        } finally {
          e.source.postMessage('set executed for ' + e.data);
        }
      }
      self.addEventListener('message', cookieHandler);)";

  RegisterStaticFile(&https_server_, "/sw.js", kWorkerScript,
                     "text/javascript");

  ASSERT_TRUE(https_server_.Start());

  // Install service worker and wait for it to be activated.
  GURL setup_url =
      https_server_.GetURL("/service_worker/create_service_worker.html");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), setup_url));
  content::EvalJsResult result =
      content::EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
                      "register('/sw.js')");
  ASSERT_EQ("DONE", result);

  // Navigate again, this time it should be active. Also add some JS helpers to
  // message the service worker asking it to set cookies.
  GURL page_url = https_server_.GetURL("/empty.html");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), page_url));

  const char kClientScript[] = R"(
      function requestCookieSet(name) {
        return new Promise(resolve => {
          navigator.serviceWorker.onmessage = e => {
            resolve(e.data);
          };
          window.sw.postMessage(name);
        });
      }
      async function lookupSw() {
        const reg = await navigator.serviceWorker.ready;
        window.sw = reg.active;
        return !!window.sw;
      }
      lookupSw();)";

  content::EvalJsResult result2 = content::EvalJs(
      browser()->tab_strip_model()->GetActiveWebContents(), kClientScript);
  EXPECT_EQ(true, result2);

  {
    CookieChangeObserver observer(
        browser()->tab_strip_model()->GetActiveWebContents());
    // Set a cookie, see that it's reported.
    content::EvalJsResult result3 =
        content::EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
                        "requestCookieSet('first')");
    EXPECT_EQ("set executed for first", result3);
    observer.Wait();

    browsing_data::CannedCookieHelper* accepted =
        GetSiteSettingsCookieContainer(browser());
    browsing_data::CannedCookieHelper* blocked =
        GetSiteSettingsBlockedCookieContainer(browser());
    EXPECT_EQ(1u, accepted->GetCookieCount());
    EXPECT_TRUE(blocked->empty());
    net::CookieList accepted_cookies = ExtractCookies(accepted);
    EXPECT_THAT(accepted_cookies, net::MatchesCookieLine("first=value"));
  }

  {
    CookieChangeObserver observer(
        browser()->tab_strip_model()->GetActiveWebContents());
    // Now set with cookies blocked.
    content_settings::CookieSettings* settings =
        CookieSettingsFactory::GetForProfile(browser()->profile()).get();
    settings->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);
    content::EvalJsResult result4 =
        content::EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
                        "requestCookieSet('second')");
    EXPECT_EQ("set executed for second", result4);
    observer.Wait();

    browsing_data::CannedCookieHelper* accepted =
        GetSiteSettingsCookieContainer(browser());
    browsing_data::CannedCookieHelper* blocked =
        GetSiteSettingsBlockedCookieContainer(browser());
    EXPECT_EQ(1u, accepted->GetCookieCount());
    EXPECT_EQ(1u, blocked->GetCookieCount());
    net::CookieList blocked_cookies = ExtractCookies(blocked);
    EXPECT_THAT(blocked_cookies, net::MatchesCookieLine("second=value"));
  }
}

class ContentSettingsWithPrerenderingBrowserTest : public ContentSettingsTest {
 public:
  ContentSettingsWithPrerenderingBrowserTest()
      : prerender_test_helper_(base::BindRepeating(
            &ContentSettingsWithPrerenderingBrowserTest::GetWebContents,
            base::Unretained(this))) {}

  void SetUp() override {
    prerender_test_helper().SetUp(embedded_test_server());
    ContentSettingsTest::SetUp();
  }

  void SetUpOnMainThread() override {
    ContentSettingsTest::SetUpOnMainThread();
    ASSERT_TRUE(embedded_test_server()->Start());
  }

  content::test::PrerenderTestHelper& prerender_test_helper() {
    return prerender_test_helper_;
  }

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

 private:
  content::test::PrerenderTestHelper prerender_test_helper_;
};

// Used to wait for non-primary pages to set a cookie (eg: prerendering pages or
// fenced frames).
class NonPrimaryPageCookieAccessObserver : public content::WebContentsObserver {
 public:
  explicit NonPrimaryPageCookieAccessObserver(
      content::WebContents* web_contents)
      : content::WebContentsObserver(web_contents) {}
  ~NonPrimaryPageCookieAccessObserver() override = default;

  void OnCookiesAccessed(content::NavigationHandle* navigation_handle,
                         const content::CookieAccessDetails& details) override {
    bool is_in_primary_frame =
        navigation_handle->GetParentFrame()
            ? navigation_handle->GetParentFrame()->GetPage().IsPrimary()
            : navigation_handle->IsInPrimaryMainFrame();
    if (is_in_primary_frame) {
      cookie_accessed_in_primary_page_ = true;
    } else {
      run_loop_.Quit();
    }
  }

  void OnCookiesAccessed(content::RenderFrameHost* rfh,
                         const content::CookieAccessDetails& details) override {
    if (rfh->GetPage().IsPrimary()) {
      cookie_accessed_in_primary_page_ = true;
    } else {
      run_loop_.Quit();
    }
  }

  bool CookieAccessedByPrimaryPage() const {
    return cookie_accessed_in_primary_page_;
  }

  // Waits for the prerendering page to set a cookie.
  void Wait() { run_loop_.Run(); }

 private:
  bool cookie_accessed_in_primary_page_ = false;
  base::RunLoop run_loop_;
};

IN_PROC_BROWSER_TEST_F(ContentSettingsWithPrerenderingBrowserTest,
                       PrerenderingPageSetsCookie) {
  const GURL main_url = embedded_test_server()->GetURL("/empty.html");
  const GURL prerender_url =
      embedded_test_server()->GetURL("/set_cookie_header.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
  ASSERT_EQ(GetWebContents()->GetLastCommittedURL(), main_url);
  auto* main_pscs = PageSpecificContentSettings::GetForFrame(
      GetWebContents()->GetPrimaryMainFrame());
  ASSERT_FALSE(main_pscs->IsContentAllowed(ContentSettingsType::COOKIES));

  {
    NonPrimaryPageCookieAccessObserver cookie_observer(GetWebContents());
    prerender_test_helper().AddPrerender(prerender_url);
    int host_id = prerender_test_helper().GetHostForUrl(prerender_url);
    content::RenderFrameHost* prerender_frame =
        prerender_test_helper().GetPrerenderedMainFrameHost(host_id);
    EXPECT_NE(prerender_frame, nullptr);
    // Ensure notification for cookie access by prerendering page has been sent.
    cookie_observer.Wait();

    auto* prerender_pscs =
        PageSpecificContentSettings::GetForFrame(prerender_frame);
    EXPECT_TRUE(prerender_pscs->IsContentAllowed(ContentSettingsType::COOKIES));
    EXPECT_EQ(prerender_pscs->allowed_local_shared_objects().GetObjectCount(),
              1u);
    // Between when the cookie was set by the prerendering page and now, the
    // main page might have accessed the cookie (for instance, when sending a
    // request for a favicon) - check for the appropriate value based on
    // observed behavior.
    EXPECT_EQ(main_pscs->allowed_local_shared_objects().GetObjectCount(),
              cookie_observer.CookieAccessedByPrimaryPage() ? 1u : 0u);
  }

  prerender_test_helper().NavigatePrimaryPage(prerender_url);

  main_pscs = PageSpecificContentSettings::GetForFrame(
      GetWebContents()->GetPrimaryMainFrame());
  EXPECT_TRUE(main_pscs->IsContentAllowed(ContentSettingsType::COOKIES));
  EXPECT_EQ(main_pscs->allowed_local_shared_objects().GetObjectCount(), 1u);
}

IN_PROC_BROWSER_TEST_F(ContentSettingsWithPrerenderingBrowserTest,
                       PrerenderingPageIframeSetsCookie) {
  const GURL main_url = embedded_test_server()->GetURL("/empty.html");
  const GURL prerender_url = embedded_test_server()->GetURL("/title1.html");
  const GURL iframe_url =
      embedded_test_server()->GetURL("/set_cookie_header.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
  ASSERT_EQ(GetWebContents()->GetLastCommittedURL(), main_url);

  auto* main_pscs = PageSpecificContentSettings::GetForFrame(
      GetWebContents()->GetPrimaryMainFrame());
  ASSERT_FALSE(main_pscs->IsContentAllowed(ContentSettingsType::COOKIES));

  prerender_test_helper().AddPrerender(prerender_url);
  int host_id = prerender_test_helper().GetHostForUrl(prerender_url);
  content::RenderFrameHost* prerender_frame =
      prerender_test_helper().GetPrerenderedMainFrameHost(host_id);
  EXPECT_NE(prerender_frame, nullptr);

  content::TestNavigationManager navigation_manager(GetWebContents(),
                                                    iframe_url);
  NonPrimaryPageCookieAccessObserver cookie_observer(GetWebContents());
  EXPECT_TRUE(content::ExecJs(
      prerender_frame,
      content::JsReplace("const iframe = document.createElement('iframe');"
                         "iframe.src = $1;"
                         "document.body.appendChild(iframe);",
                         iframe_url)));
  EXPECT_TRUE(navigation_manager.WaitForRequestStart());
  navigation_manager.ResumeNavigation();
  cookie_observer.Wait();
  navigation_manager.WaitForNavigationFinished();

  auto* prerender_pscs =
      PageSpecificContentSettings::GetForFrame(prerender_frame);
  EXPECT_TRUE(prerender_pscs->IsContentAllowed(ContentSettingsType::COOKIES));
  EXPECT_EQ(prerender_pscs->allowed_local_shared_objects().GetObjectCount(),
            1u);
  // Between when the cookie was set by the prerendering page and now, the
  // main page might have accessed the cookie (for instance, when sending a
  // request for a favicon) - check for the appropriate value based on observed
  // behavior.
  EXPECT_EQ(main_pscs->allowed_local_shared_objects().GetObjectCount(),
            cookie_observer.CookieAccessedByPrimaryPage() ? 1u : 0u);
}

class ContentSettingsWithFencedFrameBrowserTest : public ContentSettingsTest {
 public:
  ContentSettingsWithFencedFrameBrowserTest() = default;
  ~ContentSettingsWithFencedFrameBrowserTest() override = default;

  void SetUpOnMainThread() override {
    ContentSettingsTest::SetUpOnMainThread();
    base::FilePath path;
    base::PathService::Get(content::DIR_TEST_DATA, &path);
    https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
    https_server_.ServeFilesFromDirectory(path);
    ASSERT_TRUE(https_server_.Start());
  }

  content::test::FencedFrameTestHelper& fenced_frame_test_helper() {
    return fenced_frame_test_helper_;
  }

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

 private:
  content::test::FencedFrameTestHelper fenced_frame_test_helper_;
};

IN_PROC_BROWSER_TEST_F(ContentSettingsWithFencedFrameBrowserTest,
                       FencedFrameSetsCookie) {
  const GURL main_url = https_server_.GetURL("a.test", "/empty.html");
  const GURL fenced_frame_url =
      https_server_.GetURL("b.test", "/fenced_frames/set_cookie_header.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
  ASSERT_EQ(GetWebContents()->GetLastCommittedURL(), main_url);
  auto* main_pscs = PageSpecificContentSettings::GetForFrame(
      GetWebContents()->GetPrimaryMainFrame());
  ASSERT_FALSE(main_pscs->IsContentAllowed(ContentSettingsType::COOKIES));

  std::unique_ptr<content::RenderFrameHostWrapper> fenced_frame;
  {
    NonPrimaryPageCookieAccessObserver cookie_observer(GetWebContents());
    fenced_frame = std::make_unique<content::RenderFrameHostWrapper>(
        fenced_frame_test_helper().CreateFencedFrame(
            GetWebContents()->GetPrimaryMainFrame(), fenced_frame_url));
    EXPECT_NE(fenced_frame, nullptr);
    // Ensure notification for cookie access by fenced frame has been sent.
    cookie_observer.Wait();
  }

  auto* ff_pscs = PageSpecificContentSettings::GetForFrame(fenced_frame->get());
  EXPECT_TRUE(ff_pscs->IsContentAllowed(ContentSettingsType::COOKIES));
  EXPECT_TRUE(main_pscs->IsContentAllowed(ContentSettingsType::COOKIES));
  EXPECT_EQ(main_pscs->allowed_local_shared_objects().GetObjectCount(), 1u);
  EXPECT_EQ(ff_pscs->allowed_local_shared_objects().GetObjectCount(), 1u);

  CookieSettingsFactory::GetForProfile(browser()->profile())
      ->SetCookieSetting((*fenced_frame)->GetLastCommittedURL(),
                         CONTENT_SETTING_BLOCK);
  {
    NonPrimaryPageCookieAccessObserver cookie_observer(GetWebContents());
    ASSERT_TRUE(content::ExecJs(fenced_frame->get(), "document.cookie"));
    cookie_observer.Wait();
  }

  EXPECT_TRUE(ff_pscs->IsContentBlocked(ContentSettingsType::COOKIES));
  EXPECT_TRUE(main_pscs->IsContentBlocked(ContentSettingsType::COOKIES));
  EXPECT_EQ(main_pscs->blocked_local_shared_objects().GetObjectCount(), 1u);
  EXPECT_EQ(ff_pscs->blocked_local_shared_objects().GetObjectCount(), 1u);

  ASSERT_TRUE(
      content::ExecJs(GetWebContents()->GetPrimaryMainFrame(),
                      "const ff = document.querySelector('fencedframe'); "
                      "ff.remove();"));
  ASSERT_TRUE(fenced_frame->WaitUntilRenderFrameDeleted());

  EXPECT_TRUE(main_pscs->IsContentBlocked(ContentSettingsType::COOKIES));
  EXPECT_EQ(main_pscs->allowed_local_shared_objects().GetObjectCount(), 1u);
  EXPECT_EQ(main_pscs->blocked_local_shared_objects().GetObjectCount(), 1u);
}

IN_PROC_BROWSER_TEST_F(ContentSettingsWithFencedFrameBrowserTest,
                       StorageAccessInFencedFrame) {
  const GURL main_url = https_server_.GetURL("a.test", "/empty.html");
  const GURL fenced_frame_url =
      https_server_.GetURL("b.test", "/browsing_data/site_data.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
  ASSERT_FALSE(GetWebContents()->GetPrimaryMainFrame()->IsErrorDocument());
  ASSERT_EQ(GetWebContents()->GetLastCommittedURL(), main_url);
  auto* main_pscs = PageSpecificContentSettings::GetForFrame(
      GetWebContents()->GetPrimaryMainFrame());

  content::RenderFrameHost* fenced_frame =
      fenced_frame_test_helper().CreateFencedFrame(
          GetWebContents()->GetPrimaryMainFrame(), fenced_frame_url);
  ASSERT_NE(fenced_frame, nullptr);
  auto* ff_pscs = PageSpecificContentSettings::GetForFrame(fenced_frame);

  std::vector<std::string> storage_types_to_test{
      "LocalStorage",     "SessionStorage", "CacheStorage", "FileSystem",
      "FileSystemAccess", "IndexedDb",      "SharedWorker", "ServiceWorker"};
  for (auto storage_type : storage_types_to_test) {
    EXPECT_TRUE(content::EvalJs(fenced_frame, "set" + storage_type + "();",
                                content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
                    .ExtractBool());
  }

  std::vector<PageSpecificContentSettings*> pscs_list;
  pscs_list.push_back(main_pscs);
  pscs_list.push_back(ff_pscs);

  for (auto* pscs : pscs_list) {
    const browsing_data::LocalSharedObjectsContainer& container =
        pscs->allowed_local_shared_objects();
    EXPECT_TRUE(pscs->IsContentAllowed(ContentSettingsType::COOKIES));
    EXPECT_EQ(container.local_storages()->GetCount(), 1u);
    EXPECT_EQ(container.session_storages()->GetCount(), 1u);
    EXPECT_EQ(container.cache_storages()->GetCount(), 1u);
    EXPECT_EQ(container.file_systems()->GetCount(), 1u);
    EXPECT_EQ(container.indexed_dbs()->GetCount(), 1u);
    EXPECT_EQ(container.shared_workers()->GetSharedWorkerCount(), 1u);
    EXPECT_EQ(container.service_workers()->GetCount(), 1u);
  }
}

IN_PROC_BROWSER_TEST_F(ContentSettingsWithFencedFrameBrowserTest,
                       RendererContentSettings) {
  const GURL main_url = https_server_.GetURL("a.test", "/empty.html");
  const GURL fenced_frame_url =
      https_server_.GetURL("b.test", "/fenced_frames/page_with_script.html");
  const GURL other_main_url = https_server_.GetURL("c.test", "/empty.html");

  auto NavigatePrimaryPageAndAddFencedFrame =
      [&]() -> content::RenderFrameHost* {
    EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
    EXPECT_FALSE(GetWebContents()->GetPrimaryMainFrame()->IsErrorDocument());
    EXPECT_EQ(GetWebContents()->GetLastCommittedURL(), main_url);
    content::RenderFrameHost* fenced_frame =
        fenced_frame_test_helper().CreateFencedFrame(
            GetWebContents()->GetPrimaryMainFrame(), fenced_frame_url);
    EXPECT_NE(fenced_frame, nullptr);
    return fenced_frame;
  };

  auto ExpectScriptBlocked = [&](content::RenderFrameHost* fenced_frame) {
    ui_test_utils::WaitForViewVisibility(
        browser(), VIEW_ID_CONTENT_SETTING_JAVASCRIPT, true);
    auto* main_pscs = PageSpecificContentSettings::GetForFrame(
        GetWebContents()->GetPrimaryMainFrame());
    auto* ff_pscs = PageSpecificContentSettings::GetForFrame(fenced_frame);
    // Script should have been blocked in the fenced frame (and reflected in
    // the PSCS of the primary page as well).
    EXPECT_TRUE(ff_pscs->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
    EXPECT_TRUE(main_pscs->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
  };

  auto ExpectScriptAllowed = [&](content::RenderFrameHost* fenced_frame) {
    EXPECT_EQ(1, EvalJs(fenced_frame, "(async () => { return 1; })();"));
    auto* ff_pscs = PageSpecificContentSettings::GetForFrame(fenced_frame);
    EXPECT_FALSE(ff_pscs->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
  };

  // Block script in (a.test, b.test).
  auto* map =
      HostContentSettingsMapFactory::GetForProfile(browser()->profile());
  map->SetContentSettingCustomScope(
      ContentSettingsPattern::FromURL(main_url),
      ContentSettingsPattern::FromURL(fenced_frame_url),
      ContentSettingsType::JAVASCRIPT, ContentSetting::CONTENT_SETTING_BLOCK);
  content::RenderFrameHost* fenced_frame =
      NavigatePrimaryPageAndAddFencedFrame();
  ExpectScriptBlocked(fenced_frame);

  // Allow script in (a.test, b.test).
  map->SetContentSettingCustomScope(
      ContentSettingsPattern::FromURL(main_url),
      ContentSettingsPattern::FromURL(fenced_frame_url),
      ContentSettingsType::JAVASCRIPT, ContentSetting::CONTENT_SETTING_ALLOW);
  fenced_frame = NavigatePrimaryPageAndAddFencedFrame();
  ExpectScriptAllowed(fenced_frame);

  // Block script in (c.test, b.test).
  map->SetContentSettingCustomScope(
      ContentSettingsPattern::FromURL(other_main_url),
      ContentSettingsPattern::FromURL(fenced_frame_url),
      ContentSettingsType::JAVASCRIPT, ContentSetting::CONTENT_SETTING_BLOCK);
  fenced_frame = NavigatePrimaryPageAndAddFencedFrame();
  ExpectScriptAllowed(fenced_frame);

  // Block script in (*, b.test) - this should not have any effect as the
  // (a.test, b.test) rule is a narrower rule and should have precedence.
  map->SetContentSettingCustomScope(
      ContentSettingsPattern::Wildcard(),
      ContentSettingsPattern::FromURL(fenced_frame_url),
      ContentSettingsType::JAVASCRIPT, ContentSetting::CONTENT_SETTING_BLOCK);
  fenced_frame = NavigatePrimaryPageAndAddFencedFrame();
  ExpectScriptAllowed(fenced_frame);

  // Remove (a.test, b.test) rule - (*, b.test) rule should now be the narrowest
  // and will be applied.
  map->SetContentSettingCustomScope(
      ContentSettingsPattern::FromURL(main_url),
      ContentSettingsPattern::FromURL(fenced_frame_url),
      ContentSettingsType::JAVASCRIPT, ContentSetting::CONTENT_SETTING_DEFAULT);
  fenced_frame = NavigatePrimaryPageAndAddFencedFrame();
  ExpectScriptBlocked(fenced_frame);
}

class ContentSettingsWorkerModulesWithFencedFrameBrowserTest
    : public ContentSettingsWorkerModulesBrowserTest {
 public:
  ContentSettingsWorkerModulesWithFencedFrameBrowserTest() = default;
  ~ContentSettingsWorkerModulesWithFencedFrameBrowserTest() override = default;

  content::test::FencedFrameTestHelper& fenced_frame_test_helper() {
    return fenced_frame_test_helper_;
  }

 private:
  content::test::FencedFrameTestHelper fenced_frame_test_helper_;
};

IN_PROC_BROWSER_TEST_F(ContentSettingsWorkerModulesWithFencedFrameBrowserTest,
                       WorkerImportModuleBlocked) {
  const std::string script = base::StringPrintf(
      "import('%s')\n"
      "  .then(module => postMessage(module.msg), _ => postMessage('Failed'));",
      "/worker_import_module_imported.js");
  RegisterStaticFile(&https_server_, "/worker_import_module_worker.js", script,
                     "text/javascript");
  https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
  ASSERT_TRUE(https_server_.Start());
  GURL main_url = https_server_.GetURL("a.test", "/title1.html");
  GURL module_url =
      https_server_.GetURL("b.test", "/worker_import_module_imported.js");
  GURL fenced_frame_url =
      https_server_.GetURL("b.test", "/worker_import_module.html");

  // Change the settings to block the script loading of
  // worker_import_module_imported.js from worker_import_module.html.
  auto* content_settings_map =
      HostContentSettingsMapFactory::GetForProfile(browser()->profile());
  content_settings_map->SetWebsiteSettingCustomScope(
      ContentSettingsPattern::FromURLNoWildcard(main_url),
      ContentSettingsPattern::FromURLNoWildcard(module_url),
      ContentSettingsType::JAVASCRIPT, base::Value(CONTENT_SETTING_BLOCK));

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

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
  ASSERT_FALSE(web_contents->GetPrimaryMainFrame()->IsErrorDocument());
  content::RenderFrameHost* fenced_frame =
      fenced_frame_test_helper().CreateFencedFrame(
          web_contents->GetPrimaryMainFrame(), fenced_frame_url);
  ASSERT_NE(nullptr, fenced_frame);

  // The import must be blocked.
  ui_test_utils::WaitForViewVisibility(
      browser(), VIEW_ID_CONTENT_SETTING_JAVASCRIPT, true);
  EXPECT_TRUE(PageSpecificContentSettings::GetForFrame(
                  web_contents->GetPrimaryMainFrame())
                  ->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
}

redpajama_set_name: RedPajamaGithub


With the threat of more assassinations on Bainbridge paralyzing the fortified island and spies in her own ranks turning her own men against her, Jillybean has to look to unlikely allies as The Black Captain brings his fury south to destroy her.
He is consumed with the idea of revenge, yet he retains a cold, calculating rationality that Jillybean does not. The stress of war, as well as the deaths of her friends and the man she loves by her own hand, is eating away at what little mental stability she has left. Again and again, she slips away into a fantasy world, and each time, she loses a little more control of the murdering thugs she calls an army.
Her only hope is to turn to The Guardians, a religious community that values faith and honor higher than life itself. The problem she faces, however, is she can't hide the fact that she is, and always will be, The Mad Queen.
Peter Meredith is the master of character driven ZA genre ( I had to buy and listen to a huge number of froggy books before I was rewarded with this prince of an author) and Callahan's performance of the gut wrenching action drama ( sans swearing)is terrific.
I need the author to stop playing with my emotions this book kept me on edge of my seat.
I cannot wait for the next book. These are my favorite characters and I hope the story goes on forever. Peter please keep writing these books....don't change a thing!
Gripping edge of seat stuff can not wait until the next book, keep them coming.


Marriage and divorce are two life-changing events that can impact a family's future financial decisions. If you are vested, your retirement benefit from MOSERS is considered "marital property." If you have been married at any time while an active member of MOSERS and are considering a divorce, your spouse may be legally entitled to receive a portion of your retirement benefit. Any amount up to 50% of the benefit accrued during marriage may go to your ex-spouse. Your benefit is not automatically divided, though. MOSERS will need a certified Division of Benefits Order (DBO) from a court of competent jurisdiction. Complete a Division of Benefits Order Request for Estimate form and submit it to MOSERS before the court date.
Your ex-spouse's benefits will not begin until the month following the date the DBO is issued, or the date of retirement, whichever is later. Benefit payments will not be paid retroactively. The benefit for service accrued before marriage or after the date on the DBO cannot be divided. The ex-spouse benefit will be based upon the formula of the plan to which you belong on the date of dissolution. Your ex-spouse's payments are eligible for cost-of-living adjustments (COLAs), based on which plan you belong to. He/she is not eligible to receive any formula increases, a temporary benefit, or any portion of your BackDROP payment. For more information, see the Divorce and Your MOSERS Benefit brochure on MOSERS' website.
The DBO will terminate automatically in the event of the death of either person. If your ex-spouse dies first, your benefit will increase by the amount otherwise payable to your ex-spouse on the first of the month following his/her death.
In the event of a divorce, you should know how your benefits will be distributed to your eligible survivors. If you chose a joint and survivor option under either the MSEP, MSEP 2000 or MSEP 2011, survivor benefits will be paid to the eligible beneficiary named on your Application for Retirement which could include your ex-spouse if you divorced after you began to receive your retirement benefits. (regardless of your marital status at the time of your death).
You were married at retirement and elected a joint and survivor benefit payment option and your spouse died,* resulting in your benefit reverting to the Life Income Annuity. If you remarry, you have one year from the date of remarriage to reelect a joint and survivor option and name your new spouse as beneficiary.
If you meet one of these criteria and marry after you retire and make your election within one year of your marriage, you may designate your new spouse as the beneficiary under a joint and survivor option. You may do this by submitting the Designation of New Spouse as Beneficiary for Retirement Benefits form online. You must submit a copy of your spouse's proof-of-age document and marriage certificate with this form.
*If your spouse dies, please notify MOSERS as soon as possible. You may be eligible for the "pop-up" provision, which would increase your monthly retirement benefit.
Please note: This information primarily applies to general state employees. If you are a judge, administrative law judge, statewide elected official or legislator, please refer to your MOSERS handbook or contact a MOSERS benefit counselor for specific guidelines.


text:
How the 50 Most Influential Physician Executives were selected
To select the 12th annual ranking of the 50 Most Influential Physician Executives and Leaders, Modern Healthcare accepted nominations from Nov. 9 to Dec. 11, 2015 through ModernHealthcare.com. The online nomination process asked for reader input on specific criteria for judging nominees, including executive responsibility, leadership qualities, professional achievement inside and outside of their respective organizations, innovation and community service.
This year's ballot once again comprised 150 names based on the total nominations submitted. From Jan. 11 to Feb. 19, readers could vote for the one candidate they believed should be included in the final roster of the 50 Most Influential Physician Executives and Leaders. More than 27,000 votes were submitted for this year's program.
As in previous years, readers' votes counted for 50% of the outcome to determine the final ranking. The other 50% was based on the opinions of Modern Healthcare's senior editors.
View a photo gallery of this year's winners, including short biographies of each nominee, and read more about the program. Galleries and profiles of winners from previous years are also available.

redpajama_set_name: RedPajamaCommonCrawl


text:
Tarot 101, My Way – Major Arcana: The Tower and the Star
August 20, 2018 August 20, 2018 / parsifalswheeldivination
This is the classic "calm after the storm" scenario: the Tower topples the unsteady empire of the Devil and a soothing calm (and new hope) descends upon the ravaged landscape in the presence of the Star. There is nothing benign about the uncompromising Mars energy of the Tower; here is the "necessary" nature of the tarot at hurricane force, undoing what was done badly in order to enable a fresh start. In older decks it was often interpreted as the price paid for arrogance: God lays low the high-and-mighty with a single stroke. In our more secular times it suggests the inevitable demise of a poorly-laid foundation, often marital or financial. The only shelter from the Tower's sweeping fury occurs when it is reversed, which suggests a "soft landing" for the expelled inhabitants, who appear to come down on their feet rather than their heads. The Tower has a well-deserved reputation for signifying accidents and other traumatic events, a steamroller that leaves nothing standing in its path. The victim is left rummaging through the rubble to see whether any sound bricks remain with which to rebuild. In psychological terms it can show a sudden revelation or (often) unpleasant epiphany, the "scales falling from one's eyes;" the unmasking of a cheating or embezzling partner is a common outcome.
The advice of the Star is to raise one's sights above the destruction and move toward the light, which can seem distressingly remote when standing at "Ground Zero." The Star spells relief from worldly cares, but there is no guarantee to be found in idle dreaming. Waiting with outstretched hand for its largesse to fall from the heavens is a recipe for disappointment; it offers a signpost but not an effortless path to salvation. The yawning gulf between reality and redemption must be crossed with resolve and clarity of vision in order to make the most of the opportunity. It is usually read as one of the "good" cards, but it demands commitment to work its magic. Without clear-eyed self-examination and a purposeful stab at self-regeneration it can remain a distant hope. Shaking off the clinging debris from the Tower's fall takes work. (I'm a firm believer in the "no free lunch" school of tarot interpretation; we only reap what we sow and nurture.)
The Major Arcana: Trump 16 – The Tower
Golden Dawn "Liber T" (S.L. Mathers):
"Ambition, fighting, war, courage. In certain combinations, destruction, danger, fall, ruin."
The Pictorial Key to the Tarot (A.E. Waite):
"Misery, distress, indigence, adversity, calamity, disgrace, deception, ruin. It is a card in particular of unforeseen catastrophe."
The Book of Thoth (Aleister Crowley):
"Quarrel, combat, danger, ruin, destruction of plans, sudden death, escape from prison."
This card depicts the demolition of anything in the life of the seeker that is blocking progress, emphatically but painfully clearing the way for new developments. It usually arrives abruptly and can feel like the "end of the world" until a measure of distance from "Ground Zero" is achieved. It is all-consuming for as long as it lasts (which usually isn't overly long), and then gives way to recuperation. It has a "scorched earth" feel to it, implying a long road to full recovery. Cliches denoting sudden reversals, like "having the rug pulled out from under," "having one's legs kicked out," "being blind-sided," "being hit between the eyes" all have residency in the Tower. The challenge is to pick oneself up and soldier on in the face of bleak prospects for redemption. Many of life's calamities come under the purview of the Tower: relationship break-ups, job terminations or demotions, serious accidents, violent encounters, anything of a threatening nature that can't be successfully headed off or defused.
Click here for further observations:
https://parsifalswheeldivination.com/2018/07/17/the-tower-moment/
The Major Arcana: Trump 17 – The Star
"Hope, faith, unexpected help. But also sometimes dreaminess, deceived hope, etc."
"Hope and bright prospects. Loss, theft, privation, abandonment."
"Hope, unexpected help, clearness of vision, realization of possibilities, spiritual insight; with bad aspects, error of judgment, dreaminess, disappointment."
The Star is usually seen as a positive card of hope and inspiration, but a more useful interpretation may simply be "clarity of vision." As an expression of "hope," it is frequently too abstract and remote to be of much practical value, thus letting hope degenerate into "wishful thinking" unless some form of affirmation is promptly received. Waite's dire negative pronouncements don't square well with the other interpretations, except as they may be related to false or misplaced hopes and the resulting loss of enthusiasm and confidence. Taking the "long view" of the Star's benevolent emanation and keeping hopes and wishes firmly in line with one's reality are rational but not especially inspiring ways to handle its often faint whispers of the wonderful future ahead. The old political cliché "Trust but verify" has a lot to recommend it when navigating by the light of the Star.
https://parsifalswheeldivination.com/2017/10/26/one-star-in-sight/
Esoteric Tarot, Tarot Card Meanings, Tarot Resources, Trumps
Learning, Tarot, Trumps
← For Lack of an Anchor . . .
Many Fish Bites →


text:
Q: R: Convert Data Frame to Transactions to Use ARules Package I have a csv file that looks like the below data frame once it is imported:
df <- read("data.csv")

ID  Name  Flag1 Flag2 Flag3 Flag4
1    a      0    F2     F3   F4
2    b     F1     0     F3   F4
3    c      0    F2     F3    0
4    d      0    F2     0    F4
5    e     F1    F2     F3    0

ID and Name are factors.  Flag1 through Flag4 are chr.
I need to use the rules package, so I need to convert this data frame into a transaction file.  I know that to do so, I need to convert all values to factors, so I did this:
as(as.factor(Flags), "transactions")

which gave me this:
Error in sort.list(y) : 'x' must be atomic for 'sort.list'
Have you called 'sort' on a list?

Then I tried reading the csv file directly:
read.transactions("data.csv", format = "basket", sep = ",")

which gave me this:
Error in asMethod(object) :
  can not coerce list with transactions with duplicated items

I'm thinking this is because of the 0's in the data, but I can't figure out a way to remove the 0's.  To use functions in the rules packages, I need the dataset to look like this, correct?
1,a,F2,F3,F4
2,b,F1,F3,F4
3,c,F2,F3
4,d,F2,F4
5,e,F1,F2,F3

Thanks ahead of time.

A: Try reading the data in the following manner:
required_Data<-read.transactions("data.csv", rm.duplicates=TRUE, format = "basket", sep = ",")
Hope that helps!

redpajama_set_name: RedPajamaStackExchange


Tekken Zaibatsu is back up and everything is right again in the world.
Got two new articles up and working on a third and fourth, an objective article "Countering generic low spam" that's aimed at helping players out against generic low poking/spamming as well as an article under wish list "Dragunov needs more high/low crush" that is just like it sounds, Dragunov really does need more high and low crush attacks. More articles should be up soon.


text:
MA in Translation (2 years, 123 ECTS points)
You can apply for the MA programme if you have a BA with a good overall grade which is recognized in Germany, and an excellent knowledge of German (ideally, TestDaF 19 points). For details, see the following websites:
the website of the German Department (in particular "Studieninteressierte")
the website of Mainz University (especially "Anerkennung ausländischer Vorbildungsnachweise")
the "anabin" database (recognition of foreign degrees)
You may have to take an aptitude test, and you may be asked to attend a foundation module in translation, language and culture as part of the MA programme.
In the MA programme, as a native speaker of English you can either study German as your only foreign language, or you can choose a second foreign language, which can be Arabic, Chinese, Dutch, French, Greek, Italian, Polish, Portuguese, Russian or Spanish. Translation from your second foreign language will usually be into German. BA-level competence is a prerequisite for all foreign languages studied.
The MA programme is modularized. It includes
four core (obligatory) modules, two in translation competence and two in translation studies and/or linguistics and/or cultural studies,
and four optional modules, which you can choose from a wide range of options.
Within the MA programme, you can choose certain itineraries, namely
specialized translation,
community interpreting,
translatology and translation teaching,
literary and media translation,
inter- and transcultural communication, and
simultaneous and consecutive interpreting (limited number of places; aptitude test)
by selecting a specific combination of optional modules. Alternatively, you may prefer a broader, less specialized spectrum of optionals.
While the standard duration of the MA is two years, in practice you will be granted considerable flexibility. If, for example, you have a young child, or work part-time, it will be possible for you to attend fewer courses per semester and complete the MA in three or even four years. If, on the other hand, you wish to complete the programme in less than two years, you can do so by attending more than the standard number of courses per semester and using your vacations for coursework and revision.
The charge for the programme is €125.13 per semester. At present, most students do not have to pay tuition fees (click here for details).
For further information about the MA in Translation, see the website "MA Translation" (in German).
Further links (in German)
MA homepage
MA aptitude test
Recognition of foreign degrees
TestDaF Institute
Goethe Institute
Letzte Aktualisierung:20. Februar 2019