Skip to content

Support contained tabs session restore and sync.#33313

Open
goodov wants to merge 6 commits intomasterfrom
containers-session-restore-sync
Open

Support contained tabs session restore and sync.#33313
goodov wants to merge 6 commits intomasterfrom
containers-session-restore-sync

Conversation

@goodov
Copy link
Member

@goodov goodov commented Jan 20, 2026

Implement session persistence for Containers.

Containers use StoragePartitionConfig for data isolation, but the config information wasn't persisted across sessions, causing tabs to lose isolation on restore.

This change encodes StoragePartitionConfig into URLs during serialization using a virtual prefix: containers+<uuid>:<actual_url>.

On serialization, the prefix is added to both SerializedNavigationEntry and PageState. On deserialization, StoragePartitionConfig is restored and the prefix removed from PageState (when feature is enabled).

When the feature is disabled, the prefix remains in PageState. Blink attempts to navigate to the unhandleable containers+uuid: scheme, resulting in a blank page rather than loading in the wrong partition.

This works with existing session/sync infrastructure and enables container tabs to survive browser restart, crash recovery, "reopen closed tab", and cross-device sync while maintaining storage isolation.

Please refer to the documentation https://github.com/brave/brave-core/blob/containers-session-restore-sync/docs/containers/session_persistence.md

Resolves brave/brave-browser#47306
Resolves brave/brave-browser#46353

@goodov goodov force-pushed the containers-session-restore-sync branch from 2242590 to 69e5237 Compare January 21, 2026 11:59
@goodov goodov force-pushed the containers-tab-ctx-menu-integration branch 2 times, most recently from fb2f727 to 6247a7a Compare January 27, 2026 13:41
Base automatically changed from containers-tab-ctx-menu-integration to master January 27, 2026 14:48
@goodov goodov force-pushed the containers-session-restore-sync branch from 69e5237 to 6af0f4f Compare January 28, 2026 08:12
@github-actions
Copy link
Contributor

github-actions bot commented Jan 28, 2026

📋 Code Owners Summary

32 file(s) changed, 19 with assigned owners

3 team(s) affected: @brave/chromium-src-reviewers, @brave/deps-reviewers, @brave/patch-reviewers


Owners and Their Files

@brave/chromium-src-reviewers — 12 file(s)

... and 7 more files

@brave/patch-reviewers — 5 file(s)

@brave/deps-reviewers — 2 file(s)

@github-actions
Copy link
Contributor

Chromium major version is behind target branch (144.0.7559.110 vs 145.0.7632.31). Please rebase.

@github-actions github-actions bot added the chromium-version-mismatch The Chromium version on the PR branch does not match the version on the target branch label Jan 29, 2026
@goodov goodov force-pushed the containers-session-restore-sync branch from 6af0f4f to 2571210 Compare February 3, 2026 16:17
@github-actions github-actions bot removed the chromium-version-mismatch The Chromium version on the PR branch does not match the version on the target branch label Feb 3, 2026
@goodov goodov force-pushed the containers-session-restore-sync branch 3 times, most recently from 9de458a to 48c57a0 Compare February 9, 2026 13:13
@goodov goodov force-pushed the containers-session-restore-sync branch 4 times, most recently from c74e22c to 5b4e565 Compare February 16, 2026 12:21
@goodov goodov marked this pull request as ready for review February 16, 2026 13:29
@goodov goodov requested review from a team as code owners February 16, 2026 13:29
@goodov
Copy link
Member Author

goodov commented Feb 17, 2026

For reviewers: please refer to a short doc that explains what's going on here in more details https://github.com/brave/brave-core/blob/containers-session-restore-sync/docs/containers/session_persistence.md

@simonhong
Copy link
Member

Sorry for late - I'll review tomorrow.

Copy link
Member

@simonhong simonhong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

++ with some trivial question. 👍🏼

@goodov goodov force-pushed the containers-session-restore-sync branch from 5b4e565 to 053645b Compare February 20, 2026 09:44
Copy link
Member

@fmarier fmarier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read through the design doc and skimmed through the changes. The approach sounds right to me.

One minor thing I'd suggest testing is restoring/syncing URLs that already have a virtual scheme (e.g. view-source:https://example.com) just to make sure the view-source: doesn't get lost in the serialization/deserialization process.

Copy link
Collaborator

@netzenbot netzenbot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review via brave-core-bot

@github-actions
Copy link
Contributor

Chromium major version is behind target branch (145.0.7632.109 vs 146.0.7680.32). Please rebase.

@github-actions github-actions bot added the chromium-version-mismatch The Chromium version on the PR branch does not match the version on the target branch label Feb 26, 2026
Copy link
Member

@bsclifton bsclifton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested it out; works great! Used the doc to try some of the tests. Turned on, loaded tabs into container. Turned off containers, made sure it uses the special prefix.

Left nit about one thing. Code LGTM! 😎👍

@goodov goodov force-pushed the containers-session-restore-sync branch from 053645b to 6ae80ad Compare February 27, 2026 17:03
@github-actions github-actions bot removed the chromium-version-mismatch The Chromium version on the PR branch does not match the version on the target branch label Feb 27, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2026

[puLL-Merge] - brave/brave-core@33313

Description

This PR implements session persistence for Brave Containers, allowing container tabs to be correctly restored after browser restart, "Reopen closed tab", and via Sync. The core approach encodes the container's StoragePartitionConfig (partition_domain + partition_name) into a virtual URL scheme prefix (e.g., containers+<uuid>:https://example.com) during serialization, and decodes it during deserialization. This ensures that when Containers is disabled, restored tabs show an unnavigable URL rather than silently mixing container storage with default storage.

Possible Issues

  1. Copyright year discrepancy: Many new files use 2026 as the copyright year (e.g., session_utils.cc, tab_restore_utils.cc, unit tests), while the feature is being developed in 2025. Some files correctly use 2025.

  2. DUMP_WILL_BE_CHECK_EQ in navigation_entry_impl.cc: The assertion in GetStoragePartitionKeyToRestore() uses DUMP_WILL_BE_CHECK_EQ which will eventually become a hard CHECK. If there's any scenario where the stored key and SiteInstance config diverge (e.g., due to redirects or race conditions), this could crash in the future.

  3. ContainersDisabledAfterRestoreBrowserTest feature list initialization: The constructor initializes feature_list_override_ conditionally based on the test name, but ContainersBrowserTest (the parent) likely also initializes a ScopedFeatureList enabling Containers. Having two ScopedFeatureList objects active simultaneously can lead to undefined behavior or test flakiness depending on initialization order.

  4. PageState manipulation fragility: PrefixTopURL and RemoveTopURLPrefix are custom additions to blink::PageState (files shown as new but diff is empty). If Blink's internal PageState serialization format changes, this could silently break. The approach of byte-level URL manipulation in a serialized blob is inherently fragile.

  5. Patch to chrome_serialized_navigation_driver.cc: The patch inserts ContentSerializedNavigationDriver::Sanitize(navigation) at the beginning of ChromeSerializedNavigationDriver::Sanitize(). If upstream changes the sanitization order or adds conflicting logic, this could cause subtle issues.

  6. Missing implementation for page_state.cc and page_state.h: The diff shows these files as new but their content is empty. The PrefixTopURL and RemoveTopURLPrefix methods are called but their implementations are not included in this PR.

Security Hotspots

  1. URL scheme parsing in RestoreStoragePartitionKeyFromUrl: The function splits on + and uses url.scheme() to extract partition info. If an attacker can craft a URL with a containers+<malicious-id>: scheme that passes IsContainersStoragePartitionKey validation, they could potentially access another container's storage partition. The UUID validation in IsContainersStoragePartitionKey is critical here.

  2. PageState manipulation: Prefixing/removing URL prefixes from serialized PageState blobs could introduce vulnerabilities if the prefix length calculation is off, potentially corrupting the blob or exposing data from adjacent memory in the serialized format.

  3. Storage partition isolation bypass: If GetStoragePartitionKeyToRestore() returns incorrect values due to timing issues between SiteInstance creation and NavigationEntry restoration, a tab could end up in the wrong storage partition, breaking container isolation.

Changes

Changes

browser/containers/containers_browsertest.cc

  • Added 5 new browser tests: GetStoragePartitionKeyToRestore, HotRestoreClosedContainerTab, PRE_CrossSiteNavigationPersistence/CrossSiteNavigationPersistence, PRE_NavigationHistoryPersistence/NavigationHistoryPersistence, PRE_MixedTabsPersistence/MixedTabsPersistence, and ContainersDisabledAfterRestoreBrowserTest for feature-disabled restore behavior.

chromium_src/components/sessions/content/content_serialized_navigation_builder.cc/.h

  • Overrides FromNavigationEntry to inject container storage partition key and URL prefix into SerializedNavigationEntry during serialization.
  • Adds BRAVE_CONTENT_SERIALIZED_NAVIGATION_BUILDER_TO_NAVIGATION_ENTRY macro to restore partition key during deserialization.

chromium_src/components/sessions/content/content_serialized_navigation_driver.cc/.h

  • Overrides GetSanitizedPageStateForPickle to prefix PageState URLs for container tabs.
  • Overrides Sanitize to detect and decode container-encoded URLs during deserialization.

chromium_src/components/sessions/core/serialized_navigation_entry.h

  • Extends SerializedNavigationEntry with storage_partition_key_ and virtual_url_prefix_ fields via macro injection.

chromium_src/content/browser/renderer_host/navigation_entry_impl.cc/.h and chromium_src/content/public/browser/navigation_entry.h

  • Adds SetStoragePartitionKeyToRestore/GetStoragePartitionKeyToRestore virtual methods to NavigationEntry and implements them in NavigationEntryImpl.

components/containers/content/browser/session_utils.cc/.h

  • New utilities for encoding (GetUrlPrefixForSessionPersistence) and decoding (RestoreStoragePartitionKeyFromUrl) container partition info in URLs.

components/containers/content/browser/tab_restore_utils.cc/.h

  • New utility GetStoragePartitionConfigToRestore to extract StoragePartitionConfig from serialized navigation entries during tab restore.

Patches (6 new patch files)

  • Patches to Chromium's BUILD.gn, chrome_serialized_navigation_driver.cc, content_serialized_navigation_builder.cc, serialized_navigation_entry.cc, and synced_session.cc to integrate the virtual URL prefix into serialization and sync flows.
sequenceDiagram
    participant Tab as Container Tab
    participant NE as NavigationEntry
    participant Builder as ContentSerializedNavigationBuilder
    participant Driver as ContentSerializedNavigationDriver
    participant SNE as SerializedNavigationEntry
    participant Disk as Disk/Sync
    participant Restore as Tab Restore

    Note over Tab,Disk: Serialization (Save)
    Tab->>NE: Has SiteInstance with StoragePartitionConfig
    NE->>Builder: FromNavigationEntry()
    Builder->>NE: GetStoragePartitionKeyToRestore()
    NE-->>Builder: {"containers", "<uuid>"}
    Builder->>SNE: set_virtual_url_prefix("containers+<uuid>:")
    Builder->>SNE: set_storage_partition_key({"containers","<uuid>"})
    SNE->>Driver: GetSanitizedPageStateForPickle()
    Driver->>SNE: PrefixTopURL("containers+<uuid>:")
    SNE->>Disk: WriteToPickle() with prefixed virtual_url

    Note over Disk,Tab: Deserialization (Restore)
    Disk->>SNE: Read from disk/sync
    SNE->>Driver: Sanitize()
    Driver->>Driver: RestoreStoragePartitionKeyFromUrl()
    Driver->>SNE: set_virtual_url(original URL)
    Driver->>SNE: set_storage_partition_key({"containers","<uuid>"})
    Driver->>SNE: RemoveTopURLPrefix from PageState
    SNE->>Builder: ToNavigationEntry()
    Builder->>NE: SetStoragePartitionKeyToRestore()
    NE->>Restore: GetStoragePartitionConfigToRestore()
    Restore->>Tab: Create tab with correct StoragePartitionConfig
Loading

return *this;
}

state.top.url_string = state.top.url_string->substr(prefix_length);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RemoveTopURLPrefix dereferences state.top.url_string without a null check. PrefixTopURL correctly guards with if (!state.top.url_string) { return *this; } — this method should do the same, since it processes deserialized PageState that may have a null URL. best practice

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Containers] Support containers in tab/session restore [Containers] Support containers in sync service

5 participants