Skip to content

Commit c74e22c

Browse files
committed
Support contained tabs session restore and sync.
1 parent 5374ffa commit c74e22c

28 files changed

+1902
-56
lines changed

browser/containers/containers_browsertest.cc

Lines changed: 644 additions & 0 deletions
Large diffs are not rendered by default.

chromium_src/chrome/browser/ui/browser_tabrestore.cc

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,49 @@
99
#include "content/public/browser/browser_context.h"
1010

1111
#if BUILDFLAG(ENABLE_CONTAINERS)
12+
#include "brave/components/containers/content/browser/storage_partition_utils.h"
13+
#include "brave/components/containers/core/common/features.h"
1214
#include "chrome/browser/tab_contents/tab_util.h"
1315

14-
#define GetSiteInstanceForNewTab(...) \
15-
GetSiteInstanceForNewTab(__VA_ARGS__, std::nullopt)
16+
#define GetSiteInstanceForNewTab(...) \
17+
GetSiteInstanceForNewTab( \
18+
__VA_ARGS__, GetStoragePartitionConfigToRestore( \
19+
browser->profile(), navigations, selected_navigation))
1620

21+
namespace {
22+
23+
std::optional<content::StoragePartitionConfig>
24+
GetStoragePartitionConfigToRestore(
25+
content::BrowserContext* browser_context,
26+
base::span<const sessions::SerializedNavigationEntry> navigations,
27+
int selected_navigation) {
28+
if (!base::FeatureList::IsEnabled(containers::features::kContainers)) {
29+
return std::nullopt;
30+
}
31+
32+
if (navigations.empty()) {
33+
return std::nullopt;
34+
}
35+
36+
if (selected_navigation < 0 ||
37+
static_cast<size_t>(selected_navigation) >= navigations.size()) {
38+
return std::nullopt;
39+
}
40+
41+
if (const auto& storage_partition_key =
42+
navigations[selected_navigation].storage_partition_key();
43+
storage_partition_key &&
44+
containers::IsContainersStoragePartitionKey(
45+
storage_partition_key->first, storage_partition_key->second)) {
46+
return content::StoragePartitionConfig::Create(
47+
browser_context, storage_partition_key->first,
48+
storage_partition_key->second, browser_context->IsOffTheRecord());
49+
}
50+
51+
return std::nullopt;
52+
}
53+
54+
} // namespace
1755
#endif // BUILDFLAG(ENABLE_CONTAINERS)
1856

1957
#include <chrome/browser/ui/browser_tabrestore.cc>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
include_rules = [
2+
"+brave/components/containers/buildflags",
3+
"+brave/components/containers/content/browser",
4+
"+brave/components/containers/core/common",
5+
]
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/* Copyright (c) 2025 The Brave Authors. All rights reserved.
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
4+
* You can obtain one at https://mozilla.org/MPL/2.0/. */
5+
6+
#include "components/sessions/content/content_serialized_navigation_builder.h"
7+
8+
#include <string>
9+
10+
#include "brave/components/containers/buildflags/buildflags.h"
11+
12+
#if BUILDFLAG(ENABLE_CONTAINERS)
13+
#include "base/containers/map_util.h"
14+
#include "brave/components/containers/content/browser/session_utils.h"
15+
#include "brave/components/containers/core/common/features.h"
16+
#endif // BUILDFLAG(ENABLE_CONTAINERS)
17+
18+
#define FromNavigationEntry FromNavigationEntry_ChromiumImpl
19+
20+
#if BUILDFLAG(ENABLE_CONTAINERS)
21+
// If the SerializedNavigationEntry contains storage partition key information ,
22+
// we copy it into the NavigationEntry so it can be used later to create the
23+
// correct SiteInstance with the right StoragePartitionConfig.
24+
//
25+
// This happens AFTER ContentSerializedNavigationDriver::Sanitize() has already
26+
// parsed the virtual URL prefix and populated the storage_partition_key field.
27+
#define BRAVE_CONTENT_SERIALIZED_NAVIGATION_BUILDER_TO_NAVIGATION_ENTRY \
28+
if (base::FeatureList::IsEnabled(containers::features::kContainers) && \
29+
navigation->storage_partition_key().has_value()) { \
30+
entry->SetStoragePartitionKeyToRestore( \
31+
navigation->storage_partition_key().value()); \
32+
}
33+
#else
34+
#define BRAVE_CONTENT_SERIALIZED_NAVIGATION_BUILDER_TO_NAVIGATION_ENTRY
35+
#endif // BUILDFLAG(ENABLE_CONTAINERS)
36+
37+
#include <components/sessions/content/content_serialized_navigation_builder.cc>
38+
39+
#undef FromNavigationEntry
40+
#undef BRAVE_CONTENT_SERIALIZED_NAVIGATION_BUILDER_TO_NAVIGATION_ENTRY
41+
42+
namespace sessions {
43+
44+
// This method is called when saving a session (browser close, tab close, manual
45+
// session save, or sync). We override it to add storage partition key
46+
// information to the serialized data.
47+
//
48+
// The SerializedNavigationEntry will then be processed by
49+
// ContentSerializedNavigationDriver::GetSanitizedPageStateForPickle() which
50+
// adds the prefix to the PageState, and finally written to disk/sync with the
51+
// virtual_url_prefix embedded in the virtual_url field.
52+
SerializedNavigationEntry
53+
ContentSerializedNavigationBuilder::FromNavigationEntry(
54+
int index,
55+
content::NavigationEntry* entry,
56+
SerializationOptions serialization_options) {
57+
SerializedNavigationEntry navigation =
58+
FromNavigationEntry_ChromiumImpl(index, entry, serialization_options);
59+
60+
#if BUILDFLAG(ENABLE_CONTAINERS)
61+
if (base::FeatureList::IsEnabled(containers::features::kContainers)) {
62+
if (auto storage_partition_key_to_restore =
63+
entry->GetStoragePartitionKeyToRestore()) {
64+
// Generate the URL prefix (e.g., "containers+<uuid>:") if it's a valid
65+
// container storage partition key.
66+
if (auto url_prefix = containers::GetUrlPrefixForSessionPersistence(
67+
*storage_partition_key_to_restore)) {
68+
// Store both the prefix and the raw partition key in the
69+
// SerializedNavigationEntry. The prefix will be used by
70+
// GetSanitizedPageStateForPickle() to modify the PageState,
71+
// and both will be embedded in the virtual_url during
72+
// final serialization.
73+
navigation.set_virtual_url_prefix(*url_prefix);
74+
navigation.set_storage_partition_key(*storage_partition_key_to_restore);
75+
}
76+
}
77+
}
78+
#endif // BUILDFLAG(ENABLE_CONTAINERS)
79+
return navigation;
80+
}
81+
82+
} // namespace sessions
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* Copyright (c) 2025 The Brave Authors. All rights reserved.
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
4+
* You can obtain one at https://mozilla.org/MPL/2.0/. */
5+
6+
#ifndef BRAVE_CHROMIUM_SRC_COMPONENTS_SESSIONS_CONTENT_CONTENT_SERIALIZED_NAVIGATION_BUILDER_H_
7+
#define BRAVE_CHROMIUM_SRC_COMPONENTS_SESSIONS_CONTENT_CONTENT_SERIALIZED_NAVIGATION_BUILDER_H_
8+
9+
#define FromNavigationEntry(...) \
10+
FromNavigationEntry_ChromiumImpl(__VA_ARGS__); \
11+
static sessions::SerializedNavigationEntry FromNavigationEntry(__VA_ARGS__)
12+
13+
#include <components/sessions/content/content_serialized_navigation_builder.h> // IWYU pragma: export
14+
15+
#undef FromNavigationEntry
16+
17+
#endif // BRAVE_CHROMIUM_SRC_COMPONENTS_SESSIONS_CONTENT_CONTENT_SERIALIZED_NAVIGATION_BUILDER_H_

chromium_src/components/sessions/content/content_serialized_navigation_driver.cc

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,23 @@
88
#include <string>
99

1010
#include "base/containers/fixed_flat_set.h"
11+
#include "brave/components/containers/buildflags/buildflags.h"
1112
#include "components/sessions/core/serialized_navigation_entry.h"
1213
#include "content/public/common/url_constants.h"
1314

15+
#if BUILDFLAG(ENABLE_CONTAINERS)
16+
#include "brave/components/containers/content/browser/session_utils.h"
17+
#include "brave/components/containers/core/common/features.h"
18+
#endif // BUILDFLAG(ENABLE_CONTAINERS)
19+
1420
#define GetSanitizedPageStateForPickle \
1521
GetSanitizedPageStateForPickle_ChromiumImpl
22+
#define Sanitize Sanitize_ChromiumImpl
23+
1624
#include <components/sessions/content/content_serialized_navigation_driver.cc>
25+
1726
#undef GetSanitizedPageStateForPickle
27+
#undef Sanitize
1828

1929
namespace sessions {
2030

@@ -47,7 +57,75 @@ std::string ContentSerializedNavigationDriver::GetSanitizedPageStateForPickle(
4757
return std::string();
4858
}
4959

50-
return GetSanitizedPageStateForPickle_ChromiumImpl(navigation);
60+
std::string page_state =
61+
GetSanitizedPageStateForPickle_ChromiumImpl(navigation);
62+
#if BUILDFLAG(ENABLE_CONTAINERS)
63+
// If this is a container tab, add the virtual URL prefix to the PageState.
64+
// This ensures that if the session is restored with Containers disabled, the
65+
// browser won't be able to navigate to the URL (the scheme is invalid).
66+
if (base::FeatureList::IsEnabled(containers::features::kContainers) &&
67+
!navigation->virtual_url_prefix().empty() && !page_state.empty()) {
68+
blink::PageState page_state_obj =
69+
blink::PageState::CreateFromEncodedData(page_state);
70+
page_state = page_state_obj.PrefixTopURL(navigation->virtual_url_prefix())
71+
.ToEncodedData();
72+
}
73+
#endif // BUILDFLAG(ENABLE_CONTAINERS)
74+
return page_state;
75+
}
76+
77+
void ContentSerializedNavigationDriver::Sanitize(
78+
SerializedNavigationEntry* navigation) const {
79+
Sanitize_ChromiumImpl(navigation);
80+
81+
#if BUILDFLAG(ENABLE_CONTAINERS)
82+
// This method is called when loading a SerializedNavigationEntry from
83+
// disk/sync, BEFORE it's converted to a NavigationEntry. It's our opportunity
84+
// to detect container-encoded URLs and prepare them for restoration.
85+
//
86+
// This works for both local session restore and cross-device sync. When a
87+
// container tab is synced to a device with Containers disabled, the URL will
88+
// remain unhandleable (the prefix won't be removed).
89+
if (base::FeatureList::IsEnabled(containers::features::kContainers)) {
90+
std::pair<std::string, std::string> storage_partition_key;
91+
size_t url_prefix_length;
92+
93+
// Try to parse the virtual_url as a container-encoded URL.
94+
// If it has the format "containers+<uuid>:https://...", this returns
95+
// the original URL and extracts the partition key.
96+
if (auto restored_virtual_url =
97+
containers::RestoreStoragePartitionKeyFromUrl(
98+
navigation->virtual_url(), storage_partition_key,
99+
url_prefix_length)) {
100+
// Extract just the prefix part for PageState manipulation.
101+
// For "containers+work:https://example.com", this extracts
102+
// "containers+work:" (everything before the original URL).
103+
navigation->set_virtual_url_prefix(
104+
navigation->virtual_url().spec().substr(0, url_prefix_length));
105+
106+
// Update the virtual_url to the original URL without the prefix.
107+
// "containers+work:https://example.com" -> "https://example.com"
108+
// This is what will be used to create the NavigationEntry.
109+
navigation->set_virtual_url(*restored_virtual_url);
110+
111+
// Store the extracted storage partition key.
112+
// This will be used by ContentSerializedNavigationBuilder::
113+
// ToNavigationEntry() to set the correct StoragePartitionConfig
114+
// when creating the NavigationEntry.
115+
navigation->set_storage_partition_key(storage_partition_key);
116+
117+
// Remove the prefix from PageState too.
118+
if (!navigation->encoded_page_state().empty()) {
119+
blink::PageState page_state_obj =
120+
blink::PageState::CreateFromEncodedData(
121+
navigation->encoded_page_state());
122+
navigation->set_encoded_page_state(
123+
page_state_obj.RemoveTopURLPrefix(url_prefix_length)
124+
.ToEncodedData());
125+
}
126+
}
127+
}
128+
#endif // BUILDFLAG(ENABLE_CONTAINERS)
51129
}
52130

53131
} // namespace sessions

chromium_src/components/sessions/content/content_serialized_navigation_driver.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@
1111
#include "components/sessions/core/serialized_navigation_driver.h"
1212
#include "components/sessions/core/serialized_navigation_entry.h"
1313

14-
#define GetSanitizedPageStateForPickle \
15-
GetSanitizedPageStateForPickle_ChromiumImpl( \
16-
const SerializedNavigationEntry* navigation) const; \
17-
std::string GetSanitizedPageStateForPickle
14+
#define GetSanitizedPageStateForPickle(...) \
15+
GetSanitizedPageStateForPickle_ChromiumImpl(__VA_ARGS__) const; \
16+
std::string GetSanitizedPageStateForPickle(__VA_ARGS__)
17+
18+
#define Sanitize(...) \
19+
Sanitize_ChromiumImpl(__VA_ARGS__) const; \
20+
void Sanitize(__VA_ARGS__)
21+
1822
#include <components/sessions/content/content_serialized_navigation_driver.h> // IWYU pragma: export
23+
1924
#undef GetSanitizedPageStateForPickle
25+
#undef Sanitize
2026

2127
#endif // BRAVE_CHROMIUM_SRC_COMPONENTS_SESSIONS_CONTENT_CONTENT_SERIALIZED_NAVIGATION_DRIVER_H_
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/* Copyright (c) 2025 The Brave Authors. All rights reserved.
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
4+
* You can obtain one at https://mozilla.org/MPL/2.0/. */
5+
6+
#ifndef BRAVE_CHROMIUM_SRC_COMPONENTS_SESSIONS_CORE_SERIALIZED_NAVIGATION_ENTRY_H_
7+
#define BRAVE_CHROMIUM_SRC_COMPONENTS_SESSIONS_CORE_SERIALIZED_NAVIGATION_ENTRY_H_
8+
9+
#include <optional>
10+
#include <string>
11+
12+
// Extend SerializedNavigationEntry with two runtime fields to support storage
13+
// partition key persistence: storage_partition_key_ and virtual_url_prefix_.
14+
//
15+
// When saving a session, these fields are populated from the NavigationEntry's
16+
// storage partition information in
17+
// ContentSerializedNavigationBuilder::FromNavigationEntry() then written to:
18+
// - disk as part of the virtual_url field in
19+
// SerializedNavigationEntry::WriteToPickle().
20+
// - sync as part of the virtual_url field in SessionNavigationToSyncData().
21+
//
22+
// When restoring a session, these fields are restored from parsing the
23+
// rewriting of the virtual_url field in
24+
// ContentSerializedNavigationDriver::Sanitize().
25+
26+
#define set_http_status_code(...) \
27+
NotUsed() const; \
28+
\
29+
private: \
30+
/* Deserialized storage partition key for this navigation. */ \
31+
std::optional<std::pair<std::string, std::string>> storage_partition_key_; \
32+
\
33+
/* The URL prefix for encoding the storage partition key. */ \
34+
std::string virtual_url_prefix_; \
35+
\
36+
public: \
37+
const std::optional<std::pair<std::string, std::string>>& \
38+
storage_partition_key() const { \
39+
return storage_partition_key_; \
40+
} \
41+
void set_storage_partition_key( \
42+
const std::pair<std::string, std::string>& key) { \
43+
storage_partition_key_ = key; \
44+
} \
45+
const std::string& virtual_url_prefix() const { \
46+
return virtual_url_prefix_; \
47+
} \
48+
void set_virtual_url_prefix(const std::string& url_prefix) { \
49+
virtual_url_prefix_ = url_prefix; \
50+
} \
51+
void set_http_status_code(__VA_ARGS__)
52+
53+
#include <components/sessions/core/serialized_navigation_entry.h> // IWYU pragma: export
54+
55+
#undef set_http_status_code
56+
57+
#endif // BRAVE_CHROMIUM_SRC_COMPONENTS_SESSIONS_CORE_SERIALIZED_NAVIGATION_ENTRY_H_

0 commit comments

Comments
 (0)