Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 149 additions & 2 deletions browser/containers/containers_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@

namespace containers {

constexpr char kTestContainerId[] = "test-container-id";

class ContainersBrowserTest : public InProcessBrowserTest {
public:
ContainersBrowserTest() : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
Expand All @@ -48,6 +50,11 @@ class ContainersBrowserTest : public InProcessBrowserTest {

~ContainersBrowserTest() override = default;

void SetUp() override {
set_open_about_blank_on_browser_launch(false);
InProcessBrowserTest::SetUp();
}

void SetUpCommandLine(base::CommandLine* command_line) override {
InProcessBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII(
Expand Down Expand Up @@ -332,6 +339,73 @@ IN_PROC_BROWSER_TEST_F(ContainersBrowserTest, IsolateCookiesAndStorage) {
GetIndexedDBJS("test_key")));
}

IN_PROC_BROWSER_TEST_F(ContainersBrowserTest,
PRE_StoragePersistenceAcrossSessions) {
const GURL url("https://a.test/simple.html");

// Navigate to the page
NavigateParams params(browser(), url, ui::PAGE_TRANSITION_LINK);
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
params.storage_partition_config = content::StoragePartitionConfig::Create(
browser()->profile(), kContainersStoragePartitionDomain, kTestContainerId,
browser()->profile()->IsOffTheRecord());
ui_test_utils::NavigateToURL(&params);

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

// Set persistent storage data
EXPECT_TRUE(content::ExecJs(
web_contents, SetCookieJS("persistent_cookie", "persistent_value")));
EXPECT_TRUE(content::ExecJs(
web_contents, SetLocalStorageJS("persistent_key", "persistent_value")));

EXPECT_TRUE(content::ExecJs(
web_contents, SetIndexedDBJS("persistent_key", "persistent_value")));

// Verify data is set
content::EvalJsResult cookie_result =
content::EvalJs(web_contents, GetCookiesJS());
EXPECT_TRUE(cookie_result.ExtractString().find(
"persistent_cookie=persistent_value") != std::string::npos);

EXPECT_EQ("persistent_value",
content::EvalJs(web_contents, GetLocalStorageJS("persistent_key")));
EXPECT_EQ("persistent_value",
content::EvalJs(web_contents, GetIndexedDBJS("persistent_key")));
}

IN_PROC_BROWSER_TEST_F(ContainersBrowserTest,
StoragePersistenceAcrossSessions) {
const GURL url("https://a.test/simple.html");

// Navigate to the page
NavigateParams params(browser(), url, ui::PAGE_TRANSITION_LINK);
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
params.storage_partition_config = content::StoragePartitionConfig::Create(
browser()->profile(), kContainersStoragePartitionDomain, kTestContainerId,
browser()->profile()->IsOffTheRecord());
ui_test_utils::NavigateToURL(&params);

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

// Verify persistent data is still available after reload
content::EvalJsResult cookie_result_reloaded =
content::EvalJs(web_contents_reloaded, GetCookiesJS());
EXPECT_TRUE(cookie_result_reloaded.ExtractString().find(
"persistent_cookie=persistent_value") != std::string::npos);

EXPECT_EQ("persistent_value",
content::EvalJs(web_contents_reloaded,
GetLocalStorageJS("persistent_key")));
EXPECT_EQ(
"persistent_value",
content::EvalJs(web_contents_reloaded, GetIndexedDBJS("persistent_key")));
}

IN_PROC_BROWSER_TEST_F(ContainersBrowserTest,
LinkNavigationInheritsContainerStoragePartition) {
const GURL url("https://a.test/simple.html");
Expand All @@ -340,8 +414,8 @@ IN_PROC_BROWSER_TEST_F(ContainersBrowserTest,
NavigateParams params(browser(), url, ui::PAGE_TRANSITION_LINK);
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
params.storage_partition_config = content::StoragePartitionConfig::Create(
browser()->profile(), kContainersStoragePartitionDomain,
"container-for-links", browser()->profile()->IsOffTheRecord());
browser()->profile(), kContainersStoragePartitionDomain, kTestContainerId,
browser()->profile()->IsOffTheRecord());
ui_test_utils::NavigateToURL(&params);

content::WebContents* container_web_contents =
Expand Down Expand Up @@ -385,6 +459,13 @@ IN_PROC_BROWSER_TEST_F(ContainersBrowserTest,
// Verify the new tab is on the correct URL
EXPECT_EQ(url, new_tab_contents->GetLastCommittedURL());

content::StoragePartition* storage_partition =
new_tab_contents->GetPrimaryMainFrame()->GetStoragePartition();
ASSERT_TRUE(storage_partition);
EXPECT_EQ(kContainersStoragePartitionDomain,
storage_partition->GetConfig().partition_domain());
EXPECT_EQ(kTestContainerId, storage_partition->GetConfig().partition_name());

// Verify the new tab has access to the same container storage partition
content::EvalJsResult cookie_result =
content::EvalJs(new_tab_contents, GetCookiesJS());
Expand Down Expand Up @@ -899,4 +980,70 @@ IN_PROC_BROWSER_TEST_F(ContainersBrowserTest, ShouldShowTabAccent) {
EXPECT_FALSE(tab_in_container->ShouldShowLargeAccentIcon());
}

IN_PROC_BROWSER_TEST_F(ContainersBrowserTest,
PRE_ServiceWorkerPersistenceAcrossSessions) {
const GURL url("https://a.test/containers/container_test.html");
const GURL worker_url("https://a.test/containers/container_worker.js");
const std::string scope = "https://a.test/containers/";

// Navigate to the page with a container
NavigateParams params(browser(), url, ui::PAGE_TRANSITION_LINK);
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
params.storage_partition_config = content::StoragePartitionConfig::Create(
browser()->profile(), kContainersStoragePartitionDomain, kTestContainerId,
browser()->profile()->IsOffTheRecord());
ui_test_utils::NavigateToURL(&params);

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

// Register service worker
EXPECT_TRUE(content::ExecJs(
web_contents, RegisterServiceWorkerJS(worker_url.spec(), scope)));

// Verify service worker is registered
EXPECT_EQ(
"registered",
content::EvalJs(web_contents, CheckServiceWorkerRegisteredJS(scope)));

// Set some persistent storage data that the service worker might use
EXPECT_TRUE(content::ExecJs(
web_contents, SetLocalStorageJS("sw_data", "persistent_value")));
EXPECT_TRUE(content::ExecJs(web_contents,
SetCookieJS("sw_cookie", "persistent_cookie")));
}

IN_PROC_BROWSER_TEST_F(ContainersBrowserTest,
ServiceWorkerPersistenceAcrossSessions) {
const GURL url("https://a.test/containers/container_test.html");
const std::string scope = "https://a.test/containers/";

// Navigate to the page with the same container
NavigateParams params(browser(), url, ui::PAGE_TRANSITION_LINK);
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
params.storage_partition_config = content::StoragePartitionConfig::Create(
browser()->profile(), kContainersStoragePartitionDomain, kTestContainerId,
browser()->profile()->IsOffTheRecord());
ui_test_utils::NavigateToURL(&params);

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

// Verify service worker is still registered after browser restart
EXPECT_EQ("registered",
content::EvalJs(web_contents_reloaded,
CheckServiceWorkerRegisteredJS(scope)));

// Verify persistent storage data is still available
EXPECT_EQ("persistent_value", content::EvalJs(web_contents_reloaded,
GetLocalStorageJS("sw_data")));

content::EvalJsResult cookie_result =
content::EvalJs(web_contents_reloaded, GetCookiesJS());
EXPECT_TRUE(cookie_result.ExtractString().find(
"sw_cookie=persistent_cookie") != std::string::npos);
}

} // namespace containers
1 change: 1 addition & 0 deletions components/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import("//brave/components/ai_chat/core/common/buildflags/buildflags.gni")
import("//brave/components/brave_wallet/common/buildflags/buildflags.gni")
import("//brave/components/brave_wayback_machine/buildflags/buildflags.gni")
import("//brave/components/containers/buildflags/buildflags.gni")
import("//brave/components/psst/buildflags/buildflags.gni")
import("//brave/components/speedreader/common/buildflags/buildflags.gni")
import("//build/config/features.gni")
Expand Down
23 changes: 20 additions & 3 deletions components/containers/content/browser/storage_partition_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

#include "brave/components/containers/content/browser/storage_partition_utils.h"

#include <algorithm>

#include "base/strings/string_util.h"
#include "brave/components/containers/core/common/features.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/storage_partition_config.h"
Expand All @@ -15,9 +18,22 @@ namespace containers {
bool IsContainersStoragePartition(
const content::StoragePartitionConfig& partition_config) {
CHECK(base::FeatureList::IsEnabled(features::kContainers));
return partition_config.partition_domain() ==
kContainersStoragePartitionDomain &&
!partition_config.partition_name().empty();
return IsContainersStoragePartitionKey(partition_config.partition_domain(),
partition_config.partition_name());
}

bool IsContainersStoragePartitionKey(std::string_view partition_domain,
std::string_view partition_name) {
CHECK(base::FeatureList::IsEnabled(features::kContainers));
Comment thread
goodov marked this conversation as resolved.
return partition_domain == kContainersStoragePartitionDomain &&
IsValidStoragePartitionKeyComponent(partition_name);
}

bool IsValidStoragePartitionKeyComponent(std::string_view component) {
CHECK(base::FeatureList::IsEnabled(features::kContainers));
return !component.empty() && std::ranges::all_of(component, [](char c) {
return base::IsAsciiAlphaNumeric(c) || c == '-';
});
}

std::optional<content::StoragePartitionConfig> MaybeInheritStoragePartition(
Expand All @@ -33,6 +49,7 @@ std::optional<content::StoragePartitionConfig> MaybeInheritStoragePartition(
}

std::string GetContainerIdForWebContents(content::WebContents* web_contents) {
CHECK(base::FeatureList::IsEnabled(features::kContainers));
if (!web_contents) {
return std::string();
}
Expand Down
17 changes: 13 additions & 4 deletions components/containers/content/browser/storage_partition_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,25 @@ class WebContents;
namespace containers {

// The partition domain identifier used for all containers storage partitions.
inline constexpr char kContainersStoragePartitionDomain[] =
"containers-default";
inline constexpr char kContainersStoragePartitionDomain[] = "containers";

// Checks whether a given StoragePartitionConfig belongs to Containers.
// Partition domain should match kContainersStoragePartitionDomain and partition
// name should be non-empty.
COMPONENT_EXPORT(CONTAINERS_CONTENT_BROWSER)
bool IsContainersStoragePartition(
const content::StoragePartitionConfig& partition_config);

// Checks whether a given StoragePartitionConfig partition domain and name
// belongs to Containers.
COMPONENT_EXPORT(CONTAINERS_CONTENT_BROWSER)
bool IsContainersStoragePartitionKey(std::string_view partition_domain,
Comment thread
goodov marked this conversation as resolved.
std::string_view partition_name);

// Checks whether a storage partition key component is not empty and contains
// only valid characters for Containers storage partitions. Valid characters are
// ASCII alphanumeric characters and '-'.
COMPONENT_EXPORT(CONTAINERS_CONTENT_BROWSER)
bool IsValidStoragePartitionKeyComponent(std::string_view component);

// Returns the StoragePartitionConfig if it is a Containers storage partition,
// otherwise returns std::nullopt. Used to conditionally inherit
// StoragePartitionConfig when creating a new SiteInstance.
Expand Down
Loading
Loading