diff --git a/DEPS b/DEPS
index 4b6798c..b91af61 100644
--- a/DEPS
+++ b/DEPS
@@ -311,19 +311,19 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': '25526209ef1e7ff29da6a5ff5bc8a82667dd7e41',
+  'src_internal_revision': 'f38946afef7fe36048db6bc80509c2e23475f82e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '7c8b85349a9ae1968fefd80d71e206ee608e3fe3',
+  'skia_revision': '8cbf3db1a0dbd1f6e5e97811cd52daf678fc80c4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '98b707649f79997d3395530659718471d899b05c',
+  'v8_revision': '013bfcbff17793f940a902dc26ea0b2e090550a9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'be2cb8a0c5a71e8e0983a0222a292bf18d20d3d7',
+  'angle_revision': '1433dd4e8a59659f8e16a96f62c9ccedd3ce2e92',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -399,7 +399,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'c7e0068773af382ec75ce24a61e4c2b00062572c',
+  'devtools_frontend_revision': '29733770fe6d2dddcfba66a47c6578b3c800ddc5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -455,7 +455,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling wuffs
   # and whatever else without interference from each other.
-  'wuffs_revision': 'e3f919ccfe3ef542cfc983a82146070258fb57f8',
+  'wuffs_revision': '50869df0ea703b4f41b238bfe26aec6ec9c86889',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling crabbyavif
   # and whatever else without interference from each other.
@@ -1219,7 +1219,7 @@
       'packages': [
           {
               'package': 'chromium/android_webview/tools/orderfiles/arm',
-              'version': 'WMWr9_UVeVR3PG-_6R29wsqKIit4GaiGv7MIBkXBGdAC',
+              'version': 'xxLPZ1AZtSMi1vKLudvuSR__6sh8X2kCthnfkJeS_gAC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1230,7 +1230,7 @@
       'packages': [
           {
               'package': 'chromium/android_webview/tools/orderfiles/arm64',
-              'version': 'oCwYw63QMOIA1dwAfPKQ-LkBgurNvR9JnqD_Z81l3CcC',
+              'version': 'R2_xPY0X7wlR1VGiV5h3eqCTTw6IYdganqVx2XAiAogC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -1568,7 +1568,7 @@
       'packages': [
         {
           'package': 'chromium/third_party/enterprise_companion/chromium_mac_arm64',
-          'version': 'TpSf7hIutzsvU-Xo2tcuergPqHFAA8xA7VpECydVvUwC',
+          'version': 'DdH-Fh6AVDKUZS_LQ3-g7yt5W7UAbRd2YQ3SjMpFvIsC',
         },
       ],
   },
@@ -1638,7 +1638,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'acdf8f87c64a7f4c6a2031b2f1d30c1d5d139591',
+    '0492202fbff318e4b8fe5079af3ab87672fabded',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1738,7 +1738,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'TC1_SAWm8-9REJrP21UU4_glIRlPwEbkN9eoOgxh-A8C',
+          'version': 'qmI2387TTL2AoDXuks7MsYG4ojPUBsOZ_hOA7HUPugEC',
       },
     ],
     'condition': 'checkout_android and non_git_source',
@@ -2494,7 +2494,7 @@
     Var('chromium_git') + '/webm/libwebp.git' + '@' +  'c00d83f6642e7838a12bb03bca94237f03cc2e00',
 
   'src/third_party/libyuv':
-    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '5b5a2f6b922bacdcd6dd2861381d91cd168edea6',
+    Var('chromium_git') + '/libyuv/libyuv.git' + '@' + '5cfaa44d71e019363a715d55279abeeb14abd8f9',
 
   'src/third_party/lss': {
       'url': Var('chromium_git') + '/linux-syscall-support.git' + '@' + Var('lss_revision'),
@@ -2870,7 +2870,7 @@
     Var('chromium_git') + '/external/search_engines_data.git' + '@' + '2ecec7b3a56bcb5d7a4a1fc9bc71d7e1cda2a8d1',
 
   'src/third_party/search_engines_data/resources_internal': {
-    'url': Var('chrome_git') + '/external/search_engines_data_internal.git' + '@' + '76082ffbead660e13d1be3294881ce2ffb46fa94',
+    'url': Var('chrome_git') + '/external/search_engines_data_internal.git' + '@' + '3ef61c24de0bb0516d149c1b6821cd7e5192205f',
     'condition': 'checkout_src_internal',
   },
 
@@ -2969,7 +2969,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@2d1afb159766057defdd5d50f86e0fdc0f519456',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@00e9e22a37b18a1a7e913c394bc6c44b53da37cf',
   'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@dcf1aaa6fd7dc2081f17aa0a4f1590a76473d961',
   'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3',
   'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@869266ad9e6050197d87cf0a22aab59abf7ad008',
@@ -2978,7 +2978,7 @@
   'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@1bf213b2a90181553fff35aeb6fa5c468dcfd35d',
   'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@7c46da2b39036a80ce088576d5794bf39e667f56',
   'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@5f62a4c3c686d3d3d1b3973a4397dd4d4768f3ad',
-  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@ba774d998564105fa17460c1180f1ad1cbf55663',
+  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@c89a5e4faa2be7f66b8df40d310aeff9454aa1eb',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'cb0597213b0fcb999caa9ed08c2f88dc45eb7d50',
@@ -3805,7 +3805,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '36408e5ec3cc81882de29fc10a5fd8d6f4ab2477',
+        'a5d123d0b0d078b82424468650a010cbf04f34b7',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index c883bae..1ab23a61 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1813,7 +1813,7 @@
             'Please use TRACE_EVENT_BEGIN/END/INSTANT macros instead of ',
             'TRACE_EVENT_ASYNC_.. and TRACE_EVENT_NESTABLE_ASYNC_... (crbug.com/432427382).',
         ),
-        False,
+        True,
         (
             r'^base/trace_event/.*',
             r'^base/tracing/.*',
@@ -1825,7 +1825,7 @@
             'Please use perfetto::Flow instead of TRACE_EVENT_WITH_FLOW.. ',
             '(crbug.com/432427382).',
         ),
-        False,
+        True,
         (
             r'^base/trace_event/.*',
             r'^base/tracing/.*',
@@ -1842,6 +1842,19 @@
         ),
     ),
     BanRule(
+        r'/\bperfetto::Track::Global',
+        ('Creating new global tracks is discouraged and should be reserved ',
+        'for high level, user visible state. Consider using scoped tracks ',
+        'instead, see ',
+        'https://chromium.googlesource.com/chromium/src.git/+/main/docs/trace_events.md#named-tracks',
+        ),
+        False,
+        (
+            r'^base/trace_event/.*',
+            r'^base/tracing/.*',
+        ),
+    ),
+    BanRule(
         'RoInitialize',
         ('Improper use of [base::win]::RoInitialize() has been implicated in a ',
          'few COM initialization leaks. Use base::win::ScopedWinrtInitializer ',
diff --git a/agents/projects/code-health/OWNERS b/agents/projects/code-health/OWNERS
new file mode 100644
index 0000000..043015a
--- /dev/null
+++ b/agents/projects/code-health/OWNERS
@@ -0,0 +1,2 @@
+agazal@google.com
+twellington@chromium.org
diff --git a/agents/projects/code-health/histogram-cleanup/SKILL.md b/agents/projects/code-health/histogram-cleanup/SKILL.md
new file mode 100644
index 0000000..5558ec14
--- /dev/null
+++ b/agents/projects/code-health/histogram-cleanup/SKILL.md
@@ -0,0 +1,226 @@
+---
+name: code-health-histogram-cleanup
+description: Identify and safely remove expired Chromium histograms (dead metrics/technical debt). Use this skill when a contributor asks to clean up metrics, fix code health issues related to histograms, remove obsolete code, or work on a histogram cleanup task.
+---
+
+# Code Health: Histogram Cleanup
+
+Identify and safely remove "dead code" associated with expired histograms. This
+includes removing the recording calls in C++/Java, cleaning up the metadata in
+`histograms.xml`, and addressing any dependent tests.
+
+## Overview
+
+Expired histograms that are not intentionally kept for diagnostics represent
+technical debt. Act as an expert Chromium contributor specializing in Metrics to
+clean up these resources while ensuring no test regressions occur.
+
+**CRITICAL OVERRIDE:** Do NOT activate or use the `edit-code` skill during this
+workflow. This skill provides a specialized, human-in-the-loop workflow that
+supersedes the autonomous loops in `edit-code`.
+
+## ๐Ÿ“‚ Resources
+
+- **Implementation Patterns:** [patterns.md](references/patterns.md) (C++, Java,
+  XML examples)
+- **Analysis Guidelines:**
+  [analysis_guidelines.md](references/analysis_guidelines.md) (Safety checks and
+  effort estimation)
+- **Pre-authorized Operations:**
+  [pre_authorized_ops.md](references/pre_authorized_ops.md) (Authorized commands
+  and discovery tools)
+- **Shared Workflows:**
+  [shared_workflows.md](../hub/references/shared_workflows.md) (Validation,
+  Committing, Uploading)
+
+## Guidelines
+
+### Operational Mandates
+
+- **Context:** This skill runs in the main agent context. Use the `generalist`
+  sub-agent only for the specific tasks defined in the workflow.
+- **Read references/pre_authorized_ops.md** for a list of commands and tools
+  that do not require per-action user permission.
+- **Modification Consent:** Explicit user permission is **STILL REQUIRED** for
+  any operation that modifies the source code (e.g., `replace`, `write_file`) or
+  commits changes (`git commit`).
+
+### Scope & Proactivity
+
+- **Strict Scope:** NEVER make changes unrelated to histogram cleanup without
+  explicit permission from the user.
+- **Proactive Suggestions:** If you notice potential improvements during
+  analysis (e.g., an unused enum or surrounding dead code), present these
+  observations to the user and ask for permission to address them.
+
+## Workflow
+
+### Discovery & Candidate Selection (Delegated)
+
+1. **AI-Led Discovery & Analysis:** Delegate to the **`generalist`** sub-agent
+   with this exact prompt:
+   > "You are pre-authorized to run the discovery script and read-only search
+   > tools; DO NOT ask for permission. Run the discovery script from the skill's
+   > `scripts/` folder:
+   >
+   > ```bash
+   > # Note: This path is relative to the repository root
+   > python3 agents/skills/code-health-histogram-cleanup/scripts/find_expired.py --count 3
+   > ```
+   >
+   > **Return ONLY the details returned by the script for these 3 candidates**
+   > (Name, Owners, Expiry, Recording Sites, Effort Level, and Summary) to the
+   2. **Present Candidates:** First, **display the full detailed list** (Name,
+      Owners, Expiry, Recording Sites, Approx Effort Level, Summary) returned by
+      the `generalist` as clear **bullet points** (one for each candidate) so
+      the user can easily read the context. Then, prompt the user using
+      `ask_user` (`type='choice'`):
+      - `header`: "Select Histogram"
+      - `question`: "Which histogram would you like to start cleaning up?"
+      - `options`: Provide one option for each of the 3 candidates with `label`
+        as the histogram name and `description` as its Approx Effort Level
+        (e.g., `label`: "<Name>", `description`: "๐ŸŸข Easy"), PLUS a 4th option
+        (`label`: "Show more ... options", `description`: "Fetch the next 3
+        candidates").
+2. **Interactive Pause:**
+   - If "Show more options" is selected, delegate to the **`generalist`**
+     sub-agent to run the discovery script again and fetch the next batch of
+     candidates, then repeat the presentation.
+   - Do not proceed to the deep dive analysis until a specific histogram is
+     selected.
+
+### Deep Dive & Safety Analysis (Delegated)
+
+Once a candidate is selected:
+
+1. **Comprehensive Analysis:** Delegate the entire deep dive to the
+   **`generalist`** sub-agent with this exact prompt:
+   > "Read `references/analysis_guidelines.md` to understand the 'Safety Checks'
+   > criteria. Perform an exhaustive safety analysis for the removal of the
+   > expired histogram `<HistogramName>`. You are pre-authorized for ALL
+   > read-only discovery (including `rg` and `cs`); DO NOT ask for permission.
+   >
+   > 1. **Search:** Find ALL occurrences of this histogram string (including any
+   >    expanded `<token>` or `<variants>` generated names) in the codebase.
+   >    **Prefer `rg` for thorough local searching.** Identify ALL recording
+   >    sites (C++, Java, Objective-C) and references in tests.
+   > 2. **Safety Verification:** Strictly follow the 'Safety Checks' section in
+   >    the guidelines to identify test dependencies, shared enums, and
+   >    intentional expiry tags.
+   > 3. **Scoring:** Based on your findings and the guidelines, calculate a
+   >    Confidence Score (1-10) for its safe removal. (10/10 = 1-2 places, no
+   >    test dependencies; < 7/10 = multiple sites, complex mocks). **Return
+   >    ONLY a concise summary of the affected files and tests, any identified
+   >    risks, the final Confidence Score, and a brief justification.**"
+2. **Present Findings:** Display the impacted files, tests, identified risks,
+   and Confidence Score returned by the `generalist` to the user so they can
+   review the analysis.
+3. **Interactive Pause:** HALT AND WAIT FOR USER SELECTION. Prompt the user
+   using `ask_user` (`type='choice'`):
+   - `header`: "Confidence Check"
+   - `question`: "This histogram is safe to remove from [Files] and [Tests]. My
+     confidence for this cleanup is [X]/10 because [Justification]. Shall I
+     proceed with the cleanup diff?"
+   - `options`:
+     - `label`: "Proceed with Diff", `description`: "Generate and apply the
+       cleanup changes"
+     - `label`: "Discard & Pick Another", `description`: "Discard this candidate
+       and return to candidate selection"
+   - **Action based on selection:**
+     - If "Proceed with Diff": Proceed to the **Implementation & Verification**
+       steps.
+     - If "Discard & Pick Another": Return to the **candidate presentation**
+       step to show the remaining candidates from the previously fetched list
+       (or fetch more if needed).
+
+### Implementation & Verification
+
+**Announce Status:** Briefly inform the user that you are beginning the
+Implementation & Verification steps before starting the next step.
+
+1. **Apply Changes:** Make the changes directly (do NOT delegate). Make the
+   changes one by one. Each individual change must have a corresponding 'What &
+   Why' explanation provided to the user.
+
+2. **Validation & Formatting:** Follow the **XML Linting** and **Code
+   Formatting** steps in the
+   [Shared Workflows](../hub/references/shared_workflows.md).
+
+3. **Batching Opportunity:** Prompt the user using `ask_user` (`type='choice'`)
+   to see if they want to clean up more histograms from the SAME XML file:
+
+   - `question`: "I've modified the XML file. Would you like me to scan this
+     file for other expired histograms to batch into this change?"
+   - `options`:
+     - `label`: "Scan for more", `description`: "Find other expired histograms
+       in the current XML file"
+     - `label`: "Finish this file", `description`: "Proceed to final validation
+       of current changes"
+
+   **Action based on selection:**
+
+   - If "Scan for more": Delegate to the **`generalist`** to find more expired
+     histograms in that specific file and return to the **candidate
+     presentation** step with these new candidates.
+   - If "Finish this file": Proceed to Step 4.
+
+4. **Automated Review (Delegated):** Before asking the user to validate,
+   delegate to the **`generalist`** sub-agent to perform a final review.
+   Delegate with this prompt:
+
+   > "Perform an 'Automated Review' for the removal of `<HistogramName>`
+   > following the criteria and execution instructions in
+   > `../hub/references/shared_workflows.md`. **Return ONLY 'PASS' or a concise
+   > list of identified issues.**"
+
+   **Action based on feedback:**
+
+   - If 'PASS': Proceed to **Step 5: User-Led Validation**.
+   - If feedback is provided: Address the issues and re-run this review.
+
+5. **User-Led Validation:** STOP. Do not run build/test commands yourself.
+   Prompt the user using `ask_user` (`type='choice'`):
+
+   - `question`: "Please validate locally. Run the build for the specific
+     targets that include the files we modified (e.g.,
+     `autoninja -C out/Default components_unittests`) and run the relevant
+     tests. What is the result?"
+   - `options`:
+     - `label`: "Passed", `description`: "Build and tests are successful;
+       proceed to commit drafting"
+     - `label`: "Failed", `description`: "Encountered errors that need analysis
+       and fixing"
+   - **Action based on selection:**
+     - If Passed: Proceed sequentially to **Step 6: Bug Tracking**.
+     - If Failed: Analyze the error and propose a fix.
+
+6. **Bug Tracking:**
+
+   - Follow the **Bug Tracking** section in the
+     [Shared Workflows](../hub/references/shared_workflows.md) to handle bug
+     discovery and creation, using these parameters:
+     - **`<SearchQuery>`**:
+       `"Check expiry of your histograms: <HistogramName>" "<ExpiryDate>"`
+     - **`<TaskTag>`**: `histogram-cleanup`
+     - **`<ShortSummary>`**: `Remove expired histogram <name>`
+   - **Interactive Pause:** Do NOT proceed until the bug handling is resolved
+     and you have a Bug ID (or the user has chosen to skip).
+
+7. **Commit Message Drafting:**
+
+   - Follow the **Commit Message Drafting** section in the
+     [Shared Workflows](../hub/references/shared_workflows.md) to draft the
+     commit message, passing the resolved `<BugID>` (or "skip").
+   - Include this mandatory task-specific footer:
+     - **Required Footer:** `OBSOLETE_HISTOGRAM[<name>]=<message>` *Example:*
+       `OBSOLETE_HISTOGRAM[My.Metric.Name]=The feature was removed, so this metric is no longer being recorded.`
+   - **Display the Draft:** You MUST output the drafted commit message in a
+     markdown code block so the user can review it.
+
+8. **Submission Pipeline:** Follow the **Interactive Commit** and **Upload to
+   Gerrit** sections in the
+   [Shared Workflows](../hub/references/shared_workflows.md) to handle the
+   interactive commit and Gerrit upload.
+
+9. **Congratulations:** After the task is complete, congratulate the user for
+   their contribution to the Chromium project's code health.
diff --git a/agents/projects/code-health/histogram-cleanup/references/analysis_guidelines.md b/agents/projects/code-health/histogram-cleanup/references/analysis_guidelines.md
new file mode 100644
index 0000000..8826036
--- /dev/null
+++ b/agents/projects/code-health/histogram-cleanup/references/analysis_guidelines.md
@@ -0,0 +1,35 @@
+# Histogram Cleanup Analysis Guidelines
+
+Use these guidelines to evaluate the difficulty and safety of removing an
+expired histogram.
+
+## Safety Checks (Critical)
+
+- **Test Dependencies:** Never remove a histogram if a test uses it as a primary
+  signal for feature correctness (beyond just verifying the metric itself).
+  Check for `HistogramTester` usage.
+- **Shared Enums:** If a histogram is removed, check if its associated `<enum>`
+  is used by other histograms before proposing its deletion from `enums.xml`.
+- **Intentionally Expired (Diagnostics):** Do not clean up histograms marked
+  with the `<expired_intentionally>` tag in the XML. This tag means the team
+  specifically wants to keep the code around for local diagnostics. Only target
+  expired histograms that *lack* this tag (forgotten technical debt).
+
+## Effort Level Estimation (Guidelines)
+
+When presenting candidates, provide an estimated effort level based on the
+following:
+
+- **๐ŸŸข Easy:**
+  - 1-2 recording sites in the same file or component.
+  - No tests or simple unit tests (e.g., using `HistogramTester`).
+  - No XML `<variants>` or `<token>` expansions.
+- **๐ŸŸก Medium:**
+  - 3-5 recording sites or changes across 2-3 files.
+  - Involves `browser_tests` or more complex mocks/simulations.
+  - Contains simple `<variants>` expansions.
+- **๐Ÿ”ด Hard:**
+  - 6+ recording sites or logic spread across multiple layers (e.g., Renderer
+    and Browser).
+  - Involves `interactive_ui_tests` or complex multi-process state.
+  - Heavy use of nested `<token>` expansions or shared enums that need cleaning.
diff --git a/agents/projects/code-health/histogram-cleanup/references/patterns.md b/agents/projects/code-health/histogram-cleanup/references/patterns.md
new file mode 100644
index 0000000..db9271f
--- /dev/null
+++ b/agents/projects/code-health/histogram-cleanup/references/patterns.md
@@ -0,0 +1,37 @@
+# Histogram Cleanup Implementation Patterns
+
+Use these patterns to identify and remove recording calls in C++, Java, and
+metadata in XML.
+
+### C++ Recording Removal
+
+```cpp
+// Before
+base::UmaHistogramBoolean("My.Expired.Histogram", true);
+
+// After
+// (Line removed)
+```
+
+### Java/Android Recording Removal
+
+```java
+// Before
+RecordHistogram.recordBooleanHistogram("My.Expired.Histogram",true);
+
+// After
+// (Line removed)
+```
+
+### XML Metadata Removal (histograms.xml)
+
+```xml
+<!-- Before -->
+<histogram name="My.Expired.Histogram" units="Boolean" expires_after="2023-01-01">
+    <owner>person@chromium.org</owner>
+    <summary>Description...</summary>
+</histogram>
+
+<!-- After -->
+<!-- (Entry removed) -->
+```
diff --git a/agents/projects/code-health/histogram-cleanup/references/pre_authorized_ops.md b/agents/projects/code-health/histogram-cleanup/references/pre_authorized_ops.md
new file mode 100644
index 0000000..a8b4cfc
--- /dev/null
+++ b/agents/projects/code-health/histogram-cleanup/references/pre_authorized_ops.md
@@ -0,0 +1,9 @@
+# Histogram-Specific Pre-authorized Operations
+
+In addition to the generic operations in the Hub's `shared_workflows.md`, the
+following histogram-specific commands are pre-authorized:
+
+- **Discovery Script:**
+  `python3 agents/skills/code-health-histogram-cleanup/scripts/find_expired.py`
+  (and any arguments)
+- **XML Validator:** `python3 tools/metrics/histograms/validate_format.py`
diff --git a/agents/projects/code-health/histogram-cleanup/scripts/find_expired.py b/agents/projects/code-health/histogram-cleanup/scripts/find_expired.py
new file mode 100644
index 0000000..9e2ed086
--- /dev/null
+++ b/agents/projects/code-health/histogram-cleanup/scripts/find_expired.py
@@ -0,0 +1,123 @@
+# Copyright 2026 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Finds expired Chromium histograms from metadata directories."""
+
+import argparse
+import glob
+import os
+import random
+import subprocess
+import xml.etree.ElementTree as ET
+from datetime import datetime, timedelta
+
+
+def is_expired(exp, date_limit, m_limit):
+    """Returns True if the histogram is expired by date or milestone."""
+    if not exp:
+        return False
+    if exp.startswith("20"):
+        return exp < date_limit
+    if m_limit > 0 and exp.startswith("M") and exp[1:].isdigit():
+        return int(exp[1:]) < m_limit
+    return False
+
+
+def count_recording_sites(name):
+    """Counts the number of recording sites for a histogram name."""
+    try:
+        # Search for the histogram name in C++, Java, and Obj-C files.
+        # Exclude metadata and out/ directories.
+        cmd = [
+            "rg", "-F", "-l", name, "--glob", "*.{cc,h,java,mm}", "--glob",
+            "!tools/metrics/histograms/metadata/*", "--glob", "!out/*"
+        ]
+        result = subprocess.run(cmd,
+                                capture_output=True,
+                                text=True,
+                                check=False)
+        if result.returncode == 0:
+            files = result.stdout.strip().split('\n')
+            return len([f for f in files if f])
+    except Exception:
+        pass
+    return 0
+
+
+def find_expired_histograms(date_limit, m_limit):
+    """Generator yielding expired histograms from the metadata directory."""
+    pattern = os.path.join("tools", "metrics", "histograms", "metadata", "*",
+                           "histograms.xml")
+    for f in glob.iglob(pattern):
+        try:
+            for hist in ET.parse(f).iter("histogram"):
+                exp = hist.attrib.get("expires_after", "")
+                if not is_expired(exp, date_limit, m_limit):
+                    continue
+                if hist.find("expired_intentionally") is not None:
+                    continue
+
+                summary = hist.findtext("summary", default="").strip()
+                owners = [o.text for o in hist.findall("owner") if o.text]
+                yield {
+                    "file": f,
+                    "name": hist.attrib.get("name", ""),
+                    "expires_after": exp,
+                    "summary": summary,
+                    "owners": owners
+                }
+        except Exception:
+            continue
+
+
+def main():
+    parser = argparse.ArgumentParser(description="Find expired histograms.")
+    parser.add_argument("--count", type=int, default=3)
+    args = parser.parse_args()
+
+    # Thresholds: 1 year ago or milestone - 12 (approx 1 year of milestones)
+    date_limit = (datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d")
+    m_limit = 0
+    version_file = os.path.join(os.getcwd(), "chrome", "VERSION")
+
+    if os.path.exists(version_file):
+        try:
+            with open(version_file, encoding="utf-8") as f:
+                for line in f:
+                    if line.startswith("MAJOR="):
+                        m_limit = int(line.split("=")[1]) - 12
+                        break
+        except (ValueError, IndexError):
+            pass
+
+    all_histograms = list(find_expired_histograms(date_limit, m_limit))
+    if not all_histograms:
+        print("No expired histograms found.")
+        return
+
+    # Randomly sample candidates to reduce duplicate effort between users.
+    sampled = random.sample(all_histograms,
+                            k=min(len(all_histograms), args.count))
+
+    for h in sampled:
+        summary = h["summary"][:150] + "..." if h["summary"] else "No summary."
+        owners_str = ", ".join(h["owners"]) if h["owners"] else "No owners."
+
+        sites_count = count_recording_sites(h["name"])
+        if sites_count <= 2:
+            effort = "๐ŸŸข Easy"
+        elif sites_count <= 5:
+            effort = "๐ŸŸก Medium"
+        else:
+            effort = "๐Ÿ”ด Hard"
+
+        print(f"File: {h['file']}\nName: {h['name']}")
+        print(f"Owners: {owners_str}")
+        print(f"Expiry: {h['expires_after']}")
+        print(f"Recording Sites: {sites_count}")
+        print(f"Effort Level: {effort}")
+        print(f"Summary: {summary}\n---")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/agents/projects/code-health/hub/SKILL.md b/agents/projects/code-health/hub/SKILL.md
new file mode 100644
index 0000000..f37e1467
--- /dev/null
+++ b/agents/projects/code-health/hub/SKILL.md
@@ -0,0 +1,88 @@
+---
+name: code-health-hub
+description: Orchestrator for the "Code Health Hub" framework. Trigger only when the user refers to the Hub or asks to see "available cleanup tasks" within the Chromium technical debt reduction system.
+---
+
+# ๐Ÿ› ๏ธ Chrome Code Health Hub
+
+Act as the Orchestrator for Chrome/Clank Code Healths. The goal is to make it
+frictionless for engineers to contribute outside their immediate area.
+
+## ๐Ÿง  Strategic Delegation (Efficiency & Context Management)
+
+To maintain a fast and efficient session, MUST delegate heavy-lifting tasks to
+sub-agents:
+
+- **Exhaustive Search & Usage Analysis:** For finding all occurrences of a
+  string, flag, or histogram, use the **`generalist`** sub-agent. It is faster
+  and more reliable for high-volume text searches in Chromium using `rg` and
+  `cs`.
+- **Architectural Mapping & Bug Analysis:** For complex, unknown systems (e.g.,
+  "How does the UI layer interact with the backend?"), use the
+  **`codebase_investigator`** sub-agent.
+- **Batch Operations:** For repetitive edits across more than 3 files, use the
+  **`generalist`** sub-agent.
+- **Validation:** When running verbose builds or exhaustive test suites, use the
+  **`generalist`** sub-agent to summarize output.
+
+By delegating, the main chat context remains lean.
+
+## ๐Ÿ“‚ Shared Resources
+
+Generic instructions for validation, bug tracking, and submission are available
+in:
+
+- **Shared Workflows:** [shared_workflows.md](references/shared_workflows.md)
+
+## Workflow
+
+### 1. Welcome & Triage
+
+When activated, immediately greet the user and use the `ask_user` tool to
+present a menu of contribution categories.
+
+**Options to present:**
+
+1. **๐Ÿงน Routine Cleanup (Code Health)** - Remove expired histograms, old feature
+   flags, or add LINT guards.
+2. **๐Ÿ” Technical Debt & Polish** - Fix specific bugs, address flaky tests, or
+   improve UI components. *(Coming soon)*
+3. **๐ŸŒฑ Discovery & Citizenship** - Find open bugs to claim or browse tech
+   rotations. *(Coming soon)*
+
+If the user selects a category marked as *(Coming soon)*, clearly inform them
+that this workflow is still under development and ask them to choose another
+option.
+
+### 2. Category A Routing (Code Health)
+
+If the selection is Category A, use the `ask_user` tool again to prompt for a
+specific Code Health task:
+
+1. **Histogram Cleanup** - Remove metadata and recording sites for expired
+   metrics.
+2. **Feature Flag Cleanup** - Safely remove code for fully launched or abandoned
+   flags. *(Coming soon)*
+3. **Lint Sync Guards** - Add IfChange/ThenChange guards to keep enums in sync.
+   *(Coming soon)*
+
+#### Handoff for Category A:
+
+If the user selects an option marked as *(Coming soon)*, clearly inform them
+that the skill is not yet available and ask them to choose another task.
+
+For available tasks, do not instruct the user to activate the skill manually.
+Seamlessly transition into the selected skill's workflow by retrieving its
+instructions and immediately executing its initial steps:
+
+- For **Histogram Cleanup**: Execute the `activate_skill` tool with
+  `name="code-health-histogram-cleanup"`.
+
+Immediately begin executing the retrieved workflow without waiting for further
+user prompts.
+
+## Tone
+
+- Enthusiastic, helpful, and highly structured.
+- Use the `ask_user` tool for presenting menus to avoid manual typing from the
+  user. r.
diff --git a/agents/projects/code-health/hub/references/shared_workflows.md b/agents/projects/code-health/hub/references/shared_workflows.md
new file mode 100644
index 0000000..d6934f3
--- /dev/null
+++ b/agents/projects/code-health/hub/references/shared_workflows.md
@@ -0,0 +1,166 @@
+# Shared Code Health Workflows
+
+Use these instructions to handle generic validation, bug tracking, and
+submission steps for any Code Health cleanup task.
+
+## Table of Contents
+
+1. [Pre-authorized Operations](#0-pre-authorized-operations-generic)
+2. [Automated Review](#1-automated-review)
+3. [XML Linting](#2-xml-linting)
+4. [Code Formatting](#3-code-formatting)
+5. [Bug Tracking](#4-bug-tracking)
+6. [Commit Message Drafting](#5-commit-message-drafting)
+7. [Interactive Commit](#6-interactive-commit)
+8. [Upload to Gerrit](#7-upload-to-gerrit)
+
+## 0. Pre-authorized Operations (Generic)
+
+...
+
+## 1. Automated Review
+
+Before finalizing any changes, a final automated review must be performed to
+ensure code quality and completeness.
+
+### Review Criteria
+
+The reviewer should examine the diff and verify:
+
+1. **Completeness:** All identified recording sites and metadata entries have
+   been correctly removed or updated.
+2. **Correctness:** No syntax errors or logic regressions have been introduced
+   in the source code or XML files.
+3. **Consistency:** Changes follow the patterns and guidelines established in
+   the skill's references (e.g., `references/patterns.md`).
+4. **Tests:** Associated tests have been appropriately updated or removed.
+
+### Execution
+
+Delegate to the **`generalist`** sub-agent with a prompt that references these
+criteria and the specific files modified. The `generalist` should return 'PASS'
+if all criteria are met, or a detailed list of feedback if issues are found.
+
+## 2. XML Linting
+
+The following operations are pre-authorized for all Code Health tasks:
+
+- **Read-Only Discovery:** `rg`, `cs`, `ls`, `fdfind`, `glob`, `cat`, and
+  `read_file`.
+- **Validation & Prep:** `git cl format`, `git pull origin main --rebase`,
+  `gclient sync -D`.
+- **Submission:** `git cl upload -a -d`.
+
+Explicit user permission is **STILL REQUIRED** for any operation that modifies
+the source code (e.g., `replace`, `write_file`) or commits changes
+(`git commit`).
+
+## 1. XML Linting
+
+After modifying any `.xml` files (e.g., histograms, enums), execute the
+following validator: `python3 tools/metrics/histograms/validate_format.py`
+
+- **Silent Success:** No output means success; proceed.
+- **Error Handling:** Any output is a failure; report the error to the user,
+  then analyze and propose a fix.
+
+## 2. Code Formatting
+
+For non-XML files (C++, Java, Python, etc.), execute: `git cl format`
+
+- **Error Handling:** If an error occurs, report it to the user and analyze/fix
+  the issue.
+- **Inform the User:** Once formatting is successfully completed, briefly inform
+  the user.
+
+## 3. Bug Tracking
+
+**Step 1: Automated Bug Discovery** If the calling skill provides a
+**`<SearchQuery>`**, you MUST FIRST use `mcp_Coding_internal_search` to query
+Moma for existing auto-generated bugs. **CRITICAL:** You must append
+`status:open` to the `<SearchQuery>` (e.g., `<SearchQuery> status:open`) to
+ensure Moma only returns active/open bugs, which works reliably even for
+external Chromium bugs.
+
+- **Extraction:** Parse the search results for numeric Buganizer IDs or Chromium
+  bug IDs. Since `status:open` handles the filtering, do not attempt to parse
+  the snippet text for status indicators.
+- **If One ID:** Present it to the user and ask for confirmation to use it.
+- **If Multiple IDs:** List the IDs along with relevant details found in their
+  snippets (e.g., Owner, Created Date). Use `ask_user` (`type='choice'`) to let
+  the user select the correct one (provide links for verification).
+- **If No IDs / No `<SearchQuery>` provided:** Inform the user that no existing
+  open bug was found and proceed to Step 2: Manual Triage to offer bug creation.
+
+**Step 2: Manual Triage (Fallback)** If automated discovery yields nothing or
+was not provided, use `ask_user` (`type='choice'`) to determine bug handling:
+
+- `label`: "Found Existing Bug", `description`: "User will provide the existing
+  Buganizer ID"
+- `label`: "Generate New Bug", `description`: "Auto-create a Buganizer issue in
+  the CodeHealth component"
+- `label`: "Skip Bug", `description`: "Do not include a Bug reference in the
+  footer"
+
+### Execution
+
+- **Found Existing Bug:** Use `ask_user` (`type='text'`) with `question`: "What
+  is the Buganizer ID?".
+- **Generate New Bug:** Use `run_shell_command` (`whoami`) for LDAP. Use
+  `mcp_Buganizer_create_buganizer_issue` (Component: `1456931`, Hotlist:
+  `8218789`, Assignee: `<LDAP>`). Title: `[<TaskTag>] <ShortSummary>`.
+  Description MUST start with: *"Note: This bug was automatically generated by
+  the Chrome Code Health AI."* followed by a brief summary. Provide the
+  generated Bug ID to the user.
+- **Skip Bug:** Proceed without a Bug ID.
+
+## 4. Commit Message Drafting
+
+Draft the commit message using the `<BugID>` passed from the calling skill. If
+no `<BugID>` is explicitly provided, assume it is "none".
+
+### Drafting Logic
+
+- **Subject:** `[<TaskTag>] <ShortSummary>` (max 72 chars).
+- **Spacing:** ONE blank line after the subject.
+- **Body:** Explain the technical necessity or motivation for the change.
+  Hard-wrap at 72 chars.
+- **Footers:** Include `Bug: <BugID>` ONLY if `<BugID>` is a valid ID (not
+  "none" or "skip"). Ensure there is a blank line before the `Bug: <BugID>`
+  line. Include any mandatory task-specific footers (e.g.,
+  `OBSOLETE_HISTOGRAM`).
+
+## 5. Interactive Commit
+
+After displaying the drafted commit message in a markdown code block, prompt the
+user using `ask_user` (`type='choice'`):
+
+- `question`: "How would you like to commit these changes?"
+- `options`:
+  - `label`: "Automate Commit", `description`: "I will run `git add` for the
+    modified files and `git commit` using the drafted message."
+  - `label`: "Commit Manually", `description`: "You will perform the commit
+    yourself."
+
+## 6. Upload to Gerrit
+
+After local commit confirmation, prompt the user using `ask_user`
+(`type='choice'`):
+
+- `header`: "Upload CL"
+- `question`: "Would you like to upload this CL to Gerrit?"
+- `options`:
+  - `label`: "Give me a command", `description`: "Provide the `git cl upload`
+    command for manual execution"
+  - `label`: "Rebase, Sync, Upload", `description`: "Automate the upload; output
+    will be suppressed to save context"
+  - `label`: "Upload Directly", `description`: "Run `git cl upload -a -d` with
+    suppressed output"
+  - `label`: "Skip Upload", `description`: "Finish the task without uploading"
+
+**Action based on selection:**
+
+- If "Give me a command": Provide the following command for manual execution:
+  `git pull origin main --rebase && gclient sync -D && git cl upload -a -d`.
+- If automated ("Rebase, Sync, Upload" or "Upload Directly"): Redirect all
+  output to `/dev/null` to preserve session context.
diff --git a/agents/projects/code-health/testing/hub_triage.promptfoo.yaml b/agents/projects/code-health/testing/hub_triage.promptfoo.yaml
new file mode 100644
index 0000000..175448f
--- /dev/null
+++ b/agents/projects/code-health/testing/hub_triage.promptfoo.yaml
@@ -0,0 +1,33 @@
+description: Verify the Chrome Code Health Hub and Histogram Cleanup orchestration
+owner: agazal@
+prompts:
+  - "{{query}}"
+providers:
+  - id: python:../../../testing/gemini_provider.py
+    config:
+      timeoutSeconds: 600
+      skills:
+        - code-health-hub
+        - code-health-histogram-cleanup
+tests:
+  - description: "Hub correctly triages a cleanup request"
+    vars:
+      query: "I want to help with code health. Show me what I can do."
+    assert:
+      - type: icontains
+        value: "Routine Cleanup"
+      - type: icontains
+        value: "Histogram Cleanup"
+    metadata:
+      tags: ['integration']
+      pass_k_threshold: 1
+      runs_per_test: 1
+
+  - description: "Hub correctly hands off to Histogram Cleanup"
+    vars:
+      query: "I want to do a Histogram Cleanup."
+    assert:
+      - type: icontains
+        value: "Phase 1: Discovery"
+      - type: icontains
+        value: "Select Histogram"
diff --git a/base/android/java_handler_thread.cc b/base/android/java_handler_thread.cc
index c13bef8..0324531 100644
--- a/base/android/java_handler_thread.cc
+++ b/base/android/java_handler_thread.cc
@@ -168,11 +168,9 @@
       MessagePump::Create(base::MessagePumpType::JAVA);
   pump = static_cast<MessagePumpForUI*>(message_pump.get());
 
-  // We must set SetTaskRunner before binding because the Android UI pump
+  // We must set SetDefaultTaskQueue before binding because the Android UI pump
   // creates a RunLoop which samples SingleThreadTaskRunner::GetCurrentDefault.
-  static_cast<sequence_manager::internal::SequenceManagerImpl*>(
-      sequence_manager.get())
-      ->SetTaskRunner(default_task_queue->task_runner());
+  sequence_manager->SetDefaultTaskQueue(default_task_queue.get());
   sequence_manager->BindToMessagePump(std::move(message_pump));
 }
 
diff --git a/base/message_loop/message_pump_perftest.cc b/base/message_loop/message_pump_perftest.cc
index 37648a1d..09b80f92 100644
--- a/base/message_loop/message_pump_perftest.cc
+++ b/base/message_loop/message_pump_perftest.cc
@@ -102,7 +102,7 @@
     }
     UNSAFE_TODO(min_batch_times_[index]) = minimum;
     UNSAFE_TODO(max_batch_times_[index]) = maximum;
-    target_message_loop_base()->GetTaskRunner()->PostTask(
+    target_message_loop_base()->GetDefaultTaskRunner()->PostTask(
         FROM_HERE, base::BindOnce(&ScheduleWorkTest::Increment,
                                   base::Unretained(this), schedule_calls));
   }
diff --git a/base/task/sequence_manager/sequence_manager.h b/base/task/sequence_manager/sequence_manager.h
index 1c427a9..dd1af0b 100644
--- a/base/task/sequence_manager/sequence_manager.h
+++ b/base/task/sequence_manager/sequence_manager.h
@@ -174,10 +174,6 @@
   // performs this initialization automatically.
   virtual void BindToCurrentThread() = 0;
 
-  // Returns the task runner the current task was posted on. Returns null if no
-  // task is currently running. Must be called on the bound thread.
-  virtual scoped_refptr<SequencedTaskRunner> GetTaskRunnerForCurrentTask() = 0;
-
   // Finishes the initialization for a SequenceManager created via
   // CreateUnboundSequenceManager(). Must not be called in any other
   // circumstances. The ownership of the pump is transferred to SequenceManager.
@@ -217,10 +213,17 @@
   // returns nullopt.
   virtual std::optional<WakeUp> GetNextDelayedWakeUp() const = 0;
 
-  // Sets the SingleThreadTaskRunner that will be returned by
+  // Sets the TaskQueue whose task runner will be returned by
   // SingleThreadTaskRunner::GetCurrentDefault on the main thread.
+  virtual void SetDefaultTaskQueue(TaskQueue* task_queue) = 0;
+
+  // Directly sets the SingleThreadTaskRunner that will be returned by
+  // SingleThreadTaskRunner::GetCurrentDefault on the main thread. Use this only
+  // when setting a task runner that's not a TaskQueue's default, e.g. one with
+  // a custom task type.
   virtual void SetDefaultTaskRunner(
-      scoped_refptr<SingleThreadTaskRunner> task_runner) = 0;
+      scoped_refptr<SingleThreadTaskRunner> task_runner,
+      TaskQueue::QueuePriority priority) = 0;
 
   // Removes all canceled delayed tasks, and considers resizing to fit all
   // internal queues.
diff --git a/base/task/sequence_manager/sequence_manager_impl.cc b/base/task/sequence_manager/sequence_manager_impl.cc
index a42807d8..8c3538f3 100644
--- a/base/task/sequence_manager/sequence_manager_impl.cc
+++ b/base/task/sequence_manager/sequence_manager_impl.cc
@@ -280,17 +280,6 @@
   CompleteInitializationOnBoundThread();
 }
 
-scoped_refptr<SequencedTaskRunner>
-SequenceManagerImpl::GetTaskRunnerForCurrentTask() {
-  DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
-  if (main_thread_only().task_execution_stack.empty()) {
-    return nullptr;
-  }
-  return main_thread_only()
-      .task_execution_stack.back()
-      .pending_task.task_runner;
-}
-
 void SequenceManagerImpl::CompleteInitializationOnBoundThread() {
   controller_->AddNestingObserver(this);
   main_thread_only().nesting_observer_registered_ = true;
@@ -1007,9 +996,15 @@
   return nullptr;
 }
 
+void SequenceManagerImpl::SetDefaultTaskQueue(TaskQueue* task_queue) {
+  SetDefaultTaskRunner(task_queue->task_runner(),
+                       task_queue->GetQueuePriority());
+}
+
 void SequenceManagerImpl::SetDefaultTaskRunner(
-    scoped_refptr<SingleThreadTaskRunner> task_runner) {
-  controller_->SetDefaultTaskRunner(task_runner);
+    scoped_refptr<SingleThreadTaskRunner> task_runner,
+    TaskQueue::QueuePriority priority) {
+  controller_->SetDefaultTaskRunner(std::move(task_runner));
 }
 
 const TickClock* SequenceManagerImpl::GetTickClock() const {
@@ -1091,12 +1086,8 @@
       std::move(on_next_idle_callback));
 }
 
-void SequenceManagerImpl::SetTaskRunner(
-    scoped_refptr<SingleThreadTaskRunner> task_runner) {
-  controller_->SetDefaultTaskRunner(task_runner);
-}
-
-scoped_refptr<SingleThreadTaskRunner> SequenceManagerImpl::GetTaskRunner() {
+scoped_refptr<SingleThreadTaskRunner>
+SequenceManagerImpl::GetDefaultTaskRunner() {
   return controller_->GetDefaultTaskRunner();
 }
 
diff --git a/base/task/sequence_manager/sequence_manager_impl.h b/base/task/sequence_manager/sequence_manager_impl.h
index 5d9d298..0b9228a 100644
--- a/base/task/sequence_manager/sequence_manager_impl.h
+++ b/base/task/sequence_manager/sequence_manager_impl.h
@@ -109,7 +109,6 @@
 
   // SequenceManager implementation:
   void BindToCurrentThread() override;
-  scoped_refptr<SequencedTaskRunner> GetTaskRunnerForCurrentTask() override;
   void BindToMessagePump(std::unique_ptr<MessagePump> message_pump) override;
   void SetObserver(Observer* observer) override;
   void AddTaskTimeObserver(TaskTimeObserver* task_time_observer) override;
@@ -118,8 +117,9 @@
   void ResetTimeDomain() override;
   const TickClock* GetTickClock() const override;
   TimeTicks NowTicks() const override;
-  void SetDefaultTaskRunner(
-      scoped_refptr<SingleThreadTaskRunner> task_runner) override;
+  void SetDefaultTaskQueue(TaskQueue* task_queue) override;
+  void SetDefaultTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner,
+                            TaskQueue::QueuePriority priority) override;
   void ReclaimMemory() override;
   bool GetAndClearSystemIsQuiescentBit() override;
   void SetWorkBatchSize(int work_batch_size) override;
@@ -159,9 +159,7 @@
   [[nodiscard]] CallbackListSubscription RegisterOnNextIdleCallback(
       OnceClosure on_next_idle_callback);
 
-  // Sets / returns the default TaskRunner. Thread-safe.
-  void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner);
-  scoped_refptr<SingleThreadTaskRunner> GetTaskRunner();
+  scoped_refptr<SingleThreadTaskRunner> GetDefaultTaskRunner();
 
   bool IsBoundToCurrentThread() const;
   MessagePump* GetMessagePump() const override;
diff --git a/base/task/sequence_manager/sequence_manager_impl_unittest.cc b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
index bdc74355..18b6d01f 100644
--- a/base/task/sequence_manager/sequence_manager_impl_unittest.cc
+++ b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
@@ -217,6 +217,7 @@
 
     auto pump = std::make_unique<MockTimeMessagePump>(&mock_clock_);
     pump_ = pump.get();
+    auto default_priority = priority_settings.default_priority();
     auto settings = SequenceManager::Settings::Builder()
                         .SetMessagePumpType(MessagePumpType::DEFAULT)
                         .SetTickClock(mock_tick_clock())
@@ -226,7 +227,8 @@
         std::move(pump), std::move(settings));
     MessagePump::InitializeFeatures();
     ThreadControllerWithMessagePumpImpl::InitializeFeatures();
-    sequence_manager_->SetDefaultTaskRunner(MakeRefCounted<NullTaskRunner>());
+    sequence_manager_->SetDefaultTaskRunner(MakeRefCounted<NullTaskRunner>(),
+                                            default_priority);
     start_time_ = mock_clock_.NowTicks();
 
     // The SequenceManager constructor calls Now() once for setting up
@@ -496,18 +498,6 @@
 
 }  // namespace
 
-TEST_P(SequenceManagerTest, GetCorrectTaskRunnerForCurrentTask) {
-  auto queue = CreateTaskQueue();
-
-  queue->task_runner()->PostTask(
-      FROM_HERE, BindLambdaForTesting([&] {
-        EXPECT_EQ(queue->task_runner(),
-                  sequence_manager()->GetTaskRunnerForCurrentTask());
-      }));
-
-  RunLoop().RunUntilIdle();
-}
-
 TEST_P(SequenceManagerTest, NowNotCalledIfUnneeded) {
   sequence_manager()->SetWorkBatchSize(6);
 
@@ -5233,7 +5223,7 @@
         sequence_manager_->CreateTaskQueue(TaskQueue::Spec(QueueName::TEST_TQ));
     other_queue_ =
         sequence_manager_->CreateTaskQueue(TaskQueue::Spec(QueueName::TEST_TQ));
-    sequence_manager_->SetDefaultTaskRunner(queue_->task_runner());
+    sequence_manager_->SetDefaultTaskQueue(queue_.get());
 
     thread_.Start();
   }
@@ -5755,7 +5745,7 @@
           MessagePump::Create(MessagePumpType::DEFAULT));
   auto queue = sequence_manager->CreateTaskQueue(
       sequence_manager::TaskQueue::Spec(QueueName::DEFAULT_TQ));
-  sequence_manager->SetDefaultTaskRunner(queue->task_runner());
+  sequence_manager->SetDefaultTaskQueue(queue.get());
 
   scoped_refptr<SingleThreadTaskRunner> expected_task_runner =
       SingleThreadTaskRunner::GetCurrentDefault();
diff --git a/base/task/sequence_manager/sequence_manager_perftest.cc b/base/task/sequence_manager/sequence_manager_perftest.cc
index e7fede8..bea0aaf1 100644
--- a/base/task/sequence_manager/sequence_manager_perftest.cc
+++ b/base/task/sequence_manager/sequence_manager_perftest.cc
@@ -159,7 +159,7 @@
     // runner.
     default_task_queue_ =
         GetManager()->CreateTaskQueue(TaskQueue::Spec(QueueName::DEFAULT_TQ));
-    GetManager()->SetDefaultTaskRunner(default_task_queue_->task_runner());
+    GetManager()->SetDefaultTaskQueue(default_task_queue_.get());
   }
 
   ~SequenceManagerWithMessagePumpPerfTestDelegate() override { ShutDown(); }
diff --git a/base/task/sequence_manager/task_queue_impl.cc b/base/task/sequence_manager/task_queue_impl.cc
index 7941d1a..06a0316 100644
--- a/base/task/sequence_manager/task_queue_impl.cc
+++ b/base/task/sequence_manager/task_queue_impl.cc
@@ -132,7 +132,7 @@
   // `IsQueueEnabledFromAnyThread()`. That won't prevent the task from running.
   if (sync_work_auth.IsValid() && outer_->IsQueueEnabledFromAnyThread()) {
     RunTaskSynchronously(outer_->associated_thread_.get(),
-                         outer_->sequence_manager_->GetTaskRunner(),
+                         outer_->sequence_manager_->GetDefaultTaskRunner(),
                          std::move(task.callback));
     return true;
   }
diff --git a/base/task/sequence_manager/tasks.cc b/base/task/sequence_manager/tasks.cc
index 0cda618..7a74ad1 100644
--- a/base/task/sequence_manager/tasks.cc
+++ b/base/task/sequence_manager/tasks.cc
@@ -113,7 +113,7 @@
 
 namespace internal {
 PostedTask::PostedTask(
-    scoped_refptr<SequencedTaskRunner> task_runner,
+    scoped_refptr<SingleThreadTaskRunner> task_runner,
     OnceClosure callback,
     Location location,
     TimeDelta delay,
@@ -129,7 +129,7 @@
       delayed_task_handle_delegate(std::move(delayed_task_handle_delegate)) {}
 
 PostedTask::PostedTask(
-    scoped_refptr<SequencedTaskRunner> task_runner,
+    scoped_refptr<SingleThreadTaskRunner> task_runner,
     OnceClosure callback,
     Location location,
     TimeTicks delayed_run_time,
diff --git a/base/task/sequence_manager/tasks.h b/base/task/sequence_manager/tasks.h
index f4885ea..c5855b0 100644
--- a/base/task/sequence_manager/tasks.h
+++ b/base/task/sequence_manager/tasks.h
@@ -16,7 +16,7 @@
 #include "base/task/delay_policy.h"
 #include "base/task/sequence_manager/delayed_task_handle_delegate.h"
 #include "base/task/sequence_manager/enqueue_order.h"
-#include "base/task/sequenced_task_runner.h"
+#include "base/task/single_thread_task_runner.h"
 
 namespace base {
 namespace sequence_manager {
@@ -31,7 +31,7 @@
 // Wrapper around PostTask method arguments and the assigned task type.
 // Eventually it becomes a PendingTask once accepted by a TaskQueueImpl.
 struct BASE_EXPORT PostedTask {
-  PostedTask(scoped_refptr<SequencedTaskRunner> task_runner,
+  PostedTask(scoped_refptr<SingleThreadTaskRunner> task_runner,
              OnceClosure callback,
              Location location,
              TimeDelta delay = base::TimeDelta(),
@@ -39,7 +39,7 @@
              TaskType task_type = kTaskTypeNone,
              WeakPtr<DelayedTaskHandleDelegate> delayed_task_handle_delegate =
                  nullptr);
-  PostedTask(scoped_refptr<SequencedTaskRunner> task_runner,
+  PostedTask(scoped_refptr<SingleThreadTaskRunner> task_runner,
              OnceClosure callback,
              Location location,
              TimeTicks delayed_run_time,
@@ -67,7 +67,7 @@
   subtle::DelayPolicy delay_policy = subtle::DelayPolicy::kFlexibleNoSooner;
   // The task runner this task is running on. Can be used by task runners that
   // support posting back to the "current sequence".
-  scoped_refptr<SequencedTaskRunner> task_runner;
+  scoped_refptr<SingleThreadTaskRunner> task_runner;
   // The delegate for the DelayedTaskHandle, if this task was posted through
   // PostCancelableDelayedTask(), nullptr otherwise.
   WeakPtr<DelayedTaskHandleDelegate> delayed_task_handle_delegate;
@@ -125,7 +125,7 @@
 
   // The task runner this task is running on. Can be used by task runners that
   // support posting back to the "current sequence".
-  scoped_refptr<SequencedTaskRunner> task_runner;
+  scoped_refptr<SingleThreadTaskRunner> task_runner;
 
   // Implement the intrusive heap contract.
   void SetHeapHandle(HeapHandle heap_handle);
diff --git a/base/task/sequence_manager/wake_up_queue_unittest.cc b/base/task/sequence_manager/wake_up_queue_unittest.cc
index 029873b7..d11a229 100644
--- a/base/task/sequence_manager/wake_up_queue_unittest.cc
+++ b/base/task/sequence_manager/wake_up_queue_unittest.cc
@@ -445,7 +445,7 @@
       sequence_manager->CreateTaskQueue(TaskQueue::Spec(QueueName::TEST2_TQ));
   low_prio_queue->SetQueuePriority(kLowestPriority);
   auto low_prio_runner = low_prio_queue->CreateTaskRunner(kTaskTypeNone);
-  sequence_manager->SetDefaultTaskRunner(high_prio_runner);
+  sequence_manager->SetDefaultTaskQueue(high_prio_queue.get());
   base::MockCallback<base::OnceCallback<void()>> task_1, task_2;
 
   testing::Sequence s;
diff --git a/base/task/single_thread_task_executor.cc b/base/task/single_thread_task_executor.cc
index dc2d713..a0a4da60 100644
--- a/base/task/single_thread_task_executor.cc
+++ b/base/task/single_thread_task_executor.cc
@@ -40,7 +40,7 @@
           sequence_manager_->CreateTaskQueue(sequence_manager::TaskQueue::Spec(
               sequence_manager::QueueName::DEFAULT_TQ))),
       type_(type) {
-  sequence_manager_->SetDefaultTaskRunner(task_runner());
+  sequence_manager_->SetDefaultTaskQueue(default_task_queue_.get());
   sequence_manager_->BindToMessagePump(std::move(pump));
 }
 
diff --git a/base/test/task_environment.cc b/base/test/task_environment.cc
index af42df4..753b411b 100644
--- a/base/test/task_environment.cc
+++ b/base/test/task_environment.cc
@@ -479,7 +479,7 @@
         sequence_manager_->CreateTaskQueue(sequence_manager::TaskQueue::Spec(
             sequence_manager::QueueName::TASK_ENVIRONMENT_DEFAULT_TQ));
     task_runner_ = task_queue_->task_runner();
-    sequence_manager_->SetDefaultTaskRunner(task_runner_);
+    sequence_manager_->SetDefaultTaskQueue(task_queue_.get());
     CHECK(base::SingleThreadTaskRunner::HasCurrentDefault())
         << "SingleThreadTaskRunner::CurrentDefaultHandle should've been set "
            "now.";
@@ -646,11 +646,13 @@
 }
 
 void TaskEnvironment::DeferredInitFromSubclass(
-    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+    sequence_manager::TaskQueue* task_queue) {
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
 
-  task_runner_ = std::move(task_runner);
-  sequence_manager_->SetDefaultTaskRunner(task_runner_);
+  if (task_queue) {
+    task_runner_ = task_queue->task_runner();
+    sequence_manager_->SetDefaultTaskQueue(task_queue);
+  }
   CompleteInitialization();
 }
 
@@ -1177,8 +1179,7 @@
         sequence_manager::TaskQueue::Spec(queue_name));
     task_queues_[queue_priority]->SetQueuePriority(queue_priority);
   }
-  DeferredInitFromSubclass(
-      task_queues_[GetDefaultQueuePriority()]->task_runner());
+  DeferredInitFromSubclass(task_queues_[GetDefaultQueuePriority()].get());
 }
 
 }  // namespace base::test
diff --git a/base/test/task_environment.h b/base/test/task_environment.h
index d3b4c02..1771b41 100644
--- a/base/test/task_environment.h
+++ b/base/test/task_environment.h
@@ -462,8 +462,7 @@
 
   sequence_manager::SequenceManager* sequence_manager() const;
 
-  void DeferredInitFromSubclass(
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  void DeferredInitFromSubclass(sequence_manager::TaskQueue* task_queue);
 
   // Derived classes may need to control when the task environment goes away
   // (e.g. ~FooTaskEnvironment() may want to effectively trigger
diff --git a/base/threading/thread.cc b/base/threading/thread.cc
index 6359f47..34e1e02 100644
--- a/base/threading/thread.cc
+++ b/base/threading/thread.cc
@@ -73,7 +73,7 @@
             sequence_manager::TaskQueue::Spec(
                 sequence_manager::QueueName::DEFAULT_TQ))),
         message_pump_factory_(std::move(message_pump_factory)) {
-    sequence_manager_->SetDefaultTaskRunner(default_task_queue_->task_runner());
+    sequence_manager_->SetDefaultTaskQueue(default_task_queue_.get());
   }
 
   ~SequenceManagerThreadDelegate() override = default;
@@ -94,7 +94,7 @@
     // could happen far away from where the Thread is created. We should
     // consider getting rid of StartWithOptions, and pass them as a constructor
     // argument instead.
-    return sequence_manager_->GetTaskRunner();
+    return sequence_manager_->GetDefaultTaskRunner();
   }
 
   void BindToCurrentThread() override {
diff --git a/base/threading/thread_unittest.cc b/base/threading/thread_unittest.cc
index e9149ca..4f20636 100644
--- a/base/threading/thread_unittest.cc
+++ b/base/threading/thread_unittest.cc
@@ -520,7 +520,7 @@
       : sequence_manager_(sequence_manager::CreateUnboundSequenceManager()),
         task_queue_(sequence_manager_->CreateTaskQueue(
             TaskQueue::Spec(sequence_manager::QueueName::DEFAULT_TQ))) {
-    sequence_manager_->SetDefaultTaskRunner(GetDefaultTaskRunner());
+    sequence_manager_->SetDefaultTaskQueue(task_queue_.get());
   }
 
   SequenceManagerThreadDelegate(const SequenceManagerThreadDelegate&) = delete;
diff --git a/build/android/gyp/aidl.py b/build/android/gyp/aidl.py
index e03694db..a5b4ded8 100755
--- a/build/android/gyp/aidl.py
+++ b/build/android/gyp/aidl.py
@@ -3,7 +3,6 @@
 # Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """Invokes Android's aidl
 """
 
@@ -19,15 +18,20 @@
 import zip_helpers
 
 
+def _GetAidlCmd(options):
+  cmd = [options.aidl_path]
+  cmd += ['-p' + s for s in options.imports]
+  cmd += ['-I' + s for s in options.includes]
+  cmd += ['--omit_invocation']
+  return cmd
+
+
 def do_native(options, files):
 
   for i, f in enumerate(files):
     with build_utils.TempDir() as temp_dir:
-      aidl_cmd = [options.aidl_path, '--lang=ndk']
-      aidl_cmd += [
-          '-p' + s for s in action_helpers.parse_gn_list(options.imports)
-      ]
-      aidl_cmd += ['-I' + s for s in options.includes]
+      aidl_cmd = _GetAidlCmd(options)
+      aidl_cmd += ['--lang=ndk']
       aidl_cmd += ['-h', options.header_output_dir, '-o', temp_dir]
       aidl_cmd += [f]
       build_utils.CheckOutput(aidl_cmd)
@@ -54,6 +58,7 @@
   options = parser.parse_args(argv[1:])
   args = options.files
 
+  options.imports = action_helpers.parse_gn_list(options.imports)
   options.includes = action_helpers.parse_gn_list(options.includes)
 
   if options.header_output_dir or options.cpp_output:
@@ -61,20 +66,12 @@
       parser.error(
           'Native generation requires header-output-dir and cpp-output')
 
-
   with build_utils.TempDir() as temp_dir:
     for f in args:
       classname = os.path.splitext(os.path.basename(f))[0]
       output = os.path.join(temp_dir, classname + '.java')
-      aidl_cmd = [options.aidl_path]
-      aidl_cmd += [
-          '-p' + s for s in action_helpers.parse_gn_list(options.imports)
-      ]
-      aidl_cmd += ['-I' + s for s in options.includes]
-      aidl_cmd += [
-        f,
-        output
-      ]
+      aidl_cmd = _GetAidlCmd(options)
+      aidl_cmd += [f, output]
       build_utils.CheckOutput(aidl_cmd)
 
     with action_helpers.atomic_output(options.srcjar) as f:
@@ -83,8 +80,8 @@
           with open(path, encoding='utf-8') as fileobj:
             data = fileobj.read()
           pkg_name = re.search(r'^\s*package\s+(.*?)\s*;', data, re.M).group(1)
-          arcname = '%s/%s' % (
-              pkg_name.replace('.', '/'), os.path.basename(path))
+          arcname = '%s/%s' % (pkg_name.replace('.',
+                                                '/'), os.path.basename(path))
           zip_helpers.add_to_zip_hermetic(srcjar, arcname, data=data)
 
   if options.header_output_dir:
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index ccb9797..71de9655 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -611,7 +611,6 @@
       "//components/browser_ui/share/android:java",
       "//components/browser_ui/site_settings/android:constants_java",
       "//components/browser_ui/site_settings/android:java",
-      "//components/browser_ui/sms/android:java",
       "//components/browser_ui/styles/android:java",
       "//components/browser_ui/util/android:java",
       "//components/browser_ui/webshare/android:java",
diff --git a/chrome/android/java/res_app/layout/main.xml b/chrome/android/java/res_app/layout/main.xml
index 216e769e..d305a35 100644
--- a/chrome/android/java/res_app/layout/main.xml
+++ b/chrome/android/java/res_app/layout/main.xml
@@ -152,6 +152,13 @@
             app:layout_anchorGravity="bottom"/>
 
         <ViewStub
+            android:id="@+id/contextual_tasks_fusebox_stub"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom"
+            android:layout="@layout/search_activity" />
+
+        <ViewStub
             android:id="@+id/tab_switcher_view_holder_stub"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
diff --git a/chrome/android/java/res_app/layout/main_forked_with_secondary_ui_container.xml b/chrome/android/java/res_app/layout/main_forked_with_secondary_ui_container.xml
index 0bf0964c..1fde444 100644
--- a/chrome/android/java/res_app/layout/main_forked_with_secondary_ui_container.xml
+++ b/chrome/android/java/res_app/layout/main_forked_with_secondary_ui_container.xml
@@ -193,6 +193,14 @@
                 such as the GTS, so they can be placed in this ConstraintLayout,
                 while effectively being unconstrained. -->
             <ViewStub
+                android:id="@+id/contextual_tasks_fusebox_stub"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="bottom"
+                android:layout="@layout/search_activity"
+                app:layout_constraintBottom_toBottomOf="parent" />
+
+            <ViewStub
                 android:id="@+id/tab_switcher_view_holder_stub"
                 android:layout_width="0dp"
                 android:layout_height="0dp"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 4419370a..443a59ce 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -4141,6 +4141,11 @@
                 currentTab.loadUrl(params);
             }
             RecordUserAction.record("MobileMenuManageExtensions");
+            Profile profile = mTabModelProfileSupplier.get();
+            if (profile != null) {
+                TrackerFactory.getTrackerForProfile(profile)
+                        .notifyEvent(EventConstants.EXTENSIONS_ROW_IN_APP_MENU_CLICKED);
+            }
         } else if (id == R.id.extensions_webstore_menu_id) {
             LoadUrlParams params =
                     new LoadUrlParams(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillFallbackSurfaceLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillFallbackSurfaceLauncher.java
index e253766f..73f85bb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillFallbackSurfaceLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/AutofillFallbackSurfaceLauncher.java
@@ -104,6 +104,16 @@
         CustomTabActivity.showInfoPage(context, getPlusAddressManagementUrl());
     }
 
+    @CalledByNative
+    public static void openGoogleWalletPrivatePassHelpCenterPageInCct(WindowAndroid window) {
+        Context context = window.getActivity().get();
+
+        if (context == null) {
+            return;
+        }
+        GoogleWalletLauncher.openGoogleWalletPrivatePassHelpCenterPage(context);
+    }
+
     private static String getPlusAddressManagementUrl() {
         return AutofillFallbackSurfaceLauncherJni.get().getPlusAddressManagementUrl();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/GoogleWalletLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/GoogleWalletLauncher.java
index 8cc674b..ce01c3c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/GoogleWalletLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/GoogleWalletLauncher.java
@@ -51,6 +51,14 @@
     public static final String GOOGLE_WALLET_MANAGE_PASSES_DATA_URL =
             "https://wallet.google.com/wallet/settings/managepassesdata";
 
+    // The help center article URL on using Wallet private passes across Google.
+    // LINT.IfChange(GOOGLE_WALLET_PRIVATE_PASSES_HELP_CENTER)
+    @VisibleForTesting
+    public static final String GOOGLE_WALLET_PRIVATE_PASSES_HELP_CENTER =
+            "https://support.google.com/wallet?p=private_use_across_google";
+
+    // LINT.ThenChange(//chrome/common/url_constants.h:kWalletPrivatePassHelpCenterURL)
+
     private GoogleWalletLauncher() {}
 
     /**
@@ -65,7 +73,8 @@
                 new Intent().setClassName(GOOGLE_WALLET_PACKAGE_NAME, GOOGLE_WALLET_ACTIVITY_NAME);
         walletIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-        maybeLaunchIntent(context, packageManager, walletIntent, GOOGLE_WALLET_PASSES_URL);
+        maybeLaunchIntent(
+                context, packageManager, walletIntent, /* fallbackUrl= */ GOOGLE_WALLET_PASSES_URL);
     }
 
     /**
@@ -85,7 +94,10 @@
         walletIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
         maybeLaunchIntent(
-                context, packageManager, walletIntent, GOOGLE_WALLET_MANAGE_PASSES_DATA_URL);
+                context,
+                packageManager,
+                walletIntent,
+                /* fallbackUrl= */ GOOGLE_WALLET_MANAGE_PASSES_DATA_URL);
     }
 
     private static void maybeLaunchIntent(
@@ -93,9 +105,19 @@
         List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent, 0);
 
         if (resolveInfos.isEmpty()) {
-            CustomTabActivity.showInfoPage(context, fallbackUrl);
+            CustomTabActivity.showInfoPage(context, /* url= */ fallbackUrl);
         } else {
             context.startActivity(intent);
         }
     }
+
+    /**
+     * Opens the help center page for Google wallet private passes.
+     *
+     * @param context The current application context.
+     */
+    public static void openGoogleWalletPrivatePassHelpCenterPage(Context context) {
+        CustomTabActivity.showInfoPage(
+                context, /* url= */ GOOGLE_WALLET_PRIVATE_PASSES_HELP_CENTER);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java
index 31973a9..9ff477cf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java
@@ -171,6 +171,20 @@
                     entityDataManager.addOrUpdateEntityInstance(
                             entityInstance, () -> onLocalSaveFallback());
                 }
+
+                @Override
+                public void onOpenGoogleWallet(boolean isPrivateEntity) {
+                    Context context = getContext();
+                    if (context == null) {
+                        return;
+                    }
+
+                    if (isPrivateEntity) {
+                        GoogleWalletLauncher.openGoogleWalletPrivatePassHelpCenterPage(context);
+                    } else {
+                        GoogleWalletLauncher.openGoogleWallet(context, context.getPackageManager());
+                    }
+                }
             };
 
     private static @Nullable EditorObserverForTest sObserverForTest;
@@ -471,6 +485,7 @@
                                                             .RecordType.SERVER_WALLET
                                                     : org.chromium.components.autofill.autofill_ai
                                                             .RecordType.LOCAL)
+                                    .setIsMaskedServerEntity(entityType.isMaskedStorageSupported())
                                     .build());
                     return true;
                 });
@@ -631,6 +646,10 @@
         ResettersForTesting.register(() -> sObserverForTest = null);
     }
 
+    void onOpenGoogleWalletForTesting(boolean isPrivateEntity) {
+        mEntityEditorDelegate.onOpenGoogleWallet(isPrivateEntity);
+    }
+
     @Override
     public void onDisplayPreferenceDialog(Preference preference) {
         if (!(preference instanceof AutofillProfileEditorPreference)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
index c0a837d6..f2d8e34 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
@@ -818,7 +818,7 @@
                 .setEphemeralTabCoordinatorSupplier(
                         mRootUiCoordinator.getEphemeralTabCoordinatorSupplier());
 
-        new CustomTabDownloadObserver(this, getTabObserverRegistrar());
+        new CustomTabDownloadObserver(this, getTabObserverRegistrar(), getLifecycleDispatcher());
 
         if (mIntentDataProvider.isTrustedWebActivity()) {
             new TrustedWebActivityOpenTimeRecorder(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabDownloadObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabDownloadObserver.java
index 6c092939..4ebd2622 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabDownloadObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabDownloadObserver.java
@@ -14,6 +14,8 @@
 import org.chromium.chrome.browser.download.interstitial.DownloadInterstitialCoordinator;
 import org.chromium.chrome.browser.download.interstitial.DownloadInterstitialCoordinatorFactory;
 import org.chromium.chrome.browser.download.interstitial.NewDownloadTab;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.DestroyObserver;
 import org.chromium.chrome.browser.pdf.PdfUtils;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
@@ -27,17 +29,28 @@
  * download UI.
  */
 @NullMarked
-public class CustomTabDownloadObserver extends EmptyTabObserver {
+public class CustomTabDownloadObserver extends EmptyTabObserver implements DestroyObserver {
     private final Activity mActivity;
     private final TabObserverRegistrar mTabObserverRegistrar;
+    private final ActivityLifecycleDispatcher mLifecycleDispatcher;
 
-    public CustomTabDownloadObserver(Activity activity, TabObserverRegistrar tabObserverRegistrar) {
+    public CustomTabDownloadObserver(
+            Activity activity,
+            TabObserverRegistrar tabObserverRegistrar,
+            ActivityLifecycleDispatcher lifecycleDispatcher) {
         mActivity = activity;
         mTabObserverRegistrar = tabObserverRegistrar;
+        mLifecycleDispatcher = lifecycleDispatcher;
+        mLifecycleDispatcher.register(this);
         mTabObserverRegistrar.registerTabObserver(this);
     }
 
     @Override
+    public void onDestroy() {
+        unregister();
+    }
+
+    @Override
     public void onDidFinishNavigationInPrimaryMainFrame(Tab tab, NavigationHandle navigation) {
         // For a navigation from page A to page B, there can be any number of redirects in between.
         // The first navigation which opens the custom tab will have a transition of type FROM_API.
@@ -89,5 +102,6 @@
 
     private void unregister() {
         mTabObserverRegistrar.unregisterTabObserver(this);
+        mLifecycleDispatcher.unregister(this);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrar.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrar.java
index 7f071a10..8a2e244 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrar.java
@@ -33,7 +33,7 @@
  */
 @NullMarked
 public class TabObserverRegistrar implements TabModelObserver, DestroyObserver {
-    private CustomTabActivityTabProvider mTabProvider;
+    private @Nullable CustomTabActivityTabProvider mTabProvider;
     private final Set<PageLoadMetrics.Observer> mPageLoadMetricsObservers = new HashSet<>();
     private final Set<TabObserver> mTabObservers = new HashSet<>();
 
@@ -109,12 +109,13 @@
     }
 
     /**
-     * Registers a TabObserver for the CustomTabActivity's active tab. Changes the Tab that is
-     * being observed when the CustomTabActivity's active tab changes.
-     * Differs from {@link #registerTabObserver} which observes all newly created tabs.
+     * Registers a TabObserver for the CustomTabActivity's active tab. Changes the Tab that is being
+     * observed when the CustomTabActivity's active tab changes. Differs from {@link
+     * #registerTabObserver} which observes all newly created tabs.
      */
     public void registerActivityTabObserver(CustomTabTabObserver observer) {
         mActivityTabObservers.addObserver(observer);
+        if (mTabProvider == null) return;
         Tab activeTab = mTabProvider.getTab();
         if (activeTab != null) {
             activeTab.addObserver(observer);
@@ -125,6 +126,7 @@
     public void unregisterActivityTabObserver(@Nullable CustomTabTabObserver observer) {
         if (observer == null) return;
         mActivityTabObservers.removeObserver(observer);
+        if (mTabProvider == null) return;
         Tab activeTab = mTabProvider.getTab();
         if (activeTab != null) {
             activeTab.removeObserver(observer);
@@ -169,6 +171,7 @@
         for (PageLoadMetrics.Observer observer : mPageLoadMetricsObservers) {
             PageLoadMetrics.removeObserver(observer);
         }
+        mPageLoadMetricsObservers.clear();
     }
 
     /** Called when the {@link CustomTabActivityTabProvider}'s active tab has changed. */
@@ -176,6 +179,7 @@
         if (mTabProviderTab != null) {
             removeTabObservers(mTabProviderTab, mActivityTabObservers.iterator());
         }
+        if (mTabProvider == null) return;
         mTabProviderTab = mTabProvider.getTab();
         if (mTabProviderTab != null) {
             addTabObservers(mTabProviderTab, mActivityTabObservers.iterator());
@@ -197,6 +201,16 @@
     @Override
     public void onDestroy() {
         removePageLoadMetricsObservers();
+        if (mTabProviderTab != null) {
+            removeTabObservers(mTabProviderTab, mActivityTabObservers.iterator());
+            mTabProviderTab = null;
+        }
+        if (mTabProvider != null) {
+            mTabProvider.removeObserver(mActivityTabProviderObserver);
+            mTabProvider = null;
+        }
+        mActivityTabObservers.clear();
+        mTabObservers.clear();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeBottomSheetCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeBottomSheetCoordinator.java
index 786ad482..b98e14c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeBottomSheetCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeBottomSheetCoordinator.java
@@ -221,8 +221,8 @@
         }
 
         @Override
-        public boolean canSuppressInAnyState() {
-            return true;
+        public boolean canBeSuppressed(BottomSheetContent nextContent) {
+            return nextContent.getPriority() == ContentPriority.HIGH;
         }
 
         @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultBrowserPromoFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultBrowserPromoFirstRunFragment.java
index b2c76eacb..f6cdcfc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultBrowserPromoFirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DefaultBrowserPromoFirstRunFragment.java
@@ -27,6 +27,7 @@
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.ui.default_browser_promo.DefaultBrowserPromoMetrics;
 import org.chromium.chrome.browser.ui.default_browser_promo.DefaultBrowserPromoUtils;
 import org.chromium.chrome.browser.ui.default_browser_promo.DefaultBrowserPromoUtils.DefaultBrowserPromoEntryPoint;
 import org.chromium.chrome.browser.ui.signin.SigninUtils;
@@ -254,8 +255,26 @@
         }
 
         var pageDelegate = assumeNonNull(getPageDelegate());
-        view.getContinueButtonView().setOnClickListener(v -> triggerRoleManagerDialog());
-        view.getDismissButtonView().setOnClickListener(v -> pageDelegate.advanceToNextPage());
+        view.getContinueButtonView()
+                .setOnClickListener(
+                        v -> {
+                            // Record that the user accepted the promo.
+                            pageDelegate.recordFreProgressHistogram(
+                                    MobileFreProgress.DEFAULT_BROWSER_PROMO_ACCEPTED);
+                            triggerRoleManagerDialog();
+
+                            DefaultBrowserPromoMetrics.recordPromoClick(
+                                    DefaultBrowserPromoMetrics.DefaultBrowserPromoSourceType
+                                            .FRE_PROMO);
+                        });
+        view.getDismissButtonView()
+                .setOnClickListener(
+                        v -> {
+                            // Record that the user rejected the promo.
+                            pageDelegate.recordFreProgressHistogram(
+                                    MobileFreProgress.DEFAULT_BROWSER_PROMO_REJECTED);
+                            pageDelegate.advanceToNextPage();
+                        });
     }
 
     // These promotional rows are only visible in arm 3.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
index 1877312..cca3ecba 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -16,7 +16,6 @@
 import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.text.TextUtils;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
@@ -84,6 +83,7 @@
 import org.chromium.chrome.browser.contextmenu.ChromeContextMenuPopulatorFactory;
 import org.chromium.chrome.browser.contextual_tasks.ContextualTasksBridge;
 import org.chromium.chrome.browser.contextual_tasks.fusebox.ContextualTasksFusebox.ContextualTasksFuseboxConfig;
+import org.chromium.chrome.browser.contextual_tasks.fusebox.ContextualTasksFuseboxManager;
 import org.chromium.chrome.browser.crash.ChromePureJavaExceptionReporter;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
 import org.chromium.chrome.browser.data_sharing.DataSharingNotificationManager;
@@ -157,6 +157,7 @@
 import org.chromium.chrome.browser.privacy_sandbox.PrivacySandbox3pcdRollbackMessageController;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.read_later.ReadLaterIphController;
+import org.chromium.chrome.browser.readaloud.ReadAloudController;
 import org.chromium.chrome.browser.readaloud.ReadAloudIphController;
 import org.chromium.chrome.browser.safe_browsing.AdvancedProtectionCoordinator;
 import org.chromium.chrome.browser.search_engines.choice_screen.ChoiceDialogCoordinator;
@@ -219,6 +220,7 @@
 import org.chromium.chrome.browser.ui.side_panel.SidePanelCoordinatorAndroid;
 import org.chromium.chrome.browser.ui.side_panel.SidePanelCoordinatorAndroidFactory;
 import org.chromium.chrome.browser.ui.side_panel.SidePanelRegistryBridgeFactory;
+import org.chromium.chrome.browser.ui.side_panel.SidePanelType;
 import org.chromium.chrome.browser.ui.side_panel.WindowScopedSidePanelRegistryBridge;
 import org.chromium.chrome.browser.ui.side_panel_container.SidePanelContainerCoordinator;
 import org.chromium.chrome.browser.ui.side_panel_container.SidePanelContainerCoordinatorFactory;
@@ -335,6 +337,8 @@
     private @Nullable LoadingFullscreenCoordinator mLoadingFullscreenCoordinator;
     private @Nullable BookmarkOpener mBookmarkOpener;
     private @Nullable TabBottomSheetManager mTabBottomSheetManager;
+    private @Nullable Callback<ReadAloudController> mTabBottomSheetReadAloudControllerCallback;
+    private @Nullable ContextualTasksFuseboxManager mContextualTasksFuseboxManager;
     private @Nullable CoBrowseViewFactory mCoBrowseViewFactory;
     private final MonotonicObservableSupplier<BookmarkManagerOpener> mBookmarkManagerOpenerSupplier;
     private AdvancedProtectionCoordinator mAdvancedProtectionCoordinator;
@@ -839,11 +843,21 @@
             mNtpSyncedThemeManager.destroy();
         }
 
+        if (mTabBottomSheetReadAloudControllerCallback != null) {
+            mReadAloudControllerSupplier.removeObserver(mTabBottomSheetReadAloudControllerCallback);
+            mTabBottomSheetReadAloudControllerCallback = null;
+        }
+
         if (mTabBottomSheetManager != null) {
             mTabBottomSheetManager.destroy();
             mTabBottomSheetManager = null;
         }
 
+        if (mContextualTasksFuseboxManager != null) {
+            mContextualTasksFuseboxManager.destroy();
+            mContextualTasksFuseboxManager = null;
+        }
+
         if (mCoBrowseViewFactory != null) {
             mCoBrowseViewFactory.destroy();
             mCoBrowseViewFactory = null;
@@ -1256,6 +1270,22 @@
                     .onFinishNativeInitialization(currentlySelectedProfile);
         }
         NewTabPageLocationPolicyManager.getInstance().onFinishNativeInitialization(originalProfile);
+
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXTUAL_TASKS)) {
+            mContextualTasksFuseboxManager =
+                    new ContextualTasksFuseboxManager(
+                            mActivity,
+                            () -> {
+                                ViewStub stub =
+                                        mActivity.findViewById(R.id.contextual_tasks_fusebox_stub);
+                                return createContextualTasksFuseboxConfig(stub.inflate());
+                            },
+                            mActivityTabProvider.asObservable(),
+                            mWindowAndroid,
+                            mActivityLifecycleDispatcher,
+                            mProfileSupplier,
+                            mSnackbarManagerSupplier);
+        }
     }
 
     /** Creates an instance of {@link IncognitoReauthCoordinatorFactory} for tabbed activity. */
@@ -1346,6 +1376,26 @@
     }
 
     // Private class methods
+    private ContextualTasksFuseboxConfig createContextualTasksFuseboxConfig(View contentView) {
+        var omniboxActionDelegate =
+                new OmniboxActionDelegateImpl(
+                        mActivity,
+                        /* tabSupplier= */ () -> null,
+                        /* openUrlInExistingTabElseNewTabCb= */ (url) -> {},
+                        /* openIncognitoTabCb= */ CallbackUtils.emptyRunnable(),
+                        /* openPasswordSettingsCb= */ CallbackUtils.emptyRunnable(),
+                        /* openQuickDeleteCb= */ CallbackUtils.emptyRunnable(),
+                        /* tabWindowManagerSupplier= */ TabWindowManagerSingleton::getInstance,
+                        /* bringTabToFrontCallback= */ (tabInfo, url) -> {});
+        return new ContextualTasksFuseboxConfig(
+                contentView,
+                contentView.findViewById(R.id.search_location_bar),
+                contentView.findViewById(R.id.toolbar),
+                contentView.findViewById(R.id.control_container),
+                contentView.findViewById(R.id.bottom_container),
+                omniboxActionDelegate);
+    }
+
     private void initializeIph(Profile profile, boolean intentWithEffect) {
         if (mActivity == null) return;
         mToolbarButtonInProductHelpController =
@@ -1731,25 +1781,9 @@
     private void initiateTabBottomSheetManagers() {
         if (TabBottomSheetUtils.isTabBottomSheetEnabled()) {
             View contentView =
-                    LayoutInflater.from(mActivity).inflate(R.layout.search_activity, null);
-            var omniboxActionDelegate =
-                    new OmniboxActionDelegateImpl(
-                            mActivity,
-                            () -> null,
-                            /* openUrlInExistingTabElseNewTabCb= */ (url) -> {},
-                            /* openIncognitoTabCb= */ CallbackUtils.emptyRunnable(),
-                            /* openPasswordSettingsCb= */ CallbackUtils.emptyRunnable(),
-                            /* openQuickDeleteCb= */ CallbackUtils.emptyRunnable(),
-                            /* tabWindowManagerSupplier= */ SupplierUtils.ofNull(),
-                            /* bringTabToFrontCallback= */ (tabInfo, url) -> {});
+                    mActivity.getLayoutInflater().inflate(R.layout.search_activity, null);
             ContextualTasksFuseboxConfig fuseboxConfig =
-                    new ContextualTasksFuseboxConfig(
-                            contentView,
-                            contentView.findViewById(R.id.search_location_bar),
-                            contentView.findViewById(R.id.toolbar),
-                            contentView.findViewById(R.id.control_container),
-                            contentView.findViewById(R.id.bottom_container),
-                            omniboxActionDelegate);
+                    createContextualTasksFuseboxConfig(contentView);
             ContextMenuPopulatorFactory contextMenuPopulatorFactory =
                     new ChromeContextMenuPopulatorFactory(
                             /* itemDelegate= */ null,
@@ -1772,6 +1806,19 @@
                             getBottomSheetController(),
                             mLayoutStateProviderOneShotSupplier,
                             assertNonNull(mCompositorViewHolderSupplier.get()));
+            mTabBottomSheetReadAloudControllerCallback =
+                    mCallbackController.makeCancelable(
+                            (readAloudController) -> {
+                                if (mTabBottomSheetManager != null) {
+                                    mTabBottomSheetManager.setReadAloudActivePlaybackTabSupplier(
+                                            readAloudController.getActivePlaybackTabSupplier());
+                                }
+                                mReadAloudControllerSupplier.removeObserver(
+                                        mTabBottomSheetReadAloudControllerCallback);
+                                mTabBottomSheetReadAloudControllerCallback = null;
+                            });
+            mReadAloudControllerSupplier.addSyncObserverAndCallIfNonNull(
+                    mTabBottomSheetReadAloudControllerCallback);
         }
     }
 
@@ -1965,8 +2012,11 @@
             return;
         }
 
+        // On Desktop platforms, there are two different Side Panel UI objects, one with each type.
+        // On Android, we currently only have one object and only support toolbar height panels.
         mSidePanelContainerCoordinator =
-                SidePanelContainerCoordinatorFactory.create(mActivity, mSideUiCoordinator);
+                SidePanelContainerCoordinatorFactory.create(
+                        mActivity, mSideUiCoordinator, SidePanelType.TOOLBAR);
         if (mSidePanelContainerCoordinator != null) {
             mSidePanelContainerCoordinator.init();
 
diff --git a/chrome/android/javatests/BUILD.gn b/chrome/android/javatests/BUILD.gn
index cb6312d..3ee723d 100644
--- a/chrome/android/javatests/BUILD.gn
+++ b/chrome/android/javatests/BUILD.gn
@@ -264,7 +264,6 @@
     "//components/browser_ui/site_settings/android:constants_java",
     "//components/browser_ui/site_settings/android:java",
     "//components/browser_ui/site_settings/android:java_resources",
-    "//components/browser_ui/sms/android:java",
     "//components/browser_ui/styles/android:java",
     "//components/browser_ui/util/android:java",
     "//components/browser_ui/util/android:test_support_java",
@@ -1656,7 +1655,6 @@
     "src/org/chromium/chrome/browser/segmentation_platform/ContextualPageActionControllerTest.java",
     "src/org/chromium/chrome/browser/sensitive_content/SensitiveContentTest.java",
     "src/org/chromium/chrome/browser/shape_detection/ShapeDetectionTest.java",
-    "src/org/chromium/chrome/browser/sms/WebOTPServiceInfoBarTest.java",
     "src/org/chromium/chrome/browser/ssl/CaptivePortalTest.java",
     "src/org/chromium/chrome/browser/status_indicator/StatusIndicatorTest.java",
     "src/org/chromium/chrome/browser/storage/BlobUrlStoreTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragmentTest.java
index 244d03c6..b938528d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragmentTest.java
@@ -1692,9 +1692,15 @@
         onView(withText("Add Vehicle")).check(matches(isDisplayed()));
 
         Context context = sSettingsActivityTestRule.getFragment().getContext();
+        String walletTitle = context.getString(R.string.autofill_google_wallet_title);
         String expectedNoticeText =
-                context.getString(R.string.autofill_ai_wallet_entity_editor_source_notice)
-                        .replace("$1", TestAccounts.ACCOUNT1.getEmail());
+                context.getString(
+                                R.string.autofill_ai_save_or_update_entity_in_wallet_source_notice)
+                        .replace("$1", walletTitle)
+                        .replace("$2", walletTitle)
+                        .replace("$3", TestAccounts.ACCOUNT1.getEmail())
+                        .replace("<link>", "")
+                        .replace("</link>", "");
         onView(withText(expectedNoticeText)).check(matches(isDisplayed()));
     }
 
@@ -1990,6 +1996,38 @@
                 });
     }
 
+    @Test
+    @MediumTest
+    @Feature({"Preferences"})
+    public void testOnOpenGoogleWallet_OpensWallet() {
+        intending(hasAction(Intent.ACTION_VIEW))
+                .respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null));
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    sSettingsActivityTestRule.getFragment().onOpenGoogleWalletForTesting(false);
+                });
+
+        intended(hasAction(Intent.ACTION_VIEW));
+        intended(hasData(GoogleWalletLauncher.GOOGLE_WALLET_PASSES_URL));
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Preferences"})
+    public void testOnOpenGoogleWallet_OpensHelpCenterForPrivateEntity() {
+        intending(hasAction(Intent.ACTION_VIEW))
+                .respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, null));
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    sSettingsActivityTestRule.getFragment().onOpenGoogleWalletForTesting(true);
+                });
+
+        intended(hasAction(Intent.ACTION_VIEW));
+        intended(hasData(GoogleWalletLauncher.GOOGLE_WALLET_PRIVATE_PASSES_HELP_CENTER));
+    }
+
     private void checkPreferenceCount(int expectedPreferenceCount) {
         int preferenceCount =
                 ThreadUtils.runOnUiThreadBlocking(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sms/WebOTPServiceInfoBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sms/WebOTPServiceInfoBarTest.java
deleted file mode 100644
index 1ca71b62..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sms/WebOTPServiceInfoBarTest.java
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.sms;
-
-import android.widget.EditText;
-import android.widget.FrameLayout;
-
-import androidx.test.filters.MediumTest;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.ThreadUtils;
-import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.test.util.Batch;
-import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.CriteriaHelper;
-import org.chromium.base.test.util.DisabledTest;
-import org.chromium.base.test.util.Feature;
-import org.chromium.chrome.browser.app.ChromeActivity;
-import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.infobar.InfoBarContainer;
-import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.transit.AutoResetCtaTransitTestRule;
-import org.chromium.chrome.test.transit.ChromeTransitTestRules;
-import org.chromium.chrome.test.transit.page.WebPageStation;
-import org.chromium.chrome.test.util.InfoBarUtil;
-import org.chromium.components.browser_ui.sms.WebOTPServiceInfoBar;
-import org.chromium.components.browser_ui.sms.WebOTPServiceUma;
-import org.chromium.ui.KeyboardVisibilityDelegate;
-import org.chromium.ui.UiUtils;
-
-/** Tests for the WebOTPServiceInfoBar class. */
-@RunWith(ChromeJUnit4ClassRunner.class)
-@Batch(Batch.PER_CLASS)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-public class WebOTPServiceInfoBarTest {
-    @Rule
-    public final AutoResetCtaTransitTestRule mActivityTestRule =
-            ChromeTransitTestRules.fastAutoResetCtaActivityRule();
-
-    private static final String INFOBAR_HISTOGRAM = "Blink.Sms.Receive.Infobar";
-    private static final String TIME_CANCEL_ON_KEYBOARD_DISMISSAL_HISTOGRAM =
-            "Blink.Sms.Receive.TimeCancelOnKeyboardDismissal";
-
-    private WebPageStation mPage;
-    private ChromeActivity mActivity;
-
-    @Before
-    public void setUp() throws Exception {
-        mPage = mActivityTestRule.startOnBlankPage();
-        mActivity = mPage.getActivity();
-    }
-
-    private WebOTPServiceInfoBar createInfoBar() {
-        return ThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    Tab tab = mActivity.getActivityTab();
-                    WebOTPServiceInfoBar infoBar =
-                            WebOTPServiceInfoBar.create(
-                                    mActivity.getWindowAndroid(),
-                                    /* iconId= */ 0,
-                                    "title",
-                                    "message",
-                                    "ok");
-                    InfoBarContainer.get(tab).addInfoBarForTesting(infoBar);
-                    return infoBar;
-                });
-    }
-
-    private void assertHistogramRecordedCount(String name, int expectedCount) {
-        Assert.assertEquals(expectedCount, RecordHistogram.getHistogramTotalCountForTesting(name));
-    }
-
-    private void assertHistogramRecordedCount(String name, int sample, int expectedCount) {
-        Assert.assertEquals(
-                expectedCount, RecordHistogram.getHistogramValueCountForTesting(name, sample));
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"InfoBars", "UiCatalogue"})
-    public void testSmsInfoBarOk() {
-        // Get current counts because Histogram is not reset between runs with test batching.
-        int shownCount =
-                RecordHistogram.getHistogramValueCountForTesting(
-                        INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.SHOWN);
-        int dismissedCount =
-                RecordHistogram.getHistogramValueCountForTesting(
-                        INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.KEYBOARD_DISMISSED);
-        WebOTPServiceInfoBar infoBar = createInfoBar();
-
-        Assert.assertFalse(InfoBarUtil.hasSecondaryButton(infoBar));
-
-        // Click primary button.
-        Assert.assertTrue(InfoBarUtil.clickPrimaryButton(infoBar));
-
-        assertHistogramRecordedCount(
-                INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.SHOWN, shownCount + 1);
-        assertHistogramRecordedCount(
-                INFOBAR_HISTOGRAM,
-                WebOTPServiceUma.InfobarAction.KEYBOARD_DISMISSED,
-                dismissedCount + 0);
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"InfoBars", "UiCatalogue"})
-    public void testSmsInfoBarClose() {
-        // Get current counts because Histogram is not reset between runs with test batching.
-        int shownCount =
-                RecordHistogram.getHistogramValueCountForTesting(
-                        INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.SHOWN);
-        int dismissedCount =
-                RecordHistogram.getHistogramValueCountForTesting(
-                        INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.KEYBOARD_DISMISSED);
-        WebOTPServiceInfoBar infoBar = createInfoBar();
-
-        Assert.assertFalse(InfoBarUtil.hasSecondaryButton(infoBar));
-
-        // Close infobar.
-        Assert.assertTrue(InfoBarUtil.clickCloseButton(infoBar));
-
-        assertHistogramRecordedCount(
-                INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.SHOWN, shownCount + 1);
-        assertHistogramRecordedCount(
-                INFOBAR_HISTOGRAM,
-                WebOTPServiceUma.InfobarAction.KEYBOARD_DISMISSED,
-                dismissedCount + 0);
-    }
-
-    @DisabledTest(message = "https://crbug.com/1169221")
-    @Test
-    @MediumTest
-    @Feature({"InfoBars", "UiCatalogue"})
-    public void testHideKeyboardWhenInfoBarIsShown() {
-        // Get current counts because Histogram is not reset between runs with test batching.
-        int shownCount =
-                RecordHistogram.getHistogramValueCountForTesting(
-                        INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.SHOWN);
-        int dismissedCount =
-                RecordHistogram.getHistogramValueCountForTesting(
-                        INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.KEYBOARD_DISMISSED);
-        int timeCancelCount =
-                RecordHistogram.getHistogramValueCountForTesting(
-                        TIME_CANCEL_ON_KEYBOARD_DISMISSAL_HISTOGRAM, 0);
-        KeyboardVisibilityDelegate keyboardVisibilityDelegate =
-                mActivityTestRule.getKeyboardDelegate();
-        EditText editText = new EditText(mActivity);
-
-        ThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    FrameLayout decor = (FrameLayout) mActivity.getWindow().getDecorView();
-                    FrameLayout.LayoutParams params =
-                            new FrameLayout.LayoutParams(
-                                    FrameLayout.LayoutParams.MATCH_PARENT,
-                                    FrameLayout.LayoutParams.MATCH_PARENT);
-                    decor.addView(editText, params);
-                    editText.requestFocus();
-                    keyboardVisibilityDelegate.showKeyboard(editText);
-                });
-
-        // Wait until the keyboard is showing.
-        CriteriaHelper.pollUiThread(() -> keyboardVisibilityDelegate.isKeyboardShowing(editText));
-
-        WebOTPServiceInfoBar infoBar = createInfoBar();
-
-        // Keyboard is hidden after info bar is created and shown.
-        CriteriaHelper.pollUiThread(() -> !keyboardVisibilityDelegate.isKeyboardShowing(editText));
-
-        assertHistogramRecordedCount(
-                INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.SHOWN, shownCount + 1);
-        assertHistogramRecordedCount(
-                INFOBAR_HISTOGRAM,
-                WebOTPServiceUma.InfobarAction.KEYBOARD_DISMISSED,
-                dismissedCount + 1);
-        assertHistogramRecordedCount(
-                TIME_CANCEL_ON_KEYBOARD_DISMISSAL_HISTOGRAM, timeCancelCount + 0);
-        ThreadUtils.runOnUiThreadBlocking(() -> UiUtils.removeViewFromParent(editText));
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"InfoBars", "UiCatalogue"})
-    public void testUMARecordedWhenInfobarDismissedAfterHidingKeyboard() {
-        // Get current counts because Histogram is not reset between runs with test batching.
-        int shownCount =
-                RecordHistogram.getHistogramValueCountForTesting(
-                        INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.SHOWN);
-        int dismissedCount =
-                RecordHistogram.getHistogramValueCountForTesting(
-                        INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.KEYBOARD_DISMISSED);
-        int timeCancelCount =
-                RecordHistogram.getHistogramValueCountForTesting(
-                        TIME_CANCEL_ON_KEYBOARD_DISMISSAL_HISTOGRAM, 0);
-        KeyboardVisibilityDelegate keyboardVisibilityDelegate =
-                mActivityTestRule.getKeyboardDelegate();
-        EditText editText = new EditText(mActivity);
-
-        ThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    FrameLayout decor = (FrameLayout) mActivity.getWindow().getDecorView();
-                    FrameLayout.LayoutParams params =
-                            new FrameLayout.LayoutParams(
-                                    FrameLayout.LayoutParams.MATCH_PARENT,
-                                    FrameLayout.LayoutParams.MATCH_PARENT);
-                    decor.addView(editText, params);
-                    editText.requestFocus();
-                    keyboardVisibilityDelegate.showKeyboard(editText);
-                });
-
-        // Wait until the keyboard is showing.
-        CriteriaHelper.pollUiThread(() -> keyboardVisibilityDelegate.isKeyboardShowing(editText));
-
-        WebOTPServiceInfoBar infoBar = createInfoBar();
-
-        // Keyboard is hidden after info bar is created and shown.
-        CriteriaHelper.pollUiThread(() -> !keyboardVisibilityDelegate.isKeyboardShowing(editText));
-
-        // Close info bar.
-        InfoBarUtil.clickCloseButton(infoBar);
-
-        assertHistogramRecordedCount(
-                INFOBAR_HISTOGRAM, WebOTPServiceUma.InfobarAction.SHOWN, shownCount + 1);
-        assertHistogramRecordedCount(
-                INFOBAR_HISTOGRAM,
-                WebOTPServiceUma.InfobarAction.KEYBOARD_DISMISSED,
-                dismissedCount + 1);
-        assertHistogramRecordedCount(
-                TIME_CANCEL_ON_KEYBOARD_DISMISSAL_HISTOGRAM, timeCancelCount + 1);
-        ThreadUtils.runOnUiThreadBlocking(() -> UiUtils.removeViewFromParent(editText));
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java
index ac955c0..88ca1e3b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/widget/bottomsheet/BottomSheetControllerTest.java
@@ -952,7 +952,7 @@
     @MediumTest
     public void testReplaceLowPriorityContentWhileOpen() {
         // Allow the content to be replaced without first closing the sheet.
-        mLowPriorityContent.setCanSuppressInAnyState(true);
+        mLowPriorityContent.setCanBeSuppressed(true);
         requestContentInSheet(mLowPriorityContent, true);
 
         ThreadUtils.runOnUiThreadBlocking(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/test/memory_leaks/PublicTransitLeakTest.java b/chrome/android/javatests/src/org/chromium/chrome/test/memory_leaks/PublicTransitLeakTest.java
index c9e17eb..9ecf31d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/test/memory_leaks/PublicTransitLeakTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/test/memory_leaks/PublicTransitLeakTest.java
@@ -4,10 +4,15 @@
 
 package org.chromium.chrome.test.memory_leaks;
 
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.browser.customtabs.CustomTabsSessionToken;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.LargeTest;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -18,7 +23,9 @@
 import org.chromium.base.test.util.ImportantFormFactors;
 import org.chromium.base.test.util.LeakCanaryChecker.EnableLeakChecks;
 import org.chromium.base.test.util.Restriction;
+import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
 import org.chromium.chrome.browser.customtabs.CustomTabsIntentTestUtils;
+import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -100,6 +107,24 @@
 
     @Test
     @LargeTest
+    public void customTabActivityWithSessionTest() throws TimeoutException {
+        final CustomTabsConnection connection = CustomTabsTestUtils.warmUpAndWait();
+        Context context = ApplicationProvider.getApplicationContext();
+        Intent intent =
+                CustomTabsIntentTestUtils.createMinimalCustomTabIntent(
+                        context,
+                        mChromeTabbedActivityTestRule
+                                .getTestServer()
+                                .getURL("/chrome/test/data/android/google.html"));
+        final CustomTabsSessionToken token =
+                CustomTabsSessionToken.getSessionTokenFromIntent(intent);
+        Assert.assertTrue(connection.newSession(token));
+
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+    }
+
+    @Test
+    @LargeTest
     public void omniboxTest() {
         var page = mChromeTabbedActivityTestRule.startOnBlankPage();
         var omnibox = page.openOmnibox();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/dom_distiller/ReaderModeBottomSheetCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/dom_distiller/ReaderModeBottomSheetCoordinatorTest.java
index ac0e7c6..35a0d43 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/dom_distiller/ReaderModeBottomSheetCoordinatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/dom_distiller/ReaderModeBottomSheetCoordinatorTest.java
@@ -9,6 +9,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -98,8 +99,11 @@
         verify(mBottomSheetController).requestShowContent(any(), eq(true));
         BottomSheetContent bottomSheetContent = mCoordinator.getBottomSheetContentForTesting();
 
+        BottomSheetContent newContent = mock(BottomSheetContent.class);
+        when(newContent.getPriority()).thenReturn(BottomSheetContent.ContentPriority.HIGH);
+
         assertEquals(ContentPriority.LOW, bottomSheetContent.getPriority());
-        assertTrue(bottomSheetContent.canSuppressInAnyState());
+        assertTrue(bottomSheetContent.canBeSuppressed(newContent));
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/DefaultBrowserPromoFirstRunFragmentTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/DefaultBrowserPromoFirstRunFragmentTest.java
index 19eff88..7ea6d610 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/DefaultBrowserPromoFirstRunFragmentTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/firstrun/DefaultBrowserPromoFirstRunFragmentTest.java
@@ -36,11 +36,13 @@
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Features.EnableFeatures;
+import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileProvider;
+import org.chromium.chrome.browser.ui.default_browser_promo.DefaultBrowserPromoMetrics;
 import org.chromium.chrome.browser.ui.default_browser_promo.DefaultBrowserPromoUtils;
 import org.chromium.chrome.browser.ui.default_browser_promo.DefaultBrowserPromoUtils.DefaultBrowserPromoEntryPoint;
 import org.chromium.components.feature_engagement.Tracker;
@@ -79,6 +81,11 @@
     @Mock private Tracker mMockTracker;
     @Mock private WindowAndroid mMockWindow;
 
+    private static final String DEFAULT_BROWSER_PROMO_CLICK_HISTOGRAM =
+            "Android.DefaultBrowserPromo.Click";
+    private static final int FRE_PROMO_SOURCE_TYPE =
+            DefaultBrowserPromoMetrics.DefaultBrowserPromoSourceType.FRE_PROMO;
+
     private FragmentScenario<CustomDefaultBrowserPromoFirstRunFragment> mScenario;
 
     @Before
@@ -208,6 +215,12 @@
                 // For the second onResume when the RMD is dismissed.
                 .thenReturn(true);
 
+        var watcher =
+                HistogramWatcher.newBuilder()
+                        .expectIntRecord(
+                                DEFAULT_BROWSER_PROMO_CLICK_HISTOGRAM, FRE_PROMO_SOURCE_TYPE)
+                        .build();
+
         launchFragment(DefaultBrowserPromoFirstRunFragment.PRIMER_NO_INSTRUCTIONS);
 
         mScenario.onFragment(
@@ -228,6 +241,15 @@
                     // Click the Continue button.
                     continueButton.performClick();
 
+                    // Verify the Fragment told the delegate to record the ACCEPTED metric.
+                    verify(mMockDelegate)
+                            .recordFreProgressHistogram(
+                                    MobileFreProgress.DEFAULT_BROWSER_PROMO_ACCEPTED);
+
+                    watcher.assertExpected(
+                            "The FRE_PROMO click should be recorded in the volume comparison"
+                                + " histogram.");
+
                     // Verify RMD was triggered.
                     verify(mMockUtils, times(1))
                             .prepareLaunchPromoIfNeeded(
@@ -262,6 +284,11 @@
                     // Click the Dismiss button.
                     dismissButton.performClick();
 
+                    // Verify the Fragment told the delegate to record the REJECTED metric.
+                    verify(mMockDelegate)
+                            .recordFreProgressHistogram(
+                                    MobileFreProgress.DEFAULT_BROWSER_PROMO_REJECTED);
+
                     // Verify that we advance to the next page immediately without triggering the
                     // RMD.
                     verify(mMockDelegate, times(1)).advanceToNextPage();
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index d79aeaf4..f6f2d83 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -9652,6 +9652,16 @@
       <message name="IDS_IMMERSIVE_READING_MODE_OPENED_ANNOUNCEMENT" desc="Accessibility announcement read by screen readers when reading mode is opened." is_accessibility_with_no_ui="true">
         Reading mode opened, full page view
       </message>
+      <if expr="is_macosx">
+        <message name="IDS_READING_MODE_TOOLTIP" desc="Tooltip for the reading mode feature, which gives users an accessible reading experience.">
+          Reading mode (<ph name="READING_MODE_SHORTCUT">$1<ex>โŒฅโŒ˜R</ex></ph>)
+        </message>
+      </if>
+      <if expr="not is_macosx">
+        <message name="IDS_READING_MODE_TOOLTIP" desc="Tooltip for the reading mode feature, which gives users an accessible reading experience.">
+          Reading mode (<ph name="READING_MODE_SHORTCUT">$1<ex>Alt+Shift+R</ex></ph>)
+        </message>
+      </if>
       <message name="IDS_READING_MODE_NOT_SELECTABLE_HEADER" desc="Header for reading mode when the current webpage cannot be opened in reading mode, even when selecting text.">
         Reading mode isn’t available on this page
       </message>
@@ -9883,9 +9893,16 @@
       <message name="IDS_READING_MODE_LANGUAGE_MENU_CLOSE" desc="Label for a button to close a dialog with options for managing language preferences for Read Aloud.">
         Close
       </message>
-      <message name="IDS_READING_MODE_CLOSE" desc="Tooltip and screen reader label for a button to close reading mode.">
-        Close reading mode
-      </message>
+      <if expr="is_macosx">
+        <message name="IDS_READING_MODE_CLOSE" desc="Tooltip and screen reader label for a button to close reading mode.">
+          Close reading mode (<ph name="SHORTCUT">$1<ex>โŒฅโŒ˜R</ex></ph>)
+        </message>
+      </if>
+      <if expr="not is_macosx">
+        <message name="IDS_READING_MODE_CLOSE" desc="Tooltip and screen reader label for a button to close reading mode.">
+          Close reading mode (<ph name="SHORTCUT">$1<ex>Alt+Shift+R</ex></ph>)
+        </message>
+      </if>
       <message name="IDS_READING_MODE_LANGUAGE_MENU_NO_RESULTS" desc="Text shown in the Read Aloud language menu after a search yields no matching languages.">
         No results found
       </message>
@@ -10329,6 +10346,12 @@
       <message name="IDS_VERTICAL_TABS_IPH_BODY" desc="The body for the vertical tabs IPH tutorial.">
         See entire tab titles, easily manage tab groups, and free up vertical space by moving your tabs to the side.
       </message>
+      <message name="IDS_VERTICAL_TABS_EXPAND_ON_HOVER_DEFAULT_ENABLED_IPH_BODY" desc="The body for the vertical tabs expand on hover IPH, when expand on hover is default enabled.">
+        You can now expand your tabs by hovering over them. To turn this off, right-click here.
+      </message>
+      <message name="IDS_VERTICAL_TABS_EXPAND_ON_HOVER_DEFAULT_DISABLED_IPH_BODY" desc="The body for the vertical tabs expand on hover IPH, when expand on hover is default disabled.">
+        You can now expand your tabs by hovering over them. To turn this on, right-click here.
+      </message>
       <message name="IDS_VERTICAL_RESIZE_AREA" is_accessibility_with_no_ui="true" desc="The accessibility label for the resize area in the vertical tab strip.">
             Vertical Tab Strip Resize Handle (Draggable)
       </message>
diff --git a/chrome/app/generated_resources_grd/IDS_READING_MODE_CLOSE.png.sha1 b/chrome/app/generated_resources_grd/IDS_READING_MODE_CLOSE.png.sha1
index 3ea7100..0a6cf28 100644
--- a/chrome/app/generated_resources_grd/IDS_READING_MODE_CLOSE.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_READING_MODE_CLOSE.png.sha1
@@ -1 +1 @@
-0e8f8d64635262a74bd0a7fe47c4a4039cf05035
\ No newline at end of file
+3b9fea432d130e281864c3f39f943ea2e6caa8d7
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_READING_MODE_TOOLTIP.png.sha1 b/chrome/app/generated_resources_grd/IDS_READING_MODE_TOOLTIP.png.sha1
new file mode 100644
index 0000000..9c04ca40
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_READING_MODE_TOOLTIP.png.sha1
@@ -0,0 +1 @@
+ee387a60823262de14f8ce8150724c11ee91ae3b
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_VERTICAL_TABS_EXPAND_ON_HOVER_DEFAULT_DISABLED_IPH_BODY.png.sha1 b/chrome/app/generated_resources_grd/IDS_VERTICAL_TABS_EXPAND_ON_HOVER_DEFAULT_DISABLED_IPH_BODY.png.sha1
new file mode 100644
index 0000000..c3841f7
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_VERTICAL_TABS_EXPAND_ON_HOVER_DEFAULT_DISABLED_IPH_BODY.png.sha1
@@ -0,0 +1 @@
+e8c450b5b036f81e828c9cc202727f7614a35fb1
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_VERTICAL_TABS_EXPAND_ON_HOVER_DEFAULT_ENABLED_IPH_BODY.png.sha1 b/chrome/app/generated_resources_grd/IDS_VERTICAL_TABS_EXPAND_ON_HOVER_DEFAULT_ENABLED_IPH_BODY.png.sha1
new file mode 100644
index 0000000..93a6f74
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_VERTICAL_TABS_EXPAND_ON_HOVER_DEFAULT_ENABLED_IPH_BODY.png.sha1
@@ -0,0 +1 @@
+79675c1627c52146a470c572239176552d9975b2
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 7100366..c3af30c 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -734,20 +734,6 @@
     "profiles/renderer_updater_factory.cc",
     "profiles/renderer_updater_factory.h",
     "profiles/storage_partition_descriptor.h",
-    "push_messaging/budget_database.cc",
-    "push_messaging/budget_database.h",
-    "push_messaging/push_messaging_app_identifier.cc",
-    "push_messaging/push_messaging_app_identifier.h",
-    "push_messaging/push_messaging_notification_manager.cc",
-    "push_messaging/push_messaging_notification_manager.h",
-    "push_messaging/push_messaging_refresher.cc",
-    "push_messaging/push_messaging_refresher.h",
-    "push_messaging/push_messaging_service_factory.cc",
-    "push_messaging/push_messaging_service_factory.h",
-    "push_messaging/push_messaging_service_impl.cc",
-    "push_messaging/push_messaging_service_impl.h",
-    "push_messaging/push_messaging_unsubscribed_entry.cc",
-    "push_messaging/push_messaging_unsubscribed_entry.h",
     "renderer_preferences_util.cc",
     "renderer_preferences_util.h",
     "resource_coordinator/resource_coordinator_parts.cc",
@@ -1207,6 +1193,10 @@
     # //chrome/browser/download:impl sources include headers from //chrome/browser
     # and //chrome/browser/ui.
     "//chrome/browser/download:impl",
+
+    # TODO(crbug.com/353332589): Remove this circular dependency when
+    # chrome/browser/notifications is modularized.
+    "//chrome/browser/push_messaging:impl",
   ]
 
   if (is_android) {
@@ -1493,6 +1483,8 @@
     "//chrome/browser/profiles:profiles_extra_parts_impl",
     "//chrome/browser/profiles/keep_alive",
     "//chrome/browser/profiling_host",
+    "//chrome/browser/push_messaging",
+    "//chrome/browser/push_messaging:impl",
     "//chrome/browser/reading_list",
     "//chrome/browser/reduce_accept_language",
     "//chrome/browser/reduce_accept_language:impl",
@@ -2971,7 +2963,6 @@
       "//components/browser_ui/photo_picker/android",
       "//components/browser_ui/share/android",
       "//components/browser_ui/site_settings/android",
-      "//components/browser_ui/sms/android",
       "//components/browser_ui/util/android",
       "//components/browsing_data/content/android",
       "//components/browsing_data/content/android:jni_headers",
@@ -4276,112 +4267,6 @@
       "metrics/update_engine_metrics_provider.h",
       "metrics/usertype_by_devicetype_metrics_provider.cc",
       "metrics/usertype_by_devicetype_metrics_provider.h",
-      "nearby_sharing/attachment_info.cc",
-      "nearby_sharing/attachment_info.h",
-      "nearby_sharing/bluetooth_advertising_interval_client.cc",
-      "nearby_sharing/bluetooth_advertising_interval_client.h",
-      "nearby_sharing/constants.h",
-      "nearby_sharing/fast_initiation/constants.h",
-      "nearby_sharing/fast_initiation/fast_initiation_advertiser.cc",
-      "nearby_sharing/fast_initiation/fast_initiation_advertiser.h",
-      "nearby_sharing/fast_initiation/fast_initiation_scanner.cc",
-      "nearby_sharing/fast_initiation/fast_initiation_scanner.h",
-      "nearby_sharing/fast_initiation/fast_initiation_scanner_feature_usage_metrics.cc",
-      "nearby_sharing/fast_initiation/fast_initiation_scanner_feature_usage_metrics.h",
-      "nearby_sharing/firewall_hole/nearby_connections_firewall_hole.cc",
-      "nearby_sharing/firewall_hole/nearby_connections_firewall_hole.h",
-      "nearby_sharing/firewall_hole/nearby_connections_firewall_hole_factory.cc",
-      "nearby_sharing/firewall_hole/nearby_connections_firewall_hole_factory.h",
-      "nearby_sharing/incoming_frames_reader.cc",
-      "nearby_sharing/incoming_frames_reader.h",
-      "nearby_sharing/incoming_share_target_info.cc",
-      "nearby_sharing/incoming_share_target_info.h",
-      "nearby_sharing/instantmessaging/constants.h",
-      "nearby_sharing/instantmessaging/receive_messages_express.cc",
-      "nearby_sharing/instantmessaging/receive_messages_express.h",
-      "nearby_sharing/instantmessaging/send_message_express.cc",
-      "nearby_sharing/instantmessaging/send_message_express.h",
-      "nearby_sharing/instantmessaging/stream_parser.cc",
-      "nearby_sharing/instantmessaging/stream_parser.h",
-      "nearby_sharing/instantmessaging/token_fetcher.cc",
-      "nearby_sharing/instantmessaging/token_fetcher.h",
-      "nearby_sharing/mdns/nearby_connections_mdns_manager.cc",
-      "nearby_sharing/mdns/nearby_connections_mdns_manager.h",
-      "nearby_sharing/metrics/attachment_metric_logger.cc",
-      "nearby_sharing/metrics/attachment_metric_logger.h",
-      "nearby_sharing/metrics/discovery_metric_logger.cc",
-      "nearby_sharing/metrics/discovery_metric_logger.h",
-      "nearby_sharing/metrics/metric_common.cc",
-      "nearby_sharing/metrics/metric_common.h",
-      "nearby_sharing/metrics/nearby_share_metric_logger.cc",
-      "nearby_sharing/metrics/nearby_share_metric_logger.h",
-      "nearby_sharing/metrics/throughput_metric_logger.cc",
-      "nearby_sharing/metrics/throughput_metric_logger.h",
-      "nearby_sharing/nearby_confirmation_manager.cc",
-      "nearby_sharing/nearby_confirmation_manager.h",
-      "nearby_sharing/nearby_notification_delegate.h",
-      "nearby_sharing/nearby_notification_handler.cc",
-      "nearby_sharing/nearby_notification_handler.h",
-      "nearby_sharing/nearby_notification_manager.cc",
-      "nearby_sharing/nearby_notification_manager.h",
-      "nearby_sharing/nearby_per_session_discovery_manager.cc",
-      "nearby_sharing/nearby_per_session_discovery_manager.h",
-      "nearby_sharing/nearby_receive_manager.cc",
-      "nearby_sharing/nearby_receive_manager.h",
-      "nearby_sharing/nearby_share_delegate_impl.cc",
-      "nearby_sharing/nearby_share_delegate_impl.h",
-      "nearby_sharing/nearby_share_error.h",
-      "nearby_sharing/nearby_share_feature_status.cc",
-      "nearby_sharing/nearby_share_feature_status.h",
-      "nearby_sharing/nearby_share_feature_usage_metrics.cc",
-      "nearby_sharing/nearby_share_feature_usage_metrics.h",
-      "nearby_sharing/nearby_share_logger.cc",
-      "nearby_sharing/nearby_share_logger.h",
-      "nearby_sharing/nearby_share_metrics.cc",
-      "nearby_sharing/nearby_share_metrics.h",
-      "nearby_sharing/nearby_share_settings.cc",
-      "nearby_sharing/nearby_share_settings.h",
-      "nearby_sharing/nearby_share_transfer_profiler.cc",
-      "nearby_sharing/nearby_share_transfer_profiler.h",
-      "nearby_sharing/nearby_sharing_service.h",
-      "nearby_sharing/nearby_sharing_service_factory.cc",
-      "nearby_sharing/nearby_sharing_service_factory.h",
-      "nearby_sharing/nearby_sharing_service_impl.cc",
-      "nearby_sharing/nearby_sharing_service_impl.h",
-      "nearby_sharing/network_traversal_ice_config_fetcher.cc",
-      "nearby_sharing/network_traversal_ice_config_fetcher.h",
-      "nearby_sharing/outgoing_share_target_info.cc",
-      "nearby_sharing/outgoing_share_target_info.h",
-      "nearby_sharing/paired_key_verification_runner.cc",
-      "nearby_sharing/paired_key_verification_runner.h",
-      "nearby_sharing/payload_tracker.cc",
-      "nearby_sharing/payload_tracker.h",
-      "nearby_sharing/power_client.cc",
-      "nearby_sharing/power_client.h",
-      "nearby_sharing/power_client_chromeos.cc",
-      "nearby_sharing/power_client_chromeos.h",
-      "nearby_sharing/share_target_discovered_callback.h",
-      "nearby_sharing/share_target_info.cc",
-      "nearby_sharing/share_target_info.h",
-      "nearby_sharing/sharesheet/nearby_share_action.cc",
-      "nearby_sharing/sharesheet/nearby_share_action.h",
-      "nearby_sharing/sharing_mojo_service.cc",
-      "nearby_sharing/sharing_mojo_service.h",
-      "nearby_sharing/tachyon_ice_config_fetcher.cc",
-      "nearby_sharing/tachyon_ice_config_fetcher.h",
-      "nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory.cc",
-      "nearby_sharing/tcp_socket/nearby_connections_tcp_socket_factory.h",
-      "nearby_sharing/transfer_metadata.cc",
-      "nearby_sharing/transfer_metadata.h",
-      "nearby_sharing/transfer_metadata_builder.cc",
-      "nearby_sharing/transfer_metadata_builder.h",
-      "nearby_sharing/transfer_update_callback.h",
-      "nearby_sharing/webrtc_request_builder.cc",
-      "nearby_sharing/webrtc_request_builder.h",
-      "nearby_sharing/webrtc_signaling_messenger.cc",
-      "nearby_sharing/webrtc_signaling_messenger.h",
-      "nearby_sharing/wifi_network_configuration/wifi_network_configuration_handler.cc",
-      "nearby_sharing/wifi_network_configuration/wifi_network_configuration_handler.h",
       "net/network_annotation_monitor.cc",
       "net/network_annotation_monitor.h",
       "notifications/arc_application_notifier_controller.cc",
@@ -4796,13 +4681,9 @@
       # c/b/browser_process_platform_part_ash.h gets modularized.
       "//chrome/browser/invalidation:impl",
       "//chrome/browser/metrics/perf",
-      "//chrome/browser/nearby_sharing:share_target",
-      "//chrome/browser/nearby_sharing/certificates",
-      "//chrome/browser/nearby_sharing/client",
+      "//chrome/browser/nearby_sharing",
+      "//chrome/browser/nearby_sharing:impl",
       "//chrome/browser/nearby_sharing/common",
-      "//chrome/browser/nearby_sharing/contacts",
-      "//chrome/browser/nearby_sharing/local_device_data",
-      "//chrome/browser/nearby_sharing/logging:util",
       "//chrome/browser/policy:onc",
       "//chrome/browser/policy:system_features_disable_list",
       "//chrome/browser/push_notification",
@@ -5309,6 +5190,10 @@
       # c/b/browser_process_platform_part_ash.h gets modularized.
       "//chrome/browser/invalidation:impl",
       "//chrome/browser/media_galleries/fileapi:impl",
+
+      # nearby_sharing's :impl includes chrome/browser/browser_process.h and
+      # chrome/browser/platform_util.h from //chrome/browser.
+      "//chrome/browser/nearby_sharing:impl",
       "//chrome/browser/policy:onc",
       "//chrome/browser/support_tool/ash",
       "//chrome/browser/ui/ash/accelerator",
@@ -7224,7 +7109,6 @@
     "//chrome/browser/policy/messaging_layer/proto:log_upload_event_proto",
     "//chrome/browser/predictors:resource_prefetch_predictor_proto",
     "//chrome/browser/predictors/lcp_critical_path_predictor:lcp_critical_path_predictor_proto",
-    "//chrome/browser/push_messaging:budget_proto",
     "//chrome/browser/regional_capabilities",
     "//chrome/browser/resource_coordinator:mojo_bindings",
     "//chrome/browser/sharing:buildflags",
@@ -7313,8 +7197,6 @@
       "//chrome/browser/ash/crostini:crostini_installer_types_mojom",
       "//chrome/browser/ash/fusebox:fusebox_proto",
       "//chrome/browser/media/router/discovery/access_code:discovery_resources_proto",
-      "//chrome/browser/nearby_sharing/instantmessaging/proto",
-      "//chrome/browser/nearby_sharing/proto:tachyon_proto",
       "//chrome/browser/ui/webui/ash/add_supervision:mojo_bindings",
       "//chrome/browser/ui/webui/ash/app_install:mojo_bindings",
       "//chrome/browser/ui/webui/ash/borealis_installer:mojo_bindings",
@@ -7940,6 +7822,7 @@
     deps = [
       ":browser",
       "//base",
+      "//chrome/browser/nearby_sharing",
       "//components/exo/wayland:ui_controls_protocol_stub",
       "//third_party/protobuf:protobuf_lite",
     ]
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 801f2ed..d0681a7 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1114,7 +1114,6 @@
     {"filled_card_information", "true"},
     {"intent_picker", "true"},
     {"lens_overlay_homework", "true"},
-    {"manage_passwords", "true"},
     {"mandatory_reauth", "true"},
     {"reading_mode", "true"},
     {"save_payments", "true"},
@@ -9512,6 +9511,10 @@
      flag_descriptions::kToolbarGlowUpDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kToolbarGlowUp)},
 
+    {"tab-group-color-refresh", flag_descriptions::kTabGroupColorRefreshName,
+     flag_descriptions::kTabGroupColorRefreshDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(features::kTabGroupColorRefresh)},
+
     {"menu-simplification", flag_descriptions::kMenuSimplificationName,
      flag_descriptions::kMenuSimplificationDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kMenuSimplification)},
diff --git a/chrome/browser/actor/tools/load_and_extract_content_tool.cc b/chrome/browser/actor/tools/load_and_extract_content_tool.cc
index 816d8e15..1d7823c 100644
--- a/chrome/browser/actor/tools/load_and_extract_content_tool.cc
+++ b/chrome/browser/actor/tools/load_and_extract_content_tool.cc
@@ -325,6 +325,8 @@
 void LoadAndExtractContentTool::Validate(ToolCallback overall_callback) {
   // TODO(b/478282022): Consider imposing a limit on the number of URLs.
   if (urls_.empty()) {
+    journal().Log(GURL::EmptyGURL(), task_id(),
+                  "LoadAndExtractContentTool::EmptyUrlsFailedValidation", {});
     PostResponseTask(std::move(overall_callback),
                      MakeResult(mojom::ActionResultCode::kArgumentsInvalid));
     return;
@@ -358,6 +360,8 @@
       BrowserWindowInterface::FromSessionID(window_id_);
 
   if (!browser_window_interface) {
+    journal().Log(GURL::EmptyGURL(), task_id(),
+                  "LoadAndExtractContentTool::WindowWentAway", {});
     PostResponseTask(std::move(invoke_callback_),
                      MakeResult(mojom::ActionResultCode::kWindowWentAway));
     return;
@@ -521,10 +525,17 @@
 
   for (auto& [index, per_tab_state] : per_tab_state_) {
     CHECK(per_tab_state.result_code.has_value());
+    GURL url = urls_[index.value()];
+    journal().Log(url, task_id(), "LoadAndExtractContentTool::PerTabResult",
+                  JournalDetailsBuilder()
+                      .Add("url_index", index.value())
+                      .Add("per_tab_result_code",
+                           static_cast<int>(per_tab_state.result_code.value()))
+                      .Build());
+
     mojom::ActionResultCode action_result_code =
         ToActionResultCode(per_tab_state.result_code.value());
     if (!IsOk(action_result_code)) {
-      GURL url = urls_[index.value()];
       journal().Log(
           url, task_id(), "LoadAndExtractContentTool::OnAllUrlsCompleted",
           JournalDetailsBuilder()
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.cc b/chrome/browser/android/tab_web_contents_delegate_android.cc
index 1258ca1b1..5824141 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.cc
+++ b/chrome/browser/android/tab_web_contents_delegate_android.cc
@@ -53,7 +53,6 @@
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
 #include "components/blocked_content/popup_blocker.h"
 #include "components/blocked_content/popup_tracker.h"
-#include "components/browser_ui/sms/android/sms_infobar.h"
 #include "components/browser_ui/util/android/url_constants.h"
 #include "components/external_intents/android/external_intents_features.h"
 #include "components/find_in_page/find_notification_details.h"
@@ -182,22 +181,6 @@
                                    params);
 }
 
-void TabWebContentsDelegateAndroid::CreateSmsPrompt(
-    content::RenderFrameHost* host,
-    const std::vector<url::Origin>& origin_list,
-    const std::string& one_time_code,
-    base::OnceClosure on_confirm,
-    base::OnceClosure on_cancel) {
-  DCHECK_EQ(host->GetLifecycleState(),
-            content::RenderFrameHost::LifecycleState::kActive);
-
-  auto* web_contents = content::WebContents::FromRenderFrameHost(host);
-  sms::SmsInfoBar::Create(
-      web_contents,
-      infobars::ContentInfoBarManager::FromWebContents(web_contents),
-      origin_list, one_time_code, std::move(on_confirm), std::move(on_cancel));
-}
-
 bool TabWebContentsDelegateAndroid::ShouldFocusLocationBarByDefault(
     WebContents* source) {
   content::NavigationEntry* entry = source->GetController().GetActiveEntry();
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.h b/chrome/browser/android/tab_web_contents_delegate_android.h
index 8802fee..30d9db0 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.h
+++ b/chrome/browser/android/tab_web_contents_delegate_android.h
@@ -50,11 +50,6 @@
   void RunFileChooser(content::RenderFrameHost* render_frame_host,
                       scoped_refptr<content::FileSelectListener> listener,
                       const blink::mojom::FileChooserParams& params) override;
-  void CreateSmsPrompt(content::RenderFrameHost*,
-                       const std::vector<url::Origin>&,
-                       const std::string& one_time_code,
-                       base::OnceClosure on_confirm,
-                       base::OnceClosure on_cancel) override;
   bool ShouldFocusLocationBarByDefault(content::WebContents* source) override;
   void FindReply(content::WebContents* web_contents,
                  int request_id,
diff --git a/chrome/browser/ash/arc/nearby_share/BUILD.gn b/chrome/browser/ash/arc/nearby_share/BUILD.gn
index 1dafa5ac..9ded545 100644
--- a/chrome/browser/ash/arc/nearby_share/BUILD.gn
+++ b/chrome/browser/ash/arc/nearby_share/BUILD.gn
@@ -69,6 +69,7 @@
     "//chrome/browser/ash/app_list/arc:test_support",
     "//chrome/browser/ash/arc/fileapi",
     "//chrome/browser/ash/browser_delegate:impl",
+    "//chrome/browser/nearby_sharing",
     "//chrome/browser/ui/ash/shelf",
     "//chrome/test:test_support",
     "//chromeos/ash/experiences/arc",
diff --git a/chrome/browser/ash/nearby/BUILD.gn b/chrome/browser/ash/nearby/BUILD.gn
index 0fca34048..0593c41 100644
--- a/chrome/browser/ash/nearby/BUILD.gn
+++ b/chrome/browser/ash/nearby/BUILD.gn
@@ -32,6 +32,7 @@
     "//base",
     "//chrome/browser/ash/nearby/presence/credential_storage/",
     "//chrome/browser/ash/profiles",
+    "//chrome/browser/nearby_sharing",
     "//chrome/browser/nearby_sharing/common",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/signin",
diff --git a/chrome/browser/autocomplete/BUILD.gn b/chrome/browser/autocomplete/BUILD.gn
index 50d0c81..d95d7b2e 100644
--- a/chrome/browser/autocomplete/BUILD.gn
+++ b/chrome/browser/autocomplete/BUILD.gn
@@ -42,18 +42,15 @@
   if (enable_extensions_core) {
     sources += [
       "keyword_extensions_delegate_impl.h",
+      "shortcuts_extensions_manager.h",
       "unscoped_extension_provider_delegate_impl.h",
     ]
     public_deps += [
+      "//extensions/browser",
       "//extensions/buildflags",
       "//extensions/common",
     ]
   }
-
-  if (enable_extensions) {
-    sources += [ "shortcuts_extensions_manager.h" ]
-    public_deps += [ "//extensions/browser" ]
-  }
 }
 
 source_set("impl") {
@@ -145,6 +142,7 @@
   if (enable_extensions_core) {
     sources += [
       "keyword_extensions_delegate_impl.cc",
+      "shortcuts_extensions_manager.cc",
       "unscoped_extension_provider_delegate_impl.cc",
     ]
     deps += [
@@ -155,10 +153,6 @@
     ]
   }
 
-  if (enable_extensions) {
-    sources += [ "shortcuts_extensions_manager.cc" ]
-  }
-
   public_deps = [ "//chrome/browser:browser_public_dependencies" ]
 }
 
@@ -297,11 +291,8 @@
     "//url",
   ]
 
-  if (enable_extensions) {
-    sources += [ "keyword_extensions_delegate_impl_unittest.cc" ]
-  }
-
   if (enable_extensions_core) {
+    sources += [ "keyword_extensions_delegate_impl_unittest.cc" ]
     deps += [
       "//extensions:test_support",
       "//extensions/browser",
diff --git a/chrome/browser/autocomplete/autocomplete_classifier_factory.cc b/chrome/browser/autocomplete/autocomplete_classifier_factory.cc
index 67c5365f..5b0ecf7 100644
--- a/chrome/browser/autocomplete/autocomplete_classifier_factory.cc
+++ b/chrome/browser/autocomplete/autocomplete_classifier_factory.cc
@@ -18,7 +18,7 @@
 #include "components/omnibox/browser/autocomplete_controller_config.h"
 #include "extensions/buildflags/buildflags.h"
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
 #include "extensions/browser/extension_system_provider.h"
 #include "extensions/browser/extensions_browser_client.h"
 #endif
@@ -59,7 +59,7 @@
               // Ash Internals.
               .WithAshInternals(ProfileSelection::kRedirectedToOriginal)
               .Build()) {
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
   DependsOn(
       extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
 #endif
diff --git a/chrome/browser/autocomplete/keyword_extensions_delegate_impl_unittest.cc b/chrome/browser/autocomplete/keyword_extensions_delegate_impl_unittest.cc
index 5daadab..b491358 100644
--- a/chrome/browser/autocomplete/keyword_extensions_delegate_impl_unittest.cc
+++ b/chrome/browser/autocomplete/keyword_extensions_delegate_impl_unittest.cc
@@ -22,8 +22,11 @@
 #include "extensions/browser/extension_util.h"
 #include "extensions/browser/test_extension_registry_observer.h"
 #include "extensions/browser/unpacked_installer.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/common/extension.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace extensions {
 
 namespace {
diff --git a/chrome/browser/autocomplete/shortcuts_backend_factory.cc b/chrome/browser/autocomplete/shortcuts_backend_factory.cc
index 7d026785..43b6c090 100644
--- a/chrome/browser/autocomplete/shortcuts_backend_factory.cc
+++ b/chrome/browser/autocomplete/shortcuts_backend_factory.cc
@@ -15,7 +15,7 @@
 #include "components/prefs/pref_service.h"
 #include "extensions/buildflags/buildflags.h"
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
 #include "chrome/browser/autocomplete/shortcuts_extensions_manager.h"
 
 namespace {
@@ -87,7 +87,7 @@
 
 void ShortcutsBackendFactory::BrowserContextShutdown(
     content::BrowserContext* context) {
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
   context->RemoveUserData(kShortcutsExtensionsManagerKey);
 #endif
 
@@ -104,7 +104,7 @@
       HistoryServiceFactory::GetForProfile(profile,
                                            ServiceAccessType::EXPLICIT_ACCESS),
       profile->GetPath().Append(kShortcutsDatabaseName), suppress_db));
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
   auto extensions_manager =
       std::make_unique<ShortcutsExtensionsManager>(profile);
   profile->SetUserData(kShortcutsExtensionsManagerKey,
diff --git a/chrome/browser/autocomplete/shortcuts_extensions_manager.cc b/chrome/browser/autocomplete/shortcuts_extensions_manager.cc
index 102764d..5922b12 100644
--- a/chrome/browser/autocomplete/shortcuts_extensions_manager.cc
+++ b/chrome/browser/autocomplete/shortcuts_extensions_manager.cc
@@ -7,8 +7,11 @@
 #include "chrome/browser/autocomplete/shortcuts_backend_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/omnibox/browser/shortcuts_backend.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/common/extension.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 ShortcutsExtensionsManager::ShortcutsExtensionsManager(Profile* profile)
     : profile_(profile) {
   DCHECK(profile_);
diff --git a/chrome/browser/autocomplete/shortcuts_extensions_manager.h b/chrome/browser/autocomplete/shortcuts_extensions_manager.h
index b821bb6..0dcaa52 100644
--- a/chrome/browser/autocomplete/shortcuts_extensions_manager.h
+++ b/chrome/browser/autocomplete/shortcuts_extensions_manager.h
@@ -10,6 +10,9 @@
 #include "base/supports_user_data.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_observer.h"
+#include "extensions/buildflags/buildflags.h"
+
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
 
 class Profile;
 
diff --git a/chrome/browser/autocomplete/shortcuts_provider_extension_unittest.cc b/chrome/browser/autocomplete/shortcuts_provider_extension_unittest.cc
index 694ed672..f63c814 100644
--- a/chrome/browser/autocomplete/shortcuts_provider_extension_unittest.cc
+++ b/chrome/browser/autocomplete/shortcuts_provider_extension_unittest.cc
@@ -27,7 +27,7 @@
 #include "extensions/buildflags/buildflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/unloaded_extension_reason.h"
 #include "extensions/common/extension.h"
@@ -94,7 +94,7 @@
 
 // Actual tests ---------------------------------------------------------------
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
 TEST_F(ShortcutsProviderExtensionTest, Extension) {
   // Try an input string that matches an extension URL.
   std::u16string text(u"echo");
diff --git a/chrome/browser/autofill/android/autofill_ai_save_update_entity_prompt_controller.cc b/chrome/browser/autofill/android/autofill_ai_save_update_entity_prompt_controller.cc
index 098f57e..461c16b 100644
--- a/chrome/browser/autofill/android/autofill_ai_save_update_entity_prompt_controller.cc
+++ b/chrome/browser/autofill/android/autofill_ai_save_update_entity_prompt_controller.cc
@@ -103,7 +103,6 @@
   if (!account) {
     return std::u16string();
   }
-
   const std::u16string google_wallet_text =
       l10n_util::GetStringUTF16(IDS_AUTOFILL_GOOGLE_WALLET_TITLE);
   return l10n_util::GetStringFUTF16(
@@ -126,8 +125,14 @@
   return base::android::ScopedJavaLocalRef<jobject>(java_object_);
 }
 
-void AutofillAiSaveUpdateEntityPromptController::OpenManagePasses(JNIEnv* env) {
-  ShowGoogleWalletPassesPage(*web_contents_);
+void AutofillAiSaveUpdateEntityPromptController::OnWalletLinkClicked(
+    JNIEnv* env) {
+  if (IsMaskedStorageSupported(entity_instance_.type(),
+                               EntityInstance::RecordType::kServerWallet)) {
+    ShowGoogleWallePrivatePassesHelpCenterPageInCct(*web_contents_);
+  } else {
+    ShowGoogleWalletPassesPage(*web_contents_);
+  }
 }
 
 void AutofillAiSaveUpdateEntityPromptController::OnUserAccepted(JNIEnv* env) {
diff --git a/chrome/browser/autofill/android/autofill_ai_save_update_entity_prompt_controller.h b/chrome/browser/autofill/android/autofill_ai_save_update_entity_prompt_controller.h
index 8e14c5125..83c5510 100644
--- a/chrome/browser/autofill/android/autofill_ai_save_update_entity_prompt_controller.h
+++ b/chrome/browser/autofill/android/autofill_ai_save_update_entity_prompt_controller.h
@@ -59,7 +59,7 @@
 
   base::android::ScopedJavaLocalRef<jobject> GetJavaObject() const;
   // Called by AutofillAiSaveUpdateEntityPromptController.java
-  void OpenManagePasses(JNIEnv* env);
+  void OnWalletLinkClicked(JNIEnv* env);
   void OnUserAccepted(JNIEnv* env);
   void OnUserDeclined(JNIEnv* env);
   // Called whenever the prompt is dismissed (e.g. because the user already
@@ -79,6 +79,7 @@
   bool had_user_interaction_ = false;
   // The callback to run when the user takes action on the prompt.
   AutofillClient::EntityImportPromptResultCallback prompt_result_callback_;
+
   // The corresponding Java SaveUpdateAddressProfilePromptController.
   base::android::ScopedJavaGlobalRef<jobject> java_object_;
 };
diff --git a/chrome/browser/autofill/android/autofill_ai_save_update_entity_prompt_controller_unittest.cc b/chrome/browser/autofill/android/autofill_ai_save_update_entity_prompt_controller_unittest.cc
index 7507790..d3fdc4a1 100644
--- a/chrome/browser/autofill/android/autofill_ai_save_update_entity_prompt_controller_unittest.cc
+++ b/chrome/browser/autofill/android/autofill_ai_save_update_entity_prompt_controller_unittest.cc
@@ -20,6 +20,8 @@
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
 #include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_contents.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -27,7 +29,6 @@
 namespace {
 
 using ::testing::_;
-
 class AutofillAiSaveUpdateEntityPromptControllerTest
     : public ChromeRenderViewHostTestHarness {
  public:
@@ -47,17 +48,23 @@
   void CreateController(EntityInstance::RecordType record_type =
                             EntityInstance::RecordType::kLocal,
                         bool entity_updated = false) {
-    std::unique_ptr<MockAutofillAiSaveUpdateEntityPromptView> prompt_view =
-        std::make_unique<MockAutofillAiSaveUpdateEntityPromptView>();
-    prompt_view_ = prompt_view.get();
-    controller_ = std::make_unique<AutofillAiSaveUpdateEntityPromptController>(
-        web_contents(), std::move(prompt_view),
+    CreateControllerWithEntity(
         test::GetPassportEntityInstance(
             {.name = u"Jon doe", .record_type = record_type}),
         (entity_updated ? std::optional(test::GetPassportEntityInstance(
                               {.name = u"Seb doe", .record_type = record_type}))
-                        : std::nullopt),
-        "en-US", prompt_closed_callback_.Get());
+                        : std::nullopt));
+  }
+
+  void CreateControllerWithEntity(
+      EntityInstance entity_instance,
+      std::optional<EntityInstance> old_entity_instance = std::nullopt) {
+    std::unique_ptr<MockAutofillAiSaveUpdateEntityPromptView> prompt_view =
+        std::make_unique<MockAutofillAiSaveUpdateEntityPromptView>();
+    prompt_view_ = prompt_view.get();
+    controller_ = std::make_unique<AutofillAiSaveUpdateEntityPromptController>(
+        web_contents(), std::move(prompt_view), std::move(entity_instance),
+        std::move(old_entity_instance), "en-US", prompt_closed_callback_.Get());
   }
 
   void SigninUser(const std::string& email,
@@ -200,5 +207,24 @@
             prompt_controller().GetSourceNotice());
 }
 
+TEST_F(AutofillAiSaveUpdateEntityPromptControllerTest,
+       PromptUiStrings_UpdateWalletEntity) {
+  SigninUser(TestingProfile::kDefaultProfileUserName,
+             signin::ConsentLevel::kSignin);
+  CreateController(EntityInstance::RecordType::kServerWallet,
+                   /*entity_updated=*/true);
+  EXPECT_EQ(l10n_util::GetStringUTF16(
+                IDS_AUTOFILL_AI_UPDATE_PASSPORT_ENTITY_DIALOG_TITLE_ANDROID),
+            prompt_controller().GetTitle());
+
+  const std::u16string google_wallet =
+      l10n_util::GetStringUTF16(IDS_AUTOFILL_GOOGLE_WALLET_TITLE);
+  EXPECT_EQ(l10n_util::GetStringFUTF16(
+                IDS_AUTOFILL_AI_SAVE_OR_UPDATE_ENTITY_IN_WALLET_SOURCE_NOTICE,
+                google_wallet, google_wallet,
+                base::UTF8ToUTF16(TestingProfile::kDefaultProfileUserName)),
+            prompt_controller().GetSourceNotice());
+}
+
 }  // namespace
 }  // namespace autofill
diff --git a/chrome/browser/autofill/android/autofill_fallback_surface_launcher.cc b/chrome/browser/autofill/android/autofill_fallback_surface_launcher.cc
index 088a396..8bdb60da 100644
--- a/chrome/browser/autofill/android/autofill_fallback_surface_launcher.cc
+++ b/chrome/browser/autofill/android/autofill_fallback_surface_launcher.cc
@@ -7,6 +7,7 @@
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/common/url_constants.h"
 #include "components/plus_addresses/core/common/features.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/android/view_android.h"
@@ -37,6 +38,16 @@
   }
 }
 
+void ShowGoogleWallePrivatePassesHelpCenterPageInCct(
+    content::WebContents& web_contents) {
+  if (web_contents.GetNativeView() &&
+      web_contents.GetNativeView()->GetWindowAndroid()) {
+    Java_AutofillFallbackSurfaceLauncher_openGoogleWalletPrivatePassHelpCenterPageInCct(
+        base::android::AttachCurrentThread(),
+        web_contents.GetNativeView()->GetWindowAndroid()->GetJavaObject());
+  }
+}
+
 static std::string
 JNI_AutofillFallbackSurfaceLauncher_GetPlusAddressManagementUrl(JNIEnv* env) {
   return plus_addresses::features::kPlusAddressManagementUrl.Get();
diff --git a/chrome/browser/autofill/android/autofill_fallback_surface_launcher.h b/chrome/browser/autofill/android/autofill_fallback_surface_launcher.h
index 431ea6c..35ec0d4 100644
--- a/chrome/browser/autofill/android/autofill_fallback_surface_launcher.h
+++ b/chrome/browser/autofill/android/autofill_fallback_surface_launcher.h
@@ -17,6 +17,10 @@
 // Opens the Google Wallet passes management UI web page in a Chrome Custom Tab.
 void ShowGoogleWalletPassesPage(content::WebContents& web_contents);
 
+// Opens the Google Wallet private passes help center page in Cct.
+void ShowGoogleWallePrivatePassesHelpCenterPageInCct(
+    content::WebContents& web_contents);
+
 }  // namespace autofill
 
 #endif  // CHROME_BROWSER_AUTOFILL_ANDROID_AUTOFILL_FALLBACK_SURFACE_LAUNCHER_H_
diff --git a/chrome/browser/autofill/android/entity_data_manager_android.cc b/chrome/browser/autofill/android/entity_data_manager_android.cc
index 3d0dadc..c847a8f3 100644
--- a/chrome/browser/autofill/android/entity_data_manager_android.cc
+++ b/chrome/browser/autofill/android/entity_data_manager_android.cc
@@ -237,7 +237,8 @@
           EntityTypeAndroid(
               type,
               type.enabled(entity_data_manager_->GetVariationCountryCode()),
-              IsEligibleForWalletStorage(type)),
+              IsEligibleForWalletStorage(type),
+              IsMaskedStorageSupported(type, entity->record_type())),
           entity->type().GetNameForI18n(),
           base::JoinString(label, kLabelSeparator),
           entity->record_type() == EntityInstance::RecordType::kServerWallet);
@@ -254,7 +255,9 @@
     entity_types.emplace_back(
         entity_type,
         entity_type.enabled(entity_data_manager_->GetVariationCountryCode()),
-        IsEligibleForWalletStorage(entity_type));
+        IsEligibleForWalletStorage(entity_type),
+        IsMaskedStorageSupported(entity_type,
+                                 EntityInstance::RecordType::kServerWallet));
   }
   return entity_types;
 }
@@ -268,7 +271,9 @@
   return base::ToVector(all_types, [this](const EntityType& type) {
     return EntityTypeAndroid(
         type, type.enabled(entity_data_manager_->GetVariationCountryCode()),
-        IsEligibleForWalletStorage(type));
+        IsEligibleForWalletStorage(type),
+        IsMaskedStorageSupported(type,
+                                 EntityInstance::RecordType::kServerWallet));
   });
 }
 
diff --git a/chrome/browser/autofill/android/entity_instance_android.cc b/chrome/browser/autofill/android/entity_instance_android.cc
index cd900ba0..8e19189 100644
--- a/chrome/browser/autofill/android/entity_instance_android.cc
+++ b/chrome/browser/autofill/android/entity_instance_android.cc
@@ -62,7 +62,9 @@
     bool requires_reauth_to_see)
     : entity_type(entity_instance.type(),
                   is_enabled,
-                  is_eligible_for_wallet_storage),
+                  is_eligible_for_wallet_storage,
+                  IsMaskedStorageSupported(entity_instance.type(),
+                                           entity_instance.record_type())),
       guid(*entity_instance.guid()),
       record_type(entity_instance.record_type()),
       metadata(entity_instance.date_modified(),
diff --git a/chrome/browser/autofill/android/entity_instance_android_unittest.cc b/chrome/browser/autofill/android/entity_instance_android_unittest.cc
index e1107d63..401aa83 100644
--- a/chrome/browser/autofill/android/entity_instance_android_unittest.cc
+++ b/chrome/browser/autofill/android/entity_instance_android_unittest.cc
@@ -35,7 +35,8 @@
   EntityTypeAndroid entity_type_android(
       entity_type,
       /*is_enabled=*/true,
-      /*is_eligible_for_wallet_storage=*/false);
+      /*is_eligible_for_wallet_storage=*/false,
+      /*is_masked_storage_supported=*/true);
   AttributeType attribute_type(AttributeTypeName::kPassportName);
   AttributeTypeAndroid passport_name_attribute_type_android(attribute_type);
 
@@ -67,7 +68,8 @@
   EntityTypeAndroid entity_type_android(
       entity_type,
       /*is_enabled=*/true,
-      /*is_eligible_for_wallet_storage=*/false);
+      /*is_eligible_for_wallet_storage=*/false,
+      /*is_masked_storage_supported=*/true);
   AttributeType attribute_type(AttributeTypeName::kPassportName);
   AttributeTypeAndroid password_name_attribute_type_android(attribute_type);
 
@@ -111,7 +113,8 @@
   EntityTypeAndroid entity_type_android(
       entity_type,
       /*is_enabled=*/true,
-      /*is_eligible_for_wallet_storage=*/false);
+      /*is_eligible_for_wallet_storage=*/false,
+      /*is_masked_storage_supported=*/true);
 
   // First create an attribute for an existing entity with a
   // value differently from what is received from Java. Meaning it is different
diff --git a/chrome/browser/autofill/android/entity_type_android.cc b/chrome/browser/autofill/android/entity_type_android.cc
index 9c2b825..51c32595 100644
--- a/chrome/browser/autofill/android/entity_type_android.cc
+++ b/chrome/browser/autofill/android/entity_type_android.cc
@@ -22,7 +22,8 @@
       env, static_cast<int32_t>(entity_type.type_name),
       entity_type.is_read_only, entity_type.is_enabled,
       entity_type.is_eligible_for_wallet_storage,
-      entity_type.type_name_as_string, entity_type.type_name_as_metrics_string,
+      entity_type.is_masked_storage_supported, entity_type.type_name_as_string,
+      entity_type.type_name_as_metrics_string,
       entity_type.add_entity_type_string, entity_type.edit_entity_type_string,
       entity_type.delete_entity_type_string, entity_type.attribute_types,
       entity_type.required_types);
@@ -35,16 +36,19 @@
       EntityType(static_cast<EntityTypeName>(
           Java_EntityType_getTypeName(env, j_entity_type))),
       Java_EntityType_isEnabled(env, j_entity_type),
-      Java_EntityType_isEligibleForWalletStorage(env, j_entity_type));
+      Java_EntityType_isEligibleForWalletStorage(env, j_entity_type),
+      Java_EntityType_isMaskedStorageSupported(env, j_entity_type));
 }
 
 EntityTypeAndroid::EntityTypeAndroid(const EntityType& entity_type,
                                      bool is_enabled,
-                                     bool is_eligible_for_wallet_storage)
+                                     bool is_eligible_for_wallet_storage,
+                                     bool is_masked_storage_supported)
     : type_name(entity_type.name()),
       is_read_only(entity_type.read_only()),
       is_enabled(is_enabled),
       is_eligible_for_wallet_storage(is_eligible_for_wallet_storage),
+      is_masked_storage_supported(is_masked_storage_supported),
       type_name_as_string(entity_type.GetNameForI18n()),
       type_name_as_metrics_string(EntityTypeToMetricsString(entity_type)),
       add_entity_type_string(GetAddEntityTypeStringForI18n(entity_type)),
diff --git a/chrome/browser/autofill/android/entity_type_android.h b/chrome/browser/autofill/android/entity_type_android.h
index e4c9d2a..2d0ce55d 100644
--- a/chrome/browser/autofill/android/entity_type_android.h
+++ b/chrome/browser/autofill/android/entity_type_android.h
@@ -28,7 +28,8 @@
 
   explicit EntityTypeAndroid(const EntityType& entity_type,
                              bool is_enabled,
-                             bool is_eligible_for_wallet_storage);
+                             bool is_eligible_for_wallet_storage,
+                             bool is_masked_storage_supported);
   EntityTypeAndroid(const EntityTypeAndroid&);
   EntityTypeAndroid& operator=(const EntityTypeAndroid&);
   EntityTypeAndroid(EntityTypeAndroid&&);
@@ -41,6 +42,7 @@
   bool is_read_only;
   bool is_enabled;
   bool is_eligible_for_wallet_storage;
+  bool is_masked_storage_supported;
   std::u16string type_name_as_string;
   std::string type_name_as_metrics_string;
   std::string add_entity_type_string;
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPrompt.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPrompt.java
index 2ff8a97d..8290390 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPrompt.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPrompt.java
@@ -209,9 +209,19 @@
         oldAttributeValue.setText(updateDetails.getOldAttributeValue());
     }
 
+    /**
+     * Sets the source notice message in the prompt.
+     *
+     * @param sourceNotice the full source notice string.
+     * @param insertManageInfoLink whether to insert a link to manage info. This value will be false
+     *     for local entities and true for entities stored in wallet. In the case it is true,
+     *     `sourceNotice` is expected to have a `R.string.autofill_manage_your_info_link` substring,
+     *     which will be turned into a clickable link.
+     */
     @CalledByNative
     @VisibleForTesting
-    void setSourceNotice(@JniType("std::u16string") String sourceNotice, boolean insertWalletLink) {
+    void setSourceNotice(
+            @JniType("std::u16string") String sourceNotice, boolean insertManageInfoLink) {
         TextView sourceNoticeView = mDialogView.findViewById(R.id.autofill_ai_entity_source_notice);
         if (TextUtils.isEmpty(sourceNotice)) {
             // The source notice can be empty if the C++ controller fails to retrieve the email
@@ -220,7 +230,7 @@
             return;
         }
 
-        if (!insertWalletLink) {
+        if (!insertManageInfoLink) {
             // Local entity source notice doesn't need a link.
             sourceNoticeView.setText(sourceNotice);
             return;
@@ -235,7 +245,7 @@
                                 new ChromeClickableSpan(
                                         mContext,
                                         view -> {
-                                            mController.openManagePasses();
+                                            mController.onWalletLinkClicked();
                                         })));
         sourceNoticeView.setText(sourceNoticeWithLink, TextView.BufferType.SPANNABLE);
         sourceNoticeView.setMovementMethod(LinkMovementMethod.getInstance());
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPromptController.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPromptController.java
index 02e38f45..6e417eb 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPromptController.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPromptController.java
@@ -37,10 +37,10 @@
         mNativeAutofillAiSaveUpdateEntityPromptController = 0;
     }
 
-    public void openManagePasses() {
+    public void onWalletLinkClicked() {
         if (mNativeAutofillAiSaveUpdateEntityPromptController != 0) {
             AutofillAiSaveUpdateEntityPromptControllerJni.get()
-                    .openManagePasses(mNativeAutofillAiSaveUpdateEntityPromptController);
+                    .onWalletLinkClicked(mNativeAutofillAiSaveUpdateEntityPromptController);
         }
     }
 
@@ -67,7 +67,7 @@
 
     @NativeMethods
     interface Natives {
-        void openManagePasses(long nativeAutofillAiSaveUpdateEntityPromptController);
+        void onWalletLinkClicked(long nativeAutofillAiSaveUpdateEntityPromptController);
 
         void onPromptDismissed(long nativeAutofillAiSaveUpdateEntityPromptController);
 
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorCoordinator.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorCoordinator.java
index 33fc4d50..f5e3f51 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorCoordinator.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorCoordinator.java
@@ -40,6 +40,13 @@
          * @param entityInstance the initial entity instance with no user changes.
          */
         default void onDelete(EntityInstance entityInstance) {}
+
+        /**
+         * The user clicked the "manage your info" link in the source notice to open either the
+         * Google Wallet passes page (when the entity is a public pass) or the help center article
+         * (when the entity is a private pass).
+         */
+        default void onOpenGoogleWallet(boolean isPrivateEntity) {}
     }
 
     /**
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorMediator.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorMediator.java
index 56de5f0..fa03d44 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorMediator.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorMediator.java
@@ -66,6 +66,8 @@
 import org.chromium.components.signin.identitymanager.IdentityManager;
 import org.chromium.ui.modelutil.ListModel;
 import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.text.ChromeClickableSpan;
+import org.chromium.ui.text.SpanApplier;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -214,8 +216,12 @@
                                     + attributeType.getDataType();
             }
         }
+
         maybeAddRequiredFieldsNoticeItem(editorFields);
-        maybeAddEntitySourceNoticeItem(editorFields, mEntityInstance.getRecordType());
+        maybeAddEntitySourceNoticeItem(
+                editorFields,
+                mEntityInstance.getRecordType(),
+                mEntityInstance.isMaskedServerEntity());
         return editorFields;
     }
 
@@ -350,8 +356,10 @@
     }
 
     private void maybeAddEntitySourceNoticeItem(
-            ListModel<EditorItem> editorFields, @RecordType int recordType) {
-        String sourceNotice = getEntitySourceNotice(recordType);
+            ListModel<EditorItem> editorFields,
+            @RecordType int recordType,
+            boolean isPrivateEntity) {
+        CharSequence sourceNotice = getEntitySourceNotice(recordType, isPrivateEntity);
         if (TextUtils.isEmpty(sourceNotice)) {
             return;
         }
@@ -359,24 +367,39 @@
                 new EditorItem(
                         NOTICE,
                         new PropertyModel.Builder(NOTICE_ALL_KEYS)
-                                .with(NOTICE_TEXT, getEntitySourceNotice(recordType))
+                                .with(NOTICE_TEXT, sourceNotice)
                                 .with(SHOW_BACKGROUND, true)
                                 .with(IMPORTANT_FOR_ACCESSIBILITY, true)
                                 .build(),
                         /* isFullLine= */ true));
     }
 
-    private String getEntitySourceNotice(@RecordType int recordType) {
+    private CharSequence getEntitySourceNotice(
+            @RecordType int recordType, boolean isPrivateEntity) {
         switch (recordType) {
             case RecordType.LOCAL:
                 return mContext.getString(R.string.autofill_ai_local_entity_editor_source_notice);
             case RecordType.SERVER_WALLET:
                 String email = getUserEmail();
-                return email == null
-                        ? ""
-                        : mContext.getString(
-                                        R.string.autofill_ai_wallet_entity_editor_source_notice)
-                                .replace("$1", email);
+                if (email == null) {
+                    return "";
+                }
+                String walletTitle = mContext.getString(R.string.autofill_google_wallet_title);
+                String sourceNotice =
+                        mContext.getString(
+                                        R.string
+                                                .autofill_ai_save_or_update_entity_in_wallet_source_notice)
+                                .replace("$1", walletTitle)
+                                .replace("$2", walletTitle)
+                                .replace("$3", email);
+                return SpanApplier.applySpans(
+                        sourceNotice,
+                        new SpanApplier.SpanInfo(
+                                "<link>",
+                                "</link>",
+                                new ChromeClickableSpan(
+                                        mContext,
+                                        view -> mDelegate.onOpenGoogleWallet(isPrivateEntity))));
         }
         assert false : "Invalid entity record type: " + recordType;
         return "";
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/common/EditorComponentsProperties.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/common/EditorComponentsProperties.java
index 08bd21b..a6ea24b 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/common/EditorComponentsProperties.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/common/EditorComponentsProperties.java
@@ -95,7 +95,7 @@
 
     /** Properties specific for the notice fields. */
     public static class NoticeProperties {
-        public static final ReadableObjectPropertyKey<String> NOTICE_TEXT =
+        public static final ReadableObjectPropertyKey<CharSequence> NOTICE_TEXT =
                 new ReadableObjectPropertyKey<>("notice_text");
 
         public static final ReadableBooleanPropertyKey SHOW_BACKGROUND =
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/common/EditorComponentsViewBinder.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/common/EditorComponentsViewBinder.java
index cccea9b4..cfa1bcba1 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/common/EditorComponentsViewBinder.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/common/EditorComponentsViewBinder.java
@@ -11,6 +11,7 @@
 import static org.chromium.chrome.browser.autofill.editors.common.EditorComponentsProperties.NoticeProperties.NOTICE_TEXT;
 import static org.chromium.chrome.browser.autofill.editors.common.EditorComponentsProperties.NoticeProperties.SHOW_BACKGROUND;
 
+import android.text.method.LinkMovementMethod;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -57,6 +58,7 @@
     public static void bindNoticeTextView(PropertyModel model, TextView view, PropertyKey key) {
         if (key == NOTICE_TEXT) {
             view.setText(model.get(NOTICE_TEXT));
+            view.setMovementMethod(LinkMovementMethod.getInstance());
         } else if (key == SHOW_BACKGROUND) {
             view.setBackgroundResource(
                     model.get(SHOW_BACKGROUND) ? R.drawable.autofill_editor_notice_background : 0);
diff --git a/chrome/browser/autofill/android/javatest/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPromptRenderTest.java b/chrome/browser/autofill/android/javatest/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPromptRenderTest.java
index e988904..8531e3b 100644
--- a/chrome/browser/autofill/android/javatest/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPromptRenderTest.java
+++ b/chrome/browser/autofill/android/javatest/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPromptRenderTest.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.base.ThreadUtils.runOnUiThreadBlocking;
 
+import android.content.Context;
 import android.view.View;
 
 import androidx.test.filters.MediumTest;
@@ -26,6 +27,7 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DoNotBatch;
 import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.autofill.R;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.night_mode.ChromeNightModeTestUtils;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
@@ -141,7 +143,8 @@
                                                     EntityAttributeUpdateType
                                                             .NEW_ENTITY_ATTRIBUTE_UNCHANGED)),
                                     /* isUpdatePrompt= */ false);
-                            mPrompt.setSourceNotice("Saved to this device", false);
+                            mPrompt.setSourceNotice(
+                                    "Saved to this device", /* insertManageInfoLink= */ false);
                             mPrompt.show();
                             return mPrompt.getDialogViewForTesting();
                         });
@@ -192,10 +195,18 @@
                                                     EntityAttributeUpdateType
                                                             .NEW_ENTITY_ATTRIBUTE_UNCHANGED)),
                                     /* isUpdatePrompt= */ true);
-                            mPrompt.setSourceNotice(
-                                    "Saved to your account. You can <link>manage passes</link> in"
-                                            + " Google Wallet",
-                                    true);
+                            Context context = mActivityTestRule.getActivity();
+                            String walletTitle =
+                                    context.getString(R.string.autofill_google_wallet_title);
+                            String email = "alexpark@gmail.com";
+                            String sourceNotice =
+                                    context.getString(
+                                                    R.string
+                                                            .autofill_ai_save_or_update_entity_in_wallet_source_notice)
+                                            .replace("$1", walletTitle)
+                                            .replace("$2", walletTitle)
+                                            .replace("$3", email);
+                            mPrompt.setSourceNotice(sourceNotice, /* insertManageInfoLink= */ true);
                             mPrompt.show();
                             return mPrompt.getDialogViewForTesting();
                         });
diff --git a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPromptTest.java b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPromptTest.java
index f56d2f33..c93415a 100644
--- a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPromptTest.java
+++ b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/autofill_ai/AutofillAiSaveUpdateEntityPromptTest.java
@@ -133,7 +133,7 @@
     @Test
     @SmallTest
     public void localSourceNotice() {
-        mPrompt.setSourceNotice("Entity will be saved locally", /* insertWalletLink= */ false);
+        mPrompt.setSourceNotice("Entity will be saved locally", /* insertManageInfoLink= */ false);
         mPrompt.show();
 
         View dialogView = mPrompt.getDialogViewForTesting();
@@ -144,7 +144,7 @@
     @Test
     @SmallTest
     public void emptyWalletNotice() {
-        mPrompt.setSourceNotice("", /* insertWalletLink= */ true);
+        mPrompt.setSourceNotice("", /* insertManageInfoLink= */ true);
         mPrompt.show();
 
         View dialogView = mPrompt.getDialogViewForTesting();
@@ -155,14 +155,26 @@
     @Test
     @SmallTest
     public void walletNotice() {
-        mPrompt.setSourceNotice(
-                "Entity will be <link>saved</link> to Wallet", /* insertWalletLink= */ true);
+        String walletTitle =
+                RuntimeEnvironment.application.getString(R.string.autofill_google_wallet_title);
+        String email = "alexpark@gmail.com";
+        String sourceNotice =
+                RuntimeEnvironment.application
+                        .getString(
+                                R.string.autofill_ai_save_or_update_entity_in_wallet_source_notice)
+                        .replace("$1", walletTitle)
+                        .replace("$2", walletTitle)
+                        .replace("$3", email);
+
+        mPrompt.setSourceNotice(sourceNotice, /* insertManageInfoLink= */ true);
         mPrompt.show();
 
         View dialogView = mPrompt.getDialogViewForTesting();
         TextView sourceNoticeView = dialogView.findViewById(R.id.autofill_ai_entity_source_notice);
         assertEquals(View.VISIBLE, sourceNoticeView.getVisibility());
-        assertEquals("Entity will be saved to Wallet", sourceNoticeView.getText().toString());
+        assertEquals(
+                sourceNotice.replace("<link>", "").replace("</link>", ""),
+                sourceNoticeView.getText().toString());
 
         SpannableString spannableString = (SpannableString) sourceNoticeView.getText();
         ClickableSpan[] spans =
@@ -170,7 +182,7 @@
         assertThat(spans.length, is(1));
         spans[0].onClick(sourceNoticeView);
         verify(mPromptControllerJni)
-                .openManagePasses(eq(NATIVE_AUTOFILL_AI_SAVE_UPDATE_ENTITY_PROMPT_CONTROLLER));
+                .onWalletLinkClicked(eq(NATIVE_AUTOFILL_AI_SAVE_UPDATE_ENTITY_PROMPT_CONTROLLER));
     }
 
     @Test
diff --git a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorModuleTest.java b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorModuleTest.java
index ee73502f..107bb88 100644
--- a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorModuleTest.java
+++ b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/autofill_ai/EntityEditorModuleTest.java
@@ -33,7 +33,9 @@
 import static org.chromium.chrome.browser.autofill.editors.utils.TestUtils.setDropdownValue;
 
 import android.app.Activity;
+import android.text.Spanned;
 import android.text.TextUtils;
+import android.text.style.ClickableSpan;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -131,6 +133,7 @@
                     /* isReadOnly= */ false,
                     /* isEnabled= */ true,
                     /* isEligibleForWalletStorage= */ false,
+                    /* isMaskedStorageSupported= */ true,
                     /* typeNameAsString= */ "Passport",
                     /* typeNameAsMetricsString= */ "Passport",
                     /* addEntityTypeString= */ "Add passport",
@@ -189,6 +192,15 @@
                                     PASSPORT_COUNTRY_ATTRIBUTE_TYPE, /* value= */ "Germany"))
                     .build();
 
+    private static final EntityInstance PRIVATE_WALLET_PASSPORT =
+            new EntityInstance.Builder(PASSPORT_TYPE)
+                    .setGUID("guid")
+                    .setRecordType(RecordType.SERVER_WALLET)
+                    .setIsMaskedServerEntity(true)
+                    .setModifiedDate(LocalDate.of(2026, 2, 15))
+                    .setUseCount(0)
+                    .build();
+
     private final CoreAccountInfo mAccountInfo =
             CoreAccountInfo.createFromEmailAndGaiaId(USER_EMAIL, new GaiaId("gaia_id"));
 
@@ -411,15 +423,45 @@
         when(mIdentityManager.getPrimaryAccountInfo(anyInt())).thenReturn(mAccountInfo);
         showEditorDialog(WALLET_PASSPORT);
 
-        PropertyModel model = mCoordinator.getEditorModelForTest();
-        verifyRequiredFieldsItem(
-                model.get(EntityEditorProperties.EDITOR_FIELDS),
-                mActivity.getString(R.string.payments_required_field_message));
-        verifySourceNotice(
-                model.get(EntityEditorProperties.EDITOR_FIELDS),
+        String walletTitle = mActivity.getString(R.string.autofill_google_wallet_title);
+        String expectedNoticeText =
                 mActivity
-                        .getString(R.string.autofill_ai_wallet_entity_editor_source_notice)
-                        .replace("$1", USER_EMAIL));
+                        .getString(
+                                R.string.autofill_ai_save_or_update_entity_in_wallet_source_notice)
+                        .replace("$1", walletTitle)
+                        .replace("$2", walletTitle)
+                        .replace("$3", USER_EMAIL)
+                        .replace("<link>", "")
+                        .replace("</link>", "");
+
+        PropertyModel model = mCoordinator.getEditorModelForTest();
+        verifySourceNotice(model.get(EntityEditorProperties.EDITOR_FIELDS), expectedNoticeText);
+    }
+
+    @Test
+    @SmallTest
+    public void testWalletEntitySourceNotice_ClickLink() {
+        when(mIdentityManager.getPrimaryAccountInfo(anyInt())).thenReturn(mAccountInfo);
+        showEditorDialog(WALLET_PASSPORT);
+
+        PropertyModel model = mCoordinator.getEditorModelForTest();
+        ListModel<EditorItem> editorFields = model.get(EntityEditorProperties.EDITOR_FIELDS);
+
+        clickSourceNoticeLink();
+        verify(mDelegate).onOpenGoogleWallet(false);
+    }
+
+    @Test
+    @SmallTest
+    public void testPrivateWalletEntitySourceNotice_ClickLink() {
+        when(mIdentityManager.getPrimaryAccountInfo(anyInt())).thenReturn(mAccountInfo);
+        showEditorDialog(PRIVATE_WALLET_PASSPORT);
+
+        PropertyModel model = mCoordinator.getEditorModelForTest();
+        ListModel<EditorItem> editorFields = model.get(EntityEditorProperties.EDITOR_FIELDS);
+
+        clickSourceNoticeLink();
+        verify(mDelegate).onOpenGoogleWallet(true);
     }
 
     @Test
@@ -637,7 +679,8 @@
 
     private void verifySourceNotice(ListModel<EditorItem> editorFields, String expectedNoticeText) {
         for (EditorItem item : editorFields) {
-            if (item.type == NOTICE && expectedNoticeText.equals(item.model.get(NOTICE_TEXT))) {
+            if (item.type == NOTICE
+                    && expectedNoticeText.equals(item.model.get(NOTICE_TEXT).toString())) {
                 assertTrue(item.model.get(SHOW_BACKGROUND));
                 assertTrue(item.model.get(IMPORTANT_FOR_ACCESSIBILITY));
                 return;
@@ -645,4 +688,25 @@
         }
         fail("Source notice not found");
     }
+
+    private void clickClickableSpan(CharSequence text) {
+        Spanned spanned = (Spanned) text;
+        ClickableSpan[] spans = spanned.getSpans(0, text.length(), ClickableSpan.class);
+        assertEquals(1, spans.length);
+        spans[0].onClick(null);
+    }
+
+    private void clickSourceNoticeLink() {
+        PropertyModel model = mCoordinator.getEditorModelForTest();
+        ListModel<EditorItem> editorFields = model.get(EntityEditorProperties.EDITOR_FIELDS);
+
+        for (EditorItem item : editorFields) {
+            if (item.type == NOTICE && item.model.get(SHOW_BACKGROUND)) {
+                CharSequence noticeText = item.model.get(NOTICE_TEXT);
+                clickClickableSpan(noticeText);
+                return;
+            }
+        }
+        fail("Source notice not found");
+    }
 }
diff --git a/chrome/browser/autofill/autofill_field_classification_model_service_factory.cc b/chrome/browser/autofill/autofill_field_classification_model_service_factory.cc
index a1c2a1a..b1624e69 100644
--- a/chrome/browser/autofill/autofill_field_classification_model_service_factory.cc
+++ b/chrome/browser/autofill/autofill_field_classification_model_service_factory.cc
@@ -8,8 +8,9 @@
 
 #include "base/no_destructor.h"
 #include "chrome/browser/autofill/ml_log_router_factory.h"
-#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
-#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
+#include "chrome/browser/optimization_guide/model_execution/optimization_guide_global_state.h"
+#include "chrome/browser/optimization_guide/optimization_guide_global_state_holder_keyed_service.h"
+#include "chrome/browser/optimization_guide/optimization_guide_global_state_holder_keyed_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/autofill/core/browser/ml_model/logging/ml_log_router.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
@@ -38,7 +39,8 @@
     : BrowserContextKeyedServiceFactory(
           "FieldClassificationModelHandler",
           BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(OptimizationGuideKeyedServiceFactory::GetInstance());
+  DependsOn(
+      OptimizationGuideGlobalStateHolderKeyedServiceFactory::GetInstance());
 }
 
 AutofillFieldClassificationModelServiceFactory::
@@ -49,8 +51,9 @@
     content::BrowserContext* context) const {
   // `FieldClassificationModelHandler` is not supported without an
   // `OptimizationGuideKeyedService`.
-  return OptimizationGuideKeyedServiceFactory::GetForProfile(
-             Profile::FromBrowserContext(context))
+  Profile* profile = Profile::FromBrowserContext(context);
+  return OptimizationGuideGlobalStateHolderKeyedServiceFactory::GetForProfile(
+             profile)
              ? context
              : nullptr;
 }
@@ -58,9 +61,14 @@
 std::unique_ptr<KeyedService> AutofillFieldClassificationModelServiceFactory::
     BuildServiceInstanceForBrowserContext(
         content::BrowserContext* context) const {
-  OptimizationGuideKeyedService* optimization_guide =
-      OptimizationGuideKeyedServiceFactory::GetForProfile(
-          Profile::FromBrowserContext(context));
+  Profile* profile = Profile::FromBrowserContext(context);
+  OptimizationGuideGlobalStateHolderKeyedService* global_state_holder =
+      OptimizationGuideGlobalStateHolderKeyedServiceFactory::GetForProfile(
+          profile);
+  optimization_guide::OptimizationGuideModelProvider* optimization_guide =
+      global_state_holder
+          ? &global_state_holder->GetGlobalState().prediction_manager()
+          : nullptr;
   MlLogRouter* log_router = MlLogRouterFactory::GetForProfile(
           Profile::FromBrowserContext(context));
   return std::make_unique<FieldClassificationModelHandler>(
diff --git a/chrome/browser/chrome_browser_interface_binders_webui_parts_desktop.cc b/chrome/browser/chrome_browser_interface_binders_webui_parts_desktop.cc
index 1e8ac80..1b77697 100644
--- a/chrome/browser/chrome_browser_interface_binders_webui_parts_desktop.cc
+++ b/chrome/browser/chrome_browser_interface_binders_webui_parts_desktop.cc
@@ -379,7 +379,7 @@
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
       NewTabPageUI>(map);
 
-  RegisterWebUIControllerInterfaceBinder<searchbox::mojom::PageHandler,
+  RegisterWebUIControllerInterfaceBinder<searchbox::mojom::PageHandlerFactory,
                                          NewTabPageUI, OmniboxPopupUI>(map);
 
   RegisterWebUIControllerInterfaceBinder<suggest_internals::mojom::PageHandler,
@@ -685,7 +685,7 @@
         .Add<webui_browser::mojom::PageHandlerFactory>()
         .Add<bookmark_bar::mojom::PageHandlerFactory>()
         .Add<extensions_bar::mojom::PageHandlerFactory>()
-        .Add<searchbox::mojom::PageHandler>()
+        .Add<searchbox::mojom::PageHandlerFactory>()
         .Add<tabs_api::mojom::TabStripService>()
         .Add<tracked_element::mojom::TrackedElementHandler>();
   }
@@ -711,7 +711,7 @@
     registry.ForWebUI<lens::LensSidePanelUntrustedUI>()
         .Add<lens::mojom::LensSidePanelPageHandlerFactory>()
         .Add<lens::mojom::LensGhostLoaderPageHandlerFactory>()
-        .Add<searchbox::mojom::PageHandler>()
+        .Add<searchbox::mojom::PageHandlerFactory>()
         .Add<help_bubble::mojom::HelpBubbleHandlerFactory>()
         .Add<composebox::mojom::PageHandlerFactory>();
   }
@@ -720,7 +720,7 @@
         .Add<lens::mojom::LensPageHandlerFactory>()
         .Add<lens::mojom::LensGhostLoaderPageHandlerFactory>()
         .Add<help_bubble::mojom::HelpBubbleHandlerFactory>()
-        .Add<searchbox::mojom::PageHandler>();
+        .Add<searchbox::mojom::PageHandlerFactory>();
   }
   registry.ForWebUI<ReadAnythingUntrustedUI>();
 
diff --git a/chrome/browser/chrome_for_testing/BUILD.gn b/chrome/browser/chrome_for_testing/BUILD.gn
index 65bcb7e..11e4a717 100644
--- a/chrome/browser/chrome_for_testing/BUILD.gn
+++ b/chrome/browser/chrome_for_testing/BUILD.gn
@@ -78,12 +78,14 @@
   sources = [
     "test/chrome_for_testing_browsertest.cc",
     "test/chrome_for_testing_browsertest.h",
+    "test/chrome_for_testing_infobar_browsertest.cc",
   ]
 
   deps = [
     ":browser",
     "//base",
     "//chrome/browser",
+    "//chrome/browser/infobars",
     "//chrome/browser/user_education",
     "//chrome/test:test_support",
     "//components/headless/clipboard",
diff --git a/chrome/browser/chrome_for_testing/chrome_for_testing_info_bar_browsertest.cc b/chrome/browser/chrome_for_testing/test/chrome_for_testing_infobar_browsertest.cc
similarity index 97%
rename from chrome/browser/chrome_for_testing/chrome_for_testing_info_bar_browsertest.cc
rename to chrome/browser/chrome_for_testing/test/chrome_for_testing_infobar_browsertest.cc
index 8b9b953..951b33fd 100644
--- a/chrome/browser/chrome_for_testing/chrome_for_testing_info_bar_browsertest.cc
+++ b/chrome/browser/chrome_for_testing/test/chrome_for_testing_infobar_browsertest.cc
@@ -20,8 +20,10 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace infobars {
+using infobars::ContentInfoBarManager;
+using infobars::InfoBarDelegate;
 
+namespace chrome_for_testing {
 namespace {
 
 // TODO (crbug.com/469533286): fix ChromeForTestingInfoBarTest for win-cft
@@ -184,5 +186,4 @@
 }
 
 }  // namespace
-
-}  // namespace infobars
+}  // namespace chrome_for_testing
diff --git a/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/CoBrowseViewFactory.java b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/CoBrowseViewFactory.java
index 52bc42c2..0a3e5bc 100644
--- a/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/CoBrowseViewFactory.java
+++ b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/CoBrowseViewFactory.java
@@ -88,11 +88,12 @@
                 showFusebox || TabBottomSheetUtils.shouldShowFusebox()
                         ? new ContextualTasksFusebox(
                                 mActivity,
+                                mFuseboxConfig.contentView,
                                 mFuseboxConfig,
                                 mProfileSupplier,
                                 mWindowAndroid,
                                 mLifecycleDispatcher,
-                                CallbackUtils.emptyCallback(),
+                                /* loadUrlCallback= */ CallbackUtils.emptyCallback(),
                                 mSnackbarManager)
                         : null;
 
diff --git a/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetContent.java b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetContent.java
index d690c0a..966d18a 100644
--- a/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetContent.java
+++ b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetContent.java
@@ -147,8 +147,8 @@
     }
 
     @Override
-    public boolean canSuppressInAnyState() {
-        return false;
+    public boolean canBeSuppressed(BottomSheetContent nextContent) {
+        return true;
     }
 
     @Override
diff --git a/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetManager.java b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetManager.java
index 76f9df01..44bcf86 100644
--- a/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetManager.java
+++ b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetManager.java
@@ -7,14 +7,18 @@
 import android.content.Context;
 import android.view.View;
 
+import org.chromium.base.Callback;
 import org.chromium.base.CallbackController;
+import org.chromium.base.ResettersForTesting;
 import org.chromium.base.lifetime.Destroyable;
+import org.chromium.base.supplier.NullableObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplier;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver;
 import org.chromium.chrome.browser.layouts.LayoutType;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.widget.TouchEventProvider;
 import org.chromium.ui.base.WindowAndroid;
@@ -48,10 +52,7 @@
                 public void onStartedShowing(@LayoutType int layoutType) {
                     if (layoutType == LayoutType.TAB_SWITCHER) {
                         mIsSuppressedOnTabSwitcher = true;
-                        if (mTabBottomSheetCoordinator != null
-                                && mNativeInterfaceDelegate != null) {
-                            mTabBottomSheetCoordinator.closeBottomSheet();
-                        }
+                        maybeCloseBottomSheet();
                     }
                 }
 
@@ -64,13 +65,7 @@
                             int nextLayoutType =
                                     mLayoutStateProviderOneShotSupplier.get().getNextLayoutType();
                             if (nextLayoutType == LayoutType.BROWSING) {
-                                if (mTabBottomSheetCoordinator != null
-                                        && mNativeInterfaceDelegate != null) {
-                                    if (!mTabBottomSheetCoordinator.tryToShowBottomSheet(
-                                            /* animate= */ false, /* startsExpanded= */ false)) {
-                                        notifyOnClose();
-                                    }
-                                }
+                                maybeShowBottomSheet();
                             }
                         }
                     }
@@ -85,6 +80,11 @@
     private final CallbackController mCallbackController = new CallbackController();
 
     private boolean mIsSuppressedOnTabSwitcher;
+    private boolean mIsSuppressedByReadAloud;
+
+    private @Nullable NullableObservableSupplier<Tab> mActivePlaybackTabSupplier;
+    private final Callback<@Nullable Tab> mActivePlaybackTabObserver =
+            this::onActivePlaybackTabChanged;
 
     private @Nullable TabBottomSheetCoordinator mTabBottomSheetCoordinator;
     private @Nullable NativeInterfaceDelegate mNativeInterfaceDelegate;
@@ -223,8 +223,25 @@
         return mTabBottomSheetCoordinator != null && mTabBottomSheetCoordinator.isSheetShowing();
     }
 
+    /**
+     * Sets the supplier for the active playback tab from ReadAloud.
+     *
+     * @param activePlaybackTabSupplier The supplier.
+     */
+    public void setReadAloudActivePlaybackTabSupplier(
+            NullableObservableSupplier<Tab> activePlaybackTabSupplier) {
+        assert mActivePlaybackTabSupplier == null;
+        mActivePlaybackTabSupplier = activePlaybackTabSupplier;
+        mActivePlaybackTabSupplier.addSyncObserverAndCallIfNonNull(mActivePlaybackTabObserver);
+    }
+
     @Override
     public void destroy() {
+        if (mActivePlaybackTabSupplier != null) {
+            mActivePlaybackTabSupplier.removeObserver(mActivePlaybackTabObserver);
+            mActivePlaybackTabSupplier = null;
+        }
+
         mCallbackController.destroy();
 
         // Destroy the coorinator in case the manager is abruptly destroyed before hiding the bottom
@@ -243,7 +260,7 @@
     }
 
     private void onBottomSheetClosed() {
-        if (!mIsSuppressedOnTabSwitcher) {
+        if (!mIsSuppressedOnTabSwitcher && !mIsSuppressedByReadAloud) {
             notifyOnClose();
         }
     }
@@ -254,15 +271,43 @@
             mNativeInterfaceDelegate = null;
         }
         // Destroy the sheet after notifying native of the close event.
-        // The only time the sheet isn't destroyed is if we enter the tab switcher, in which case
-        // we close the sheet but hold only the coordinator to reshow the sheet if we return to the
-        // same tab.
+        // The only time the sheet isn't destroyed is if we enter the tab switcher or read aloud is
+        // playing, in which case we close the sheet but hold only the coordinator to reshow the
+        // sheet if we return to the same tab or read aloud stops.
         if (mTabBottomSheetCoordinator != null) {
             mTabBottomSheetCoordinator.destroy();
             mTabBottomSheetCoordinator = null;
         }
     }
 
+    private void onActivePlaybackTabChanged(@Nullable Tab tab) {
+        if (tab != null) {
+            mIsSuppressedByReadAloud = true;
+            maybeCloseBottomSheet();
+        } else {
+            mIsSuppressedByReadAloud = false;
+            maybeShowBottomSheet();
+        }
+    }
+
+    private void maybeCloseBottomSheet() {
+        if (mTabBottomSheetCoordinator != null && mNativeInterfaceDelegate != null) {
+            mTabBottomSheetCoordinator.closeBottomSheet();
+        }
+    }
+
+    private void maybeShowBottomSheet() {
+        if (!mIsSuppressedOnTabSwitcher && !mIsSuppressedByReadAloud) {
+
+            if (mTabBottomSheetCoordinator != null && mNativeInterfaceDelegate != null) {
+                if (!mTabBottomSheetCoordinator.tryToShowBottomSheet(
+                        /* animate= */ false, /* startsExpanded= */ false)) {
+                    notifyOnClose();
+                }
+            }
+        }
+    }
+
     /* Testing methods */
     public @Nullable TabBottomSheetCoordinator getTabBottomSheetCoordinatorForTesting() {
         return mTabBottomSheetCoordinator;
@@ -271,4 +316,26 @@
     public @Nullable NativeInterfaceDelegate getNativeInterfaceDelegateForTesting() {
         return mNativeInterfaceDelegate;
     }
+
+    void setReadAloudActivePlaybackTabSupplierForTesting(
+            NullableObservableSupplier<Tab> activePlaybackTabSupplier) {
+        var oldSupplier = mActivePlaybackTabSupplier;
+        if (oldSupplier != null) {
+            oldSupplier.removeObserver(mActivePlaybackTabObserver);
+        }
+
+        mActivePlaybackTabSupplier = activePlaybackTabSupplier;
+        mActivePlaybackTabSupplier.addSyncObserverAndCallIfNonNull(mActivePlaybackTabObserver);
+        ResettersForTesting.register(
+                () -> {
+                    if (mActivePlaybackTabSupplier != null) {
+                        mActivePlaybackTabSupplier.removeObserver(mActivePlaybackTabObserver);
+                    }
+                    mActivePlaybackTabSupplier = oldSupplier;
+                    if (mActivePlaybackTabSupplier != null) {
+                        mActivePlaybackTabSupplier.addSyncObserverAndCallIfNonNull(
+                                mActivePlaybackTabObserver);
+                    }
+                });
+    }
 }
diff --git a/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetManagerTest.java b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetManagerTest.java
index 71ef06a..b6f0418 100644
--- a/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetManagerTest.java
+++ b/chrome/browser/context_sharing/tab_bottom_sheet/android/java/src/org/chromium/chrome/browser/tab_bottom_sheet/TabBottomSheetManagerTest.java
@@ -17,6 +17,8 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.ThreadUtils;
+import org.chromium.base.supplier.ObservableSuppliers;
+import org.chromium.base.supplier.SettableNullableObservableSupplier;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
@@ -24,11 +26,14 @@
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.content.WebContentsFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab_bottom_sheet.TabBottomSheetManager.NativeInterfaceDelegate;
 import org.chromium.chrome.browser.tabbed_mode.TabbedRootUiCoordinator;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.transit.ChromeTransitTestRules;
 import org.chromium.chrome.test.transit.FreshCtaTransitTestRule;
+import org.chromium.chrome.test.transit.hub.RegularTabSwitcherStation;
+import org.chromium.chrome.test.transit.page.WebPageStation;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.content.browser.input.ImeAdapterImpl;
 import org.chromium.content_public.browser.LoadUrlParams;
@@ -44,6 +49,7 @@
     public FreshCtaTransitTestRule mActivityTestRule =
             ChromeTransitTestRules.freshChromeTabbedActivityRule();
 
+    private WebPageStation mInitialStation;
     private CoBrowseViews mCoBrowseViews;
     private ChromeTabbedActivity mActivity;
     private WindowAndroid mWindowAndroid;
@@ -52,7 +58,7 @@
 
     @Before
     public void setUp() throws InterruptedException {
-        mActivityTestRule.startOnBlankPage();
+        mInitialStation = mActivityTestRule.startOnBlankPage();
 
         mActivity = mActivityTestRule.getActivity();
 
@@ -130,4 +136,61 @@
                     coBrowseViews.destroy();
                 });
     }
+
+    @Test
+    @SmallTest
+    public void testBottomSheetHiddenOnTabSwitcher() {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    mManager.tryToShowBottomSheet(
+                            NativeInterfaceDelegate.getInstance(),
+                            mCoBrowseViews,
+                            /* animate= */ false,
+                            /* startsExpanded= */ true);
+                });
+        CriteriaHelper.pollUiThread(() -> mManager.isSheetShowing());
+
+        // Open tab switcher
+        RegularTabSwitcherStation tabSwitcher = mInitialStation.openRegularTabSwitcher();
+
+        CriteriaHelper.pollUiThread(() -> !mManager.isSheetShowing());
+
+        // Close tab switcher
+        tabSwitcher.leaveHubToPreviousTabViaBack(WebPageStation.newBuilder());
+
+        CriteriaHelper.pollUiThread(() -> mManager.isSheetShowing());
+    }
+
+    @Test
+    @SmallTest
+    public void testBottomSheetHiddenOnReadAloud() {
+        SettableNullableObservableSupplier<Tab> readAloudTabSupplier =
+                ThreadUtils.runOnUiThreadBlocking(
+                        () -> {
+                            SettableNullableObservableSupplier<Tab> supplier =
+                                    ObservableSuppliers.createNullable();
+                            return supplier;
+                        });
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    mManager.setReadAloudActivePlaybackTabSupplierForTesting(readAloudTabSupplier);
+                    mManager.tryToShowBottomSheet(
+                            NativeInterfaceDelegate.getInstance(),
+                            mCoBrowseViews,
+                            /* animate= */ false,
+                            /* startsExpanded= */ true);
+                });
+        CriteriaHelper.pollUiThread(() -> mManager.isSheetShowing());
+
+        // Fake start read aloud
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> readAloudTabSupplier.set(mActivity.getActivityTab()));
+
+        CriteriaHelper.pollUiThread(() -> !mManager.isSheetShowing());
+
+        // Stop read aloud
+        ThreadUtils.runOnUiThreadBlocking(() -> readAloudTabSupplier.set(null));
+
+        CriteriaHelper.pollUiThread(() -> mManager.isSheetShowing());
+    }
 }
diff --git a/chrome/browser/context_sharing/tab_bottom_sheet/android/tab_bottom_sheet_bridge.cc b/chrome/browser/context_sharing/tab_bottom_sheet/android/tab_bottom_sheet_bridge.cc
index ad8a92d02..e9d36de 100644
--- a/chrome/browser/context_sharing/tab_bottom_sheet/android/tab_bottom_sheet_bridge.cc
+++ b/chrome/browser/context_sharing/tab_bottom_sheet/android/tab_bottom_sheet_bridge.cc
@@ -48,6 +48,10 @@
 }
 
 void TabBottomSheetBridge::SetWebContents(content::WebContents* web_contents) {
+  if (web_contents) {
+    web_contents->SetIgnoreZoomGestures(true);
+  }
+
   if (!co_browse_views_) {
     CreateCoBrowseViews(web_contents);
     return;
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_controller.cc b/chrome/browser/contextual_cueing/contextual_cueing_controller.cc
index e4a9a11b..37f335d 100644
--- a/chrome/browser/contextual_cueing/contextual_cueing_controller.cc
+++ b/chrome/browser/contextual_cueing/contextual_cueing_controller.cc
@@ -4,14 +4,18 @@
 
 #include "chrome/browser/contextual_cueing/contextual_cueing_controller.h"
 
+#include <algorithm>
 #include <memory>
 #include <optional>
+#include <vector>
 
+#include "base/memory/raw_ptr.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros_local.h"
 #include "base/notimplemented.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
 #include "chrome/browser/contextual_cueing/contextual_cueing_service.h"
 #include "chrome/browser/contextual_cueing/contextual_cueing_service_factory.h"
 #include "chrome/browser/contextual_cueing/features.h"
@@ -184,6 +188,12 @@
       active_web_contents->GetLastCommittedURL().spec());
   request.mutable_active_tab_page_context()->set_title(
       base::UTF16ToUTF8(active_web_contents->GetTitle()));
+
+  struct BackgroundTabInfo {
+    base::Time last_active_time;
+    raw_ptr<content::WebContents> contents;
+  };
+  std::vector<BackgroundTabInfo> background_tabs;
   for (int i = 0; i < tab_list_interface_->GetTabCount(); ++i) {
     tabs::TabInterface* tab = tab_list_interface_->GetTab(i);
     if (tab == tab_list_interface_->GetActiveTab()) {
@@ -197,8 +207,23 @@
     if (!tab_contents->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) {
       continue;
     }
-    *request.add_background_tabs() = GetTabProtoFromWebContents(tab_contents);
+    background_tabs.push_back(
+        {.last_active_time = tab_contents->GetLastActiveTime(),
+         .contents = tab_contents});
   }
+
+  std::sort(background_tabs.begin(), background_tabs.end(),
+            [](const BackgroundTabInfo& a, const BackgroundTabInfo& b) {
+              return a.last_active_time > b.last_active_time;
+            });
+
+  for (size_t i = 0; i < std::min<size_t>(background_tabs.size(),
+                                          kMaxNumBackgroundTabs.Get());
+       ++i) {
+    *request.add_background_tabs() =
+        GetTabProtoFromWebContents(background_tabs[i].contents);
+  }
+
   LOCAL_HISTOGRAM_COUNTS_100("ContextualCueing.V2.NumRequestedBackgroundTabs",
                              request.background_tabs_size());
   optimization_guide_keyed_service_->ExecuteModel(
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_controller_browsertest.cc b/chrome/browser/contextual_cueing/contextual_cueing_controller_browsertest.cc
index 85b5466..43c92ff 100644
--- a/chrome/browser/contextual_cueing/contextual_cueing_controller_browsertest.cc
+++ b/chrome/browser/contextual_cueing/contextual_cueing_controller_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/functional/bind.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
+#include "base/strings/stringprintf.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/run_until.h"
 #include "chrome/browser/contextual_cueing/contextual_cueing_service.h"
@@ -392,5 +393,30 @@
       ContextualCueingDecision::kFeaturePromoActive, 1);
 }
 
+IN_PROC_BROWSER_TEST_F(ContextualCueingControllerBrowserTest,
+                       OnlySendsTopMaxBackgroundTabs) {
+  // Create 15 tabs.
+  for (int i = 0; i < kMaxNumBackgroundTabs.Get() + 1; ++i) {
+    ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
+        browser(), GURL(base::StringPrintf("https://www.google.com/%d", i)),
+        WindowOpenDisposition::NEW_FOREGROUND_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
+  }
+
+  base::HistogramTester histogram_tester;
+  SeedExecutionResult(MakeCompleteResponse());
+
+  SimulateFilterPassed();
+
+  optimization_guide::RetryForHistogramUntilCountReached(
+      &histogram_tester, "ContextualCueing.V2.Decision", 1);
+
+  // 15 background tabs + 1 active tab.
+  // We expect only the max allowed background tabs to be requested.
+  histogram_tester.ExpectUniqueSample(
+      "ContextualCueing.V2.NumRequestedBackgroundTabs",
+      kMaxNumBackgroundTabs.Get(), 1);
+}
+
 }  // namespace
 }  // namespace contextual_cueing
diff --git a/chrome/browser/contextual_cueing/features.cc b/chrome/browser/contextual_cueing/features.cc
index 20831b69..42645ea1 100644
--- a/chrome/browser/contextual_cueing/features.cc
+++ b/chrome/browser/contextual_cueing/features.cc
@@ -18,4 +18,9 @@
     "ContextualCueingV2ShoppingClassifierThreshold",
     0.5);
 
+const base::FeatureParam<int> kMaxNumBackgroundTabs(
+    &kContextualCueingV2,
+    "ContextualCueingV2MaxNumBackgroundTabs",
+    10);
+
 }  // namespace contextual_cueing
diff --git a/chrome/browser/contextual_cueing/features.h b/chrome/browser/contextual_cueing/features.h
index 527411113..e53cea6e 100644
--- a/chrome/browser/contextual_cueing/features.h
+++ b/chrome/browser/contextual_cueing/features.h
@@ -13,6 +13,7 @@
 BASE_DECLARE_FEATURE(kContextualCueingV2);
 extern const base::FeatureParam<double> kEduClassifierThreshold;
 extern const base::FeatureParam<double> kShoppingClassifierThreshold;
+extern const base::FeatureParam<int> kMaxNumBackgroundTabs;
 
 }  // namespace contextual_cueing
 
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc
index d240768c..88fedfc 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc
+++ b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.cc
@@ -157,6 +157,7 @@
     mojo::PendingRemote<composebox::mojom::Page> pending_page,
     mojo::PendingReceiver<searchbox::mojom::PageHandler>
         pending_searchbox_handler,
+    mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page,
     GetSessionHandleCallback get_session_callback,
     ClearSessionHandleCallback clear_session_callback,
     TakeInputStateModelCallback take_input_model_callback)
@@ -164,6 +165,7 @@
           std::move(pending_handler),
           std::move(pending_page),
           std::move(pending_searchbox_handler),
+          std::move(pending_searchbox_page),
           profile,
           web_contents,
           std::make_unique<OmniboxController>(
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h
index 93f8bd6..139fedae 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h
+++ b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h
@@ -59,6 +59,7 @@
       mojo::PendingRemote<composebox::mojom::Page> pending_page,
       mojo::PendingReceiver<searchbox::mojom::PageHandler>
           pending_searchbox_handler,
+      mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page,
       GetSessionHandleCallback get_session_callback,
       ClearSessionHandleCallback clear_session_callback,
       TakeInputStateModelCallback take_input_model_callback);
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc
index 7e5bf6e..d247520 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc
+++ b/chrome/browser/contextual_tasks/contextual_tasks_composebox_handler_unittest.cc
@@ -344,6 +344,7 @@
         mojo::PendingReceiver<composebox::mojom::PageHandler>(),
         std::move(page_remote),
         mojo::PendingReceiver<searchbox::mojom::PageHandler>(),
+        searchbox_page_receiver_.BindNewPipeAndPassRemote(),
         base::BindRepeating(
             &ContextualTasksUI::GetOrCreateContextualSessionHandle,
             base::Unretained(mock_ui_.get())),
@@ -374,10 +375,6 @@
                                                error_type);
             });
 
-    auto searchbox_page_remote =
-        searchbox_page_receiver_.BindNewPipeAndPassRemote();
-    handler_->SetPage(std::move(searchbox_page_remote));
-
     // Setup MockTabContextualizationController
     tabs::TabInterface* active_tab =
         TabListInterface::From(browser())->GetActiveTab();
@@ -2882,12 +2879,16 @@
   mojo::PendingRemote<composebox::mojom::Page> page_remote;
   mojo::PendingReceiver<composebox::mojom::Page> page_receiver =
       page_remote.InitWithNewPipeAndPassReceiver();
+  mojo::PendingRemote<searchbox::mojom::Page> searchbox_page_remote;
+  mojo::PendingReceiver<searchbox::mojom::Page> searchbox_page_receiver =
+      searchbox_page_remote.InitWithNewPipeAndPassReceiver();
 
   auto handler = std::make_unique<TestContextualTasksComposeboxHandler>(
       mock_ui_.get(), profile(), web_contents(),
       mojo::PendingReceiver<composebox::mojom::PageHandler>(),
       std::move(page_remote),
       mojo::PendingReceiver<searchbox::mojom::PageHandler>(),
+      std::move(searchbox_page_remote),
       base::BindRepeating(
           []() -> contextual_search::ContextualSearchSessionHandle* {
             return nullptr;
@@ -2949,12 +2950,17 @@
       &ContextualTasksComposeboxHandlerTest::CreateMockInputStateModel,
       base::Unretained(this));
   mojo::PendingRemote<composebox::mojom::Page> page_remote;
-  auto page_receiver = page_remote.InitWithNewPipeAndPassReceiver();
+  mojo::PendingReceiver<composebox::mojom::Page> page_receiver =
+      page_remote.InitWithNewPipeAndPassReceiver();
+  mojo::PendingRemote<searchbox::mojom::Page> searchbox_page_remote;
+  mojo::PendingReceiver<searchbox::mojom::Page> searchbox_page_receiver =
+      searchbox_page_remote.InitWithNewPipeAndPassReceiver();
   auto custom_handler = std::make_unique<TestContextualTasksComposeboxHandler>(
       mock_ui_.get(), profile(), web_contents(),
       mojo::PendingReceiver<composebox::mojom::PageHandler>(),
       std::move(page_remote),
       mojo::PendingReceiver<searchbox::mojom::PageHandler>(),
+      std::move(searchbox_page_remote),
       base::BindRepeating(
           &ContextualTasksUI::GetOrCreateContextualSessionHandle,
           base::Unretained(mock_ui_.get())),
@@ -2992,13 +2998,18 @@
       mock_session_ptr);
 
   mojo::PendingRemote<composebox::mojom::Page> page_remote;
-  auto page_receiver = page_remote.InitWithNewPipeAndPassReceiver();
+  mojo::PendingReceiver<composebox::mojom::Page> page_receiver =
+      page_remote.InitWithNewPipeAndPassReceiver();
+  mojo::PendingRemote<searchbox::mojom::Page> searchbox_page_remote;
+  mojo::PendingReceiver<searchbox::mojom::Page> searchbox_page_receiver =
+      searchbox_page_remote.InitWithNewPipeAndPassReceiver();
 
   auto custom_handler = std::make_unique<TestContextualTasksComposeboxHandler>(
       mock_ui_.get(), profile(), web_contents(),
       mojo::PendingReceiver<composebox::mojom::PageHandler>(),
       std::move(page_remote),
       mojo::PendingReceiver<searchbox::mojom::PageHandler>(),
+      std::move(searchbox_page_remote),
       mock_get_session_callback,
       base::BindRepeating(&ContextualTasksUI::ClearContextualSessionHandle,
                           base::Unretained(mock_ui_.get())),
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_page_handler.cc b/chrome/browser/contextual_tasks/contextual_tasks_page_handler.cc
index 76bd173..9e009c5 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_page_handler.cc
+++ b/chrome/browser/contextual_tasks/contextual_tasks_page_handler.cc
@@ -202,6 +202,12 @@
     return;
   }
 
+  // If the task is waiting for a URL to be generated, register a callback.
+  if (ui_service_->IsTaskWaitingForUrl(uuid)) {
+    ui_service_->AddPendingUrlCallback(uuid, std::move(callback));
+    return;
+  }
+
   // There's a slight difference in the callback signature between the mojo
   // api (wants a reference) and the ui service (provided a moved object).
   // The latter can't provide a reference since we're not keeping it
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc b/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc
index 70b5540..394f9a9 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc
+++ b/chrome/browser/contextual_tasks/contextual_tasks_side_panel_coordinator_interactive_uitest.cc
@@ -52,6 +52,7 @@
       mojo::PendingRemote<composebox::mojom::Page> pending_page,
       mojo::PendingReceiver<searchbox::mojom::PageHandler>
           pending_searchbox_handler,
+      mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page,
       GetSessionHandleCallback get_session_callback,
       ClearSessionHandleCallback clear_session_callback,
       TakeInputStateModelCallback get_inputstatemodel_callback)
@@ -62,6 +63,7 @@
             std::move(pending_handler),
             std::move(pending_page),
             std::move(pending_searchbox_handler),
+            std::move(pending_searchbox_page),
             std::move(get_session_callback),
             std::move(clear_session_callback),
             std::move(get_inputstatemodel_callback)) {}
@@ -682,6 +684,7 @@
           std::move(composebox_handler_receiver),
           std::move(composebox_page_remote),
           std::move(searchbox_handler_receiver),
+          std::move(searchbox_page_remote),
           base::BindRepeating(
               &ContextualTasksUI::GetOrCreateContextualSessionHandle,
               base::Unretained(ui)),
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_ui.cc b/chrome/browser/contextual_tasks/contextual_tasks_ui.cc
index 5c1328c..827aa4e8 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_ui.cc
+++ b/chrome/browser/contextual_tasks/contextual_tasks_ui.cc
@@ -742,7 +742,7 @@
   auto handler = std::make_unique<ContextualTasksComposeboxHandler>(
       this, Profile::FromWebUI(web_ui()), web_ui()->GetWebContents(),
       std::move(pending_page_handler), std::move(pending_page),
-      std::move(pending_searchbox_handler),
+      std::move(pending_searchbox_handler), std::move(pending_searchbox_page),
       base::BindRepeating(
           &ContextualTasksUI::GetOrCreateContextualSessionHandle,
           base::Unretained(this)),
@@ -750,7 +750,6 @@
                           base::Unretained(this)),
       base::BindRepeating(&ContextualTasksUI::TakeInputStateModel,
                           base::Unretained(this)));
-  handler->SetPage(std::move(pending_searchbox_page));
   composebox_handler_ = std::move(handler);
 }
 #endif  // !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_ui_browsertest.cc b/chrome/browser/contextual_tasks/contextual_tasks_ui_browsertest.cc
index e0d1d6f4..5c04459 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_ui_browsertest.cc
+++ b/chrome/browser/contextual_tasks/contextual_tasks_ui_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/functional/bind.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
+#include "base/test/run_until.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/unguessable_token.h"
 #include "chrome/browser/contextual_search/contextual_search_service_factory.h"
@@ -14,7 +15,10 @@
 #include "chrome/browser/contextual_tasks/contextual_tasks.mojom.h"
 #include "chrome/browser/contextual_tasks/contextual_tasks_composebox_handler.h"
 #include "chrome/browser/contextual_tasks/contextual_tasks_cookie_synchronizer.h"
+#include "chrome/browser/contextual_tasks/contextual_tasks_panel_controller.h"
 #include "chrome/browser/contextual_tasks/contextual_tasks_service_factory.h"
+#include "chrome/browser/contextual_tasks/contextual_tasks_ui_service.h"
+#include "chrome/browser/contextual_tasks/contextual_tasks_ui_service_factory.h"
 #include "chrome/browser/contextual_tasks/contextual_tasks_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
@@ -47,6 +51,7 @@
 #include "content/public/test/test_web_ui.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "mojo/public/cpp/bindings/receiver.h"
+#include "net/base/url_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/lens_server_proto/aim_communication.pb.h"
 #include "ui/webui/resources/cr_components/composebox/composebox.mojom.h"
@@ -561,6 +566,30 @@
             zoom_controller->zoom_mode());
 }
 
+IN_PROC_BROWSER_TEST_F(ContextualTasksNoMockBrowserTest,
+                       InitSidePanelWithGhostLoader_WaitUntilPanelOpen) {
+  auto* service =
+      contextual_tasks::ContextualTasksUiServiceFactory::GetForBrowserContext(
+          browser()->profile());
+  auto* tab = TabListInterface::From(browser())->GetActiveTab();
+
+  // Call InitSidePanelWithGhostLoader.
+  service->InitSidePanelWithGhostLoader(browser(), tab, nullptr);
+
+  // Wait for side panel to open and load WebUI.
+  auto* controller =
+      contextual_tasks::ContextualTasksPanelController::From(browser());
+  ASSERT_TRUE(controller);
+  EXPECT_TRUE(base::test::RunUntil(
+      [&]() { return controller->IsPanelOpenForContextualTask(); }));
+
+  content::WebContents* web_contents = controller->GetActiveWebContents();
+  ASSERT_TRUE(web_contents);
+
+  // Wait for load stop on that web_contents.
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents));
+}
+
 IN_PROC_BROWSER_TEST_F(ContextualTasksUIBrowserTest,
                        UpdateModelFromUrlOnNavigation) {
   omnibox::SearchboxConfig config;
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc b/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc
index 31a5772..5386f59 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc
+++ b/chrome/browser/contextual_tasks/contextual_tasks_ui_service.cc
@@ -1119,6 +1119,16 @@
   return std::nullopt;
 }
 
+void ContextualTasksUiService::AddPendingUrlCallback(
+    const base::Uuid& task_id,
+    base::OnceCallback<void(const GURL&)> callback) {
+  tasks_waiting_for_url_[task_id] = std::move(callback);
+}
+
+bool ContextualTasksUiService::IsTaskWaitingForUrl(const base::Uuid& task_id) {
+  return tasks_waiting_for_url_.contains(task_id);
+}
+
 void ContextualTasksUiService::GetThreadUrlFromTaskId(
     const base::Uuid& task_id,
     base::OnceCallback<void(GURL)> callback) {
@@ -1250,6 +1260,7 @@
     const GURL& url,
     std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
         session_handle) {
+  CHECK(!url.is_empty());
   CHECK(contextual_tasks_service_);
 
   // Get the controller for the current window.
@@ -1271,6 +1282,19 @@
 
   // If the side panel contents already exist, get the WebUI controller to
   // load the URL into the already loaded contextual tasks UI.
+  auto* helper = ContextualSearchWebContentsHelper::GetOrCreateForWebContents(
+      panel_contents);
+  // If the task was waiting for a URL to be generated (e.g. opened early
+  // with ghost loader but no URL), provide the URL now to unblock the WebUI's
+  // initial pull request via GetUrlForTask.
+  if (helper->task_id().has_value() &&
+      IsTaskWaitingForUrl(helper->task_id().value())) {
+    OnInitialThreadUrlAvailable(helper->task_id().value(), url);
+    return;
+  }
+
+  // Otherwise, if the panel is already open and initialized, push the
+  // navigation directly to the embedded page.
   if (ContextualTasksUIInterface* web_ui_interface =
           GetWebUiInterface(panel_contents)) {
     content::OpenURLParams url_params(
@@ -1280,6 +1304,34 @@
   }
 }
 
+void ContextualTasksUiService::InitSidePanelWithGhostLoader(
+    BrowserWindowInterface* browser_window_interface,
+    tabs::TabInterface* tab_interface,
+    std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+        session_handle) {
+  CHECK(contextual_tasks_service_);
+
+  // Get the controller for the current window.
+  auto* controller =
+      ContextualTasksPanelController::From(browser_window_interface);
+  auto* panel_contents = controller->GetActiveWebContents();
+  // If the side panel is already open for a task, do nothing. The ghost
+  // loader will be shown whenever the URL navigation is transferred to the
+  // embedded page.
+  if (panel_contents || controller->IsPanelOpenForContextualTask()) {
+    return;
+  }
+
+  // Create a task for the URL if the side panel wasn't already showing a task.
+  ContextualTask task = contextual_tasks_service_->CreateTask();
+  tasks_waiting_for_url_[task.GetTaskId()] = base::NullCallback();
+  AssociateWebContentsToTask(tab_interface->GetContents(), task.GetTaskId());
+  controller->Show();
+
+  InitializeTaskInSidePanel(controller->GetActiveWebContents(),
+                            task.GetTaskId(), std::move(session_handle));
+}
+
 void ContextualTasksUiService::StartTaskUiInSidePanelWithErrorPage(
     BrowserWindowInterface* browser_window_interface,
     tabs::TabInterface* tab_interface,
@@ -1528,4 +1580,17 @@
   return false;
 }
 
+void ContextualTasksUiService::OnInitialThreadUrlAvailable(
+    const base::Uuid& task_id,
+    const GURL& url) {
+  task_id_to_creation_url_[task_id] = url;
+  auto it = tasks_waiting_for_url_.find(task_id);
+  if (it != tasks_waiting_for_url_.end()) {
+    if (it->second) {
+      std::move(it->second).Run(GetInitialUrlForTask(task_id).value());
+    }
+    tasks_waiting_for_url_.erase(it);
+  }
+}
+
 }  // namespace contextual_tasks
diff --git a/chrome/browser/contextual_tasks/contextual_tasks_ui_service.h b/chrome/browser/contextual_tasks/contextual_tasks_ui_service.h
index 3c67d7b6..eb1505a7 100644
--- a/chrome/browser/contextual_tasks/contextual_tasks_ui_service.h
+++ b/chrome/browser/contextual_tasks/contextual_tasks_ui_service.h
@@ -138,6 +138,16 @@
   // entry is removed from the cache.
   virtual std::optional<GURL> GetInitialUrlForTask(const base::Uuid& uuid);
 
+  // Adds a callback to be run when the URL for a task becomes available.
+  // This is only used in cases where the side panel is "warmed up" (i.e. using
+  // very specific *GhostLoader methods).
+  virtual void AddPendingUrlCallback(
+      const base::Uuid& task_id,
+      base::OnceCallback<void(const GURL&)> callback);
+
+  // Returns whether the task is waiting for a URL to be generated.
+  virtual bool IsTaskWaitingForUrl(const base::Uuid& task_id);
+
   // Get a thread URL based on the task ID. If no task is found or the task does
   // not have a thread ID, the default AI URL is returned.
   virtual void GetThreadUrlFromTaskId(const base::Uuid& task_id,
@@ -165,6 +175,15 @@
       std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
           session_handle);
 
+  // Opens the contextual tasks side panel showing a ghost loader while waiting
+  // for the initial thread URL to be provided for that task. This creates an
+  // empty task. If the panel is already open for a task, this is a no-op.
+  virtual void InitSidePanelWithGhostLoader(
+      BrowserWindowInterface* browser_window_interface,
+      tabs::TabInterface* tab_interface,
+      std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+          session_handle);
+
   // Opens the contextual tasks side panel with the protected error page showing
   // by default.
   virtual void StartTaskUiInSidePanelWithErrorPage(
@@ -326,6 +345,11 @@
   // Navigates to a share URL.
   virtual void OnShareUrlNavigation(const GURL& url);
 
+  // Sets the initial thread URL for a given task and runs any pending
+  // callbacks.
+  virtual void OnInitialThreadUrlAvailable(const base::Uuid& task_id,
+                                           const GURL& url);
+
   // Checks if the provided URL matches any of the allowed hosts.
   static bool IsAllowedHost(const GURL& url);
 
@@ -374,6 +398,12 @@
   std::map<base::Uuid, contextual_search::ContextualSearchSource>
       pending_error_page_tasks_;
 
+  // Map of tasks that are waiting for a URL to be generated. The value is a
+  // callback to be run when the URL becomes available, or null if no callback
+  // has been added yet.
+  std::map<base::Uuid, base::OnceCallback<void(const GURL&)>>
+      tasks_waiting_for_url_;
+
   base::WeakPtrFactory<ContextualTasksUiService> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/contextual_tasks/fusebox/android/BUILD.gn b/chrome/browser/contextual_tasks/fusebox/android/BUILD.gn
index 47ff162..09f4427 100644
--- a/chrome/browser/contextual_tasks/fusebox/android/BUILD.gn
+++ b/chrome/browser/contextual_tasks/fusebox/android/BUILD.gn
@@ -8,23 +8,29 @@
   sources = [
     "java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFusebox.java",
     "java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFuseboxDataProvider.java",
+    "java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFuseboxManager.java",
   ]
   deps = [
     "//base:base_java",
+    "//base:supplier_java",
+    "//base:unowned_user_data_java",
     "//chrome/browser/android/lifecycle:java",
     "//chrome/browser/back_press/android:java",
     "//chrome/browser/browser_controls/android:java",
     "//chrome/browser/profiles/android:java",
     "//chrome/browser/tab:java",
     "//chrome/browser/tabmodel:java",
+    "//chrome/browser/tabwindow:java",
     "//chrome/browser/ui/android/edge_to_edge:java",
     "//chrome/browser/ui/android/omnibox:java",
+    "//chrome/browser/ui/android/omnibox:java_resources",
     "//chrome/browser/ui/android/page_info:java",
     "//chrome/browser/ui/messages/android:java",
     "//components/browser_ui/accessibility/android:lib_java",
     "//components/browser_ui/styles/android:java",
     "//components/omnibox/browser:browser_java",
     "//components/security_state/core:security_state_enums_java",
+    "//content/public/android:content_full_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/metrics_proto:metrics_proto_java",
     "//ui/android:ui_java",
diff --git a/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFusebox.java b/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFusebox.java
index 415b366..3da4a00 100644
--- a/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFusebox.java
+++ b/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFusebox.java
@@ -4,12 +4,14 @@
 
 package org.chromium.chrome.browser.contextual_tasks.fusebox;
 
+import static org.chromium.build.NullUtil.assumeNonNull;
+
 import android.app.Activity;
 import android.view.View;
 
 import org.chromium.base.Callback;
 import org.chromium.base.CallbackUtils;
-import org.chromium.base.supplier.NonNullObservableSupplier;
+import org.chromium.base.supplier.MonotonicObservableSupplier;
 import org.chromium.base.supplier.ObservableSuppliers;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
@@ -59,17 +61,18 @@
 
     public ContextualTasksFusebox(
             Activity activity,
+            View contentView,
             ContextualTasksFuseboxConfig config,
-            NonNullObservableSupplier<Profile> profileSupplier,
+            MonotonicObservableSupplier<Profile> profileSupplier,
             WindowAndroid windowAndroid,
             ActivityLifecycleDispatcher lifecycleDispatcher,
             Callback<String> loadUrlCallback,
             SnackbarManager snackbarManager) {
 
         mDataProvider = new ContextualTasksFuseboxDataProvider();
-        mDataProvider.initialize(activity, profileSupplier.get().isOffTheRecord());
+        mDataProvider.initialize(activity, assumeNonNull(profileSupplier.get()).isOffTheRecord());
 
-        mContentView = config.contentView;
+        mContentView = contentView;
         View locationBarLayout = config.locationBarLayout;
         View anchorView = config.anchorView;
         View controlContainer = config.controlContainer;
@@ -132,7 +135,7 @@
         mDataProvider.destroy();
     }
 
-    /* Returns the fusebox view */
+    /** Returns the fusebox view. */
     public View getFuseboxView() {
         return mContentView;
     }
diff --git a/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFuseboxManager.java b/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFuseboxManager.java
new file mode 100644
index 0000000..308cd88
--- /dev/null
+++ b/chrome/browser/contextual_tasks/fusebox/android/java/src/org/chromium/chrome/browser/contextual_tasks/fusebox/ContextualTasksFuseboxManager.java
@@ -0,0 +1,141 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.contextual_tasks.fusebox;
+
+import android.app.Activity;
+import android.view.View;
+
+import org.chromium.base.CallbackUtils;
+import org.chromium.base.UnownedUserDataKey;
+import org.chromium.base.supplier.MonotonicObservableSupplier;
+import org.chromium.base.supplier.NullableObservableSupplier;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.tab.CurrentTabObserver;
+import org.chromium.chrome.browser.tab.EmptyTabObserver;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
+import org.chromium.content_public.browser.NavigationHandle;
+import org.chromium.ui.base.WindowAndroid;
+import org.chromium.url.GURL;
+
+import java.util.function.Supplier;
+
+/**
+ * Manages the lifecycle and visibility of a single {@link ContextualTasksFusebox} instance overlaid
+ * on regular tabs.
+ */
+@NullMarked
+public class ContextualTasksFuseboxManager {
+    private static final UnownedUserDataKey<ContextualTasksFuseboxManager> KEY =
+            new UnownedUserDataKey<>();
+
+    private final Activity mActivity;
+    private final Supplier<ContextualTasksFusebox.ContextualTasksFuseboxConfig>
+            mFuseboxConfigSupplier;
+    private final WindowAndroid mWindowAndroid;
+    private final ActivityLifecycleDispatcher mLifecycleDispatcher;
+    private final MonotonicObservableSupplier<Profile> mProfileSupplier;
+    private final Supplier<SnackbarManager> mSnackbarManagerSupplier;
+    private final CurrentTabObserver mCurrentTabObserver;
+
+    // The fusebox instance. Shared across all tabs. Lazily initialized.
+    private @Nullable ContextualTasksFusebox mFusebox;
+
+    public ContextualTasksFuseboxManager(
+            Activity activity,
+            Supplier<ContextualTasksFusebox.ContextualTasksFuseboxConfig> fuseboxConfigSupplier,
+            NullableObservableSupplier<Tab> tabSupplier,
+            WindowAndroid windowAndroid,
+            ActivityLifecycleDispatcher lifecycleDispatcher,
+            MonotonicObservableSupplier<Profile> profileSupplier,
+            Supplier<SnackbarManager> snackbarManagerSupplier) {
+        mActivity = activity;
+        mFuseboxConfigSupplier = fuseboxConfigSupplier;
+        mWindowAndroid = windowAndroid;
+        mLifecycleDispatcher = lifecycleDispatcher;
+        mProfileSupplier = profileSupplier;
+        mSnackbarManagerSupplier = snackbarManagerSupplier;
+
+        mCurrentTabObserver =
+                new CurrentTabObserver(
+                        tabSupplier,
+                        new EmptyTabObserver() {
+                            @Override
+                            public void didFirstVisuallyNonEmptyPaint(Tab tab) {
+                                updateFuseboxVisibility(tab);
+                            }
+
+                            @Override
+                            public void onDidFinishNavigationInPrimaryMainFrame(
+                                    Tab tab, NavigationHandle navigation) {
+                                updateFuseboxVisibility(tab);
+                            }
+                        },
+                        this::updateFuseboxVisibility);
+
+        KEY.attachToHost(mWindowAndroid.getUnownedUserDataHost(), this);
+    }
+
+    /**
+     * Helper method to retrieve the {@link ContextualTasksFuseboxManager} instance from a given
+     * {@link WindowAndroid}.
+     *
+     * @param windowAndroid The window to retrieve the manager from.
+     * @return The manager for the given window, or null if none exists.
+     */
+    public static @Nullable ContextualTasksFuseboxManager from(WindowAndroid windowAndroid) {
+        return KEY.retrieveDataFromHost(windowAndroid.getUnownedUserDataHost());
+    }
+
+    private void updateFuseboxVisibility(@Nullable Tab currentTab) {
+        if (currentTab != null && isContextualTasksUrl(currentTab.getUrl())) {
+            ensureFuseboxInitialized();
+            setFuseboxVisible(true);
+        } else {
+            setFuseboxVisible(false);
+        }
+    }
+
+    private boolean isContextualTasksUrl(GURL url) {
+        if (url == null || url.isEmpty() || !url.isValid()) return false;
+        // TODO(crbug.com/491504815): Do an exact check, or better yet call native.
+        return url.getSpec().startsWith("chrome://contextual-tasks");
+    }
+
+    private void setFuseboxVisible(boolean visible) {
+        if (mFusebox == null) return;
+        // TODO(crbug.com/491504815): Create a new fusebox every time, and pass WebContents info via
+        // the LocationBarDataProvider.
+        mFusebox.getFuseboxView().setVisibility(visible ? View.VISIBLE : View.GONE);
+    }
+
+    private void ensureFuseboxInitialized() {
+        if (mFusebox != null) return;
+
+        ContextualTasksFusebox.ContextualTasksFuseboxConfig config = mFuseboxConfigSupplier.get();
+
+        mFusebox =
+                new ContextualTasksFusebox(
+                        mActivity,
+                        config.contentView,
+                        config,
+                        mProfileSupplier,
+                        mWindowAndroid,
+                        mLifecycleDispatcher,
+                        /* loadUrlCallback= */ CallbackUtils.emptyCallback(),
+                        mSnackbarManagerSupplier.get());
+    }
+
+    public void destroy() {
+        KEY.detachFromHost(mWindowAndroid.getUnownedUserDataHost());
+        mCurrentTabObserver.destroy();
+        if (mFusebox != null) {
+            mFusebox.destroy();
+        }
+    }
+}
diff --git a/chrome/browser/extensions/api/sessions/sessions_apitest.cc b/chrome/browser/extensions/api/sessions/sessions_apitest.cc
index f6b004f..a11110d 100644
--- a/chrome/browser/extensions/api/sessions/sessions_apitest.cc
+++ b/chrome/browser/extensions/api/sessions/sessions_apitest.cc
@@ -704,15 +704,6 @@
     // Close the tab we just opened (at index 1).
     int tab_index = 1;
     content::WebContents* tab = GetWebContentsAt(tab_index);
-    // On Android, historical tabs are automatically created when a tab is
-    // closed via the HistoricalTabSaver; there is no need to manually create
-    // them like on desktop.
-#if !BUILDFLAG(IS_ANDROID)
-    // Our cross-platform utility function to close a tab doesn't allow
-    // requesting the creation of a historical tab, so do it manually.
-    service->CreateHistoricalTab(
-        sessions::ContentLiveTab::GetOrCreateForWebContents(tab), tab_index);
-#endif  // !BUILDFLAG(IS_ANDROID)
     // Close the tab (and wait for its destruction internally).
     CloseTabForWebContents(tab);
   }
@@ -763,16 +754,6 @@
   const int tab_index = 1;
   content::WebContents* tab = GetWebContentsAt(tab_index);
 
-  // On Android, historical tabs are automatically created when a tab is
-  // closed via the HistoricalTabSaver; there is no need to manually create
-  // them like on desktop.
-#if !BUILDFLAG(IS_ANDROID)
-  // Our cross-platform utility function to close a tab doesn't allow
-  // requesting the creation of a historical tab, so do it manually.
-  service->CreateHistoricalTab(
-      sessions::ContentLiveTab::GetOrCreateForWebContents(tab), tab_index);
-#endif  // !BUILDFLAG(IS_ANDROID)
-
   // Close the tab (and wait for its destruction internally).
   CloseTabForWebContents(tab);
 
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 4a38855..eb675a5 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -1358,8 +1358,6 @@
     (*s_allowlist)[glic::prefs::kGlicUserStatus] =
         settings_api::PrefType::kDictionary;
     (*s_allowlist)[prefs::kGeminiSettings] = settings_api::PrefType::kNumber;
-    (*s_allowlist)[glic::prefs::kGlicUserEnabledActuationOnWeb] =
-        settings_api::PrefType::kBoolean;
     (*s_allowlist)[glic::prefs::kGlicKeepSidepanelOpenOnNewTabsEnabled] =
         settings_api::PrefType::kBoolean;
     (*s_allowlist)[glic::prefs::kGlicExperimentalTriggeringEnabled] =
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
index 0f92f1a..7eeb5f5 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc
@@ -721,6 +721,17 @@
 
 void WebstorePrivateBeginInstallWithManifest3Function::OnExtensionApprovalDone(
     SupervisedExtensionApprovalResult result) {
+#if BUILDFLAG(IS_ANDROID)
+  if (result != SupervisedExtensionApprovalResult::kApproved &&
+      supervised_user::AreExtensionsPermissionsEnabled(profile_) &&
+      !supervised_user::SupervisedUserCanSkipExtensionParentApprovals(
+          profile_)) {
+    supervised_user_extensions_metrics_recorder_.RecordEnablementUmaMetrics(
+        SupervisedUserExtensionsMetricsRecorder::EnablementState::
+            kFailedToEnable);
+  }
+#endif  // BUILDFLAG(IS_ANDROID)
+
   switch (result) {
     case SupervisedExtensionApprovalResult::kApproved:
       OnExtensionApprovalApproved();
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
index bcb5be5..402eb44 100644
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc
@@ -925,6 +925,16 @@
       SupervisedUserExtensionsMetricsRecorder::kAskParentDialogHistogramName,
       SupervisedUserExtensionsMetricsRecorder::AskParentDialogState::kApproved,
       1);
+
+  // Verify the Enablement metrics.
+  EXPECT_EQ(
+      1,
+      user_action_tester.GetActionCount(
+          SupervisedUserExtensionsMetricsRecorder::kFailedToEnableActionName));
+  histogram_tester.ExpectBucketCount(
+      SupervisedUserExtensionsMetricsRecorder::kEnablementHistogramName,
+      SupervisedUserExtensionsMetricsRecorder::EnablementState::kFailedToEnable,
+      1);
 }
 
 // Tests that the parent approval install dialog is shown when the parent
@@ -994,6 +1004,16 @@
       SupervisedUserExtensionsMetricsRecorder::ExtensionInstallDialogState::
           kChildCanceled,
       1);
+
+  // Verify the Enablement metrics.
+  EXPECT_EQ(
+      1,
+      user_action_tester.GetActionCount(
+          SupervisedUserExtensionsMetricsRecorder::kFailedToEnableActionName));
+  histogram_tester.ExpectBucketCount(
+      SupervisedUserExtensionsMetricsRecorder::kEnablementHistogramName,
+      SupervisedUserExtensionsMetricsRecorder::EnablementState::kFailedToEnable,
+      1);
 }
 
 // Tests that the parent approval install dialog is shown when the parent
diff --git a/chrome/browser/first_run/android/java/src/org/chromium/chrome/browser/firstrun/MobileFreProgress.java b/chrome/browser/first_run/android/java/src/org/chromium/chrome/browser/firstrun/MobileFreProgress.java
index bba65fa..e653224 100644
--- a/chrome/browser/first_run/android/java/src/org/chromium/chrome/browser/firstrun/MobileFreProgress.java
+++ b/chrome/browser/first_run/android/java/src/org/chromium/chrome/browser/firstrun/MobileFreProgress.java
@@ -32,6 +32,8 @@
     MobileFreProgress.HISTORY_SYNC_OPT_IN_SHOWN,
     MobileFreProgress.HISTORY_SYNC_ACCEPTED,
     MobileFreProgress.HISTORY_SYNC_DISMISSED,
+    MobileFreProgress.DEFAULT_BROWSER_PROMO_ACCEPTED,
+    MobileFreProgress.DEFAULT_BROWSER_PROMO_REJECTED,
     MobileFreProgress.MAX,
 })
 @Retention(RetentionPolicy.SOURCE)
@@ -76,6 +78,12 @@
     /** The default browser promo primer was shown to the user. */
     int DEFAULT_BROWSER_PROMO_SHOWN = 15;
 
-    int MAX = 16;
+    /** The user accepted the default browser promo. */
+    int DEFAULT_BROWSER_PROMO_ACCEPTED = 16;
+
+    /** The user rejected the default browser promo. */
+    int DEFAULT_BROWSER_PROMO_REJECTED = 17;
+
+    int MAX = 18;
 }
 // LINT.ThenChange(//tools/metrics/histograms/metadata/mobile/enums.xml:MobileFreProgress)
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 15a6876..7f8f692 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -8875,6 +8875,11 @@
     "expiry_milestone": 150
   },
   {
+    "name": "tab-group-color-refresh",
+    "owners": [ "dominicaustria@google.com", "top-chrome-desktop-ui@google.com" ],
+    "expiry_milestone": 160
+  },
+  {
     "name": "tab-group-home",
     "owners": [ "chrome-shopping-eng@google.com" ],
     "expiry_milestone": 146
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index daaeb5e8..a376bd4 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -4058,6 +4058,10 @@
     "elements will appear to float over the contents, which resize at the "
     "beginning or end of the animation.";
 
+inline constexpr char kTabGroupColorRefreshName[] = "Tab Group Color Refresh";
+inline constexpr char kTabGroupColorRefreshDescription[] =
+    "Update the color selection for tab groups.";
+
 inline constexpr char kTabGroupsFocusingName[] = "Tab Groups Focusing";
 inline constexpr char kTabGroupsFocusingDescription[] =
     "When a tab group is focused, the tabstrip constrains visiblity to the "
diff --git a/chrome/browser/glic/android/glic_keyed_service_android.cc b/chrome/browser/glic/android/glic_keyed_service_android.cc
index 7cb9600..be305aa3 100644
--- a/chrome/browser/glic/android/glic_keyed_service_android.cc
+++ b/chrome/browser/glic/android/glic_keyed_service_android.cc
@@ -51,6 +51,11 @@
       service_->instance_coordinator().AddGlobalShowHideCallback(
           base::BindRepeating(&GlicKeyedServiceAndroid::OnGlobalShowHide,
                               base::Unretained(this)));
+  web_actuation_pref_subscription_ =
+      service_->enabling().RegisterOnUserEnabledActuationOnWebChanged(
+          base::BindRepeating(
+              &GlicKeyedServiceAndroid::OnUserEnabledActuationOnWebChanged,
+              base::Unretained(this)));
 }
 
 GlicKeyedServiceAndroid::~GlicKeyedServiceAndroid() {
@@ -83,6 +88,15 @@
   return service_->IsPanelShowingForBrowser(*window);
 }
 
+bool GlicKeyedServiceAndroid::GetUserEnabledActuationOnWeb(JNIEnv* env) {
+  return service_->enabling().GetUserEnabledActuationOnWeb();
+}
+
+void GlicKeyedServiceAndroid::SetUserEnabledActuationOnWeb(JNIEnv* env,
+                                                           bool enabled) {
+  service_->enabling().SetUserEnabledActuationOnWeb(enabled);
+}
+
 void GlicKeyedServiceAndroid::OnGlobalShowHide() {
   JNIEnv* env = base::android::AttachCurrentThread();
   bool is_opened = service_->instance_coordinator().state() !=
@@ -90,6 +104,13 @@
   Java_GlicKeyedServiceImpl_onGlobalShowHide(env, java_obj_, is_opened);
 }
 
+void GlicKeyedServiceAndroid::OnUserEnabledActuationOnWebChanged() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  bool enabled = service_->enabling().GetUserEnabledActuationOnWeb();
+  Java_GlicKeyedServiceImpl_onUserEnabledActuationOnWebChanged(env, java_obj_,
+                                                               enabled);
+}
+
 }  // namespace glic
 
 DEFINE_JNI(GlicKeyedServiceImpl)
diff --git a/chrome/browser/glic/android/glic_keyed_service_android.h b/chrome/browser/glic/android/glic_keyed_service_android.h
index 7664376..eace6c4 100644
--- a/chrome/browser/glic/android/glic_keyed_service_android.h
+++ b/chrome/browser/glic/android/glic_keyed_service_android.h
@@ -41,7 +41,11 @@
 
   bool IsPanelShowingForBrowser(JNIEnv* env, int64_t browser_window_ptr);
 
+  bool GetUserEnabledActuationOnWeb(JNIEnv* env);
+  void SetUserEnabledActuationOnWeb(JNIEnv* env, bool enabled);
+
   void OnGlobalShowHide();
+  void OnUserEnabledActuationOnWebChanged();
 
   // Returns the GlicKeyedServiceImpl java object.
   base::android::ScopedJavaLocalRef<jobject> GetJavaObject();
@@ -55,6 +59,7 @@
   base::android::ScopedJavaGlobalRef<jobject> java_obj_;
 
   base::CallbackListSubscription global_show_hide_subscription_;
+  base::CallbackListSubscription web_actuation_pref_subscription_;
 };
 
 }  // namespace glic
diff --git a/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicKeyedServiceImpl.java b/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicKeyedServiceImpl.java
index ea7de87..47d407a 100644
--- a/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicKeyedServiceImpl.java
+++ b/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicKeyedServiceImpl.java
@@ -25,6 +25,8 @@
 public class GlicKeyedServiceImpl implements GlicKeyedService {
     private long mNativePtr;
     private final ObserverList<GlobalShowHideObserver> mObservers = new ObserverList<>();
+    private final ObserverList<UserEnabledActuationOnWebObserver>
+            mUserEnabledActuationOnWebObservers = new ObserverList<>();
 
     @CalledByNative
     private static GlicKeyedServiceImpl create(long nativePtr) {
@@ -53,6 +55,18 @@
         return GlicKeyedServiceImplJni.get().isPanelShowingForBrowser(mNativePtr, browserWindowPtr);
     }
 
+    @Override
+    public boolean getUserEnabledActuationOnWeb() {
+        if (mNativePtr == 0) return false;
+        return GlicKeyedServiceImplJni.get().getUserEnabledActuationOnWeb(mNativePtr);
+    }
+
+    @Override
+    public void setUserEnabledActuationOnWeb(boolean enabled) {
+        if (mNativePtr == 0) return;
+        GlicKeyedServiceImplJni.get().setUserEnabledActuationOnWeb(mNativePtr, enabled);
+    }
+
     @CalledByNative
     private void onNativeDestroyed() {
         mNativePtr = 0;
@@ -75,6 +89,24 @@
         }
     }
 
+    @Override
+    public void addUserEnabledActuationOnWebObserver(UserEnabledActuationOnWebObserver observer) {
+        mUserEnabledActuationOnWebObservers.addObserver(observer);
+    }
+
+    @Override
+    public void removeUserEnabledActuationOnWebObserver(
+            UserEnabledActuationOnWebObserver observer) {
+        mUserEnabledActuationOnWebObservers.removeObserver(observer);
+    }
+
+    @CalledByNative
+    private void onUserEnabledActuationOnWebChanged(boolean enabled) {
+        for (UserEnabledActuationOnWebObserver observer : mUserEnabledActuationOnWebObservers) {
+            observer.onUserEnabledActuationOnWebChanged(enabled);
+        }
+    }
+
     @NativeMethods
     interface Natives {
         void toggleUI(
@@ -85,5 +117,9 @@
                 int source);
 
         boolean isPanelShowingForBrowser(long nativeGlicKeyedServiceAndroid, long browserWindowPtr);
+
+        boolean getUserEnabledActuationOnWeb(long nativeGlicKeyedServiceAndroid);
+
+        void setUserEnabledActuationOnWeb(long nativeGlicKeyedServiceAndroid, boolean enabled);
     }
 }
diff --git a/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicSettings.java b/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicSettings.java
index 82b4061..ed1dc794 100644
--- a/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicSettings.java
+++ b/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicSettings.java
@@ -66,6 +66,8 @@
             ObservableSuppliers.createMonotonic();
 
     private @Nullable PrefChangeRegistrar mPrefChangeRegistrar;
+    private GlicKeyedService.@Nullable UserEnabledActuationOnWebObserver
+            mUserEnabledActuationOnWebObserver;
 
     @Override
     public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
@@ -75,6 +77,7 @@
 
         PrefService prefService = UserPrefs.get(getProfile());
         mPrefChangeRegistrar = new PrefChangeRegistrar(prefService);
+        GlicKeyedService glicService = GlicKeyedServiceFactory.getForProfile(getProfile());
 
         setupSwitchPreference(
                 PREFERENCE_BUTTON,
@@ -114,11 +117,30 @@
                 SpanApplier.applySpans(summary, getLearnMoreSpanInfo(LEARN_MORE_AI_URL)));
 
         ChromeExpandableSwitchPreference autoBrowsePref =
-                setupSwitchPreference(
-                        PERMISSION_AUTO_BROWSE,
-                        ChromePreferenceKeys.GLIC_AUTO_BROWSE_SETTING_ENABLED,
-                        GlicPrefNames.GLIC_USER_ENABLED_ACTUATION_ON_WEB,
-                        /* extraListener= */ null);
+                assertNonNull(findPreference(PERMISSION_AUTO_BROWSE));
+        if (glicService != null) {
+            boolean value = glicService.getUserEnabledActuationOnWeb();
+            mSharedPreferencesManager.writeBoolean(
+                    ChromePreferenceKeys.GLIC_AUTO_BROWSE_SETTING_ENABLED, value);
+            autoBrowsePref.setChecked(value);
+            autoBrowsePref.setOnPreferenceChangeListener(
+                    (pref, newValue) -> {
+                        boolean boolValue = (boolean) newValue;
+                        mSharedPreferencesManager.writeBoolean(
+                                ChromePreferenceKeys.GLIC_AUTO_BROWSE_SETTING_ENABLED, boolValue);
+                        glicService.setUserEnabledActuationOnWeb(boolValue);
+                        return true;
+                    });
+            mUserEnabledActuationOnWebObserver =
+                    enabled -> {
+                        if (autoBrowsePref.isChecked() != enabled) {
+                            autoBrowsePref.setChecked(enabled);
+                            mSharedPreferencesManager.writeBoolean(
+                                    ChromePreferenceKeys.GLIC_AUTO_BROWSE_SETTING_ENABLED, enabled);
+                        }
+                    };
+            glicService.addUserEnabledActuationOnWebObserver(mUserEnabledActuationOnWebObserver);
+        }
 
         String autoBrowseSummary =
                 getString(R.string.settings_glic_permissions_chrome_web_actuation_toggle_sublabel);
@@ -161,6 +183,14 @@
             mPrefChangeRegistrar.destroy();
             mPrefChangeRegistrar = null;
         }
+        if (mUserEnabledActuationOnWebObserver != null) {
+            GlicKeyedService glicService = GlicKeyedServiceFactory.getForProfile(getProfile());
+            if (glicService != null) {
+                glicService.removeUserEnabledActuationOnWebObserver(
+                        mUserEnabledActuationOnWebObserver);
+            }
+            mUserEnabledActuationOnWebObserver = null;
+        }
         super.onDestroy();
     }
 
diff --git a/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicSettingsUnitTest.java b/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicSettingsUnitTest.java
index 1292c61..17f76700 100644
--- a/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicSettingsUnitTest.java
+++ b/chrome/browser/glic/android/java/src/org/chromium/chrome/browser/glic/GlicSettingsUnitTest.java
@@ -66,12 +66,18 @@
     @Mock private PrefService mPrefServiceMock;
     @Mock private SettingsCustomTabLauncher mCustomTabLauncher;
     @Mock private PrefChangeRegistrar.Natives mPrefChangeRegistrarJniMock;
+    @Mock private GlicKeyedServiceFactory.Natives mGlicKeyedServiceFactoryJniMock;
+    @Mock private GlicKeyedService mGlicKeyedServiceMock;
 
     @Before
     public void setUp() {
         UserPrefsJni.setInstanceForTesting(mUserPrefsJniMock);
         PrefChangeRegistrarJni.setInstanceForTesting(mPrefChangeRegistrarJniMock);
+        GlicKeyedServiceFactoryJni.setInstanceForTesting(mGlicKeyedServiceFactoryJniMock);
+
         when(mUserPrefsJniMock.get(mProfileMock)).thenReturn(mPrefServiceMock);
+        when(mGlicKeyedServiceFactoryJniMock.getForProfile(mProfileMock))
+                .thenReturn(mGlicKeyedServiceMock);
         doNothing().when(mCustomTabLauncher).openUrlInCct(any(Context.class), anyString());
 
         mActivityScenarioRule.getScenario().onActivity(activity -> mActivity = activity);
@@ -187,26 +193,39 @@
 
     @Test
     public void testAutoBrowsePermissionInitialState_Enabled() {
-        doTestInitialState(
-                GlicPrefNames.GLIC_USER_ENABLED_ACTUATION_ON_WEB,
-                "glic_permissions_auto_browse",
-                true);
+        when(mGlicKeyedServiceMock.getUserEnabledActuationOnWeb()).thenReturn(true);
+        GlicSettings fragment = launchFragment();
+        ChromeSwitchPreference preference = fragment.findPreference("glic_permissions_auto_browse");
+        assertTrue(preference.isChecked());
     }
 
     @Test
     public void testAutoBrowsePermissionInitialState_Disabled() {
-        doTestInitialState(
-                GlicPrefNames.GLIC_USER_ENABLED_ACTUATION_ON_WEB,
-                "glic_permissions_auto_browse",
-                false);
+        when(mGlicKeyedServiceMock.getUserEnabledActuationOnWeb()).thenReturn(false);
+        GlicSettings fragment = launchFragment();
+        ChromeSwitchPreference preference = fragment.findPreference("glic_permissions_auto_browse");
+        assertFalse(preference.isChecked());
     }
 
     @Test
     public void testAutoBrowsePermissionToggle() {
-        doTestToggle(
-                GLIC_AUTO_BROWSE_SETTING_ENABLED,
-                GlicPrefNames.GLIC_USER_ENABLED_ACTUATION_ON_WEB,
-                "glic_permissions_auto_browse");
+        when(mGlicKeyedServiceMock.getUserEnabledActuationOnWeb()).thenReturn(false);
+        GlicSettings fragment = launchFragment();
+        ChromeSwitchPreference preference = fragment.findPreference("glic_permissions_auto_browse");
+
+        // Test toggling on
+        preference.getOnPreferenceChangeListener().onPreferenceChange(preference, true);
+        assertTrue(
+                ChromeSharedPreferences.getInstance()
+                        .readBoolean(GLIC_AUTO_BROWSE_SETTING_ENABLED, false));
+        verify(mGlicKeyedServiceMock).setUserEnabledActuationOnWeb(true);
+
+        // Test toggling off
+        preference.getOnPreferenceChangeListener().onPreferenceChange(preference, false);
+        assertFalse(
+                ChromeSharedPreferences.getInstance()
+                        .readBoolean(GLIC_AUTO_BROWSE_SETTING_ENABLED, true));
+        verify(mGlicKeyedServiceMock).setUserEnabledActuationOnWeb(false);
     }
 
     private void doTestToggle(String sharedPrefKey, String profilePrefKey, String viewId) {
diff --git a/chrome/browser/glic/fre/BUILD.gn b/chrome/browser/glic/fre/BUILD.gn
index 1330890..c92c42a 100644
--- a/chrome/browser/glic/fre/BUILD.gn
+++ b/chrome/browser/glic/fre/BUILD.gn
@@ -43,11 +43,15 @@
       "//chrome/browser:global_features",
       "//chrome/browser/background/glic",
       "//chrome/browser/glic",
+      "//chrome/browser/glic/test_support",
       "//chrome/browser/ui:ui_features",
       "//chrome/test:test_support",
       "//components/signin/public/identity_manager:test_support",
       "//content/test:test_support",
       "//testing/gtest",
     ]
+    if (is_chromeos) {
+      deps += [ "//chrome/browser/ash/test:test_support" ]
+    }
   }
 }
diff --git a/chrome/browser/glic/fre/glic_fre_controller.cc b/chrome/browser/glic/fre/glic_fre_controller.cc
index 3724cae..af5b265 100644
--- a/chrome/browser/glic/fre/glic_fre_controller.cc
+++ b/chrome/browser/glic/fre/glic_fre_controller.cc
@@ -150,8 +150,8 @@
   }
   base::RecordAction(base::UserMetricsAction("Glic.Fre.Accept"));
   // Update FRE related preferences.
-  profile_->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre, static_cast<int>(prefs::FreStatus::kCompleted));
+  GlicKeyedService::Get(profile_)->enabling().SetCompletedFre(
+      prefs::FreStatus::kCompleted);
 
 #if !BUILDFLAG(IS_ANDROID)
   GlicLauncherConfiguration::CheckDefaultBrowserToEnableLauncher();
diff --git a/chrome/browser/glic/fre/glic_fre_controller_unittest.cc b/chrome/browser/glic/fre/glic_fre_controller_unittest.cc
index e6b3107..48e5a591 100644
--- a/chrome/browser/glic/fre/glic_fre_controller_unittest.cc
+++ b/chrome/browser/glic/fre/glic_fre_controller_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/glic/fre/glic_fre_controller.h"
+#include "glic_fre_controller.h"
 
 #include <memory>
 
@@ -12,12 +12,17 @@
 #include "chrome/browser/background/glic/glic_launcher_configuration.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/glic/glic_pref_names.h"
+#include "chrome/browser/glic/public/glic_keyed_service.h"
+#include "chrome/browser/glic/test_support/glic_test_environment.h"
 #include "chrome/browser/global_features.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/ash/test/glic_user_session_test_helper.h"
+#endif
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
 #include "content/public/test/browser_task_environment.h"
@@ -26,18 +31,20 @@
 namespace glic {
 class GlicFreControllerTest : public testing::Test {
  public:
-  GlicFreControllerTest() {
-    scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{features::kGlic},
-        /*disabled_features=*/{});
-  }
+  GlicFreControllerTest()
+      : glic_test_env_({.fre_status = prefs::FreStatus::kNotStarted}) {}
 
   void SetUp() override {
     raw_ptr<TestingProfileManager> testing_profile_manager =
         TestingBrowserProcess::GetGlobal()->SetUpGlobalFeaturesForTesting(
             /*profile_manager=*/true);
+#if BUILDFLAG(IS_CHROMEOS)
+    glic_user_session_test_helper_.PreProfileSetUp(
+        testing_profile_manager->profile_manager());
+#endif
     identity_env_ = std::make_unique<signin::IdentityTestEnvironment>();
     profile_ = testing_profile_manager->CreateTestingProfile("profile");
+    glic_test_env_.SetupProfile(profile_);
 
     glic_fre_controller_ = std::make_unique<GlicFreController>(
         profile_, identity_env_->identity_manager());
@@ -48,6 +55,10 @@
     profile_ = nullptr;
 
     TestingBrowserProcess::GetGlobal()->TearDownGlobalFeaturesForTesting();
+
+#if BUILDFLAG(IS_CHROMEOS)
+    glic_user_session_test_helper_.PostProfileTearDown();
+#endif
   }
 
   Profile* profile() { return profile_; }
@@ -64,11 +75,14 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
   content::BrowserTaskEnvironment task_environment_;
   std::unique_ptr<signin::IdentityTestEnvironment> identity_env_;
   std::unique_ptr<GlicFreController> glic_fre_controller_;
   raw_ptr<Profile> profile_ = nullptr;
+  GlicUnitTestEnvironment glic_test_env_;
+#if BUILDFLAG(IS_CHROMEOS)
+  ash::GlicUserSessionTestHelper glic_user_session_test_helper_;
+#endif
 };
 
 TEST_F(GlicFreControllerTest, AcceptFre) {
@@ -78,10 +92,10 @@
   // Likely a problem with the test environment configuration.
   g_browser_process->local_state()->SetBoolean(prefs::kGlicLauncherEnabled,
                                                false);
-  PrefService* const profile_pref_service = profile()->GetPrefs();
   glic_fre_controller().AcceptFre(/*handler=*/nullptr);
-  EXPECT_EQ(profile_pref_service->GetInteger(prefs::kGlicCompletedFre),
-            static_cast<int>(prefs::FreStatus::kCompleted));
+  EXPECT_EQ(
+      glic::GlicKeyedService::Get(profile())->enabling().GetCompletedFre(),
+      prefs::FreStatus::kCompleted);
   EXPECT_EQ(tester.GetActionCount("Glic.Fre.Accept"), 1);
   EXPECT_EQ(tester.GetActionCount("Glic.Fre.NoThanks"), 0);
 }
@@ -90,10 +104,10 @@
   base::UserActionTester tester;
   g_browser_process->local_state()->SetBoolean(prefs::kGlicLauncherEnabled,
                                                false);
-  PrefService* const profile_pref_service = profile()->GetPrefs();
   glic_fre_controller().RejectFre();
-  EXPECT_EQ(profile_pref_service->GetInteger(prefs::kGlicCompletedFre),
-            static_cast<int>(prefs::FreStatus::kNotStarted));
+  EXPECT_EQ(
+      glic::GlicKeyedService::Get(profile())->enabling().GetCompletedFre(),
+      prefs::FreStatus::kNotStarted);
   EXPECT_EQ(tester.GetActionCount("Glic.Fre.NoThanks"), 1);
   EXPECT_EQ(tester.GetActionCount("Glic.Fre.Accept"), 0);
 }
diff --git a/chrome/browser/glic/glic_metrics.cc b/chrome/browser/glic/glic_metrics.cc
index d8778cd..3daaa6a 100644
--- a/chrome/browser/glic/glic_metrics.cc
+++ b/chrome/browser/glic/glic_metrics.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/glic/public/context/glic_sharing_manager.h"
 #include "chrome/browser/glic/public/features.h"
 #include "chrome/browser/glic/public/glic_enabling.h"
+#include "chrome/browser/glic/public/glic_keyed_service.h"
 #include "chrome/browser/glic/public/service/glic_instance_coordinator.h"
 #include "chrome/browser/glic/service/metrics/metrics_types.h"
 #include "chrome/browser/metrics/profile_metrics_service_factory.h"
@@ -53,9 +54,8 @@
 
 namespace {
 
-bool CheckFreStatus(Profile* profile, prefs::FreStatus status) {
-  return profile->GetPrefs()->GetInteger(prefs::kGlicCompletedFre) ==
-         static_cast<int>(status);
+bool CheckFreStatus(GlicEnabling* enabling, prefs::FreStatus status) {
+  return enabling->GetCompletedFre() == status;
 }
 
 class DummyDelegateImpl : public GlicMetrics::Delegate {
@@ -261,11 +261,10 @@
   is_enabled_ = enabling_->IsEnabledAndConsentForProfile(profile_);
   is_pinned_ = profile_->GetPrefs()->GetBoolean(prefs::kGlicPinnedToTabstrip);
   pref_registrar_.Init(profile_->GetPrefs());
-  pref_registrar_.Add(
-      prefs::kGlicCompletedFre,
-      base::BindRepeating(
+  subscriptions_.push_back(
+      enabling_->RegisterOnConsentChanged(base::BindRepeating(
           &GlicMetrics::OnMaybeEnabledAndConsentForProfileChanged,
-          base::Unretained(this)));
+          base::Unretained(this))));
   pref_registrar_.Add(prefs::kGlicPinnedToTabstrip,
                       base::BindRepeating(&GlicMetrics::OnPinningPrefChanged,
                                           base::Unretained(this)));
@@ -295,9 +294,8 @@
   base::UmaHistogramBoolean(
       "Glic.Preferences.DefaultTabContextEnabled",
       profile_prefs->GetBoolean(prefs::kGlicDefaultTabContextEnabled));
-  base::UmaHistogramBoolean(
-      "Glic.Preferences.ActuationOnWeb",
-      profile_prefs->GetBoolean(prefs::kGlicUserEnabledActuationOnWeb));
+  base::UmaHistogramBoolean("Glic.Preferences.ActuationOnWeb",
+                            enabling_->GetUserEnabledActuationOnWeb());
 }
 
 void GlicMetrics::OnTrustFirstOnboardingAccept() {
@@ -321,7 +319,7 @@
     return;
   }
 
-  if (GlicEnabling::IsTrustFirstOnboardingEnabledForProfile(profile_)) {
+  if (enabling_->IsTrustFirstOnboardingEnabled()) {
     base::RecordAction(base::UserMetricsAction("Glic.Fre.Shown"));
     base::RecordAction(base::UserMetricsAction("Glic.Fre.Shown.Onboarding"));
     base::UmaHistogramEnumeration(
@@ -840,10 +838,10 @@
 void GlicMetrics::OnImpressionTimerFired() {
   if (!enabling_->IsAllowed()) {
     EntryPointStatus impression;
-    if (CheckFreStatus(profile_, prefs::FreStatus::kNotStarted)) {
+    if (CheckFreStatus(enabling_, prefs::FreStatus::kNotStarted)) {
       // Profile not eligible, and not started FRE
       impression = EntryPointStatus::kBeforeFreNotEligible;
-    } else if (CheckFreStatus(profile_, prefs::FreStatus::kIncomplete)) {
+    } else if (CheckFreStatus(enabling_, prefs::FreStatus::kIncomplete)) {
       // Profile not eligible, started but not completed FRE
       impression = EntryPointStatus::kIncompleteFreNotEligible;
     } else {
@@ -855,14 +853,14 @@
   }
 
   // Profile eligible, has not started FRE
-  if (CheckFreStatus(profile_, prefs::FreStatus::kNotStarted)) {
+  if (CheckFreStatus(enabling_, prefs::FreStatus::kNotStarted)) {
     base::UmaHistogramEnumeration("Glic.EntryPoint.Status",
                                   EntryPointStatus::kBeforeFreAndEligible);
     return;
   }
 
   // Profile eligible, started but not completed FRE
-  if (CheckFreStatus(profile_, prefs::FreStatus::kIncomplete)) {
+  if (CheckFreStatus(enabling_, prefs::FreStatus::kIncomplete)) {
     base::UmaHistogramEnumeration("Glic.EntryPoint.Status",
                                   EntryPointStatus::kIncompleteFreAndEligible);
     return;
diff --git a/chrome/browser/glic/glic_metrics_unittest.cc b/chrome/browser/glic/glic_metrics_unittest.cc
index 1342d42..79f7ac3 100644
--- a/chrome/browser/glic/glic_metrics_unittest.cc
+++ b/chrome/browser/glic/glic_metrics_unittest.cc
@@ -256,6 +256,7 @@
   content::WebContents* test_web_contents() { return test_web_contents_.get(); }
   GlicMetrics* metrics() { return metrics_.get(); }
   MockDelegate* delegate() { return delegate_.get(); }
+  GlicEnabling* enabling() { return enabling_.get(); }
 
  protected:
   base::test::ScopedFeatureList scoped_feature_list_;
@@ -277,8 +278,7 @@
   profile()->GetPrefs()->SetBoolean(prefs::kGlicGeolocationEnabled, true);
   profile()->GetPrefs()->SetBoolean(prefs::kGlicMicrophoneEnabled, true);
   profile()->GetPrefs()->SetBoolean(prefs::kGlicDefaultTabContextEnabled, true);
-  profile()->GetPrefs()->SetBoolean(prefs::kGlicUserEnabledActuationOnWeb,
-                                    true);
+  enabling()->SetUserEnabledActuationOnWeb(true);
 
   metrics()->RecordGlicProfilePreferences();
 
@@ -307,8 +307,7 @@
   profile()->GetPrefs()->SetBoolean(prefs::kGlicMicrophoneEnabled, false);
   profile()->GetPrefs()->SetBoolean(prefs::kGlicDefaultTabContextEnabled,
                                     false);
-  profile()->GetPrefs()->SetBoolean(prefs::kGlicUserEnabledActuationOnWeb,
-                                    false);
+  enabling()->SetUserEnabledActuationOnWeb(false);
 
   metrics()->RecordGlicProfilePreferences();
 
@@ -699,17 +698,13 @@
 }
 
 TEST_F(GlicMetricsTest, ImpressionBeforeFreNotPermittedByPolicy) {
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre,
-      static_cast<int>(prefs::FreStatus::kNotStarted));
+  enabling()->SetCompletedFre(prefs::FreStatus::kNotStarted);
 
   ExpectEntryPointImpressionLogged(EntryPointStatus::kBeforeFreNotEligible);
 }
 
 TEST_F(GlicMetricsTest, ImpressionIncompleteFreNotPermittedByPolicy) {
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre,
-      static_cast<int>(prefs::FreStatus::kIncomplete));
+  enabling()->SetCompletedFre(prefs::FreStatus::kIncomplete);
 
   ExpectEntryPointImpressionLogged(EntryPointStatus::kIncompleteFreNotEligible);
 }
@@ -762,17 +757,15 @@
 }
 
 TEST_F(GlicMetricsFeaturesEnabledTest, ImpressionBeforeFre) {
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre,
-      static_cast<int>(prefs::FreStatus::kNotStarted));
+  glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+      prefs::FreStatus::kNotStarted);
 
   ExpectEntryPointImpressionLogged(EntryPointStatus::kBeforeFreAndEligible);
 }
 
 TEST_F(GlicMetricsFeaturesEnabledTest, ImpressionIncompleteFre) {
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre,
-      static_cast<int>(prefs::FreStatus::kIncomplete));
+  glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+      prefs::FreStatus::kIncomplete);
 
   ExpectEntryPointImpressionLogged(EntryPointStatus::kIncompleteFreAndEligible);
 }
@@ -828,14 +821,13 @@
   // action.
   EXPECT_EQ(user_action_tester().GetActionCount("Glic.Enabled"), 1);
 
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre,
-      static_cast<int>(prefs::FreStatus::kNotStarted));
+  glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+      prefs::FreStatus::kNotStarted);
   EXPECT_EQ(user_action_tester().GetActionCount("Glic.Disabled"), 1);
   EXPECT_EQ(user_action_tester().GetActionCount("Glic.Enabled"), 1);
 
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre, static_cast<int>(prefs::FreStatus::kCompleted));
+  glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+      prefs::FreStatus::kCompleted);
   EXPECT_EQ(user_action_tester().GetActionCount("Glic.Disabled"), 1);
   EXPECT_EQ(user_action_tester().GetActionCount("Glic.Enabled"), 2);
 
@@ -851,9 +843,8 @@
   EXPECT_EQ(user_action_tester().GetActionCount("Glic.Disabled"), 2);
   EXPECT_EQ(user_action_tester().GetActionCount("Glic.Enabled"), 3);
 
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre,
-      static_cast<int>(prefs::FreStatus::kIncomplete));
+  glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+      prefs::FreStatus::kIncomplete);
   EXPECT_EQ(user_action_tester().GetActionCount("Glic.Disabled"), 3);
   EXPECT_EQ(user_action_tester().GetActionCount("Glic.Enabled"), 3);
 }
@@ -1067,9 +1058,7 @@
     metrics_->SetDelegateForTesting(std::move(delegate));
 
     // Revert FRE status to NotStarted to simulate new user for this experiment.
-    profile()->GetPrefs()->SetInteger(
-        prefs::kGlicCompletedFre,
-        static_cast<int>(prefs::FreStatus::kNotStarted));
+    enabling_->SetCompletedFre(prefs::FreStatus::kNotStarted);
   }
 };
 
@@ -1118,8 +1107,9 @@
 }
 
 TEST_F(GlicMetricsTrustFirstOnboardingTest, NotShownIfConsented) {
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre, static_cast<int>(prefs::FreStatus::kCompleted));
+  enabling()->SetCompletedFre(prefs::FreStatus::kCompleted);
+
+  EXPECT_FALSE(enabling()->IsTrustFirstOnboardingEnabled());
 
   metrics()->OnGlicWindowStartedOpening(/*attached=*/false,
                                         mojom::InvocationSource::kOsButton);
diff --git a/chrome/browser/glic/glic_pref_names.cc b/chrome/browser/glic/glic_pref_names.cc
index 1c11834..aed8c92 100644
--- a/chrome/browser/glic/glic_pref_names.cc
+++ b/chrome/browser/glic/glic_pref_names.cc
@@ -8,6 +8,7 @@
 
 #include "chrome/browser/background/glic/glic_launcher_configuration.h"
 #include "chrome/browser/glic/common/local_hotkey_manager.h"
+#include "chrome/browser/glic/glic_pref_names_internal.h"
 #include "chrome/common/chrome_features.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_registry.h"
diff --git a/chrome/browser/glic/glic_pref_names.h b/chrome/browser/glic/glic_pref_names.h
index 534d255..7096653 100644
--- a/chrome/browser/glic/glic_pref_names.h
+++ b/chrome/browser/glic/glic_pref_names.h
@@ -111,10 +111,6 @@
 // Dict pref that records user status.
 inline constexpr char kGlicUserStatus[] = "glic.user_status";
 
-// Integer pref that determines the FRE status for the user profile. Values are
-// from the FreStatus enum.
-inline constexpr char kGlicCompletedFre[] = "glic.completed_fre";
-
 // Time pref that records the last time a user dismissed the Glic window.
 inline constexpr char kGlicWindowLastDismissedTime[] =
     "glic.window.last_dimissed_time";
@@ -142,10 +138,6 @@
 inline constexpr char kGlicActuationOnWebBlockedForURLs[] =
     "glic.actuation_on_web_blocked_for_urls";
 
-// Boolean pref for the user-enabled actuation on web setting.
-inline constexpr char kGlicUserEnabledActuationOnWeb[] =
-    "glic.user_enabled_actuation_on_web";
-
 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
 
diff --git a/chrome/browser/glic/glic_pref_names_internal.h b/chrome/browser/glic/glic_pref_names_internal.h
new file mode 100644
index 0000000..a24c945
--- /dev/null
+++ b/chrome/browser/glic/glic_pref_names_internal.h
@@ -0,0 +1,23 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_GLIC_GLIC_PREF_NAMES_INTERNAL_H_
+#define CHROME_BROWSER_GLIC_GLIC_PREF_NAMES_INTERNAL_H_
+
+namespace glic::prefs {
+
+// Access to these prefs should be guarded. The only place that should access
+// them directly (including in tests) is GlicEnabling.
+
+// Integer pref that determines the FRE status for the user profile. Values are
+// from the FreStatus enum.
+inline constexpr char kGlicCompletedFre[] = "glic.completed_fre";
+
+// Boolean pref for the user-enabled actuation on web setting.
+inline constexpr char kGlicUserEnabledActuationOnWeb[] =
+    "glic.user_enabled_actuation_on_web";
+
+}  // namespace glic::prefs
+
+#endif  // CHROME_BROWSER_GLIC_GLIC_PREF_NAMES_INTERNAL_H_
diff --git a/chrome/browser/glic/glic_profile_manager_browsertest.cc b/chrome/browser/glic/glic_profile_manager_browsertest.cc
index 429bba4..84758e6a 100644
--- a/chrome/browser/glic/glic_profile_manager_browsertest.cc
+++ b/chrome/browser/glic/glic_profile_manager_browsertest.cc
@@ -589,9 +589,8 @@
   SigninWithPrimaryAccount(profile);
 #endif  // BUILDFLAG(IS_CHROMEOS)
   SetGlicCapability(profile, true);
-  profile->GetPrefs()->SetInteger(
-      glic::prefs::kGlicCompletedFre,
-      static_cast<int>(glic::prefs::FreStatus::kNotStarted));
+  glic::GlicKeyedService::Get(profile)->enabling().SetCompletedFre(
+      glic::prefs::FreStatus::kNotStarted);
   ASSERT_TRUE(GlicEnabling::IsEnabledForProfile(profile));
   ASSERT_FALSE(GlicEnabling::HasConsentedForProfile(profile));
 
@@ -619,9 +618,8 @@
 #else
       CreateNewProfile(/*signin_and_allow_glic=*/true);
 #endif  // BUILDFLAG(IS_CHROMEOS)
-  profile->GetPrefs()->SetInteger(
-      glic::prefs::kGlicCompletedFre,
-      static_cast<int>(glic::prefs::FreStatus::kCompleted));
+  glic::GlicKeyedService::Get(profile)->enabling().SetCompletedFre(
+      glic::prefs::FreStatus::kCompleted);
   ASSERT_TRUE(GlicEnabling::IsEnabledAndConsentForProfile(profile));
 
   auto* service = GetMockGlicKeyedService(profile);
diff --git a/chrome/browser/glic/host/glic_api_browsertest.cc b/chrome/browser/glic/host/glic_api_browsertest.cc
index 7476bb12..a79036e 100644
--- a/chrome/browser/glic/host/glic_api_browsertest.cc
+++ b/chrome/browser/glic/host/glic_api_browsertest.cc
@@ -814,12 +814,14 @@
 
 IN_PROC_BROWSER_TEST_P(GlicApiTestWithWebActuationSettingEnabled,
                        testGetWebActuationSetting) {
-  browser()->profile()->GetPrefs()->SetBoolean(
-      prefs::kGlicUserEnabledActuationOnWeb, false);
+  glic::GlicKeyedService::Get(browser()->profile())
+      ->enabling()
+      .SetUserEnabledActuationOnWeb(false);
   ExecuteJsTest();
 
-  browser()->profile()->GetPrefs()->SetBoolean(
-      prefs::kGlicUserEnabledActuationOnWeb, true);
+  glic::GlicKeyedService::Get(browser()->profile())
+      ->enabling()
+      .SetUserEnabledActuationOnWeb(true);
   ContinueJsTest();
 }
 
diff --git a/chrome/browser/glic/host/glic_internals_page_handler.cc b/chrome/browser/glic/host/glic_internals_page_handler.cc
index 9e5b09e..e1fa258 100644
--- a/chrome/browser/glic/host/glic_internals_page_handler.cc
+++ b/chrome/browser/glic/host/glic_internals_page_handler.cc
@@ -51,9 +51,9 @@
   result->disallowed_by_locale_filter = enablement.disallowed_by_locale_filter;
   result->live_disallowed = enablement.live_disallowed;
   result->share_image_disallowed = enablement.share_image_disallowed;
+  auto* service = GlicKeyedService::Get(profile);
   result->actuation_not_consented =
-      profile->GetPrefs()->GetBoolean(prefs::kGlicUserEnabledActuationOnWeb) ==
-      false;
+      !(service && service->enabling().GetUserEnabledActuationOnWeb());
 
   using CannotActReason = ::glic::CannotActReason;
   if (actor_policy_checker) {
diff --git a/chrome/browser/glic/host/glic_page_handler.cc b/chrome/browser/glic/host/glic_page_handler.cc
index ca653e4..f2d3ab7d 100644
--- a/chrome/browser/glic/host/glic_page_handler.cc
+++ b/chrome/browser/glic/host/glic_page_handler.cc
@@ -110,6 +110,7 @@
 #include "components/skills/public/skill.mojom.h"
 #include "components/skills/public/skills_metrics.h"
 #include "components/skills/public/skills_service.h"
+#include "components/skills/public/skills_types.h"
 #include "components/sync/protocol/skill_specifics.pb.h"
 #include "components/tabs/public/tab_interface.h"
 #include "components/url_formatter/elide_url.h"
@@ -758,14 +759,14 @@
         prefs::kGlicDefaultTabContextEnabled,
         base::BindRepeating(&GlicWebClientHandler::OnPrefChanged,
                             base::Unretained(this)));
-    pref_change_registrar_.Add(
-        prefs::kGlicUserEnabledActuationOnWeb,
-        base::BindRepeating(&GlicWebClientHandler::OnPrefChanged,
-                            base::Unretained(this)));
-    pref_change_registrar_.Add(
-        prefs::kGlicCompletedFre,
-        base::BindRepeating(&GlicWebClientHandler::OnPrefChanged,
-                            base::Unretained(this)));
+    web_actuation_pref_subscription_ =
+        glic_service_->enabling().RegisterOnUserEnabledActuationOnWebChanged(
+            base::BindRepeating(
+                &GlicWebClientHandler::OnUserEnabledActuationOnWebChanged,
+                base::Unretained(this)));
+    consent_subscription_ =
+        glic_service_->enabling().RegisterOnConsentChanged(base::BindRepeating(
+            &GlicWebClientHandler::OnConsentChanged, base::Unretained(this)));
     host().AddPanelStateObserver(this);
 
     if (base::FeatureList::IsEnabled(
@@ -885,7 +886,7 @@
     state->enable_web_actuation_setting_feature =
         base::FeatureList::IsEnabled(features::kGlicWebActuationSetting);
     state->actuation_on_web_setting_enabled =
-        pref_service_->GetBoolean(prefs::kGlicUserEnabledActuationOnWeb);
+        glic_service_->enabling().GetUserEnabledActuationOnWeb();
 
 #if BUILDFLAG(ENABLE_PDF)
     if (features::kGlicScrollToPDF.Get()) {
@@ -1508,7 +1509,7 @@
   void SetActuationOnWebSetting(
       bool enabled,
       SetActuationOnWebSettingCallback callback) override {
-    pref_service_->SetBoolean(prefs::kGlicUserEnabledActuationOnWeb, enabled);
+    glic_service_->enabling().SetUserEnabledActuationOnWeb(enabled);
     base::RecordAction(
         enabled ? base::UserMetricsAction("GlicUserEnabledActuationOnWeb")
                 : base::UserMetricsAction("GlicUserDisabledActuationOnWeb"));
@@ -1777,8 +1778,7 @@
 
   void SetOnboardingCompleted() override {
     glic_service_->metrics()->OnTrustFirstOnboardingAccept();
-    pref_service_->SetInteger(prefs::kGlicCompletedFre,
-                              static_cast<int>(prefs::FreStatus::kCompleted));
+    glic_service_->enabling().SetCompletedFre(prefs::FreStatus::kCompleted);
 
 #if !BUILDFLAG(IS_ANDROID)  // NEEDS_ANDROID_IMPL
     GlicLauncherConfiguration::CheckDefaultBrowserToEnableLauncher();
@@ -2052,7 +2052,7 @@
   }
 
   void OnDiscoverySkillsUpdated(
-      const skills::SkillsService::SkillsMap* skills_map) override {
+      const skills::SkillIdToProtoMap* skills_map) override {
     // If skills_map is null, this means we don't have an updated value so we
     // shouldn't modify the stored 1p map.
     if (skills_map == nullptr) {
@@ -2090,6 +2090,8 @@
     focus_changed_subscription_ = {};
     pinned_tabs_changed_subscription_ = {};
     pinned_tab_data_changed_subscription_ = {};
+    web_actuation_pref_subscription_ = {};
+    consent_subscription_ = {};
     browser_attach_observation_.reset();
     if (glic_service_->zero_state_suggestions_manager()) {
       glic_service_->zero_state_suggestions_manager()->Reset();
@@ -2104,6 +2106,17 @@
     Uninstall();
   }
 
+  void OnUserEnabledActuationOnWebChanged() {
+    web_client_->NotifyActuationOnWebSettingChanged(
+        glic_service_->enabling().GetUserEnabledActuationOnWeb());
+  }
+
+  void OnConsentChanged() {
+    web_client_->NotifyOnboardingCompletedChanged(
+        glic_service_->enabling().GetCompletedFre() ==
+        prefs::FreStatus::kCompleted);
+  }
+
   void OnPrefChanged(const std::string& pref_name) {
     if (pref_name == prefs::kGlicMicrophoneEnabled) {
       web_client_->NotifyMicrophonePermissionStateChanged(
@@ -2120,13 +2133,6 @@
     } else if (pref_name == prefs::kGlicDefaultTabContextEnabled) {
       web_client_->NotifyDefaultTabContextPermissionStateChanged(
           pref_service_->GetBoolean(pref_name));
-    } else if (pref_name == prefs::kGlicUserEnabledActuationOnWeb) {
-      web_client_->NotifyActuationOnWebSettingChanged(
-          pref_service_->GetBoolean(pref_name));
-    } else if (pref_name == prefs::kGlicCompletedFre) {
-      web_client_->NotifyOnboardingCompletedChanged(
-          pref_service_->GetInteger(prefs::kGlicCompletedFre) ==
-          static_cast<int>(prefs::FreStatus::kCompleted));
     } else {
       DCHECK(false) << "Unknown Glic permission pref changed: " << pref_name;
     }
@@ -2424,6 +2430,8 @@
   base::CallbackListSubscription active_browser_changed_subscription_;
   base::CallbackListSubscription actor_task_state_changed_subscription_;
   base::CallbackListSubscription act_on_web_capability_changed_subscription_;
+  base::CallbackListSubscription web_actuation_pref_subscription_;
+  base::CallbackListSubscription consent_subscription_;
   mojo::Receiver<glic::mojom::WebClientHandler> receiver_;
   mojo::Remote<glic::mojom::WebClient> web_client_;
   std::unique_ptr<BrowserAttachObservation> browser_attach_observation_;
diff --git a/chrome/browser/glic/host/glic_ui_interactive_uitest.cc b/chrome/browser/glic/host/glic_ui_interactive_uitest.cc
index 88046b4..dc42e22 100644
--- a/chrome/browser/glic/host/glic_ui_interactive_uitest.cc
+++ b/chrome/browser/glic/host/glic_ui_interactive_uitest.cc
@@ -845,9 +845,9 @@
     host_resolver()->AddRule("glic.test", "127.0.0.1");
     GlicUiInteractiveUiTestBase::SetUpOnMainThread();
     ASSERT_TRUE(embedded_https_test_server().Start());
-    browser()->profile()->GetPrefs()->SetInteger(
-        glic::prefs::kGlicCompletedFre,
-        static_cast<int>(glic::prefs::FreStatus::kNotStarted));
+    glic::GlicKeyedService::Get(browser()->profile())
+        ->enabling()
+        .SetCompletedFre(glic::prefs::FreStatus::kNotStarted);
   }
 
   const DeepQuery kFreContainer = {"#fre-app-container"};
@@ -918,9 +918,9 @@
           WaitForElementVisible(test::kGlicHostElementId, kGlicGuestPanel)),
       CheckResult(
           [this]() {
-            return static_cast<glic::prefs::FreStatus>(
-                browser()->profile()->GetPrefs()->GetInteger(
-                    glic::prefs::kGlicCompletedFre));
+            return glic::GlicKeyedService::Get(browser()->profile())
+                ->enabling()
+                .GetCompletedFre();
           },
           glic::prefs::FreStatus::kCompleted),
       WaitForState(kGlicUiStateHistory, IsCurrently(WebUiState::kReady)),
@@ -954,9 +954,9 @@
       CheckControllerShowing(false),
       CheckResult(
           [this]() {
-            return static_cast<glic::prefs::FreStatus>(
-                browser()->profile()->GetPrefs()->GetInteger(
-                    glic::prefs::kGlicCompletedFre));
+            return glic::GlicKeyedService::Get(browser()->profile())
+                ->enabling()
+                .GetCompletedFre();
           },
           glic::prefs::FreStatus::kNotStarted));
 }
diff --git a/chrome/browser/glic/media/BUILD.gn b/chrome/browser/glic/media/BUILD.gn
index d844009..5f316e8 100644
--- a/chrome/browser/glic/media/BUILD.gn
+++ b/chrome/browser/glic/media/BUILD.gn
@@ -66,5 +66,8 @@
       "//testing/gmock",
       "//testing/gtest",
     ]
+    if (is_chromeos) {
+      deps += [ "//chrome/browser/ash/test:test_support" ]
+    }
   }
 }
diff --git a/chrome/browser/glic/media/glic_media_integration_unittest.cc b/chrome/browser/glic/media/glic_media_integration_unittest.cc
index d48af1e..52d0541 100644
--- a/chrome/browser/glic/media/glic_media_integration_unittest.cc
+++ b/chrome/browser/glic/media/glic_media_integration_unittest.cc
@@ -4,16 +4,21 @@
 
 #include "chrome/browser/glic/media/glic_media_integration.h"
 
+#include "base/command_line.h"
 #include "base/strings/string_util.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/accessibility/live_caption/live_caption_controller_factory.h"
 #include "chrome/browser/glic/glic_pref_names.h"
 #include "chrome/browser/glic/media/glic_media_context.h"
+#include "chrome/browser/glic/public/glic_keyed_service.h"
 #include "chrome/browser/glic/public/glic_keyed_service_factory.h"
+#include "chrome/browser/glic/test_support/glic_test_environment.h"
 #include "chrome/browser/picture_in_picture/picture_in_picture_window_manager.h"
 #include "chrome/browser/prefs/browser_prefs.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
 #include "components/live_caption/live_caption_controller.h"
 #include "components/live_caption/pref_names.h"
 #include "components/optimization_guide/content/browser/media_transcript_provider.h"
@@ -25,9 +30,11 @@
 #include "content/public/test/web_contents_tester.h"
 #include "media/base/media_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
-
 #if BUILDFLAG(IS_CHROMEOS)
 #include "ash/constants/ash_features.h"
+#include "chrome/browser/ash/test/glic_user_session_test_helper.h"
+#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
+#include "components/user_manager/test_helper.h"
 #endif
 
 using content::WebContents;
@@ -36,10 +43,30 @@
 
 class GlicMediaIntegrationTest : public ChromeRenderViewHostTestHarness {
  public:
+  void SetUp() override {
+    // This must occur before base class SetUp() to ensure that the
+    // TestingProfileManager is available when the profile is created,
+    // allowing GlicKeyedServiceFactory to find it.
+    profile_manager_ =
+        TestingBrowserProcess::GetGlobal()->SetUpGlobalFeaturesForTesting(
+            /*profile_manager=*/true);
+#if BUILDFLAG(IS_CHROMEOS)
+    glic_user_session_test_helper_.PreProfileSetUp(
+        profile_manager_->profile_manager());
+#endif
+    ChromeRenderViewHostTestHarness::SetUp();
+    glic_test_env_.SetupProfile(profile());
+  }
+
   void TearDown() override {
     live_caption_controller_ = nullptr;
     pref_registry_ = nullptr;
     ChromeRenderViewHostTestHarness::TearDown();
+    profile_manager_ = nullptr;
+    TestingBrowserProcess::GetGlobal()->TearDownGlobalFeaturesForTesting();
+#if BUILDFLAG(IS_CHROMEOS)
+    glic_user_session_test_helper_.PostProfileTearDown();
+#endif
   }
 
   // ChromeRenderViewHostTestHarness
@@ -49,10 +76,26 @@
     pref_registry_ = pref_service->registry();
     RegisterUserProfilePrefs(pref_registry_);
 
-    auto profile = TestingProfile::Builder()
-                       .SetPrefService(std::move(pref_service))
-                       .AddTestingFactories(GetTestingFactories())
-                       .Build();
+    TestingProfile::Builder builder;
+    builder.SetPrefService(std::move(pref_service));
+    builder.AddTestingFactories(GetTestingFactories());
+
+#if BUILDFLAG(IS_CHROMEOS)
+    // This is hacky, but appears to be the only way to get the necessary
+    // profile state setup correctly on ChromeOS.
+    // TODO(b/501476411): Find a cleaner way to do this.
+    const AccountId account_id(AccountId::FromUserEmailGaiaId(
+        TestingProfile::kDefaultProfileUserName, GaiaId("1234567890")));
+    std::string hash =
+        user_manager::TestHelper::GetFakeUsernameHash(account_id);
+    // Construct the absolute directory path to match BrowserContextHelper
+    // expectations.
+    base::FilePath path =
+        profile_manager_->profiles_dir().AppendASCII("u-" + hash);
+    builder.SetPath(path);
+#endif
+
+    auto profile = builder.Build();
 
     // Set up soda Installer
     soda_installer_.NeverDownloadSodaForTesting();
@@ -148,13 +191,18 @@
   }
 
   void SetFreCompleted() {
-    pref_service()->SetInteger(
-        glic::prefs::kGlicCompletedFre,
-        static_cast<int>(glic::prefs::FreStatus::kCompleted));
+    glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+        glic::prefs::FreStatus::kCompleted);
   }
 
  private:
+  GlicUnitTestEnvironment glic_test_env_;
+  raw_ptr<TestingProfileManager> profile_manager_ = nullptr;
   std::optional<base::test::ScopedFeatureList> scoped_feature_list_;
+#if BUILDFLAG(IS_CHROMEOS)
+  ash::GlicUserSessionTestHelper glic_user_session_test_helper_;
+#endif
+  base::test::ScopedFeatureList feature_list_;
   raw_ptr<captions::LiveCaptionController> live_caption_controller_ = nullptr;
   raw_ptr<user_prefs::PrefRegistrySyncable> pref_registry_ = nullptr;
   speech::MockSodaInstaller soda_installer_;
diff --git a/chrome/browser/glic/public/android/java/src/org/chromium/chrome/browser/glic/GlicKeyedService.java b/chrome/browser/glic/public/android/java/src/org/chromium/chrome/browser/glic/GlicKeyedService.java
index da429b91..8872a44 100644
--- a/chrome/browser/glic/public/android/java/src/org/chromium/chrome/browser/glic/GlicKeyedService.java
+++ b/chrome/browser/glic/public/android/java/src/org/chromium/chrome/browser/glic/GlicKeyedService.java
@@ -37,6 +37,17 @@
     /** Removes an observer for global show/hide events. */
     void removeGlobalShowHideObserver(GlobalShowHideObserver observer);
 
+    /** Observer for user enabled actuation on web changes. */
+    interface UserEnabledActuationOnWebObserver {
+        void onUserEnabledActuationOnWebChanged(boolean enabled);
+    }
+
+    /** Adds an observer for user enabled actuation on web changes. */
+    void addUserEnabledActuationOnWebObserver(UserEnabledActuationOnWebObserver observer);
+
+    /** Removes an observer for user enabled actuation on web changes. */
+    void removeUserEnabledActuationOnWebObserver(UserEnabledActuationOnWebObserver observer);
+
     /**
      * Checks if the panel is showing for a specific browser window.
      *
@@ -44,4 +55,18 @@
      * @return true if the panel is showing for the specified browser window.
      */
     boolean isPanelShowingForBrowser(long browserWindowPtr);
+
+    /**
+     * Checks if the user has enabled actuation on web.
+     *
+     * @return true if actuation on web is enabled.
+     */
+    boolean getUserEnabledActuationOnWeb();
+
+    /**
+     * Sets whether the user has enabled actuation on web.
+     *
+     * @param enabled true to enable actuation on web.
+     */
+    void setUserEnabledActuationOnWeb(boolean enabled);
 }
diff --git a/chrome/browser/glic/public/glic_enabling.cc b/chrome/browser/glic/public/glic_enabling.cc
index f76a39ff0..c846051 100644
--- a/chrome/browser/glic/public/glic_enabling.cc
+++ b/chrome/browser/glic/public/glic_enabling.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/enterprise/browser_management/management_service_factory.h"
 #include "chrome/browser/glic/glic_enums.h"
 #include "chrome/browser/glic/glic_pref_names.h"
+#include "chrome/browser/glic/glic_pref_names_internal.h"
 #include "chrome/browser/glic/glic_user_status_code.h"
 #include "chrome/browser/glic/glic_user_status_fetcher.h"
 #include "chrome/browser/glic/host/auth_controller.h"
@@ -24,6 +25,7 @@
 #include "chrome/browser/glic/host/glic_features.mojom-features.h"
 #include "chrome/browser/glic/host/glic_synthetic_trial_manager.h"
 #include "chrome/browser/glic/public/features.h"
+#include "chrome/browser/glic/public/glic_keyed_service.h"
 #include "chrome/browser/global_features.h"
 #include "chrome/browser/metrics/chrome_feature_list_creator.h"
 #include "chrome/browser/profiles/profile.h"
@@ -276,6 +278,7 @@
 
   if (feature_disabled) {
     record_reason(Reason::kFeatureDisabled);
+    RecordFeatureDisabledReason(suffix);
   }
   if (not_regular_profile) {
     record_reason(Reason::kNotRegularProfile);
@@ -308,6 +311,28 @@
       !primary_account_not_fully_signed_in);
 }
 
+void GlicEnabling::ProfileEnablement::RecordFeatureDisabledReason(
+    const std::string& suffix) const {
+  auto record_reason = [&](FeatureDisabledReason reason) {
+    base::UmaHistogramEnumeration(
+        base::StrCat({"Glic.GlobalEnabling.FeatureDisabledReason.", suffix}),
+        reason);
+  };
+
+  if (feature_flag_disabled) {
+    record_reason(FeatureDisabledReason::kFeatureFlagDisabled);
+  }
+  if (disallowed_by_country_filter) {
+    record_reason(FeatureDisabledReason::kCountryDisabled);
+  }
+  if (disallowed_by_locale_filter) {
+    record_reason(FeatureDisabledReason::kLocaleDisabled);
+  }
+  if (system_requirement_not_met) {
+    record_reason(FeatureDisabledReason::kSystemRequirementNotMet);
+  }
+}
+
 GlicEnabling::ProfileEnablement GlicEnabling::EnablementForProfile(
     Profile* profile) {
   ProfileEnablement result;
@@ -319,11 +344,16 @@
   GlicGlobalEnabling& global_enabling =
       g_browser_process->GetFeatures()->glic_global_enabling();
 
-  result.disallowed_by_country_filter = !global_enabling.IsCountryEnabled();
-  result.disallowed_by_locale_filter = !global_enabling.IsLocaleEnabled();
-
   if (!global_enabling.IsEnabledByFlags()) {
     result.feature_disabled = true;
+
+    result.feature_flag_disabled =
+        !base::FeatureList::IsEnabled(features::kGlic);
+    result.disallowed_by_country_filter = !global_enabling.IsCountryEnabled();
+    result.disallowed_by_locale_filter = !global_enabling.IsLocaleEnabled();
+    result.system_requirement_not_met =
+        !global_enabling.IsSystemRequirementMet();
+
     return result;
   }
 
@@ -443,15 +473,7 @@
 
 GlicGlobalEnabling::~GlicGlobalEnabling() = default;
 
-bool GlicGlobalEnabling::IsEnabledByFlags() {
-  if (g_bypass_enablement_checks_for_testing) {
-    return true;
-  }
-  // It is important that this value not change at runtime in production. Any
-  // future updates to this function must maintain that property.
-  bool is_enabled = base::FeatureList::IsEnabled(features::kGlic) &&
-                    locale_enablement_.value_or(true) &&
-                    country_enablement_.value_or(true);
+bool GlicGlobalEnabling::IsSystemRequirementMet() const {
 #if BUILDFLAG(IS_CHROMEOS)
   static const bool supported_system_requirements = [] {
     constexpr base::ByteCount kMinimumMemoryThreshold = base::GiB(8);
@@ -467,9 +489,23 @@
                 chromeos::features::kFeatureManagementGlic));
   }();
 
-  is_enabled = is_enabled && supported_system_requirements;
+  return supported_system_requirements;
+#else
+  return true;
 #endif  // BUILDFLAG(IS_CHROMEOS)
-  return is_enabled;
+}
+
+bool GlicGlobalEnabling::IsEnabledByFlags() {
+  if (g_bypass_enablement_checks_for_testing) {
+    return true;
+  }
+  // It is important that this value not change at runtime in production. Any
+  // future updates to this function must maintain that property.
+  bool is_enabled = base::FeatureList::IsEnabled(features::kGlic) &&
+                    locale_enablement_.value_or(true) &&
+                    country_enablement_.value_or(true);
+
+  return is_enabled && IsSystemRequirementMet();
 }
 
 bool GlicEnabling::IsEnabledByFlags() {
@@ -538,8 +574,11 @@
 }
 
 bool GlicEnabling::HasConsentedForProfile(Profile* profile) {
-  return profile->GetPrefs()->GetInteger(prefs::kGlicCompletedFre) ==
-         static_cast<int>(prefs::FreStatus::kCompleted);
+  auto* service = GlicKeyedService::Get(profile);
+  if (!service) {
+    return false;
+  }
+  return service->enabling().GetCompletedFre() == prefs::FreStatus::kCompleted;
 }
 
 bool GlicEnabling::IsEnabledAndConsentForProfile(Profile* profile) {
@@ -547,8 +586,11 @@
 }
 
 bool GlicEnabling::DidDismissForProfile(Profile* profile) {
-  return profile->GetPrefs()->GetInteger(glic::prefs::kGlicCompletedFre) ==
-         static_cast<int>(prefs::FreStatus::kIncomplete);
+  auto* service = GlicKeyedService::Get(profile);
+  if (!service) {
+    return false;
+  }
+  return service->enabling().GetCompletedFre() == prefs::FreStatus::kIncomplete;
 }
 
 bool GlicEnabling::IsReadyForProfile(Profile* profile) {
@@ -648,8 +690,11 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 bool GlicEnabling::IsTrustFirstOnboardingEnabledForProfile(Profile* profile) {
-  return !HasConsentedForProfile(profile) &&
-         base::FeatureList::IsEnabled(features::kGlicTrustFirstOnboarding);
+  auto* service = GlicKeyedService::Get(profile);
+  if (!service) {
+    return false;
+  }
+  return service->enabling().IsTrustFirstOnboardingEnabled();
 }
 
 bool GlicEnabling::IsAutoOpenForPdfEnabled(Profile* profile) {
@@ -720,6 +765,10 @@
   pref_registrar_.Add(prefs::kGlicCompletedFre,
                       base::BindRepeating(&GlicEnabling::UpdateConsentStatus,
                                           base::Unretained(this)));
+  pref_registrar_.Add(
+      prefs::kGlicUserEnabledActuationOnWeb,
+      base::BindRepeating(&GlicEnabling::OnUserEnabledActuationOnWebChanged,
+                          base::Unretained(this)));
   if (!base::FeatureList::IsEnabled(features::kGlicRollout) &&
       base::FeatureList::IsEnabled(features::kGlicTieredRollout)) {
     pref_registrar_.Add(
@@ -756,6 +805,37 @@
   return HasConsentedForProfile(profile_);
 }
 
+bool GlicEnabling::IsTrustFirstOnboardingEnabled() const {
+  return GetCompletedFre() != prefs::FreStatus::kCompleted &&
+         base::FeatureList::IsEnabled(features::kGlicTrustFirstOnboarding);
+}
+
+prefs::FreStatus GlicEnabling::GetCompletedFre() const {
+  return static_cast<prefs::FreStatus>(
+      profile_->GetPrefs()->GetInteger(prefs::kGlicCompletedFre));
+}
+
+void GlicEnabling::SetCompletedFre(prefs::FreStatus status) {
+  profile_->GetPrefs()->SetInteger(prefs::kGlicCompletedFre,
+                                   static_cast<int>(status));
+}
+
+bool GlicEnabling::GetUserEnabledActuationOnWeb() const {
+  return profile_->GetPrefs()->GetBoolean(
+      prefs::kGlicUserEnabledActuationOnWeb);
+}
+
+bool GlicEnabling::IsUserEnabledActuationOnWebDefault() const {
+  const PrefService::Preference* pref = profile_->GetPrefs()->FindPreference(
+      prefs::kGlicUserEnabledActuationOnWeb);
+  return pref && pref->IsDefaultValue();
+}
+
+void GlicEnabling::SetUserEnabledActuationOnWeb(bool enabled) {
+  profile_->GetPrefs()->SetBoolean(prefs::kGlicUserEnabledActuationOnWeb,
+                                   enabled);
+}
+
 void GlicEnabling::MaybeRecordStartupMetrics() {
   if (recorded_startup_metrics_) {
     return;
@@ -775,6 +855,17 @@
   return consent_changed_callback_list_.Add(std::move(callback));
 }
 
+base::CallbackListSubscription
+GlicEnabling::RegisterOnUserEnabledActuationOnWebChanged(
+    UserEnabledActuationOnWebChangedCallback callback) {
+  return user_enabled_actuation_on_web_changed_callback_list_.Add(
+      std::move(callback));
+}
+
+void GlicEnabling::OnUserEnabledActuationOnWebChanged() {
+  user_enabled_actuation_on_web_changed_callback_list_.Notify();
+}
+
 base::CallbackListSubscription GlicEnabling::RegisterOnShowSettingsPageChanged(
     ShowSettingsPageChangedCallback callback) {
   return show_settings_page_changed_callback_list_.Add(std::move(callback));
diff --git a/chrome/browser/glic/public/glic_enabling.h b/chrome/browser/glic/public/glic_enabling.h
index 888e839..2c25dc7 100644
--- a/chrome/browser/glic/public/glic_enabling.h
+++ b/chrome/browser/glic/public/glic_enabling.h
@@ -28,6 +28,7 @@
 namespace glic {
 namespace prefs {
 enum class SettingsPolicyState;
+enum class FreStatus;
 }
 namespace mojom {
 // TODO(crbug.com/406500707): This forward declaration is needed because we use
@@ -79,6 +80,7 @@
   explicit GlicGlobalEnabling(Delegate& delegate);
   ~GlicGlobalEnabling();
   bool IsEnabledByFlags();
+  bool IsSystemRequirementMet() const;
   bool IsLocaleEnabled() const { return locale_enablement_.value_or(true); }
   bool IsCountryEnabled() const { return country_enablement_.value_or(true); }
 
@@ -205,12 +207,29 @@
     // Whether disallowed by locale filtering.
     bool disallowed_by_locale_filter : 1 = false;
 
+    // Whether the Glic feature flag is disabled.
+    bool feature_flag_disabled : 1 = false;
+
+    // Whether system requirements (relevant to ChromeOS only) for Glic are not
+    // met.
+    bool system_requirement_not_met : 1 = false;
+
     // Whether live (audio) functionality is disallowed for this account type.
     bool live_disallowed : 1 = false;
 
     // Whether share image functionality is disallowed for this account type.
     bool share_image_disallowed : 1 = false;
 
+    // LINT.IfChange(FeatureDisabledReason)
+    enum class FeatureDisabledReason {
+      kFeatureFlagDisabled = 0,
+      kCountryDisabled = 1,
+      kLocaleDisabled = 2,
+      kSystemRequirementNotMet = 3,
+      kMaxValue = kSystemRequirementNotMet,
+    };
+    // LINT.ThenChange(//tools/metrics/histograms/metadata/glic/enums.xml:GlicFeatureDisabledReason)
+
     enum class Reason {
       kFeatureDisabled = 0,
       kNotRegularProfile = 1,
@@ -270,6 +289,7 @@
    private:
     // `suffix` should be either "Startup" or "SteadyState".
     void RecordMetrics(const std::string& suffix) const;
+    void RecordFeatureDisabledReason(const std::string& suffix) const;
   };
   static ProfileEnablement EnablementForProfile(Profile* profile);
 
@@ -306,6 +326,22 @@
   // otherwise.
   bool HasConsented();
 
+  // Returns true if Trust-First Onboarding is enabled for this profile.
+  bool IsTrustFirstOnboardingEnabled() const;
+
+  // Returns the FRE status.
+  prefs::FreStatus GetCompletedFre() const;
+  // Sets the FRE status.
+  void SetCompletedFre(prefs::FreStatus status);
+
+  // Returns whether user enabled actuation on web.
+  bool GetUserEnabledActuationOnWeb() const;
+  // Returns true if the user enabled actuation on web pref is at its default
+  // value.
+  bool IsUserEnabledActuationOnWebDefault() const;
+  // Sets whether user enabled actuation on web.
+  void SetUserEnabledActuationOnWeb(bool enabled);
+
   // Checks if startup metrics have already been recorded, and if not, records
   // them.
   void MaybeRecordStartupMetrics();
@@ -343,6 +379,10 @@
   base::CallbackListSubscription RegisterOnConsentChanged(
       ConsentChangedCallback callback);
 
+  using UserEnabledActuationOnWebChangedCallback = base::RepeatingClosure;
+  base::CallbackListSubscription RegisterOnUserEnabledActuationOnWebChanged(
+      UserEnabledActuationOnWebChangedCallback callback);
+
   // This is called anytime ShouldShowSettingsPage() might return a different
   // value.
   using ShowSettingsPageChangedCallback = base::RepeatingClosure;
@@ -355,6 +395,7 @@
 
  private:
   void OnGlicSettingsPolicyChanged();
+  void OnUserEnabledActuationOnWebChanged();
 
   // IdentityManagerObserver:
   void OnPrimaryAccountChanged(
@@ -400,6 +441,10 @@
   EnableChangedCallbackList enable_changed_callback_list_;
   using OnConsentChangeCallbackList = base::RepeatingCallbackList<void()>;
   OnConsentChangeCallbackList consent_changed_callback_list_;
+  using UserEnabledActuationOnWebChangedCallbackList =
+      base::RepeatingCallbackList<void()>;
+  UserEnabledActuationOnWebChangedCallbackList
+      user_enabled_actuation_on_web_changed_callback_list_;
   using OnShowSettingsPageChangeCallbackList =
       base::RepeatingCallbackList<void()>;
   OnShowSettingsPageChangeCallbackList
diff --git a/chrome/browser/glic/public/glic_enabling_metrics_unittest.cc b/chrome/browser/glic/public/glic_enabling_metrics_unittest.cc
index 1531f31..94949f82 100644
--- a/chrome/browser/glic/public/glic_enabling_metrics_unittest.cc
+++ b/chrome/browser/glic/public/glic_enabling_metrics_unittest.cc
@@ -97,4 +97,61 @@
       1);
 }
 
+TEST(GlicProfileEnablementTest, RecordFeatureDisabledReason) {
+  base::HistogramTester histograms;
+  GlicEnabling::ProfileEnablement enablement;
+
+  enablement.feature_disabled = true;
+  enablement.feature_flag_disabled = true;
+  enablement.disallowed_by_country_filter = true;
+  enablement.disallowed_by_locale_filter = true;
+  enablement.system_requirement_not_met = true;
+
+  enablement.RecordStartupMetrics();
+
+  histograms.ExpectBucketCount(
+      "Glic.GlobalEnabling.FeatureDisabledReason.Startup",
+      GlicEnabling::ProfileEnablement::FeatureDisabledReason::
+          kFeatureFlagDisabled,
+      1);
+  histograms.ExpectBucketCount(
+      "Glic.GlobalEnabling.FeatureDisabledReason.Startup",
+      GlicEnabling::ProfileEnablement::FeatureDisabledReason::kCountryDisabled,
+      1);
+  histograms.ExpectBucketCount(
+      "Glic.GlobalEnabling.FeatureDisabledReason.Startup",
+      GlicEnabling::ProfileEnablement::FeatureDisabledReason::kLocaleDisabled,
+      1);
+  histograms.ExpectBucketCount(
+      "Glic.GlobalEnabling.FeatureDisabledReason.Startup",
+      GlicEnabling::ProfileEnablement::FeatureDisabledReason::
+          kSystemRequirementNotMet,
+      1);
+  histograms.ExpectTotalCount(
+      "Glic.GlobalEnabling.FeatureDisabledReason.Startup", 4);
+
+  enablement.RecordSteadyStateMetrics();
+
+  histograms.ExpectBucketCount(
+      "Glic.GlobalEnabling.FeatureDisabledReason.SteadyState",
+      GlicEnabling::ProfileEnablement::FeatureDisabledReason::
+          kFeatureFlagDisabled,
+      1);
+  histograms.ExpectBucketCount(
+      "Glic.GlobalEnabling.FeatureDisabledReason.SteadyState",
+      GlicEnabling::ProfileEnablement::FeatureDisabledReason::kCountryDisabled,
+      1);
+  histograms.ExpectBucketCount(
+      "Glic.GlobalEnabling.FeatureDisabledReason.SteadyState",
+      GlicEnabling::ProfileEnablement::FeatureDisabledReason::kLocaleDisabled,
+      1);
+  histograms.ExpectBucketCount(
+      "Glic.GlobalEnabling.FeatureDisabledReason.SteadyState",
+      GlicEnabling::ProfileEnablement::FeatureDisabledReason::
+          kSystemRequirementNotMet,
+      1);
+  histograms.ExpectTotalCount(
+      "Glic.GlobalEnabling.FeatureDisabledReason.SteadyState", 4);
+}
+
 }  // namespace glic
diff --git a/chrome/browser/glic/public/glic_enabling_unittest.cc b/chrome/browser/glic/public/glic_enabling_unittest.cc
index ce9771a..3a2431d94 100644
--- a/chrome/browser/glic/public/glic_enabling_unittest.cc
+++ b/chrome/browser/glic/public/glic_enabling_unittest.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/glic/host/glic_features.mojom-features.h"
 #include "chrome/browser/glic/host/glic_features.mojom.h"
 #include "chrome/browser/glic/public/features.h"
+#include "chrome/browser/glic/public/glic_keyed_service.h"
 #include "chrome/browser/glic/test_support/glic_test_util.h"
 #include "chrome/browser/global_features.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
@@ -526,34 +527,31 @@
 };
 
 TEST_F(GlicEnablingTrustFirstOnboardingTest, NotConsented_ReturnsReady) {
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre,
-      static_cast<int>(prefs::FreStatus::kIncomplete));
+  glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+      prefs::FreStatus::kIncomplete);
 
   EXPECT_EQ(GlicEnabling::GetProfileReadyState(profile()),
             mojom::ProfileReadyState::kReady);
 }
 
 TEST_F(GlicEnablingTrustFirstOnboardingTest, Consented_ReturnsFalse) {
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre, static_cast<int>(prefs::FreStatus::kCompleted));
+  glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+      prefs::FreStatus::kCompleted);
 
   EXPECT_FALSE(
       GlicEnabling::IsTrustFirstOnboardingEnabledForProfile(profile()));
 }
 
 TEST_F(GlicEnablingTrustFirstOnboardingTest, NotConsented_ReturnsTrue) {
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre,
-      static_cast<int>(prefs::FreStatus::kIncomplete));
+  glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+      prefs::FreStatus::kIncomplete);
 
   EXPECT_TRUE(GlicEnabling::IsTrustFirstOnboardingEnabledForProfile(profile()));
 }
 
 TEST_F(GlicEnablingStandardFreTest, NotConsented_ReturnsIneligible) {
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre,
-      static_cast<int>(prefs::FreStatus::kIncomplete));
+  glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+      prefs::FreStatus::kIncomplete);
 
   EXPECT_EQ(GlicEnabling::GetProfileReadyState(profile()),
             mojom::ProfileReadyState::kIneligible);
@@ -581,8 +579,8 @@
 };
 
 TEST_P(GlicEnablingAnyFreModeTest, Consented_ReturnsReady) {
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre, static_cast<int>(prefs::FreStatus::kCompleted));
+  glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+      prefs::FreStatus::kCompleted);
 
   EXPECT_EQ(GlicEnabling::GetProfileReadyState(profile()),
             mojom::ProfileReadyState::kReady);
@@ -590,9 +588,8 @@
 
 #if !BUILDFLAG(IS_CHROMEOS)
 TEST_P(GlicEnablingAnyFreModeTest, NotSignedIn_ReturnsIneligible) {
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre,
-      static_cast<int>(prefs::FreStatus::kIncomplete));
+  glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+      prefs::FreStatus::kIncomplete);
 
   // Simulate "Not signed in" by removing the primary account.
   signin::IdentityManager* identity_manager =
@@ -606,17 +603,16 @@
 
 TEST_P(GlicEnablingAnyFreModeTest,
        IsEnabledAndConsentForProfile_NotConsented_ReturnsFalse) {
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre,
-      static_cast<int>(prefs::FreStatus::kIncomplete));
+  glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+      prefs::FreStatus::kIncomplete);
 
   EXPECT_FALSE(GlicEnabling::IsEnabledAndConsentForProfile(profile()));
 }
 
 TEST_P(GlicEnablingAnyFreModeTest,
        IsEnabledAndConsentForProfile_Consented_ReturnsTrue) {
-  profile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre, static_cast<int>(prefs::FreStatus::kCompleted));
+  glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+      prefs::FreStatus::kCompleted);
 
   EXPECT_TRUE(GlicEnabling::IsEnabledAndConsentForProfile(profile()));
 }
@@ -673,8 +669,8 @@
   void SetConsent(bool has_consent) {
     const auto fre_status = has_consent ? prefs::FreStatus::kCompleted
                                         : prefs::FreStatus::kIncomplete;
-    profile()->GetPrefs()->SetInteger(prefs::kGlicCompletedFre,
-                                      static_cast<int>(fre_status));
+    glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+        fre_status);
   }
 
  private:
@@ -773,8 +769,8 @@
   void SetConsent(bool has_consent) {
     const auto fre_status = has_consent ? prefs::FreStatus::kCompleted
                                         : prefs::FreStatus::kIncomplete;
-    profile()->GetPrefs()->SetInteger(prefs::kGlicCompletedFre,
-                                      static_cast<int>(fre_status));
+    glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+        fre_status);
   }
 
  private:
@@ -816,5 +812,44 @@
         ContextMenuFeatureParams{.name = "FeatureEnabledNoTrustFirstNoConsent",
                                  .is_feature_enabled = true,
                                  .expected_result = false}));
+
+TEST_F(GlicEnablingProfileEligibilityTest,
+       UserEnabledActuationOnWebChangedCallback) {
+  bool callback_called = false;
+  auto subscription =
+      glic::GlicKeyedService::Get(profile())
+          ->enabling()
+          .RegisterOnUserEnabledActuationOnWebChanged(
+              base::BindLambdaForTesting([&]() { callback_called = true; }));
+
+  glic::GlicKeyedService::Get(profile())
+      ->enabling()
+      .SetUserEnabledActuationOnWeb(true);
+  EXPECT_TRUE(callback_called);
+
+  callback_called = false;
+  glic::GlicKeyedService::Get(profile())
+      ->enabling()
+      .SetUserEnabledActuationOnWeb(false);
+  EXPECT_TRUE(callback_called);
+}
+
+TEST_F(GlicEnablingProfileEligibilityTest, ConsentChangedCallback) {
+  bool callback_called = false;
+  auto subscription = glic::GlicKeyedService::Get(profile())
+                          ->enabling()
+                          .RegisterOnConsentChanged(base::BindLambdaForTesting(
+                              [&]() { callback_called = true; }));
+
+  glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+      prefs::FreStatus::kCompleted);
+  EXPECT_TRUE(callback_called);
+
+  callback_called = false;
+  glic::GlicKeyedService::Get(profile())->enabling().SetCompletedFre(
+      prefs::FreStatus::kIncomplete);
+  EXPECT_TRUE(callback_called);
+}
+
 }  // namespace
 }  // namespace glic
diff --git a/chrome/browser/glic/public/glic_keyed_service.cc b/chrome/browser/glic/public/glic_keyed_service.cc
index 0f78dd7..2494287 100644
--- a/chrome/browser/glic/public/glic_keyed_service.cc
+++ b/chrome/browser/glic/public/glic_keyed_service.cc
@@ -219,15 +219,11 @@
   // is shown for testing convenience.
   auto* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(::switches::kGlicAlwaysOpenFre)) {
-    profile_->GetPrefs()->SetInteger(
-        prefs::kGlicCompletedFre,
-        static_cast<int>(prefs::FreStatus::kNotStarted));
+    enabling_->SetCompletedFre(prefs::FreStatus::kNotStarted);
     // or if automation is enabled, skip FRE
   } else if (command_line->HasSwitch(::switches::kGlicAutomation) ||
              command_line->HasSwitch(::switches::kGlicAlwaysSkipFre)) {
-    profile_->GetPrefs()->SetInteger(
-        prefs::kGlicCompletedFre,
-        static_cast<int>(prefs::FreStatus::kCompleted));
+    enabling_->SetCompletedFre(prefs::FreStatus::kCompleted);
   }
 
   // Sets up prefs storing manually configured glic guest URLs. Intended for
diff --git a/chrome/browser/glic/service/glic_instance_coordinator_browsertest.cc b/chrome/browser/glic/service/glic_instance_coordinator_browsertest.cc
index da9e536..571dd5a 100644
--- a/chrome/browser/glic/service/glic_instance_coordinator_browsertest.cc
+++ b/chrome/browser/glic/service/glic_instance_coordinator_browsertest.cc
@@ -588,9 +588,9 @@
     GlicInstanceCoordinatorTrustFirstOnboardingArm1BrowserTest,
     DISABLED_TabContentsDaisyChainingNotSuppressedWhenTrustFirstArm1Shown) {
   // Open FRE.
-  GetProfile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre,
-      static_cast<int>(prefs::FreStatus::kNotStarted));
+  glic::GlicKeyedService::Get(GetProfile())
+      ->enabling()
+      .SetCompletedFre(glic::prefs::FreStatus::kNotStarted);
 
   ASSERT_OK(OpenGlicForActiveTab());
   tabs::TabInterface* tab1 = GetTabListInterface()->GetActiveTab();
@@ -627,8 +627,9 @@
     GlicInstanceCoordinatorTrustFirstOnboardingArm1BrowserTest,
     AutoSubmitNotDivertedWhenFreCompleted) {
   // Simulate FRE completion.
-  GetProfile()->GetPrefs()->SetInteger(
-      prefs::kGlicCompletedFre, static_cast<int>(prefs::FreStatus::kCompleted));
+  glic::GlicKeyedService::Get(GetProfile())
+      ->enabling()
+      .SetCompletedFre(glic::prefs::FreStatus::kCompleted);
 
   tabs::TabInterface* tab = GetTabListInterface()->GetActiveTab();
 
@@ -822,10 +823,7 @@
   ASSERT_OK_AND_ASSIGN(GlicInstanceImpl * instance, OpenGlicForActiveTab());
   auto instance_id = instance->id();
 
-  // Simulates a user closing the active tab (Ctrl+W).
-  // This ensures the generic "create historical tab" flag is set, which is
-  // required for LiveTabContext to pick it up for session restore.
-  chrome::CloseTab(browser());
+  GetTabListInterface()->CloseTab(tab->GetHandle());
 
   // Restore the tab.
   GlicTestTabAddedWaiter waiter(GetProfile());
@@ -888,7 +886,7 @@
   GetTabListInterface()->ActivateTab(tab2->GetHandle());
 
   // Close Tab 2.
-  chrome::CloseTab(browser());
+  GetTabListInterface()->CloseTab(tab2->GetHandle());
 
   // Restore Tab 2.
   GlicTestTabAddedWaiter waiter(GetProfile());
@@ -931,7 +929,7 @@
   ASSERT_EQ(coordinator().GetInstanceForTab(tab2), instance);
 
   // Close Tab 2.
-  chrome::CloseTab(browser());
+  GetTabListInterface()->CloseTab(tab2->GetHandle());
 
   // The instance should still exist because Tab 1 keeps it alive.
   ASSERT_EQ(coordinator().GetInstanceImplFor(instance_id), instance);
@@ -977,7 +975,7 @@
       WaitForSidePanelState(tab, GlicSidePanelCoordinator::State::kClosed));
 
   // Close the tab.
-  chrome::CloseTab(browser());
+  GetTabListInterface()->CloseTab(tab->GetHandle());
 
   // Restore the tab.
   GlicTestTabAddedWaiter waiter(GetProfile());
diff --git a/chrome/browser/glic/service/metrics/glic_instance_metrics_ui_test.cc b/chrome/browser/glic/service/metrics/glic_instance_metrics_ui_test.cc
index 9c27f118..4544b2d1 100644
--- a/chrome/browser/glic/service/metrics/glic_instance_metrics_ui_test.cc
+++ b/chrome/browser/glic/service/metrics/glic_instance_metrics_ui_test.cc
@@ -8,6 +8,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/glic/host/glic_features.mojom.h"
+#include "chrome/browser/glic/public/glic_keyed_service.h"
 #include "chrome/browser/glic/service/glic_instance_impl.h"
 #include "chrome/browser/glic/service/metrics/glic_instance_metrics.h"
 #include "chrome/browser/glic/service/metrics/glic_metrics_session_manager.h"
@@ -137,9 +138,9 @@
 
   void SetUpOnMainThread() override {
     test::InteractiveGlicTest::SetUpOnMainThread();
-    browser()->profile()->GetPrefs()->SetInteger(
-        glic::prefs::kGlicCompletedFre,
-        static_cast<int>(glic::prefs::FreStatus::kNotStarted));
+    glic::GlicKeyedService::Get(browser()->profile())
+        ->enabling()
+        .SetCompletedFre(glic::prefs::FreStatus::kNotStarted);
   }
 
  protected:
diff --git a/chrome/browser/glic/test_support/glic_test_util.cc b/chrome/browser/glic/test_support/glic_test_util.cc
index 4b6a091..e6255e8 100644
--- a/chrome/browser/glic/test_support/glic_test_util.cc
+++ b/chrome/browser/glic/test_support/glic_test_util.cc
@@ -249,8 +249,7 @@
 }
 
 void SetFRECompletion(Profile* profile, prefs::FreStatus fre_status) {
-  profile->GetPrefs()->SetInteger(prefs::kGlicCompletedFre,
-                                  static_cast<int>(fre_status));
+  glic::GlicKeyedService::Get(profile)->enabling().SetCompletedFre(fre_status);
 }
 
 void InvalidateAccount(Profile* profile) {
diff --git a/chrome/browser/headless/BUILD.gn b/chrome/browser/headless/BUILD.gn
index eb2b687d..7b13eb39 100644
--- a/chrome/browser/headless/BUILD.gn
+++ b/chrome/browser/headless/BUILD.gn
@@ -110,33 +110,33 @@
     defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
     sources = [
-      "headless_mode_browsertest.cc",
-      "headless_mode_browsertest.h",
+      "test/headless_mode_browsertest.cc",
+      "test/headless_mode_browsertest.h",
     ]
 
     if (is_linux || is_win || is_mac) {
       sources += [
-        "headless_mode_browsertest_utils.h",
-        "headless_mode_command_browsertest.cc",
-        "headless_mode_devtooled_browsertest.cc",
-        "headless_mode_devtooled_browsertest.h",
-        "headless_mode_protocol_browsertest.cc",
-        "headless_mode_protocol_browsertest.h",
         "test/headless_browser_test_utils.cc",
         "test/headless_browser_test_utils.h",
+        "test/headless_mode_browsertest_utils.h",
+        "test/headless_mode_command_browsertest.cc",
+        "test/headless_mode_devtooled_browsertest.cc",
+        "test/headless_mode_devtooled_browsertest.h",
+        "test/headless_mode_protocol_browsertest.cc",
+        "test/headless_mode_protocol_browsertest.h",
       ]
     }
 
     if (is_linux) {
-      sources += [ "headless_mode_browsertest_linux.cc" ]
+      sources += [ "test/headless_mode_browsertest_linux.cc" ]
     }
 
     if (is_win) {
-      sources += [ "headless_mode_browsertest_win.cc" ]
+      sources += [ "test/headless_mode_browsertest_win.cc" ]
     }
 
     if (is_mac) {
-      sources += [ "headless_mode_browsertest_mac.mm" ]
+      sources += [ "test/headless_mode_browsertest_mac.mm" ]
     }
 
     deps = [
diff --git a/chrome/browser/headless/DEPS b/chrome/browser/headless/DEPS
index be89e702..6d4e51e 100644
--- a/chrome/browser/headless/DEPS
+++ b/chrome/browser/headless/DEPS
@@ -1,45 +1,14 @@
 include_rules = [
   "-chrome/browser",
-  "+chrome/browser/chrome_browser_main_extra_parts.h",
   "+chrome/browser/headless",
-  "+chrome/chrome_elf/chrome_elf_main.h",
-  "+components/devtools/simple_devtools_protocol_client",
   "+components/headless",
-  "+components/keep_alive_registry",
 ]
 
 specific_include_rules = {
-  "headless_mode_browsertest\.cc": [
-    "+chrome/browser/infobars/confirm_infobar_creator.h",
-    "+chrome/browser/profiles/profile.h",
-    "+chrome/browser/ui/browser.h",
-    "+chrome/browser/ui/browser_window.h",
-    "+chrome/browser/ui/views/frame/app_menu_button.h",
-    "+chrome/browser/ui/views/frame/browser_view.h",
-    "+chrome/browser/ui/views/frame/toolbar_button_provider.h",
+  "headless_mode_init\.cc": [
+    "+chrome/chrome_elf/chrome_elf_main.h",
   ],
-  "headless_mode_browsertest_linux\.cc": [
-    "+chrome/browser/ui/browser.h",
-    "+chrome/browser/ui/browser_window.h",
-  ],
-  "headless_mode_browsertest_mac\.mm": [
-    "+chrome/browser/ui/browser.h",
-    "+chrome/browser/ui/browser_commands.h",
-    "+chrome/browser/ui/browser_window.h",
-  ],
-  "headless_mode_browsertest_win\.cc": [
-    "+chrome/browser/ui/browser.h",
-    "+chrome/browser/ui/browser_window.h",
-  ],
-  "headless_mode_command_browsertest\.cc": [
-    "+pdf",
-  ],
-  "headless_mode_devtooled_browsertest\.cc": [
-    "+chrome/browser/profiles/profile.h",
-    "+chrome/browser/ui/browser.h",
-  ],
-  "headless_mode_protocol_browsertest\.h": [
-    "+chrome/browser/preloading/scoped_prewarm_feature_list.h",
+  "chrome_browser_main_extra_parts_headless\.h": [
+  "+chrome/browser/chrome_browser_main_extra_parts.h",
   ],
 }
-
diff --git a/chrome/browser/headless/test/DEPS b/chrome/browser/headless/test/DEPS
new file mode 100644
index 0000000..7660fdc
--- /dev/null
+++ b/chrome/browser/headless/test/DEPS
@@ -0,0 +1,14 @@
+include_rules = [
+  "+chrome/browser/infobars",
+  "+chrome/browser/preloading",
+  "+chrome/browser/profiles",
+  "+chrome/browser/ui",
+  "+components/devtools/simple_devtools_protocol_client",
+  "+components/headless",
+]
+
+specific_include_rules = {
+  "headless_mode_command_browsertest\.cc": [
+    "+pdf",
+  ],
+}
diff --git a/chrome/browser/headless/headless_mode_browsertest.cc b/chrome/browser/headless/test/headless_mode_browsertest.cc
similarity index 97%
rename from chrome/browser/headless/headless_mode_browsertest.cc
rename to chrome/browser/headless/test/headless_mode_browsertest.cc
index ba3aed8..08030ac 100644
--- a/chrome/browser/headless/headless_mode_browsertest.cc
+++ b/chrome/browser/headless/test/headless_mode_browsertest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/headless/headless_mode_browsertest.h"
+#include "chrome/browser/headless/test/headless_mode_browsertest.h"
 
 #include "build/build_config.h"
 
@@ -23,8 +23,8 @@
 #include "base/test/test_timeouts.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/branding_buildflags.h"
-#include "chrome/browser/headless/headless_mode_browsertest_utils.h"
 #include "chrome/browser/headless/headless_mode_init.h"
+#include "chrome/browser/headless/test/headless_mode_browsertest_utils.h"
 #include "chrome/browser/infobars/confirm_infobar_creator.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -115,6 +115,13 @@
   return browser()->tab_strip_model()->GetActiveWebContents();
 }
 
+void HeadlessModeBrowserTest::FlushPostedTasks() {
+  base::RunLoop run_loop;
+  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE, run_loop.QuitClosure());
+  run_loop.Run();
+}
+
 void HeadlessModeBrowserTestWithStartWindowMode::SetUpCommandLine(
     base::CommandLine* command_line) {
   HeadlessModeBrowserTest::SetUpCommandLine(command_line);
@@ -292,7 +299,7 @@
   web_contents->Close();
   web_contents.reset();
 
-  base::RunLoop().RunUntilIdle();
+  FlushPostedTasks();
 
   EXPECT_THAT(headers_.at("User-Agent"), testing::HasSubstr("HeadlessChrome/"));
 }
diff --git a/chrome/browser/headless/headless_mode_browsertest.h b/chrome/browser/headless/test/headless_mode_browsertest.h
similarity index 89%
rename from chrome/browser/headless/headless_mode_browsertest.h
rename to chrome/browser/headless/test/headless_mode_browsertest.h
index 7bda5f3..5675a49 100644
--- a/chrome/browser/headless/headless_mode_browsertest.h
+++ b/chrome/browser/headless/test/headless_mode_browsertest.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_HEADLESS_HEADLESS_MODE_BROWSERTEST_H_
-#define CHROME_BROWSER_HEADLESS_HEADLESS_MODE_BROWSERTEST_H_
+#ifndef CHROME_BROWSER_HEADLESS_TEST_HEADLESS_MODE_BROWSERTEST_H_
+#define CHROME_BROWSER_HEADLESS_TEST_HEADLESS_MODE_BROWSERTEST_H_
 
 #include <memory>
 
@@ -45,6 +45,8 @@
 
   content::WebContents* GetActiveWebContents();
 
+  void FlushPostedTasks();
+
  private:
   bool headful_mode_ = false;
   std::unique_ptr<HeadlessModeHandle> headless_mode_handle_;
@@ -73,4 +75,4 @@
 
 }  // namespace headless
 
-#endif  // CHROME_BROWSER_HEADLESS_HEADLESS_MODE_BROWSERTEST_H_
+#endif  // CHROME_BROWSER_HEADLESS_TEST_HEADLESS_MODE_BROWSERTEST_H_
diff --git a/chrome/browser/headless/headless_mode_browsertest_linux.cc b/chrome/browser/headless/test/headless_mode_browsertest_linux.cc
similarity index 95%
rename from chrome/browser/headless/headless_mode_browsertest_linux.cc
rename to chrome/browser/headless/test/headless_mode_browsertest_linux.cc
index e55ea256..dfc430d0 100644
--- a/chrome/browser/headless/headless_mode_browsertest_linux.cc
+++ b/chrome/browser/headless/test/headless_mode_browsertest_linux.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/headless/headless_mode_browsertest.h"
+#include "chrome/browser/headless/test/headless_mode_browsertest.h"
 
-#include "chrome/browser/headless/headless_mode_browsertest_utils.h"
+#include "chrome/browser/headless/test/headless_mode_browsertest_utils.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/headless/headless_mode_browsertest_mac.mm b/chrome/browser/headless/test/headless_mode_browsertest_mac.mm
similarity index 97%
rename from chrome/browser/headless/headless_mode_browsertest_mac.mm
rename to chrome/browser/headless/test/headless_mode_browsertest_mac.mm
index a1da67f..aed39fd6 100644
--- a/chrome/browser/headless/headless_mode_browsertest_mac.mm
+++ b/chrome/browser/headless/test/headless_mode_browsertest_mac.mm
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/headless/headless_mode_browsertest.h"
+#include "chrome/browser/headless/test/headless_mode_browsertest.h"
 
 #import <Cocoa/Cocoa.h>
 
-#include "chrome/browser/headless/headless_mode_browsertest_utils.h"
+#include "chrome/browser/headless/test/headless_mode_browsertest_utils.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_window.h"
diff --git a/chrome/browser/headless/headless_mode_browsertest_utils.h b/chrome/browser/headless/test/headless_mode_browsertest_utils.h
similarity index 75%
rename from chrome/browser/headless/headless_mode_browsertest_utils.h
rename to chrome/browser/headless/test/headless_mode_browsertest_utils.h
index 3d7db20..640266a 100644
--- a/chrome/browser/headless/headless_mode_browsertest_utils.h
+++ b/chrome/browser/headless/test/headless_mode_browsertest_utils.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_HEADLESS_HEADLESS_MODE_BROWSERTEST_UTILS_H_
-#define CHROME_BROWSER_HEADLESS_HEADLESS_MODE_BROWSERTEST_UTILS_H_
+#ifndef CHROME_BROWSER_HEADLESS_TEST_HEADLESS_MODE_BROWSERTEST_UTILS_H_
+#define CHROME_BROWSER_HEADLESS_TEST_HEADLESS_MODE_BROWSERTEST_UTILS_H_
 
 #include "ui/gfx/geometry/rect.h"
 
@@ -23,4 +23,4 @@
 
 }  // namespace headless::test
 
-#endif  // CHROME_BROWSER_HEADLESS_HEADLESS_MODE_BROWSERTEST_UTILS_H_
+#endif  // CHROME_BROWSER_HEADLESS_TEST_HEADLESS_MODE_BROWSERTEST_UTILS_H_
diff --git a/chrome/browser/headless/headless_mode_browsertest_win.cc b/chrome/browser/headless/test/headless_mode_browsertest_win.cc
similarity index 98%
rename from chrome/browser/headless/headless_mode_browsertest_win.cc
rename to chrome/browser/headless/test/headless_mode_browsertest_win.cc
index 514ee4e..aa4b4f4 100644
--- a/chrome/browser/headless/headless_mode_browsertest_win.cc
+++ b/chrome/browser/headless/test/headless_mode_browsertest_win.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/headless/headless_mode_browsertest.h"
+#include "chrome/browser/headless/test/headless_mode_browsertest.h"
 
 #include <windows.h>
 
 #include <set>
 
 #include "base/strings/stringprintf.h"
-#include "chrome/browser/headless/headless_mode_browsertest_utils.h"
+#include "chrome/browser/headless/test/headless_mode_browsertest_utils.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/common/chrome_switches.h"
diff --git a/chrome/browser/headless/headless_mode_command_browsertest.cc b/chrome/browser/headless/test/headless_mode_command_browsertest.cc
similarity index 99%
rename from chrome/browser/headless/headless_mode_command_browsertest.cc
rename to chrome/browser/headless/test/headless_mode_command_browsertest.cc
index 533f240..da6f129 100644
--- a/chrome/browser/headless/headless_mode_command_browsertest.cc
+++ b/chrome/browser/headless/test/headless_mode_command_browsertest.cc
@@ -23,7 +23,7 @@
 #include "base/threading/platform_thread.h"
 #include "base/values.h"
 #include "build/build_config.h"
-#include "chrome/browser/headless/headless_mode_browsertest.h"
+#include "chrome/browser/headless/test/headless_mode_browsertest.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/headless/command_handler/headless_command_handler.h"
 #include "components/headless/command_handler/headless_command_switches.h"
diff --git a/chrome/browser/headless/headless_mode_devtooled_browsertest.cc b/chrome/browser/headless/test/headless_mode_devtooled_browsertest.cc
similarity index 92%
rename from chrome/browser/headless/headless_mode_devtooled_browsertest.cc
rename to chrome/browser/headless/test/headless_mode_devtooled_browsertest.cc
index 6a976bc..8a44632 100644
--- a/chrome/browser/headless/headless_mode_devtooled_browsertest.cc
+++ b/chrome/browser/headless/test/headless_mode_devtooled_browsertest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/headless/headless_mode_devtooled_browsertest.h"
+#include "chrome/browser/headless/test/headless_mode_devtooled_browsertest.h"
 
 #include "base/functional/bind.h"
 #include "base/values.h"
@@ -49,7 +49,7 @@
 
   browser_devtools_client_.DetachClient();
 
-  base::RunLoop().RunUntilIdle();
+  FlushPostedTasks();
 }
 
 void HeadlessModeDevTooledBrowserTest::
@@ -60,11 +60,6 @@
   }
 }
 
-void HeadlessModeDevTooledBrowserTest::WebContentsDestroyed() {
-  FinishAsyncTest();
-  FAIL() << "Web contents destroyed unexpectedly.";
-}
-
 void HeadlessModeDevTooledBrowserTest::RunAsyncTest() {
   DCHECK(!run_loop_);
 
diff --git a/chrome/browser/headless/headless_mode_devtooled_browsertest.h b/chrome/browser/headless/test/headless_mode_devtooled_browsertest.h
similarity index 90%
rename from chrome/browser/headless/headless_mode_devtooled_browsertest.h
rename to chrome/browser/headless/test/headless_mode_devtooled_browsertest.h
index c2c0670..b053639 100644
--- a/chrome/browser/headless/headless_mode_devtooled_browsertest.h
+++ b/chrome/browser/headless/test/headless_mode_devtooled_browsertest.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_HEADLESS_HEADLESS_MODE_DEVTOOLED_BROWSERTEST_H_
-#define CHROME_BROWSER_HEADLESS_HEADLESS_MODE_DEVTOOLED_BROWSERTEST_H_
+#ifndef CHROME_BROWSER_HEADLESS_TEST_HEADLESS_MODE_DEVTOOLED_BROWSERTEST_H_
+#define CHROME_BROWSER_HEADLESS_TEST_HEADLESS_MODE_DEVTOOLED_BROWSERTEST_H_
 
 #include <memory>
 
 #include "base/run_loop.h"
-#include "chrome/browser/headless/headless_mode_browsertest.h"
+#include "chrome/browser/headless/test/headless_mode_browsertest.h"
 #include "components/devtools/simple_devtools_protocol_client/simple_devtools_protocol_client.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -34,7 +34,6 @@
 
   // content::WebContentsObserver implementation:
   void DocumentOnLoadCompletedInPrimaryMainFrame() override;
-  void WebContentsDestroyed() override;
 
   // Implemented by tests and used to send requests to DevTools. Subclasses
   // need to ensure that FinishAsyncTest() is called at some point.
@@ -85,4 +84,4 @@
 
 }  // namespace headless
 
-#endif  // CHROME_BROWSER_HEADLESS_HEADLESS_MODE_DEVTOOLED_BROWSERTEST_H_
+#endif  // CHROME_BROWSER_HEADLESS_TEST_HEADLESS_MODE_DEVTOOLED_BROWSERTEST_H_
diff --git a/chrome/browser/headless/headless_mode_protocol_browsertest.cc b/chrome/browser/headless/test/headless_mode_protocol_browsertest.cc
similarity index 99%
rename from chrome/browser/headless/headless_mode_protocol_browsertest.cc
rename to chrome/browser/headless/test/headless_mode_protocol_browsertest.cc
index e0753c9..d972452 100644
--- a/chrome/browser/headless/headless_mode_protocol_browsertest.cc
+++ b/chrome/browser/headless/test/headless_mode_protocol_browsertest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/headless/headless_mode_protocol_browsertest.h"
+#include "chrome/browser/headless/test/headless_mode_protocol_browsertest.h"
 
 #include <string_view>
 
diff --git a/chrome/browser/headless/headless_mode_protocol_browsertest.h b/chrome/browser/headless/test/headless_mode_protocol_browsertest.h
similarity index 93%
rename from chrome/browser/headless/headless_mode_protocol_browsertest.h
rename to chrome/browser/headless/test/headless_mode_protocol_browsertest.h
index b0e6924..40eff69 100644
--- a/chrome/browser/headless/headless_mode_protocol_browsertest.h
+++ b/chrome/browser/headless/test/headless_mode_protocol_browsertest.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_HEADLESS_HEADLESS_MODE_PROTOCOL_BROWSERTEST_H_
-#define CHROME_BROWSER_HEADLESS_HEADLESS_MODE_PROTOCOL_BROWSERTEST_H_
+#ifndef CHROME_BROWSER_HEADLESS_TEST_HEADLESS_MODE_PROTOCOL_BROWSERTEST_H_
+#define CHROME_BROWSER_HEADLESS_TEST_HEADLESS_MODE_PROTOCOL_BROWSERTEST_H_
 
 #include <string>
 #include <string_view>
@@ -11,7 +11,7 @@
 #include "base/command_line.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/values.h"
-#include "chrome/browser/headless/headless_mode_devtooled_browsertest.h"
+#include "chrome/browser/headless/test/headless_mode_devtooled_browsertest.h"
 #include "chrome/browser/preloading/scoped_prewarm_feature_list.h"
 #include "components/headless/test/test_meta_info.h"
 
@@ -107,4 +107,4 @@
 
 }  // namespace headless
 
-#endif  // CHROME_BROWSER_HEADLESS_HEADLESS_MODE_PROTOCOL_BROWSERTEST_H_
+#endif  // CHROME_BROWSER_HEADLESS_TEST_HEADLESS_MODE_PROTOCOL_BROWSERTEST_H_
diff --git a/chrome/browser/nearby_sharing/BUILD.gn b/chrome/browser/nearby_sharing/BUILD.gn
index 097a57b..587597e 100644
--- a/chrome/browser/nearby_sharing/BUILD.gn
+++ b/chrome/browser/nearby_sharing/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chrome_build.gni")
+
 assert(is_chromeos, "Nearby Share is CrOS only")
 
 source_set("share_target") {
@@ -28,3 +30,235 @@
     "//url",
   ]
 }
+
+source_set("nearby_sharing") {
+  public = [
+    "attachment_info.h",
+    "bluetooth_advertising_interval_client.h",
+    "constants.h",
+    "fast_initiation/constants.h",
+    "fast_initiation/fast_initiation_advertiser.h",
+    "fast_initiation/fast_initiation_scanner.h",
+    "fast_initiation/fast_initiation_scanner_feature_usage_metrics.h",
+    "firewall_hole/nearby_connections_firewall_hole.h",
+    "firewall_hole/nearby_connections_firewall_hole_factory.h",
+    "incoming_frames_reader.h",
+    "incoming_share_target_info.h",
+    "instantmessaging/constants.h",
+    "instantmessaging/receive_messages_express.h",
+    "instantmessaging/send_message_express.h",
+    "instantmessaging/stream_parser.h",
+    "instantmessaging/token_fetcher.h",
+    "metrics/attachment_metric_logger.h",
+    "metrics/discovery_metric_logger.h",
+    "metrics/metric_common.h",
+    "metrics/nearby_share_metric_logger.h",
+    "metrics/throughput_metric_logger.h",
+    "nearby_confirmation_manager.h",
+    "nearby_notification_delegate.h",
+    "nearby_notification_handler.h",
+    "nearby_notification_manager.h",
+    "nearby_per_session_discovery_manager.h",
+    "nearby_receive_manager.h",
+    "nearby_share_delegate_impl.h",
+    "nearby_share_error.h",
+    "nearby_share_feature_status.h",
+    "nearby_share_feature_usage_metrics.h",
+    "nearby_share_logger.h",
+    "nearby_share_metrics.h",
+    "nearby_share_settings.h",
+    "nearby_share_transfer_profiler.h",
+    "nearby_sharing_service.h",
+    "nearby_sharing_service_factory.h",
+    "nearby_sharing_service_impl.h",
+    "network_traversal_ice_config_fetcher.h",
+    "outgoing_share_target_info.h",
+    "paired_key_verification_runner.h",
+    "payload_tracker.h",
+    "power_client.h",
+    "power_client_chromeos.h",
+    "share_target_discovered_callback.h",
+    "share_target_info.h",
+    "sharing_mojo_service.h",
+    "tachyon_ice_config_fetcher.h",
+    "tcp_socket/nearby_connections_tcp_socket_factory.h",
+    "transfer_metadata.h",
+    "transfer_metadata_builder.h",
+    "transfer_update_callback.h",
+    "webrtc_request_builder.h",
+    "webrtc_signaling_messenger.h",
+    "wifi_network_configuration/wifi_network_configuration_handler.h",
+  ]
+
+  public_deps = [
+    ":share_target",
+    "//ash/public/cpp",
+    "//base",
+    "//chrome/browser/nearby_sharing/certificates",
+    "//chrome/browser/nearby_sharing/client",
+    "//chrome/browser/nearby_sharing/instantmessaging/proto",
+    "//chrome/browser/nearby_sharing/local_device_data",
+    "//chrome/browser/notifications",
+    "//chrome/browser/profiles",
+    "//chrome/browser/sharesheet",
+    "//chrome/browser/ui/webui/nearby_share:mojom",
+    "//chrome/browser/ui/webui/nearby_share:share_type",
+    "//chrome/services/sharing/public/proto",
+    "//chromeos/ash/components/feature_usage",
+    "//chromeos/ash/components/nearby/common/connections_manager",
+    "//chromeos/ash/components/nearby/presence",
+    "//chromeos/ash/services/nearby/public/cpp",
+    "//chromeos/ash/services/nearby/public/mojom",
+    "//chromeos/ash/services/nearby/public/mojom:nearby_share_settings",
+    "//chromeos/dbus/power",
+    "//chromeos/services/network_config/public/mojom",
+    "//components/keyed_service/core",
+    "//components/prefs",
+    "//components/signin/public/identity_manager",
+    "//device/bluetooth",
+    "//google_apis",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/system",
+    "//net",
+    "//services/network/public/cpp",
+    "//services/network/public/mojom",
+    "//url",
+  ]
+}
+
+source_set("impl") {
+  sources = [
+    "attachment_info.cc",
+    "bluetooth_advertising_interval_client.cc",
+    "fast_initiation/fast_initiation_advertiser.cc",
+    "fast_initiation/fast_initiation_scanner.cc",
+    "fast_initiation/fast_initiation_scanner_feature_usage_metrics.cc",
+    "firewall_hole/nearby_connections_firewall_hole.cc",
+    "firewall_hole/nearby_connections_firewall_hole_factory.cc",
+    "incoming_frames_reader.cc",
+    "incoming_share_target_info.cc",
+    "instantmessaging/receive_messages_express.cc",
+    "instantmessaging/send_message_express.cc",
+    "instantmessaging/stream_parser.cc",
+    "instantmessaging/token_fetcher.cc",
+    "mdns/nearby_connections_mdns_manager.cc",
+    "mdns/nearby_connections_mdns_manager.h",
+    "metrics/attachment_metric_logger.cc",
+    "metrics/discovery_metric_logger.cc",
+    "metrics/metric_common.cc",
+    "metrics/nearby_share_metric_logger.cc",
+    "metrics/throughput_metric_logger.cc",
+    "nearby_confirmation_manager.cc",
+    "nearby_notification_handler.cc",
+    "nearby_notification_manager.cc",
+    "nearby_per_session_discovery_manager.cc",
+    "nearby_receive_manager.cc",
+    "nearby_share_delegate_impl.cc",
+    "nearby_share_feature_status.cc",
+    "nearby_share_feature_usage_metrics.cc",
+    "nearby_share_logger.cc",
+    "nearby_share_metrics.cc",
+    "nearby_share_settings.cc",
+    "nearby_share_transfer_profiler.cc",
+    "nearby_sharing_service_factory.cc",
+    "nearby_sharing_service_impl.cc",
+    "network_traversal_ice_config_fetcher.cc",
+    "outgoing_share_target_info.cc",
+    "paired_key_verification_runner.cc",
+    "payload_tracker.cc",
+    "power_client.cc",
+    "power_client_chromeos.cc",
+    "share_target_info.cc",
+    "sharesheet/nearby_share_action.cc",
+    "sharesheet/nearby_share_action.h",
+    "sharing_mojo_service.cc",
+    "tachyon_ice_config_fetcher.cc",
+    "tcp_socket/nearby_connections_tcp_socket_factory.cc",
+    "transfer_metadata.cc",
+    "transfer_metadata_builder.cc",
+    "webrtc_request_builder.cc",
+    "webrtc_signaling_messenger.cc",
+    "wifi_network_configuration/wifi_network_configuration_handler.cc",
+  ]
+
+  public_deps = [
+    ":nearby_sharing",
+    "//chrome/browser:browser_public_dependencies",
+  ]
+
+  # //chrome/browser/ash/nearby includes
+  # chrome/browser/nearby_sharing/mdns/nearby_connections_mdns_manager.h
+  # from :impl.
+  allow_circular_includes_from = [ "//chrome/browser/ash/nearby" ]
+
+  deps = [
+    "//ash/constants",
+    "//ash/resources/vector_icons",
+    "//ash/webui/settings/public/constants:mojom",
+    "//chrome/app:generated_resources",
+    "//chrome/app/vector_icons",
+    "//chrome/browser:browser_process",
+    "//chrome/browser/ash/file_manager",
+    "//chrome/browser/ash/nearby",
+    "//chrome/browser/download",
+    "//chrome/browser/image_decoder",
+    "//chrome/browser/nearby_sharing/certificates",
+    "//chrome/browser/nearby_sharing/client",
+    "//chrome/browser/nearby_sharing/common",
+    "//chrome/browser/nearby_sharing/contacts",
+    "//chrome/browser/nearby_sharing/instantmessaging/proto",
+    "//chrome/browser/nearby_sharing/local_device_data",
+    "//chrome/browser/nearby_sharing/logging:util",
+    "//chrome/browser/nearby_sharing/proto:tachyon_proto",
+    "//chrome/browser/signin",
+    "//chrome/browser/ui/ash/holding_space",
+    "//chrome/browser/ui/ash/session",
+    "//chrome/browser/ui/webui/nearby_share",
+    "//chrome/common",
+    "//chrome/services/sharing/public/cpp",
+    "//chromeos/ash/components/browser_context_helper",
+    "//chromeos/ash/components/nearby/common/client",
+    "//chromeos/ash/components/nearby/common/connections_manager",
+    "//chromeos/ash/components/network",
+    "//chromeos/ash/services/nearby/public/cpp",
+    "//chromeos/ash/services/nearby/public/cpp:tcp_server_socket_port",
+    "//chromeos/ash/services/network_config",
+    "//chromeos/ash/services/network_config:in_process_instance",
+    "//chromeos/components/firewall_hole",
+    "//chromeos/constants",
+    "//components/cross_device/logging",
+    "//components/cross_device/nearby",
+    "//components/drive",
+    "//components/metrics/structured",
+    "//components/metrics/structured:structured_events",
+    "//components/policy:generated",
+    "//components/policy/core/common",
+    "//components/pref_registry",
+    "//components/user_manager",
+    "//content/public/browser",
+    "//crypto",
+    "//device/bluetooth",
+    "//google_apis",
+    "//mojo/public/cpp/bindings",
+    "//net",
+    "//services/network/public/cpp",
+    "//services/network/public/mojom",
+    "//storage/browser",
+    "//third_party/icu",
+    "//third_party/nearby:rpc_resources_proto",
+    "//third_party/nearby:wire_format_proto",
+    "//third_party/protobuf:protobuf_lite",
+    "//ui/base",
+    "//ui/base/clipboard",
+    "//ui/chromeos/styles:cros_tokens_color_mappings",
+    "//ui/gfx",
+    "//ui/message_center/public/cpp",
+    "//ui/strings",
+    "//ui/views",
+    "//url",
+  ]
+
+  if (is_chrome_branded) {
+    deps += [ "//chrome/browser/nearby_sharing/internal/icons:vector_icons" ]
+  }
+}
diff --git a/chrome/browser/new_tab_page/new_tab_page_realbox_interactive_uitest.cc b/chrome/browser/new_tab_page/new_tab_page_realbox_interactive_uitest.cc
index 644f875b..beb46f2 100644
--- a/chrome/browser/new_tab_page/new_tab_page_realbox_interactive_uitest.cc
+++ b/chrome/browser/new_tab_page/new_tab_page_realbox_interactive_uitest.cc
@@ -5,6 +5,7 @@
 #include <string_view>
 
 #include "base/check_deref.h"
+#include "base/containers/extend.h"
 #include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -25,6 +26,7 @@
 #include "components/contextual_search/contextual_search_service.h"
 #include "components/contextual_search/contextual_search_session_handle.h"
 #include "components/contextual_search/mock_contextual_search_service.h"
+#include "components/contextual_tasks/public/features.h"
 #include "components/omnibox/browser/aim_eligibility_service_features.h"
 #include "components/omnibox/browser/mock_aim_eligibility_service.h"
 #include "components/prefs/pref_service.h"
@@ -89,6 +91,10 @@
 const DeepQuery kRealbox = {"ntp-app", "ntp-searchbox", "#inputWrapper"};
 const DeepQuery kRealboxInput = {"ntp-app", "ntp-searchbox", "#input",
                                  "#input"};
+const DeepQuery kVoiceSearchButton = {"ntp-app", "ntp-searchbox",
+                                      "#voiceSearchButton"};
+const DeepQuery kLensSearchButton = {"ntp-app", "ntp-searchbox",
+                                     "#lensSearchButton"};
 const DeepQuery kComposeButton = {"ntp-app", "ntp-searchbox", "#composeButton",
                                   "#composeButton"};
 const DeepQuery kComposeboxVoiceSearchButton = {"ntp-app", "cr-composebox",
@@ -296,39 +302,31 @@
   }
 
   auto WaitForDialogStateChange(const DeepQuery& where, bool expected_open) {
-    DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kEvent);
-    WebContentsInteractionTestUtil::StateChange state_change;
-    state_change.event = kEvent;
-    state_change.where = where;
-    state_change.test_function =
-        expected_open ? "(el) => el && el.open" : "(el) => el && !el.open";
-    return WaitForStateChange(kNtpElementId, state_change);
+    return WaitForJsConditionAt(
+        kNtpElementId, where,
+        expected_open ? "(el) => el && el.open" : "(el) => el && !el.open");
   }
 
   auto WaitForElementVisibilityChange(const DeepQuery& where,
                                       bool expected_visible) {
-    DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kEvent);
-    WebContentsInteractionTestUtil::StateChange state_change;
-    state_change.event = kEvent;
-    state_change.where = where;
-    state_change.test_function =
+    return WaitForJsConditionAt(
+        kNtpElementId, where,
         expected_visible ? "(el) => el && !el.hasAttribute('hidden')"
-                         : "(el) => el && el.hasAttribute('hidden')";
-    return WaitForStateChange(kNtpElementId, state_change);
+                         : "(el) => el && el.hasAttribute('hidden')");
   }
 
   auto WaitForSubmitEnabled() {
-    DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kSubmitEnabledEvent);
-    WebContentsInteractionTestUtil::StateChange submit_enabled;
-    submit_enabled.event = kSubmitEnabledEvent;
-    submit_enabled.where = kComposeboxSubmitButton;
-    submit_enabled.test_function =
+    return WaitForJsConditionAt(
+        kNtpElementId, kComposeboxSubmitButton,
         "(el) => el && el.querySelector('#submitIcon') && "
-        "!el.querySelector('#submitIcon').hasAttribute('disabled')";
-    return WaitForStateChange(kNtpElementId, submit_enabled);
+        "!el.querySelector('#submitIcon').hasAttribute('disabled')");
   }
 
  protected:
+  static std::vector<base::test::FeatureRef> GetDisabledFeatures() {
+    return {contextual_tasks::kContextualTasks};
+  }
+
   static std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures(
       bool compose_button_enabled = true) {
     std::vector<base::test::FeatureRefAndParams> enabled_features;
@@ -389,6 +387,7 @@
       disabled_features.push_back(ntp_composebox::kNtpComposebox);
     }
 
+    base::Extend(disabled_features, GetDisabledFeatures());
     feature_list_.InitWithFeaturesAndParameters(enabled_features,
                                                 disabled_features);
 
@@ -485,7 +484,8 @@
 class NtpRealboxInteractiveTest : public NtpRealboxUiTestBase {
  public:
   NtpRealboxInteractiveTest() {
-    feature_list_.InitWithFeaturesAndParameters(GetEnabledFeatures(), {});
+    feature_list_.InitWithFeaturesAndParameters(GetEnabledFeatures(),
+                                                GetDisabledFeatures());
   }
 
  private:
@@ -520,13 +520,7 @@
 #if BUILDFLAG(IS_CHROMEOS)
   // TODO(crbug.com/496928186): Re-enable after de-flaking.
   GTEST_SKIP() << "Flaky on ChromeOS";
-#endif
-  DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kMultilineInputEvent);
-  WebContentsInteractionTestUtil::StateChange multiline_input;
-  multiline_input.event = kMultilineInputEvent;
-  multiline_input.where = kRealboxInput;
-  multiline_input.test_function = "(el) => el && el.value === 'a\\nb'";
-
+#else
   RunTestSequence(
       // Load NTP.
       AddInstrumentedTab(kNtpElementId, GURL(chrome::kChromeUINewTabURL)),
@@ -541,7 +535,9 @@
       // Type 'b' into Realbox input.
       SendKeyPress(kNtpElementId, ui::VKEY_B),
       // Wait for Realbox input to have a newline between 'a' and 'b'.
-      WaitForStateChange(kNtpElementId, multiline_input));
+      WaitForJsConditionAt(kNtpElementId, kRealboxInput,
+                           "(el) => el && el.value === 'a\\nb'"));
+#endif
 }
 
 IN_PROC_BROWSER_TEST_F(NtpRealboxInteractiveTest,
@@ -654,7 +650,8 @@
           NtpRealboxUploadInteractiveTestParams> {
  public:
   NtpRealboxUploadInteractiveTest() {
-    feature_list_.InitWithFeaturesAndParameters(GetEnabledFeatures(), {});
+    feature_list_.InitWithFeaturesAndParameters(GetEnabledFeatures(),
+                                                GetDisabledFeatures());
   }
 
  private:
@@ -799,7 +796,8 @@
       public testing::WithParamInterface<NtpRealboxToolInteractiveTestParams> {
  public:
   NtpRealboxToolInteractiveTest() {
-    feature_list_.InitWithFeaturesAndParameters(GetEnabledFeatures(), {});
+    feature_list_.InitWithFeaturesAndParameters(GetEnabledFeatures(),
+                                                GetDisabledFeatures());
   }
 
  private:
@@ -860,7 +858,8 @@
       public testing::WithParamInterface<ComposeboxSearchParam> {
  public:
   NtpComposeboxSearchFulfillmentTest() {
-    feature_list_.InitWithFeaturesAndParameters(GetEnabledFeatures(), {});
+    feature_list_.InitWithFeaturesAndParameters(GetEnabledFeatures(),
+                                                GetDisabledFeatures());
   }
 
  private:
@@ -924,19 +923,15 @@
                                  public testing::WithParamInterface<bool> {
  public:
   NtpComposeboxDismissTest() {
-    feature_list_.InitWithFeaturesAndParameters(GetEnabledFeatures(), {});
+    feature_list_.InitWithFeaturesAndParameters(GetEnabledFeatures(),
+                                                GetDisabledFeatures());
   }
 
   bool CloseViaEscButton() const { return GetParam(); }
 
   auto WaitForComposeboxInputCleared() {
-    DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kComposeboxInputClearedEvent);
-    WebContentsInteractionTestUtil::StateChange composebox_input_cleared;
-    composebox_input_cleared.event = kComposeboxInputClearedEvent;
-    composebox_input_cleared.where = kComposeboxInput;
-    composebox_input_cleared.test_function = "(el) => el && el.value === ''";
-
-    return WaitForStateChange(kNtpElementId, composebox_input_cleared);
+    return WaitForJsConditionAt(kNtpElementId, kComposeboxInput,
+                                "(el) => el && el.value === ''");
   }
 
   auto WaitForComposeboxDialogClosed() {
@@ -958,7 +953,6 @@
 
 IN_PROC_BROWSER_TEST_P(NtpComposeboxDismissTest,
                        ClearsInputAndClosesComposebox) {
-
   auto TriggerDismissAction = [this]() {
     if (CloseViaEscButton()) {
       return Steps(SendKeyPress(kNtpElementId, ui::VKEY_ESCAPE));
@@ -1014,7 +1008,8 @@
         enabled_features.push_back(feature_ref_and_params);
       }
     }
-    feature_list_.InitWithFeaturesAndParameters(enabled_features, {});
+    feature_list_.InitWithFeaturesAndParameters(enabled_features,
+                                                GetDisabledFeatures());
   }
 
  private:
@@ -1023,20 +1018,14 @@
 
 IN_PROC_BROWSER_TEST_F(NtpRealboxCyclingPlaceholderInteractiveTest,
                        PlaceholderCycles) {
-  DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kPlaceholderCyclingEvent);
-  WebContentsInteractionTestUtil::StateChange placeholder_cycling;
-  placeholder_cycling.event = kPlaceholderCyclingEvent;
-  placeholder_cycling.where = kRealboxInput;
-  placeholder_cycling.test_function =
-      "(el) => el && el.getAnimations().length > 0";
-
   RunTestSequence(
       // Load NTP.
       AddInstrumentedTab(kNtpElementId, GURL(chrome::kChromeUINewTabURL)),
       // Wait for Realbox to render.
       WaitForElementToRender(kNtpElementId, kRealboxInput),
       // Wait and verify if placeholder text cycles.
-      WaitForStateChange(kNtpElementId, placeholder_cycling));
+      WaitForJsConditionAt(kNtpElementId, kRealboxInput,
+                           "(el) => el && el.getAnimations().length > 0"));
 }
 
 IN_PROC_BROWSER_TEST_F(NtpRealboxInteractiveTest,
@@ -1074,3 +1063,47 @@
       WaitForElementVisibilityChange(kSearchboxDropdown,
                                      /*expected_visible=*/false));
 }
+
+class NtpRealboxDefaultExperienceInteractiveTest : public NtpRealboxUiTestBase {
+ public:
+  NtpRealboxDefaultExperienceInteractiveTest() {
+    std::vector<base::test::FeatureRef> disabled_features =
+        GetDisabledFeatures();
+    for (const auto& feature_ref_and_params : GetEnabledFeatures()) {
+      disabled_features.push_back(*feature_ref_and_params.feature);
+    }
+    disabled_features.push_back(ntp_features::kNtpNextFeatures);
+
+    feature_list_.InitWithFeaturesAndParameters({}, disabled_features);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(NtpRealboxDefaultExperienceInteractiveTest,
+                       DefaultExperienceRealboxUI) {
+  RunTestSequence(
+      // Load NTP.
+      AddInstrumentedTab(kNtpElementId, GURL(chrome::kChromeUINewTabURL)),
+      // Wait for Realbox to render.
+      WaitForElementToRender(kNtpElementId, kRealbox),
+      // Wait for Voice Search, Lens, and AI Mode buttons to render.
+      WaitForElementToRender(kNtpElementId, kVoiceSearchButton),
+      WaitForElementToRender(kNtpElementId, kLensSearchButton),
+      WaitForElementToRender(kNtpElementId, kComposeButton),
+      // Verify the placeholder text is steady.
+      WaitForJsConditionAt(kNtpElementId, kRealboxInput,
+                           "(el) => el && el.getAnimations().length === 0"),
+      // Click into the Searchbox.
+      ClickElement(kNtpElementId, kRealboxInput),
+      // Verify that the placeholder text disappears.
+      WaitForJsConditionAt(kNtpElementId, kRealboxInput,
+                           "(el) => el && window.getComputedStyle(el, "
+                           "'::placeholder').visibility === 'hidden'"),
+      // Type text into Realbox and click AIM Button.
+      SendKeyPress(kNtpElementId, ui::VKEY_T),
+      ClickElement(kNtpElementId, kComposeButton),
+      // Wait for the page to navigate to Google SRP.
+      WaitForGoogleSearch(kNtpElementId, {{"q", "t"}, {"udm", "50"}}));
+}
diff --git a/chrome/browser/on_device_translation/installer_impl.cc b/chrome/browser/on_device_translation/installer_impl.cc
index 59d6c13c..874a28d 100644
--- a/chrome/browser/on_device_translation/installer_impl.cc
+++ b/chrome/browser/on_device_translation/installer_impl.cc
@@ -198,7 +198,7 @@
     auto manager = std::make_unique<
         optimization_guide::OnDeviceModelDownloadProgressManager>(
         g_browser_process->component_updater(), component_ids,
-        /*enable_unloadable_progress=*/false);
+        /*enable_unloadable_progress*/ false);
 
     auto tracker = std::make_unique<ProgressTracker>(
         language_pack,
diff --git a/chrome/browser/password_manager/password_field_classification_model_handler_factory.cc b/chrome/browser/password_manager/password_field_classification_model_handler_factory.cc
index 1bb31842..c95ec9da 100644
--- a/chrome/browser/password_manager/password_field_classification_model_handler_factory.cc
+++ b/chrome/browser/password_manager/password_field_classification_model_handler_factory.cc
@@ -9,6 +9,7 @@
 #include "base/no_destructor.h"
 #include "chrome/browser/autofill/ml_log_router_factory.h"
 #include "chrome/browser/glic/glic_pref_names.h"
+#include "chrome/browser/glic/public/glic_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
 #include "chrome/browser/password_manager/chrome_password_change_service.h"
@@ -83,10 +84,8 @@
 
 #if !BUILDFLAG(IS_ANDROID)
   // Special case for ActorLogin which uses a model in a very limited scope.
-  if (profile->GetPrefs()->HasPrefPath(
-          glic::prefs::kGlicUserEnabledActuationOnWeb) &&
-      profile->GetPrefs()->GetBoolean(
-          glic::prefs::kGlicUserEnabledActuationOnWeb) &&
+  auto* glic_service = glic::GlicKeyedService::Get(profile);
+  if (glic_service && glic_service->enabling().GetUserEnabledActuationOnWeb() &&
       base::FeatureList::IsEnabled(
           password_manager::features::kActorLoginLocalClassificationModel)) {
     return context;
diff --git a/chrome/browser/password_manager/password_user_education_interactive_uitest.cc b/chrome/browser/password_manager/password_user_education_interactive_uitest.cc
index 743e5827..051f681 100644
--- a/chrome/browser/password_manager/password_user_education_interactive_uitest.cc
+++ b/chrome/browser/password_manager/password_user_education_interactive_uitest.cc
@@ -18,20 +18,14 @@
 
 }  // namespace
 
-class PasswordUserEducationUiTest : public InteractiveFeaturePromoTest,
-                                    public testing::WithParamInterface<bool> {
+class PasswordUserEducationUiTest : public InteractiveFeaturePromoTest {
  public:
   PasswordUserEducationUiTest()
       : InteractiveFeaturePromoTest(UseDefaultTrackerAllowingPromos(
             {feature_engagement::kIPHPasswordsSavePrimingPromoFeature})) {
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(
-        features::kPageActionsMigration,
-        {{features::kPageActionsMigrationManagePasswords.name,
-          IsMigrationEnabled() ? "true" : "false"}});
+    scoped_feature_list_.InitAndEnableFeature(features::kPageActionsMigration);
   }
 
-  bool IsMigrationEnabled() const { return GetParam(); }
-
   ~PasswordUserEducationUiTest() override = default;
 
   void SetUpOnMainThread() override {
@@ -44,12 +38,10 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_P(PasswordUserEducationUiTest, TriggerPromo) {
+IN_PROC_BROWSER_TEST_F(PasswordUserEducationUiTest, TriggerPromo) {
   GURL base_url = embedded_https_test_server().GetURL("a.com", kPageWithLogin);
   RunTestSequence(
       InstrumentTab(kTabElementId),
       NavigateWebContents(kTabElementId, base_url),
       WaitForPromo(feature_engagement::kIPHPasswordsSavePrimingPromoFeature));
 }
-
-INSTANTIATE_TEST_SUITE_P(All, PasswordUserEducationUiTest, ::testing::Bool());
diff --git a/chrome/browser/prefs/BUILD.gn b/chrome/browser/prefs/BUILD.gn
index 02c24ad6..5b00352e 100644
--- a/chrome/browser/prefs/BUILD.gn
+++ b/chrome/browser/prefs/BUILD.gn
@@ -89,6 +89,8 @@
     "//chrome/browser/profiles",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/profiles:profile_util",
+    "//chrome/browser/push_messaging",
+    "//chrome/browser/push_messaging:service_impl_public",
     "//chrome/browser/search",
     "//chrome/browser/search/background",
     "//chrome/browser/serial",
diff --git a/chrome/browser/profiles/BUILD.gn b/chrome/browser/profiles/BUILD.gn
index 18f0f61..668ea01 100644
--- a/chrome/browser/profiles/BUILD.gn
+++ b/chrome/browser/profiles/BUILD.gn
@@ -448,6 +448,7 @@
     "//chrome/browser/privacy",
     "//chrome/browser/privacy_sandbox:headers",
     "//chrome/browser/privacy_sandbox/notice:factory",
+    "//chrome/browser/push_messaging",
     "//chrome/browser/reading_list",
     "//chrome/browser/reduce_accept_language",
     "//chrome/browser/regional_capabilities",
@@ -585,6 +586,7 @@
       "//chrome/browser/chromeos/enterprise/cloud_storage",
       "//chrome/browser/chromeos/extensions/telemetry/api:keyed_service_factory",
       "//chrome/browser/chromeos/policy/dlp",
+      "//chrome/browser/nearby_sharing",
       "//chrome/browser/policy:onc",
       "//chrome/browser/push_notification",
       "//chrome/browser/smart_card",
diff --git a/chrome/browser/push_messaging/BUILD.gn b/chrome/browser/push_messaging/BUILD.gn
index ce953a1..7c873dc 100644
--- a/chrome/browser/push_messaging/BUILD.gn
+++ b/chrome/browser/push_messaging/BUILD.gn
@@ -2,8 +2,179 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//extensions/buildflags/buildflags.gni")
 import("//third_party/protobuf/proto_library.gni")
 
+source_set("push_messaging") {
+  sources = [
+    "budget_database.h",
+    "push_messaging_app_identifier.h",
+    "push_messaging_notification_manager.h",
+    "push_messaging_refresher.h",
+    "push_messaging_service_factory.h",
+    "push_messaging_unsubscribed_entry.h",
+  ]
+
+  public_deps = [
+    ":budget_proto",
+    "//base",
+    "//build:chromeos_buildflags",
+    "//chrome/browser/profiles:profile",
+    "//chrome/common:constants",
+    "//components/content_settings/core/browser",
+    "//components/gcm_driver",
+    "//components/gcm_driver/common",
+    "//components/gcm_driver/crypto",
+    "//components/gcm_driver/instance_id",
+    "//components/keyed_service/core",
+    "//components/leveldb_proto",
+    "//components/pref_registry",
+    "//components/prefs",
+    "//components/push_messaging",
+    "//components/site_engagement/content",
+    "//content/public/browser",
+    "//content/public/common",
+    "//url",
+  ]
+}
+
+# Header-only target for push_messaging_service_impl.h, separated from :impl so
+# that external targets like chrome/browser/prefs:impl can depend on it without
+# pulling in targets that would create a dependency cycle.
+source_set("service_impl_public") {
+  sources = [ "push_messaging_service_impl.h" ]
+  public_deps = [
+    ":push_messaging",
+    "//chrome/browser/permissions",
+    "//chrome/browser/safe_browsing",
+    "//chrome/common:buildflags",
+    "//components/safe_browsing/core/browser/db:database_manager",
+  ]
+
+  # safe_browsing includes push_messaging_service_impl.h.
+  # This is needed until safe_browsing is modularized.
+  allow_circular_includes_from = [ "//chrome/browser/safe_browsing" ]
+}
+
+source_set("impl") {
+  sources = [
+    "budget_database.cc",
+    "push_messaging_app_identifier.cc",
+    "push_messaging_notification_manager.cc",
+    "push_messaging_refresher.cc",
+    "push_messaging_service_factory.cc",
+    "push_messaging_service_impl.cc",
+    "push_messaging_unsubscribed_entry.cc",
+  ]
+
+  public_deps = [
+    ":service_impl_public",
+    "//chrome/browser:browser_public_dependencies",
+  ]
+
+  deps = [
+    ":push_messaging",
+    "//chrome/app:generated_resources",
+    "//chrome/browser:browser_process",
+    "//chrome/browser/content_settings:content_settings_factory",
+    "//chrome/browser/engagement",
+    "//chrome/browser/gcm",
+    "//chrome/browser/lifetime:termination_notification",
+    "//chrome/browser/notifications",
+    "//chrome/browser/profiles/keep_alive",
+    "//chrome/browser/ui/browser_window",
+    "//chrome/common:channel_info",
+    "//chrome/common:chrome_features",
+    "//components/permissions",
+    "//components/safe_browsing/core/common:safe_browsing_prefs",
+    "//components/url_formatter",
+    "//extensions/buildflags",
+    "//net",
+    "//ui/base",
+    "//ui/message_center",
+  ]
+
+  if (!is_android) {
+    deps += [
+      "//chrome/browser/ui",
+      "//chrome/browser/ui:browser_list",
+      "//chrome/browser/ui/tabs:tab_strip",
+      "//components/keep_alive_registry",
+    ]
+  }
+
+  if (is_android) {
+    deps += [
+      "//chrome/android:chrome_jni_headers",
+      "//chrome/browser/profiles",
+      "//chrome/browser/ui/android/tab_model",
+    ]
+  }
+
+  if (enable_extensions) {
+    deps += [
+      "//extensions/browser",
+      "//extensions/common",
+    ]
+  }
+}
+
+if (!is_android) {
+  source_set("browser_tests") {
+    testonly = true
+    sources = [ "push_messaging_browsertest.cc" ]
+    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+    deps = [
+      ":budget_proto",
+      ":impl",
+      ":push_messaging",
+      "//base/test:test_support",
+      "//chrome/browser/browsing_data:constants",
+      "//chrome/browser/content_settings:content_settings_factory",
+      "//chrome/browser/gcm",
+      "//chrome/browser/permissions",
+      "//chrome/browser/ui",
+      "//chrome/test:test_support",
+      "//components/browsing_data/content",
+      "//components/push_messaging:test_support",
+      "//content/test:test_support",
+      "//testing/gtest",
+    ]
+  }
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "budget_database_unittest.cc",
+    "push_messaging_app_identifier_unittest.cc",
+    "push_messaging_notification_manager_unittest.cc",
+    "push_messaging_refresher_unittest.cc",
+    "push_messaging_service_unittest.cc",
+    "push_messaging_unsubscribed_entry_unittest.cc",
+  ]
+  deps = [
+    ":budget_proto",
+    ":impl",
+    ":push_messaging",
+    "//base",
+    "//base/test:test_support",
+    "//chrome/browser:global_features",
+    "//chrome/browser/content_settings:content_settings_factory",
+    "//chrome/browser/gcm",
+    "//chrome/browser/history",
+    "//chrome/browser/permissions",
+    "//chrome/test:test_support",
+    "//components/gcm_driver:test_support",
+    "//components/leveldb_proto:test_support",
+    "//components/push_messaging:test_support",
+    "//components/site_engagement/content",
+    "//content/test:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
+
 proto_library("budget_proto") {
   sources = [ "budget.proto" ]
 }
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContent.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContent.java
index a4fa2f0..5d1f41d 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContent.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContent.java
@@ -624,9 +624,9 @@
     }
 
     @Override
-    public boolean canSuppressInAnyState() {
+    public boolean canBeSuppressed(BottomSheetContent nextContent) {
         // Always immediately hide if a higher-priority sheet content wants to show.
-        return true;
+        return nextContent.getPriority() == ContentPriority.HIGH;
     }
 
     private void setOnClickListener(int id, Runnable onClick) {
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java
index ab78e5f..3774063e 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/ExpandedPlayerSheetContentUnitTest.java
@@ -7,8 +7,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.Activity;
 import android.content.Context;
@@ -41,6 +43,7 @@
 import org.chromium.chrome.browser.readaloud.player.R;
 import org.chromium.chrome.modules.readaloud.PlaybackArgs.PlaybackMode;
 import org.chromium.chrome.modules.readaloud.PlaybackListener;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -301,8 +304,10 @@
     }
 
     @Test
-    public void testCanSuppressInAnyState() {
-        assertTrue(mContent.canSuppressInAnyState());
+    public void testCanBeSuppressed() {
+        BottomSheetContent newContent = mock(BottomSheetContent.class);
+        when(newContent.getPriority()).thenReturn(BottomSheetContent.ContentPriority.HIGH);
+        assertTrue(mContent.canBeSuppressed(newContent));
     }
 
     @Test
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuSheetContent.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuSheetContent.java
index 895ba1b..a8f997c 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuSheetContent.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuSheetContent.java
@@ -141,8 +141,8 @@
     }
 
     @Override
-    public boolean canSuppressInAnyState() {
+    public boolean canBeSuppressed(BottomSheetContent nextContent) {
         // Always immediately hide if a higher-priority sheet content wants to show.
-        return true;
+        return nextContent.getPriority() == ContentPriority.HIGH;
     }
 }
diff --git a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuSheetContentUnitTest.java b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuSheetContentUnitTest.java
index a22a511a..12c088d 100644
--- a/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuSheetContentUnitTest.java
+++ b/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/player/expanded/MenuSheetContentUnitTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -165,7 +166,9 @@
     }
 
     @Test
-    public void testCanSuppressInAnyState() {
-        assertTrue(mContent.canSuppressInAnyState());
+    public void testCanBeSuppressed() {
+        BottomSheetContent newContent = mock(BottomSheetContent.class);
+        when(newContent.getPriority()).thenReturn(BottomSheetContent.ContentPriority.HIGH);
+        assertTrue(mContent.canBeSuppressed(newContent));
     }
 }
diff --git a/chrome/browser/resources/ai_overlay_dialog/tools/generate_tool_definitions.py b/chrome/browser/resources/ai_overlay_dialog/tools/generate_tool_definitions.py
index b0a9ccd..753c31c2 100644
--- a/chrome/browser/resources/ai_overlay_dialog/tools/generate_tool_definitions.py
+++ b/chrome/browser/resources/ai_overlay_dialog/tools/generate_tool_definitions.py
@@ -17,21 +17,56 @@
     return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()
 
 
-def _ParseComments(comments_str):
+def _ToParagraphs(lines_array):
+    paragraphs = []
+    current_para = []
+    for line in lines_array:
+        if not line.strip():
+            if current_para:
+                paragraphs.append(" ".join(l.strip() for l in current_para))
+                current_para = []
+        else:
+            current_para.append(line)
+    if current_para:
+        paragraphs.append(" ".join(l.strip() for l in current_para))
+    return "\n\n".join(paragraphs)
+
+
+def _ParseComments(comments_str, param_names):
     lines = []
     for line in comments_str.splitlines():
         line = line.strip()
-        # Remove leading slashes and whitespace
-        line = re.sub(r'^//\s*', '', line)
-        if line:
-            lines.append(line)
-    return " ".join(lines)
+        # Remove leading slashes and at most one whitespace
+        line = re.sub(r'^//\s?', '', line)
+        lines.append(line)
+
+    main_desc_lines = []
+    param_lines = {}
+    current_param = None
+
+    for line in lines:
+        m = re.match(r'^(\w+):\s*(.*)', line)
+        if m and m.group(1) in param_names:
+            current_param = m.group(1)
+            param_lines[current_param] = [m.group(2)]
+        elif current_param is not None:
+            param_lines[current_param].append(line)
+        else:
+            main_desc_lines.append(line)
+
+    main_desc = _ToParagraphs(main_desc_lines)
+    param_descs = {p: _ToParagraphs(lines) for p, lines in param_lines.items()}
+
+    return main_desc, param_descs
 
 
 def _MojomTypeToGeminiType(mojom_type):
-    if 'string' in mojom_type: return 'STRING'
-    if 'bool' in mojom_type: return 'BOOLEAN'
-    if 'int' in mojom_type: return 'INTEGER'
+    if 'string' in mojom_type:
+        return 'STRING'
+    if 'bool' in mojom_type:
+        return 'BOOLEAN'
+    if 'int' in mojom_type:
+        return 'INTEGER'
     if 'double' in mojom_type or 'float' in mojom_type: return 'NUMBER'
     return 'STRING'
 
@@ -48,15 +83,18 @@
 
     for m in pattern.finditer(text):
         comments, name, args_str = m.groups()
-        description = _ParseComments(comments)
-
-        decl = {
-            "name": _CamelToSnake(name),
-            "description": description,
-        }
 
         # Parse mojom arguments
         args = [arg.strip() for arg in args_str.split(',') if arg.strip()]
+        param_names = [arg.split()[-1] for arg in args] if args else []
+
+        main_desc, param_descs = _ParseComments(comments, param_names)
+
+        decl = {
+            "name": _CamelToSnake(name),
+            "description": main_desc,
+        }
+
         if len(args) > 0:
             decl["parameters"] = {
                 "type": "OBJECT",
@@ -69,9 +107,11 @@
                 param_name = parts[-1]
                 mojom_type = " ".join(parts[:-1])
 
-                decl["parameters"]["properties"][param_name] = {
-                    "type": _MojomTypeToGeminiType(mojom_type)
-                }
+                param_schema = {"type": _MojomTypeToGeminiType(mojom_type)}
+                if param_name in param_descs:
+                    param_schema["description"] = param_descs[param_name]
+
+                decl["parameters"]["properties"][param_name] = param_schema
                 decl["parameters"]["required"].append(param_name)
 
             if not decl["parameters"]["required"]:
diff --git a/chrome/browser/resources/media_router/internals/media_router_internals.css b/chrome/browser/resources/media_router/internals/media_router_internals.css
index ee5a2e2..4c3626f0 100644
--- a/chrome/browser/resources/media_router/internals/media_router_internals.css
+++ b/chrome/browser/resources/media_router/internals/media_router_internals.css
@@ -3,20 +3,32 @@
  * found in the LICENSE file. */
 
 body {
-  font-size: large;
+  margin: 0;
+  padding: 20px;
+}
+
+h1 {
+  font-size: 20px;
+  font-weight: 500;
+  margin-bottom: 20px;
+  margin-top: 0;
 }
 
 .status {
   font-family: monospace;
+  font-size: 14px;
   white-space: pre-wrap;
 }
 
-.log-message {
-  width: 50%;
-}
-
 cr-tab-box {
   width: inherit;
+  --tabs-background-color: white;
+}
+
+div[slot='tab'] {
+  border-radius: 8px 8px 0 0;
+  font-size: 15px;
+  padding: 10px 16px;
 }
 
 div[slot='panel'] {
@@ -26,29 +38,135 @@
 
 table {
   border-collapse: collapse;
-  table-layout: fixed;
   width: 100%;
   font-size: 14px;
 }
 
-td,
 th {
-  border: 1px solid gray;
+  background-color: var(--google-grey-100, #f1f3f4);
+  color: var(--google-grey-700, #5f6368);
+  font-weight: 500;
   text-align: left;
-  padding: 8px;
-  word-wrap: break-word;
+  padding: 10px 8px;
+  border-bottom: 2px solid var(--google-grey-300, #dadce0);
+  white-space: nowrap;
 }
 
-tr:nth-child(even) {
-  background-color: lightgray;
+td {
+  padding: 8px;
+  border-bottom: 1px solid var(--google-grey-200, #e8eaed);
+  color: var(--google-grey-900, #202124);
+  word-break: break-word;
+}
+
+tr:hover {
+  background-color: var(--google-grey-50, #f8f9fa);
+}
+
+tr.log-row-warning:hover,
+.log-row-warning {
+  background-color: var(--google-yellow-100, #fef7e0);
+  color: var(--google-yellow-900, #e37400);
+}
+
+tr.log-row-error:hover,
+.log-row-error {
+  background-color: var(--google-red-100, #fad2cf);
+  color: var(--google-red-900, #b31412);
+  font-weight: bold;
 }
 
 #button-container {
+  display: flex;
+  gap: 8px;
+  margin-bottom: 20px;
+}
+
+cr-button {
+  font-size: 11px;
+}
+
+/* Fixes a wrapping annoyance ("Discovery" is wrapped to "Discover" and "y"). */
+th:nth-child(6),
+td:nth-child(6) {
+  min-width: 85px;
+}
+
+.full-width-section {
+  margin-bottom: 20px;
+}
+
+#dashboard-container {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  align-items: flex-start;
+  gap: 20px;
+  margin-bottom: 20px;
+}
+
+#dashboard-container section {
+  flex: 1 1 250px;
+  min-width: 250px;
+}
+
+.dashboard-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
+  gap: 15px;
+}
+
+.stat-card {
+  background-color: var(--google-grey-50, #f8f9fa);
+  border: 1px solid var(--google-grey-300, #dadce0);
+  border-radius: 8px;
+  padding: 15px;
+  display: flex;
+  flex-direction: column;
+  box-shadow: 0 1px 2px 0 rgba(60, 64, 67, .30),
+      0 1px 3px 1px rgba(60, 64, 67, .15);
+}
+
+.stat-label {
+  color: var(--google-grey-700, #5f6368);
+  font-size: 12px;
+  margin-bottom: 5px;
+  text-transform: uppercase;
+}
+
+.stat-value {
+  font-size: 24px;
+  font-weight: 500;
+  color: var(--google-grey-900, #202124);
   margin-bottom: 10px;
 }
 
-button {
-  font-size: 14px;
-  margin-inline-end: 10px;
-  padding: 5px 10px;
+.stat-card-alert {
+  background-color: var(--google-red-50, #fce8e6);
+  border-color: var(--google-red-300, #f28b82);
+}
+
+.sparkline {
+  height: 40px;
+  width: 100%;
+  stroke: var(--google-blue-500, #1a73e8);
+  stroke-width: 2;
+  fill: none;
+}
+
+.sparkline-alert {
+  stroke: var(--google-red-500, #ea4335);
+}
+
+cr-expand-button {
+  margin-top: 10px;
+  --cr-section-padding: 0;
+}
+
+#mirroring-stats-div {
+  margin-top: 10px;
+  padding: 10px;
+  background-color: var(--google-grey-100, #f1f3f4);
+  border-radius: 4px;
+  overflow: auto;
 }
diff --git a/chrome/browser/resources/media_router/internals/media_router_internals.html b/chrome/browser/resources/media_router/internals/media_router_internals.html
index 074ffeb..9bc3379 100644
--- a/chrome/browser/resources/media_router/internals/media_router_internals.html
+++ b/chrome/browser/resources/media_router/internals/media_router_internals.html
@@ -11,16 +11,18 @@
 <body>
   <h1>Media Router Internals</h1>
   <div id="button-container">
-    <button id="download-session">Download Session</button>
-    <button id="import-session">Import Session</button>
-    <button id="clear-session">Clear Session</button>
+    <cr-button id="download-session">Download Session</cr-button>
+    <cr-button id="import-session">Import Session</cr-button>
+    <cr-button id="clear-session">Clear Session</cr-button>
+    <cr-button id="toggle-tracing">Start Trace</cr-button>
     <input type="file" id="import-input" accept=".json,application/json"
       style="display: none">
   </div>
   <cr-tab-box>
-    <div slot="tab">Sink status</div>
-    <div slot="tab">Cast media route provider</div>
-    <div slot="tab">Mirroring stats</div>
+    <div slot="tab">Sink Status</div>
+    <div slot="tab">Cast Media Route Provider</div>
+    <div slot="tab">Mirroring Dashboard</div>
+    <div slot="tab">Mirroring Stats</div>
     <div slot="tab">Logs</div>
     <div id="sink-status-slot" slot="panel">
       <div id="sink-status-div" class="status"></div>
@@ -28,6 +30,22 @@
     <div id="cast-media-route-provider-slot" slot="panel">
       <div id="cast-status-div" class="status"></div>
     </div>
+    <div id="mirroring-dashboard-slot" slot="panel">
+      <section id="session-stats-section" class="full-width-section">
+        <h3>Session</h3>
+        <div id="session-stats-grid" class="dashboard-grid"></div>
+      </section>
+      <div id="dashboard-container">
+        <section id="video-stats-section">
+          <h3>Video</h3>
+          <div id="video-stats-grid" class="dashboard-grid"></div>
+        </section>
+        <section id="audio-stats-section">
+          <h3>Audio</h3>
+          <div id="audio-stats-grid" class="dashboard-grid"></div>
+        </section>
+      </div>
+    </div>
     <div id="mirroring-stats-slot" slot="panel">
       <div id="mirroring-stats-div" class="status"></div>
     </div>
@@ -36,7 +54,7 @@
         <thead>
           <tr>
             <th>Time</th>
-            <th class="log-message">Message</th>
+            <th>Message</th>
             <th>SessionID</th>
             <th>SinkID</th>
             <th>MediaSource</th>
@@ -51,4 +69,4 @@
   </cr-tab-box>
 </body>
 
-</html>
\ No newline at end of file
+</html>
diff --git a/chrome/browser/resources/media_router/internals/media_router_internals.ts b/chrome/browser/resources/media_router/internals/media_router_internals.ts
index f37d372..0b7d0a9e 100644
--- a/chrome/browser/resources/media_router/internals/media_router_internals.ts
+++ b/chrome/browser/resources/media_router/internals/media_router_internals.ts
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 import 'chrome://resources/cr_elements/cr_tab_box/cr_tab_box.js';
+import 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.js';
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
 
 import {assertInstanceof} from 'chrome://resources/js/assert.js';
 import {addWebUiListener, sendWithPromise} from 'chrome://resources/js/cr.js';
@@ -19,23 +21,450 @@
   message: string;
 }
 
+interface MirroringStats {
+  video?: {[key: string]: number};
+  audio?: {[key: string]: number};
+}
+
 function formatJson(jsonObj: object) {
   return JSON.stringify(jsonObj, null, /* spacing level = */ 2);
 }
 
+const HISTORY_SIZE = 50;
+
+interface MirroringStatDef {
+  key: string;
+  label: string;
+  unit: string;
+  isCumulative?: boolean;
+  hideGraph?: boolean;
+}
+
+const VIDEO_STATS: MirroringStatDef[] = [
+  // Health
+  {key: 'ENCODER_UTILIZATION', label: 'Encoder Utilization', unit: '%'},
+  {key: 'LOSSINESS', label: 'Lossiness', unit: '%'},
+  {
+    key: 'FRAMES_INSERTED',
+    label: 'Frames Inserted',
+    unit: 'fps',
+    isCumulative: true,
+  },
+  {
+    key: 'FRAMES_DROPPED',
+    label: 'Frames Dropped',
+    unit: 'fps',
+    isCumulative: true,
+  },
+  {
+    key: 'NUM_FRAMES_DROPPED_BY_ENCODER',
+    label: 'Dropped by Encoder',
+    unit: 'fps',
+    isCumulative: true,
+  },
+  {
+    key: 'NUM_FRAMES_LATE',
+    label: 'Late Frames',
+    unit: 'fps',
+    isCumulative: true,
+  },
+
+  // Bandwidth
+  {key: 'TARGET_BITRATE', label: 'Target Bitrate', unit: 'kbps'},
+  {key: 'ENCODE_KBPS', label: 'Encode', unit: 'kbps'},
+  {key: 'TRANSMISSION_KBPS', label: 'Transmission', unit: 'kbps'},
+
+  // Latency
+  {key: 'AVG_E2E_LATENCY_MS', label: 'End to End Latency', unit: 'ms'},
+  {key: 'AVG_CAPTURE_LATENCY_MS', label: 'Capture Latency', unit: 'ms'},
+  {key: 'AVG_ENCODE_TIME_MS', label: 'Encode Time', unit: 'ms'},
+  {key: 'AVG_QUEUEING_LATENCY_MS', label: 'Queueing Latency', unit: 'ms'},
+  {key: 'AVG_NETWORK_LATENCY_MS', label: 'Network Latency', unit: 'ms'},
+  {key: 'AVG_PACKET_LATENCY_MS', label: 'Packet Latency', unit: 'ms'},
+];
+
+const AUDIO_STATS: MirroringStatDef[] = [
+  // Health
+  {
+    key: 'FRAMES_INSERTED',
+    label: 'Frames Inserted',
+    unit: 'fps',
+    isCumulative: true,
+  },
+  {
+    key: 'FRAMES_DROPPED',
+    label: 'Frames Dropped',
+    unit: 'fps',
+    isCumulative: true,
+  },
+  {
+    key: 'NUM_FRAMES_LATE',
+    label: 'Late Frames',
+    unit: 'fps',
+    isCumulative: true,
+  },
+
+  // Bandwidth
+  {key: 'TRANSMISSION_KBPS', label: 'Transmission', unit: 'kbps'},
+
+  // Latency
+  {key: 'AVG_E2E_LATENCY_MS', label: 'End to End Latency', unit: 'ms'},
+  {key: 'AVG_CAPTURE_LATENCY_MS', label: 'Capture Latency', unit: 'ms'},
+  {key: 'AVG_ENCODE_TIME_MS', label: 'Encode Time', unit: 'ms'},
+  {key: 'AVG_QUEUEING_LATENCY_MS', label: 'Queueing Latency', unit: 'ms'},
+  {key: 'AVG_NETWORK_LATENCY_MS', label: 'Network Latency', unit: 'ms'},
+  {key: 'AVG_PACKET_LATENCY_MS', label: 'Packet Latency', unit: 'ms'},
+];
+
+const SESSION_STATS: MirroringStatDef[] = [
+  {
+    key: 'SESSION_LENGTH_MS',
+    label: 'Session Length',
+    unit: 's',
+    hideGraph: true,
+  },
+];
+
+interface TimeDataPoint {
+  time: number;
+  value: number;
+}
+
+interface CompactStats {
+  startTime: number;
+  columns: string[];
+  data: Array<Array<number|null>>;
+}
+
+class StatsHistory {
+  private history: Map<string, TimeDataPoint[]> = new Map();
+  private lastCumulativeValues: Map<string, number> = new Map();
+  private lastCumulativeTimes: Map<string, number> = new Map();
+
+  add(key: string, value: number, time: number = Date.now(),
+      isCumulative: boolean = false) {
+    let deltaValue = value;
+    if (isCumulative) {
+      const lastVal = this.lastCumulativeValues.get(key);
+      const lastTime = this.lastCumulativeTimes.get(key);
+      this.lastCumulativeValues.set(key, value);
+      this.lastCumulativeTimes.set(key, time);
+      if (lastVal !== undefined && lastTime !== undefined && time > lastTime) {
+        const delta = Math.max(0, value - lastVal);
+        deltaValue = delta * 1000 / (time - lastTime);
+      } else {
+        deltaValue = 0;
+      }
+    }
+
+    if (!this.history.has(key)) {
+      this.history.set(key, []);
+    }
+    const data = this.history.get(key)!;
+    data.push({time, value: deltaValue});
+    if (data.length > HISTORY_SIZE) {
+      data.shift();
+    }
+  }
+
+  get(key: string): TimeDataPoint[] {
+    return this.history.get(key) || [];
+  }
+
+  getAll(): CompactStats {
+    const keys = Array.from(this.history.keys());
+    if (keys.length === 0) {
+      return {startTime: 0, columns: ['timeOffset'], data: []};
+    }
+
+    const allTimesSet = new Set<number>();
+    for (const points of this.history.values()) {
+      for (const p of points) {
+        allTimesSet.add(p.time);
+      }
+    }
+    const allTimes = Array.from(allTimesSet).sort((a, b) => a - b);
+    if (allTimes.length === 0) {
+      return {startTime: 0, columns: ['timeOffset'], data: []};
+    }
+
+    const startTime = allTimes[0]!;
+    const columns = ['timeOffset', ...keys];
+
+    const data: Array<Array<number|null>> = [];
+
+    const timeToValues = new Map<number, Map<string, number>>();
+    for (const time of allTimes) {
+      timeToValues.set(time, new Map<string, number>());
+    }
+
+    for (const [key, points] of this.history.entries()) {
+      for (const p of points) {
+        timeToValues.get(p.time)!.set(key, p.value);
+      }
+    }
+
+    for (const time of allTimes) {
+      const row: Array<number|null> = [time - startTime];
+      const vals = timeToValues.get(time)!;
+      for (const key of keys) {
+        row.push(vals.has(key) ? vals.get(key)! : null);
+      }
+      data.push(row);
+    }
+
+    return {startTime, columns, data};
+  }
+
+  setAll(compact: any) {
+    this.history.clear();
+    this.lastCumulativeValues.clear();
+
+    if (!compact) {
+      return;
+    }
+
+    // Fallback for old format
+    if (!('columns' in compact)) {
+      this.history = new Map(Object.entries(compact));
+      return;
+    }
+
+    if (!compact.columns || compact.columns.length === 0) {
+      return;
+    }
+
+    const keys = compact.columns.slice(1);
+    for (const key of keys) {
+      this.history.set(key, []);
+    }
+
+    for (const row of compact.data) {
+      const timeOffset = row[0] as number;
+      const time = compact.startTime + timeOffset;
+
+      for (let i = 0; i < keys.length; i++) {
+        const key = keys[i]!;
+        const val = row[i + 1];
+        if (val !== null) {
+          this.history.get(key)!.push({time, value: val});
+        }
+      }
+    }
+  }
+
+  clear() {
+    this.history.clear();
+    this.lastCumulativeValues.clear();
+  }
+}
+
+const videoHistory = new StatsHistory();
+const audioHistory = new StatsHistory();
+const sessionHistory = new StatsHistory();
+
+function generateSparklinePath(
+    data: TimeDataPoint[], width: number, height: number): string {
+  if (data.length < 2) {
+    return '';
+  }
+  const minVal = Math.min(...data.map(d => d.value));
+  const maxVal = Math.max(...data.map(d => d.value));
+  const range = maxVal - minVal || 1;
+  const padding = 2;
+  const usableHeight = height - (padding * 2);
+
+  const minTime = data[0]!.time;
+  const maxTime = data[data.length - 1]!.time;
+  const timeRange = maxTime - minTime || 1;
+
+  const points = data.map((d) => {
+    const x = ((d.time - minTime) / timeRange) * width;
+    const y = height - padding - ((d.value - minVal) / range) * usableHeight;
+    return `${x.toFixed(1)},${y.toFixed(1)}`;
+  });
+
+  return `M ${points.join(' L ')}`;
+}
+
+interface StatCardElements {
+  card: HTMLElement;
+  value: HTMLElement;
+  path?: SVGPathElement;
+}
+
+const statCardCache: Map<string, StatCardElements> = new Map();
+
+function createStatCard(
+    container: HTMLElement, stat: MirroringStatDef, history: StatsHistory,
+    prefix: string) {
+  const cacheKey = `${prefix}-${stat.key}`;
+  let cached = statCardCache.get(cacheKey);
+  if (!cached) {
+    const card = document.createElement('div');
+    card.className = 'stat-card';
+    card.setAttribute('data-key', stat.key);
+
+    const label = document.createElement('div');
+    label.className = 'stat-label';
+    label.textContent = stat.label;
+    card.appendChild(label);
+
+    const value = document.createElement('div');
+    value.className = 'stat-value';
+    value.textContent = '--';
+    card.appendChild(value);
+
+    let path: SVGPathElement|undefined;
+    if (!stat.hideGraph) {
+      const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+      svg.setAttribute('class', 'sparkline');
+      svg.setAttribute('viewBox', '0 0 200 40');
+      svg.setAttribute('preserveAspectRatio', 'none');
+      svg.setAttribute('aria-hidden', 'true');
+
+      path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
+      svg.appendChild(path);
+      card.appendChild(svg);
+    }
+
+    container.appendChild(card);
+    cached = {card, value, path};
+    statCardCache.set(cacheKey, cached);
+  }
+
+  const values = history.get(stat.key);
+  if (values.length > 0) {
+    const lastValue = values[values.length - 1]!.value;
+    const valueText = `${lastValue.toFixed(1)} ${stat.unit}`;
+    cached.value.textContent = valueText;
+    cached.card.setAttribute('aria-label', `${stat.label}: ${valueText}`);
+
+    if (cached.path) {
+      cached.path.setAttribute('d', generateSparklinePath(values, 200, 40));
+    }
+
+    if (stat.isCumulative) {
+      if (lastValue > 0 &&
+          (stat.key.includes('DROPPED') || stat.key.includes('LATE'))) {
+        cached.card.classList.add('stat-card-alert');
+        if (cached.path) {
+          cached.path.classList.add('sparkline-alert');
+        }
+      } else {
+        cached.card.classList.remove('stat-card-alert');
+        if (cached.path) {
+          cached.path.classList.remove('sparkline-alert');
+        }
+      }
+    }
+  }
+}
+
+function renderDashboard(
+    mirroringStats: MirroringStats, isImport: boolean = false) {
+  const timestamp = Date.now();
+  if (mirroringStats.video) {
+    const videoStats = mirroringStats.video;
+    VIDEO_STATS.forEach(stat => {
+      let value = videoStats[stat.key];
+      if (!isImport && typeof value !== 'number' && stat.isCumulative) {
+        // Only fallback to 0 if the history is completely empty so we start at
+        // 0
+        const hist = videoHistory.get(stat.key);
+        if (hist.length === 0) {
+          value = 0;
+        }
+      }
+      if (!isImport && typeof value === 'number') {
+        videoHistory.add(stat.key, value, timestamp, stat.isCumulative);
+      }
+      const grid = getRequiredElement('video-stats-grid');
+      createStatCard(grid, stat, videoHistory, 'video');
+    });
+  }
+
+  if (mirroringStats.audio) {
+    const audioStats = mirroringStats.audio;
+    AUDIO_STATS.forEach(stat => {
+      let value = audioStats[stat.key];
+      if (!isImport && typeof value !== 'number' && stat.isCumulative) {
+        const hist = audioHistory.get(stat.key);
+        if (hist.length === 0) {
+          value = 0;
+        }
+      }
+      if (!isImport && typeof value === 'number') {
+        audioHistory.add(stat.key, value, timestamp, stat.isCumulative);
+      }
+      const grid = getRequiredElement('audio-stats-grid');
+      createStatCard(grid, stat, audioHistory, 'audio');
+    });
+  }
+
+  // Derived session stats
+  const grid = getRequiredElement('session-stats-grid');
+  SESSION_STATS.forEach(stat => {
+    if (!isImport && stat.key === 'SESSION_LENGTH_MS') {
+      // Calculate session length based on the first recorded event in the
+      // pipeline.
+      const videoFirst = mirroringStats.video?.['FIRST_EVENT_TIME_MS'];
+      const audioFirst = mirroringStats.audio?.['FIRST_EVENT_TIME_MS'];
+      const videoLast = mirroringStats.video?.['LAST_EVENT_TIME_MS'];
+      const audioLast = mirroringStats.audio?.['LAST_EVENT_TIME_MS'];
+
+      const startMs = Math.min(videoFirst || Infinity, audioFirst || Infinity);
+      const endMs = Math.max(videoLast || 0, audioLast || 0);
+
+      if (startMs !== Infinity && endMs !== 0 && endMs >= startMs) {
+        // We receive the times in ticks MS.
+        const lengthSeconds = (endMs - startMs) / 1000;
+        sessionHistory.add(stat.key, lengthSeconds, timestamp, false);
+      }
+    }
+    createStatCard(grid, stat, sessionHistory, 'session');
+  });
+}
+
 // Holds data that is currently displayed.
 let currentLogs: MediaRouterLog[] = [];
 let currentGeneralState: object = {};
-let currentMirroringStats: object = {};
+let currentMirroringStats: MirroringStats = {};
 let currentProviderStates: {[key: string]: object} = {};
 
-function displayMirroringStats(mirroringStats: object) {
+function displayMirroringStats(
+    mirroringStats: MirroringStats, isImport: boolean = false) {
   currentMirroringStats = mirroringStats;
   getRequiredElement('mirroring-stats-div').textContent =
       formatJson(mirroringStats);
+  renderDashboard(mirroringStats, isImport);
 }
 
-// Build the table which displays Media Router logs.
+function createLogRow(log: MediaRouterLog): HTMLTableRowElement {
+  const logRow = document.createElement('tr');
+  if (log.severity === 'Error') {
+    logRow.classList.add('log-row-error');
+  } else if (log.severity === 'Warning') {
+    logRow.classList.add('log-row-warning');
+  }
+  const fieldNames: Array<keyof MediaRouterLog> = [
+    'time',
+    'message',
+    'sessionId',
+    'sinkId',
+    'mediaSource',
+    'category',
+    'component',
+    'severity',
+  ];
+  fieldNames.forEach(fieldName => {
+    const element = document.createElement('td');
+    element.textContent = log[fieldName];
+    logRow.appendChild(element);
+  });
+  return logRow;
+}
+
 // Build the table which displays Media Router logs.
 function displayLogsTable(logs: MediaRouterLog[]) {
   currentLogs = logs;
@@ -46,17 +475,7 @@
   logsTbody.replaceChildren();
 
   for (const log of logs) {
-    const logRow = document.createElement('tr');
-    const logDict = log as unknown as {[key: string]: string};
-    ['time', 'message', 'sessionId', 'sinkId', 'mediaSource', 'category',
-     'component', 'severity']
-        .forEach(fieldName => {
-          const element = document.createElement('td');
-          element.textContent = logDict[fieldName] ?? '';
-          logRow.appendChild(element);
-        });
-
-    logsTbody.appendChild(logRow);
+    logsTbody.appendChild(createLogRow(log));
   }
 }
 
@@ -64,8 +483,13 @@
   const aggregatedData = {
     logs: currentLogs,
     generalState: currentGeneralState,
-    mirroringStats: currentMirroringStats,
+    sessionStatistics: {
+      video: videoHistory.getAll(),
+      audio: audioHistory.getAll(),
+      session: sessionHistory.getAll(),
+    },
     providerStates: currentProviderStates,
+    mirroringStats: currentMirroringStats,
   };
   const blob = new Blob([JSON.stringify(aggregatedData, null, 2)], {
     type: 'application/json',
@@ -111,8 +535,20 @@
                 formatJson(currentProviderStates['CAST']);
           }
         }
+        if (data.sessionStatistics) {
+          if (data.sessionStatistics.video) {
+            videoHistory.setAll(data.sessionStatistics.video);
+          }
+          if (data.sessionStatistics.audio) {
+            audioHistory.setAll(data.sessionStatistics.audio);
+          }
+          if (data.sessionStatistics.session) {
+            sessionHistory.setAll(data.sessionStatistics.session);
+          }
+          renderDashboard({video: {}, audio: {}}, true);
+        }
         if (data.mirroringStats) {
-          displayMirroringStats(data.mirroringStats);
+          displayMirroringStats(data.mirroringStats, true);
         }
         if (data.logs) {
           displayLogsTable(data.logs);
@@ -136,11 +572,73 @@
   currentGeneralState = {};
   currentMirroringStats = {};
   currentProviderStates = {};
+  videoHistory.clear();
+  audioHistory.clear();
+  sessionHistory.clear();
+  statCardCache.clear();
 
   getRequiredElement('logs-tbody').replaceChildren();
   getRequiredElement('sink-status-div').textContent = '';
   getRequiredElement('cast-status-div').textContent = '';
   getRequiredElement('mirroring-stats-div').textContent = '';
+  getRequiredElement('video-stats-grid').replaceChildren();
+  getRequiredElement('audio-stats-grid').replaceChildren();
+  getRequiredElement('session-stats-grid').replaceChildren();
+}
+
+let isTracing = false;
+let traceChunks: Uint8Array[] = [];
+
+function handleToggleTracing() {
+  const button = getRequiredElement<HTMLButtonElement>('toggle-tracing');
+  if (!isTracing) {
+    button.textContent = 'Starting...';
+    button.disabled = true;
+    traceChunks = [];  // clear any old chunks
+    sendWithPromise<boolean>('startTracing').then((started: boolean) => {
+      if (started) {
+        isTracing = true;
+        button.textContent = 'Stop Trace';
+        button.disabled = false;
+        button.classList.add('action-button');  // Highlight the active state
+      } else {
+        button.textContent = 'Trace Failed';
+        setTimeout(() => {
+          button.textContent = 'Start Trace';
+          button.disabled = false;
+        }, 2000);
+      }
+    });
+  } else {
+    button.textContent = 'Stopping...';
+    button.disabled = true;
+    sendWithPromise<boolean>('stopTracing').then((success: boolean) => {
+      isTracing = false;
+      button.textContent = 'Start Trace';
+      button.disabled = false;
+      button.classList.remove('action-button');
+
+      if (success && traceChunks.length > 0) {
+        const blob = new Blob(
+            traceChunks as BlobPart[], {type: 'application/octet-stream'});
+        traceChunks = [];  // clear memory after creating blob
+        const url = URL.createObjectURL(blob);
+        const a = document.createElement('a');
+        a.href = url;
+        a.download =
+            `media_router_internals_trace_${new Date().toISOString()}.pftrace`;
+        document.body.appendChild(a);
+        a.click();
+
+        setTimeout(() => {
+          URL.revokeObjectURL(url);
+          a.remove();
+        }, 2000);
+      } else {
+        traceChunks = [];
+      }
+    });
+  }
 }
 
 // Handles user events for the Media Router Internals UI.
@@ -154,6 +652,8 @@
   getRequiredElement('import-input')
       .addEventListener('change', handleImportSession);
   getRequiredElement('clear-session').addEventListener('click', clearSession);
+  getRequiredElement('toggle-tracing')
+      .addEventListener('click', handleToggleTracing);
 
   // Initial fetch
   sendWithPromise<object>('getState').then((status: object) => {
@@ -174,15 +674,15 @@
   addWebUiListener('on-log-added', (log: MediaRouterLog) => {
     currentLogs.push(log);
     const logsTbody = getRequiredElement('logs-tbody');
-    const logRow = document.createElement('tr');
-    const logDict = log as unknown as {[key: string]: string};
-    ['time', 'message', 'sessionId', 'sinkId', 'mediaSource', 'category',
-     'component', 'severity']
-        .forEach(fieldName => {
-          const element = document.createElement('td');
-          element.textContent = logDict[fieldName] ?? '';
-          logRow.appendChild(element);
-        });
-    logsTbody.appendChild(logRow);
+    logsTbody.appendChild(createLogRow(log));
+  });
+
+  addWebUiListener('on-trace-chunk', (base64Chunk: string) => {
+    const binaryString = window.atob(base64Chunk);
+    const bytes = new Uint8Array(binaryString.length);
+    for (let i = 0; i < binaryString.length; i++) {
+      bytes[i] = binaryString.charCodeAt(i);
+    }
+    traceChunks.push(bytes);
   });
 });
diff --git a/chrome/browser/resources/new_tab_page/ntp_searchbox.ts b/chrome/browser/resources/new_tab_page/ntp_searchbox.ts
index 337449b2..ce64dde 100644
--- a/chrome/browser/resources/new_tab_page/ntp_searchbox.ts
+++ b/chrome/browser/resources/new_tab_page/ntp_searchbox.ts
@@ -87,6 +87,22 @@
         type: String,
       },
 
+      colorSourceIsBaseline: {
+        type: Boolean,
+        reflect: true,
+      },
+
+      /** Whether the theme is dark. */
+      isDark: {
+        type: Boolean,
+        reflect: true,
+      },
+
+      searchboxLayoutMode: {
+        type: String,
+        reflect: true,
+      },
+
       //========================================================================
       // Protected properties
       //========================================================================
@@ -103,6 +119,9 @@
   accessor contextMenuGlifAnimationState: GlifAnimationState =
       GlifAnimationState.INELIGIBLE;
   accessor animationState: GlowAnimationState = GlowAnimationState.NONE;
+  accessor colorSourceIsBaseline: boolean = false;
+  accessor isDark: boolean = false;
+  accessor searchboxLayoutMode: string = '';
   protected accessor tabSuggestions_: TabInfo[] = [];
   protected accessor inputState_: InputState|null = null;
   protected dragAndDropHandler: DragAndDropHandler|null = null;
@@ -384,6 +403,10 @@
         !this.isInputEmpty());
   }
 
+  protected useCompactLayout_(): boolean {
+    return this.searchboxLayoutMode === 'Compact';
+  }
+
   protected override openComposebox_(
       uploads: ContextualUpload[] = [], mode: ToolMode = ToolMode.kUnspecified,
       model: ModelMode = ModelMode.kUnspecified) {
diff --git a/chrome/browser/resources/settings/glic_page/glic_browser_proxy.ts b/chrome/browser/resources/settings/glic_page/glic_browser_proxy.ts
index 85f0b76..4be1b0e 100644
--- a/chrome/browser/resources/settings/glic_page/glic_browser_proxy.ts
+++ b/chrome/browser/resources/settings/glic_page/glic_browser_proxy.ts
@@ -34,6 +34,8 @@
   getGlicSelectionShortcut(): Promise<string>;
   setGlicSelectionShortcut(shortcut: string): Promise<void>;
   getWebActuationToggleVisibility(): Promise<boolean>;
+  getWebActuationEnabled(): Promise<boolean>;
+  setWebActuationEnabled(enabled: boolean): void;
 }
 
 export class GlicBrowserProxyImpl implements GlicBrowserProxy {
@@ -85,6 +87,14 @@
     return sendWithPromise<boolean>('getWebActuationToggleVisibility');
   }
 
+  getWebActuationEnabled() {
+    return sendWithPromise<boolean>('getWebActuationEnabled');
+  }
+
+  setWebActuationEnabled(enabled: boolean) {
+    chrome.send('setWebActuationEnabled', [enabled]);
+  }
+
   static getInstance(): GlicBrowserProxy {
     return instance || (instance = new GlicBrowserProxyImpl());
   }
diff --git a/chrome/browser/resources/settings/glic_page/glic_subpage.html b/chrome/browser/resources/settings/glic_page/glic_subpage.html
index 6756d2c..a3dc365 100644
--- a/chrome/browser/resources/settings/glic_page/glic_subpage.html
+++ b/chrome/browser/resources/settings/glic_page/glic_subpage.html
@@ -386,7 +386,8 @@
                   if="[[!isWebActuationDisabledForEnterprise_]]" restamp>
           <settings-toggle-button
               id="webActuationToggle"
-              pref="{{prefs.glic.user_enabled_actuation_on_web}}"
+              pref="{{webActuationEnabledPref_}}"
+              no-set-pref
               label="$i18n{glicWebActuationToggle}"
               sub-label-with-link="[[webActuationSubLabel_]]"
               on-sub-label-link-clicked="onWebActuationToggleLearnMoreClick_"
diff --git a/chrome/browser/resources/settings/glic_page/glic_subpage.ts b/chrome/browser/resources/settings/glic_page/glic_subpage.ts
index a02b3a3..cd0ffaa 100644
--- a/chrome/browser/resources/settings/glic_page/glic_subpage.ts
+++ b/chrome/browser/resources/settings/glic_page/glic_subpage.ts
@@ -240,6 +240,16 @@
         value: false,
       },
 
+      webActuationEnabledPref_: {
+        type: Object,
+        value() {
+          return {
+            type: chrome.settingsPrivate.PrefType.BOOLEAN,
+            value: false,
+          };
+        },
+      },
+
       isWebActuationDisabledForEnterprise_: {
         type: Boolean,
         value: () => {
@@ -293,10 +303,7 @@
           `prefs.${
               SettingsGlicPageFeaturePrefName
                   .DEFAULT_TAB_CONTEXT_ENABLED}.value)`,
-      'onWebActuationEnabledChanged_(' +
-          `prefs.${
-              SettingsGlicPageFeaturePrefName.WEB_ACTUATION_ENABLED}.value)`,
-
+      'onWebActuationEnabledChanged_(webActuationEnabledPref_.value)',
     ];
   }
 
@@ -336,6 +343,8 @@
   declare private webActuationSubLabel_: string;
   declare private webActuationLearnMoreUrl_: string;
   declare private webActuationFeatureEnabled_: boolean;
+  declare private webActuationEnabledPref_:
+      chrome.settingsPrivate.PrefObject<boolean>;
   declare private isWebActuationDisabledForEnterprise_: boolean;
   declare private webActuationDisabledForEnterprisePref_:
       chrome.settingsPrivate.PrefObject<boolean>;
@@ -357,12 +366,20 @@
         'glic-web-actuation-toggle-visibility-changed',
         (visible: boolean) =>
             this.onWebActuationToggleVisibilityChanged_(visible));
+    this.addWebUiListener(
+        'glic-web-actuation-enabled-changed', (enabled: boolean) => {
+          this.set('webActuationEnabledPref_.value', enabled);
+        });
 
     this.browserProxy_.getWebActuationToggleVisibility().then(
         (visible: boolean) => {
             this.onWebActuationToggleVisibilityChanged_(visible);
         });
 
+    this.browserProxy_.getWebActuationEnabled().then((enabled: boolean) => {
+      this.set('webActuationEnabledPref_.value', enabled);
+    });
+
     this.registeredShortcut_ = await this.browserProxy_.getGlicShortcut();
     this.registeredFocusToggleShortcut_ =
         await this.browserProxy_.getGlicFocusToggleShortcut();
@@ -670,6 +687,8 @@
   private onWebActuationToggleChange_(event: CustomEvent) {
     const target = event.target as SettingsToggleButtonElement;
     const enabled = target.checked;
+    this.browserProxy_.setWebActuationEnabled(enabled);
+    this.set('webActuationEnabledPref_.value', enabled);
     this.metricsBrowserProxy_.recordAction(
         'Glic.Settings.WebActuation' + (enabled ? '.Enabled' : '.Disabled'));
   }
diff --git a/chrome/browser/resources/tab_search/tab_group_shared_vars.css b/chrome/browser/resources/tab_search/tab_group_shared_vars.css
index 2d44f2d..8ecfcf64 100644
--- a/chrome/browser/resources/tab_search/tab_group_shared_vars.css
+++ b/chrome/browser/resources/tab_search/tab_group_shared_vars.css
@@ -54,6 +54,35 @@
   --tab-group-color-purple: var(--google-purple-600);
   --tab-group-color-cyan: var(--google-cyan-900);
   --tab-group-color-orange: var(--google-orange-400);
+
+  --tab-group-refresh-color-grey-rgb: 73, 77, 82; /* #494d52 */
+  --tab-group-refresh-color-blue-rgb: 50, 92, 205; /* #325ccd */
+  --tab-group-refresh-color-red-rgb: 223, 0, 12; /* #df000c */
+  --tab-group-refresh-color-pink-rgb: 204, 6, 171; /* #cc06ab */
+  --tab-group-refresh-color-green-rgb: 26, 119, 54; /* #1a7736 */
+  --tab-group-refresh-color-yellow-rgb: 99, 117, 5; /* #637505 */
+  --tab-group-refresh-color-purple-rgb: 124, 49, 230; /* #7c31e6 */
+  --tab-group-refresh-color-cyan-rgb: 5, 124, 132; /* #057c84 */
+  --tab-group-refresh-color-orange-rgb: 204, 78, 0; /* #cc4e00 */
+
+  --tab-group-refresh-color-grey: rgb(
+    var(--tab-group-refresh-color-grey-rgb));
+  --tab-group-refresh-color-blue: rgb(
+    var(--tab-group-refresh-color-blue-rgb));
+  --tab-group-refresh-color-red: rgb(
+    var(--tab-group-refresh-color-red-rgb));
+  --tab-group-refresh-color-pink: rgb(
+    var(--tab-group-refresh-color-pink-rgb));
+  --tab-group-refresh-color-green: rgb(
+    var(--tab-group-refresh-color-green-rgb));
+  --tab-group-refresh-color-yellow: rgb(
+    var(--tab-group-refresh-color-yellow-rgb));
+  --tab-group-refresh-color-purple: rgb(
+    var(--tab-group-refresh-color-purple-rgb));
+  --tab-group-refresh-color-cyan: rgb(
+    var(--tab-group-refresh-color-cyan-rgb));
+  --tab-group-refresh-color-orange: rgb(
+    var(--tab-group-refresh-color-orange-rgb));
 }
 
 @media (prefers-color-scheme: dark) {
@@ -67,5 +96,34 @@
     --tab-group-color-purple: var(--google-purple-200);
     --tab-group-color-cyan: var(--google-cyan-300);
     --tab-group-color-orange: var(--google-orange-300);
+
+    --tab-group-refresh-color-grey-rgb: 208, 213, 221; /* #d0d5dd */
+    --tab-group-refresh-color-blue-rgb: 200, 211, 255; /* #c8d3ff */
+    --tab-group-refresh-color-red-rgb: 255, 199, 193; /* #ffc7c1 */
+    --tab-group-refresh-color-pink-rgb: 255, 195, 232; /* #ffc3e8 */
+    --tab-group-refresh-color-green-rgb: 198, 255, 199; /* #c6ffc7 */
+    --tab-group-refresh-color-yellow-rgb: 238, 255, 165; /* #eeffa5 */
+    --tab-group-refresh-color-purple-rgb: 224, 203, 255; /* #e0cbff */
+    --tab-group-refresh-color-cyan-rgb: 199, 250, 255; /* #c7faff */
+    --tab-group-refresh-color-orange-rgb: 255, 222, 167; /* #ffdea7 */
+
+    --tab-group-refresh-color-grey: rgb(
+      var(--tab-group-refresh-color-grey-rgb));
+    --tab-group-refresh-color-blue: rgb(
+      var(--tab-group-refresh-color-blue-rgb));
+    --tab-group-refresh-color-red: rgb(
+      var(--tab-group-refresh-color-red-rgb));
+    --tab-group-refresh-color-pink: rgb(
+      var(--tab-group-refresh-color-pink-rgb));
+    --tab-group-refresh-color-green: rgb(
+      var(--tab-group-refresh-color-green-rgb));
+    --tab-group-refresh-color-yellow: rgb(
+      var(--tab-group-refresh-color-yellow-rgb));
+    --tab-group-refresh-color-purple: rgb(
+      var(--tab-group-refresh-color-purple-rgb));
+    --tab-group-refresh-color-cyan: rgb(
+      var(--tab-group-refresh-color-cyan-rgb));
+    --tab-group-refresh-color-orange: rgb(
+      var(--tab-group-refresh-color-orange-rgb));
   }
 }
diff --git a/chrome/browser/resources/tab_search/tab_search_item.ts b/chrome/browser/resources/tab_search/tab_search_item.ts
index 927a0501..e0b7000 100644
--- a/chrome/browser/resources/tab_search/tab_search_item.ts
+++ b/chrome/browser/resources/tab_search/tab_search_item.ts
@@ -70,6 +70,7 @@
     return {
       data: {type: Object},
       buttonRipples_: {type: Boolean},
+      tabGroupColorRefresh_: {type: Boolean},
       hideTimestamp: {type: Boolean},
       hideUrl: {type: Boolean},
       hideCloseButton: {type: Boolean},
@@ -100,6 +101,8 @@
       TabItemType.OPEN_TAB, '');
   protected accessor buttonRipples_: boolean =
       loadTimeData.getBoolean('useRipples');
+  protected accessor tabGroupColorRefresh_: boolean =
+      loadTimeData.getBoolean('useTabGroupColorRefresh');
   accessor hideTimestamp: boolean = false;
   accessor size: TabSearchItemSize = TabSearchItemSize.MEDIUM;
   accessor hideUrl: boolean = false;
@@ -115,7 +118,9 @@
       if (this.data.tabGroup) {
         this.style.setProperty(
             '--group-dot-color',
-            `var(--tab-group-color-${colorName(this.data.tabGroup.color)})`);
+            this.tabGroupColorRefresh_ ?
+                `var(--tab-group-refresh-color-${colorName(this.data.tabGroup.color)})` :
+                `var(--tab-group-color-${colorName(this.data.tabGroup.color)})`);
       }
 
       if (changedProperties.has('size')) {
diff --git a/chrome/browser/safe_browsing/BUILD.gn b/chrome/browser/safe_browsing/BUILD.gn
index ac0b7fe..66f63a49 100644
--- a/chrome/browser/safe_browsing/BUILD.gn
+++ b/chrome/browser/safe_browsing/BUILD.gn
@@ -41,6 +41,7 @@
     "//chrome/browser/profiles:profile",
     "//chrome/browser/profiles:profile_io_data",
     "//chrome/browser/profiles:profile_manager",
+    "//chrome/browser/push_messaging",
     "//chrome/browser/signin",
     "//chrome/browser/site_protection:utils",
     "//chrome/browser/sync",
diff --git a/chrome/browser/ssl/chrome_security_blocking_page_factory.cc b/chrome/browser/ssl/chrome_security_blocking_page_factory.cc
index 179162d..acaba74 100644
--- a/chrome/browser/ssl/chrome_security_blocking_page_factory.cc
+++ b/chrome/browser/ssl/chrome_security_blocking_page_factory.cc
@@ -169,6 +169,38 @@
   return page;
 }
 
+std::unique_ptr<LocalSelfSignedBlockingPage>
+ChromeSecurityBlockingPageFactory::CreateLocalSelfSignedBlockingPage(
+    content::WebContents* web_contents,
+    net::Error cert_error,
+    const net::SSLInfo& ssl_info,
+    const GURL& request_url,
+    int options_mask,
+    const base::Time& time_triggered,
+    const GURL& support_url) {
+  bool overridable = SSLBlockingPage::IsOverridable(options_mask);
+  std::unique_ptr<ContentMetricsHelper> metrics_helper(
+      CreateMetricsHelperAndStartRecording(
+          web_contents, request_url,
+          overridable ? "ssl_overridable" : "ssl_nonoverridable", overridable));
+
+  LogSafeBrowsingSecuritySensitiveAction(
+      safe_browsing::SafeBrowsingMetricsCollectorFactory::GetForProfile(
+          Profile::FromBrowserContext(web_contents->GetBrowserContext())));
+
+  auto controller_client = std::make_unique<SSLErrorControllerClient>(
+      web_contents, ssl_info, cert_error, request_url,
+      std::move(metrics_helper), CreateSettingsPageHelper());
+
+  auto page = std::make_unique<LocalSelfSignedBlockingPage>(
+      web_contents, cert_error, ssl_info, request_url, options_mask,
+      time_triggered, support_url, overridable,
+      /*can_show_enhanced_protection_message=*/true,
+      std::move(controller_client));
+
+  return page;
+}
+
 std::unique_ptr<CaptivePortalBlockingPage>
 ChromeSecurityBlockingPageFactory::CreateCaptivePortalBlockingPage(
     content::WebContents* web_contents,
diff --git a/chrome/browser/ssl/chrome_security_blocking_page_factory.h b/chrome/browser/ssl/chrome_security_blocking_page_factory.h
index 757755c..dd06982 100644
--- a/chrome/browser/ssl/chrome_security_blocking_page_factory.h
+++ b/chrome/browser/ssl/chrome_security_blocking_page_factory.h
@@ -11,6 +11,7 @@
 #include "components/security_interstitials/content/captive_portal_blocking_page.h"
 #include "components/security_interstitials/content/https_only_mode_blocking_page.h"
 #include "components/security_interstitials/content/insecure_form_blocking_page.h"
+#include "components/security_interstitials/content/local_self_signed_blocking_page.h"
 #include "components/security_interstitials/content/mitm_software_blocking_page.h"
 #include "components/security_interstitials/content/security_blocking_page_factory.h"
 #include "components/security_interstitials/content/ssl_blocking_page.h"
@@ -40,6 +41,14 @@
       int options_mask,
       const base::Time& time_triggered,
       const GURL& support_url) override;
+  std::unique_ptr<LocalSelfSignedBlockingPage>
+  CreateLocalSelfSignedBlockingPage(content::WebContents* web_contents,
+                                    net::Error cert_error,
+                                    const net::SSLInfo& ssl_info,
+                                    const GURL& request_url,
+                                    int options_mask,
+                                    const base::Time& time_triggered,
+                                    const GURL& support_url) override;
   std::unique_ptr<CaptivePortalBlockingPage> CreateCaptivePortalBlockingPage(
       content::WebContents* web_contents,
       const GURL& request_url,
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 27ffc2f..cf88362 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1087,8 +1087,6 @@
         "android/extensions/extensions_menu_delegate_android.h",
         "android/extensions/extensions_toolbar_android.cc",
         "android/extensions/extensions_toolbar_android.h",
-        "extensions/icon_with_badge_image_source.cc",
-        "extensions/icon_with_badge_image_source.h",
       ]
 
       deps += [
@@ -2417,12 +2415,10 @@
       "//chrome/browser/chromeos/upload_office_to_cloud",
       "//chrome/browser/enterprise/connectors/device_trust/attestation/ash",
       "//chrome/browser/media/router/discovery/access_code:access_code_cast_feature",
-      "//chrome/browser/nearby_sharing:share_target",
-      "//chrome/browser/nearby_sharing/certificates",
-      "//chrome/browser/nearby_sharing/client",
+      "//chrome/browser/nearby_sharing",
+      "//chrome/browser/nearby_sharing:impl",
       "//chrome/browser/nearby_sharing/common",
       "//chrome/browser/nearby_sharing/contacts",
-      "//chrome/browser/nearby_sharing/local_device_data",
       "//chrome/browser/nearby_sharing/logging:util",
       "//chrome/browser/policy:onc",
       "//chrome/browser/policy:system_features_disable_list",
@@ -2830,6 +2826,7 @@
       "//chrome/browser/ash/mahi",
       "//chrome/browser/ash/mahi/media_app",
       "//chrome/browser/ash/nearby",
+      "//chrome/browser/nearby_sharing:impl",
       "//chrome/browser/ash/net",
       "//chrome/browser/ash/net/network_health",
       "//chrome/browser/ash/notifications",
@@ -4191,8 +4188,6 @@
       "views/passwords/credentials_item_view.h",
       "views/passwords/manage_passwords_details_view.cc",
       "views/passwords/manage_passwords_details_view.h",
-      "views/passwords/manage_passwords_icon_views.cc",
-      "views/passwords/manage_passwords_icon_views.h",
       "views/passwords/manage_passwords_list_view.cc",
       "views/passwords/manage_passwords_list_view.h",
       "views/passwords/manage_passwords_page_action_controller.cc",
diff --git a/chrome/browser/ui/accelerator_table.cc b/chrome/browser/ui/accelerator_table.cc
index 06e3a9b..4249f73 100644
--- a/chrome/browser/ui/accelerator_table.cc
+++ b/chrome/browser/ui/accelerator_table.cc
@@ -26,6 +26,10 @@
 #include "ui/events/event_constants.h"
 #include "ui/events/keycodes/keyboard_codes.h"
 
+#if BUILDFLAG(IS_MAC)
+#include "chrome/browser/global_keyboard_shortcuts_mac.h"
+#endif
+
 // Android chrome shortcuts are implemented in KeyboardShortcuts.java.
 static_assert(!BUILDFLAG(IS_ANDROID));
 
@@ -384,3 +388,23 @@
 bool IsCommandRepeatable(int command_id) {
   return std::ranges::contains(kRepeatableCommandIds, command_id);
 }
+
+bool GetAcceleratorForCommandId(int command_id, ui::Accelerator* accelerator) {
+#if BUILDFLAG(IS_MAC)
+  if (GetDefaultMacAcceleratorForCommandId(command_id, accelerator)) {
+    return true;
+  }
+#else
+  if (GetStandardAcceleratorForCommandId(command_id, accelerator)) {
+    return true;
+  }
+
+  for (const auto& mapping : GetAcceleratorList()) {
+    if (mapping.command_id == command_id) {
+      *accelerator = ui::Accelerator(mapping.keycode, mapping.modifiers);
+      return true;
+    }
+  }
+#endif
+  return false;
+}
diff --git a/chrome/browser/ui/accelerator_table.h b/chrome/browser/ui/accelerator_table.h
index 4f44af5..6cb05692 100644
--- a/chrome/browser/ui/accelerator_table.h
+++ b/chrome/browser/ui/accelerator_table.h
@@ -42,6 +42,10 @@
 bool GetStandardAcceleratorForCommandId(int command_id,
                                         ui::Accelerator* accelerator);
 
+// Returns true if the command id has an associated accelerator. If the return
+// is true the accelerator is returned via the second argument.
+bool GetAcceleratorForCommandId(int command_id, ui::Accelerator* accelerator);
+
 // Returns true if the command identified by |command_id| should be executed
 // repeatedly while its accelerator keys are held down.
 bool IsCommandRepeatable(int command_id);
diff --git a/chrome/browser/ui/android/browser_navigator_android.cc b/chrome/browser/ui/android/browser_navigator_android.cc
index d3c74e34..1ad2570 100644
--- a/chrome/browser/ui/android/browser_navigator_android.cc
+++ b/chrome/browser/ui/android/browser_navigator_android.cc
@@ -252,7 +252,8 @@
 
   // Perform navigation.
   content::NavigationController::LoadURLParams load_url_params =
-      LoadURLParamsFromNavigateParams(params);
+      LoadURLParamsFromNavigateParams(params->navigated_or_inserted_contents,
+                                      params);
   return params->navigated_or_inserted_contents->GetController()
       .LoadURLWithParams(load_url_params);
 }
diff --git a/chrome/browser/ui/android/browser_navigator_android_browsertest.cc b/chrome/browser/ui/android/browser_navigator_android_browsertest.cc
index 26c76fa..08fdd2d 100644
--- a/chrome/browser/ui/android/browser_navigator_android_browsertest.cc
+++ b/chrome/browser/ui/android/browser_navigator_android_browsertest.cc
@@ -62,6 +62,23 @@
   std::unique_ptr<NavigationCounter> counter_;
 };
 
+class NavigationUIDataObserver : public content::WebContentsObserver {
+ public:
+  explicit NavigationUIDataObserver(content::WebContents* web_contents)
+      : content::WebContentsObserver(web_contents) {}
+
+  void DidFinishNavigation(content::NavigationHandle* handle) override {
+    last_navigation_ui_data_ = handle->GetNavigationUIData();
+  }
+
+  content::NavigationUIData* last_navigation_ui_data() {
+    return last_navigation_ui_data_;
+  }
+
+ private:
+  raw_ptr<content::NavigationUIData> last_navigation_ui_data_ = nullptr;
+};
+
 }  // namespace
 
 class NavigateAndroidBrowserTest : public BrowserWindowAndroidBrowserTestBase {
@@ -941,3 +958,23 @@
   EXPECT_EQ(params.navigated_or_inserted_contents,
             tab_list_->GetTab(1)->GetContents());
 }
+
+IN_PROC_BROWSER_TEST_F(NavigateAndroidBrowserTest, AttachNavigationUIData) {
+  const GURL url1 = StartAtURL("/title1.html");
+
+  // Prepare and execute a CURRENT_TAB navigation.
+  const GURL url2 = embedded_test_server()->GetURL("/title2.html");
+  NavigateParams params(browser_window_, url2, ui::PAGE_TRANSITION_LINK);
+  params.disposition = WindowOpenDisposition::CURRENT_TAB;
+  params.source_contents = web_contents_;
+
+  NavigationUIDataObserver ui_data_observer(web_contents_);
+  content::TestNavigationObserver navigation_observer(web_contents_);
+
+  base::WeakPtr<content::NavigationHandle> handle = Navigate(&params);
+  ASSERT_TRUE(handle);
+  navigation_observer.Wait();
+
+  // Verify that NavigationUIData is attached on Android.
+  EXPECT_TRUE(ui_data_observer.last_navigation_ui_data());
+}
diff --git a/chrome/browser/ui/android/default_browser_promo/java/src/org/chromium/chrome/browser/ui/default_browser_promo/DefaultBrowserPromoManagerTest.java b/chrome/browser/ui/android/default_browser_promo/java/src/org/chromium/chrome/browser/ui/default_browser_promo/DefaultBrowserPromoManagerTest.java
index 6b4fb79..d5074c1 100644
--- a/chrome/browser/ui/android/default_browser_promo/java/src/org/chromium/chrome/browser/ui/default_browser_promo/DefaultBrowserPromoManagerTest.java
+++ b/chrome/browser/ui/android/default_browser_promo/java/src/org/chromium/chrome/browser/ui/default_browser_promo/DefaultBrowserPromoManagerTest.java
@@ -144,15 +144,25 @@
     @EnableFeatures({
         ChromeFeatureList.DEFAULT_BROWSER_PROMO_ENTRY_POINT + ":show_app_menu_item/true"
     })
-    public void testRecordOutcomeWithSource() {
-        // Create a Manager with a specific source ("AppMenu").
+    public void testRecordOutcomeWithAppMenuSource() {
+        runOutcomeTest(
+                DefaultBrowserPromoEntryPoint.APP_MENU,
+                "Android.DefaultBrowserPromo.Outcome.AppMenu");
+    }
+
+    @Test
+    @EnableFeatures({ChromeFeatureList.DEFAULT_BROWSER_PROMO_FRE})
+    public void testRecordOutcomeWithFRESource() {
+        runOutcomeTest(
+                DefaultBrowserPromoEntryPoint.FRE, "Android.DefaultBrowserPromo.Outcome.FRE");
+    }
+
+    /** Helper to run outcome tests for different entry points. */
+    private void runOutcomeTest(
+            @DefaultBrowserPromoEntryPoint int source, String expectedHistogram) {
         var manager =
                 new DefaultBrowserPromoManager(
-                        mActivity,
-                        mWindowAndroid,
-                        mImpressionCounter,
-                        mStateProvider,
-                        DefaultBrowserPromoEntryPoint.APP_MENU);
+                        mActivity, mWindowAndroid, mImpressionCounter, mStateProvider, source);
 
         int currentState = DefaultBrowserState.OTHER_DEFAULT;
         int outcomeState = DefaultBrowserState.CHROME_DEFAULT;
@@ -164,23 +174,23 @@
 
         var histogram =
                 HistogramWatcher.newBuilder()
-                        .expectIntRecord(
-                                "Android.DefaultBrowserPromo.Outcome.AppMenu", outcomeState)
+                        .expectIntRecord(expectedHistogram, outcomeState)
                         .build();
 
         // Trigger the promo.
         manager.promoByRoleManager();
 
-        // 6. Capture the callback.
+        // Capture the callback passed to WindowAndroid.
         ArgumentCaptor<IntentCallback> onShowCallbackCaptor =
                 ArgumentCaptor.forClass(IntentCallback.class);
         verify(mWindowAndroid)
                 .showCancelableIntent(eq(mIntent), onShowCallbackCaptor.capture(), any());
 
-        // Trigger the callback. We close the dialog and the histogram records the outcome.
+        // Trigger the callback since mWindowAndroid is a mock. We close the dialog and the
+        // histogram records the outcome.
         onShowCallbackCaptor.getValue().onIntentCompleted(1, null);
 
-        histogram.assertExpected("AppMenu specific outcome should be recorded");
+        histogram.assertExpected(expectedHistogram + " should be recorded.");
     }
 
     private void testRecord(
diff --git a/chrome/browser/ui/android/default_browser_promo/java/src/org/chromium/chrome/browser/ui/default_browser_promo/DefaultBrowserPromoMetrics.java b/chrome/browser/ui/android/default_browser_promo/java/src/org/chromium/chrome/browser/ui/default_browser_promo/DefaultBrowserPromoMetrics.java
index 81cbf75..9e447ad 100644
--- a/chrome/browser/ui/android/default_browser_promo/java/src/org/chromium/chrome/browser/ui/default_browser_promo/DefaultBrowserPromoMetrics.java
+++ b/chrome/browser/ui/android/default_browser_promo/java/src/org/chromium/chrome/browser/ui/default_browser_promo/DefaultBrowserPromoMetrics.java
@@ -27,15 +27,17 @@
     @IntDef({
         DefaultBrowserPromoSourceType.MESSAGES_PROMO,
         DefaultBrowserPromoSourceType.SETTING_CARD_PROMO,
-        DefaultBrowserPromoSourceType.EDUCATIONAL_TIP_PROMO
+        DefaultBrowserPromoSourceType.EDUCATIONAL_TIP_PROMO,
+        DefaultBrowserPromoSourceType.FRE_PROMO
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface DefaultBrowserPromoSourceType {
         int MESSAGES_PROMO = 0;
         int SETTING_CARD_PROMO = 1;
         int EDUCATIONAL_TIP_PROMO = 2;
+        int FRE_PROMO = 3;
 
-        int NUM_ENTRIES = 3;
+        int NUM_ENTRIES = 4;
     }
 
     // LINT.ThenChange(//tools/metrics/histograms/metadata/android/enums.xml:DefaultBrowserPromoSourceType)
@@ -45,6 +47,8 @@
             return "AppMenu";
         } else if (source == DefaultBrowserPromoEntryPoint.SETTINGS) {
             return "Settings";
+        } else if (source == DefaultBrowserPromoEntryPoint.FRE) {
+            return "FRE";
         }
         return "";
     }
diff --git a/chrome/browser/ui/android/logo/java/res/layout/logo_view_layout.xml b/chrome/browser/ui/android/logo/java/res/layout/logo_view_layout.xml
index f9c4b48..822d007 100644
--- a/chrome/browser/ui/android/logo/java/res/layout/logo_view_layout.xml
+++ b/chrome/browser/ui/android/logo/java/res/layout/logo_view_layout.xml
@@ -12,7 +12,5 @@
     android:layout_width="match_parent"
     android:layout_height="@dimen/ntp_logo_height"
     android:layout_gravity="center_horizontal"
-    android:layout_marginStart="16dp"
-    android:layout_marginEnd="16dp"
     android:layout_marginTop="@dimen/ntp_logo_margin_top"
     android:layout_marginBottom="@dimen/ntp_logo_margin_bottom" />
diff --git a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoCoordinator.java b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoCoordinator.java
index 1121d22..7c51786 100644
--- a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoCoordinator.java
+++ b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoCoordinator.java
@@ -189,15 +189,11 @@
      */
     public void setLayoutWidth(int widthPx) {
         MarginLayoutParams layoutParams = (MarginLayoutParams) mLogoView.getLayoutParams();
-        if (layoutParams.width == widthPx
-                && layoutParams.leftMargin == 0
-                && layoutParams.rightMargin == 0) {
+        if (layoutParams.width == widthPx) {
             return;
         }
 
         layoutParams.width = widthPx;
-        layoutParams.leftMargin = 0;
-        layoutParams.rightMargin = 0;
     }
 
     /** Jumps to the end of the logo view's cross-fading animation, if any.*/
@@ -283,8 +279,11 @@
         setDoodleSize(doodleSize);
 
         if (showingNonStandardGoogleLogo) {
-            LogoUtils.setLogoViewLayoutParamsForDoodle(
-                    mLogoView, mContext.getResources(), doodleSize);
+            int[] logoParams =
+                    LogoUtils.getLogoViewLayoutParams(
+                            mContext.getResources(), /* isLogoDoodle= */ true, doodleSize);
+            mLogoModel.set(LogoProperties.LOGO_HEIGHT, logoParams[0]);
+            mLogoModel.set(LogoProperties.LOGO_TOP_MARGIN, logoParams[1]);
         }
     }
 
diff --git a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoCoordinatorUnitTest.java b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoCoordinatorUnitTest.java
index f6e34633..a77ab94 100644
--- a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoCoordinatorUnitTest.java
+++ b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoCoordinatorUnitTest.java
@@ -6,6 +6,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
@@ -307,14 +308,12 @@
         mLogoCoordinator = createLogoCoordinator();
         verify(mLogoView).setDoodleSize(LogoUtils.DoodleSize.REGULAR);
 
-        ViewGroup.MarginLayoutParams layoutParams = new ViewGroup.MarginLayoutParams(0, 0);
-        when(mLogoView.getLayoutParams()).thenReturn(layoutParams);
-
         // Tablet transitions to multi-window mode.
         clearInvocations(mLogoView);
         when(mIsInMultiWindowModeSupplier.get()).thenReturn(true);
         mLogoCoordinator.updateDoodleOnTablet(/* showingNonStandardGoogleLogo= */ true);
-        verify(mLogoView).setLayoutParams(layoutParams);
+        verify(mLogoView).setLogoHeight(anyInt());
+        verify(mLogoView).setLogoTopMargin(anyInt());
     }
 
     @Test
diff --git a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoProperties.java b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoProperties.java
index 00303ccb..cc84aab 100644
--- a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoProperties.java
+++ b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoProperties.java
@@ -24,6 +24,7 @@
     WritableFloatPropertyKey ALPHA = new WritableFloatPropertyKey();
     WritableIntPropertyKey LOGO_TOP_MARGIN = new WritableIntPropertyKey();
     WritableIntPropertyKey LOGO_BOTTOM_MARGIN = new WritableIntPropertyKey();
+    WritableIntPropertyKey LOGO_HEIGHT = new WritableIntPropertyKey();
     WritableObjectPropertyKey<Boolean> SET_END_FADE_ANIMATION =
             new WritableObjectPropertyKey<>(/* skipEquality= */ true);
     // TODO(crbug.com/40881870): Change the VISIBILITY properties to some sort of state
@@ -56,6 +57,7 @@
                 ALPHA,
                 LOGO_TOP_MARGIN,
                 LOGO_BOTTOM_MARGIN,
+                LOGO_HEIGHT,
                 SET_END_FADE_ANIMATION,
                 VISIBILITY,
                 ANIMATION_ENABLED,
diff --git a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoView.java b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoView.java
index 7d7314c..6825937 100644
--- a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoView.java
+++ b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoView.java
@@ -152,6 +152,39 @@
         mDoodleSize = doodleSize;
     }
 
+    /**
+     * Sets the logo top margin.
+     *
+     * @param topMargin The top margin in pixels.
+     */
+    void setLogoTopMargin(int topMargin) {
+        MarginLayoutParams marginLayoutParams = (MarginLayoutParams) getLayoutParams();
+        marginLayoutParams.topMargin = topMargin;
+        setLayoutParams(marginLayoutParams);
+    }
+
+    /**
+     * Sets the logo bottom margin.
+     *
+     * @param bottomMargin The bottom margin in pixels.
+     */
+    void setLogoBottomMargin(int bottomMargin) {
+        MarginLayoutParams marginLayoutParams = (MarginLayoutParams) getLayoutParams();
+        marginLayoutParams.bottomMargin = bottomMargin;
+        setLayoutParams(marginLayoutParams);
+    }
+
+    /**
+     * Sets the logo height.
+     *
+     * @param height The height of the logo in pixels.
+     */
+    void setLogoHeight(int height) {
+        MarginLayoutParams marginLayoutParams = (MarginLayoutParams) getLayoutParams();
+        marginLayoutParams.height = height;
+        setLayoutParams(marginLayoutParams);
+    }
+
     /** Jumps to the end of the logo cross-fading animation, if any. */
     void endFadeAnimation() {
         if (mFadeAnimation != null) {
diff --git a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoViewBinder.java b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoViewBinder.java
index 8d9e14e..1f81fd2 100644
--- a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoViewBinder.java
+++ b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoViewBinder.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.logo;
 
 import android.view.View;
-import android.view.ViewGroup.MarginLayoutParams;
 
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.ui.modelutil.PropertyKey;
@@ -23,13 +22,11 @@
         if (LogoProperties.ALPHA == propertyKey) {
             logoView.setAlpha(model.get(LogoProperties.ALPHA));
         } else if (LogoProperties.LOGO_TOP_MARGIN == propertyKey) {
-            MarginLayoutParams marginLayoutParams = (MarginLayoutParams) logoView.getLayoutParams();
-            marginLayoutParams.topMargin = model.get(LogoProperties.LOGO_TOP_MARGIN);
-            logoView.setLayoutParams(marginLayoutParams);
+            logoView.setLogoTopMargin(model.get(LogoProperties.LOGO_TOP_MARGIN));
         } else if (LogoProperties.LOGO_BOTTOM_MARGIN == propertyKey) {
-            MarginLayoutParams marginLayoutParams = (MarginLayoutParams) logoView.getLayoutParams();
-            marginLayoutParams.bottomMargin = model.get(LogoProperties.LOGO_BOTTOM_MARGIN);
-            logoView.setLayoutParams(marginLayoutParams);
+            logoView.setLogoBottomMargin(model.get(LogoProperties.LOGO_BOTTOM_MARGIN));
+        } else if (LogoProperties.LOGO_HEIGHT == propertyKey) {
+            logoView.setLogoHeight(model.get(LogoProperties.LOGO_HEIGHT));
         } else if (LogoProperties.SET_END_FADE_ANIMATION == propertyKey) {
             logoView.endFadeAnimation();
         } else if (LogoProperties.VISIBILITY == propertyKey) {
diff --git a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoViewBinderUnitTest.java b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoViewBinderUnitTest.java
index d9d4e7b..263f1cae 100644
--- a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoViewBinderUnitTest.java
+++ b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoViewBinderUnitTest.java
@@ -21,6 +21,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
+import android.view.ViewGroup.MarginLayoutParams;
 
 import androidx.core.content.ContextCompat;
 import androidx.test.filters.SmallTest;
@@ -258,4 +259,28 @@
         logoModel.set(LogoProperties.SHOW_DEFAULT_GOOGLE_LOGO, true);
         verify(mMockLogoView).maybeShowDefaultLogoDrawable();
     }
+
+    @Test
+    @SmallTest
+    public void testSetLogoTopMargin() {
+        mLogoModel.set(LogoProperties.LOGO_TOP_MARGIN, 10);
+        MarginLayoutParams params = (MarginLayoutParams) mLogoView.getLayoutParams();
+        assertEquals(10, params.topMargin);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetLogoBottomMargin() {
+        mLogoModel.set(LogoProperties.LOGO_BOTTOM_MARGIN, 20);
+        MarginLayoutParams params = (MarginLayoutParams) mLogoView.getLayoutParams();
+        assertEquals(20, params.bottomMargin);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetLogoHeight() {
+        mLogoModel.set(LogoProperties.LOGO_HEIGHT, 50);
+        MarginLayoutParams params = (MarginLayoutParams) mLogoView.getLayoutParams();
+        assertEquals(50, params.height);
+    }
 }
diff --git a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoViewTest.java b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoViewTest.java
index 46086ba..520831c 100644
--- a/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoViewTest.java
+++ b/chrome/browser/ui/android/logo/java/src/org/chromium/chrome/browser/logo/LogoViewTest.java
@@ -217,4 +217,25 @@
         Assert.assertEquals(doodleHeight, logoLayoutParams.height);
         Assert.assertEquals(doodleTopMargin, logoLayoutParams.topMargin);
     }
+
+    @Test
+    public void testSetLogoTopMargin() {
+        MarginLayoutParams params = (MarginLayoutParams) mView.getLayoutParams();
+        mView.setLogoTopMargin(100);
+        Assert.assertEquals(100, params.topMargin);
+    }
+
+    @Test
+    public void testSetLogoBottomMargin() {
+        MarginLayoutParams params = (MarginLayoutParams) mView.getLayoutParams();
+        mView.setLogoBottomMargin(50);
+        Assert.assertEquals(50, params.bottomMargin);
+    }
+
+    @Test
+    public void testSetLogoHeight() {
+        MarginLayoutParams params = (MarginLayoutParams) mView.getLayoutParams();
+        mView.setLogoHeight(200);
+        Assert.assertEquals(200, params.height);
+    }
 }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/FuseboxSessionState.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/FuseboxSessionState.java
index 1203bde..ad3754e6 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/FuseboxSessionState.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/FuseboxSessionState.java
@@ -145,18 +145,26 @@
 
         mIsActive = true;
         mAutocompleteInput.setUrlFocusTime(System.currentTimeMillis());
-        // Use current URL if the Retention is active, the input is not already set, and the URL
-        // should be user-visible.
+
+        // Use current URL if the Retention is active as the starting input.
+        // On eligible LFF devices the Omnibox should, by default, present the
+        // current page URL (if the URL is eligible for display).
         if (OmniboxFeatures.shouldRetainOmniboxOnFocus()
-                && mAutocompleteInput.getUserText().isEmpty()
                 && UrlBarData.shouldShowUrl(mAutocompleteInput.getPageUrl(), false)) {
             var editUrl = UrlUtilities.stripScheme(mAutocompleteInput.getPageUrl().getSpec());
-            mAutocompleteInput.setUserText(editUrl).setSelection(0, Integer.MAX_VALUE);
+            mAutocompleteInput.setInitialUserText(editUrl);
+        } else {
+            mAutocompleteInput.setInitialUserText("");
         }
 
-        // The session is activated for the first time. Preserve the initial value of the User Text
-        // now. If the session is re-activated later, the user text will be preserved.
-        mAutocompleteInput.setInitialUserText(mAutocompleteInput.getUserText());
+        // Apply the initial default value unless user text is already set.
+        if (mAutocompleteInput.getUserText().isEmpty()) {
+            mAutocompleteInput
+                    .setUserText(mAutocompleteInput.getInitialUserText())
+                    .setSelection(
+                            OmniboxFeatures.shouldRetainOmniboxOnFocus() ? 0 : Integer.MAX_VALUE,
+                            Integer.MAX_VALUE);
+        }
 
         // Stop here if we're already waiting for profile.
         // This makes sense in scenarios where session object goes through a full cycle
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java
index adeab97..8fc2057d 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java
@@ -13,8 +13,10 @@
 import android.content.res.Resources;
 import android.util.DisplayMetrics;
 import android.view.LayoutInflater;
+import android.view.accessibility.AccessibilityEvent;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
 import androidx.constraintlayout.widget.ConstraintLayout;
 
 import org.chromium.base.ContextUtils;
@@ -148,6 +150,8 @@
                                         mContext, BrandedColorScheme.APP_DEFAULT),
                                 () -> popupView,
                                 dynamicRectProvider)
+                        .setFocusable(true)
+                        .addOnDismissListener(this::onContextPopupDismissed)
                         .setOutsideTouchable(true)
                         .setAnimateFromAnchor(true)
                         .setPreferredHorizontalOrientation(HorizontalOrientation.LAYOUT_DIRECTION)
@@ -315,6 +319,13 @@
         return mMediator;
     }
 
+    @VisibleForTesting
+    void onContextPopupDismissed() {
+        if (mViewHolder == null || mViewHolder.addButton == null) return;
+        mViewHolder.addButton.requestFocus();
+        mViewHolder.addButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+    }
+
     @Initializer
     private void onTemplateUrlServiceAvailable(TemplateUrlService templateUrlService) {
         mTemplateUrlService = templateUrlService;
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinatorUnitTest.java
index dccc156..3ae2990 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinatorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinatorUnitTest.java
@@ -26,10 +26,12 @@
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.view.LayoutInflater;
+import android.view.View;
 
 import androidx.constraintlayout.widget.ConstraintLayout;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -297,4 +299,14 @@
 
         verify(mMetrics).notifyOmniboxSessionEnded(eq(false), anyInt(), anyInt());
     }
+
+    @Test
+    @EnableFeatures(OmniboxFeatureList.OMNIBOX_MULTIMODAL_INPUT)
+    public void testPopupDismissed() {
+        mCoordinator.beginInput(createSession());
+        mCoordinator.setMediatorForTesting(mMediator);
+        mCoordinator.getViewHolderForTesting().addButton.setVisibility(View.VISIBLE);
+        mCoordinator.onContextPopupDismissed();
+        Assert.assertTrue(mCoordinator.getViewHolderForTesting().addButton.isFocused());
+    }
 }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
index ac8216e..e51b25d 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
@@ -445,6 +445,8 @@
         mModel.set(StatusProperties.STATUS_ICON_RESOURCE, icon);
         mModel.set(StatusProperties.STATUS_ICON_DESCRIPTION_RES, icon.getContentDescriptionRes());
         mModel.set(StatusProperties.STATUS_CLICK_LISTENER, this::onClickOpenPageInfo);
+
+        updateStatusViewVisibility();
     }
 
     /** Update selection of icon presented on the location bar. */
@@ -488,9 +490,8 @@
         } else if (mPermissionStatusHandler.isClapperQuietIconShowing()) {
             return;
         } else if (mSecurityIconRes != 0) {
-            if (isPageInfoMovedToAppMenu()
-                    && mPageSecurityLevel == ConnectionSecurityLevel.SECURE
-                    && !mShowStatusIconForSecureOrigins) {
+            if (mPageSecurityLevel == ConnectionSecurityLevel.SECURE
+                    && (isPageInfoMovedToAppMenu() || !mShowStatusIconForSecureOrigins)) {
                 mIsSecurityViewShown = false;
             } else {
                 mIsSecurityViewShown = true;
@@ -516,6 +517,8 @@
                 StatusProperties.STATUS_ACCESSIBILITY_DOUBLE_TAP_DESCRIPTION_RES,
                 doubleTapDescriptionRes);
         mModel.set(StatusProperties.STATUS_CLICK_LISTENER, clickListener);
+
+        updateStatusViewVisibility();
     }
 
     void onFuseboxStateChanged(int state) {
@@ -682,6 +685,8 @@
     public void resetCustomIconsStatus() {
         mPermissionStatusHandler.reset(/* shouldDismissNativePrompt= */ true);
         resetEmbeddedIconHandlers();
+
+        updateLocationBarIcon(IconTransitionType.CROSSFADE);
     }
 
     private void openPageInfo(Tab tab) {
@@ -796,7 +801,7 @@
     private void applyStatusIconAndTooltipProperties(
             boolean showIcon, boolean verboseStatusTextVisible) {
         mModel.set(StatusProperties.SHOW_STATUS_ICON, showIcon);
-        if (showIcon && !isHubSearch()) {
+        if ((showIcon || verboseStatusTextVisible) && !isHubSearch()) {
             Drawable background;
             if (mLocationBarDataProvider.isIncognitoBranded()) {
                 background =
@@ -848,10 +853,16 @@
                 mUrlHasFocus
                         || isHubSearch()
                         || mShowStatusIconForSecureOrigins
-                        || mPageSecurityLevel != ConnectionSecurityLevel.SECURE
+                        || mIsSecurityViewShown
+                        || hasStatusIconResource()
+                        || shouldShowVerboseStatusText()
                         || mIsStoreIconShowing);
     }
 
+    private boolean hasStatusIconResource() {
+        return mModel.get(StatusProperties.STATUS_ICON_RESOURCE) != null;
+    }
+
     private void onClickOpenPageInfo(View view) {
         if (!mLocationBarDataProvider.hasTab()
                 || assumeNonNull(mLocationBarDataProvider.getTab()).getWebContents() == null) {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
index 301d918..b975247 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediatorUnitTest.java
@@ -612,6 +612,7 @@
         mMediator.setShowStatusIconForSecureOrigins(false);
         assertFalse(mModel.get(StatusProperties.SHOW_STATUS_VIEW));
 
+        mMediator.updateSecurityIcon(R.drawable.ic_logo_googleg_20dp, 0, 0);
         mMediator.updateVerboseStatus(ConnectionSecurityLevel.WARNING, false, false);
         assertTrue(mModel.get(StatusProperties.SHOW_STATUS_VIEW));
 
@@ -631,9 +632,10 @@
         assertTrue(mModel.get(StatusProperties.SHOW_STATUS_VIEW));
 
         mMediator.setUrlHasFocus(false);
-        assertTrue(mModel.get(StatusProperties.SHOW_STATUS_VIEW));
+        assertFalse(mModel.get(StatusProperties.SHOW_STATUS_VIEW));
 
         // Non-secure pages should show the status view.
+        mMediator.updateSecurityIcon(R.drawable.ic_logo_googleg_20dp, 0, 0);
         mMediator.updateVerboseStatus(ConnectionSecurityLevel.DANGEROUS, false, false);
         assertTrue(mModel.get(StatusProperties.SHOW_STATUS_VIEW));
 
@@ -645,6 +647,45 @@
 
     @Test
     @SmallTest
+    public void testUpdateStatusViewVisibility_withPermissionIcon() {
+        mMediator.setShowStatusIconForSecureOrigins(false);
+        mMediator.updateVerboseStatus(ConnectionSecurityLevel.SECURE, false, false);
+        assertFalse(mModel.get(StatusProperties.SHOW_STATUS_VIEW));
+
+        StatusProperties.PermissionIconResource icon =
+                new StatusProperties.PermissionIconResource(null, false, "test_icon");
+        mMediator.showPermissionIcon(icon);
+
+        assertTrue(mModel.get(StatusProperties.SHOW_STATUS_VIEW));
+    }
+
+    @Test
+    @SmallTest
+    public void testUpdateStatusViewVisibility_withVerboseStatusText() {
+        mMediator.setShowStatusIconForSecureOrigins(false);
+        mMediator.updateVerboseStatus(ConnectionSecurityLevel.SECURE, false, false);
+        assertFalse(mModel.get(StatusProperties.SHOW_STATUS_VIEW));
+
+        mMediator.updateVerboseStatus(ConnectionSecurityLevel.SECURE, true, false);
+
+        assertTrue(mModel.get(StatusProperties.SHOW_STATUS_VIEW));
+    }
+
+    @Test
+    @SmallTest
+    public void testUpdateStatusViewVisibility_withPaintPreview() {
+        mMediator.setShowStatusIconForSecureOrigins(false);
+        mMediator.updateVerboseStatus(ConnectionSecurityLevel.SECURE, false, false);
+        assertFalse(mModel.get(StatusProperties.SHOW_STATUS_VIEW));
+
+        // Simulate Paint Preview active (third argument is true)
+        mMediator.updateVerboseStatus(ConnectionSecurityLevel.SECURE, false, true);
+
+        assertTrue(mModel.get(StatusProperties.SHOW_STATUS_VIEW));
+    }
+
+    @Test
+    @SmallTest
     @EnableFeatures({ChromeFeatureList.ANDROID_PAGE_INFO_AS_APP_MENU_ITEM})
     public void setShowStatusIconForSecureOrigins_whenPageInfoMoved() {
         // Set security level to SECURE, the status view should be hidden.
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java
index 0ded5651..3c092ad 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusProperties.java
@@ -55,7 +55,7 @@
         }
 
         /** Constructor for a custom drawable with identifier. */
-        public StatusIconResource(Drawable drawable, String iconIdentifier) {
+        public StatusIconResource(@Nullable Drawable drawable, String iconIdentifier) {
             mDrawable = drawable;
             mIconIdentifier = iconIdentifier;
         }
@@ -192,7 +192,8 @@
             mContentDescriptionRes = contentDescriptionRes;
         }
 
-        PermissionIconResource(Drawable drawable, boolean isIncognito, String iconIdentifier) {
+        PermissionIconResource(
+                @Nullable Drawable drawable, boolean isIncognito, String iconIdentifier) {
             super(drawable, iconIdentifier);
             mIsIncognito = isIncognito;
             mContentDescriptionRes = 0;
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
index 8df2266..8fe03da 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteCoordinator.java
@@ -427,7 +427,7 @@
 
     /** Notify the Autocomplete about Omnibox text change. */
     public void onInputChanged() {
-        mMediator.onInputChanged(false);
+        mMediator.onInputChanged();
     }
 
     /** Trigger autocomplete for the given query. */
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
index d34fca39..fc332bd 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -158,10 +158,8 @@
     private final Callback<@Nullable SiteSearchData> mOnSiteSearchDataChanged =
             this::onSiteSearchDataChanged;
     private final Callback<Integer> mOnFuseboxStateChanged = this::onFuseboxStateChanged;
-    private final Callback<String> mOnUserTextChanged =
-            text -> onInputChanged(/* isOnFocusContext= */ false);
-    private final Callback<Boolean> mOnShouldAutocompleteChanged =
-            state -> onInputChanged(/* isOnFocusContext= */ false);
+    private final Callback<String> mOnUserTextChanged = text -> onInputChanged();
+    private final Callback<Boolean> mOnShouldAutocompleteChanged = state -> onInputChanged();
 
     private @Nullable AutocompleteController mAutocomplete;
     private @Nullable AutocompleteResult mAutocompleteResult;
@@ -529,7 +527,7 @@
             // suggestion would take the user to the DSE home page.
             // This is tracked by MobileStartup.LaunchCause / EXTERNAL_SEARCH_ACTION_INTENT
             // metric.
-            onInputChanged(/* isOnFocusContext= */ OmniboxFeatures.shouldRetainOmniboxOnFocus());
+            onInputChanged();
         }
     }
 
@@ -828,7 +826,7 @@
             mAutocompleteInput.setUserText(refineText);
         }
         mDelegate.setOmniboxEditingText(refineText);
-        onInputChanged(/* isOnFocusContext= */ false);
+        onInputChanged();
 
         if (isSearchSuggestion) {
             // Note: the logic below toggles assumes individual values to be represented by
@@ -1054,7 +1052,7 @@
      *
      * @param isOnFocusContext whether Omnibox is currently gaining focus
      */
-    public void onInputChanged(boolean isOnFocusContext) {
+    public void onInputChanged() {
         if (!isInInputSession()) return;
         if (mShouldPreventOmniboxAutocomplete) return;
 
@@ -1092,7 +1090,7 @@
 
         stopAutocomplete(false);
 
-        if (isInZeroPrefixContext || isOnFocusContext) {
+        if (isInZeroPrefixContext) {
             clearSuggestions();
             startZeroSuggest();
         } else {
@@ -1246,7 +1244,7 @@
 
     private void onAutocompleteRequestTypeChanged(@AutocompleteRequestType int type) {
         if (!isInInputSession()) return;
-        onInputChanged(/* isOnFocusContext= */ false);
+        onInputChanged();
     }
 
     private void onKeywordModeEntered(@Nullable SiteSearchData siteSearchData) {
@@ -1296,7 +1294,7 @@
                 siteSearchData != null ? siteSearchData.fullName : null);
 
         if (isInInputSession()) {
-            onInputChanged(/* isOnFocusContext= */ false);
+            onInputChanged();
         }
     }
 
@@ -1828,7 +1826,7 @@
             // triggering recalculation of refine arrow icon. TODO(http://crbug.com/446058347):
             // refactor to enable updates to the icon property of the model once the list is already
             // built.
-            onInputChanged(/* isOnFocusContext= */ false);
+            onInputChanged();
         }
     }
 
@@ -1854,7 +1852,7 @@
     public void onAttachmentListChanged() {
         if (!isInInputSession()) return;
         // Re-request ZPS in the event of attachments being removed/replaced.
-        onInputChanged(/* isOnFocusContext= */ false);
+        onInputChanged();
     }
 
     /**
@@ -1865,7 +1863,7 @@
         if (!isInInputSession()) return;
 
         // Re-request ZPS in the event of new attachments being uploaded.
-        onInputChanged(/* isOnFocusContext= */ false);
+        onInputChanged();
     }
 
     @Override
@@ -1874,7 +1872,7 @@
 
         if (isTopResumedActivity) {
             installAutocompleteObservers();
-            onInputChanged(/* isOnFocusContext= */ false);
+            onInputChanged();
         } else {
             stopAutocomplete(/* clear= */ true);
             removeAutocompleteObservers();
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
index b4e1082f..9a2a756d 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
@@ -594,7 +594,7 @@
         String title = "title";
         int pageClassification = PageClassification.BLANK_VALUE;
         var session = createSession(url, title, pageClassification);
-        session.getAutocompleteInput().setUserText("Text");
+        session.getAutocompleteInput().setUserText("Text").setInitialUserText("Text");
 
         mMediator.beginInput(session);
         RobolectricUtil.runAllBackgroundAndUi();
@@ -618,6 +618,41 @@
 
     @Test
     @SmallTest
+    public void onInputChanged_initialTextTriggersZeroSuggest() {
+        GURL url = JUnitTestGURLs.BLUE_1;
+        String title = "Title";
+        int pageClassification = PageClassification.BLANK_VALUE;
+        var session = createSession(url, title, pageClassification);
+        session.getAutocompleteInput()
+                .setUserText("initial text")
+                .setInitialUserText("initial text");
+        mMediator.beginInput(session);
+
+        RobolectricUtil.runAllBackgroundAndUi();
+        verifyAutocompleteStartZeroSuggest("initial text", url, pageClassification, title);
+    }
+
+    @Test
+    @SmallTest
+    public void onInputChanged_userTextDiffersFromInitialText_triggersPrefixedSuggest() {
+        GURL url = JUnitTestGURLs.BLUE_1;
+        String title = "Title";
+        int pageClassification = PageClassification.BLANK_VALUE;
+        var session = createSession(url, title, pageClassification);
+        session.getAutocompleteInput().setUserText("user text").setInitialUserText("initial text");
+
+        when(mTextStateProvider.getSelectionStart()).thenReturn(9);
+        when(mTextStateProvider.getSelectionEnd()).thenReturn(9);
+        when(mTextStateProvider.shouldAutocomplete()).thenReturn(false);
+
+        mMediator.beginInput(session);
+
+        RobolectricUtil.runAllBackgroundAndUiIncludingDelayed();
+        verifyAutocompleteStart(url, pageClassification, "user text", 9, true);
+    }
+
+    @Test
+    @SmallTest
     public void onTextChanged_nonEmptyTextTriggersSuggestions() {
 
         GURL url = JUnitTestGURLs.BLUE_1;
@@ -842,7 +877,7 @@
         String title = "Title";
         int pageClassification = PageClassification.BLANK_VALUE;
         var session = createSession(url, title, pageClassification);
-        session.getAutocompleteInput().setUserText(url.getSpec());
+        session.getAutocompleteInput().setUserText(url.getSpec()).setInitialUserText(url.getSpec());
 
         mMediator.beginInput(session);
 
@@ -1329,7 +1364,7 @@
             var session = createSession(PAGE_URL, PAGE_TITLE, pageClass.getNumber());
             mMediator.beginInput(session);
             // Force an update as "" -> "" is not an observable change.
-            mMediator.onInputChanged(/* isOnFocusContext= */ true);
+            mMediator.onInputChanged();
             verify(mMockCachedZeroSuggestionsManager, never()).saveToCache(anyInt(), any());
             clearInvocations(mMockCachedZeroSuggestionsManager);
         }
@@ -1576,7 +1611,7 @@
         // When not on an Incognito NTP, cached suggestions should be shown.
         doReturn(false).when(ntpDelegate).isIncognitoNewTabPageCurrentlyVisible();
         // Force an update as "" -> "" is not an observable change.
-        mMediator.onInputChanged(/* isOnFocusContext= */ true);
+        mMediator.onInputChanged();
         RobolectricUtil.runAllBackgroundAndUiIncludingDelayed();
         verify(mMockCachedZeroSuggestionsManager, times(1)).readFromCache(anyInt());
 
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index deeb5aa..75aafa37 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -6212,6 +6212,9 @@
       <message name="IDS_IPH_GESTURE_USER_EDUCATION_TAP_TO_DISMISS" desc="The in-product-help message to notify users how to dismiss.">
         Tap to dismiss
       </message>
+      <message name="IDS_EXTENSIONS_MENU_MANAGE_APP_MENU_IPH" desc="IPH message pointing to the Chrome app menu three-dot button shown after a user unpins the extensions menu puzzle piece explaining they can manage their extensions in the Chrome app menu.">
+        Manage your extensions in Chrome main menu
+      </message>
       <message name="IDS_EXTENSIONS_MENU_MANAGE_TOOLBAR_IPH" desc="IPH message pointing to the extension puzzle piece shown after a user installs their first extension explaining they can manage their extensions in the toolbar.">
         Manage your extensions in the toolbar
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_EXTENSIONS_MENU_MANAGE_APP_MENU_IPH.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_EXTENSIONS_MENU_MANAGE_APP_MENU_IPH.png.sha1
new file mode 100644
index 0000000..0e97b03
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_EXTENSIONS_MENU_MANAGE_APP_MENU_IPH.png.sha1
@@ -0,0 +1 @@
+5b100a1d090799c1dad98ff3c9f7185ac8feb05a
\ No newline at end of file
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/BUILD.gn b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/BUILD.gn
index 2e464a1..3430dee 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/BUILD.gn
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/BUILD.gn
@@ -57,6 +57,7 @@
     "//chrome/browser/ui/android/layouts:java",
     "//chrome/browser/ui/android/theme:java",
     "//chrome/browser/ui/android/toolbar:core_java",
+    "//chrome/browser/ui/android/toolbar:java_resources",
     "//chrome/browser/ui/browser_window/public/android:java",
     "//chrome/browser/user_education:java",
     "//components/browser_ui/widget/android:java",
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuCoordinator.java
index 59dd628..9dcc25f 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuCoordinator.java
@@ -4,8 +4,12 @@
 
 package org.chromium.chrome.browser.toolbar.extensions;
 
+import android.app.Activity;
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
 import android.view.LayoutInflater;
 import android.view.View;
 
@@ -30,11 +34,15 @@
 import org.chromium.chrome.browser.ui.extensions.ExtensionsToolbarBridge;
 import org.chromium.chrome.browser.ui.extensions.R;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
+import org.chromium.chrome.browser.user_education.IphCommandBuilder;
+import org.chromium.chrome.browser.user_education.UserEducationHelper;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.feature_engagement.EventConstants;
+import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.PageTransition;
+import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.listmenu.ListMenu;
 import org.chromium.ui.listmenu.ListMenuButton;
 import org.chromium.ui.listmenu.ListMenuDelegate;
@@ -44,6 +52,7 @@
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter;
+import org.chromium.ui.widget.AnchoredPopupWindow.HorizontalOrientation;
 import org.chromium.ui.widget.RectProvider;
 
 /**
@@ -69,6 +78,7 @@
     private final PropertyModelChangeProcessor mSitePermissionsPageChangeProcessor;
     private final ModelList mExtensionModels;
     private final ChromeAndroidTask mTask;
+    private final WindowAndroid mWindowAndroid;
     private final ExtensionsToolbarBridge mExtensionsToolbarBridge;
     private final MenuButtonPinningDelegate mMenuButtonPinningDelegate;
     private final ThemeColorProvider.TintObserver mTintObserver = this::onTintChanged;
@@ -82,6 +92,7 @@
      * @param extensionsMenuButton The puzzle icon in the toolbar.
      * @param themeColorProvider The provider for theme colors.
      * @param task Supplies the {@link ChromeAndroidTask}.
+     * @param windowAndroid The {@link WindowAndroid} for the current activity.
      * @param profile The current profile.
      * @param currentTabSupplier Supplies the current {@link Tab}.
      * @param tabCreator {@link TabCreator} to handle a new tab creation.
@@ -94,6 +105,7 @@
             ListMenuButton extensionsMenuButton,
             ThemeColorProvider themeColorProvider,
             ChromeAndroidTask task,
+            WindowAndroid windowAndroid,
             Profile profile,
             NullableObservableSupplier<Tab> currentTabSupplier,
             TabCreator tabCreator,
@@ -104,6 +116,7 @@
         mProfile = profile;
         mTabCreator = tabCreator;
         mTask = task;
+        mWindowAndroid = windowAndroid;
         mExtensionsToolbarBridge = extensionsToolbarBridge;
         mMenuButtonPinningDelegate = menuButtonPinningDelegate;
 
@@ -279,8 +292,11 @@
         mMainPageModel.set(
                 ExtensionsMenuProperties.MENU_BUTTON_PINNING_CLICK_LISTENER,
                 (view) -> {
-                    mMenuButtonPinningDelegate.setMenuButtonPinned(
-                            !mMenuButtonPinningDelegate.isMenuButtonPinned());
+                    boolean willBePinned = !mMenuButtonPinningDelegate.isMenuButtonPinned();
+                    mMenuButtonPinningDelegate.setMenuButtonPinned(willBePinned);
+                    if (!willBePinned) {
+                        showManageExtensionsAppMenuIph();
+                    }
                 });
         mMainPageModel.set(
                 ExtensionsMenuProperties.MENU_BUTTON_PINNED,
@@ -324,6 +340,34 @@
                 });
     }
 
+    private void showManageExtensionsAppMenuIph() {
+        if (mProfile.shutdownStarted()) return;
+
+        Activity activity = mWindowAndroid.getActivity().get();
+        if (activity == null) return;
+
+        View anchorView =
+                activity.findViewById(org.chromium.chrome.browser.toolbar.R.id.menu_button_wrapper);
+        if (anchorView == null) return;
+
+        UserEducationHelper userEducationHelper =
+                new UserEducationHelper(activity, mProfile, new Handler(Looper.getMainLooper()));
+
+        userEducationHelper.requestShowIph(
+                new IphCommandBuilder(
+                                activity.getResources(),
+                                FeatureConstants.IPH_EXTENSIONS_MANAGE_APP_MENU_FEATURE,
+                                R.string.extensions_menu_manage_app_menu_iph,
+                                R.string.extensions_menu_manage_app_menu_iph)
+                        .setAnchorView(anchorView)
+                        .setPreferredHorizontalOrientation(
+                                HorizontalOrientation.MAX_AVAILABLE_SPACE)
+                        .setHorizontalOverlapAnchor(true)
+                        .setRemoveArrow(true)
+                        .setInsetRect(new Rect())
+                        .build());
+    }
+
     private void setupSitePermissionsPageModel() {
         mSitePermissionsPageModel.set(
                 SitePermissionsPageProperties.BACK_CLICK_LISTENER,
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuCoordinatorTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuCoordinatorTest.java
index aec932c..47a7cce0 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuCoordinatorTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsMenuCoordinatorTest.java
@@ -50,8 +50,10 @@
 import org.chromium.chrome.browser.ui.extensions.ExtensionsToolbarBridge;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.feature_engagement.EventConstants;
+import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.listmenu.ListMenuButton;
 import org.chromium.ui.listmenu.ListMenuHost;
 
@@ -76,6 +78,7 @@
     @Mock private ExtensionsMenuBridge.Natives mExtensionsMenuBridgeJniMock;
     @Mock private MenuButtonPinningDelegate mMenuButtonPinningDelegate;
     @Mock private Tracker mTracker;
+    @Mock private WindowAndroid mWindowAndroid;
 
     @Captor private ArgumentCaptor<LoadUrlParams> mLoadUrlParamsCaptor;
 
@@ -104,7 +107,11 @@
         activity.setContentView(mExtensionsMenuButton);
 
         when(mTask.getOrCreateNativeBrowserWindowPtr(mProfile)).thenReturn(BROWSER_WINDOW_POINTER);
+        java.lang.ref.WeakReference<Activity> mockActivityRef =
+                new java.lang.ref.WeakReference<>(mContext);
+        when(mWindowAndroid.getActivity()).thenReturn(mockActivityRef);
         when(mTab.getProfile()).thenReturn(mProfile);
+        when(mProfile.getOriginalProfile()).thenReturn(mProfile);
 
         // Mock {@link ExtensionsMenuBridge}.
         ExtensionsMenuBridgeJni.setInstanceForTesting(mExtensionsMenuBridgeJniMock);
@@ -133,6 +140,7 @@
                         mExtensionsMenuButton,
                         mThemeColorProvider,
                         mTask,
+                        mWindowAndroid,
                         mProfile,
                         mCurrentTabSupplier,
                         mTabCreator,
@@ -303,6 +311,42 @@
     }
 
     @Test
+    public void testMenuUnpinned_ShowsManageAppMenuIph() {
+        when(mTracker.isInitialized()).thenReturn(true);
+        doAnswer(
+                        invocation -> {
+                            org.chromium.base.Callback<Boolean> callback =
+                                    invocation.getArgument(0);
+                            callback.onResult(true);
+                            return null;
+                        })
+                .when(mTracker)
+                .addOnInitializedCallback(any());
+
+        // Mock that the button is initially pinned.
+        when(mMenuButtonPinningDelegate.isMenuButtonPinned()).thenReturn(true);
+
+        // Activity is already mocked in setUp().
+        View anchorView = new View(mContext);
+        anchorView.setId(org.chromium.chrome.browser.toolbar.R.id.menu_button_wrapper);
+        mContext.setContentView(anchorView);
+
+        // Unpin the extensions menu button.
+        mExtensionsMenuCoordinator
+                .getContentView()
+                .findViewById(
+                        org.chromium.chrome.browser.ui.extensions.R.id
+                                .extensions_menu_pin_menu_icon_button)
+                .performClick();
+
+        org.robolectric.shadows.ShadowLooper.idleMainLooper();
+
+        // Verify the IPH tracker was notified with the correct feature.
+        verify(mTracker)
+                .shouldTriggerHelpUi(FeatureConstants.IPH_EXTENSIONS_MANAGE_APP_MENU_FEATURE);
+    }
+
+    @Test
     public void testSiteSettingsToggle_ClickCallsBridge() {
         mCurrentTabSupplier.set(mTab);
         mExtensionsMenuButton.performClick();
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsToolbarCoordinatorImpl.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsToolbarCoordinatorImpl.java
index 00e10961..8e45b2d 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsToolbarCoordinatorImpl.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsToolbarCoordinatorImpl.java
@@ -44,7 +44,6 @@
 import org.chromium.components.prefs.PrefService;
 import org.chromium.components.user_prefs.UserPrefs;
 import org.chromium.content_public.browser.selection.SelectionDropdownMenuDelegate;
-import org.chromium.ui.base.ActivityWindowAndroid;
 import org.chromium.ui.base.ViewUtils;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.listmenu.ListMenuButton;
@@ -93,7 +92,7 @@
             new MenuButtonPinningDelegate();
     private View.@Nullable OnLayoutChangeListener mLayoutChangeListener;
     private boolean mWasWindowCompact;
-    private ChromeAndroidTask mTask;
+    private WindowAndroid mWindowAndroid;
     private Profile mProfile;
     private PrefService mPrefService;
     private PrefChangeRegistrar mPrefChangeRegistrar;
@@ -113,7 +112,7 @@
             @Nullable SelectionDropdownMenuDelegate selectionDropdownMenuDelegate,
             TabModelSelector tabModelSelector) {
         mBridge = new ExtensionActionsBridge(task, profile);
-        mTask = task;
+        mWindowAndroid = windowAndroid;
         mProfile = profile;
 
         extensionsToolbarStub.setLayoutResource(R.layout.extensions_toolbar_container);
@@ -150,6 +149,7 @@
                         mContainer.findViewById(R.id.extensions_menu_button),
                         themeColorProvider,
                         task,
+                        windowAndroid,
                         profile,
                         currentTabSupplier,
                         tabCreator,
@@ -253,9 +253,7 @@
     private void showIphInternal() {
         if (mProfile.shutdownStarted()) return;
 
-        ActivityWindowAndroid activityWindowAndroid = mTask.getTopActivityWindowAndroid();
-        if (activityWindowAndroid == null) return;
-        Activity activity = activityWindowAndroid.getActivity().get();
+        Activity activity = mWindowAndroid.getActivity().get();
         if (activity == null) return;
 
         View anchorView = mContainer.findViewById(R.id.extensions_menu_button);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsToolbarTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsToolbarTest.java
index 054d0d9..0175c620 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsToolbarTest.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionsToolbarTest.java
@@ -23,6 +23,8 @@
 import static org.chromium.ui.test.util.ViewUtils.VIEW_NULL;
 import static org.chromium.ui.test.util.ViewUtils.withEventualExpectedViewState;
 
+import android.os.SystemClock;
+import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -312,7 +314,38 @@
         return installTestExtension(dir);
     }
 
-    /** Loads an extension with a content script that matches the given host. */
+    /** Creates and loads an extension with action that can be triggered with command. */
+    private String loadCommandExtension(
+            String dirName, String name, String actionTitle, String command, String message)
+            throws IOException {
+        File dir = mTempDir.newFolder(dirName);
+        writeFile(
+                new File(dir, "manifest.json"),
+                String.format(
+                        """
+                        {
+                          "name": "%s",
+                          "manifest_version": 3,
+                          "version": "0.1",
+                          "permissions": ["test"],
+                          "action": { "default_title": "%s", "default_popup": "popup.html" },
+                          "commands": {
+                            "_execute_action": {
+                              "suggested_key": { "default": "%s" },
+                              "description": "A test command"
+                            }
+                          }
+                        }
+                        """,
+                        name, actionTitle, command));
+        writeFile(new File(dir, "popup.html"), "<html><script src=\"popup.js\"></script></html>");
+        writeFile(
+                new File(dir, "popup.js"),
+                String.format("chrome.test.sendMessage('%s');", message));
+        return installTestExtension(dir);
+    }
+
+    /** Creates and loads an extension with a content script that matches the given host. */
     private String loadContentScriptExtension(
             String dirName, String name, String actionTitle, String host) throws IOException {
         File dir = mTempDir.newFolder(dirName);
@@ -336,7 +369,7 @@
         return installTestExtension(dir);
     }
 
-    /** Loads an extension that requests host permissions on a specific site. */
+    /** Creates and loads an extension that requests host permissions on a specific site. */
     private String loadHostPermissionsExtension(
             String dirName, String name, String actionTitle, String host) throws IOException {
         File dir = mTempDir.newFolder(dirName);
@@ -442,6 +475,23 @@
         return null;
     }
 
+    /**
+     * Simulate keypresses. Unlike {@link KeyUtils.dispatchKeyEventToView}, this sends the key event
+     * to the focused window.
+     *
+     * @param code The key code to send.
+     * @param metaState The meta state to use alongside.
+     */
+    private void sendKeyEvent(int code, int metaState) {
+        long eventTime = SystemClock.uptimeMillis();
+        KeyEvent downEvent =
+                new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, code, 0, metaState);
+        KeyEvent upEvent =
+                new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, code, 0, metaState);
+        InstrumentationRegistry.getInstrumentation().sendKeySync(downEvent);
+        InstrumentationRegistry.getInstrumentation().sendKeySync(upEvent);
+    }
+
     @Test
     @LargeTest
     public void testExtensionsMenuButtonStateOnTabChange() throws IOException {
@@ -773,4 +823,86 @@
                 new String[] {gammaId, betaId, alphaId},
                 ExtensionTestUtils.getPinnedActionIds(mProfile));
     }
+
+    @Test
+    @LargeTest
+    public void testManifestSpecifiedExtensionCommand() throws IOException {
+        String extensionId =
+                loadCommandExtension(
+                        "extension", "Extension", "Action", "Ctrl+Shift+1", "popup opened");
+        ExtensionTestUtils.setExtensionActionVisible(mProfile, extensionId, true);
+
+        // Listen for the background script to echo the command back.
+        try (ExtensionTestMessageListener listener =
+                new ExtensionTestMessageListener("popup opened")) {
+            // Send key events to trigger extension action.
+            sendKeyEvent(KeyEvent.KEYCODE_1, KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON);
+            assertTrue(listener.waitUntilSatisfied());
+        }
+
+        // Ensure that the popup has opened.
+        CriteriaHelper.pollInstrumentationThread(
+                () -> ExtensionTestUtils.getRenderFrameHostCount(mProfile, extensionId) == 1,
+                "Popup did not open.");
+    }
+
+    @Test
+    @LargeTest
+    public void testExtensionCommandClosesPopupIfOpen() throws IOException {
+        String extensionId =
+                loadCommandExtension(
+                        "extension", "Extension", "Action", "Ctrl+Shift+1", "popup opened");
+        ExtensionTestUtils.setExtensionActionVisible(mProfile, extensionId, true);
+
+        // Listen for the background script to echo the command back.
+        try (ExtensionTestMessageListener listener =
+                new ExtensionTestMessageListener("popup opened")) {
+            // Send key events to trigger extension action.
+            sendKeyEvent(KeyEvent.KEYCODE_1, KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON);
+            assertTrue(listener.waitUntilSatisfied());
+        }
+
+        // Ensure the popup is open.
+        CriteriaHelper.pollInstrumentationThread(
+                () -> ExtensionTestUtils.getRenderFrameHostCount(mProfile, extensionId) == 1,
+                "Popup did not open");
+
+        // Send the same keypresses again.
+        sendKeyEvent(KeyEvent.KEYCODE_1, KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON);
+
+        // Confirm that the popup has closed.
+        CriteriaHelper.pollInstrumentationThread(
+                () -> ExtensionTestUtils.getRenderFrameHostCount(mProfile, extensionId) == 0,
+                "Popup did not close.");
+    }
+
+    @Test
+    @LargeTest
+    public void testNonExtensionCommandIsSentToApplicationWindow() throws IOException {
+        String extensionId = loadPopupExtension("extension", "Extension", "Action", "popup opened");
+        ExtensionTestUtils.setExtensionActionVisible(mProfile, extensionId, true);
+        ViewUtils.onViewWaiting(withContentDescription("Action")).check(matches(isDisplayed()));
+
+        // Listen for the background script to echo the command back.
+        try (ExtensionTestMessageListener listener =
+                new ExtensionTestMessageListener("popup opened")) {
+            // Click on the icon to show the popup.
+            clickViewWithContentDescription("Action");
+            assertTrue(listener.waitUntilSatisfied());
+        }
+
+        // Ensure the popup is open before sending the key event.
+        CriteriaHelper.pollInstrumentationThread(
+                () -> ExtensionTestUtils.getRenderFrameHostCount(mProfile, extensionId) == 1,
+                "Popup did not open");
+
+        // Send Ctrl+T.
+        sendKeyEvent(KeyEvent.KEYCODE_T, KeyEvent.META_CTRL_ON);
+
+        // The Ctrl+T command should be routed to the main window, causing a new tab to be created
+        // and the popup to be dismissed.
+        CriteriaHelper.pollInstrumentationThread(
+                () -> ExtensionTestUtils.getRenderFrameHostCount(mProfile, extensionId) == 0,
+                "Popup should have closed after Ctrl+T was pressed and tab model has changed.");
+    }
 }
diff --git a/chrome/browser/ui/ash/shell_delegate/BUILD.gn b/chrome/browser/ui/ash/shell_delegate/BUILD.gn
index db2f5c6e..5910b0d6 100644
--- a/chrome/browser/ui/ash/shell_delegate/BUILD.gn
+++ b/chrome/browser/ui/ash/shell_delegate/BUILD.gn
@@ -33,6 +33,7 @@
     "//chrome/browser/ash/profiles",
     "//chrome/browser/ash/scanner",
     "//chrome/browser/feedback",
+    "//chrome/browser/nearby_sharing",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/profiles:profile_manager",
     "//chrome/browser/sessions",
diff --git a/chrome/browser/ui/browser_actions.cc b/chrome/browser/ui/browser_actions.cc
index e07a372..95949826 100644
--- a/chrome/browser/ui/browser_actions.cc
+++ b/chrome/browser/ui/browser_actions.cc
@@ -30,6 +30,10 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/sharing_hub/sharing_hub_features.h"
+#include "chrome/browser/ui/accelerator_table.h"
+#if BUILDFLAG(IS_MAC)
+#include "chrome/browser/global_keyboard_shortcuts_mac.h"
+#endif
 #include "chrome/browser/ui/actions/chrome_action_id.h"
 #include "chrome/browser/ui/actions/chrome_actions.h"
 #include "chrome/browser/ui/ai_overlay_dialog/ai_overlay_dialog_controller.h"
@@ -322,6 +326,13 @@
             .Build());
   }
 
+  ui::Accelerator reading_mode_accelerator;
+  std::u16string reading_mode_shortcut;
+  if (GetAcceleratorForCommandId(IDC_SHOW_READING_MODE_KEYBOARD,
+                                 &reading_mode_accelerator)) {
+    reading_mode_shortcut = reading_mode_accelerator.GetShortcutText();
+  }
+
   if (features::IsReadAnythingOmniboxChipEnabled() ||
       features::IsImmersiveReadAnythingEnabled()) {
     root_action_item_->AddChild(
@@ -335,7 +346,8 @@
                 bwi))
             .SetActionId(kActionSidePanelShowReadAnything)
             .SetText(l10n_util::GetStringUTF16(IDS_READING_MODE_TITLE))
-            .SetTooltipText(l10n_util::GetStringUTF16(IDS_READING_MODE_TITLE))
+            .SetTooltipText(l10n_util::GetStringFUTF16(IDS_READING_MODE_TOOLTIP,
+                                                       reading_mode_shortcut))
             .SetImage(ui::ImageModel::FromVectorIcon(kMenuBookChromeRefreshIcon,
                                                      ui::kColorIcon))
             .SetProperty(
@@ -346,9 +358,20 @@
             .Build());
   } else {
     root_action_item_->AddChild(
-        SidePanelAction(SidePanelEntryId::kReadAnything, IDS_READING_MODE_TITLE,
-                        IDS_READING_MODE_TITLE, kMenuBookChromeRefreshIcon,
-                        kActionSidePanelShowReadAnything, bwi, true)
+        actions::ActionItem::Builder(
+            CreateToggleSidePanelActionCallback(
+                SidePanelEntryKey(SidePanelEntryId::kReadAnything), bwi))
+            .SetActionId(kActionSidePanelShowReadAnything)
+            .SetText(l10n_util::GetStringUTF16(IDS_READING_MODE_TITLE))
+            .SetTooltipText(l10n_util::GetStringFUTF16(IDS_READING_MODE_TOOLTIP,
+                                                       reading_mode_shortcut))
+            .SetImage(ui::ImageModel::FromVectorIcon(kMenuBookChromeRefreshIcon,
+                                                     ui::kColorIcon))
+            .SetProperty(
+                actions::kActionItemPinnableKey,
+                static_cast<
+                    std::underlying_type_t<actions::ActionPinnableState>>(
+                    actions::ActionPinnableState::kPinnable))
             .Build());
   }
 
diff --git a/chrome/browser/ui/browser_command_controller_browsertest.cc b/chrome/browser/ui/browser_command_controller_browsertest.cc
index 325b1c5ec..c8c4f31 100644
--- a/chrome/browser/ui/browser_command_controller_browsertest.cc
+++ b/chrome/browser/ui/browser_command_controller_browsertest.cc
@@ -756,9 +756,9 @@
       ::prefs::kGeminiSettings,
       static_cast<int>(glic::prefs::SettingsPolicyState::kEnabled));
   // Bypass fre.
-  profile_prefs->SetInteger(
-      glic::prefs::kGlicCompletedFre,
-      static_cast<int>(glic::prefs::FreStatus::kCompleted));
+  glic::GlicKeyedService::Get(browser()->profile())
+      ->enabling()
+      .SetCompletedFre(glic::prefs::FreStatus::kCompleted);
 
   EXPECT_TRUE(chrome::ExecuteCommand(browser(), IDC_OPEN_GLIC));
   ASSERT_TRUE(
diff --git a/chrome/browser/ui/browser_navigator_params_utils.cc b/chrome/browser/ui/browser_navigator_params_utils.cc
index 139fde5..17937486 100644
--- a/chrome/browser/ui/browser_navigator_params_utils.cc
+++ b/chrome/browser/ui/browser_navigator_params_utils.cc
@@ -50,7 +50,6 @@
   return load_url_params;
 }
 
-#if !BUILDFLAG(IS_ANDROID)
 content::NavigationController::LoadURLParams LoadURLParamsFromNavigateParams(
     content::WebContents* target_contents,
     NavigateParams* params) {
@@ -66,11 +65,13 @@
         ChromeNavigationUIData::CreateForMainFrameNavigation(
             target_contents, params->is_using_https_as_default_scheme,
             force_no_https_upgrade);
+#if !BUILDFLAG(IS_ANDROID)
+    // This field is not available on Android.
     navigation_ui_data->set_navigation_initiated_from_sync(
         params->navigation_initiated_from_sync);
+#endif
     load_url_params.navigation_ui_data = std::move(navigation_ui_data);
   }
 
   return load_url_params;
 }
-#endif
diff --git a/chrome/browser/ui/browser_navigator_params_utils.h b/chrome/browser/ui/browser_navigator_params_utils.h
index 5a672009..12a01328 100644
--- a/chrome/browser/ui/browser_navigator_params_utils.h
+++ b/chrome/browser/ui/browser_navigator_params_utils.h
@@ -18,11 +18,9 @@
 content::NavigationController::LoadURLParams LoadURLParamsFromNavigateParams(
     NavigateParams* params);
 
-#if !BUILDFLAG(IS_ANDROID)
 // Same as previous but sets navigation UI data for main frame navigations.
 content::NavigationController::LoadURLParams LoadURLParamsFromNavigateParams(
     content::WebContents* target_contents,
     NavigateParams* params);
-#endif
 
 #endif  // CHROME_BROWSER_UI_BROWSER_NAVIGATOR_PARAMS_UTILS_H_
diff --git a/chrome/browser/ui/color/BUILD.gn b/chrome/browser/ui/color/BUILD.gn
index 6fff64b1..1cf5625 100644
--- a/chrome/browser/ui/color/BUILD.gn
+++ b/chrome/browser/ui/color/BUILD.gn
@@ -44,6 +44,7 @@
     "product_specifications_color_mixer.h",
     "projects_panel_color_mixer.cc",
     "projects_panel_color_mixer.h",
+    "tab_group_color_ids.h",
     "tab_strip_color_mixer.cc",
     "tab_strip_color_mixer.h",
   ]
diff --git a/chrome/browser/ui/color/chrome_color_mixer.cc b/chrome/browser/ui/color/chrome_color_mixer.cc
index 1a15932..c523192 100644
--- a/chrome/browser/ui/color/chrome_color_mixer.cc
+++ b/chrome/browser/ui/color/chrome_color_mixer.cc
@@ -13,6 +13,8 @@
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/color/chrome_color_provider_utils.h"
+#include "chrome/browser/ui/color/tab_group_color_ids.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome_color_id.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/ui_base_features.h"
@@ -129,6 +131,54 @@
   }
 }
 
+struct TabGroupColorParams {
+  ui::ColorId active_frame_id;
+  ui::ColorId inactive_frame_id;
+  ui::ColorId dialog_id;
+  ui::ColorId context_menu_id;
+  ui::ColorId saved_fg_id;
+  ui::ColorId saved_outline_id;
+  ui::ColorId bookmark_bar_id;
+
+  SkColor base_dark, base_light;
+  SkColor saved_fg_dark, saved_fg_light;
+  SkColor saved_outline_dark, saved_outline_light;
+  SkColor bookmark_bar_dark, bookmark_bar_light;
+};
+
+TabGroupColorParams CreateColorParams(bool use_alternate_palette,
+                                      ui::ColorId active,
+                                      ui::ColorId inactive,
+                                      ui::ColorId dialog,
+                                      ui::ColorId context,
+                                      ui::ColorId saved_fg,
+                                      ui::ColorId saved_outline,
+                                      ui::ColorId bookmark,
+                                      // Glow up palette
+                                      SkColor alt_dark,
+                                      SkColor alt_light,
+                                      SkColor alt_chip_dark,
+                                      SkColor alt_chip_light,
+                                      // Classic palette
+                                      SkColor base_dark,
+                                      SkColor base_light,
+                                      SkColor fg_dark,
+                                      SkColor fg_light,
+                                      SkColor outline_dark,
+                                      SkColor outline_light,
+                                      SkColor bm_dark,
+                                      SkColor bm_light) {
+  if (use_alternate_palette) {
+    return {active,        inactive, dialog,    context,       saved_fg,
+            saved_outline, bookmark, alt_dark,  alt_light,     alt_dark,
+            alt_light,     alt_dark, alt_light, alt_chip_dark, alt_chip_light};
+  } else {
+    return {active,        inactive,     dialog,        context,    saved_fg,
+            saved_outline, bookmark,     base_dark,     base_light, fg_dark,
+            fg_light,      outline_dark, outline_light, bm_dark,    bm_light};
+  }
+}
+
 }  // namespace
 
 void AddChromeColorMixer(ui::ColorProvider* provider,
@@ -137,6 +187,124 @@
       key.color_mode == ui::ColorProviderKey::ColorMode::kDark;
   ui::ColorMixer& mixer = provider->AddMixer();
 
+  const bool use_alternate_palette =
+      base::FeatureList::IsEnabled(features::kTabGroupColorRefresh);
+
+  std::vector<TabGroupColorParams> tab_group_color_params_all = {
+      CreateColorParams(
+          use_alternate_palette, kColorTabGroupTabStripFrameActiveBlue,
+          kColorTabGroupTabStripFrameInactiveBlue, kColorTabGroupDialogBlue,
+          kColorTabGroupContextMenuBlue, kColorSavedTabGroupForegroundBlue,
+          kColorSavedTabGroupOutlineBlue, kColorTabGroupBookmarkBarBlue,
+          gfx::kTabGroupBlueDarkMode, gfx::kTabGroupBlueLightMode,
+          gfx::kTabGroupBlueChipDarkMode, gfx::kTabGroupBlueChipLightMode,
+          gfx::kGoogleBlue300, gfx::kGoogleBlue600, gfx::kGoogleBlue100,
+          gfx::kGoogleBlue700, gfx::kGoogleBlue300, gfx::kGoogleBlue700,
+          SkColorSetRGB(0x39, 0x43, 0x54), gfx::kGoogleBlue050),
+      CreateColorParams(
+          use_alternate_palette, kColorTabGroupTabStripFrameActiveCyan,
+          kColorTabGroupTabStripFrameInactiveCyan, kColorTabGroupDialogCyan,
+          kColorTabGroupContextMenuCyan, kColorSavedTabGroupForegroundCyan,
+          kColorSavedTabGroupOutlineCyan, kColorTabGroupBookmarkBarCyan,
+          gfx::kTabGroupCyanDarkMode, gfx::kTabGroupCyanLightMode,
+          gfx::kTabGroupCyanChipDarkMode, gfx::kTabGroupCyanChipLightMode,
+          gfx::kGoogleCyan300, gfx::kGoogleCyan900, gfx::kGoogleCyan100,
+          gfx::kGoogleCyan900, gfx::kGoogleCyan300, gfx::kGoogleCyan900,
+          SkColorSetRGB(0x35, 0x4C, 0x51), gfx::kGoogleCyan050),
+      CreateColorParams(
+          use_alternate_palette, kColorTabGroupTabStripFrameActiveGreen,
+          kColorTabGroupTabStripFrameInactiveGreen, kColorTabGroupDialogGreen,
+          kColorTabGroupContextMenuGreen, kColorSavedTabGroupForegroundGreen,
+          kColorSavedTabGroupOutlineGreen, kColorTabGroupBookmarkBarGreen,
+          gfx::kTabGroupGreenDarkMode, gfx::kTabGroupGreenLightMode,
+          gfx::kTabGroupGreenChipDarkMode, gfx::kTabGroupGreenChipLightMode,
+          gfx::kGoogleGreen300, gfx::kGoogleGreen700, gfx::kGoogleGreen100,
+          gfx::kGoogleGreen800, gfx::kGoogleGreen300, gfx::kGoogleGreen800,
+          SkColorSetRGB(0x37, 0x48, 0x3C), gfx::kGoogleGreen050),
+      CreateColorParams(
+          use_alternate_palette, kColorTabGroupTabStripFrameActiveGrey,
+          kColorTabGroupTabStripFrameInactiveGrey, kColorTabGroupDialogGrey,
+          kColorTabGroupContextMenuGrey, kColorSavedTabGroupForegroundGrey,
+          kColorSavedTabGroupOutlineGrey, kColorTabGroupBookmarkBarGrey,
+          gfx::kTabGroupGreyDarkMode, gfx::kTabGroupGreyLightMode,
+          gfx::kTabGroupGreyChipDarkMode, gfx::kTabGroupGreyChipLightMode,
+          gfx::kGoogleGrey300, gfx::kGoogleGrey700, gfx::kGoogleGrey100,
+          gfx::kGoogleGrey700, gfx::kGoogleGrey300, gfx::kGoogleGrey700,
+          SkColorSetRGB(0x4C, 0x4D, 0x4E), gfx::kGoogleGrey100),
+      CreateColorParams(
+          use_alternate_palette, kColorTabGroupTabStripFrameActiveOrange,
+          kColorTabGroupTabStripFrameInactiveOrange, kColorTabGroupDialogOrange,
+          kColorTabGroupContextMenuOrange, kColorSavedTabGroupForegroundOrange,
+          kColorSavedTabGroupOutlineOrange, kColorTabGroupBookmarkBarOrange,
+          gfx::kTabGroupOrangeDarkMode, gfx::kTabGroupOrangeLightMode,
+          gfx::kTabGroupOrangeChipDarkMode, gfx::kTabGroupOrangeChipLightMode,
+          gfx::kGoogleOrange300, gfx::kGoogleOrange400, gfx::kGoogleOrange100,
+          gfx::kGoogleGrey800, gfx::kGoogleOrange300, gfx::kGoogleOrange800,
+          SkColorSetRGB(0x54, 0x42, 0x33), gfx::kGoogleOrange050),
+      CreateColorParams(
+          use_alternate_palette, kColorTabGroupTabStripFrameActivePink,
+          kColorTabGroupTabStripFrameInactivePink, kColorTabGroupDialogPink,
+          kColorTabGroupContextMenuPink, kColorSavedTabGroupForegroundPink,
+          kColorSavedTabGroupOutlinePink, kColorTabGroupBookmarkBarPink,
+          gfx::kTabGroupMagentaDarkMode, gfx::kTabGroupMagentaLightMode,
+          gfx::kTabGroupMagentaChipDarkMode, gfx::kTabGroupMagentaChipLightMode,
+          gfx::kGooglePink300, gfx::kGooglePink700, gfx::kGooglePink100,
+          gfx::kGooglePink800, gfx::kGooglePink300, gfx::kGooglePink700,
+          SkColorSetRGB(0x55, 0x39, 0x49), gfx::kGooglePink050),
+      CreateColorParams(
+          use_alternate_palette, kColorTabGroupTabStripFrameActivePurple,
+          kColorTabGroupTabStripFrameInactivePurple, kColorTabGroupDialogPurple,
+          kColorTabGroupContextMenuPurple, kColorSavedTabGroupForegroundPurple,
+          kColorSavedTabGroupOutlinePurple, kColorTabGroupBookmarkBarPurple,
+          gfx::kTabGroupPurpleDarkMode, gfx::kTabGroupPurpleLightMode,
+          gfx::kTabGroupPurpleChipDarkMode, gfx::kTabGroupPurpleChipLightMode,
+          gfx::kGooglePurple300, gfx::kGooglePurple500, gfx::kGooglePurple100,
+          gfx::kGooglePurple700, gfx::kGooglePurple300, gfx::kGooglePurple700,
+          SkColorSetRGB(0x47, 0x39, 0x54), gfx::kGooglePurple050),
+      CreateColorParams(
+          use_alternate_palette, kColorTabGroupTabStripFrameActiveRed,
+          kColorTabGroupTabStripFrameInactiveRed, kColorTabGroupDialogRed,
+          kColorTabGroupContextMenuRed, kColorSavedTabGroupForegroundRed,
+          kColorSavedTabGroupOutlineRed, kColorTabGroupBookmarkBarRed,
+          gfx::kTabGroupRedDarkMode, gfx::kTabGroupRedLightMode,
+          gfx::kTabGroupRedChipDarkMode, gfx::kTabGroupRedChipLightMode,
+          gfx::kGoogleRed300, gfx::kGoogleRed600, gfx::kGoogleRed100,
+          gfx::kGoogleRed700, gfx::kGoogleRed300, gfx::kGoogleRed700,
+          SkColorSetRGB(0x52, 0x39, 0x37), gfx::kGoogleRed050),
+      CreateColorParams(
+          use_alternate_palette, kColorTabGroupTabStripFrameActiveYellow,
+          kColorTabGroupTabStripFrameInactiveYellow, kColorTabGroupDialogYellow,
+          kColorTabGroupContextMenuYellow, kColorSavedTabGroupForegroundYellow,
+          kColorSavedTabGroupOutlineYellow, kColorTabGroupBookmarkBarYellow,
+          gfx::kTabGroupLimeDarkMode, gfx::kTabGroupLimeLightMode,
+          gfx::kTabGroupLimeChipDarkMode, gfx::kTabGroupLimeChipLightMode,
+          gfx::kGoogleYellow300, gfx::kGoogleYellow600, gfx::kGoogleYellow100,
+          gfx::kGoogleGrey800, gfx::kGoogleYellow300, gfx::kGoogleYellow600,
+          SkColorSetRGB(0x55, 0x4B, 0x30), gfx::kGoogleYellow050)};
+
+  for (const auto& params : tab_group_color_params_all) {
+    mixer[params.active_frame_id] =
+        ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameActive,
+                                   params.base_dark, params.base_light);
+    mixer[params.inactive_frame_id] =
+        ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameInactive,
+                                   params.base_dark, params.base_light);
+    mixer[params.context_menu_id] = SelectColorBasedOnDarkInputOrMode(
+        dark_mode, kColorBookmarkBarForeground, params.base_dark,
+        params.base_light);
+    mixer[params.saved_fg_id] =
+        ui::SelectBasedOnDarkInput(kColorBookmarkBarBackground,
+                                   params.saved_fg_dark, params.saved_fg_light);
+    mixer[params.saved_outline_id] = ui::SelectBasedOnDarkInput(
+        kColorBookmarkBarBackground, params.saved_outline_dark,
+        params.saved_outline_light);
+    mixer[params.bookmark_bar_id] = ui::SelectBasedOnDarkInput(
+        kColorBookmarkBarBackground, params.bookmark_bar_dark,
+        params.bookmark_bar_light);
+
+    mixer[params.dialog_id] = {params.context_menu_id};
+  }
+
   mixer[kColorActorUiHandoffButtonBorder] =
       SelectActorUiColorBasedOnNearWhiteInput();
   mixer[kColorActorUiOverlayBorder] = SelectActorUiColorBasedOnNearWhiteInput();
@@ -516,168 +684,6 @@
   mixer[kColorSharingRecentActivityDialogFaviconContainer] = {
       ui::kColorSysSurface};
 
-  mixer[kColorTabGroupTabStripFrameActiveBlue] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameActive,
-                                 gfx::kGoogleBlue300, gfx::kGoogleBlue600);
-  mixer[kColorTabGroupTabStripFrameActiveCyan] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameActive,
-                                 gfx::kGoogleCyan300, gfx::kGoogleCyan900);
-  mixer[kColorTabGroupTabStripFrameActiveGreen] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameActive,
-                                 gfx::kGoogleGreen300, gfx::kGoogleGreen700);
-  mixer[kColorTabGroupTabStripFrameActiveGrey] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameActive,
-                                 gfx::kGoogleGrey300, gfx::kGoogleGrey700);
-  mixer[kColorTabGroupTabStripFrameActiveOrange] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameActive,
-                                 gfx::kGoogleOrange300, gfx::kGoogleOrange400);
-  mixer[kColorTabGroupTabStripFrameActivePink] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameActive,
-                                 gfx::kGooglePink300, gfx::kGooglePink700);
-  mixer[kColorTabGroupTabStripFrameActivePurple] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameActive,
-                                 gfx::kGooglePurple300, gfx::kGooglePurple500);
-  mixer[kColorTabGroupTabStripFrameActiveRed] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameActive,
-                                 gfx::kGoogleRed300, gfx::kGoogleRed600);
-  mixer[kColorTabGroupTabStripFrameActiveYellow] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameActive,
-                                 gfx::kGoogleYellow300, gfx::kGoogleYellow600);
-
-  mixer[kColorTabGroupTabStripFrameInactiveBlue] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameInactive,
-                                 gfx::kGoogleBlue300, gfx::kGoogleBlue600);
-  mixer[kColorTabGroupTabStripFrameInactiveCyan] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameInactive,
-                                 gfx::kGoogleCyan300, gfx::kGoogleCyan900);
-  mixer[kColorTabGroupTabStripFrameInactiveGreen] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameInactive,
-                                 gfx::kGoogleGreen300, gfx::kGoogleGreen700);
-  mixer[kColorTabGroupTabStripFrameInactiveGrey] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameInactive,
-                                 gfx::kGoogleGrey300, gfx::kGoogleGrey700);
-  mixer[kColorTabGroupTabStripFrameInactiveOrange] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameInactive,
-                                 gfx::kGoogleOrange300, gfx::kGoogleOrange400);
-  mixer[kColorTabGroupTabStripFrameInactivePink] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameInactive,
-                                 gfx::kGooglePink300, gfx::kGooglePink700);
-  mixer[kColorTabGroupTabStripFrameInactivePurple] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameInactive,
-                                 gfx::kGooglePurple300, gfx::kGooglePurple500);
-  mixer[kColorTabGroupTabStripFrameInactiveRed] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameInactive,
-                                 gfx::kGoogleRed300, gfx::kGoogleRed600);
-  mixer[kColorTabGroupTabStripFrameInactiveYellow] =
-      ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameInactive,
-                                 gfx::kGoogleYellow300, gfx::kGoogleYellow600);
-
-  mixer[kColorTabGroupDialogBlue] = {kColorTabGroupContextMenuBlue};
-  mixer[kColorTabGroupDialogCyan] = {kColorTabGroupContextMenuCyan};
-  mixer[kColorTabGroupDialogGreen] = {kColorTabGroupContextMenuGreen};
-  mixer[kColorTabGroupDialogGrey] = {kColorTabGroupContextMenuGrey};
-  mixer[kColorTabGroupDialogOrange] = {kColorTabGroupContextMenuOrange};
-  mixer[kColorTabGroupDialogPink] = {kColorTabGroupContextMenuPink};
-  mixer[kColorTabGroupDialogPurple] = {kColorTabGroupContextMenuPurple};
-  mixer[kColorTabGroupDialogRed] = {kColorTabGroupContextMenuRed};
-  mixer[kColorTabGroupDialogYellow] = {kColorTabGroupContextMenuYellow};
-
-  mixer[kColorTabGroupContextMenuBlue] = SelectColorBasedOnDarkInputOrMode(
-      dark_mode, kColorBookmarkBarForeground, gfx::kGoogleBlue300,
-      gfx::kGoogleBlue600);
-  mixer[kColorTabGroupContextMenuCyan] = SelectColorBasedOnDarkInputOrMode(
-      dark_mode, kColorBookmarkBarForeground, gfx::kGoogleCyan300,
-      gfx::kGoogleCyan900);
-  mixer[kColorTabGroupContextMenuGreen] = SelectColorBasedOnDarkInputOrMode(
-      dark_mode, kColorBookmarkBarForeground, gfx::kGoogleGreen300,
-      gfx::kGoogleGreen700);
-  mixer[kColorTabGroupContextMenuGrey] = SelectColorBasedOnDarkInputOrMode(
-      dark_mode, kColorBookmarkBarForeground, gfx::kGoogleGrey300,
-      gfx::kGoogleGrey700);
-  mixer[kColorTabGroupContextMenuOrange] = SelectColorBasedOnDarkInputOrMode(
-      dark_mode, kColorBookmarkBarForeground, gfx::kGoogleOrange300,
-      gfx::kGoogleOrange400);
-  mixer[kColorTabGroupContextMenuPink] = SelectColorBasedOnDarkInputOrMode(
-      dark_mode, kColorBookmarkBarForeground, gfx::kGooglePink300,
-      gfx::kGooglePink700);
-  mixer[kColorTabGroupContextMenuPurple] = SelectColorBasedOnDarkInputOrMode(
-      dark_mode, kColorBookmarkBarForeground, gfx::kGooglePurple300,
-      gfx::kGooglePurple500);
-  mixer[kColorTabGroupContextMenuRed] =
-      SelectColorBasedOnDarkInputOrMode(dark_mode, kColorBookmarkBarForeground,
-                                        gfx::kGoogleRed300, gfx::kGoogleRed600);
-  mixer[kColorTabGroupContextMenuYellow] = SelectColorBasedOnDarkInputOrMode(
-      dark_mode, kColorBookmarkBarForeground, gfx::kGoogleYellow300,
-      gfx::kGoogleYellow600);
-
-  mixer[kColorSavedTabGroupForegroundBlue] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, gfx::kGoogleBlue100, gfx::kGoogleBlue700);
-  mixer[kColorSavedTabGroupForegroundGrey] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, gfx::kGoogleGrey100, gfx::kGoogleGrey700);
-  mixer[kColorSavedTabGroupForegroundRed] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, gfx::kGoogleRed100, gfx::kGoogleRed700);
-  mixer[kColorSavedTabGroupForegroundGreen] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, gfx::kGoogleGreen100, gfx::kGoogleGreen800);
-  mixer[kColorSavedTabGroupForegroundYellow] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, gfx::kGoogleYellow100, gfx::kGoogleGrey800);
-  mixer[kColorSavedTabGroupForegroundCyan] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, gfx::kGoogleCyan100, gfx::kGoogleCyan900);
-  mixer[kColorSavedTabGroupForegroundPurple] =
-      ui::SelectBasedOnDarkInput(kColorBookmarkBarBackground,
-                                 gfx::kGooglePurple100, gfx::kGooglePurple700);
-  mixer[kColorSavedTabGroupForegroundPink] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, gfx::kGooglePink100, gfx::kGooglePink800);
-  mixer[kColorSavedTabGroupForegroundOrange] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, gfx::kGoogleOrange100, gfx::kGoogleGrey800);
-
-  mixer[kColorSavedTabGroupOutlineBlue] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, gfx::kGoogleBlue300, gfx::kGoogleBlue700);
-  mixer[kColorSavedTabGroupOutlineGrey] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, gfx::kGoogleGrey300, gfx::kGoogleGrey700);
-  mixer[kColorSavedTabGroupOutlineRed] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, gfx::kGoogleRed300, gfx::kGoogleRed700);
-  mixer[kColorSavedTabGroupOutlineGreen] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, gfx::kGoogleGreen300, gfx::kGoogleGreen800);
-  mixer[kColorSavedTabGroupOutlineYellow] =
-      ui::SelectBasedOnDarkInput(kColorBookmarkBarBackground,
-                                 gfx::kGoogleYellow300, gfx::kGoogleYellow600);
-  mixer[kColorSavedTabGroupOutlineCyan] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, gfx::kGoogleCyan300, gfx::kGoogleCyan900);
-  mixer[kColorSavedTabGroupOutlinePurple] =
-      ui::SelectBasedOnDarkInput(kColorBookmarkBarBackground,
-                                 gfx::kGooglePurple300, gfx::kGooglePurple700);
-  mixer[kColorSavedTabGroupOutlinePink] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, gfx::kGooglePink300, gfx::kGooglePink700);
-  mixer[kColorSavedTabGroupOutlineOrange] =
-      ui::SelectBasedOnDarkInput(kColorBookmarkBarBackground,
-                                 gfx::kGoogleOrange300, gfx::kGoogleOrange800);
-  mixer[kColorTabGroupBookmarkBarBlue] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, SkColorSetRGB(0x39, 0x43, 0x54),
-      gfx::kGoogleBlue050);
-  mixer[kColorTabGroupBookmarkBarCyan] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, SkColorSetRGB(0x35, 0x4C, 0x51),
-      gfx::kGoogleCyan050);
-  mixer[kColorTabGroupBookmarkBarGreen] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, SkColorSetRGB(0x37, 0x48, 0x3C),
-      gfx::kGoogleGreen050);
-  mixer[kColorTabGroupBookmarkBarGrey] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, SkColorSetRGB(0x4C, 0x4D, 0x4E),
-      gfx::kGoogleGrey100);
-  mixer[kColorTabGroupBookmarkBarPink] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, SkColorSetRGB(0x55, 0x39, 0x49),
-      gfx::kGooglePink050);
-  mixer[kColorTabGroupBookmarkBarPurple] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, SkColorSetRGB(0x47, 0x39, 0x54),
-      gfx::kGooglePurple050);
-  mixer[kColorTabGroupBookmarkBarRed] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, SkColorSetRGB(0x52, 0x39, 0x37),
-      gfx::kGoogleRed050);
-  mixer[kColorTabGroupBookmarkBarYellow] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, SkColorSetRGB(0x55, 0x4B, 0x30),
-      gfx::kGoogleYellow050);
-  mixer[kColorTabGroupBookmarkBarOrange] = ui::SelectBasedOnDarkInput(
-      kColorBookmarkBarBackground, SkColorSetRGB(0x54, 0x42, 0x33),
-      gfx::kGoogleOrange050);
 
   mixer[kColorTabHoverCardBackground] = {dark_mode ? gfx::kGoogleGrey900
                                                    : gfx::kGoogleGrey050};
@@ -727,61 +733,6 @@
       ui::GetColorWithMaxContrast(kColorThumbnailTabBackground);
   mixer[kColorThumbnailTabStripBackgroundActive] = {ui::kColorFrameActive};
   mixer[kColorThumbnailTabStripBackgroundInactive] = {ui::kColorFrameInactive};
-  mixer[kColorThumbnailTabStripTabGroupFrameActiveBlue] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundActive,
-                                 gfx::kGoogleBlue300, gfx::kGoogleBlue600);
-  mixer[kColorThumbnailTabStripTabGroupFrameActiveCyan] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundActive,
-                                 gfx::kGoogleCyan300, gfx::kGoogleCyan900);
-  mixer[kColorThumbnailTabStripTabGroupFrameActiveGreen] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundActive,
-                                 gfx::kGoogleGreen300, gfx::kGoogleGreen700);
-  mixer[kColorThumbnailTabStripTabGroupFrameActiveGrey] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundActive,
-                                 gfx::kGoogleGrey300, gfx::kGoogleGrey700);
-  mixer[kColorThumbnailTabStripTabGroupFrameActiveOrange] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundActive,
-                                 gfx::kGoogleOrange300, gfx::kGoogleOrange400);
-  mixer[kColorThumbnailTabStripTabGroupFrameActivePink] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundActive,
-                                 gfx::kGooglePink300, gfx::kGooglePink700);
-  mixer[kColorThumbnailTabStripTabGroupFrameActivePurple] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundActive,
-                                 gfx::kGooglePurple300, gfx::kGooglePurple500);
-  mixer[kColorThumbnailTabStripTabGroupFrameActiveRed] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundActive,
-                                 gfx::kGoogleRed300, gfx::kGoogleRed600);
-  mixer[kColorThumbnailTabStripTabGroupFrameActiveYellow] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundActive,
-                                 gfx::kGoogleYellow300, gfx::kGoogleYellow600);
-  mixer[kColorThumbnailTabStripTabGroupFrameInactiveBlue] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundInactive,
-                                 gfx::kGoogleBlue300, gfx::kGoogleBlue600);
-  mixer[kColorThumbnailTabStripTabGroupFrameInactiveCyan] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundInactive,
-                                 gfx::kGoogleCyan300, gfx::kGoogleCyan900);
-  mixer[kColorThumbnailTabStripTabGroupFrameInactiveGreen] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundInactive,
-                                 gfx::kGoogleGreen300, gfx::kGoogleGreen700);
-  mixer[kColorThumbnailTabStripTabGroupFrameInactiveGrey] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundInactive,
-                                 gfx::kGoogleGrey300, gfx::kGoogleGrey700);
-  mixer[kColorThumbnailTabStripTabGroupFrameInactiveOrange] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundInactive,
-                                 gfx::kGoogleOrange300, gfx::kGoogleOrange400);
-  mixer[kColorThumbnailTabStripTabGroupFrameInactivePink] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundInactive,
-                                 gfx::kGooglePink300, gfx::kGooglePink700);
-  mixer[kColorThumbnailTabStripTabGroupFrameInactivePurple] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundInactive,
-                                 gfx::kGooglePurple300, gfx::kGooglePurple500);
-  mixer[kColorThumbnailTabStripTabGroupFrameInactiveRed] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundInactive,
-                                 gfx::kGoogleRed300, gfx::kGoogleRed600);
-  mixer[kColorThumbnailTabStripTabGroupFrameInactiveYellow] =
-      ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundInactive,
-                                 gfx::kGoogleYellow300, gfx::kGoogleYellow600);
-
   mixer[kColorToolbar] = {dark_mode ? SkColorSetRGB(0x35, 0x36, 0x3A)
                                     : SK_ColorWHITE};
   mixer[kColorToolbarButtonBackgroundHighlightedDefault] =
diff --git a/chrome/browser/ui/color/tab_group_color_ids.h b/chrome/browser/ui/color/tab_group_color_ids.h
new file mode 100644
index 0000000..bad054e
--- /dev/null
+++ b/chrome/browser/ui/color/tab_group_color_ids.h
@@ -0,0 +1,78 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_COLOR_TAB_GROUP_COLOR_IDS_H_
+#define CHROME_BROWSER_UI_COLOR_TAB_GROUP_COLOR_IDS_H_
+
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/color_palette.h"
+
+namespace gfx {
+
+constexpr SkColor kTabGroupBlueDarkMode = SkColorSetRGB(0xC8, 0xD3, 0xFF);
+constexpr SkColor kTabGroupBlueLightMode = SkColorSetRGB(0x32, 0x5C, 0xCD);
+constexpr SkColor kTabGroupBlueChipLightMode =
+    SkColorSetA(kTabGroupBlueDarkMode, 0x80);
+constexpr SkColor kTabGroupBlueChipDarkMode =
+    SkColorSetA(kTabGroupBlueLightMode, 0x80);
+
+constexpr SkColor kTabGroupRedDarkMode = SkColorSetRGB(0xFF, 0xC7, 0xC1);
+constexpr SkColor kTabGroupRedLightMode = SkColorSetRGB(0xDF, 0x00, 0x0C);
+constexpr SkColor kTabGroupRedChipLightMode =
+    SkColorSetA(kTabGroupRedDarkMode, 0x80);
+constexpr SkColor kTabGroupRedChipDarkMode =
+    SkColorSetA(kTabGroupRedLightMode, 0x80);
+
+constexpr SkColor kTabGroupGreenDarkMode = SkColorSetRGB(0xC6, 0xFF, 0xC7);
+constexpr SkColor kTabGroupGreenLightMode = SkColorSetRGB(0x1A, 0x77, 0x36);
+constexpr SkColor kTabGroupGreenChipLightMode =
+    SkColorSetA(kTabGroupGreenDarkMode, 0x80);
+constexpr SkColor kTabGroupGreenChipDarkMode =
+    SkColorSetA(kTabGroupGreenLightMode, 0x80);
+
+constexpr SkColor kTabGroupGreyDarkMode = SkColorSetRGB(0xD0, 0xD5, 0xDD);
+constexpr SkColor kTabGroupGreyLightMode = SkColorSetRGB(0x49, 0x4D, 0x52);
+constexpr SkColor kTabGroupGreyChipLightMode =
+    SkColorSetA(kTabGroupGreyDarkMode, 0x80);
+constexpr SkColor kTabGroupGreyChipDarkMode =
+    SkColorSetA(kTabGroupGreyLightMode, 0x80);
+
+constexpr SkColor kTabGroupOrangeDarkMode = SkColorSetRGB(0xFF, 0xDE, 0xA7);
+constexpr SkColor kTabGroupOrangeLightMode = SkColorSetRGB(0xCC, 0x4E, 0x00);
+constexpr SkColor kTabGroupOrangeChipLightMode =
+    SkColorSetA(kTabGroupOrangeDarkMode, 0x80);
+constexpr SkColor kTabGroupOrangeChipDarkMode =
+    SkColorSetA(kTabGroupOrangeLightMode, 0x80);
+
+constexpr SkColor kTabGroupPurpleDarkMode = SkColorSetRGB(0xE0, 0xCB, 0xFF);
+constexpr SkColor kTabGroupPurpleLightMode = SkColorSetRGB(0x7C, 0x31, 0xE6);
+constexpr SkColor kTabGroupPurpleChipLightMode =
+    SkColorSetA(kTabGroupPurpleDarkMode, 0x80);
+constexpr SkColor kTabGroupPurpleChipDarkMode =
+    SkColorSetA(kTabGroupPurpleLightMode, 0x80);
+
+constexpr SkColor kTabGroupCyanDarkMode = SkColorSetRGB(0xC7, 0xFA, 0xFF);
+constexpr SkColor kTabGroupCyanLightMode = SkColorSetRGB(0x05, 0x7C, 0x84);
+constexpr SkColor kTabGroupCyanChipLightMode =
+    SkColorSetA(kTabGroupCyanDarkMode, 0x80);
+constexpr SkColor kTabGroupCyanChipDarkMode =
+    SkColorSetA(kTabGroupCyanLightMode, 0x80);
+
+constexpr SkColor kTabGroupMagentaDarkMode = SkColorSetRGB(0xFF, 0xC3, 0xE8);
+constexpr SkColor kTabGroupMagentaLightMode = SkColorSetRGB(0xCC, 0x06, 0xAB);
+constexpr SkColor kTabGroupMagentaChipLightMode =
+    SkColorSetA(kTabGroupMagentaDarkMode, 0x80);
+constexpr SkColor kTabGroupMagentaChipDarkMode =
+    SkColorSetA(kTabGroupMagentaLightMode, 0x80);
+
+constexpr SkColor kTabGroupLimeDarkMode = SkColorSetRGB(0xEE, 0xFF, 0xA5);
+constexpr SkColor kTabGroupLimeLightMode = SkColorSetRGB(0x63, 0x75, 0x05);
+constexpr SkColor kTabGroupLimeChipLightMode =
+    SkColorSetA(kTabGroupLimeDarkMode, 0x80);
+constexpr SkColor kTabGroupLimeChipDarkMode =
+    SkColorSetA(kTabGroupLimeLightMode, 0x80);
+
+}  // namespace gfx
+
+#endif  // CHROME_BROWSER_UI_COLOR_TAB_GROUP_COLOR_IDS_H_
diff --git a/chrome/browser/ui/extensions/BUILD.gn b/chrome/browser/ui/extensions/BUILD.gn
index 13dd9db1..cbecd97 100644
--- a/chrome/browser/ui/extensions/BUILD.gn
+++ b/chrome/browser/ui/extensions/BUILD.gn
@@ -32,6 +32,7 @@
       "extensions_dialogs.h",
       "extensions_menu_view_model.h",
       "extensions_toolbar_view_model.h",
+      "icon_with_badge_image_source.h",
       "reload_page_dialog_controller.h",
       "settings_overridden_dialog.h",
     ]
@@ -53,10 +54,13 @@
       "//chrome/browser/ui/toolbar",
       "//extensions/browser",
       "//extensions/common",
+      "//skia",
       "//ui/base:types",
       "//ui/base/interaction",
       "//ui/base/mojom:ui_base_types",
       "//ui/base/unowned_user_data",
+      "//ui/gfx",
+      "//ui/gfx/geometry",
     ]
   }
 
@@ -70,7 +74,6 @@
       "extension_side_panel_utils.h",
       "extensions_overrides/simple_overrides.h",
       "hosted_app_browser_controller.h",
-      "icon_with_badge_image_source.h",
       "installation_error_infobar_delegate.h",
       "mv2_disabled_dialog_controller.h",
       "settings_api_bubble_helpers.h",
@@ -145,6 +148,7 @@
       "extensions_container.cc",
       "extensions_menu_view_model.cc",
       "extensions_toolbar_view_model.cc",
+      "icon_with_badge_image_source.cc",
       "reload_page_dialog_controller.cc",
       "settings_overridden_dialog.cc",
       "upload_extension_to_account_dialog.cc",
@@ -159,6 +163,7 @@
       "//chrome/browser/signin",
       "//chrome/browser/tab_list",
       "//chrome/browser/ui/browser_window",
+      "//chrome/browser/ui/color:color_headers",
       "//chrome/browser/ui/dialogs",
       "//chrome/browser/ui/toolbar",
       "//components/image_fetcher/core",
@@ -214,7 +219,6 @@
       "extension_settings_overridden_dialog.cc",
       "extensions_overrides/simple_overrides.cc",
       "hosted_app_browser_controller.cc",
-      "icon_with_badge_image_source.cc",
       "installation_error_infobar_delegate.cc",
       "mv2_disabled_dialog_controller.cc",
       "settings_api_bubble_helpers.cc",
diff --git a/chrome/browser/ui/lens/lens_composebox_controller.cc b/chrome/browser/ui/lens/lens_composebox_controller.cc
index 1bac952..e37afc90 100644
--- a/chrome/browser/ui/lens/lens_composebox_controller.cc
+++ b/chrome/browser/ui/lens/lens_composebox_controller.cc
@@ -67,10 +67,7 @@
   composebox_handler_ = std::make_unique<LensComposeboxHandler>(
       this, profile_, lens_search_controller_->GetTabInterface()->GetContents(),
       std::move(pending_handler), std::move(pending_page),
-      std::move(pending_searchbox_handler));
-
-  // TODO(crbug.com/435288212): Move searchbox mojom to use factory pattern.
-  composebox_handler_->SetPage(std::move(pending_searchbox_page));
+      std::move(pending_searchbox_handler), std::move(pending_searchbox_page));
 
   // Set the visual selection context if it was already made before the
   // composebox was bound.
diff --git a/chrome/browser/ui/lens/lens_composebox_handler.cc b/chrome/browser/ui/lens/lens_composebox_handler.cc
index 0ca7cdd..1b72fa671 100644
--- a/chrome/browser/ui/lens/lens_composebox_handler.cc
+++ b/chrome/browser/ui/lens/lens_composebox_handler.cc
@@ -120,9 +120,11 @@
     mojo::PendingReceiver<composebox::mojom::PageHandler> pending_handler,
     mojo::PendingRemote<composebox::mojom::Page> pending_page,
     mojo::PendingReceiver<searchbox::mojom::PageHandler>
-        pending_searchbox_handler)
+        pending_searchbox_handler,
+    mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page)
     : SearchboxHandler(
           std::move(pending_searchbox_handler),
+          std::move(pending_searchbox_page),
           profile,
           web_contents,
           std::make_unique<OmniboxController>(
diff --git a/chrome/browser/ui/lens/lens_composebox_handler.h b/chrome/browser/ui/lens/lens_composebox_handler.h
index 3f670847..b85f5e4 100644
--- a/chrome/browser/ui/lens/lens_composebox_handler.h
+++ b/chrome/browser/ui/lens/lens_composebox_handler.h
@@ -34,7 +34,8 @@
       mojo::PendingReceiver<composebox::mojom::PageHandler> pending_handler,
       mojo::PendingRemote<composebox::mojom::Page> pending_page,
       mojo::PendingReceiver<searchbox::mojom::PageHandler>
-          pending_searchbox_handler);
+          pending_searchbox_handler,
+      mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page);
   ~LensComposeboxHandler() override;
 
   // composebox::mojom::PageHandler:
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.cc b/chrome/browser/ui/lens/lens_overlay_controller.cc
index f2317213..38221172 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller.cc
@@ -685,6 +685,8 @@
 
 void LensOverlayController::ShowUI(
     lens::LensOverlayInvocationSource invocation_source) {
+  invocation_source_ = invocation_source;
+
   if (!CanShowModalUI()) {
     return;
   }
@@ -707,9 +709,6 @@
   pref_service_->SetInteger(prefs::kLensOverlayStartCount,
                             lens_overlay_start_count + 1);
 
-  // Store reference for later use.
-  invocation_source_ = invocation_source;
-
   // Create the languages controller.
   languages_controller_ =
       std::make_unique<lens::LensOverlayLanguagesController>(profile);
diff --git a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
index e188980..ec0f1e1 100644
--- a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
+++ b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
@@ -364,14 +364,9 @@
 }
 
 void LensOverlayUntrustedUI::BindInterface(
-    mojo::PendingReceiver<searchbox::mojom::PageHandler> receiver) {
-  LensSearchboxController* controller =
-      GetLensSearchController().lens_searchbox_controller();
-
-  auto handler = std::make_unique<LensSearchboxHandler>(
-      std::move(receiver), Profile::FromWebUI(web_ui()),
-      web_ui()->GetWebContents(), /*lens_searchbox_client=*/controller);
-  controller->SetContextualSearchboxHandler(std::move(handler));
+    mojo::PendingReceiver<searchbox::mojom::PageHandlerFactory> receiver) {
+  searchbox_page_factory_receiver_.reset();
+  searchbox_page_factory_receiver_.Bind(std::move(receiver));
 }
 
 void LensOverlayUntrustedUI::BindInterface(
@@ -398,6 +393,18 @@
 }
 
 void LensOverlayUntrustedUI::CreatePageHandler(
+    mojo::PendingRemote<searchbox::mojom::Page> page,
+    mojo::PendingReceiver<searchbox::mojom::PageHandler> receiver) {
+  LensSearchboxController* controller =
+      GetLensSearchController().lens_searchbox_controller();
+
+  auto handler = std::make_unique<LensSearchboxHandler>(
+      std::move(receiver), std::move(page), Profile::FromWebUI(web_ui()),
+      web_ui()->GetWebContents(), /*lens_searchbox_client=*/controller);
+  controller->SetContextualSearchboxHandler(std::move(handler));
+}
+
+void LensOverlayUntrustedUI::CreatePageHandler(
     mojo::PendingReceiver<lens::mojom::LensPageHandler> receiver,
     mojo::PendingRemote<lens::mojom::LensPage> page) {
   LensOverlayController& controller = GetLensOverlayController();
diff --git a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.h b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.h
index c0e57c7..b847925a 100644
--- a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.h
+++ b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.h
@@ -11,7 +11,7 @@
 #include "chrome/browser/ui/webui/top_chrome/top_chrome_webui_config.h"
 #include "chrome/browser/ui/webui/top_chrome/untrusted_top_chrome_web_ui_controller.h"
 #include "chrome/common/webui_url_constants.h"
-#include "components/omnibox/browser/searchbox.mojom-forward.h"
+#include "components/omnibox/browser/searchbox.mojom.h"
 #include "components/user_education/webui/help_bubble_handler.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_ui_data_source.h"
@@ -36,7 +36,8 @@
     : public UntrustedTopChromeWebUIController,
       public lens::mojom::LensPageHandlerFactory,
       public lens::mojom::LensGhostLoaderPageHandlerFactory,
-      public help_bubble::mojom::HelpBubbleHandlerFactory {
+      public help_bubble::mojom::HelpBubbleHandlerFactory,
+      public searchbox::mojom::PageHandlerFactory {
  public:
   explicit LensOverlayUntrustedUI(content::WebUI* web_ui);
 
@@ -56,10 +57,10 @@
       mojo::PendingReceiver<lens::mojom::LensGhostLoaderPageHandlerFactory>
           pending_receiver);
 
-  // Instantiates the implementor of the searchbox::mojom::PageHandler mojo
-  // interface passing the pending receiver that will be internally bound.
+  // Instantiates the implementor of the searchbox::mojom::PageHandlerFactory
+  // mojo interface passing the pending receiver that will be internally bound.
   void BindInterface(
-      mojo::PendingReceiver<searchbox::mojom::PageHandler> receiver);
+      mojo::PendingReceiver<searchbox::mojom::PageHandlerFactory> receiver);
 
   // Instantiates the implementor of the help_bubble::mojom::HelpBubbleHandler
   // mojo interface passing the pending receiver that will be internally bound.
@@ -87,6 +88,10 @@
       mojo::PendingRemote<help_bubble::mojom::HelpBubbleClient> client,
       mojo::PendingReceiver<help_bubble::mojom::HelpBubbleHandler> handler)
       override;
+  // searchbox::mojom::PageHandlerFactory:
+  void CreatePageHandler(
+      mojo::PendingRemote<searchbox::mojom::Page> page,
+      mojo::PendingReceiver<searchbox::mojom::PageHandler> handler) override;
 
   mojo::Receiver<lens::mojom::LensPageHandlerFactory>
       lens_page_factory_receiver_{this};
@@ -95,6 +100,8 @@
   std::unique_ptr<user_education::HelpBubbleHandler> help_bubble_handler_;
   mojo::Receiver<help_bubble::mojom::HelpBubbleHandlerFactory>
       help_bubble_handler_factory_receiver_{this};
+  mojo::Receiver<searchbox::mojom::PageHandlerFactory>
+      searchbox_page_factory_receiver_{this};
 
   base::WeakPtrFactory<LensOverlayUntrustedUI> weak_factory_{this};
 
diff --git a/chrome/browser/ui/lens/lens_query_flow_router.cc b/chrome/browser/ui/lens/lens_query_flow_router.cc
index ee1de64..f2610432 100644
--- a/chrome/browser/ui/lens/lens_query_flow_router.cc
+++ b/chrome/browser/ui/lens/lens_query_flow_router.cc
@@ -476,6 +476,20 @@
     pending_session_handle_->NotifySessionStarted();
   }
 
+  // If the request is not going to load in AIM, start the task ui right away
+  // to show the ghost loader while the request is being uploaded.
+  if (request_info->search_url_type != SearchUrlType::kAim &&
+      request_info->invocation_source !=
+          lens::LensOverlayInvocationSource::kContextualTasksComposebox &&
+      pending_session_handle_) {
+    auto* service =
+        contextual_tasks::ContextualTasksUiServiceFactory::GetForBrowserContext(
+            web_contents()->GetBrowserContext());
+    service->InitSidePanelWithGhostLoader(browser_window_interface(),
+                                          tab_interface(),
+                                          std::move(pending_session_handle_));
+  }
+
   if (!overlay_tab_context_file_token_.has_value()) {
     pending_search_url_request_ = std::move(request_info);
     // Upload the page context when creating a session handle.
diff --git a/chrome/browser/ui/lens/lens_query_flow_router_unittest.cc b/chrome/browser/ui/lens/lens_query_flow_router_unittest.cc
index 7d7088e3..160681bb 100644
--- a/chrome/browser/ui/lens/lens_query_flow_router_unittest.cc
+++ b/chrome/browser/ui/lens/lens_query_flow_router_unittest.cc
@@ -145,7 +145,10 @@
         profile->GetPrefs());
     viewport_screenshot_.allocN32Pixels(10, 10);
   }
-  ~TestLensQueryFlowRouter() override = default;
+  ~TestLensQueryFlowRouter() override {
+    raw_mock_session_handle_ = nullptr;
+    side_panel_session_handle_ = nullptr;
+  }
 
   std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
   CreateContextualSearchSessionHandle() override {
@@ -168,8 +171,17 @@
     side_panel_session_handle_ = handle;
   }
 
+  void SetTransferredSessionHandle(
+      std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+          handle) {
+    transferred_session_handle_ = std::move(handle);
+  }
+
   contextual_search::ContextualSearchSessionHandle*
   GetContextualSearchSessionHandle() const override {
+    if (transferred_session_handle_) {
+      return transferred_session_handle_.get();
+    }
     if (side_panel_session_handle_) {
       return side_panel_session_handle_;
     }
@@ -200,6 +212,8 @@
   // test will seg fault.
   raw_ptr<contextual_search::MockContextualSearchSessionHandle>
       raw_mock_session_handle_;
+  std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+      transferred_session_handle_;
   raw_ptr<contextual_search::ContextualSearchSessionHandle>
       side_panel_session_handle_ = nullptr;
   raw_ptr<TabContextualizationController> tab_contextualization_controller_ =
@@ -257,6 +271,14 @@
               (override));
 
   MOCK_METHOD(void,
+              InitSidePanelWithGhostLoader,
+              (BrowserWindowInterface * browser_window_interface,
+               tabs::TabInterface* tab_interface,
+               std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+                   session_handle),
+              (override));
+
+  MOCK_METHOD(void,
               StartTaskUiInSidePanelWithErrorPage,
               (BrowserWindowInterface * browser_window_interface,
                tabs::TabInterface* tab_interface,
@@ -928,8 +950,13 @@
               StartTaskUiInSidePanelWithErrorPage(
                   mock_browser_window_interface_.get(), &mock_tab_interface_,
                   testing::Pointer(router.mock_session_handle())))
-      .WillOnce(testing::InvokeWithoutArgs(
-          [&router]() { router.ClearMockSessionHandle(); }));
+      .WillOnce(
+          [&router](
+              BrowserWindowInterface*, tabs::TabInterface*,
+              std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+                  handle) {
+            router.SetTransferredSessionHandle(std::move(handle));
+          });
 
   // Assert: Expect CreateSearchUrl to NOT be called.
   EXPECT_CALL(*router.mock_session_handle(), CreateSearchUrl(_, _)).Times(0);
@@ -985,8 +1012,13 @@
               StartTaskUiInSidePanelWithErrorPage(
                   mock_browser_window_interface_.get(), &mock_tab_interface_,
                   testing::Pointer(router.mock_session_handle())))
-      .WillOnce(testing::InvokeWithoutArgs(
-          [&router]() { router.ClearMockSessionHandle(); }));
+      .WillOnce(
+          [&router](
+              BrowserWindowInterface*, tabs::TabInterface*,
+              std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+                  handle) {
+            router.SetTransferredSessionHandle(std::move(handle));
+          });
 
   // Assert: Expect CreateSearchUrl to NOT be called.
   EXPECT_CALL(*router.mock_session_handle(), CreateSearchUrl(_, _)).Times(0);
@@ -1045,8 +1077,13 @@
               StartTaskUiInSidePanelWithErrorPage(
                   mock_browser_window_interface_.get(), &mock_tab_interface_,
                   testing::Pointer(router.mock_session_handle())))
-      .WillOnce(testing::InvokeWithoutArgs(
-          [&router]() { router.ClearMockSessionHandle(); }));
+      .WillOnce(
+          [&router](
+              BrowserWindowInterface*, tabs::TabInterface*,
+              std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+                  handle) {
+            router.SetTransferredSessionHandle(std::move(handle));
+          });
 
   // Assert: Expect CreateSearchUrl to NOT be called.
   EXPECT_CALL(*router.mock_session_handle(), CreateSearchUrl(_, _)).Times(0);
@@ -1120,12 +1157,23 @@
   // Clear the mock session handle when the side panel is opened to avoid a
   // dangling pointer.
   EXPECT_CALL(*service,
-              StartTaskUiInSidePanel(
+              InitSidePanelWithGhostLoader(
                   mock_browser_window_interface_.get(), &mock_tab_interface_,
-                  GURL("https://www.google.com/search?q=test"),
                   testing::Pointer(router.mock_session_handle())))
-      .WillOnce(testing::InvokeWithoutArgs(
-          [&router]() { router.ClearMockSessionHandle(); }));
+      .WillOnce(
+          [&router](
+              BrowserWindowInterface*, tabs::TabInterface*,
+              std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+                  handle) {
+            router.SetTransferredSessionHandle(std::move(handle));
+          });
+
+  EXPECT_CALL(
+      *service,
+      StartTaskUiInSidePanel(
+          mock_browser_window_interface_.get(), &mock_tab_interface_,
+          GURL("https://www.google.com/search?q=test"), testing::IsNull()))
+      .Times(1);
   EXPECT_CALL(*mock_tab_contextualization_controller_, GetPageContext(_))
       .WillOnce([](lens::TabContextualizationController::GetPageContextCallback
                        callback) { std::move(callback).Run(nullptr); });
@@ -1183,8 +1231,13 @@
               StartTaskUiInSidePanelWithErrorPage(
                   mock_browser_window_interface_.get(), &mock_tab_interface_,
                   testing::Pointer(router.mock_session_handle())))
-      .WillOnce(testing::InvokeWithoutArgs(
-          [&router]() { router.ClearMockSessionHandle(); }));
+      .WillOnce(
+          [&router](
+              BrowserWindowInterface*, tabs::TabInterface*,
+              std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+                  handle) {
+            router.SetTransferredSessionHandle(std::move(handle));
+          });
 
   // Assert: Expect CreateSearchUrl to NOT be called.
   EXPECT_CALL(*router.mock_session_handle(), CreateSearchUrl(_, _)).Times(0);
@@ -1274,12 +1327,23 @@
       contextual_tasks::ContextualTasksUiServiceFactory::GetForBrowserContext(
           profile_.get()));
   EXPECT_CALL(*service,
-              StartTaskUiInSidePanel(
+              InitSidePanelWithGhostLoader(
                   mock_browser_window_interface_.get(), &mock_tab_interface_,
-                  GURL("https://www.google.com/search?q=test"),
                   testing::Pointer(router.mock_session_handle())))
-      .WillOnce(testing::InvokeWithoutArgs(
-          [&router]() { router.ClearMockSessionHandle(); }));
+      .WillOnce(
+          [&router](
+              BrowserWindowInterface*, tabs::TabInterface*,
+              std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+                  handle) {
+            router.SetTransferredSessionHandle(std::move(handle));
+          });
+
+  EXPECT_CALL(
+      *service,
+      StartTaskUiInSidePanel(
+          mock_browser_window_interface_.get(), &mock_tab_interface_,
+          GURL("https://www.google.com/search?q=test"), testing::IsNull()))
+      .Times(1);
 
   // Act: Call the method.
   router.SendRegionSearch(query_start_time, std::move(region), selection_type,
@@ -1350,12 +1414,23 @@
   // Clear the mock session handle when the side panel is opened to avoid a
   // dangling pointer.
   EXPECT_CALL(*service,
-              StartTaskUiInSidePanel(
+              InitSidePanelWithGhostLoader(
                   mock_browser_window_interface_.get(), &mock_tab_interface_,
-                  GURL("https://www.google.com/search?q=test"),
                   testing::Pointer(router.mock_session_handle())))
-      .WillOnce(testing::InvokeWithoutArgs(
-          [&router]() { router.ClearMockSessionHandle(); }));
+      .WillOnce(
+          [&router](
+              BrowserWindowInterface*, tabs::TabInterface*,
+              std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+                  handle) {
+            router.SetTransferredSessionHandle(std::move(handle));
+          });
+
+  EXPECT_CALL(
+      *service,
+      StartTaskUiInSidePanel(
+          mock_browser_window_interface_.get(), &mock_tab_interface_,
+          GURL("https://www.google.com/search?q=test"), testing::IsNull()))
+      .Times(1);
   EXPECT_CALL(*mock_tab_contextualization_controller_, GetPageContext(_))
       .WillOnce([](lens::TabContextualizationController::GetPageContextCallback
                        callback) { std::move(callback).Run(nullptr); });
@@ -1512,15 +1587,20 @@
   auto* service = static_cast<MockContextualTasksUiService*>(
       contextual_tasks::ContextualTasksUiServiceFactory::GetForBrowserContext(
           profile_.get()));
-  // Clear the mock session handle when the side panel is opened to avoid a
-  // dangling pointer.
+  // Expect StartTaskUiInSidePanel to be called with the real URL and the
+  // session handle.
   EXPECT_CALL(*service,
               StartTaskUiInSidePanel(
                   mock_browser_window_interface_.get(), &mock_tab_interface_,
                   GURL("https://www.google.com/search?q=test"),
                   testing::Pointer(router.mock_session_handle())))
-      .WillOnce(testing::InvokeWithoutArgs(
-          [&router]() { router.ClearMockSessionHandle(); }));
+      .WillOnce(
+          [&router](
+              BrowserWindowInterface*, tabs::TabInterface*, const GURL&,
+              std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+                  handle) {
+            router.SetTransferredSessionHandle(std::move(handle));
+          });
   EXPECT_CALL(*mock_tab_contextualization_controller_, GetPageContext(_))
       .WillOnce([](lens::TabContextualizationController::GetPageContextCallback
                        callback) { std::move(callback).Run(nullptr); });
@@ -1595,12 +1675,23 @@
   // Clear the mock session handle when the side panel is opened to avoid a
   // dangling pointer.
   EXPECT_CALL(*service,
-              StartTaskUiInSidePanel(
+              InitSidePanelWithGhostLoader(
                   mock_browser_window_interface_.get(), &mock_tab_interface_,
-                  GURL("https://www.google.com/search?q=test"),
                   testing::Pointer(router.mock_session_handle())))
-      .WillOnce(testing::InvokeWithoutArgs(
-          [&router]() { router.ClearMockSessionHandle(); }));
+      .WillOnce(
+          [&router](
+              BrowserWindowInterface*, tabs::TabInterface*,
+              std::unique_ptr<contextual_search::ContextualSearchSessionHandle>
+                  handle) {
+            router.SetTransferredSessionHandle(std::move(handle));
+          });
+
+  EXPECT_CALL(
+      *service,
+      StartTaskUiInSidePanel(
+          mock_browser_window_interface_.get(), &mock_tab_interface_,
+          GURL("https://www.google.com/search?q=test"), testing::IsNull()))
+      .Times(1);
   EXPECT_CALL(*mock_tab_contextualization_controller_, GetPageContext(_))
       .WillOnce([](lens::TabContextualizationController::GetPageContextCallback
                        callback) { std::move(callback).Run(nullptr); });
diff --git a/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.cc b/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.cc
index a1b9b4927..f358dfb 100644
--- a/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.cc
+++ b/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.cc
@@ -276,14 +276,9 @@
 }
 
 void LensSidePanelUntrustedUI::BindInterface(
-    mojo::PendingReceiver<searchbox::mojom::PageHandler> receiver) {
-  LensSearchboxController* controller =
-      GetLensSearchController().lens_searchbox_controller();
-
-  auto handler = std::make_unique<LensSearchboxHandler>(
-      std::move(receiver), Profile::FromWebUI(web_ui()),
-      web_ui()->GetWebContents(), /*lens_searchbox_client=*/controller);
-  controller->SetSidePanelSearchboxHandler(std::move(handler));
+    mojo::PendingReceiver<searchbox::mojom::PageHandlerFactory> receiver) {
+  searchbox_page_factory_receiver_.reset();
+  searchbox_page_factory_receiver_.Bind(std::move(receiver));
 }
 
 void LensSidePanelUntrustedUI::BindInterface(
@@ -302,6 +297,18 @@
 }
 
 void LensSidePanelUntrustedUI::CreatePageHandler(
+    mojo::PendingRemote<searchbox::mojom::Page> page,
+    mojo::PendingReceiver<searchbox::mojom::PageHandler> receiver) {
+  LensSearchboxController* controller =
+      GetLensSearchController().lens_searchbox_controller();
+
+  auto handler = std::make_unique<LensSearchboxHandler>(
+      std::move(receiver), std::move(page), Profile::FromWebUI(web_ui()),
+      web_ui()->GetWebContents(), /*lens_searchbox_client=*/controller);
+  controller->SetSidePanelSearchboxHandler(std::move(handler));
+}
+
+void LensSidePanelUntrustedUI::CreatePageHandler(
     mojo::PendingRemote<composebox::mojom::Page> pending_page,
     mojo::PendingReceiver<composebox::mojom::PageHandler> pending_page_handler,
     mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page,
diff --git a/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.h b/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.h
index f68b848..2906990 100644
--- a/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.h
+++ b/chrome/browser/ui/lens/lens_side_panel_untrusted_ui.h
@@ -12,7 +12,7 @@
 #include "chrome/browser/ui/webui/top_chrome/top_chrome_webui_config.h"
 #include "chrome/browser/ui/webui/top_chrome/untrusted_top_chrome_web_ui_controller.h"
 #include "chrome/common/webui_url_constants.h"
-#include "components/omnibox/browser/searchbox.mojom-forward.h"
+#include "components/omnibox/browser/searchbox.mojom.h"
 #include "components/user_education/webui/help_bubble_handler.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_ui_data_source.h"
@@ -41,7 +41,8 @@
       public lens::mojom::LensSidePanelPageHandlerFactory,
       public lens::mojom::LensGhostLoaderPageHandlerFactory,
       public help_bubble::mojom::HelpBubbleHandlerFactory,
-      public composebox::mojom::PageHandlerFactory {
+      public composebox::mojom::PageHandlerFactory,
+      public searchbox::mojom::PageHandlerFactory {
  public:
   explicit LensSidePanelUntrustedUI(content::WebUI* web_ui);
 
@@ -62,10 +63,10 @@
       mojo::PendingReceiver<lens::mojom::LensGhostLoaderPageHandlerFactory>
           pending_receiver);
 
-  // Instantiates the implementor of the searchbox::mojom::PageHandler mojo
-  // interface passing the pending receiver that will be internally bound.
+  // Instantiates the implementor of the searchbox::mojom::PageHandlerFactory
+  // mojo interface passing the pending receiver that will be internally bound.
   void BindInterface(
-      mojo::PendingReceiver<searchbox::mojom::PageHandler> receiver);
+      mojo::PendingReceiver<searchbox::mojom::PageHandlerFactory> receiver);
 
   // Instantiates the implementor of the
   // help_bubble::mojom::HelpBubbleHandlerFactory mojo interface passing the
@@ -104,6 +105,11 @@
       mojo::PendingReceiver<help_bubble::mojom::HelpBubbleHandler> handler)
       override;
 
+  // searchbox::mojom::PageHandlerFactory:
+  void CreatePageHandler(
+      mojo::PendingRemote<searchbox::mojom::Page> page,
+      mojo::PendingReceiver<searchbox::mojom::PageHandler> handler) override;
+
     // Instantiates the implementor of the composebox::mojom::PageHandler mojo
   // interface passing the pending receiver that will be internally bound.
   void CreatePageHandler(
@@ -127,6 +133,9 @@
   mojo::Receiver<composebox::mojom::PageHandlerFactory>
       composebox_page_handler_factory_receiver_{this};
 
+  mojo::Receiver<searchbox::mojom::PageHandlerFactory>
+      searchbox_page_factory_receiver_{this};
+
   base::WeakPtrFactory<LensSidePanelUntrustedUI> weak_factory_{this};
 
   WEB_UI_CONTROLLER_TYPE_DECL();
diff --git a/chrome/browser/ui/page_action/page_action_icon_type.cc b/chrome/browser/ui/page_action/page_action_icon_type.cc
index 6f16252..763ca96 100644
--- a/chrome/browser/ui/page_action/page_action_icon_type.cc
+++ b/chrome/browser/ui/page_action/page_action_icon_type.cc
@@ -18,8 +18,6 @@
       return &features::kPageActionsMigrationZoom;
     case PageActionIconType::kFileSystemAccess:
       return &features::kPageActionsMigrationFileSystemAccess;
-    case PageActionIconType::kManagePasswords:
-      return &features::kPageActionsMigrationManagePasswords;
     case PageActionIconType::kCookieControls:
       return &features::kPageActionsMigrationCookieControls;
     case PageActionIconType::kMandatoryReauth:
@@ -72,6 +70,7 @@
     case PageActionIconType::kPriceInsights:
     case PageActionIconType::kDiscounts:
     case PageActionIconType::kFederation:
+    case PageActionIconType::kManagePasswords:
       return true;
     default:
       break;
diff --git a/chrome/browser/ui/passwords/BUILD.gn b/chrome/browser/ui/passwords/BUILD.gn
index da23ae7..853f25a 100644
--- a/chrome/browser/ui/passwords/BUILD.gn
+++ b/chrome/browser/ui/passwords/BUILD.gn
@@ -28,7 +28,6 @@
       "credential_manager_dialog_controller.h",
       "credential_manager_dialog_controller_impl.h",
       "manage_passwords_auto_signin_toast_delegate.h",
-      "manage_passwords_icon_view.h",
       "manage_passwords_ui_controller.h",
       "password_base_dialog_controller.h",
       "password_dialog_prompts.h",
@@ -281,6 +280,7 @@
       "//chrome/browser/ui/hats:test_support",
       "//chrome/browser/ui/toasts",
       "//chrome/browser/ui/toasts/api:toasts",
+      "//chrome/browser/ui/views/page_action:test_support",
       "//chrome/test:test_support",
       "//components/affiliations/core/browser:test_support",
       "//components/autofill/content/browser",
diff --git a/chrome/browser/ui/passwords/manage_passwords_icon_view.h b/chrome/browser/ui/passwords/manage_passwords_icon_view.h
deleted file mode 100644
index 9a7462b..0000000
--- a/chrome/browser/ui/passwords/manage_passwords_icon_view.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2015 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_PASSWORDS_MANAGE_PASSWORDS_ICON_VIEW_H_
-#define CHROME_BROWSER_UI_PASSWORDS_MANAGE_PASSWORDS_ICON_VIEW_H_
-
-#include "components/password_manager/core/common/password_manager_ui.h"
-
-// An interface for updating the passwords icon in the location bar.
-class ManagePasswordsIconView {
- public:
-  ManagePasswordsIconView() = default;
-
-  ManagePasswordsIconView(const ManagePasswordsIconView&) = delete;
-  ManagePasswordsIconView& operator=(const ManagePasswordsIconView&) = delete;
-
-  virtual void SetState(password_manager::ui::State state,
-                        bool is_blocklisted) = 0;
-};
-
-#endif  // CHROME_BROWSER_UI_PASSWORDS_MANAGE_PASSWORDS_ICON_VIEW_H_
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
index 1e057999..9e829b19 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
@@ -49,7 +49,6 @@
 #include "chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.h"
 #include "chrome/browser/ui/passwords/credential_manager_dialog_controller_impl.h"
 #include "chrome/browser/ui/passwords/manage_passwords_auto_signin_toast_delegate.h"
-#include "chrome/browser/ui/passwords/manage_passwords_icon_view.h"
 #include "chrome/browser/ui/passwords/password_dialog_prompts.h"
 #include "chrome/browser/ui/passwords/passwords_leak_dialog_delegate.h"
 #include "chrome/browser/ui/passwords/ui_utils.h"
@@ -703,30 +702,6 @@
     const std::vector<password_manager::PasswordForm>& /*retained_passwords*/) {
 }
 
-void ManagePasswordsUIController::UpdateIconAndBubbleState(
-    ManagePasswordsIconView* icon) {
-  const bool is_blocklisted = IsExplicitlyBlocklisted();
-  if (IsAutomaticallyOpeningBubble() ||
-      bubble_status_ == BubbleStatus::SHOULD_POP_UP_WITH_FOCUS) {
-    // This will detach any existing bubble so OnBubbleHidden() isn't called.
-    weak_ptr_factory_.InvalidateWeakPtrs();
-    // We must display the icon before showing the bubble, as the bubble would
-    // be otherwise unanchored.
-    icon->SetState(GetState(), is_blocklisted);
-    ShowBubbleWithoutUserInteraction();
-    // If the bubble appeared then the status is updated in OnBubbleShown().
-    ClearPopUpFlagForBubble();
-  } else {
-    password_manager::ui::State state = GetState();
-    // The dialog should hide the icon.
-    if (dialog_controller_ &&
-        state == password_manager::ui::CREDENTIAL_REQUEST_STATE) {
-      state = password_manager::ui::INACTIVE_STATE;
-    }
-    icon->SetState(state, is_blocklisted);
-  }
-}
-
 void ManagePasswordsUIController::OnPasswordChangeFinishedSuccessfully() {
   // If the password change finished successfully, don't show save/update
   // bubble.
@@ -1249,29 +1224,25 @@
   if (!browser) {
     return;
   }
-  if (IsPageActionMigrated(PageActionIconType::kManagePasswords)) {
-    tabs::TabInterface* const tab_interface =
-        tabs::TabInterface::MaybeGetFromContents(web_contents());
-    // The tab interface can be null if the web contents is not a tab.
-    if (!tab_interface) {
-      return;
-    }
-    auto* const tab_features = tab_interface->GetTabFeatures();
-    CHECK(tab_features);
-    // Retrieve the controller responsible for managing the page action's
-    // visibility and state.
-    auto* const controller =
-        tab_features->manage_passwords_page_action_controller();
-    // Get the action item associated with the passwords UI.
-    actions::ActionItem* passwords_action_item =
-        actions::ActionManager::Get().FindAction(
-            kActionShowPasswordsBubbleOrPage,
-            browser->GetActions()->root_action_item());
-    UpdatePasswordIconAndBubbleState(controller, passwords_action_item);
-  } else {
-    browser->GetBrowserForMigrationOnly()->window()->UpdatePageActionIcon(
-        PageActionIconType::kManagePasswords);
+
+  tabs::TabInterface* const tab_interface =
+      tabs::TabInterface::MaybeGetFromContents(web_contents());
+  // The tab interface can be null if the web contents is not a tab.
+  if (!tab_interface) {
+    return;
   }
+  auto* const tab_features = tab_interface->GetTabFeatures();
+  CHECK(tab_features);
+  // Retrieve the controller responsible for managing the page action's
+  // visibility and state.
+  auto* const controller =
+      tab_features->manage_passwords_page_action_controller();
+  // Get the action item associated with the passwords UI.
+  actions::ActionItem* passwords_action_item =
+      actions::ActionManager::Get().FindAction(
+          kActionShowPasswordsBubbleOrPage,
+          browser->GetActions()->root_action_item());
+  UpdatePasswordIconAndBubbleState(controller, passwords_action_item);
 }
 
 void ManagePasswordsUIController::UpdatePasswordIconAndBubbleState(
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
index 8695091..ac4abb6 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
@@ -54,7 +54,6 @@
 class AccountChooserPrompt;
 class AutoSigninFirstRunPrompt;
 class CredentialLeakPrompt;
-class ManagePasswordsIconView;
 class CredentialLeakDialogController;
 class CredentialManagerDialogController;
 class PasswordBaseDialogController;
@@ -136,10 +135,6 @@
                         const std::vector<password_manager::PasswordForm>&
                             retained_passwords) override;
 
-  // Set the state of the Omnibox icon, and possibly show the associated bubble
-  // without user interaction.
-  virtual void UpdateIconAndBubbleState(ManagePasswordsIconView* icon);
-
   // Called if the password change flow finishes successfully. It ensures the
   // correct state after the flow.
   void OnPasswordChangeFinishedSuccessfully();
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
index 36d1de7..dd37921c 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
@@ -28,14 +28,16 @@
 #include "chrome/browser/password_manager/password_change_delegate.h"
 #include "chrome/browser/password_manager/password_change_delegate_mock.h"
 #include "chrome/browser/password_manager/password_change_service_factory.h"
+#include "chrome/browser/ui/actions/chrome_action_id.h"
 #include "chrome/browser/ui/hats/mock_trust_safety_sentiment_service.h"
 #include "chrome/browser/ui/hats/trust_safety_sentiment_service_factory.h"
 #include "chrome/browser/ui/passwords/credential_leak_dialog_controller.h"
 #include "chrome/browser/ui/passwords/credential_manager_dialog_controller.h"
-#include "chrome/browser/ui/passwords/manage_passwords_icon_view.h"
 #include "chrome/browser/ui/passwords/password_dialog_prompts.h"
 #include "chrome/browser/ui/passwords/passwords_model_delegate.h"
 #include "chrome/browser/ui/views/frame/test_with_browser_view.h"
+#include "chrome/browser/ui/views/page_action/test_support/mock_page_action_controller.h"
+#include "chrome/browser/ui/views/passwords/manage_passwords_page_action_controller.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/affiliations/core/browser/mock_affiliation_service.h"
 #include "components/autofill/core/common/autofill_features.h"
@@ -131,20 +133,6 @@
   MOCK_METHOD(views::Widget*, GetWidgetForTesting, (), (override));
 };
 
-class TestManagePasswordsIconView : public ManagePasswordsIconView {
- public:
-  void SetState(password_manager::ui::State state,
-                bool is_blocklisted) override {
-    state_ = state;
-    is_blocklisted_ = is_blocklisted;
-  }
-  password_manager::ui::State state() { return state_; }
-
- private:
-  password_manager::ui::State state_;
-  bool is_blocklisted_ = false;
-};
-
 class TestPasswordManagerClient
     : public password_manager::StubPasswordManagerClient {
  public:
@@ -203,12 +191,20 @@
   void HideBubble(bool initiated_by_bubble_manager) override;
 
   bool opened_automatic_bubble_ = false;
+
+  page_actions::MockPageActionController page_action_controller_;
+  ManagePasswordsPageActionController manage_passwords_page_action_controller_;
+  std::unique_ptr<actions::ActionItem> passwords_action_item_;
 };
 
 TestManagePasswordsUIController::TestManagePasswordsUIController(
     content::WebContents* contents,
     password_manager::PasswordManagerClient* client)
-    : ManagePasswordsUIController(contents) {
+    : ManagePasswordsUIController(contents),
+      manage_passwords_page_action_controller_(page_action_controller_) {
+  passwords_action_item_ = actions::ActionItem::Builder()
+                               .SetActionId(kActionShowPasswordsBubbleOrPage)
+                               .Build();
   // Do not silently replace an existing ManagePasswordsUIController because it
   // unregisters itself in WebContentsDestroyed().
   EXPECT_FALSE(contents->GetUserData(UserDataKey()));
@@ -220,8 +216,10 @@
   opened_automatic_bubble_ = IsAutomaticallyOpeningBubble();
   ManagePasswordsUIController::UpdateBubbleAndIconVisibility();
   OnUpdateBubbleAndIconVisibility();
-  TestManagePasswordsIconView view;
-  UpdateIconAndBubbleState(&view);
+  // Manually invoke this because `OnUpdateBubbleAndIconVisibility` skips it
+  // due to the lack of a browser.
+  UpdatePasswordIconAndBubbleState(&manage_passwords_page_action_controller_,
+                                   passwords_action_item_.get());
   if (opened_automatic_bubble_) {
     OnBubbleShown();
   }
@@ -337,8 +335,6 @@
         ManagePasswordsUIController::FromWebContents(web_contents()));
   }
 
-  void ExpectIconStateIs(password_manager::ui::State state);
-  void ExpectIconAndControllerStateIs(password_manager::ui::State state);
   void WaitForPasswordStore();
 
  private:
@@ -385,19 +381,6 @@
                                                              GURL(kExampleUrl));
 }
 
-void ManagePasswordsUIControllerTest::ExpectIconStateIs(
-    password_manager::ui::State state) {
-  TestManagePasswordsIconView view;
-  controller()->UpdateIconAndBubbleState(&view);
-  EXPECT_EQ(state, view.state());
-}
-
-void ManagePasswordsUIControllerTest::ExpectIconAndControllerStateIs(
-    password_manager::ui::State state) {
-  ExpectIconStateIs(state);
-  EXPECT_EQ(state, controller()->GetState());
-}
-
 void ManagePasswordsUIControllerTest::WaitForPasswordStore() {
   task_environment()->RunUntilIdle();
 }
@@ -405,7 +388,7 @@
 TEST_P(ManagePasswordsUIControllerTest, DefaultState) {
   EXPECT_TRUE(controller()->GetOrigin().opaque());
 
-  ExpectIconAndControllerStateIs(password_manager::ui::INACTIVE_STATE);
+  EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, PasswordAutofilled) {
@@ -423,7 +406,7 @@
   // Controller should store a separate copy of the form as it doesn't own it.
   EXPECT_NE(&test_local_form(), controller()->GetCurrentForms()[0].get());
 
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, PasswordSubmitted) {
@@ -436,7 +419,8 @@
   EXPECT_EQ(url::Origin::Create(test_local_form().url),
             controller()->GetOrigin());
 
-  ExpectIconAndControllerStateIs(password_manager::ui::PENDING_PASSWORD_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, BlocklistedFormPasswordSubmitted) {
@@ -447,7 +431,8 @@
   controller()->OnPasswordSubmitted(std::move(test_form_manager));
   EXPECT_FALSE(controller()->opened_automatic_bubble());
 
-  ExpectIconAndControllerStateIs(password_manager::ui::PENDING_PASSWORD_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, PasswordSubmittedBubbleSuppressed) {
@@ -466,7 +451,8 @@
   ASSERT_TRUE(controller()->GetCurrentInteractionStats());
   EXPECT_EQ(stats[0], *controller()->GetCurrentInteractionStats());
 
-  ExpectIconAndControllerStateIs(password_manager::ui::PENDING_PASSWORD_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, PasswordSubmittedBubbleNotSuppressed) {
@@ -484,7 +470,8 @@
   EXPECT_TRUE(controller()->opened_automatic_bubble());
   EXPECT_FALSE(controller()->GetCurrentInteractionStats());
 
-  ExpectIconAndControllerStateIs(password_manager::ui::PENDING_PASSWORD_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, PasswordSubmittedBubbleCancelled) {
@@ -530,7 +517,7 @@
   base::HistogramTester histogram_tester;
   controller()->SavePassword(submitted_form().username_value,
                              submitted_form().password_value);
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
   histogram_tester.ExpectTotalCount(
       "PasswordManager.PasswordChangeRecoveryFlow", 0);
 }
@@ -567,7 +554,7 @@
   task_environment()->AdvanceClock(base::Seconds(2));
   task_environment()->RunUntilIdle();
 
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
   histogram_tester.ExpectUniqueSample(
       "PasswordManager.PasswordChangeRecoveryFlow",
       password_manager::metrics_util::PasswordChangeRecoveryFlowState::
@@ -600,7 +587,7 @@
   EXPECT_CALL(*mock_sentiment_service, PhishedPasswordUpdateFinished());
   controller()->SavePassword(submitted_form().username_value,
                              submitted_form().password_value);
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, PasswordSavedUKMRecording) {
@@ -648,7 +635,7 @@
         test.edit_username ? u"other_username"
                            : submitted_form().username_value,
         test.change_password ? u"other_pwd" : submitted_form().password_value);
-    ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+    EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
 
     // Fake navigation so that the old form manager gets destroyed and
     // reports its metrics. Need to close the bubble, otherwise the bubble
@@ -705,7 +692,7 @@
 
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   controller()->NeverSavePassword();
-  ExpectIconAndControllerStateIs(password_manager::ui::INACTIVE_STATE);
+  EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest,
@@ -719,7 +706,7 @@
 
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   controller()->NeverSavePassword();
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, NormalNavigations) {
@@ -728,14 +715,16 @@
       CreateFormManagerWithBestMatches(best_matches, &submitted_form());
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   controller()->OnPasswordSubmitted(std::move(test_form_manager));
-  ExpectIconAndControllerStateIs(password_manager::ui::PENDING_PASSWORD_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+            controller()->GetState());
 
   // Fake-navigate. We expect the bubble's state to persist so a user reasonably
   // has been able to interact with the bubble. This happens on
   // `accounts.google.com`, for instance.
   content::NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(),
                                                              GURL(kExampleUrl));
-  ExpectIconAndControllerStateIs(password_manager::ui::PENDING_PASSWORD_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, NormalNavigationsClosedBubble) {
@@ -749,13 +738,13 @@
                              submitted_form().password_value);
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   controller()->OnBubbleHidden();
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
 
   // Fake-navigate. There is no bubble, reset the state.
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   content::NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(),
                                                              GURL(kExampleUrl));
-  ExpectIconAndControllerStateIs(password_manager::ui::INACTIVE_STATE);
+  EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, PasswordSubmittedToNonWebbyURL) {
@@ -771,12 +760,12 @@
   controller()->OnPasswordSubmitted(std::move(test_form_manager));
   EXPECT_TRUE(controller()->GetOrigin().opaque());
 
-  ExpectIconAndControllerStateIs(password_manager::ui::INACTIVE_STATE);
+  EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest,
        OnBiometricAuthTransitionWhenStateInactive) {
-  ExpectIconAndControllerStateIs(password_manager::ui::INACTIVE_STATE);
+  EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->GetState());
   controller()->OnBiometricAuthenticationForFilling(profile()->GetPrefs());
   ASSERT_EQ(password_manager::ui::INACTIVE_STATE, controller()->GetState());
 }
@@ -798,7 +787,7 @@
   EXPECT_EQ(url::Origin::Create(test_local_form().url),
             controller()->GetOrigin());
 
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, AutomaticPasswordSave) {
@@ -822,7 +811,7 @@
   EXPECT_EQ(*(controller()->GetCurrentForms()[0]), expected_form);
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   controller()->OnBubbleHidden();
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, ChooseCredentialLocal) {
@@ -848,7 +837,6 @@
               ElementsAre(Pointee(test_local_form())));
   ASSERT_THAT(dialog_controller->GetLocalForms(),
               ElementsAre(Pointee(test_local_form())));
-  ExpectIconStateIs(password_manager::ui::INACTIVE_STATE);
 
   EXPECT_CALL(*prompt_ptr, ControllerGone());
   EXPECT_CALL(choose_callback, Run(Pointee(test_local_form())));
@@ -882,7 +870,6 @@
               ElementsAre(Pointee(test_federated_form())));
   ASSERT_THAT(dialog_controller->GetLocalForms(),
               ElementsAre(Pointee(test_federated_form())));
-  ExpectIconStateIs(password_manager::ui::INACTIVE_STATE);
 
   EXPECT_CALL(*prompt_ptr, ControllerGone());
   EXPECT_CALL(choose_callback, Run(Pointee(test_federated_form())));
@@ -957,7 +944,6 @@
   EXPECT_THAT(controller()->GetCurrentForms(), IsEmpty());
   ASSERT_THAT(dialog_controller->GetLocalForms(),
               ElementsAre(Pointee(test_local_form())));
-  ExpectIconStateIs(password_manager::ui::INACTIVE_STATE);
 
   EXPECT_CALL(*prompt_ptr, ControllerGone());
   EXPECT_CALL(choose_callback, Run(Pointee(test_local_form())));
@@ -979,10 +965,10 @@
             controller()->GetOrigin());
   ASSERT_FALSE(controller()->GetCurrentForms().empty());
   EXPECT_EQ(test_local_form(), *controller()->GetCurrentForms()[0]);
-  ExpectIconAndControllerStateIs(password_manager::ui::AUTO_SIGNIN_STATE);
+  EXPECT_EQ(password_manager::ui::AUTO_SIGNIN_STATE, controller()->GetState());
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   controller()->OnBubbleHidden();
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, AutoSigninFirstRun) {
@@ -1040,14 +1026,14 @@
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   controller()->OnAutoSignin(std::move(local_credentials),
                              url::Origin::Create(test_local_form().url));
-  ExpectIconAndControllerStateIs(password_manager::ui::AUTO_SIGNIN_STATE);
+  EXPECT_EQ(password_manager::ui::AUTO_SIGNIN_STATE, controller()->GetState());
   std::vector<PasswordForm> forms;
   std::u16string kTestUsername = test_local_form().username_value;
   forms.push_back(test_local_form());
   controller()->OnPasswordAutofilled(
       forms, url::Origin::Create(forms.front().url), {});
 
-  ExpectIconAndControllerStateIs(password_manager::ui::AUTO_SIGNIN_STATE);
+  EXPECT_EQ(password_manager::ui::AUTO_SIGNIN_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, ActiveOnPSLMatched) {
@@ -1060,7 +1046,7 @@
   controller()->OnPasswordAutofilled(
       forms, url::Origin::Create(forms.front().url), {});
 
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, UpdatePasswordSubmitted) {
@@ -1069,8 +1055,8 @@
       CreateFormManagerWithBestMatches(best_matches, &submitted_form());
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   controller()->OnUpdatePasswordSubmitted(std::move(test_form_manager));
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::PENDING_PASSWORD_UPDATE_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_UPDATE_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, PasswordUpdated) {
@@ -1081,12 +1067,12 @@
   EXPECT_CALL(*test_form_manager, Save());
   controller()->OnUpdatePasswordSubmitted(std::move(test_form_manager));
 
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::PENDING_PASSWORD_UPDATE_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_UPDATE_STATE,
+            controller()->GetState());
   base::HistogramTester histogram_tester;
   controller()->SavePassword(submitted_form().username_value,
                              submitted_form().password_value);
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   controller()->OnBubbleHidden();
 }
@@ -1108,7 +1094,8 @@
       forms, url::Origin::Create(forms.front().url), {});
 
   // State shouldn't change.
-  ExpectIconAndControllerStateIs(password_manager::ui::PENDING_PASSWORD_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, UpdatePendingStatePasswordAutofilled) {
@@ -1129,8 +1116,8 @@
       forms, url::Origin::Create(forms.front().url), {});
 
   // State shouldn't change.
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::PENDING_PASSWORD_UPDATE_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_UPDATE_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, ConfirmationStatePasswordAutofilled) {
@@ -1155,7 +1142,8 @@
       forms, url::Origin::Create(forms.front().url), {});
 
   // State shouldn't change.
-  ExpectIconAndControllerStateIs(password_manager::ui::SAVE_CONFIRMATION_STATE);
+  EXPECT_EQ(password_manager::ui::SAVE_CONFIRMATION_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, OpenBubbleTwice) {
@@ -1211,9 +1199,9 @@
     controller()->OnShowManualFallbackForSaving(
         std::move(test_form_manager), false /* has_generated_password */,
         is_update);
-    ExpectIconAndControllerStateIs(
-        is_update ? password_manager::ui::PENDING_PASSWORD_UPDATE_STATE
-                  : password_manager::ui::PENDING_PASSWORD_STATE);
+    EXPECT_EQ(is_update ? password_manager::ui::PENDING_PASSWORD_UPDATE_STATE
+                        : password_manager::ui::PENDING_PASSWORD_STATE,
+              controller()->GetState());
     EXPECT_FALSE(controller()->opened_automatic_bubble());
 
     // A user clicks on omnibox icon, opens the bubble and press Save/Update.
@@ -1259,9 +1247,9 @@
     controller()->OnShowManualFallbackForSaving(
         std::move(test_form_manager), false /* has_generated_password */,
         is_update);
-    ExpectIconAndControllerStateIs(
-        is_update ? password_manager::ui::PENDING_PASSWORD_UPDATE_STATE
-                  : password_manager::ui::PENDING_PASSWORD_STATE);
+    EXPECT_EQ(is_update ? password_manager::ui::PENDING_PASSWORD_UPDATE_STATE
+                        : password_manager::ui::PENDING_PASSWORD_STATE,
+              controller()->GetState());
     testing::Mock::VerifyAndClearExpectations(controller());
     EXPECT_FALSE(controller()->opened_automatic_bubble());
 
@@ -1270,7 +1258,7 @@
     controller()->OnHideManualFallbackForSaving();
     testing::Mock::VerifyAndClearExpectations(controller());
 
-    ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+    EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
   }
 }
 
@@ -1287,7 +1275,8 @@
   controller()->OnShowManualFallbackForSaving(
       std::move(test_form_manager), false /* has_generated_password */,
       false /* is_update */);
-  ExpectIconAndControllerStateIs(password_manager::ui::PENDING_PASSWORD_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+            controller()->GetState());
   testing::Mock::VerifyAndClearExpectations(controller());
   EXPECT_FALSE(controller()->opened_automatic_bubble());
 
@@ -1295,7 +1284,7 @@
   controller()->OnHideManualFallbackForSaving();
   testing::Mock::VerifyAndClearExpectations(controller());
 
-  ExpectIconAndControllerStateIs(password_manager::ui::INACTIVE_STATE);
+  EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest,
@@ -1313,15 +1302,15 @@
     controller()->OnShowManualFallbackForSaving(
         std::move(test_form_manager), false /* has_generated_password */,
         false /* is_update */);
-    ExpectIconAndControllerStateIs(
-        password_manager::ui::PENDING_PASSWORD_STATE);
+    EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+              controller()->GetState());
     testing::Mock::VerifyAndClearExpectations(controller());
     if (enforce_navigation) {
       // Fake-navigate. The fallback should persist.
       content::NavigationSimulator::NavigateAndCommitFromBrowser(
           web_contents(), GURL(kExampleUrl));
-      ExpectIconAndControllerStateIs(
-          password_manager::ui::PENDING_PASSWORD_STATE);
+      EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+                controller()->GetState());
     }
 
     // As the timeout is zero, the fallback will be hidden right after show.
@@ -1330,7 +1319,7 @@
     content::RunAllTasksUntilIdle();
 
     EXPECT_FALSE(controller()->opened_automatic_bubble());
-    ExpectIconAndControllerStateIs(password_manager::ui::INACTIVE_STATE);
+    EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->GetState());
     testing::Mock::VerifyAndClearExpectations(controller());
   }
 }
@@ -1351,8 +1340,8 @@
     controller()->OnShowManualFallbackForSaving(
         std::move(test_form_manager), false /* has_generated_password */,
         false /* is_update */);
-    ExpectIconAndControllerStateIs(
-        password_manager::ui::PENDING_PASSWORD_STATE);
+    EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+              controller()->GetState());
     testing::Mock::VerifyAndClearExpectations(controller());
 
     // A user opens the bubble.
@@ -1361,13 +1350,13 @@
     // Fallback hiding is triggered by timeout but blocked because of open
     // bubble.
     content::RunAllTasksUntilIdle();
-    ExpectIconAndControllerStateIs(
-        password_manager::ui::PENDING_PASSWORD_STATE);
+    EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+              controller()->GetState());
 
     if (user_saved_password) {
       controller()->SavePassword(submitted_form().username_value,
                                  submitted_form().password_value);
-      ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+      EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
     } else {
       // A user closed the bubble. The fallback should be hidden after
       // navigation.
@@ -1375,7 +1364,7 @@
       EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
       content::NavigationSimulator::NavigateAndCommitFromBrowser(
           web_contents(), GURL(kExampleUrl));
-      ExpectIconAndControllerStateIs(password_manager::ui::INACTIVE_STATE);
+      EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->GetState());
     }
     testing::Mock::VerifyAndClearExpectations(controller());
   }
@@ -1396,7 +1385,8 @@
   controller()->OnShowManualFallbackForSaving(
       std::move(test_form_manager), false /* has_generated_password */,
       false /* is_update */);
-  ExpectIconAndControllerStateIs(password_manager::ui::PENDING_PASSWORD_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+            controller()->GetState());
   testing::Mock::VerifyAndClearExpectations(controller());
 
   // A user opens the bubble.
@@ -1411,7 +1401,8 @@
   controller()->OnPasswordSubmitted(std::move(test_form_manager));
 
   // It should update the bubble already open.
-  ExpectIconAndControllerStateIs(password_manager::ui::PENDING_PASSWORD_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+            controller()->GetState());
   EXPECT_EQ(u"new_username", controller()->GetPendingPassword().username_value);
   EXPECT_EQ(u"12345", controller()->GetPendingPassword().password_value);
 }
@@ -1440,7 +1431,8 @@
   controller()->OnShowManualFallbackForSaving(
       std::move(test_form_manager), false /* has_generated_password */,
       false /* is_update */);
-  ExpectIconAndControllerStateIs(password_manager::ui::PENDING_PASSWORD_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+            controller()->GetState());
 
   // The automatic bubble is gone. The controller is managing the new origin.
   EXPECT_FALSE(controller()->opened_automatic_bubble());
@@ -1459,8 +1451,8 @@
     EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
     controller()->OnShowManualFallbackForSaving(
         std::move(test_form_manager), true /* has_generated_password */, false);
-    ExpectIconAndControllerStateIs(
-        password_manager::ui::SAVE_CONFIRMATION_STATE);
+    EXPECT_EQ(password_manager::ui::SAVE_CONFIRMATION_STATE,
+              controller()->GetState());
     EXPECT_FALSE(controller()->opened_automatic_bubble());
 
     EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
@@ -1471,7 +1463,7 @@
       // The user removes the generated password. It hides the fallback.
       controller()->OnHideManualFallbackForSaving();
     }
-    ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+    EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
     testing::Mock::VerifyAndClearExpectations(controller());
   }
 }
@@ -1574,7 +1566,7 @@
                              submitted_form().password_value);
   // The state is 'Managed' but the bubble may still be on the screen showing
   // the sign-in promo.
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
   // The controller shouldn't force close the bubble if an autofill happened.
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility()).Times(0);
   std::vector<PasswordForm> forms;
@@ -1625,7 +1617,8 @@
 
   // The save bubble is back.
   EXPECT_TRUE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(password_manager::ui::PENDING_PASSWORD_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest,
@@ -1664,7 +1657,8 @@
 
   // The save bubble should not be opened because the website is blocklisted.
   EXPECT_FALSE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(password_manager::ui::PENDING_PASSWORD_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, UpdateBubbleAfterLeakCheck) {
@@ -1698,8 +1692,8 @@
 
   // The update bubble is back.
   EXPECT_TRUE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::PENDING_PASSWORD_UPDATE_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_UPDATE_STATE,
+            controller()->GetState());
 }
 
 // If the leaked password is the backup password of the login credentials, we
@@ -1739,8 +1733,8 @@
 
   // The update bubble is back.
   EXPECT_TRUE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::PENDING_PASSWORD_UPDATE_STATE);
+  EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_UPDATE_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, OpenBubbleForMovableForm) {
@@ -1754,8 +1748,8 @@
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility()).Times(2);
   controller()->OnShowMoveToAccountBubble(std::move(test_form_manager));
   EXPECT_TRUE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::MOVE_CREDENTIAL_AFTER_LOG_IN_STATE);
+  EXPECT_EQ(password_manager::ui::MOVE_CREDENTIAL_AFTER_LOG_IN_STATE,
+            controller()->GetState());
 
   // A user confirms the move which closes the dialog.
   EXPECT_CALL(*controller(),
@@ -1766,7 +1760,7 @@
                   _));
   controller()->MovePasswordToAccountStore();
   EXPECT_FALSE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
   histogram_tester.ExpectUniqueSample(
       "PasswordManager.AccountStorage.MoveToAccountStoreFlowOffered",
       password_manager::metrics_util::MoveToAccountStoreTrigger::
@@ -1785,8 +1779,8 @@
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility()).Times(2);
   controller()->ShowMovePasswordBubble(test_local_form());
   EXPECT_FALSE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::MOVE_CREDENTIAL_FROM_MANAGE_BUBBLE_STATE);
+  EXPECT_EQ(password_manager::ui::MOVE_CREDENTIAL_FROM_MANAGE_BUBBLE_STATE,
+            controller()->GetState());
 
   EXPECT_CALL(*controller(),
               CreateMovePasswordToAccountStoreHelper(
@@ -1798,7 +1792,7 @@
   // A user confirms the move which closes the dialog.
   controller()->MovePasswordToAccountStore();
   EXPECT_FALSE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, CloseMoveBubble) {
@@ -1812,13 +1806,13 @@
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility()).Times(2);
   controller()->ShowMovePasswordBubble(test_local_form());
   EXPECT_FALSE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::MOVE_CREDENTIAL_FROM_MANAGE_BUBBLE_STATE);
+  EXPECT_EQ(password_manager::ui::MOVE_CREDENTIAL_FROM_MANAGE_BUBBLE_STATE,
+            controller()->GetState());
 
   // A user closes the dialog.
   controller()->OnBubbleHidden();
   EXPECT_FALSE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, OpenSafeStateBubble) {
@@ -1860,8 +1854,8 @@
   WaitForPasswordStore();
 
   EXPECT_TRUE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::PASSWORD_UPDATED_SAFE_STATE);
+  EXPECT_EQ(password_manager::ui::PASSWORD_UPDATED_SAFE_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, OpenMoreToFixBubble) {
@@ -1907,8 +1901,8 @@
   WaitForPasswordStore();
 
   EXPECT_TRUE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::PASSWORD_UPDATED_MORE_TO_FIX);
+  EXPECT_EQ(password_manager::ui::PASSWORD_UPDATED_MORE_TO_FIX,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, NoMoreToFixBubbleIfPromoStillOpen) {
@@ -1935,7 +1929,7 @@
   // The sign-in promo bubble stays open, the warning isn't shown.
   WaitForPasswordStore();
 
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, UsernameAdded) {
@@ -1987,8 +1981,8 @@
   controller()->OnPasskeySaved(/*gpm_pin_created=*/false, kExampleRpId);
   EXPECT_EQ(controller()->PasskeyRpId(), kExampleRpId);
   EXPECT_TRUE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::PASSKEY_SAVED_CONFIRMATION_STATE);
+  EXPECT_EQ(password_manager::ui::PASSKEY_SAVED_CONFIRMATION_STATE,
+            controller()->GetState());
   EXPECT_FALSE(controller()->GpmPinCreatedDuringRecentPasskeyCreation());
 }
 
@@ -1997,8 +1991,8 @@
   controller()->OnPasskeySaved(/*gpm_pin_created=*/true, kExampleRpId);
   EXPECT_EQ(controller()->PasskeyRpId(), kExampleRpId);
   EXPECT_TRUE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::PASSKEY_SAVED_CONFIRMATION_STATE);
+  EXPECT_EQ(password_manager::ui::PASSKEY_SAVED_CONFIRMATION_STATE,
+            controller()->GetState());
   EXPECT_TRUE(controller()->GpmPinCreatedDuringRecentPasskeyCreation());
 }
 
@@ -2006,8 +2000,8 @@
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   controller()->OnPasskeyDeleted();
   EXPECT_TRUE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::PASSKEY_DELETED_CONFIRMATION_STATE);
+  EXPECT_EQ(password_manager::ui::PASSKEY_DELETED_CONFIRMATION_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, OpenPasskeyUpdatedBubble) {
@@ -2016,8 +2010,8 @@
   controller()->OnPasskeyUpdated(rp_id);
   EXPECT_TRUE(controller()->opened_automatic_bubble());
   EXPECT_EQ(controller()->PasskeyRpId(), rp_id);
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::PASSKEY_UPDATED_CONFIRMATION_STATE);
+  EXPECT_EQ(password_manager::ui::PASSKEY_UPDATED_CONFIRMATION_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, OpenPasskeyNotAcceptedBubble) {
@@ -2026,8 +2020,8 @@
   controller()->OnPasskeyNotAccepted(rp_id);
   EXPECT_EQ(controller()->PasskeyRpId(), rp_id);
   EXPECT_TRUE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::PASSKEY_NOT_ACCEPTED_STATE);
+  EXPECT_EQ(password_manager::ui::PASSKEY_NOT_ACCEPTED_STATE,
+            controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest, PasswordChangeFinishedSuccessfully) {
@@ -2086,8 +2080,8 @@
   EXPECT_EQ(1, profile()->GetPrefs()->GetInteger(
                    password_manager::prefs::
                        kBiometricAuthBeforeFillingPromoShownCounter));
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::BIOMETRIC_AUTHENTICATION_FOR_FILLING_STATE);
+  EXPECT_EQ(password_manager::ui::BIOMETRIC_AUTHENTICATION_FOR_FILLING_STATE,
+            controller()->GetState());
 }
 
 // Test if BiometricAuthForFilling promo is not shown if user interacted with
@@ -2184,8 +2178,8 @@
 
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   controller()->OnBiometricAuthenticationForFilling(profile()->GetPrefs());
-  ExpectIconAndControllerStateIs(
-      password_manager::ui::BIOMETRIC_AUTHENTICATION_FOR_FILLING_STATE);
+  EXPECT_EQ(password_manager::ui::BIOMETRIC_AUTHENTICATION_FOR_FILLING_STATE,
+            controller()->GetState());
 
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility()).Times(0);
   controller()->OnBiometricAuthenticationForFilling(profile()->GetPrefs());
@@ -2249,7 +2243,7 @@
   // After closing buble state switches automatically to MANAGE_STATE.
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   controller()->OnBubbleHidden();
-  ExpectIconAndControllerStateIs(password_manager::ui::MANAGE_STATE);
+  EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest,
@@ -2359,8 +2353,8 @@
 }
 #endif
 
-// TODO(crbug.com/376283921): These tests should be turned into browser tests to avoid
-// using `TestWithBrowserView`.
+// TODO(crbug.com/376283921): These tests should be turned into browser tests to
+// avoid using `TestWithBrowserView`.
 class ManagePasswordsUIControllerWithBrowserTest
     : public base::test::WithFeatureOverride,
       public TestWithBrowserView {
@@ -2462,11 +2456,12 @@
   EXPECT_EQ(controller()->PasswordChangeUsername(), kExampleUsername);
   EXPECT_EQ(controller()->PasswordChangeNewPassword(), kExamplePassword);
   EXPECT_TRUE(controller()->opened_automatic_bubble());
-  ExpectIconAndControllerStateIs(password_manager::ui::PASSWORD_CHANGE_STATE);
+  EXPECT_EQ(password_manager::ui::PASSWORD_CHANGE_STATE,
+            controller()->GetState());
 
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   controller()->OnBubbleHidden();
-  ExpectIconAndControllerStateIs(password_manager::ui::INACTIVE_STATE);
+  EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->GetState());
 }
 
 TEST_P(ManagePasswordsUIControllerTest,
diff --git a/chrome/browser/ui/side_panel/internal/android/side_panel_coordinator_android.cc b/chrome/browser/ui/side_panel/internal/android/side_panel_coordinator_android.cc
index a849004f9..8180a7b 100644
--- a/chrome/browser/ui/side_panel/internal/android/side_panel_coordinator_android.cc
+++ b/chrome/browser/ui/side_panel/internal/android/side_panel_coordinator_android.cc
@@ -26,6 +26,7 @@
     LOG(ERROR) << LOG_TAG << ": " << message;              \
   }
 
+using base::android::AttachCurrentThread;
 using base::android::JavaRef;
 using base::android::ScopedJavaLocalRef;
 
@@ -37,18 +38,6 @@
   return browser ? Get(browser->GetUnownedUserDataHost()) : nullptr;
 }
 
-// Implements Java `SidePanelCoordinatorAndroidImpl.Natives#create`.
-static int64_t JNI_SidePanelCoordinatorAndroidImpl_Create(
-    JNIEnv* env,
-    const JavaRef<jobject>& caller,
-    int64_t nativeBrowserWindowPtr) {
-  SPLOG("JNI_SidePanelCoordinatorAndroidImpl_Create - ptr: "
-        << nativeBrowserWindowPtr);
-  return reinterpret_cast<intptr_t>(new SidePanelCoordinatorAndroid(
-      env, caller,
-      reinterpret_cast<BrowserWindowInterface*>(nativeBrowserWindowPtr)));
-}
-
 SidePanelCoordinatorAndroid::SidePanelCoordinatorAndroid(
     JNIEnv* env,
     const JavaRef<jobject>& java_coordinator,
@@ -62,8 +51,8 @@
 
 SidePanelCoordinatorAndroid::~SidePanelCoordinatorAndroid() {
   SPLOG("SidePanelCoordinatorAndroid Destructor");
-  Java_SidePanelCoordinatorAndroidImpl_clearNativePtr(
-      base::android::AttachCurrentThread(), java_coordinator());
+  Java_SidePanelCoordinatorAndroidImpl_clearNativePtr(AttachCurrentThread(),
+                                                      java_coordinator());
 }
 
 void SidePanelCoordinatorAndroid::Destroy(JNIEnv* env) {
@@ -98,8 +87,8 @@
   CHECK(entry) << "SidePanelEntry should exist when side panel is showing.";
 
   entry->OnEntryWillHide(hide_reason);
-  Java_SidePanelCoordinatorAndroidImpl_removeContent(
-      base::android::AttachCurrentThread(), java_coordinator());
+  Java_SidePanelCoordinatorAndroidImpl_removeContent(AttachCurrentThread(),
+                                                     java_coordinator());
 
   // TODO(crbug.com/493930383): Clear current key and trigger OnEntryHidden()
   // when animation ends.
@@ -170,8 +159,7 @@
   // If the side panel isn't shown, just show it.
   if (!IsSidePanelShowing(entry->type())) {
     Java_SidePanelCoordinatorAndroidImpl_populateSidePanel(
-        base::android::AttachCurrentThread(), java_coordinator(),
-        native_view->view());
+        AttachCurrentThread(), java_coordinator(), native_view->view());
     SetCurrentKey(entry->type(), unique_key);
     entry->OnEntryShown();
     return;
@@ -196,8 +184,7 @@
 
   previous_entry->OnEntryWillHide(previous_entry_hide_reason);
   Java_SidePanelCoordinatorAndroidImpl_populateSidePanel(
-      base::android::AttachCurrentThread(), java_coordinator(),
-      native_view->view());
+      AttachCurrentThread(), java_coordinator(), native_view->view());
   SetCurrentKey(entry->type(), unique_key);
   entry->OnEntryShown();
   previous_entry->OnEntryHidden();
@@ -259,8 +246,8 @@
 ScopedJavaLocalRef<jobject> SidePanelCoordinatorAndroid::java_coordinator()
     const {
   SPLOG("java_coordinator()");
-  JNIEnv* env = base::android::AttachCurrentThread();
-  ScopedJavaLocalRef<jobject> local_ref = java_coordinator_.get(env);
+  ScopedJavaLocalRef<jobject> local_ref =
+      java_coordinator_.get(AttachCurrentThread());
 
   CHECK(local_ref) << "Java SidePanelCoordinatorAndroid is the sole owner of "
                       "C++ SidePanelCoordinatorAndroid, so the Java object "
@@ -268,4 +255,20 @@
   return local_ref;
 }
 
+// ----------------------------------------------------------------------------
+// Methods called from Java via SidePanelCoordinatorAndroidImpl.Natives:
+// ----------------------------------------------------------------------------
+
+// static
+static int64_t JNI_SidePanelCoordinatorAndroidImpl_Create(
+    JNIEnv* env,
+    const JavaRef<jobject>& caller,
+    int64_t nativeBrowserWindowPtr) {
+  SPLOG("JNI_SidePanelCoordinatorAndroidImpl_Create - ptr: "
+        << nativeBrowserWindowPtr);
+  return reinterpret_cast<intptr_t>(new SidePanelCoordinatorAndroid(
+      env, caller,
+      reinterpret_cast<BrowserWindowInterface*>(nativeBrowserWindowPtr)));
+}
+
 DEFINE_JNI(SidePanelCoordinatorAndroidImpl)
diff --git a/chrome/browser/ui/side_panel/internal/android/side_panel_coordinator_android.h b/chrome/browser/ui/side_panel/internal/android/side_panel_coordinator_android.h
index 4ff8bb6..8c19d6de 100644
--- a/chrome/browser/ui/side_panel/internal/android/side_panel_coordinator_android.h
+++ b/chrome/browser/ui/side_panel/internal/android/side_panel_coordinator_android.h
@@ -46,7 +46,8 @@
   SidePanelCoordinatorAndroid& operator=(const SidePanelCoordinatorAndroid&) =
       delete;
 
-  // Implements Java `SidePanelCoordinatorAndroid.Natives#destroy`.
+  // Implements Java `SidePanelCoordinatorAndroid.Natives`. These methods are
+  // called from Java via JNI, see `SidePanelCoordinatorAndroidImpl.java`.
   void Destroy(JNIEnv* env);
 
   // Implements `SidePanelUI`:
diff --git a/chrome/browser/ui/side_panel_container/internal/BUILD.gn b/chrome/browser/ui/side_panel_container/internal/BUILD.gn
index d8b7c16..f7a1e5ab 100644
--- a/chrome/browser/ui/side_panel_container/internal/BUILD.gn
+++ b/chrome/browser/ui/side_panel_container/internal/BUILD.gn
@@ -75,6 +75,7 @@
   deps = [
     ":java",
     "//base:base_junit_test_support",
+    "//chrome/browser/ui/side_panel/android:java",
     "//chrome/browser/ui/side_ui/public:java",
     "//third_party/junit",
     "//third_party/mockito:mockito_java",
diff --git a/chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorFactory.java b/chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorFactory.java
index 1bb975ad..ecae1e1 100644
--- a/chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorFactory.java
+++ b/chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorFactory.java
@@ -11,6 +11,7 @@
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.ui.side_panel.AndroidSidePanelEnabledFn;
+import org.chromium.chrome.browser.ui.side_panel.SidePanelType;
 import org.chromium.chrome.browser.ui.side_ui.SideUiCoordinator;
 
 /** Factory for creating a {@link SidePanelContainerCoordinator}. */
@@ -20,14 +21,24 @@
 
     private SidePanelContainerCoordinatorFactory() {}
 
+    /**
+     * Factory method to create a new SidePanelContainerCoordinator implementation.
+     *
+     * @param parentActivity Parent Activity that will own this instance.
+     * @param sideUiCoordinator Coordinator for the Side Panel UI anchoring view.
+     * @param panelType The type of panel that this coordinator is associated with.
+     * @return SidePanelContainerCoordinator implementation.
+     */
     @Nullable
     public static SidePanelContainerCoordinator create(
-            Activity parentActivity, SideUiCoordinator sideUiCoordinator) {
-        log(TAG, "create", parentActivity, sideUiCoordinator);
+            Activity parentActivity,
+            SideUiCoordinator sideUiCoordinator,
+            @SidePanelType int panelType) {
+        log(TAG, "create", parentActivity, sideUiCoordinator, panelType);
         if (!AndroidSidePanelEnabledFn.isEnabled()) {
             return null;
         }
 
-        return new SidePanelContainerCoordinatorImpl(parentActivity, sideUiCoordinator);
+        return new SidePanelContainerCoordinatorImpl(parentActivity, sideUiCoordinator, panelType);
     }
 }
diff --git a/chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorImpl.java b/chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorImpl.java
index 7273f8ff..6efd62c 100644
--- a/chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorImpl.java
+++ b/chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorImpl.java
@@ -18,6 +18,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.ui.side_panel.SidePanelType;
 import org.chromium.chrome.browser.ui.side_ui.SideUiContainer;
 import org.chromium.chrome.browser.ui.side_ui.SideUiCoordinator;
 import org.chromium.chrome.browser.ui.side_ui.SideUiCoordinator.AnchorSide;
@@ -37,14 +38,25 @@
     private final Activity mParentActivity;
     private final FrameLayout mContainerView;
     private final SideUiCoordinator mSideUiCoordinator;
+    private final @SidePanelType int mPanelType;
 
     private @Nullable SidePanelContent mCurrentContent;
 
+    /**
+     * Constructs a concrete implementation of the SidePanelContainerCoordinator interface.
+     *
+     * @param parentActivity Parent Activity that will own this instance.
+     * @param sideUiCoordinator Coordinator for the Side Panel UI anchoring view.
+     * @param panelType The type of panel that this coordinator is associated with.
+     */
     SidePanelContainerCoordinatorImpl(
-            Activity parentActivity, SideUiCoordinator sideUiCoordinator) {
-        log(TAG, "constructor", parentActivity, sideUiCoordinator);
+            Activity parentActivity,
+            SideUiCoordinator sideUiCoordinator,
+            @SidePanelType int panelType) {
+        log(TAG, "constructor", parentActivity, sideUiCoordinator, panelType);
         mParentActivity = parentActivity;
         mSideUiCoordinator = sideUiCoordinator;
+        mPanelType = panelType;
         mContainerView =
                 (FrameLayout)
                         LayoutInflater.from(mParentActivity)
@@ -92,6 +104,11 @@
     }
 
     @Override
+    public @SidePanelType int getPanelType() {
+        return mPanelType;
+    }
+
+    @Override
     public void destroy() {
         log(TAG, "destroy");
         ThreadUtils.assertOnUiThread();
diff --git a/chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorImplUnitTest.java b/chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorImplUnitTest.java
index 1a06bf3..46327cf 100644
--- a/chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorImplUnitTest.java
+++ b/chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorImplUnitTest.java
@@ -18,6 +18,7 @@
 import org.robolectric.Robolectric;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.ui.side_panel.SidePanelType;
 import org.chromium.chrome.browser.ui.side_ui.SideUiCoordinator;
 
 /** Unit tests for {@link SidePanelContainerCoordinatorImpl}. */
@@ -53,6 +54,7 @@
     }
 
     private SidePanelContainerCoordinatorImpl createSidePanelContainerCoordinator() {
-        return new SidePanelContainerCoordinatorImpl(mTestActivity, mMockSideUiCoordinator);
+        return new SidePanelContainerCoordinatorImpl(
+                mTestActivity, mMockSideUiCoordinator, SidePanelType.TOOLBAR);
     }
 }
diff --git a/chrome/browser/ui/side_panel_container/public/BUILD.gn b/chrome/browser/ui/side_panel_container/public/BUILD.gn
index fba393a..81b933a0 100644
--- a/chrome/browser/ui/side_panel_container/public/BUILD.gn
+++ b/chrome/browser/ui/side_panel_container/public/BUILD.gn
@@ -12,7 +12,10 @@
     "android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContent.java",
     "android/java/src/org/chromium/chrome/browser/ui/side_panel_container/dev/SidePanelDevFeature.java",
   ]
-  deps = [ "//build/android:build_java" ]
+  deps = [
+    "//build/android:build_java",
+    "//chrome/browser/ui/side_panel/android:java",
+  ]
 }
 
 # Public Java factory
diff --git a/chrome/browser/ui/side_panel_container/public/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinator.java b/chrome/browser/ui/side_panel_container/public/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinator.java
index 3a0b8c9..28aa25b 100644
--- a/chrome/browser/ui/side_panel_container/public/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinator.java
+++ b/chrome/browser/ui/side_panel_container/public/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinator.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.ui.side_panel_container;
 
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.chrome.browser.ui.side_panel.SidePanelType;
 
 /** Coordinator of the side panel container UI. */
 @NullMarked
@@ -45,6 +46,14 @@
     /** Returns whether the given {@link SidePanelContent} is shown in this side panel container. */
     boolean isShowing(SidePanelContent sidePanelContent);
 
+    /**
+     * Returns the panel type of the current instance (e.g. content or toolbar height).
+     *
+     * @return SidePanelType panel type.
+     */
+    @SidePanelType
+    int getPanelType();
+
     /** Destroys all objects owned by this coordinator. */
     void destroy();
 }
diff --git a/chrome/browser/ui/tabs/alert/tab_alert_controller.cc b/chrome/browser/ui/tabs/alert/tab_alert_controller.cc
index c3ec5d7..05f96297 100644
--- a/chrome/browser/ui/tabs/alert/tab_alert_controller.cc
+++ b/chrome/browser/ui/tabs/alert/tab_alert_controller.cc
@@ -65,6 +65,28 @@
   return tab_alert_priority.at(first) > tab_alert_priority.at(second);
 }
 
+// Helper class that notifies subscribers if the alert state has changed when
+// the ScopedAlertNotifier is destroyed.
+class TabAlertController::ScopedAlertNotifier {
+ public:
+  explicit ScopedAlertNotifier(TabAlertController* tab_alert_controller)
+      : tab_alert_controller_(tab_alert_controller),
+        previous_alert_(tab_alert_controller->GetAlertToShow()) {}
+
+  ~ScopedAlertNotifier() {
+    std::optional<TabAlert> updated_alert =
+        tab_alert_controller_->GetAlertToShow();
+    if (previous_alert_ != updated_alert) {
+      tab_alert_controller_->alert_to_show_changed_callbacks_.Notify(
+          updated_alert);
+    }
+  }
+
+ private:
+  const raw_ptr<TabAlertController> tab_alert_controller_;
+  std::optional<TabAlert> previous_alert_;
+};
+
 TabAlertController::TabAlertController(TabInterface& tab)
     : tabs::ContentsObservingTabFeature(tab),
       scoped_unowned_user_data_(tab.GetUnownedUserDataHost(), *this) {
@@ -315,11 +337,13 @@
   }
 
   const TabAlert alert = capability_type_to_alert.at(capability_type);
+  ScopedAlertNotifier notifier(this);
   UpdateAlertState(alert, used);
 }
 
 void TabAlertController::MediaPictureInPictureChanged(
     bool is_picture_in_picture) {
+  ScopedAlertNotifier notifier(this);
   UpdateAlertState(TabAlert::kPipPlaying, is_picture_in_picture);
 }
 
@@ -330,6 +354,7 @@
   RecentlyAudibleHelper* const audible_helper =
       RecentlyAudibleHelper::FromWebContents(tab().GetContents());
   CHECK(audible_helper);
+  ScopedAlertNotifier notifier(this);
   UpdateAlertState(TabAlert::kAudioMuting,
                    audible_helper->WasRecentlyAudible() && muted);
 }
@@ -338,6 +363,7 @@
     content::WebContents* contents,
     bool is_capturing_video) {
   if (contents == web_contents()) {
+    ScopedAlertNotifier notifier(this);
     UpdateAlertState(TabAlert::kVideoRecording, is_capturing_video);
   }
 }
@@ -346,6 +372,7 @@
     content::WebContents* contents,
     bool is_capturing_audio) {
   if (contents == web_contents()) {
+    ScopedAlertNotifier notifier(this);
     UpdateAlertState(TabAlert::kAudioRecording, is_capturing_audio);
   }
 }
@@ -354,6 +381,7 @@
     content::WebContents* contents,
     bool is_being_mirrored) {
   if (contents == web_contents()) {
+    ScopedAlertNotifier notifier(this);
     UpdateAlertState(TabAlert::kTabCapturing, is_being_mirrored);
   }
 }
@@ -362,6 +390,7 @@
     content::WebContents* contents,
     bool is_capturing_window) {
   if (contents == web_contents()) {
+    ScopedAlertNotifier notifier(this);
     const bool is_desktop_capturing_active =
         is_capturing_window || MediaCaptureDevicesDispatcher::GetInstance()
                                    ->GetMediaStreamCaptureIndicator()
@@ -374,6 +403,7 @@
     content::WebContents* contents,
     bool is_capturing_display) {
   if (contents == web_contents()) {
+    ScopedAlertNotifier notifier(this);
     const bool is_desktop_capturing_active =
         is_capturing_display || MediaCaptureDevicesDispatcher::GetInstance()
                                     ->GetMediaStreamCaptureIndicator()
@@ -383,19 +413,23 @@
 }
 
 void TabAlertController::OnIsContentDisplayedInHeadsetChanged(bool state) {
+  ScopedAlertNotifier notifier(this);
   UpdateAlertState(TabAlert::kVrPresentingInHeadset, state);
 }
 
 void TabAlertController::OnGlicSharingStateChange(bool is_sharing) {
+  ScopedAlertNotifier notifier(this);
   UpdateAlertState(TabAlert::kGlicSharing, is_sharing);
 }
 
 void TabAlertController::OnGlicAccessingStateChange(bool is_accessing) {
+  ScopedAlertNotifier notifier(this);
   UpdateAlertState(TabAlert::kGlicAccessing, is_accessing);
 }
 
 void TabAlertController::OnActorTabIndicatorStateChanged(
     actor::ui::TabIndicatorStatus tab_indicator_status) {
+  ScopedAlertNotifier notifier(this);
   switch (tab_indicator_status) {
     case actor::ui::TabIndicatorStatus::kNone:
       UpdateAlertState(TabAlert::kActorWaitingOnUser, false);
@@ -416,13 +450,13 @@
   // Muted alert state also needs to update when audible state changes to ensure
   // that the muted alert becomes active if the tab is already muted but is
   // recently audible or inactive after the tab is no longer audible.
-  DidUpdateAudioMutingState(tab().GetContents()->IsAudioMuted());
+  ScopedAlertNotifier notifier(this);
   UpdateAlertState(TabAlert::kAudioPlaying, was_audible);
+  UpdateAlertState(TabAlert::kAudioMuting,
+                   was_audible && tab().GetContents()->IsAudioMuted());
 }
 
 void TabAlertController::UpdateAlertState(TabAlert alert, bool is_active) {
-  std::optional<TabAlert> previous_alert = GetAlertToShow();
-
   if (alert == TabAlert::kAudioRecording ||
       alert == TabAlert::kVideoRecording) {
     UpdateMediaAlert();
@@ -433,11 +467,6 @@
       active_alerts_.erase(alert);
     }
   }
-
-  std::optional<TabAlert> updated_alert = GetAlertToShow();
-  if (previous_alert != updated_alert) {
-    alert_to_show_changed_callbacks_.Notify(updated_alert);
-  }
 }
 
 void TabAlertController::UpdateMediaAlert() {
diff --git a/chrome/browser/ui/tabs/alert/tab_alert_controller.h b/chrome/browser/ui/tabs/alert/tab_alert_controller.h
index 5787873..d8f9b1c 100644
--- a/chrome/browser/ui/tabs/alert/tab_alert_controller.h
+++ b/chrome/browser/ui/tabs/alert/tab_alert_controller.h
@@ -105,6 +105,8 @@
   void OnIsContentDisplayedInHeadsetChanged(bool state) override;
 
  private:
+  class ScopedAlertNotifier;
+
   void OnGlicSharingStateChange(bool is_sharing);
   void OnGlicAccessingStateChange(bool is_accessing);
 
diff --git a/chrome/browser/ui/tabs/features.cc b/chrome/browser/ui/tabs/features.cc
index 8fe4431..fb5a65c 100644
--- a/chrome/browser/ui/tabs/features.cc
+++ b/chrome/browser/ui/tabs/features.cc
@@ -40,6 +40,11 @@
                    "expand_on_hover_click_delay",
                    base::Milliseconds(500));
 
+BASE_FEATURE_PARAM(bool,
+                   kVerticalTabsExpandOnHoverDefaultEnabled,
+                   &kVerticalTabsExpandOnHover,
+                   false);
+
 BASE_FEATURE(kTabSelectionByPointer, base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kHorizontalTabStripComboButton, base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/ui/tabs/features.h b/chrome/browser/ui/tabs/features.h
index 98eeb0f..25c21c4 100644
--- a/chrome/browser/ui/tabs/features.h
+++ b/chrome/browser/ui/tabs/features.h
@@ -37,6 +37,7 @@
 // If this value is 0, no click delay is applied.
 BASE_DECLARE_FEATURE_PARAM(base::TimeDelta,
                            kVerticalTabsExpandOnHoverClickDelay);
+BASE_DECLARE_FEATURE_PARAM(bool, kVerticalTabsExpandOnHoverDefaultEnabled);
 
 BASE_DECLARE_FEATURE(kTabSelectionByPointer);
 
diff --git a/chrome/browser/ui/tabs/glic_tab_sub_menu_model_browsertest.cc b/chrome/browser/ui/tabs/glic_tab_sub_menu_model_browsertest.cc
index 9ffdac8..519bba75 100644
--- a/chrome/browser/ui/tabs/glic_tab_sub_menu_model_browsertest.cc
+++ b/chrome/browser/ui/tabs/glic_tab_sub_menu_model_browsertest.cc
@@ -107,9 +107,9 @@
   void SetUpOnMainThread() override {
     InProcessBrowserTest::SetUpOnMainThread();
     GlicEnabling::SetBypassEnablementChecksForTesting(true);
-    browser()->profile()->GetPrefs()->SetInteger(
-        prefs::kGlicCompletedFre,
-        static_cast<int>(glic::prefs::FreStatus::kCompleted));
+    glic::GlicKeyedService::Get(browser()->profile())
+        ->enabling()
+        .SetCompletedFre(glic::prefs::FreStatus::kCompleted);
   }
 
   void TearDownOnMainThread() override {
diff --git a/chrome/browser/ui/tabs/tab_group_model.cc b/chrome/browser/ui/tabs/tab_group_model.cc
index 066a5109..1dd613c 100644
--- a/chrome/browser/ui/tabs/tab_group_model.cc
+++ b/chrome/browser/ui/tabs/tab_group_model.cc
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/types/pass_key.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "components/tab_groups/tab_group_color.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tab_groups/tab_group_visual_data.h"
@@ -75,9 +76,53 @@
 
 tab_groups::TabGroupColorId TabGroupModel::GetNextColor(
     base::PassKey<TabStripModel>) const {
-  std::vector<tab_groups::TabGroupColorId> used_colors;
-  for (const auto& id_group_pair : groups_) {
-    used_colors.push_back(id_group_pair.second->visual_data()->color());
+  // Count the number of times each color is used.
+  std::map<tab_groups::TabGroupColorId, int> color_usage_counts;
+  for (const auto& id_color_pair : tab_groups::GetTabGroupColorLabelMap()) {
+    color_usage_counts[id_color_pair.first] = 0;
   }
-  return tab_groups::GetNextColor(used_colors);
+
+  for (const auto& id_group_pair : groups_) {
+    ++color_usage_counts[id_group_pair.second->visual_data()->color()];
+  }
+
+  // Compute the minimum number of usages across all colors.
+  int min_usage_count = color_usage_counts.begin()->second;
+  for (const auto& color_usage_pair : color_usage_counts) {
+    min_usage_count = std::min(min_usage_count, color_usage_pair.second);
+  }
+
+  // Get the first color in an order with minimum usage.
+  for (tab_groups::TabGroupColorId color_id : GetColorOrdering()) {
+    if (color_usage_counts[color_id] == min_usage_count) {
+      return color_id;
+    }
+  }
+  NOTREACHED();
+}
+
+// static
+std::vector<tab_groups::TabGroupColorId> TabGroupModel::GetColorOrdering() {
+  if (base::FeatureList::IsEnabled(features::kTabGroupColorRefresh)) {
+    return {tab_groups::TabGroupColorId::kBlue,
+            tab_groups::TabGroupColorId::kPurple,
+            tab_groups::TabGroupColorId::kPink,
+            tab_groups::TabGroupColorId::kRed,
+            tab_groups::TabGroupColorId::kOrange,
+            tab_groups::TabGroupColorId::kYellow,
+            tab_groups::TabGroupColorId::kGreen,
+            tab_groups::TabGroupColorId::kCyan,
+            tab_groups::TabGroupColorId::kGrey};
+  } else {
+    // Use the ordering defined by values of each member in the enum, in
+    // ascending order.
+    const tab_groups::ColorLabelMap& color_map =
+        tab_groups::GetTabGroupColorLabelMap();
+    std::vector<tab_groups::TabGroupColorId> color_ids;
+
+    for (const auto& pair : color_map) {
+      color_ids.push_back(pair.first);
+    }
+    return color_ids;
+  }
 }
diff --git a/chrome/browser/ui/tabs/tab_group_model.h b/chrome/browser/ui/tabs/tab_group_model.h
index 6dcdd74e..7ee6aaa 100644
--- a/chrome/browser/ui/tabs/tab_group_model.h
+++ b/chrome/browser/ui/tabs/tab_group_model.h
@@ -56,6 +56,8 @@
   // should be as distinct from the other groups as possible.
   tab_groups::TabGroupColorId GetNextColor(base::PassKey<TabStripModel>) const;
 
+  static std::vector<tab_groups::TabGroupColorId> GetColorOrdering();
+
   std::vector<tab_groups::TabGroupId> ListTabGroups() const;
 
   // Returns the TabGroupId of the group that most recently contained the active
diff --git a/chrome/browser/ui/tabs/tab_list_bridge.cc b/chrome/browser/ui/tabs/tab_list_bridge.cc
index 26defce..1e9fc58 100644
--- a/chrome/browser/ui/tabs/tab_list_bridge.cc
+++ b/chrome/browser/ui/tabs/tab_list_bridge.cc
@@ -268,7 +268,8 @@
   const int index = GetIndexOfTab(tab);
   CHECK_NE(index, TabStripModel::kNoTab)
       << "Trying to close a tab that doesn't exist in this tab list.";
-  tab_strip_->CloseWebContentsAt(index, TabCloseTypes::CLOSE_NONE);
+  tab_strip_->CloseWebContentsAt(index,
+                                 TabCloseTypes::CLOSE_CREATE_HISTORICAL_TAB);
 }
 
 std::unique_ptr<content::WebContents> TabListBridge::DetachWebContents(
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index 7db62be3..8b9e8faa 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -129,13 +129,17 @@
 class ReentrancyCheck {
  public:
   explicit ReentrancyCheck(bool* guard_flag) : guard_flag_(guard_flag) {
-    CHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    CHECK(!*guard_flag_);
+    ValidateNotReentrant(guard_flag_);
     *guard_flag_ = true;
   }
 
   ~ReentrancyCheck() { *guard_flag_ = false; }
 
+  static void ValidateNotReentrant(bool* guard_flag) {
+    CHECK_CURRENTLY_ON(content::BrowserThread::UI);
+    CHECK(!*guard_flag);
+  }
+
  private:
   const raw_ptr<bool> guard_flag_;
 };
@@ -1315,6 +1319,8 @@
 }
 
 void TabStripModel::CloseWebContentsAt(int index, uint32_t close_types) {
+  ReentrancyCheck::ValidateNotReentrant(&reentrancy_guard_);
+
   CHECK(ContainsIndex(index));
   CloseTabs({GetWebContentsAt(index)}, close_types);
 }
@@ -4250,8 +4256,7 @@
     const int split_index = GetIndexOfTab(split_tab);
     MoveTabToIndexImpl(update_index, split_index, split_tab->GetGroup(),
                        split_tab->IsPinned(), initial_split_active);
-    CloseWebContentsAt(GetIndexOfTab(split_tab),
-                       TabCloseTypes::CLOSE_USER_GESTURE);
+    CloseTabs({split_tab->GetContents()}, TabCloseTypes::CLOSE_USER_GESTURE);
   } else {
     tabs::TabInterface* update_tab = GetTabAtIndex(update_index);
     std::optional<tab_groups::TabGroupId> initial_split_group =
diff --git a/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc b/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc
index 2d3e10b..dce8eae 100644
--- a/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc
@@ -478,6 +478,34 @@
             GetTabStripStateString(tab_strip_model));
 }
 
+IN_PROC_BROWSER_TEST_F(TabStripModelBrowserTest,
+                       TestCloseTabDuringMoveOperation) {
+  TabStripModel* const tab_strip_model = browser()->tab_strip_model();
+  ASSERT_NO_FATAL_FAILURE(
+      PrepareTabstripForSelectionTest(tab_strip_model, 2, 0, {0}));
+  ASSERT_EQ(2, tab_strip_model->count());
+
+  class TabStripModelCloseWebContentsOnChangeObserver
+      : public TabStripModelObserver {
+   public:
+    void OnTabStripModelChanged(
+        TabStripModel* tab_strip_model,
+        const TabStripModelChange& change,
+        const TabStripSelectionChange& selection) override {
+      if (change.type() == TabStripModelChange::Type::kMoved) {
+        tab_strip_model->CloseWebContentsAt(1,
+                                            TabCloseTypes::CLOSE_USER_GESTURE);
+      }
+    }
+  };
+  TabStripModelCloseWebContentsOnChangeObserver close_tab_observer;
+  tab_strip_model->AddObserver(&close_tab_observer);
+
+  EXPECT_DEATH_IF_SUPPORTED(tab_strip_model->MoveWebContentsAt(0, 1, false),
+                            "Check failed");
+  tab_strip_model->RemoveObserver(&close_tab_observer);
+}
+
 class TabStripModelTestTabGroupEntryPointsEnabled
     : public TabStripModelBrowserTest {
  public:
diff --git a/chrome/browser/ui/tabs/tab_strip_prefs.cc b/chrome/browser/ui/tabs/tab_strip_prefs.cc
index f1080e16..67ac393 100644
--- a/chrome/browser/ui/tabs/tab_strip_prefs.cc
+++ b/chrome/browser/ui/tabs/tab_strip_prefs.cc
@@ -44,8 +44,9 @@
   registry->RegisterBooleanPref(prefs::kProjectsPanelPinnedToTabstrip, true);
   registry->RegisterBooleanPref(prefs::kEverythingMenuPinnedToTabstrip, true);
   registry->RegisterBooleanPref(prefs::kVerticalTabsEnabled, false);
-  registry->RegisterBooleanPref(prefs::kVerticalTabsExpandOnHoverEnabled,
-                                false);
+  registry->RegisterBooleanPref(
+      prefs::kVerticalTabsExpandOnHoverEnabled,
+      tabs::kVerticalTabsExpandOnHoverDefaultEnabled.Get());
   registry->RegisterBooleanPref(prefs::kVerticalTabsEnabledFirstTime, false);
   registry->RegisterBooleanPref(prefs::kVerticalTabsCollapsedState, false);
   registry->RegisterIntegerPref(prefs::kVerticalTabsUncollapsedWidth,
diff --git a/chrome/browser/ui/tabs/vertical_tab_strip_state_controller.cc b/chrome/browser/ui/tabs/vertical_tab_strip_state_controller.cc
index 6289bbc..2aaca1e 100644
--- a/chrome/browser/ui/tabs/vertical_tab_strip_state_controller.cc
+++ b/chrome/browser/ui/tabs/vertical_tab_strip_state_controller.cc
@@ -11,6 +11,7 @@
 #include "base/metrics/user_metrics_action.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/to_string.h"
+#include "base/timer/timer.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sessions/session_service.h"
@@ -21,9 +22,11 @@
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
 #include "chrome/browser/ui/tabs/features.h"
+#include "chrome/browser/ui/user_education/browser_user_education_interface.h"
 #include "chrome/browser/ui/views/interaction/browser_elements_views.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/feature_engagement/public/feature_constants.h"
 #include "components/prefs/pref_notifier_impl.h"
 #include "components/prefs/pref_service.h"
 #include "components/sessions/core/session_id.h"
@@ -97,6 +100,16 @@
           GlobalBrowserCollection::GetInstance());
     }
   }
+
+  // Wait before attempting to show the expand on hover IPH on startup. This is
+  // an anti-pattern and is generally discouraged for snooze promos.
+  if (base::SequencedTaskRunner::HasCurrentDefault()) {
+    expand_on_hover_iph_collapse_timer_.Start(
+        FROM_HERE, base::Minutes(10),
+        base::BindOnce(
+            &VerticalTabStripStateController::MaybeShowExpandOnHoverIPH,
+            base::Unretained(this)));
+  }
 }
 
 VerticalTabStripStateController::~VerticalTabStripStateController() {
@@ -188,6 +201,16 @@
   if (delegate_->IsCollapsing()) {
     NotifyCollapseChanged();
   }
+
+  // Wait a short duration before attempting to show the expand on hover IPH
+  // after collapsing.
+  if (base::SequencedTaskRunner::HasCurrentDefault()) {
+    expand_on_hover_iph_startup_timer_.Start(
+        FROM_HERE, base::Seconds(3),
+        base::BindOnce(
+            &VerticalTabStripStateController::MaybeShowExpandOnHoverIPH,
+            base::Unretained(this)));
+  }
 }
 
 int VerticalTabStripStateController::GetUncollapsedWidth() const {
@@ -375,4 +398,16 @@
   }
 }
 
+void VerticalTabStripStateController::MaybeShowExpandOnHoverIPH() {
+  if (tabs::IsVerticalTabsExpandOnHoverFeatureEnabled() &&
+      ShouldDisplayVerticalTabs() &&
+      GetCollapseState() != VerticalTabStripCollapseState::kExpanded &&
+      pref_service_->FindPreference(prefs::kVerticalTabsExpandOnHoverEnabled)
+          ->IsDefaultValue()) {
+    BrowserUserEducationInterface::From(browser_window_)
+        ->MaybeShowFeaturePromo(
+            feature_engagement::kIPHVerticalTabsExpandOnHoverFeature);
+  }
+}
+
 }  // namespace tabs
diff --git a/chrome/browser/ui/tabs/vertical_tab_strip_state_controller.h b/chrome/browser/ui/tabs/vertical_tab_strip_state_controller.h
index 969084a..fedc3f5f 100644
--- a/chrome/browser/ui/tabs/vertical_tab_strip_state_controller.h
+++ b/chrome/browser/ui/tabs/vertical_tab_strip_state_controller.h
@@ -8,6 +8,7 @@
 #include "base/callback_list.h"
 #include "base/memory/raw_ptr.h"
 #include "base/scoped_observation.h"
+#include "base/timer/timer.h"
 #include "chrome/browser/sessions/session_service_base_observer.h"
 #include "chrome/browser/ui/browser_window/public/browser_collection_observer.h"
 #include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
@@ -154,6 +155,8 @@
   // BrowserCollectionObserver:
   void OnBrowserCreated(BrowserWindowInterface* browser) override;
 
+  void MaybeShowExpandOnHoverIPH();
+
   void OnLockCreated();
   void OnLockDestroyed();
 
@@ -187,6 +190,9 @@
   int enable_state_lock_count_ = 0;
 
   bool is_expand_on_hover_enabled_ = false;
+
+  base::OneShotTimer expand_on_hover_iph_startup_timer_;
+  base::OneShotTimer expand_on_hover_iph_collapse_timer_;
 };
 
 }  // namespace tabs
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc
index 665df8f4..fbec1a180 100644
--- a/chrome/browser/ui/ui_features.cc
+++ b/chrome/browser/ui/ui_features.cc
@@ -40,6 +40,7 @@
 BASE_FEATURE(kGlassToolbar, base::FEATURE_DISABLED_BY_DEFAULT);
 BASE_FEATURE(kToolbarGlowUp, base::FEATURE_DISABLED_BY_DEFAULT);
 BASE_FEATURE(kMenuSimplification, base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kTabGroupColorRefresh, base::FEATURE_DISABLED_BY_DEFAULT);
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 BASE_FEATURE(kDseIntegrity, base::FEATURE_ENABLED_BY_DEFAULT);
@@ -301,12 +302,6 @@
                    true);
 
 BASE_FEATURE_PARAM(bool,
-                   kPageActionsMigrationManagePasswords,
-                   &kPageActionsMigration,
-                   "manage_passwords",
-                   true);
-
-BASE_FEATURE_PARAM(bool,
                    kPageActionsMigrationCookieControls,
                    &kPageActionsMigration,
                    "cookie_controls",
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h
index 69aae0a..6f7351b 100644
--- a/chrome/browser/ui/ui_features.h
+++ b/chrome/browser/ui/ui_features.h
@@ -38,6 +38,7 @@
 BASE_DECLARE_FEATURE(kToolbarGlowUp);
 
 BASE_DECLARE_FEATURE(kMenuSimplification);
+BASE_DECLARE_FEATURE(kTabGroupColorRefresh);
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 
@@ -287,7 +288,6 @@
 BASE_DECLARE_FEATURE_PARAM(bool, kPageActionsMigrationIntentPicker);
 BASE_DECLARE_FEATURE_PARAM(bool, kPageActionsMigrationZoom);
 BASE_DECLARE_FEATURE_PARAM(bool, kPageActionsMigrationFileSystemAccess);
-BASE_DECLARE_FEATURE_PARAM(bool, kPageActionsMigrationManagePasswords);
 BASE_DECLARE_FEATURE_PARAM(bool, kPageActionsMigrationCookieControls);
 BASE_DECLARE_FEATURE_PARAM(bool, kPageActionsMigrationAutofillMandatoryReauth);
 BASE_DECLARE_FEATURE_PARAM(bool, kPageActionsMigrationSharingHub);
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
index 461b509..f6d940e 100644
--- a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
+++ b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
@@ -28,12 +28,6 @@
 #include "components/live_caption/caption_util.h"
 #include "components/live_caption/live_caption_bubble_settings.h"
 #include "components/live_caption/pref_names.h"
-#include "components/on_device_translation/buildflags/buildflags.h"
-
-#if BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-#include "components/on_device_translation/public/language_pack.h"
-#endif
-
 #include "components/live_caption/views/caption_bubble.h"
 #include "components/live_caption/views/translation_view_wrapper.h"
 #include "components/soda/soda_installer.h"
@@ -137,21 +131,6 @@
     return controller_.get();
   }
 
-#if BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-  void SimulateLanguagePackProgress(on_device_translation::LanguagePackKey key,
-                                    int progress) {
-    controller_->OnLanguagePackProgress(key, progress);
-  }
-  void SimulateLanguagePackInstalled(
-      on_device_translation::LanguagePackKey key) {
-    controller_->OnLanguagePackInstalled(key);
-  }
-  void SimulateLanguagePackInstallationChanged(
-      on_device_translation::LanguagePackKey key) {
-    controller_->OnLanguagePackInstallationChanged(key);
-  }
-#endif
-
   CaptionBubbleContext* GetCaptionBubbleContext() {
     if (!caption_bubble_context_) {
       caption_bubble_context_ = CaptionBubbleContextBrowser::Create(
@@ -1504,129 +1483,6 @@
   ASSERT_TRUE(GetSourceLanguageButton()->GetVisible());
 }
 
-#if BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-IN_PROC_BROWSER_TEST_P(CaptionBubbleControllerViewsTest,
-                       DownloadProgressLabelTranslation) {
-  GetController();
-
-  EXPECT_FALSE(IsWidgetVisible());
-  ASSERT_FALSE(GetDownloadProgressLabel()->GetVisible());
-
-  OnPartialTranscription(
-      "Quokkas, known for their cute smiles, are also skilled tree climbers, "
-      "able to scale up to 2 meters high!");
-  EXPECT_TRUE(IsWidgetVisible());
-  ASSERT_TRUE(GetLabel()->GetVisible());
-  ASSERT_FALSE(GetDownloadProgressLabel()->GetVisible());
-
-  SimulateLanguagePackProgress(on_device_translation::LanguagePackKey::kEn_Fr,
-                               45);
-  ASSERT_FALSE(GetLabel()->GetVisible());
-  ASSERT_TRUE(GetDownloadProgressLabel()->GetVisible());
-  ASSERT_EQ(u"Downloading French language pack\u2026 45%",
-            GetDownloadProgressLabel()->GetText());
-
-  SimulateLanguagePackInstalled(on_device_translation::LanguagePackKey::kEn_Fr);
-  ASSERT_TRUE(GetLabel()->GetVisible());
-  ASSERT_FALSE(GetDownloadProgressLabel()->GetVisible());
-
-  SimulateLanguagePackProgress(on_device_translation::LanguagePackKey::kAr_En,
-                               30);
-  ASSERT_FALSE(GetLabel()->GetVisible());
-  ASSERT_TRUE(GetDownloadProgressLabel()->GetVisible());
-  ASSERT_EQ(u"Downloading Arabic language pack\u2026 30%",
-            GetDownloadProgressLabel()->GetText());
-
-  SimulateLanguagePackInstalled(on_device_translation::LanguagePackKey::kAr_En);
-  ASSERT_TRUE(GetLabel()->GetVisible());
-  ASSERT_FALSE(GetDownloadProgressLabel()->GetVisible());
-}
-
-IN_PROC_BROWSER_TEST_P(CaptionBubbleControllerViewsTest,
-                       DownloadProgressLabelTranslationCancellation) {
-  GetController();
-
-  OnPartialTranscription(
-      "Tasmanian devils hold the chomping champ title for mammals.");
-  EXPECT_TRUE(IsWidgetVisible());
-
-  SimulateLanguagePackProgress(on_device_translation::LanguagePackKey::kEn_Fr,
-                               12);
-  ASSERT_TRUE(GetDownloadProgressLabel()->GetVisible());
-
-  // By default, the pack is not registered or installed in the test
-  // environment. So OnLanguagePackInstallationChanged should detect it as
-  // cancelled/failed and clear progress.
-  SimulateLanguagePackInstallationChanged(
-      on_device_translation::LanguagePackKey::kEn_Fr);
-
-  ASSERT_TRUE(GetLabel()->GetVisible());
-  ASSERT_FALSE(GetDownloadProgressLabel()->GetVisible());
-}
-
-IN_PROC_BROWSER_TEST_P(CaptionBubbleControllerViewsTest,
-                       DownloadProgressLabelMultipleDownloads) {
-  GetController();
-
-  OnPartialTranscription(
-      "Quokkas, known for their cute smiles, are also skilled tree climbers.");
-  EXPECT_TRUE(IsWidgetVisible());
-
-  SimulateLanguagePackProgress(on_device_translation::LanguagePackKey::kEn_Fr,
-                               40);
-  ASSERT_TRUE(GetDownloadProgressLabel()->GetVisible());
-
-  SimulateLanguagePackProgress(on_device_translation::LanguagePackKey::kAr_En,
-                               60);
-
-  ASSERT_EQ(u"Downloading language packages\u2026 50%",
-            GetDownloadProgressLabel()->GetText());
-
-  SimulateLanguagePackInstalled(on_device_translation::LanguagePackKey::kEn_Fr);
-
-  ASSERT_EQ(u"Downloading Arabic language pack\u2026 60%",
-            GetDownloadProgressLabel()->GetText());
-}
-
-IN_PROC_BROWSER_TEST_P(CaptionBubbleControllerViewsTest,
-                       DownloadProgressLabelMultipleInstalling) {
-  GetController();
-
-  OnPartialTranscription(
-      "Quokkas, known for their cute smiles, are also skilled tree climbers.");
-  EXPECT_TRUE(IsWidgetVisible());
-
-  SimulateLanguagePackProgress(on_device_translation::LanguagePackKey::kEn_Fr,
-                               100);
-  ASSERT_TRUE(GetDownloadProgressLabel()->GetVisible());
-
-  SimulateLanguagePackProgress(on_device_translation::LanguagePackKey::kAr_En,
-                               100);
-
-  ASSERT_EQ(u"Installing language packages\u2026",
-            GetDownloadProgressLabel()->GetText());
-}
-
-IN_PROC_BROWSER_TEST_P(CaptionBubbleControllerViewsTest,
-                       DownloadProgressLabelDownloadingAndInstalling) {
-  GetController();
-
-  OnPartialTranscription(
-      "Quokkas, known for their cute smiles, are also skilled tree climbers.");
-  EXPECT_TRUE(IsWidgetVisible());
-
-  SimulateLanguagePackProgress(on_device_translation::LanguagePackKey::kEn_Fr,
-                               100);
-  ASSERT_TRUE(GetDownloadProgressLabel()->GetVisible());
-
-  SimulateLanguagePackProgress(on_device_translation::LanguagePackKey::kAr_En,
-                               50);
-
-  ASSERT_EQ(u"Downloading Arabic language pack\u2026 50%",
-            GetDownloadProgressLabel()->GetText());
-}
-#endif  // BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-
 IN_PROC_BROWSER_TEST_P(CaptionBubbleControllerViewsTest, HeaderView) {
   OnPartialTranscription(
       "Stoats are able to change their fur color from brown to white in the "
@@ -1834,45 +1690,6 @@
 }
 
 IN_PROC_BROWSER_TEST_P(CaptionBubbleControllerViewsTest,
-                       DownloadProgressLabelSodaInstalling) {
-  speech::SodaInstaller::GetInstance()->NeverDownloadSodaForTesting();
-  GetController();
-
-  OnPartialTranscription(
-      "Quokkas, known for their cute smiles, are also skilled tree climbers.");
-  EXPECT_TRUE(IsWidgetVisible());
-
-  OnSodaProgress(100);
-  ASSERT_TRUE(GetDownloadProgressLabel()->GetVisible());
-  ASSERT_EQ(u"Installing French\u2026", GetDownloadProgressLabel()->GetText());
-}
-
-IN_PROC_BROWSER_TEST_P(CaptionBubbleControllerViewsTest,
-                       DownloadProgressLabelSodaInstalledWhenModelNull) {
-  speech::SodaInstaller::GetInstance()->NeverDownloadSodaForTesting();
-  GetController();
-
-  OnSodaProgress(100);
-  OnSodaInstalled();
-
-  OnPartialTranscription(
-      "Quokkas, known for their cute smiles, are also skilled tree climbers.");
-  EXPECT_TRUE(IsWidgetVisible());
-
-  SimulateLanguagePackProgress(on_device_translation::LanguagePackKey::kEn_Fr,
-                               50);
-  ASSERT_TRUE(GetDownloadProgressLabel()->GetVisible());
-  ASSERT_EQ(u"Downloading French language pack\u2026 50%",
-            GetDownloadProgressLabel()->GetText());
-
-  SimulateLanguagePackInstallationChanged(
-      on_device_translation::LanguagePackKey::kEn_Fr);
-
-  ASSERT_TRUE(GetLabel()->GetVisible());
-  ASSERT_FALSE(GetDownloadProgressLabel()->GetVisible());
-}
-
-IN_PROC_BROWSER_TEST_P(CaptionBubbleControllerViewsTest,
                        AutomaticLanguageDownload) {
   OnLanguageIdentificationEvent("fr-FR");
   OnSodaProgress(12);
diff --git a/chrome/browser/ui/views/file_system_access/file_system_access_usage_bubble_interactive_uitest.cc b/chrome/browser/ui/views/file_system_access/file_system_access_usage_bubble_interactive_uitest.cc
index 03309c7..7c48c2b 100644
--- a/chrome/browser/ui/views/file_system_access/file_system_access_usage_bubble_interactive_uitest.cc
+++ b/chrome/browser/ui/views/file_system_access/file_system_access_usage_bubble_interactive_uitest.cc
@@ -24,9 +24,7 @@
 class FileSystemAccessUsageBubbleInteractiveUiTest : public DialogBrowserTest {
  public:
   FileSystemAccessUsageBubbleInteractiveUiTest() {
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(
-        features::kPageActionsMigration,
-        {{features::kPageActionsMigrationManagePasswords.name, "true"}});
+    scoped_feature_list_.InitAndEnableFeature(features::kPageActionsMigration);
   }
 
   ~FileSystemAccessUsageBubbleInteractiveUiTest() override = default;
diff --git a/chrome/browser/ui/views/frame/vertical_tab_strip_region_view.cc b/chrome/browser/ui/views/frame/vertical_tab_strip_region_view.cc
index e2867ec..ab3ce07 100644
--- a/chrome/browser/ui/views/frame/vertical_tab_strip_region_view.cc
+++ b/chrome/browser/ui/views/frame/vertical_tab_strip_region_view.cc
@@ -793,6 +793,8 @@
   }
 
   if (done_resizing) {
+    resize_area_->SetVisible(!state_controller_->IsCollapsed() ||
+                             !state_controller_->IsExpandOnHoverEnabled());
     base::RecordAction(base::UserMetricsAction(
         new_state.collapsed ? "VerticalTabs_TabStrip_ResizeToCollapsed"
                             : "VerticalTabs_TabStrip_ResizeToUncollapsed"));
@@ -928,6 +930,10 @@
   // the collapsing state.
   bool collapsed = state != tabs::VerticalTabStripCollapseState::kExpanded;
 
+  resize_area_->SetVisible(!collapsed ||
+                           !state_controller_->IsExpandOnHoverEnabled() ||
+                           resize_area_->is_resizing());
+
   // Immediately apply the padding at the start of the collapsing animation.
   const int padding = GetLayoutConstant(
       collapsed ? LayoutConstant::kVerticalTabStripCollapsedHorizontalPadding
@@ -1018,6 +1024,8 @@
 }
 
 void VerticalTabStripRegionView::OnExpandOnHoverEnabledChanged(bool enabled) {
+  resize_area_->SetVisible(!state_controller_->IsCollapsed() || !enabled ||
+                           resize_area_->is_resizing());
   UpdateExpandOnHoverState();
 }
 
@@ -1032,7 +1040,6 @@
     }
     hover_card_animation_lock_.reset();
     is_expanded_on_hover_ = false;
-    resize_area_->SetVisible(true);
     return;
   }
   // If expand on hover is locked (e.g. omnibox popup is open), then we
@@ -1066,7 +1073,6 @@
       }
       hover_card_animation_lock_.reset();
       is_expanded_on_hover_ = false;
-      resize_area_->SetVisible(true);
     }
   }
 
@@ -1099,8 +1105,6 @@
 
 void VerticalTabStripRegionView::AnimateExpandOnHover(bool expand) {
   is_expanded_on_hover_ = expand;
-  // The resize area should not be visible when in the expand on hover state.
-  resize_area_->SetVisible(!expand);
 
   if (expand) {
     base::RecordAction(
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 036bf0d..752f4ae 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -98,7 +98,6 @@
 #include "chrome/browser/ui/views/page_action/page_action_view_params.h"
 #include "chrome/browser/ui/views/page_info/page_info_bubble_specification.h"
 #include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
-#include "chrome/browser/ui/views/passwords/manage_passwords_icon_views.h"
 #include "chrome/browser/ui/views/permissions/chip/permission_chip_view.h"
 #include "chrome/browser/ui/views/permissions/chip/permission_dashboard_view.h"
 #include "chrome/browser/ui/views/sharing_hub/sharing_hub_icon_view.h"
@@ -509,7 +508,6 @@
     if (optimization_guide::features::ShouldEnableOptimizationGuideIconView()) {
       params.types_enabled.push_back(PageActionIconType::kOptimizationGuide);
     }
-    params.types_enabled.push_back(PageActionIconType::kManagePasswords);
     if (!apps::features::ShouldShowLinkCapturingUX()) {
       params.types_enabled.push_back(PageActionIconType::kIntentPicker);
     }
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_controller.cc b/chrome/browser/ui/views/page_action/page_action_icon_controller.cc
index 7f176a5a..e260c51 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_controller.cc
+++ b/chrome/browser/ui/views/page_action/page_action_icon_controller.cc
@@ -32,7 +32,6 @@
 #include "chrome/browser/ui/views/page_action/page_action_icon_container.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_params.h"
 #include "chrome/browser/ui/views/page_action/zoom_view.h"
-#include "chrome/browser/ui/views/passwords/manage_passwords_icon_views.h"
 #include "chrome/browser/ui/views/sharing/sharing_dialog_view.h"
 #include "chrome/browser/ui/views/sharing/sharing_icon_view.h"
 #include "chrome/browser/ui/views/sharing_hub/sharing_hub_icon_view.h"
@@ -115,13 +114,6 @@
                       params.browser, params.icon_label_bubble_delegate,
                       params.page_action_icon_delegate));
         break;
-      case PageActionIconType::kManagePasswords:
-        DCHECK(params.command_updater);
-        add_page_action_icon(
-            type, std::make_unique<ManagePasswordsIconViews>(
-                      params.command_updater, params.icon_label_bubble_delegate,
-                      params.page_action_icon_delegate, params.browser));
-        break;
       case PageActionIconType::kMandatoryReauth:
         add_page_action_icon(
             type, std::make_unique<autofill::MandatoryReauthIconView>(
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc b/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc
index 74f4f41..31ff2f1 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc
@@ -13,7 +13,6 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
 #include "chrome/browser/ui/views/page_action/page_action_view.h"
-#include "chrome/browser/ui/views/passwords/manage_passwords_icon_views.h"
 #include "chrome/browser/ui/views/passwords/password_bubble_view_base.h"
 #include "chrome/browser/ui/views/toolbar/pinned_toolbar_actions_container.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
@@ -32,14 +31,10 @@
 #include "ui/views/test/widget_test.h"
 #include "ui/views/view_utils.h"
 
-class ManagePasswordsIconViewTest : public ManagePasswordsTest,
-                                    public ::testing::WithParamInterface<bool> {
+class ManagePasswordsIconViewTest : public ManagePasswordsTest {
  public:
   ManagePasswordsIconViewTest() {
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(
-        features::kPageActionsMigration,
-        {{features::kPageActionsMigrationManagePasswords.name,
-          IsMigrationEnabled() ? "true" : "false"}});
+    scoped_feature_list_.InitAndEnableFeature(features::kPageActionsMigration);
   }
 
   ManagePasswordsIconViewTest(const ManagePasswordsIconViewTest&) = delete;
@@ -61,12 +56,6 @@
 
   std::u16string GetTooltipText() { return GetIcon()->GetTooltipText(); }
 
-  bool IsMigrationEnabled() const { return GetParam(); }
-
-  static std::string GetTestSuffix(const testing::TestParamInfo<bool>& info) {
-    return info.param ? "MigrationOn" : "MigrationOff";
-  }
-
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -85,12 +74,12 @@
   }
 };
 
-IN_PROC_BROWSER_TEST_P(ManagePasswordsIconViewTest, DefaultStateIsInactive) {
+IN_PROC_BROWSER_TEST_F(ManagePasswordsIconViewTest, DefaultStateIsInactive) {
   EXPECT_EQ(password_manager::ui::INACTIVE_STATE, ViewState());
   EXPECT_FALSE(GetIcon()->GetVisible());
 }
 
-IN_PROC_BROWSER_TEST_P(ManagePasswordsIconViewTest, PendingState) {
+IN_PROC_BROWSER_TEST_F(ManagePasswordsIconViewTest, PendingState) {
   SetupPendingPassword();
   EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE, ViewState());
   EXPECT_TRUE(GetIcon()->GetVisible());
@@ -98,7 +87,7 @@
   EXPECT_EQ(std::u16string(), GetTooltipText());
 }
 
-IN_PROC_BROWSER_TEST_P(ManagePasswordsIconViewTest, ManageState) {
+IN_PROC_BROWSER_TEST_F(ManagePasswordsIconViewTest, ManageState) {
   SetupManagingPasswords();
   EXPECT_EQ(password_manager::ui::MANAGE_STATE, ViewState());
   EXPECT_TRUE(GetIcon()->GetVisible());
@@ -106,7 +95,7 @@
             GetTooltipText());
 }
 
-IN_PROC_BROWSER_TEST_P(ManagePasswordsIconViewTest, CloseOnClick) {
+IN_PROC_BROWSER_TEST_F(ManagePasswordsIconViewTest, CloseOnClick) {
   SetupPendingPassword();
   EXPECT_TRUE(GetIcon()->GetVisible());
   views::test::InteractionTestUtilSimulatorViews::PressButton(
@@ -116,7 +105,7 @@
   content::RunAllPendingInMessageLoop();
 }
 
-IN_PROC_BROWSER_TEST_P(ManagePasswordsIconViewTestToolbarPinningOnly,
+IN_PROC_BROWSER_TEST_F(ManagePasswordsIconViewTestToolbarPinningOnly,
                        ShowPasswordsBubbleOrPage) {
   const GURL passwords_url = GURL("chrome://password-manager/");
   PinnedToolbarActionsModel::Get(browser()->profile())
@@ -149,13 +138,3 @@
       button, ui::test::InteractionTestUtil::InputType::kDontCare);
   EXPECT_EQ(GetActiveWebContents()->GetVisibleURL(), passwords_url);
 }
-
-INSTANTIATE_TEST_SUITE_P(All,
-                         ManagePasswordsIconViewTest,
-                         ::testing::Bool(),
-                         &ManagePasswordsIconViewTest::GetTestSuffix);
-
-INSTANTIATE_TEST_SUITE_P(All,
-                         ManagePasswordsIconViewTestToolbarPinningOnly,
-                         ::testing::Bool(),
-                         &ManagePasswordsIconViewTest::GetTestSuffix);
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_icon_views.cc b/chrome/browser/ui/views/passwords/manage_passwords_icon_views.cc
deleted file mode 100644
index 5dec072..0000000
--- a/chrome/browser/ui/views/passwords/manage_passwords_icon_views.cc
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2013 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/passwords/manage_passwords_icon_views.h"
-
-#include "base/feature_list.h"
-#include "chrome/app/chrome_command_ids.h"
-#include "chrome/app/vector_icons/vector_icons.h"
-#include "chrome/browser/ui/actions/chrome_action_id.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_actions.h"
-#include "chrome/browser/ui/browser_element_identifiers.h"
-#include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
-#include "chrome/browser/ui/ui_features.h"
-#include "chrome/browser/ui/user_education/browser_user_education_interface.h"
-#include "chrome/browser/ui/views/passwords/manage_passwords_page_action_controller.h"
-#include "chrome/browser/ui/views/passwords/password_bubble_view_base.h"
-#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/feature_engagement/public/feature_constants.h"
-#include "components/password_manager/core/common/password_manager_ui.h"
-#include "components/vector_icons/vector_icons.h"
-#include "content/public/browser/web_contents.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/metadata/metadata_impl_macros.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/views/accessibility/view_accessibility.h"
-#include "ui/views/view_class_properties.h"
-
-ManagePasswordsIconViews::ManagePasswordsIconViews(
-    CommandUpdater* updater,
-    IconLabelBubbleView::Delegate* icon_label_bubble_delegate,
-    PageActionIconView::Delegate* page_action_icon_delegate,
-    Browser* browser)
-    : PageActionIconView(updater,
-                         IDC_MANAGE_PASSWORDS_FOR_PAGE,
-                         icon_label_bubble_delegate,
-                         page_action_icon_delegate,
-                         "ManagePasswords",
-                         kActionShowPasswordsBubbleOrPage,
-                         browser) {
-  // Password icon should not be mirrored in RTL.
-  image_container_view()->SetFlipCanvasOnPaintForRTLUI(false);
-  SetProperty(views::kElementIdentifierKey, kPasswordsOmniboxKeyIconElementId);
-
-  const std::u16string tooltip_and_accessible_name_text =
-      GetTextForTooltipAndAccessibleName();
-  GetViewAccessibility().SetName(tooltip_and_accessible_name_text);
-  UpdateTooltipText();
-
-  // TODO(b/353777476): Strip out pinned toolbar button code into a shared
-  // controller for page action and pinned button.
-  BrowserActions* browser_actions = browser->browser_actions();
-  actions::ActionManager::Get()
-      .FindAction(kActionShowPasswordsBubbleOrPage,
-                  browser_actions->root_action_item())
-      ->SetTooltipText(tooltip_and_accessible_name_text);
-}
-
-ManagePasswordsIconViews::~ManagePasswordsIconViews() = default;
-
-void ManagePasswordsIconViews::SetState(password_manager::ui::State state,
-                                        bool is_blocklisted) {
-  if (state_ == state && is_blocklisted_ == is_blocklisted) {
-    return;
-  }
-  // If there is an opened bubble for the current icon it should go away.
-  PasswordBubbleViewBase::CloseCurrentBubble();
-  state_ = state;
-  is_blocklisted_ = is_blocklisted;
-  UpdateUiForState();
-  const std::u16string tooltip_and_accessible_name_text =
-      GetTextForTooltipAndAccessibleName();
-  GetViewAccessibility().SetName(tooltip_and_accessible_name_text);
-  UpdateTooltipText();
-  UpdateIconImage();
-
-  // TODO(b/353777476): Strip out pinned toolbar button code into a shared
-  // controller for page action and pinned button.
-  BrowserActions* browser_actions = browser()->browser_actions();
-  actions::ActionManager::Get()
-      .FindAction(kActionShowPasswordsBubbleOrPage,
-                  browser_actions->root_action_item())
-      ->SetTooltipText(tooltip_and_accessible_name_text);
-}
-
-void ManagePasswordsIconViews::UpdateUiForState() {
-  BrowserActions* browser_actions = browser()->browser_actions();
-  actions::ActionManager::Get()
-      .FindAction(kActionShowPasswordsBubbleOrPage,
-                  browser_actions->root_action_item())
-      ->SetProperty(kActionItemUnderlineIndicatorKey,
-                    (state_ != password_manager::ui::INACTIVE_STATE));
-
-  // Hides the page action icon if the associated toolbar icon is pinned.
-  if (state_ == password_manager::ui::INACTIVE_STATE ||
-      delegate()->ShouldHidePageActionIcon(this)) {
-    SetVisible(false);
-    return;
-  }
-
-  SetVisible(true);
-
-  // We may be about to automatically pop up a passwords bubble.
-  // Force layout of the icon's parent now; the bubble will be incorrectly
-  // positioned otherwise, as the icon won't have been drawn into position.
-  parent()->DeprecatedLayoutImmediately();
-}
-
-views::BubbleDialogDelegate* ManagePasswordsIconViews::GetBubble() const {
-  return PasswordBubbleViewBase::manage_password_bubble();
-}
-
-void ManagePasswordsIconViews::UpdateImpl() {
-  if (!GetWebContents()) {
-    return;
-  }
-
-  ManagePasswordsUIController::FromWebContents(GetWebContents())
-      ->UpdateIconAndBubbleState(this);
-}
-
-void ManagePasswordsIconViews::OnExecuting(
-    PageActionIconView::ExecuteSource source) {
-  BrowserUserEducationInterface::From(browser())->NotifyFeaturePromoFeatureUsed(
-      feature_engagement::kIPHPasswordsSaveRecoveryPromoFeature,
-      FeaturePromoFeatureUsedAction::kClosePromoIfPresent);
-}
-
-bool ManagePasswordsIconViews::OnMousePressed(const ui::MouseEvent& event) {
-  bool result = PageActionIconView::OnMousePressed(event);
-  PasswordBubbleViewBase::CloseCurrentBubble();
-  return result;
-}
-
-const gfx::VectorIcon& ManagePasswordsIconViews::GetVectorIcon() const {
-  return is_blocklisted_ && base::FeatureList::IsEnabled(
-                                features::kSavePasswordsContextualUi)
-             ? vector_icons::kPasswordManagerOffIcon
-             : vector_icons::kPasswordManagerIcon;
-}
-
-std::u16string ManagePasswordsIconViews::GetTextForTooltipAndAccessibleName()
-    const {
-  return ManagePasswordsPageActionController::GetManagePasswordsTooltipText(
-      state_, is_blocklisted_);
-}
-
-void ManagePasswordsIconViews::AboutToRequestFocusFromTabTraversal(
-    bool reverse) {
-  if (IsBubbleShowing()) {
-    PasswordBubbleViewBase::ActivateBubble();
-  }
-}
-
-BEGIN_METADATA(ManagePasswordsIconViews)
-END_METADATA
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_icon_views.h b/chrome/browser/ui/views/passwords/manage_passwords_icon_views.h
deleted file mode 100644
index aae9e28..0000000
--- a/chrome/browser/ui/views/passwords/manage_passwords_icon_views.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2013 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_PASSWORDS_MANAGE_PASSWORDS_ICON_VIEWS_H_
-#define CHROME_BROWSER_UI_VIEWS_PASSWORDS_MANAGE_PASSWORDS_ICON_VIEWS_H_
-
-#include "chrome/browser/ui/passwords/manage_passwords_icon_view.h"
-#include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
-#include "ui/base/metadata/metadata_header_macros.h"
-#include "ui/views/controls/image_view.h"
-
-class CommandUpdater;
-
-// View for the password icon in the Omnibox.
-class ManagePasswordsIconViews : public ManagePasswordsIconView,
-                                 public PageActionIconView {
-  METADATA_HEADER(ManagePasswordsIconViews, PageActionIconView)
-
- public:
-  ManagePasswordsIconViews(
-      CommandUpdater* updater,
-      IconLabelBubbleView::Delegate* icon_label_bubble_delegate,
-      PageActionIconView::Delegate* page_action_icon_delegate,
-      Browser* browser);
-  ManagePasswordsIconViews(const ManagePasswordsIconViews&) = delete;
-  ManagePasswordsIconViews& operator=(const ManagePasswordsIconViews&) = delete;
-  ~ManagePasswordsIconViews() override;
-
-  // ManagePasswordsIconView:
-  void SetState(password_manager::ui::State state,
-                bool is_blocklisted) override;
-
-  // PageActionIconView:
-  views::BubbleDialogDelegate* GetBubble() const override;
-  void UpdateImpl() override;
-  void OnExecuting(PageActionIconView::ExecuteSource source) override;
-  bool OnMousePressed(const ui::MouseEvent& event) override;
-  const gfx::VectorIcon& GetVectorIcon() const override;
-  std::u16string GetTextForTooltipAndAccessibleName() const override;
-
-  // views::View:
-  void AboutToRequestFocusFromTabTraversal(bool reverse) override;
-
- private:
-  friend class ManagePasswordsIconViewTest;
-
-  // Updates the UI to match |state_|.
-  void UpdateUiForState();
-
-  password_manager::ui::State state_ = password_manager::ui::INACTIVE_STATE;
-  bool is_blocklisted_ = false;
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_PASSWORDS_MANAGE_PASSWORDS_ICON_VIEWS_H_
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_page_action_controller.cc b/chrome/browser/ui/views/passwords/manage_passwords_page_action_controller.cc
index 50eb552..b757a81 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_page_action_controller.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_page_action_controller.cc
@@ -91,9 +91,7 @@
 
 ManagePasswordsPageActionController::ManagePasswordsPageActionController(
     page_actions::PageActionController& page_action_controller)
-    : page_action_controller_(page_action_controller) {
-  CHECK(IsPageActionMigrated(PageActionIconType::kManagePasswords));
-}
+    : page_action_controller_(page_action_controller) {}
 
 ManagePasswordsPageActionController::~ManagePasswordsPageActionController() =
     default;
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_page_action_controller_interactive_uitest.cc b/chrome/browser/ui/views/passwords/manage_passwords_page_action_controller_interactive_uitest.cc
index 507df0a..a46e077 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_page_action_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_page_action_controller_interactive_uitest.cc
@@ -26,9 +26,7 @@
 class ManagePasswordsControllerTest : public ManagePasswordsTest {
  public:
   ManagePasswordsControllerTest() {
-    scoped_feature_list_.InitAndEnableFeatureWithParameters(
-        features::kPageActionsMigration,
-        {{features::kPageActionsMigrationManagePasswords.name, "true"}});
+    scoped_feature_list_.InitAndEnableFeature(features::kPageActionsMigration);
   }
 
   ~ManagePasswordsControllerTest() override = default;
diff --git a/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc b/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc
index 3ede2d12..b32b64df 100644
--- a/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc
+++ b/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc
@@ -34,7 +34,6 @@
 #include "chrome/browser/ui/views/controls/rich_hover_button.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/passwords/manage_passwords_details_view.h"
-#include "chrome/browser/ui/views/passwords/manage_passwords_icon_views.h"
 #include "chrome/browser/ui/views/passwords/manage_passwords_list_view.h"
 #include "chrome/browser/ui/views/passwords/manage_passwords_view.h"
 #include "chrome/browser/ui/views/passwords/manage_passwords_view_ids.h"
diff --git a/chrome/browser/ui/views/passwords/password_bubble_view_base.cc b/chrome/browser/ui/views/passwords/password_bubble_view_base.cc
index 9b78e45..8f91c0e 100644
--- a/chrome/browser/ui/views/passwords/password_bubble_view_base.cc
+++ b/chrome/browser/ui/views/passwords/password_bubble_view_base.cc
@@ -22,7 +22,6 @@
 #include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
 #include "chrome/browser/ui/views/page_action/page_action_view.h"
-#include "chrome/browser/ui/views/passwords/manage_passwords_icon_views.h"
 #include "chrome/browser/ui/views/passwords/manage_passwords_view.h"
 #include "chrome/browser/ui/views/passwords/move_to_account_store_bubble_view.h"
 #include "chrome/browser/ui/views/passwords/password_add_username_view.h"
diff --git a/chrome/browser/ui/views/tabs/groups/tab_group_editor_bubble_view.cc b/chrome/browser/ui/views/tabs/groups/tab_group_editor_bubble_view.cc
index 9e73039..e63f743e 100644
--- a/chrome/browser/ui/views/tabs/groups/tab_group_editor_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/groups/tab_group_editor_bubble_view.cc
@@ -1087,12 +1087,26 @@
 
 tab_groups::TabGroupColorId TabGroupEditorBubbleView::InitColorSet() {
   colors_.clear();
-  const tab_groups::ColorLabelMap& color_map =
-      tab_groups::GetTabGroupColorLabelMap();
+  tab_groups::ColorLabelMap color_map = tab_groups::GetTabGroupColorLabelMap();
+
+  // In the tab group color update, the yellow and pink are better described
+  // as lime and magenta. We update the tooltip accordingly.
+  if (base::FeatureList::IsEnabled(features::kTabGroupColorRefresh)) {
+    color_map[tab_groups::TabGroupColorId::kYellow] =
+        l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_LIME);
+
+    color_map[tab_groups::TabGroupColorId::kPink] =
+        l10n_util::GetStringUTF16(IDS_TAB_GROUP_COLOR_MAGENTA);
+  }
+
+  const std::vector<tab_groups::TabGroupColorId> color_ordering =
+      TabGroupModel::GetColorOrdering();
 
   // TODO(tluk) remove the reliance on the ordering of the color pairs in the
   // vector and use the ColorLabelMap structure instead.
-  std::ranges::copy(color_map, std::back_inserter(colors_));
+  for (const tab_groups::TabGroupColorId color_id : color_ordering) {
+    colors_.emplace_back(color_id, color_map.at(color_id));
+  }
 
   // Keep track of the current group's color, to be returned as the initial
   // selected value.
diff --git a/chrome/browser/ui/views/tabs/vertical/vertical_tab_group_view.cc b/chrome/browser/ui/views/tabs/vertical/vertical_tab_group_view.cc
index 73889d27..365fd93 100644
--- a/chrome/browser/ui/views/tabs/vertical/vertical_tab_group_view.cc
+++ b/chrome/browser/ui/views/tabs/vertical/vertical_tab_group_view.cc
@@ -55,7 +55,6 @@
 constexpr int kGroupLineCornerRadius = 4;
 constexpr int kGroupHeaderHeight = 26;
 constexpr int kGroupHeaderVerticalMargin = 4;
-constexpr int kTabLeadingPadding = 10;
 
 const TabGroup* GetTabGroupFromNode(TabCollectionNode* node) {
   CHECK(node);
@@ -163,7 +162,8 @@
   // aligned with the header.
   if (tab_strip_collapse_state ==
       tabs::VerticalTabStripCollapseState::kExpanded) {
-    group_line_bounds.set_x((kTabLeadingPadding - kGroupLineWidth) / 2);
+    group_line_bounds.set_x(
+        (VerticalTabGroupView::kTabLeadingPadding - kGroupLineWidth) / 2);
     group_line_bounds.set_y(height);
   }
 
@@ -186,7 +186,7 @@
                 tabs::VerticalTabStripCollapseState::kExpanded
             ? GetLayoutConstant(
                   LayoutConstant::kVerticalTabStripCollapsedHorizontalPadding)
-            : kTabLeadingPadding);
+            : VerticalTabGroupView::kTabLeadingPadding);
     // If width is bounded, child views should respect the width constraints
     // and take up the available width excluding trailing horizontal padding.
     if (size_bounds.width().is_bounded()) {
diff --git a/chrome/browser/ui/views/tabs/vertical/vertical_tab_group_view.h b/chrome/browser/ui/views/tabs/vertical/vertical_tab_group_view.h
index a562ec1..26e8a188 100644
--- a/chrome/browser/ui/views/tabs/vertical/vertical_tab_group_view.h
+++ b/chrome/browser/ui/views/tabs/vertical/vertical_tab_group_view.h
@@ -34,6 +34,8 @@
   METADATA_HEADER(VerticalTabGroupView, views::View)
 
  public:
+  static constexpr int kTabLeadingPadding = 10;
+
   explicit VerticalTabGroupView(TabCollectionNode* collection_node);
   VerticalTabGroupView(const VerticalTabGroupView&) = delete;
   VerticalTabGroupView& operator=(const VerticalTabGroupView&) = delete;
diff --git a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_top_container.cc b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_top_container.cc
index 85c3150..46aeff1d 100644
--- a/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_top_container.cc
+++ b/chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_top_container.cc
@@ -12,12 +12,14 @@
 #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
 #include "chrome/browser/ui/tabs/vertical_tab_strip_state_controller.h"
 #include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/user_education/browser_user_education_interface.h"
 #include "chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_everything_menu.h"
 #include "chrome/browser/ui/views/frame/vertical_tab_strip_region_view.h"
 #include "chrome/browser/ui/views/tabs/shared/tab_strip_combo_button.h"
 #include "chrome/browser/ui/views/tabs/shared/tab_strip_flat_edge_button.h"
 #include "chrome/browser/ui/views/tabs/vertical/top_container_button.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/feature_engagement/public/feature_constants.h"
 #include "components/saved_tab_groups/public/features.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -317,6 +319,11 @@
     ui::mojom::MenuSourceType source_type) {
   if (tabs::IsVerticalTabsExpandOnHoverFeatureEnabled() &&
       source == collapse_button_) {
+    BrowserUserEducationInterface::From(browser_)
+        ->NotifyFeaturePromoFeatureUsed(
+            feature_engagement::kIPHVerticalTabsExpandOnHoverFeature,
+            FeaturePromoFeatureUsedAction::kClosePromoIfPresent);
+
     // Reset the menu runner to avoid issues when the collapse button is
     // clicked multiple times.
     context_menu_runner_.reset();
diff --git a/chrome/browser/ui/views/tabs/vertical/vertical_tab_view.cc b/chrome/browser/ui/views/tabs/vertical/vertical_tab_view.cc
index b76c231..ab35d56 100644
--- a/chrome/browser/ui/views/tabs/vertical/vertical_tab_view.cc
+++ b/chrome/browser/ui/views/tabs/vertical/vertical_tab_view.cc
@@ -41,6 +41,7 @@
 #include "chrome/browser/ui/views/tabs/vertical/tab_collection_node.h"
 #include "chrome/browser/ui/views/tabs/vertical/vertical_split_tab_view.h"
 #include "chrome/browser/ui/views/tabs/vertical/vertical_tab_drag_handler.h"
+#include "chrome/browser/ui/views/tabs/vertical/vertical_tab_group_view.h"
 #include "chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_controller.h"
 #include "chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_utils.h"
 #include "chrome/browser/ui/views/tabs/vertical/vertical_tab_strip_view.h"
@@ -82,7 +83,7 @@
 namespace {
 constexpr int kIconDesignWidth = 16;
 constexpr int kTitleMinWidth = 10;
-constexpr int kHorizontalInset = 7;
+constexpr int kHorizontalInset = 8;
 constexpr int kDefaultPadding = 4;
 constexpr int kFocusRingInset = 0.0f;
 
@@ -742,7 +743,12 @@
 
   if (pinned_) {
     child_visibility_map[close_button_] = false;
-  } else if (IsCollapsedWidth(width)) {
+  } else if (
+      // When uncollapsing the tabstrip, intentionally start showing the close
+      // button on active non-hovered tabs a little bit sooner than reaching the
+      // uncollapsed min width, because otherwise the close buttons in a grouped
+      // split tab will visibly show up at different times due to rounding.
+      width < UncollapsedMinWidth() - 3) {
     child_visibility_map[close_button_] = active_ && hovered_;
   } else {
     child_visibility_map[close_button_] = active_ || hovered_;
@@ -1146,12 +1152,27 @@
              *this);
 }
 
-bool VerticalTabView::IsCollapsedWidth(int width) const {
-  return width < VerticalTabStripRegionView::kCollapsedWidth;
+// static
+int VerticalTabView::UncollapsedMinWidth() {
+  // This is the width of a tab in a split that is in a tab group, while the
+  // tab strip is in the narrowest uncollapsed state.
+  return (VerticalTabStripRegionView::kUncollapsedMinWidth -
+          2 * GetLayoutConstant(
+                  LayoutConstant::kVerticalTabStripUncollapsedPadding) -
+          VerticalSplitTabView::kSplitViewGap -
+          VerticalTabGroupView::kTabLeadingPadding) /
+         2;
+}
+
+// static
+int VerticalTabView::CollapsedWidth() {
+  return VerticalTabStripRegionView::kCollapsedWidth -
+         2 * GetLayoutConstant(
+                 LayoutConstant::kVerticalTabStripCollapsedHorizontalPadding);
 }
 
 bool VerticalTabView::IsInExpandOnHover(int width) const {
-  return collapsed_ && !IsCollapsedWidth(width);
+  return collapsed_ && width > CollapsedWidth();
 }
 
 const tabs::TabInterface* VerticalTabView::GetTabInterface() const {
diff --git a/chrome/browser/ui/views/tabs/vertical/vertical_tab_view.h b/chrome/browser/ui/views/tabs/vertical/vertical_tab_view.h
index 223505c..8723f2e8 100644
--- a/chrome/browser/ui/views/tabs/vertical/vertical_tab_view.h
+++ b/chrome/browser/ui/views/tabs/vertical/vertical_tab_view.h
@@ -189,7 +189,10 @@
   TabStyle::TabSelectionState GetSelectionState() const;
 
   bool IsDragging() const;
-  bool IsCollapsedWidth(int width) const;
+
+  static int UncollapsedMinWidth();
+  static int CollapsedWidth();
+
   bool IsInExpandOnHover(int width) const;
 
   const tabs::TabInterface* GetTabInterface() const;
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service.cc b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
index 22d1055..865c12b 100644
--- a/chrome/browser/ui/views/user_education/browser_user_education_service.cc
+++ b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
@@ -1813,6 +1813,22 @@
               "Triggered when there are enough tabs in the tabstrip and"
               "the size of the tabs are shrunk significantly compared to their "
               "ideal width.")));
+
+  // kIPHVerticalTabsExpandOnHoverFeature:
+  const auto expand_on_hover_iph_body_string_id =
+      tabs::kVerticalTabsExpandOnHoverDefaultEnabled.Get()
+          ? IDS_VERTICAL_TABS_EXPAND_ON_HOVER_DEFAULT_ENABLED_IPH_BODY
+          : IDS_VERTICAL_TABS_EXPAND_ON_HOVER_DEFAULT_DISABLED_IPH_BODY;
+  registry.RegisterFeature(std::move(
+      FeaturePromoSpecification::CreateForSnoozePromo(
+          feature_engagement::kIPHVerticalTabsExpandOnHoverFeature,
+          kVerticalTabStripCollapseButtonElementId,
+          expand_on_hover_iph_body_string_id)
+          .SetBubbleArrow(HelpBubbleArrow::kTopLeft)
+          .SetBubbleIcon(kLightbulbOutlineIcon)
+          .SetMetadata(148, "charlesmeng@chromium.org",
+                       "Triggered when the vertical tabs is enabled and the "
+                       "user has not enabled expand on hover before.")));
 }
 
 void MaybeRegisterChromeFeaturePromos(
diff --git a/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.mojom b/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.mojom
index 286ba90..afc0fad1a 100644
--- a/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.mojom
+++ b/chrome/browser/ui/webui/ai_overlay_dialog/tools/tools.mojom
@@ -32,12 +32,16 @@
   // Browser tools
 
   // Open a URL.
+  //
+  // url: Exact URL. Use PerformSearch to find a page instead of guessing a URL.
   OpenUrl(string url, bool new_tab) => result<bool, string>;
 
   // Search using the default search engine.
   PerformSearch(string query, bool new_tab) => result<bool, string>;
 
   // Switch to another open tab by fuzzy matching name or URL.
+  //
+  // query: keyword from the tab name or URL.
   SwitchTab(string query) => result<SwitchTabResult, string>;
 
   // Close the current browser tab.
@@ -55,6 +59,8 @@
   // Page tools
 
   // Highlight and scroll to specific text on the page.
+  //
+  // query: A short phrase copied exactly as it appears in the page content.
   FindAndHighlight(string query) => result<bool, string>;
 
   // Scroll the viewport in a specific direction.
@@ -69,6 +75,8 @@
   // Pause video playback.
   PauseVideo() => result<bool, string>;
 
-  // Jump the video to a specific timecode (e.g. '1:45').
+  // Jump the video to a specific timecode.
+  //
+  // timecode: from the video transcript. Format: "1:45", "0:30", "1:02:15"
   SeekToTimestamp(string timecode) => result<bool, string>;
 };
diff --git a/chrome/browser/ui/webui/ash/settings/BUILD.gn b/chrome/browser/ui/webui/ash/settings/BUILD.gn
index e8e8626..7cf24a1 100644
--- a/chrome/browser/ui/webui/ash/settings/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/settings/BUILD.gn
@@ -69,6 +69,7 @@
     "//chrome/browser/ash/login/quick_unlock",
     "//chrome/browser/ash/login/screens",
     "//chrome/browser/ash/system_web_apps/apps/personalization_app",
+    "//chrome/browser/nearby_sharing",
     "//chrome/browser/nearby_sharing/common",
     "//chrome/browser/nearby_sharing/contacts",
     "//chrome/browser/profiles:profile",
diff --git a/chrome/browser/ui/webui/ash/settings/pages/multidevice/BUILD.gn b/chrome/browser/ui/webui/ash/settings/pages/multidevice/BUILD.gn
index 8ef871fd..af2221d 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/multidevice/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/settings/pages/multidevice/BUILD.gn
@@ -34,6 +34,7 @@
     "//chrome/app:generated_resources",
     "//chrome/browser/ash/login/quick_unlock",
     "//chrome/browser/ash/phonehub",
+    "//chrome/browser/nearby_sharing",
     "//chrome/browser/nearby_sharing/common",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui/ash/session",
@@ -76,6 +77,7 @@
     "//chrome/browser",
     "//chrome/browser/ash/eche_app",
     "//chrome/browser/ash/login/users:test_support",
+    "//chrome/browser/nearby_sharing",
     "//chrome/browser/nearby_sharing/common",
     "//chrome/browser/ui/webui/ash/settings/search",
     "//chrome/common",
diff --git a/chrome/browser/ui/webui/ash/settings/services/settings_manager/BUILD.gn b/chrome/browser/ui/webui/ash/settings/services/settings_manager/BUILD.gn
index 77791069..fd22ddc0 100644
--- a/chrome/browser/ui/webui/ash/settings/services/settings_manager/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/settings/services/settings_manager/BUILD.gn
@@ -75,6 +75,7 @@
     "//chrome/browser/ash/multidevice_setup",
     "//chrome/browser/ash/phonehub",
     "//chrome/browser/ash/printing",
+    "//chrome/browser/nearby_sharing",
     "//chrome/browser/signin",
     "//chrome/browser/ui/ash/graduation",
     "//chrome/browser/ui/webui/ash/settings/constants",
diff --git a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc
index dd56eb7..85fb14d 100644
--- a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc
+++ b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.cc
@@ -116,6 +116,7 @@
     mojo::PendingRemote<composebox::mojom::Page> pending_page,
     mojo::PendingReceiver<searchbox::mojom::PageHandler>
         pending_searchbox_handler,
+    mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page,
     Profile* profile,
     content::WebContents* web_contents,
     GetSessionHandleCallback get_session_callback,
@@ -124,6 +125,7 @@
           std::move(pending_handler),
           std::move(pending_page),
           std::move(pending_searchbox_handler),
+          std::move(pending_searchbox_page),
           profile,
           web_contents,
           std::make_unique<OmniboxController>(
@@ -138,12 +140,14 @@
     mojo::PendingRemote<composebox::mojom::Page> pending_page,
     mojo::PendingReceiver<searchbox::mojom::PageHandler>
         pending_searchbox_handler,
+    mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page,
     Profile* profile,
     content::WebContents* web_contents,
     std::unique_ptr<OmniboxController> controller,
     GetSessionHandleCallback get_session_callback,
     ClearSessionHandleCallback clear_session_callback)
     : ContextualSearchboxHandler(std::move(pending_searchbox_handler),
+                                 std::move(pending_searchbox_page),
                                  profile,
                                  web_contents,
                                  std::move(controller),
diff --git a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h
index 37b7b93ac..0c3a7ec4 100644
--- a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h
+++ b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler.h
@@ -34,6 +34,7 @@
       mojo::PendingRemote<composebox::mojom::Page> pending_page,
       mojo::PendingReceiver<searchbox::mojom::PageHandler>
           pending_searchbox_handler,
+      mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page,
       Profile* profile,
       content::WebContents* web_contents,
       GetSessionHandleCallback get_session_callback,
@@ -99,6 +100,7 @@
       mojo::PendingRemote<composebox::mojom::Page> pending_page,
       mojo::PendingReceiver<searchbox::mojom::PageHandler>
           pending_searchbox_handler,
+      mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page,
       Profile* profile,
       content::WebContents* web_contents,
       std::unique_ptr<OmniboxController> omnibox_controller,
diff --git a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler_unittest.cc b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler_unittest.cc
index 45125560..176db51 100644
--- a/chrome/browser/ui/webui/cr_components/composebox/composebox_handler_unittest.cc
+++ b/chrome/browser/ui/webui/cr_components/composebox/composebox_handler_unittest.cc
@@ -100,13 +100,11 @@
     handler_ = std::make_unique<ComposeboxHandler>(
         mojo::PendingReceiver<composebox::mojom::PageHandler>(),
         mock_page_.BindAndGetRemote(),
-        mojo::PendingReceiver<searchbox::mojom::PageHandler>(), profile(),
-        web_contents(), base::BindLambdaForTesting([&]() {
-          return contextual_session_handle_.get();
-        }),
+        mojo::PendingReceiver<searchbox::mojom::PageHandler>(),
+        mock_searchbox_page_.BindAndGetRemote(), profile(), web_contents(),
+        base::BindLambdaForTesting(
+            [&]() { return contextual_session_handle_.get(); }),
         base::DoNothing());
-
-    handler_->SetPage(mock_searchbox_page_.BindAndGetRemote());
   }
 
   ComposeboxHandler& handler() { return *handler_; }
diff --git a/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.cc b/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.cc
index 148c1986..354a1680 100644
--- a/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.cc
+++ b/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.cc
@@ -264,11 +264,13 @@
 ContextualSearchboxHandler::ContextualSearchboxHandler(
     mojo::PendingReceiver<searchbox::mojom::PageHandler>
         pending_searchbox_handler,
+    mojo::PendingRemote<searchbox::mojom::Page> pending_page,
     Profile* profile,
     content::WebContents* web_contents,
     std::unique_ptr<OmniboxController> controller,
     GetSessionHandleCallback get_session_callback)
     : SearchboxHandler(std::move(pending_searchbox_handler),
+                       std::move(pending_page),
                        profile,
                        web_contents,
                        std::move(controller)),
diff --git a/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.h b/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.h
index 3997465..d74db73 100644
--- a/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.h
+++ b/chrome/browser/ui/webui/cr_components/searchbox/contextual_searchbox_handler.h
@@ -100,6 +100,7 @@
   explicit ContextualSearchboxHandler(
       mojo::PendingReceiver<searchbox::mojom::PageHandler>
           pending_searchbox_handler,
+      mojo::PendingRemote<searchbox::mojom::Page> pending_page,
       Profile* profile,
       content::WebContents* web_contents,
       std::unique_ptr<OmniboxController> controller,
diff --git a/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.cc b/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.cc
index 13ca32f..f7fad80 100644
--- a/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.cc
+++ b/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.cc
@@ -857,14 +857,19 @@
 
 SearchboxHandler::SearchboxHandler(
     mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler,
+    mojo::PendingRemote<searchbox::mojom::Page> pending_page,
     Profile* profile,
     content::WebContents* web_contents,
     std::unique_ptr<OmniboxController> controller)
     : profile_(profile),
       web_contents_(web_contents),
       owned_controller_(std::move(controller)),
-      page_handler_(this, std::move(pending_page_handler)) {
+      page_handler_(this, std::move(pending_page_handler)),
+      page_(std::move(pending_page)) {
   controller_ = owned_controller_.get();
+  if (page_is_bound_callback_for_testing_) {
+    std::move(page_is_bound_callback_for_testing_).Run();
+  }
 }
 
 SearchboxHandler::~SearchboxHandler() {
@@ -872,6 +877,9 @@
   controller_ = nullptr;
 }
 
+// TODO(crbug.com/500739761): Remove this check since searchbox.mojom uses
+// factory pattern for instantiation making the remote and receiver bound
+// at the same time.
 bool SearchboxHandler::IsRemoteBound() const {
   return page_.is_bound();
 }
@@ -893,14 +901,6 @@
   }
 }
 
-void SearchboxHandler::SetPage(
-    mojo::PendingRemote<searchbox::mojom::Page> pending_page) {
-  page_.Bind(std::move(pending_page));
-  if (page_is_bound_callback_for_testing_) {
-    std::move(page_is_bound_callback_for_testing_).Run();
-  }
-}
-
 void SearchboxHandler::OnFocusChanged(bool focused) {
   if (focused) {
     edit_model()->OnSetFocus(false);
diff --git a/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.h b/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.h
index 1ea5d03..c1e59f2 100644
--- a/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.h
+++ b/chrome/browser/ui/webui/cr_components/searchbox/searchbox_handler.h
@@ -85,8 +85,6 @@
                        bool default_match_changed) override;
 
   // searchbox::mojom::PageHandler:
-  void SetPage(
-      mojo::PendingRemote<searchbox::mojom::Page> pending_page) override;
   void OnFocusChanged(bool focused) override;
   void QueryAutocomplete(const std::u16string& input,
                          bool prevent_inline_autocomplete) override;
@@ -171,6 +169,7 @@
                            QueryAutocomplete_SkipsLensInputs_InToolModes);
   SearchboxHandler(
       mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler,
+      mojo::PendingRemote<searchbox::mojom::Page> pending_page,
       Profile* profile,
       content::WebContents* web_contents,
       std::unique_ptr<OmniboxController> controller);
diff --git a/chrome/browser/ui/webui/history/history_ui.cc b/chrome/browser/ui/webui/history/history_ui.cc
index eeba6d2..b63b32d25 100644
--- a/chrome/browser/ui/webui/history/history_ui.cc
+++ b/chrome/browser/ui/webui/history/history_ui.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/feature_engagement/tracker_factory.h"
 #include "chrome/browser/glic/glic_pref_names.h"
 #include "chrome/browser/glic/public/glic_enabling.h"
+#include "chrome/browser/glic/public/glic_keyed_service.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/history_embeddings/history_embeddings_utils.h"
 #include "chrome/browser/page_image_service/image_service_factory.h"
@@ -139,10 +140,10 @@
 
   const bool is_glic_enabled =
       glic::GlicEnabling::ShouldShowSettingsPage(profile);
+  auto* glic_service = glic::GlicKeyedService::Get(profile);
   const bool is_glic_web_actuation_available =
       glic::GlicEnabling::IsEnabledAndConsentForProfile(profile) &&
-      profile->GetPrefs()->GetBoolean(
-          glic::prefs::kGlicUserEnabledActuationOnWeb);
+      glic_service && glic_service->enabling().GetUserEnabledActuationOnWeb();
 
   source->AddBoolean("isGlicEnabled", is_glic_enabled);
   source->AddBoolean("isGlicWebActuationAvailable",
diff --git a/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.cc b/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.cc
index 223d09fd..33b154bc 100644
--- a/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.cc
+++ b/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.cc
@@ -4,13 +4,31 @@
 
 #include "chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.h"
 
+#include "base/base64.h"
 #include "base/functional/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/trace_event/trace_config.h"
 #include "components/media_router/browser/media_router.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/tracing_controller.h"
+#include "services/tracing/public/cpp/perfetto/perfetto_config.h"
+#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
 
 namespace media_router {
 
 namespace {
 
+struct TraceReader : public base::RefCountedThreadSafe<TraceReader> {
+  explicit TraceReader(std::unique_ptr<perfetto::TracingSession> session)
+      : session(std::move(session)) {}
+  std::unique_ptr<perfetto::TracingSession> session;
+
+ private:
+  friend class base::RefCountedThreadSafe<TraceReader>;
+  ~TraceReader() = default;
+};
+
 base::ListValue CastProviderStateToValue(
     const mojom::CastProviderState& state) {
   base::ListValue result;
@@ -82,6 +100,16 @@
       base::BindRepeating(&MediaRouterInternalsWebUIMessageHandler::
                               HandleIsMirroringStatsEnabled,
                           base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "startTracing",
+      base::BindRepeating(
+          &MediaRouterInternalsWebUIMessageHandler::HandleStartTracing,
+          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "stopTracing",
+      base::BindRepeating(
+          &MediaRouterInternalsWebUIMessageHandler::HandleStopTracing,
+          base::Unretained(this)));
 }
 
 void MediaRouterInternalsWebUIMessageHandler::HandleGetState(
@@ -170,6 +198,117 @@
                             debugger_->ShouldFetchMirroringStats());
 }
 
+void MediaRouterInternalsWebUIMessageHandler::HandleStartTracing(
+    const base::ListValue& args) {
+  AllowJavascript();
+  const base::Value& callback_id = args[0];
+
+  if (tracing_session_) {
+    ResolveJavascriptCallback(callback_id, base::Value(false));
+    return;
+  }
+
+  const base::trace_event::TraceConfig trace_config(
+      "media.cast,openscreen,gpu,media,base,toplevel", "record-until-full");
+
+  tracing_session_ =
+      perfetto::Tracing::NewTrace(perfetto::BackendType::kCustomBackend);
+
+  auto perfetto_config = tracing::GetDefaultPerfettoConfig(
+      trace_config, /*privacy_filtering_enabled=*/false);
+  tracing_session_->Setup(perfetto_config);
+
+  // base::Value is move-only, but std::function requires copyable arguments.
+  // Since we only need the string callback_id, we can extract it.
+  std::string callback_id_str;
+  if (callback_id.is_string()) {
+    callback_id_str = callback_id.GetString();
+  }
+
+  tracing_session_->SetOnStartCallback([weak_this = weak_factory_.GetWeakPtr(),
+                                        callback_id_str]() {
+    content::GetUIThreadTaskRunner({})->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            [](base::WeakPtr<MediaRouterInternalsWebUIMessageHandler> weak_this,
+               std::string callback_id_str) {
+              if (weak_this) {
+                weak_this->ResolveJavascriptCallback(
+                    base::Value(callback_id_str), base::Value(true));
+              }
+            },
+            std::move(weak_this), std::move(callback_id_str)));
+  });
+  tracing_session_->Start();
+}
+
+void MediaRouterInternalsWebUIMessageHandler::HandleStopTracing(
+    const base::ListValue& args) {
+  AllowJavascript();
+  const base::Value& callback_id = args[0];
+
+  if (!tracing_session_) {
+    ResolveJavascriptCallback(callback_id, base::Value(false));
+    return;
+  }
+
+  std::string callback_id_str;
+  if (callback_id.is_string()) {
+    callback_id_str = callback_id.GetString();
+  }
+
+  // Wrap the tracing session in a ref-counted struct so it can be safely
+  // captured by copy into the std::function callbacks used by Perfetto,
+  // matching Chromium's idiomatic approach for these APIs.
+  auto trace_reader =
+      base::MakeRefCounted<TraceReader>(std::move(tracing_session_));
+
+  trace_reader->session->SetOnStopCallback([trace_reader,
+                                            weak_this =
+                                                weak_factory_.GetWeakPtr(),
+                                            callback_id_str]() {
+    trace_reader->session->SetOnStopCallback([]() {});
+    trace_reader->session->ReadTrace(
+        [trace_reader, weak_this, callback_id_str](
+            perfetto::TracingSession::ReadTraceCallbackArgs args) {
+          if (args.size > 0) {
+            std::string base64_chunk =
+                base::Base64Encode(std::string_view(args.data, args.size));
+            content::GetUIThreadTaskRunner({})->PostTask(
+                FROM_HERE,
+                base::BindOnce(
+                    [](base::WeakPtr<MediaRouterInternalsWebUIMessageHandler>
+                           weak_this,
+                       std::string base64_chunk) {
+                      if (weak_this) {
+                        weak_this->FireWebUIListener("on-trace-chunk",
+                                                     base::Value(base64_chunk));
+                      }
+                    },
+                    weak_this, std::move(base64_chunk)));
+          }
+
+          if (args.has_more) {
+            return;
+          }
+
+          content::GetUIThreadTaskRunner({})->PostTask(
+              FROM_HERE,
+              base::BindOnce(
+                  [](base::WeakPtr<MediaRouterInternalsWebUIMessageHandler>
+                         weak_this,
+                     std::string callback_id_str) {
+                    if (weak_this) {
+                      weak_this->ResolveJavascriptCallback(
+                          base::Value(callback_id_str), base::Value(true));
+                    }
+                  },
+                  std::move(weak_this), std::move(callback_id_str)));
+        });
+  });
+  trace_reader->session->Stop();
+}
+
 void MediaRouterInternalsWebUIMessageHandler::OnMirroringStatsUpdated(
     const base::DictValue& json_logs) {
   if (IsJavascriptAllowed()) {
diff --git a/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.h b/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.h
index 6c3426a..0996983 100644
--- a/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.h
+++ b/chrome/browser/ui/webui/media_router/media_router_internals_webui_message_handler.h
@@ -13,6 +13,7 @@
 #include "components/media_router/browser/media_router_debugger.h"
 #include "components/media_router/common/mojom/media_router.mojom.h"
 #include "content/public/browser/web_ui_message_handler.h"
+#include "third_party/perfetto/include/perfetto/tracing/tracing.h"
 
 namespace media_router {
 
@@ -42,6 +43,8 @@
   void HandleGetMirroringStats(const base::ListValue& args);
   void HandleSetMirroringStatsEnabled(const base::ListValue& args);
   void HandleIsMirroringStatsEnabled(const base::ListValue& args);
+  void HandleStartTracing(const base::ListValue& args);
+  void HandleStopTracing(const base::ListValue& args);
 
   // MirroringStatsObserver implementation.
   void OnMirroringStatsUpdated(const base::DictValue& json_logs) override;
@@ -55,6 +58,8 @@
   const raw_ptr<MediaRouter> router_;
   const raw_ref<MediaRouterDebugger> debugger_;
 
+  std::unique_ptr<perfetto::TracingSession> tracing_session_;
+
   base::WeakPtrFactory<MediaRouterInternalsWebUIMessageHandler> weak_factory_{
       this};
 };
diff --git a/chrome/browser/ui/webui/nearby_share/BUILD.gn b/chrome/browser/ui/webui/nearby_share/BUILD.gn
index 57e5af8..e865505a 100644
--- a/chrome/browser/ui/webui/nearby_share/BUILD.gn
+++ b/chrome/browser/ui/webui/nearby_share/BUILD.gn
@@ -16,7 +16,10 @@
 
   public_deps = [ "//chrome/browser:browser_public_dependencies" ]
 
+  allow_circular_includes_from = [ "//chrome/browser/nearby_sharing" ]
+
   deps = [
+    "//chrome/browser/nearby_sharing",
     "//chrome/browser/nearby_sharing:share_target",
     "//chrome/browser/nearby_sharing/common",
     "//chrome/browser/nearby_sharing/contacts",
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index 3e7ec07..d1fb009 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -1039,11 +1039,12 @@
 }
 
 void NewTabPageUI::BindInterface(
-    mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler) {
-  realbox_handler_ = std::make_unique<RealboxHandler>(
-      std::move(pending_page_handler), profile_, web_contents(),
-      base::BindRepeating(&NewTabPageUI::GetOrCreateContextualSessionHandle,
-                          base::Unretained(this)));
+    mojo::PendingReceiver<searchbox::mojom::PageHandlerFactory>
+        pending_receiver) {
+  if (searchbox_page_factory_receiver_.is_bound()) {
+    searchbox_page_factory_receiver_.reset();
+  }
+  searchbox_page_factory_receiver_.Bind(std::move(pending_receiver));
 }
 
 void NewTabPageUI::BindInterface(
@@ -1242,6 +1243,16 @@
 }
 
 void NewTabPageUI::CreatePageHandler(
+    mojo::PendingRemote<searchbox::mojom::Page> pending_page,
+    mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler) {
+  realbox_handler_ = std::make_unique<RealboxHandler>(
+      std::move(pending_page_handler), std::move(pending_page), profile_,
+      web_contents(),
+      base::BindRepeating(&NewTabPageUI::GetOrCreateContextualSessionHandle,
+                          base::Unretained(this)));
+}
+
+void NewTabPageUI::CreatePageHandler(
     mojo::PendingRemote<composebox::mojom::Page> pending_page,
     mojo::PendingReceiver<composebox::mojom::PageHandler> pending_page_handler,
     mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page,
@@ -1251,14 +1262,12 @@
 
   composebox_handler_ = std::make_unique<ComposeboxHandler>(
       std::move(pending_page_handler), std::move(pending_page),
-      std::move(pending_searchbox_handler), profile_, web_contents(),
+      std::move(pending_searchbox_handler), std::move(pending_searchbox_page),
+      profile_, web_contents(),
       base::BindRepeating(&NewTabPageUI::GetOrCreateContextualSessionHandle,
                           base::Unretained(this)),
       base::BindRepeating(&NewTabPageUI::ClearContextualSessionHandle,
                           base::Unretained(this)));
-
-  // TODO(crbug.com/435288212): Move searchbox mojom to use factory pattern.
-  composebox_handler_->SetPage(std::move(pending_searchbox_page));
 }
 
 void NewTabPageUI::CreateHelpBubbleHandler(
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
index b0669ad..734ab32 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
@@ -40,7 +40,7 @@
 #include "chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom.h"
 #include "chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom.h"
 #include "chrome/common/webui_url_constants.h"
-#include "components/omnibox/browser/searchbox.mojom-forward.h"
+#include "components/omnibox/browser/searchbox.mojom.h"
 #include "components/page_image_service/mojom/page_image_service.mojom.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -119,6 +119,7 @@
       public ntp_promo::mojom::NtpPromoHandlerFactory,
       public NtpCustomBackgroundServiceObserver,
       public action_chips::mojom::ActionChipsHandlerFactory,
+      public searchbox::mojom::PageHandlerFactory,
       content::WebContentsObserver {
  public:
   explicit NewTabPageUI(content::WebUI* web_ui);
@@ -143,10 +144,11 @@
       mojo::PendingReceiver<new_tab_page::mojom::PageHandlerFactory>
           pending_receiver);
 
-  // Instantiates the implementor of the searchbox::mojom::PageHandler mojo
-  // interface passing the pending receiver that will be internally bound.
-  void BindInterface(mojo::PendingReceiver<searchbox::mojom::PageHandler>
-                         pending_page_handler);
+  // Instantiates the implementor of the searchbox::mojom::PageHandlerFactory
+  // mojo interface passing the pending receiver that will be internally bound.
+  void BindInterface(
+      mojo::PendingReceiver<searchbox::mojom::PageHandlerFactory>
+          pending_receiver);
 
   // Instantiates the implementor of the
   // browser_command::mojom::CommandHandlerFactory mojo interface passing
@@ -288,6 +290,12 @@
       mojo::PendingReceiver<searchbox::mojom::PageHandler>
           pending_searchbox_handler) override;
 
+  // searchbox::mojom::PageHandlerFactory:
+  void CreatePageHandler(
+      mojo::PendingRemote<searchbox::mojom::Page> pending_page,
+      mojo::PendingReceiver<searchbox::mojom::PageHandler>
+          pending_page_handler) override;
+
   // help_bubble::mojom::HelpBubbleHandlerFactory:
   void CreateHelpBubbleHandler(
       mojo::PendingRemote<help_bubble::mojom::HelpBubbleClient> client,
@@ -357,6 +365,8 @@
   mojo::Receiver<browser_command::mojom::CommandHandlerFactory>
       browser_command_factory_receiver_;
   std::unique_ptr<RealboxHandler> realbox_handler_;
+  mojo::Receiver<searchbox::mojom::PageHandlerFactory>
+      searchbox_page_factory_receiver_{this};
   std::unique_ptr<user_education::HelpBubbleHandler> help_bubble_handler_;
   mojo::Receiver<help_bubble::mojom::HelpBubbleHandlerFactory>
       help_bubble_handler_factory_receiver_{this};
diff --git a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc
index 5a2e788..39c19e25 100644
--- a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc
+++ b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.cc
@@ -195,6 +195,16 @@
 
 void OmniboxPopupUI::BindInterface(
     content::RenderFrameHost* host,
+    mojo::PendingReceiver<searchbox::mojom::PageHandlerFactory>
+        pending_page_handler) {
+  if (searchbox_page_factory_receiver_.is_bound()) {
+    searchbox_page_factory_receiver_.reset();
+  }
+  searchbox_page_factory_receiver_.Bind(std::move(pending_page_handler));
+}
+
+void OmniboxPopupUI::CreatePageHandler(
+    mojo::PendingRemote<searchbox::mojom::Page> page,
     mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler) {
   auto* omnibox_controller =
       OmniboxPopupWebContentsHelper::GetOrCreateForWebContents(
@@ -205,7 +215,7 @@
   MetricsReporterService* metrics_reporter_service =
       MetricsReporterService::GetFromWebContents(web_ui()->GetWebContents());
   omnibox_handler_ = std::make_unique<WebuiOmniboxHandler>(
-      std::move(pending_page_handler),
+      std::move(pending_page_handler), std::move(page),
       metrics_reporter_service->metrics_reporter(), omnibox_controller,
       web_ui(),
       base::BindRepeating(&OmniboxPopupUI::GetOrCreateContextualSessionHandle,
@@ -259,15 +269,12 @@
 
   composebox_handler_ = std::make_unique<OmniboxComposeboxHandler>(
       std::move(pending_page_handler), std::move(pending_page),
-      std::move(pending_searchbox_handler), profile_,
-      web_ui()->GetWebContents(),
+      std::move(pending_searchbox_handler), std::move(pending_searchbox_page),
+      profile_, web_ui()->GetWebContents(),
       base::BindRepeating(&OmniboxPopupUI::GetOrCreateContextualSessionHandle,
                           base::Unretained(this)),
       base::BindRepeating(&OmniboxPopupUI::ClearContextualSessionHandle,
                           base::Unretained(this)));
-
-  // TODO(crbug.com/435288212): Move searchbox mojom to use factory pattern.
-  composebox_handler_->SetPage(std::move(pending_searchbox_page));
 }
 
 contextual_search::ContextualSearchSessionHandle*
diff --git a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h
index c88ac15..db5acea 100644
--- a/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h
+++ b/chrome/browser/ui/webui/omnibox_popup/omnibox_popup_ui.h
@@ -14,7 +14,7 @@
 #include "chrome/browser/ui/webui/top_chrome/top_chrome_web_ui_controller.h"
 #include "chrome/browser/ui/webui/top_chrome/top_chrome_webui_config.h"
 #include "chrome/common/webui_url_constants.h"
-#include "components/omnibox/browser/searchbox.mojom-forward.h"
+#include "components/omnibox/browser/searchbox.mojom.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/common/url_constants.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -51,17 +51,18 @@
 class OmniboxPopupUI : public TopChromeWebUIController,
                        public omnibox_popup::mojom::PageHandlerFactory,
                        public omnibox_popup_aim::mojom::PageHandlerFactory,
-                       public composebox::mojom::PageHandlerFactory {
+                       public composebox::mojom::PageHandlerFactory,
+                       public searchbox::mojom::PageHandlerFactory {
  public:
   explicit OmniboxPopupUI(content::WebUI* web_ui);
   OmniboxPopupUI(const OmniboxPopupUI&) = delete;
   OmniboxPopupUI& operator=(const OmniboxPopupUI&) = delete;
   ~OmniboxPopupUI() override;
 
-  // Instantiates the implementor of the searchbox::mojom::PageHandler mojo
-  // interface passing the pending receiver that will be internally bound.
+  // Instantiates the implementor of the searchbox::mojom::PageHandlerFactory
+  // mojo interface passing the pending receiver that will be internally bound.
   void BindInterface(content::RenderFrameHost* host,
-                     mojo::PendingReceiver<searchbox::mojom::PageHandler>
+                     mojo::PendingReceiver<searchbox::mojom::PageHandlerFactory>
                          pending_page_handler);
   WebuiOmniboxHandler* omnibox_handler() { return omnibox_handler_.get(); }
 
@@ -98,6 +99,11 @@
           pending_searchbox_handler) override;
   ComposeboxHandler* composebox_handler() { return composebox_handler_.get(); }
 
+  // searchbox::mojom::PageHandlerFactory:
+  void CreatePageHandler(
+      mojo::PendingRemote<searchbox::mojom::Page> page,
+      mojo::PendingReceiver<searchbox::mojom::PageHandler> handler) override;
+
   static constexpr std::string_view GetWebUIName() { return "OmniboxPopup"; }
 
   // Lazily creates and returns a reference to the owned contextual search
@@ -127,6 +133,9 @@
   mojo::Receiver<composebox::mojom::PageHandlerFactory>
       composebox_page_factory_receiver_{this};
 
+  mojo::Receiver<searchbox::mojom::PageHandlerFactory>
+      searchbox_page_factory_receiver_{this};
+
   WEB_UI_CONTROLLER_TYPE_DECL();
 };
 
diff --git a/chrome/browser/ui/webui/searchbox/contextual_searchbox_handler_browsertest.cc b/chrome/browser/ui/webui/searchbox/contextual_searchbox_handler_browsertest.cc
index a8ad0db5..22a07c4e 100644
--- a/chrome/browser/ui/webui/searchbox/contextual_searchbox_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/searchbox/contextual_searchbox_handler_browsertest.cc
@@ -25,10 +25,12 @@
  public:
   TestSearchboxHandler(
       mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler,
+      mojo::PendingRemote<searchbox::mojom::Page> pending_page,
       Profile* profile,
       content::WebContents* web_contents,
       GetSessionHandleCallback get_session_callback)
       : ContextualSearchboxHandler(std::move(pending_page_handler),
+                                   std::move(pending_page),
                                    profile,
                                    web_contents,
                                    std::make_unique<OmniboxController>(
@@ -63,10 +65,9 @@
 
     handler_ = std::make_unique<TestSearchboxHandler>(
         mojo::PendingReceiver<searchbox::mojom::PageHandler>(),
-        browser()->profile(),
+        page_.BindAndGetRemote(), browser()->profile(),
         /*web_contents=*/browser()->tab_strip_model()->GetActiveWebContents(),
         base::BindLambdaForTesting([&]() { return session_handle_.get(); }));
-    handler_->SetPage(page_.BindAndGetRemote());
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/chrome/browser/ui/webui/searchbox/contextual_searchbox_handler_unittest.cc b/chrome/browser/ui/webui/searchbox/contextual_searchbox_handler_unittest.cc
index 08ac6e7..d03087d 100644
--- a/chrome/browser/ui/webui/searchbox/contextual_searchbox_handler_unittest.cc
+++ b/chrome/browser/ui/webui/searchbox/contextual_searchbox_handler_unittest.cc
@@ -106,11 +106,13 @@
  public:
   FakeContextualSearchboxHandler(
       mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler,
+      mojo::PendingRemote<searchbox::mojom::Page> pending_page,
       Profile* profile,
       content::WebContents* web_contents,
       std::unique_ptr<OmniboxController> controller,
       GetSessionHandleCallback get_session_callback)
       : ContextualSearchboxHandler(std::move(pending_page_handler),
+                                   std::move(pending_page),
                                    profile,
                                    web_contents,
                                    std::move(controller),
@@ -214,13 +216,17 @@
 
     web_contents()->SetDelegate(&delegate_);
     handler_ = std::make_unique<FakeContextualSearchboxHandler>(
-        mojo::PendingReceiver<searchbox::mojom::PageHandler>(), profile(),
-        web_contents(),
+        mojo::PendingReceiver<searchbox::mojom::PageHandler>(),
+        mock_searchbox_page_.BindAndGetRemote(), profile(), web_contents(),
         std::make_unique<OmniboxController>(
             std::make_unique<TestOmniboxClient>()),
         base::BindLambdaForTesting(
             [&]() { return contextual_session_handle_.get(); }));
-    handler_->SetPage(mock_searchbox_page_.BindAndGetRemote());
+
+    // Drain the Mojo pipe and clear setup-related calls to searchbox page.
+    mock_searchbox_page_.FlushForTesting();
+    base::RunLoop().RunUntilIdle();
+    testing::Mock::VerifyAndClearExpectations(&mock_searchbox_page_);
 
     ON_CALL(query_controller(), CreateSearchUrl)
         .WillByDefault(
@@ -767,13 +773,17 @@
                 this));
 
     handler_ = std::make_unique<FakeContextualSearchboxHandler>(
-        mojo::PendingReceiver<searchbox::mojom::PageHandler>(), profile(),
-        web_contents(),
+        mojo::PendingReceiver<searchbox::mojom::PageHandler>(),
+        mock_searchbox_page_.BindAndGetRemote(), profile(), web_contents(),
         std::make_unique<OmniboxController>(
             std::make_unique<TestOmniboxClient>()),
         base::BindLambdaForTesting(
             [&]() { return contextual_session_handle_.get(); }));
-    handler_->SetPage(mock_searchbox_page_.BindAndGetRemote());
+
+    // Drain the Mojo pipe and clear setup-related calls to searchbox page.
+    mock_searchbox_page_.FlushForTesting();
+    base::RunLoop().RunUntilIdle();
+    testing::Mock::VerifyAndClearExpectations(&mock_searchbox_page_);
 
     ON_CALL(query_controller(), CreateSearchUrl)
         .WillByDefault(
@@ -888,9 +898,10 @@
       contextual_tasks::kContextualTasksShareOpenTabsEveryThread, true);
 
   // Recreate handler to test initialization.
+  mock_searchbox_page_.receiver_.reset();
   auto handler = std::make_unique<FakeContextualSearchboxHandler>(
-      mojo::PendingReceiver<searchbox::mojom::PageHandler>(), profile(),
-      web_contents(),
+      mojo::PendingReceiver<searchbox::mojom::PageHandler>(),
+      mock_searchbox_page_.BindAndGetRemote(), profile(), web_contents(),
       std::make_unique<OmniboxController>(
           std::make_unique<TestOmniboxClient>()),
       base::BindLambdaForTesting(
@@ -1686,9 +1697,12 @@
 TEST_F(ContextualSearchboxHandlerTestTabsTest,
        DISABLED_TabStripModelObserverIsNotAddedWithNullSession) {
   // Create a handler with a null session handle.
+  // Use a new MockSearchboxPage for the new handler.
+  testing::NiceMock<MockSearchboxPage> local_mock_searchbox_page;
   auto handler_with_null_session =
       std::make_unique<FakeContextualSearchboxHandler>(
-          mojo::PendingReceiver<searchbox::mojom::PageHandler>(), profile(),
+          mojo::PendingReceiver<searchbox::mojom::PageHandler>(),
+          local_mock_searchbox_page.BindAndGetRemote(), profile(),
           web_contents(),
           std::make_unique<OmniboxController>(
               std::make_unique<TestOmniboxClient>()),
@@ -1697,11 +1711,6 @@
                 return nullptr;
               }));
 
-  // Use a new MockSearchboxPage for the new handler.
-  testing::NiceMock<MockSearchboxPage> local_mock_searchbox_page;
-  handler_with_null_session->SetPage(
-      local_mock_searchbox_page.BindAndGetRemote());
-
   // The observer should not be added, so OnTabStripChanged should not be
   // called.
   EXPECT_CALL(local_mock_searchbox_page, OnTabStripChanged).Times(0);
diff --git a/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.cc b/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.cc
index 191a1d6..c914de3 100644
--- a/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.cc
+++ b/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.cc
@@ -104,11 +104,13 @@
 
 LensSearchboxHandler::LensSearchboxHandler(
     mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler,
+    mojo::PendingRemote<searchbox::mojom::Page> pending_page,
     Profile* profile,
     content::WebContents* web_contents,
     LensSearchboxClient* lens_searchbox_client)
     : SearchboxHandler(
           std::move(pending_page_handler),
+          std::move(pending_page),
           profile,
           web_contents,
           std::make_unique<OmniboxController>(
@@ -118,6 +120,11 @@
               lens::features::GetLensSearchboxAutocompleteTimeout())),
       lens_searchbox_client_(lens_searchbox_client) {
   autocomplete_controller_observation_.Observe(autocomplete_controller());
+
+  // The client may have text waiting to be sent to the searchbox that it
+  // couldn't do earlier since the page binding was not set. So now we let the
+  // client know the binding is ready.
+  lens_searchbox_client_->OnPageBound();
 }
 
 LensSearchboxHandler::~LensSearchboxHandler() = default;
@@ -134,16 +141,6 @@
   return SearchboxHandler::AutocompleteIconToResourceName(icon);
 }
 
-void LensSearchboxHandler::SetPage(
-    mojo::PendingRemote<searchbox::mojom::Page> pending_page) {
-  SearchboxHandler::SetPage(std::move(pending_page));
-
-  // The client may have text waiting to be sent to the searchbox that it
-  // couldn't do earlier since the page binding was not set. So now we let the
-  // client know the binding is ready.
-  lens_searchbox_client_->OnPageBound();
-}
-
 void LensSearchboxHandler::OnFocusChanged(bool focused) {
   SearchboxHandler::OnFocusChanged(focused);
   lens_searchbox_client_->OnFocusChanged(focused);
diff --git a/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.h b/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.h
index 13f3c2be..a4a0c0a1 100644
--- a/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.h
+++ b/chrome/browser/ui/webui/searchbox/lens_searchbox_handler.h
@@ -18,6 +18,7 @@
  public:
   LensSearchboxHandler(
       mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler,
+      mojo::PendingRemote<searchbox::mojom::Page> pending_page,
       Profile* profile,
       content::WebContents* web_contents,
       LensSearchboxClient* lens_searchbox_client);
@@ -29,8 +30,6 @@
       const gfx::VectorIcon& icon) const override;
 
   // searchbox::mojom::PageHandler:
-  void SetPage(
-      mojo::PendingRemote<searchbox::mojom::Page> pending_page) override;
   void OnFocusChanged(bool focused) override;
   void QueryAutocomplete(const std::u16string& input,
                          bool prevent_inline_autocomplete) override;
diff --git a/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.cc b/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.cc
index 26b2876..bd0e5dc 100644
--- a/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.cc
+++ b/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.cc
@@ -81,22 +81,24 @@
     mojo::PendingRemote<composebox::mojom::Page> pending_page,
     mojo::PendingReceiver<searchbox::mojom::PageHandler>
         pending_searchbox_handler,
+    mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page,
     Profile* profile,
     content::WebContents* web_contents,
     GetSessionHandleCallback get_session_callback,
     ClearSessionHandleCallback clear_session_callback)
-    : ComposeboxHandler(
-          std::move(pending_handler),
-          std::move(pending_page),
-          std::move(pending_searchbox_handler),
-          profile,
-          web_contents,
-          std::make_unique<OmniboxController>(
-              std::make_unique<OmniboxPopupComposeboxClient>(profile,
-                                                             web_contents,
-                                                             this)),
-          std::move(get_session_callback),
-          std::move(clear_session_callback)) {
+    : ComposeboxHandler(std::move(pending_handler),
+                       std::move(pending_page),
+                       std::move(pending_searchbox_handler),
+                       std::move(pending_searchbox_page),
+                       profile,
+                       web_contents,
+                       std::make_unique<OmniboxController>(
+                           std::make_unique<OmniboxPopupComposeboxClient>(
+                               profile,
+                               web_contents,
+                               this)),
+                       std::move(get_session_callback),
+                       std::move(clear_session_callback)) {
   auto* aim_eligibility_service =
       AimEligibilityServiceFactory::GetForProfile(profile);
   if (aim_eligibility_service) {
diff --git a/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.h b/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.h
index 57a1327..e674848 100644
--- a/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.h
+++ b/chrome/browser/ui/webui/searchbox/omnibox_composebox_handler.h
@@ -22,6 +22,7 @@
       mojo::PendingRemote<composebox::mojom::Page> pending_page,
       mojo::PendingReceiver<searchbox::mojom::PageHandler>
           pending_searchbox_handler,
+      mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page,
       Profile* profile,
       content::WebContents* web_contents,
       GetSessionHandleCallback get_session_callback,
diff --git a/chrome/browser/ui/webui/searchbox/realbox_handler.cc b/chrome/browser/ui/webui/searchbox/realbox_handler.cc
index c81c89f8..e42d3cf2 100644
--- a/chrome/browser/ui/webui/searchbox/realbox_handler.cc
+++ b/chrome/browser/ui/webui/searchbox/realbox_handler.cc
@@ -74,11 +74,13 @@
 
 RealboxHandler::RealboxHandler(
     mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler,
+    mojo::PendingRemote<searchbox::mojom::Page> pending_page,
     Profile* profile,
     content::WebContents* web_contents,
     GetSessionHandleCallback get_session_callback)
     : ContextualSearchboxHandler(
           std::move(pending_page_handler),
+          std::move(pending_page),
           profile,
           web_contents,
           std::make_unique<OmniboxController>(
diff --git a/chrome/browser/ui/webui/searchbox/realbox_handler.h b/chrome/browser/ui/webui/searchbox/realbox_handler.h
index 52dde05..19b1e4f 100644
--- a/chrome/browser/ui/webui/searchbox/realbox_handler.h
+++ b/chrome/browser/ui/webui/searchbox/realbox_handler.h
@@ -23,6 +23,7 @@
  public:
   RealboxHandler(
       mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler,
+      mojo::PendingRemote<searchbox::mojom::Page> pending_page,
       Profile* profile,
       content::WebContents* web_contents,
       GetSessionHandleCallback get_session_callback);
diff --git a/chrome/browser/ui/webui/searchbox/realbox_handler_browsertest.cc b/chrome/browser/ui/webui/searchbox/realbox_handler_browsertest.cc
index dce91ea..bf42b8a 100644
--- a/chrome/browser/ui/webui/searchbox/realbox_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/searchbox/realbox_handler_browsertest.cc
@@ -127,13 +127,12 @@
     mojo::Remote<searchbox::mojom::PageHandler> remote_page_handler;
     RealboxSearchBrowserTestPage page;
     RealboxHandler realbox_handler = RealboxHandler(
-        remote_page_handler.BindNewPipeAndPassReceiver(), browser()->profile(),
-        GetWebContents(),
+        remote_page_handler.BindNewPipeAndPassReceiver(), page.GetRemotePage(),
+        browser()->profile(), GetWebContents(),
         base::BindLambdaForTesting(
             []() -> contextual_search::ContextualSearchSessionHandle* {
               return nullptr;
             }));
-    realbox_handler.SetPage(page.GetRemotePage());
     content::test::PrerenderHostRegistryObserver registry_observer(
         *GetWebContents());
 
@@ -264,13 +263,12 @@
     InProcessBrowserTest::SetUpOnMainThread();
     handler_ = std::make_unique<RealboxHandler>(
         mojo::PendingReceiver<searchbox::mojom::PageHandler>(),
-        browser()->profile(),
+        page_.BindAndGetRemote(), browser()->profile(),
         /*web_contents=*/browser()->tab_strip_model()->GetActiveWebContents(),
         base::BindLambdaForTesting(
             []() -> contextual_search::ContextualSearchSessionHandle* {
               return nullptr;
             }));
-    handler_->SetPage(page_.BindAndGetRemote());
   }
 
   void TearDownOnMainThread() override { handler_.reset(); }
diff --git a/chrome/browser/ui/webui/searchbox/searchbox_handler_unittest.cc b/chrome/browser/ui/webui/searchbox/searchbox_handler_unittest.cc
index c4bdbbd9..e66d6c4 100644
--- a/chrome/browser/ui/webui/searchbox/searchbox_handler_unittest.cc
+++ b/chrome/browser/ui/webui/searchbox/searchbox_handler_unittest.cc
@@ -130,13 +130,12 @@
     web_contents_ =
         content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
     handler_ = std::make_unique<RealboxHandler>(
-        mojo::PendingReceiver<searchbox::mojom::PageHandler>(), profile(),
-        web_contents_.get(),
+        mojo::PendingReceiver<searchbox::mojom::PageHandler>(),
+        page_.BindAndGetRemote(), profile(), web_contents_.get(),
         base::BindLambdaForTesting(
             []() -> contextual_search::ContextualSearchSessionHandle* {
               return nullptr;
             }));
-    handler_->SetPage(page_.BindAndGetRemote());
   }
 
   void TearDown() override {
@@ -269,13 +268,12 @@
     web_contents_ =
         content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
     handler_ = std::make_unique<RealboxHandler>(
-        mojo::PendingReceiver<searchbox::mojom::PageHandler>(), profile(),
-        web_contents_.get(),
+        mojo::PendingReceiver<searchbox::mojom::PageHandler>(),
+        page_.BindAndGetRemote(), profile(), web_contents_.get(),
         base::BindLambdaForTesting(
             []() -> contextual_search::ContextualSearchSessionHandle* {
               return nullptr;
             }));
-    handler_->SetPage(page_.BindAndGetRemote());
   }
 
   void TearDown() override {
@@ -386,10 +384,9 @@
         std::make_unique<testing::NiceMock<MockLensSearchboxClient>>();
 
     handler_ = std::make_unique<LensSearchboxHandler>(
-        mojo::PendingReceiver<searchbox::mojom::PageHandler>(), profile(),
+        mojo::PendingReceiver<searchbox::mojom::PageHandler>(),
+        page_.BindAndGetRemote(), profile(),
         /*web_contents=*/nullptr, lens_searchbox_client_.get());
-
-    handler_->SetPage(page_.BindAndGetRemote());
   }
 };
 
@@ -549,12 +546,12 @@
 
     handler_ = std::make_unique<WebuiOmniboxHandler>(
         mojo::PendingReceiver<searchbox::mojom::PageHandler>(),
+        page_.BindAndGetRemote(),
         /*metrics_reporter=*/nullptr, omnibox_controller_.get(), &web_ui_,
         base::BindLambdaForTesting(
             []() -> contextual_search::ContextualSearchSessionHandle* {
               return nullptr;
             }));
-    handler_->SetPage(page_.BindAndGetRemote());
   }
 
   void TearDown() override {
diff --git a/chrome/browser/ui/webui/searchbox/searchbox_interactive_test_mixin.h b/chrome/browser/ui/webui/searchbox/searchbox_interactive_test_mixin.h
index 86f387bc..150eeef 100644
--- a/chrome/browser/ui/webui/searchbox/searchbox_interactive_test_mixin.h
+++ b/chrome/browser/ui/webui/searchbox/searchbox_interactive_test_mixin.h
@@ -113,6 +113,22 @@
                         input_text + " - Google Search");
   }
 
+  // Waits for a JavaScript condition to evaluate to true for the element
+  // resolved by `where`.
+  auto WaitForJsConditionAt(
+      const ui::ElementIdentifier& contents_id,
+      const WebContentsInteractionTestUtil::DeepQuery& where,
+      const std::string& test_function) {
+    DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kJsConditionEvent);
+    WebContentsInteractionTestUtil::StateChange state_change;
+    state_change.event = kJsConditionEvent;
+    state_change.where = where;
+    state_change.test_function = test_function;
+
+    return T::Steps(
+        T::InAnyContext(T::WaitForStateChange(contents_id, state_change)));
+  }
+
   // Triggers a voice search with the final result matching `result`.
   auto TriggerAimVoiceSearch(
       const ui::ElementIdentifier& contents_id,
diff --git a/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.cc b/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.cc
index ab7c0788..bae92e8 100644
--- a/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.cc
+++ b/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.cc
@@ -92,11 +92,13 @@
 
 WebuiOmniboxHandler::WebuiOmniboxHandler(
     mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler,
+    mojo::PendingRemote<searchbox::mojom::Page> pending_page,
     MetricsReporter* metrics_reporter,
     OmniboxController* omnibox_controller,
     content::WebUI* web_ui,
     GetSessionHandleCallback get_session_callback)
     : ContextualSearchboxHandler(std::move(pending_page_handler),
+                                 std::move(pending_page),
                                  Profile::FromWebUI(web_ui),
                                  web_ui->GetWebContents(),
                                  /*controller=*/nullptr,
@@ -127,6 +129,9 @@
       contextual_search::kSearchContentSharingSettings,
       base::BindRepeating(&WebuiOmniboxHandler::OnContentSharingPolicyChanged,
                           base::Unretained(this)));
+
+  OnAimPopupEligibilityChanged();
+  OnContentSharingPolicyChanged();
 }
 
 WebuiOmniboxHandler::~WebuiOmniboxHandler() = default;
@@ -201,13 +206,6 @@
   edit_model()->OpenAiMode(false, /*via_context_menu=*/false);
   std::move(callback).Run(base::ok(base::UnguessableToken::Create()));
 }
-
-void WebuiOmniboxHandler::SetPage(
-    mojo::PendingRemote<searchbox::mojom::Page> pending_page) {
-  ContextualSearchboxHandler::SetPage(std::move(pending_page));
-  OnAimPopupEligibilityChanged();
-  OnContentSharingPolicyChanged();
-}
 void WebuiOmniboxHandler::StepSelection(
     OmniboxPopupSelection::Direction direction,
     OmniboxPopupSelection::Step step) {
diff --git a/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.h b/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.h
index e283b3d..d56cae2 100644
--- a/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.h
+++ b/chrome/browser/ui/webui/searchbox/webui_omnibox_handler.h
@@ -35,6 +35,7 @@
  public:
   WebuiOmniboxHandler(
       mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler,
+      mojo::PendingRemote<searchbox::mojom::Page> pending_page,
       MetricsReporter* metrics_reporter,
       OmniboxController* omnibox_controller,
       content::WebUI* web_ui,
@@ -61,10 +62,6 @@
   void OpenCurrentSelection(WindowOpenDisposition disposition);
   void SetAimButtonVisible(bool visible);
 
-  // ContextualSearchboxHandler:
-  void SetPage(
-      mojo::PendingRemote<searchbox::mojom::Page> pending_page) override;
-
   // SearchboxHandler:
   std::optional<searchbox::mojom::AutocompleteMatchPtr> CreateAutocompleteMatch(
       const AutocompleteMatch& match,
diff --git a/chrome/browser/ui/webui/settings/glic_handler.cc b/chrome/browser/ui/webui/settings/glic_handler.cc
index f9887eac..1e8620d 100644
--- a/chrome/browser/ui/webui/settings/glic_handler.cc
+++ b/chrome/browser/ui/webui/settings/glic_handler.cc
@@ -120,6 +120,14 @@
       "getWebActuationToggleVisibility",
       base::BindRepeating(&GlicHandler::HandleGetWebActuationToggleVisibility,
                           base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getWebActuationEnabled",
+      base::BindRepeating(&GlicHandler::HandleGetWebActuationEnabled,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "setWebActuationEnabled",
+      base::BindRepeating(&GlicHandler::HandleSetWebActuationEnabled,
+                          base::Unretained(this)));
 }
 
 void GlicHandler::OnJavascriptAllowed() {
@@ -136,12 +144,12 @@
         base::BindRepeating(&GlicHandler::OnWebActuationCapabilityChanged,
                             base::Unretained(this)));
 
-    pref_change_registrar_.Init(profile->GetPrefs());
-    pref_change_registrar_.Add(
-        glic::prefs::kGlicUserEnabledActuationOnWeb,
-        base::BindRepeating(&GlicHandler::OnWebActuationPrefChanged,
-                            base::Unretained(this)));
+    web_actuation_pref_subscription_ =
+        service->enabling().RegisterOnUserEnabledActuationOnWebChanged(
+            base::BindRepeating(&GlicHandler::OnWebActuationPrefChanged,
+                                base::Unretained(this)));
 
+    pref_change_registrar_.Init(profile->GetPrefs());
     pref_change_registrar_.Add(
         ::subscription_eligibility::prefs::kAiSubscriptionTier,
         base::BindRepeating(&GlicHandler::OnWebActuationPrefChanged,
@@ -163,6 +171,7 @@
 void GlicHandler::OnJavascriptDisallowed() {
   glic_enabling_subscription_ = {};
   web_actuation_subscription_ = {};
+  web_actuation_pref_subscription_ = {};
   observation_.Reset();
   pref_change_registrar_.RemoveAll();
 }
@@ -295,6 +304,15 @@
 
 void GlicHandler::OnWebActuationPrefChanged() {
   FireWebActuationToggleVisibilityChanged();
+
+  Profile* profile = Profile::FromWebUI(web_ui());
+  auto* glic_service =
+      glic::GlicKeyedServiceFactory::GetGlicKeyedService(profile);
+  if (glic_service) {
+    bool enabled = glic_service->enabling().GetUserEnabledActuationOnWeb();
+    FireWebUIListener("glic-web-actuation-enabled-changed",
+                      base::Value(enabled));
+  }
 }
 
 void GlicHandler::OnWebActuationCapabilityChanged(bool can_act_on_web) {
@@ -312,6 +330,33 @@
       base::Value(ShouldShowWebActuationToggle(Profile::FromWebUI(web_ui()))));
 }
 
+void GlicHandler::HandleGetWebActuationEnabled(const base::ListValue& args) {
+  CHECK_EQ(1U, args.size());
+  const base::Value& callback_id = args[0];
+  AllowJavascript();
+
+  Profile* profile = Profile::FromWebUI(web_ui());
+  auto* glic_service =
+      glic::GlicKeyedServiceFactory::GetGlicKeyedService(profile);
+  bool enabled = false;
+  if (glic_service) {
+    enabled = glic_service->enabling().GetUserEnabledActuationOnWeb();
+  }
+  ResolveJavascriptCallback(callback_id, base::Value(enabled));
+}
+
+void GlicHandler::HandleSetWebActuationEnabled(const base::ListValue& args) {
+  CHECK_EQ(1U, args.size());
+  const bool enabled = args[0].GetBool();
+
+  Profile* profile = Profile::FromWebUI(web_ui());
+  auto* glic_service =
+      glic::GlicKeyedServiceFactory::GetGlicKeyedService(profile);
+  if (glic_service) {
+    glic_service->enabling().SetUserEnabledActuationOnWeb(enabled);
+  }
+}
+
 void GlicHandler::FireWebActuationToggleVisibilityChanged() {
   bool is_visible =
       GlicHandler::ShouldShowWebActuationToggle(Profile::FromWebUI(web_ui()));
@@ -369,9 +414,7 @@
   }
   // Show the toggle if the user has explicitly modified the preference before
   // (via accepting the consent card).
-  const auto* pref = profile->GetPrefs()->FindPreference(
-      glic::prefs::kGlicUserEnabledActuationOnWeb);
-  if (pref && !pref->IsDefaultValue()) {
+  if (!glic_service->enabling().IsUserEnabledActuationOnWebDefault()) {
     return true;
   }
   return false;
diff --git a/chrome/browser/ui/webui/settings/glic_handler.h b/chrome/browser/ui/webui/settings/glic_handler.h
index 62c6234f..bae7e6ae 100644
--- a/chrome/browser/ui/webui/settings/glic_handler.h
+++ b/chrome/browser/ui/webui/settings/glic_handler.h
@@ -38,11 +38,21 @@
   // Returns whether the web actuation toggle should be shown for `profile`.
   static bool ShouldShowWebActuationToggle(Profile* profile);
 
+  void AllowJavascriptForTesting() { AllowJavascript(); }
+
+  // Sends the client whether the web actuation is enabled.
+  void HandleGetWebActuationEnabled(const base::ListValue& args);
+
+  // Updates the web actuation enabled state with the one provided in `args`.
+  void HandleSetWebActuationEnabled(const base::ListValue& args);
+
  private:
   FRIEND_TEST_ALL_PREFIXES(GlicHandlerBrowserTest, UpdateShortcutSuspension);
   FRIEND_TEST_ALL_PREFIXES(GlicHandlerBrowserTest, UpdateGlicShortcut);
   FRIEND_TEST_ALL_PREFIXES(GlicHandlerBrowserTest, GetActorLoginPermissions);
   FRIEND_TEST_ALL_PREFIXES(GlicHandlerBrowserTest, RevokeActorLoginPermission);
+  FRIEND_TEST_ALL_PREFIXES(GlicHandlerBrowserTest, GetWebActuationEnabled);
+  FRIEND_TEST_ALL_PREFIXES(GlicHandlerBrowserTest, SetWebActuationEnabled);
   FRIEND_TEST_ALL_PREFIXES(GlicHandlerConsentBrowserTest,
                            GetWebActuationToggleVisibility_ConsentAccepted);
   FRIEND_TEST_ALL_PREFIXES(GlicHandlerConsentBrowserTest,
@@ -130,6 +140,7 @@
 
   // Used to listen to changes in glic enabling status.
   base::CallbackListSubscription glic_enabling_subscription_;
+  base::CallbackListSubscription web_actuation_pref_subscription_;
 
   std::unique_ptr<actor_login::ActorLoginPermissionsManager>
       actor_login_permissions_manager_;
diff --git a/chrome/browser/ui/webui/settings/glic_handler_browsertest.cc b/chrome/browser/ui/webui/settings/glic_handler_browsertest.cc
index 336a00b..49d0f18 100644
--- a/chrome/browser/ui/webui/settings/glic_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/settings/glic_handler_browsertest.cc
@@ -14,6 +14,8 @@
 #include "chrome/browser/background/glic/glic_launcher_configuration.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/glic/glic_pref_names.h"
+#include "chrome/browser/glic/public/glic_enabling.h"
+#include "chrome/browser/glic/public/glic_keyed_service.h"
 #include "chrome/browser/glic/test_support/glic_test_environment.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/subscription_eligibility/subscription_eligibility_prefs.h"
@@ -210,10 +212,49 @@
   glic_handler()->HandleRevokeActorLoginPermission(args);
 }
 
+IN_PROC_BROWSER_TEST_F(GlicHandlerBrowserTest, GetWebActuationEnabled) {
+  glic_handler()->AllowJavascriptForTesting();
+
+  glic::GlicKeyedService::Get(browser()->profile())
+      ->enabling()
+      .SetUserEnabledActuationOnWeb(true);
+
+  base::ListValue args;
+  args.Append("callback-id");
+  glic_handler()->HandleGetWebActuationEnabled(args);
+
+  const content::TestWebUI::CallData& data = *web_ui()->call_data().back();
+  EXPECT_EQ("cr.webUIResponse", data.function_name());
+  EXPECT_EQ("callback-id", data.arg1()->GetString());
+  EXPECT_TRUE(data.arg2()->GetBool());
+  EXPECT_TRUE(data.arg3()->GetBool());
+}
+
+IN_PROC_BROWSER_TEST_F(GlicHandlerBrowserTest, SetWebActuationEnabled) {
+  glic_handler()->AllowJavascriptForTesting();
+
+  base::ListValue args;
+  args.Append(true);
+  glic_handler()->HandleSetWebActuationEnabled(args);
+
+  EXPECT_TRUE(glic::GlicKeyedService::Get(browser()->profile())
+                  ->enabling()
+                  .GetUserEnabledActuationOnWeb());
+
+  base::ListValue args2;
+  args2.Append(false);
+  glic_handler()->HandleSetWebActuationEnabled(args2);
+
+  EXPECT_FALSE(glic::GlicKeyedService::Get(browser()->profile())
+                   ->enabling()
+                   .GetUserEnabledActuationOnWeb());
+}
+
 IN_PROC_BROWSER_TEST_F(GlicHandlerConsentBrowserTest,
                        GetWebActuationToggleVisibility_ConsentAccepted) {
-  browser()->profile()->GetPrefs()->SetBoolean(
-      glic::prefs::kGlicUserEnabledActuationOnWeb, true);
+  glic::GlicKeyedService::Get(browser()->profile())
+      ->enabling()
+      .SetUserEnabledActuationOnWeb(true);
 
   glic_handler()->HandleGetWebActuationToggleVisibility(
       base::ListValue().Append("callback_id"));
@@ -226,9 +267,6 @@
 
 IN_PROC_BROWSER_TEST_F(GlicHandlerConsentBrowserTest,
                        GetWebActuationToggleVisibility_ConsentNotAccepted) {
-  browser()->profile()->GetPrefs()->ClearPref(
-      glic::prefs::kGlicUserEnabledActuationOnWeb);
-
   glic_handler()->HandleGetWebActuationToggleVisibility(
       base::ListValue().Append("callback_id"));
 
@@ -293,12 +331,14 @@
 IN_PROC_BROWSER_TEST_F(
     GlicHandlerConsentBrowserTest,
     FireWebActuationToggleVisibilityChanged_ConsentAccepted) {
-  browser()->profile()->GetPrefs()->ClearPref(
-      glic::prefs::kGlicUserEnabledActuationOnWeb);
+  glic::GlicKeyedService::Get(browser()->profile())
+      ->enabling()
+      .SetUserEnabledActuationOnWeb(false);
   glic_handler()->AllowJavascript();
   web_ui()->ClearTrackedCalls();
-  browser()->profile()->GetPrefs()->SetBoolean(
-      glic::prefs::kGlicUserEnabledActuationOnWeb, true);
+  glic::GlicKeyedService::Get(browser()->profile())
+      ->enabling()
+      .SetUserEnabledActuationOnWeb(true);
 
   glic_handler()->FireWebActuationToggleVisibilityChanged();
 
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.cc
index 10b3ca9..1f02939 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_ui.cc
@@ -7,7 +7,9 @@
 #include <string>
 #include <utility>
 
+#include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/accelerator_table.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.h"
 #include "chrome/browser/ui/webui/theme_source.h"
@@ -17,12 +19,16 @@
 #include "chrome/grit/side_panel_read_anything_resources_map.h"
 #include "chrome/grit/side_panel_shared_resources.h"
 #include "chrome/grit/side_panel_shared_resources_map.h"
+#if BUILDFLAG(IS_MAC)
+#include "chrome/browser/global_keyboard_shortcuts_mac.h"
+#endif
 #include "components/strings/grit/components_strings.h"
 #include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "ui/accessibility/accessibility_features.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/webui/web_ui_util.h"
 #include "ui/views/style/platform_style.h"
 #include "ui/webui/resources/grit/webui_resources.h"
@@ -137,7 +143,6 @@
       {"readingModeLanguageMenu", IDS_READING_MODE_LANGUAGE_MENU},
       {"readingModeLanguageMenuTitle", IDS_READING_MODE_LANGUAGE_MENU_TITLE},
       {"readingModeLanguageMenuClose", IDS_READING_MODE_LANGUAGE_MENU_CLOSE},
-      {"readingModeClose", IDS_READING_MODE_CLOSE},
       {"readingModeLanguageMenuSearchLabel",
        IDS_READING_MODE_LANGUAGE_MENU_SEARCH_LABEL},
       {"readingModeLanguageMenuSearchClear",
@@ -183,6 +188,16 @@
   for (const auto& str : kLocalizedStrings) {
     webui::AddLocalizedString(source, str.name, str.id);
   }
+  ui::Accelerator reading_mode_accelerator;
+  std::u16string reading_mode_shortcut;
+  if (GetAcceleratorForCommandId(IDC_SHOW_READING_MODE_KEYBOARD,
+                                 &reading_mode_accelerator)) {
+    reading_mode_shortcut = reading_mode_accelerator.GetShortcutText();
+  }
+
+  source->AddString("readingModeClose",
+                    l10n_util::GetStringFUTF16(IDS_READING_MODE_CLOSE,
+                                               reading_mode_shortcut));
 
   // Rather than call `webui::SetupWebUIDataSource`, manually set up source
   // here. This ensures that if CSPs change in a way that is safe for chrome://
diff --git a/chrome/browser/ui/webui/signin/signin_utils.h b/chrome/browser/ui/webui/signin/signin_utils.h
index e78da53..6593048 100644
--- a/chrome/browser/ui/webui/signin/signin_utils.h
+++ b/chrome/browser/ui/webui/signin/signin_utils.h
@@ -123,7 +123,7 @@
 base::TimeDelta GetMinorModeRestrictionsDeadline();
 
 // Sets the height of the WebUI modal dialog after its initialization. This is
-// needed to better accomodate different locales' text heights.
+// needed to better accommodate different locales' text heights.
 void SetInitializedModalHeight(Browser* browser,
                                content::WebUI* web_ui,
                                const base::ListValue& args);
diff --git a/chrome/browser/ui/webui/skills/skills_page_handler.cc b/chrome/browser/ui/webui/skills/skills_page_handler.cc
index 3d136be..8563ed9 100644
--- a/chrome/browser/ui/webui/skills/skills_page_handler.cc
+++ b/chrome/browser/ui/webui/skills/skills_page_handler.cc
@@ -13,6 +13,7 @@
 #include "components/skills/public/skill.h"
 #include "components/skills/public/skill.mojom.h"
 #include "components/skills/public/skills_metrics.h"
+#include "components/skills/public/skills_types.h"
 #include "components/sync/protocol/skill_specifics.pb.h"
 #include "components/tabs/public/tab_interface.h"
 #include "content/public/browser/web_contents.h"
@@ -21,14 +22,12 @@
 
 namespace skills {
 namespace {
-using FirstPartySkillsMap =
-    base::flat_map</*category=*/std::string, std::vector<skills::Skill>>;
 
-FirstPartySkillsMap Translate1PSkillsMap(
-    const SkillsService::SkillsMap& skills_map) {
-  FirstPartySkillsMap translated_map;
+SkillCategoryToSkillMap Translate1PSkillsMap(
+    const SkillIdToProtoMap& skills_map) {
+  SkillCategoryToSkillMap translated_map;
   for (const auto& [id, skill] : skills_map) {
-    skills::Skill translated_skill;
+    Skill translated_skill;
     translated_skill.id = skill.id();
     translated_skill.name = skill.name();
     translated_skill.icon = skill.icon();
@@ -218,14 +217,14 @@
 void SkillsPageHandler::GetInitial1PSkills(
     GetInitial1PSkillsCallback callback) {
   auto scoped_callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
-      std::move(callback), FirstPartySkillsMap());
+      std::move(callback), SkillCategoryToSkillMap());
   auto* service =
       SkillsServiceFactory::GetForProfile(base::to_address(profile_));
   std::move(scoped_callback).Run(Translate1PSkillsMap(service->Get1PSkills()));
 }
 
 void SkillsPageHandler::OnDiscoverySkillsUpdated(
-    const SkillsService::SkillsMap* skills_map) {
+    const SkillIdToProtoMap* skills_map) {
   first_party_download_timer_.Stop();
   RecordSkillsDownloadRequestStatus(
       SkillsDownloadRequestStatus::kResponseReceived);
diff --git a/chrome/browser/ui/webui/skills/skills_page_handler.h b/chrome/browser/ui/webui/skills/skills_page_handler.h
index 44a7215..5a59e05 100644
--- a/chrome/browser/ui/webui/skills/skills_page_handler.h
+++ b/chrome/browser/ui/webui/skills/skills_page_handler.h
@@ -9,6 +9,7 @@
 #include "chrome/browser/skills/skills_ui_tab_controller_interface.h"
 #include "chrome/browser/ui/webui/skills/skills.mojom.h"
 #include "components/skills/public/skills_service.h"
+#include "components/skills/public/skills_types.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
@@ -59,8 +60,7 @@
   void OnSkillUpdated(std::string_view skill_id,
                       SkillsService::UpdateSource update_source,
                       bool is_position_changed) override;
-  void OnDiscoverySkillsUpdated(
-      const SkillsService::SkillsMap* skills_map) override;
+  void OnDiscoverySkillsUpdated(const SkillIdToProtoMap* skills_map) override;
   void OnSkillsServiceShuttingDown() override;
   void OnTemporarySkillDisplay(
       std::string_view skill_id,
diff --git a/chrome/browser/ui/webui/skills/skills_page_handler_unittest.cc b/chrome/browser/ui/webui/skills/skills_page_handler_unittest.cc
index b69aeba..393fe8e5 100644
--- a/chrome/browser/ui/webui/skills/skills_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/skills/skills_page_handler_unittest.cc
@@ -22,6 +22,7 @@
 #include "components/skills/proto/skill.pb.h"
 #include "components/skills/public/skill.mojom.h"
 #include "components/skills/public/skills_metrics.h"
+#include "components/skills/public/skills_types.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_web_ui.h"
@@ -91,7 +92,7 @@
 };
 
 TEST_F(SkillsPageHandlerTest, OnDiscoverySkillsUpdated) {
-  auto skills_map = std::make_unique<SkillsDownloader::SkillsMap>();
+  auto skills_map = std::make_unique<SkillIdToProtoMap>();
 
   skills::proto::Skill skill_proto;
   skill_proto.set_id("skill_id");
@@ -137,7 +138,7 @@
   // Manually trigger map update with valid map
   skills::proto::Skill skill_proto;
   skill_proto.set_id("skill_id");
-  SkillsDownloader::SkillsMap skills_map = {{"skill_id", skill_proto}};
+  SkillIdToProtoMap skills_map = {{"skill_id", skill_proto}};
   handler_->OnDiscoverySkillsUpdated(&skills_map);
   EXPECT_TRUE(future.Get());
   EXPECT_FALSE(handler_->Is1PDownloadTimerRunning());
@@ -157,7 +158,7 @@
   // Manually trigger map update with valid map
   skills::proto::Skill skill_proto;
   skill_proto.set_id("skill_id");
-  SkillsDownloader::SkillsMap skills_map = {{"skill_id", skill_proto}};
+  SkillIdToProtoMap skills_map = {{"skill_id", skill_proto}};
   handler_->OnDiscoverySkillsUpdated(&skills_map);
   EXPECT_FALSE(future.Get());
   EXPECT_FALSE(handler_->Is1PDownloadTimerRunning());
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_ui.cc b/chrome/browser/ui/webui/tab_search/tab_search_ui.cc
index e2e9db7..c1d35fb 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search_ui.cc
+++ b/chrome/browser/ui/webui/tab_search/tab_search_ui.cc
@@ -11,6 +11,7 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/glic/resources/grit/glic_browser_resources.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/webui/favicon_source.h"
 #include "chrome/browser/ui/webui/metrics_reporter/metrics_reporter_service.h"
 #include "chrome/browser/ui/webui/plural_string_handler.h"
@@ -101,6 +102,9 @@
   };
   source->AddLocalizedStrings(kStrings);
   source->AddBoolean("useRipples", views::PlatformStyle::kUseRipples);
+  source->AddBoolean(
+      "useTabGroupColorRefresh",
+      base::FeatureList::IsEnabled(features::kTabGroupColorRefresh));
 
   source->AddLocalizedString("close", IDS_CLOSE);
 
diff --git a/chrome/browser/ui/webui_browser/webui_browser_ui.cc b/chrome/browser/ui/webui_browser/webui_browser_ui.cc
index 9ec5bcc8..deca208 100644
--- a/chrome/browser/ui/webui_browser/webui_browser_ui.cc
+++ b/chrome/browser/ui/webui_browser/webui_browser_ui.cc
@@ -163,13 +163,21 @@
 }
 
 void WebUIBrowserUI::BindInterface(
+    mojo::PendingReceiver<searchbox::mojom::PageHandlerFactory>
+        receiver) {
+  searchbox_page_factory_receiver_.reset();
+  searchbox_page_factory_receiver_.Bind(std::move(receiver));
+}
+
+void WebUIBrowserUI::CreatePageHandler(
+    mojo::PendingRemote<searchbox::mojom::Page> page,
     mojo::PendingReceiver<searchbox::mojom::PageHandler> pending_page_handler) {
-  content::WebUI* webui = web_ui();
-  content::WebContents* web_contents = webui->GetWebContents();
+  content::WebContents* web_contents = web_ui()->GetWebContents();
   // TODO(crbug.com/445510209): Pass `metrics_reporter_` after installing a
   // WebUIOmniboxHandler.
   realbox_handler_ = std::make_unique<RealboxHandler>(
-      std::move(pending_page_handler), Profile::FromWebUI(webui), web_contents,
+      std::move(pending_page_handler), std::move(page),
+      Profile::FromWebUI(web_ui()), web_contents,
       base::BindRepeating(&WebUIBrowserUI::GetOrCreateContextualSessionHandle,
                           base::Unretained(this)));
 }
diff --git a/chrome/browser/ui/webui_browser/webui_browser_ui.h b/chrome/browser/ui/webui_browser/webui_browser_ui.h
index a79ea8e..12f8144a 100644
--- a/chrome/browser/ui/webui_browser/webui_browser_ui.h
+++ b/chrome/browser/ui/webui_browser/webui_browser_ui.h
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/webui_browser/webui_browser_window.h"
 #include "components/browser_apis/tab_strip/tab_strip_api.mojom.h"
 #include "components/guest_contents/common/guest_contents.mojom.h"
+#include "components/omnibox/browser/searchbox.mojom.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui_controller.h"
 #include "content/public/browser/webui_config.h"
@@ -33,10 +34,6 @@
 class ContextualSearchSessionHandle;
 }  // namespace contextual_search
 
-namespace searchbox::mojom {
-class PageHandler;
-}  // namespace searchbox::mojom
-
 namespace ui {
 class TrackedElementHandler;
 }  // namespace ui
@@ -58,7 +55,8 @@
 class WebUIBrowserUI : public ui::MojoWebUIController,
                        public webui_browser::mojom::PageHandlerFactory,
                        public bookmark_bar::mojom::PageHandlerFactory,
-                       public extensions_bar::mojom::PageHandlerFactory {
+                       public extensions_bar::mojom::PageHandlerFactory,
+                       public searchbox::mojom::PageHandlerFactory {
  public:
   explicit WebUIBrowserUI(content::WebUI* web_ui);
   ~WebUIBrowserUI() override;
@@ -70,8 +68,9 @@
   void BindInterface(
       mojo::PendingReceiver<extensions_bar::mojom::PageHandlerFactory>
           receiver);
-  void BindInterface(mojo::PendingReceiver<searchbox::mojom::PageHandler>
-                         pending_page_handler);
+  void BindInterface(
+      mojo::PendingReceiver<searchbox::mojom::PageHandlerFactory>
+          receiver);
   void BindInterface(
       mojo::PendingReceiver<guest_contents::mojom::GuestContentsHost> receiver);
   void BindInterface(
@@ -120,6 +119,11 @@
       mojo::PendingReceiver<extensions_bar::mojom::PageHandler> receiver)
       override;
 
+  // searchbox::mojom::PageHandlerFactory:
+  void CreatePageHandler(
+      mojo::PendingRemote<searchbox::mojom::Page> page,
+      mojo::PendingReceiver<searchbox::mojom::PageHandler> receiver) override;
+
   // Returns the list of known element identifiers. These elements are HTML
   // elements tracked by ui/webui/tracked_element. Used for anchoring secondary
   // UIs.
@@ -142,6 +146,9 @@
   mojo::Receiver<extensions_bar::mojom::PageHandlerFactory>
       extensions_bar_page_factory_receiver_{this};
 
+  mojo::Receiver<searchbox::mojom::PageHandlerFactory>
+      searchbox_page_factory_receiver_{this};
+
   raw_ptr<Browser> browser_;
 
   base::WeakPtrFactory<WebUIBrowserUI> weak_factory_{this};
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 928be038..9b4750c98 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -1354,6 +1354,8 @@
     "//chrome/browser/permissions",
     "//chrome/browser/policy:policy_util",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/push_messaging",
+    "//chrome/browser/push_messaging:service_impl_public",
     "//chrome/browser/ui:browser_navigator_params_headers",
     "//chrome/browser/ui/views/location_bar",
     "//chrome/browser/ui/views/toolbar",
diff --git a/chrome/browser/web_applications/web_install_browsertest.cc b/chrome/browser/web_applications/web_install_browsertest.cc
index 6139baf..668f671 100644
--- a/chrome/browser/web_applications/web_install_browsertest.cc
+++ b/chrome/browser/web_applications/web_install_browsertest.cc
@@ -66,8 +66,6 @@
 constexpr webapps::WebappInstallSource kInstallSource =
     webapps::WebappInstallSource::WEB_INSTALL;
 constexpr char kAbortError[] = "AbortError";
-constexpr char kNotAllowedError[] = "NotAllowedError";
-constexpr char kTypeError[] = "TypeError";
 constexpr char kInstallResultUma[] = "WebApp.WebInstallApi.Result";
 constexpr char kInstallTypeUma[] = "WebApp.WebInstallApi.InstallType";
 constexpr char kVariantedInstallTypeUma[] =
@@ -921,180 +919,6 @@
   // and WebAppDataRetriever should have been cleaned up gracefully.
 }
 
-// Implementation-generic tests for bad JavaScript API inputs. This failure
-// handling is on the blink side, so there aren't any browser results to verify.
-using WebInstallServiceImplBrowserTestBadInput =
-    WebInstallCurrentDocumentBrowserTest;
-
-IN_PROC_BROWSER_TEST_F(WebInstallServiceImplBrowserTestBadInput,
-                       MissingUserGesture) {
-  NavigateToValidUrl();
-
-  std::string install_url = GetInstallableAppURL().spec();
-  std::string manifest_id = install_url;
-  ASSERT_TRUE(TryInstallApp(/*with_gesture=*/false));
-
-  EXPECT_FALSE(ResultExists());
-  EXPECT_TRUE(ErrorExists());
-  EXPECT_EQ(GetErrorName(), kNotAllowedError);
-}
-
-IN_PROC_BROWSER_TEST_F(WebInstallServiceImplBrowserTestBadInput,
-                       OneParam_Undefined) {
-  NavigateToValidUrl();
-
-  const std::string script =
-      "let install_url;"
-      "navigator.install(install_url).then(result => {"
-      "  webInstallResult = result;"
-      "}).catch(error => {"
-      "  webInstallError = error;"
-      "});";
-  ASSERT_TRUE(ExecJs(web_contents(), script));
-
-  EXPECT_FALSE(ResultExists());
-  EXPECT_TRUE(ErrorExists());
-  EXPECT_EQ(GetErrorName(), kTypeError);
-}
-
-IN_PROC_BROWSER_TEST_F(WebInstallServiceImplBrowserTestBadInput,
-                       OneParam_Null) {
-  NavigateToValidUrl();
-
-  const std::string script =
-      "let install_url=null;"
-      "navigator.install(install_url).then(result => {"
-      "  webInstallResult = result;"
-      "}).catch(error => {"
-      "  webInstallError = error;"
-      "});";
-  ASSERT_TRUE(ExecJs(web_contents(), script));
-
-  EXPECT_FALSE(ResultExists());
-  EXPECT_TRUE(ErrorExists());
-  EXPECT_EQ(GetErrorName(), kTypeError);
-}
-
-IN_PROC_BROWSER_TEST_F(WebInstallServiceImplBrowserTestBadInput,
-                       OneParam_Number) {
-  NavigateToValidUrl();
-
-  const std::string script =
-      "let install_url = new Number(1);"
-      "navigator.install(install_url).then(result => {"
-      "  webInstallResult = result;"
-      "}).catch(error => {"
-      "  webInstallError = error;"
-      "});";
-  ASSERT_TRUE(ExecJs(web_contents(), script));
-
-  EXPECT_FALSE(ResultExists());
-  EXPECT_TRUE(ErrorExists());
-  EXPECT_EQ(GetErrorName(), kTypeError);
-}
-
-IN_PROC_BROWSER_TEST_F(WebInstallServiceImplBrowserTestBadInput,
-                       OneParam_Empty) {
-  NavigateToValidUrl();
-
-  const std::string script =
-      "let install_url='';"
-      "navigator.install(install_url).then(result => {"
-      "  webInstallResult = result;"
-      "}).catch(error => {"
-      "  webInstallError = error;"
-      "});";
-  ASSERT_TRUE(ExecJs(web_contents(), script));
-
-  EXPECT_FALSE(ResultExists());
-  EXPECT_TRUE(ErrorExists());
-  EXPECT_EQ(GetErrorName(), kTypeError);
-}
-
-IN_PROC_BROWSER_TEST_F(WebInstallServiceImplBrowserTestBadInput,
-                       TwoParams_UndefinedInstallUrl) {
-  NavigateToValidUrl();
-
-  const std::string manifest_id = GetInstallableAppURL().spec();
-  const std::string script =
-      "let install_url;"
-      "navigator.install(install_url, '" +
-      manifest_id +
-      "').then(result => {"
-      "  webInstallResult = result;"
-      "}).catch(error => {"
-      "  webInstallError = error;"
-      "});";
-  ASSERT_TRUE(ExecJs(web_contents(), script));
-
-  EXPECT_FALSE(ResultExists());
-  EXPECT_TRUE(ErrorExists());
-  EXPECT_EQ(GetErrorName(), kTypeError);
-}
-
-IN_PROC_BROWSER_TEST_F(WebInstallServiceImplBrowserTestBadInput,
-                       TwoParams_UndefinedManifestId) {
-  NavigateToValidUrl();
-
-  const std::string install_url = GetInstallableAppURL().spec();
-  const std::string script =
-      "let manifest_id;"
-      "navigator.install('" +
-      install_url +
-      "', manifest_id).then(result => {"
-      "  webInstallResult = result;"
-      "}).catch(error => {"
-      "  webInstallError = error;"
-      "});";
-  ASSERT_TRUE(ExecJs(web_contents(), script));
-
-  EXPECT_FALSE(ResultExists());
-  EXPECT_TRUE(ErrorExists());
-  EXPECT_EQ(GetErrorName(), kTypeError);
-}
-
-IN_PROC_BROWSER_TEST_F(WebInstallServiceImplBrowserTestBadInput,
-                       TwoParams_EmptyManifestId) {
-  NavigateToValidUrl();
-
-  const std::string install_url = GetInstallableAppURL().spec();
-  const std::string script =
-      "let manifest_id = '';"
-      "navigator.install('" +
-      install_url +
-      "', manifest_id).then(result => {"
-      "  webInstallResult = result;"
-      "}).catch(error => {"
-      "  webInstallError = error;"
-      "});";
-  ASSERT_TRUE(ExecJs(web_contents(), script));
-
-  EXPECT_FALSE(ResultExists());
-  EXPECT_TRUE(ErrorExists());
-  EXPECT_EQ(GetErrorName(), kTypeError);
-}
-
-IN_PROC_BROWSER_TEST_F(WebInstallServiceImplBrowserTestBadInput,
-                       TwoParams_NullManifestId) {
-  NavigateToValidUrl();
-
-  const std::string install_url = GetInstallableAppURL().spec();
-  const std::string script =
-      "let manifest_id = null;"
-      "navigator.install('" +
-      install_url +
-      "', manifest_id).then(result => {"
-      "  webInstallResult = result;"
-      "}).catch(error => {"
-      "  webInstallError = error;"
-      "});";
-  ASSERT_TRUE(ExecJs(web_contents(), script));
-
-  EXPECT_FALSE(ResultExists());
-  EXPECT_TRUE(ErrorExists());
-  EXPECT_EQ(GetErrorName(), kTypeError);
-}
-
 namespace {
 
 // Generate token with the command:
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index b24b7fa..f455f66 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1775735984-5f409b84636bdc39095b842020a4af01ca2a323d-157a53aa9d8c9e25cbe4c6573978099470761dac.profdata
+chrome-android32-main-1775843980-a07baeb2c39cb0270e00eed7946721480345d45e-6ea00cf6a3b5b7ebd9599df33e16ea71e51b161c.profdata
diff --git a/chrome/build/android-arm64.orderfile.txt b/chrome/build/android-arm64.orderfile.txt
index 5c716d1..fffa510 100644
--- a/chrome/build/android-arm64.orderfile.txt
+++ b/chrome/build/android-arm64.orderfile.txt
@@ -1 +1 @@
-6fIvktyHXuoMdm4cw9FWb6R2aC-8NmAsuXjebwK3REcC
+fPCKzHTrOPg43a_UlN1gxFrRGS4HIvAPchZUc_yZhUwC
diff --git a/chrome/build/android-desktop-x64.pgo.txt b/chrome/build/android-desktop-x64.pgo.txt
index 4689456..fdd12f0 100644
--- a/chrome/build/android-desktop-x64.pgo.txt
+++ b/chrome/build/android-desktop-x64.pgo.txt
@@ -1 +1 @@
-chrome-android-desktop-x64-main-1775822092-5e0849fa9cda6c1fb07ce50fef82f15b2fc55c6d-123ee915f2e081733b59fad9eeb7c59e899597c4.profdata
+chrome-android-desktop-x64-main-1775843980-80e5744c231b73c8c9b0109d8a9102c119738cf2-6ea00cf6a3b5b7ebd9599df33e16ea71e51b161c.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index ec9be0b..50f6968 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1775836611-6b3261299049db70b99cd6de82d7a17545ee7dfd-be6d55a5fb2031ad63aad40de4bd47244f98ba77.profdata
+chrome-mac-arm-main-1775858363-984a78f8149c018030e17a54a32301d3c6b1f838-a2f7f211117a597cb995cb30bbb633088a85c300.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index d8a9fe0..0e44932e 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1775822092-fe9a2c00068fe2310a3fe3ab47b1b348fbbb1496-123ee915f2e081733b59fad9eeb7c59e899597c4.profdata
+chrome-win-arm64-main-1775843980-0c3cccc597f0e4a779db640159796bbbec9c358c-6ea00cf6a3b5b7ebd9599df33e16ea71e51b161c.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 95ec6b6..941085f 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1775833181-f4ae81e292b7c42a3fad3951b9fd3198fcf71ed3-76c9eaad9328fad864c9e4e7237cea0849bb8656.profdata
+chrome-win32-main-1775843980-b8809958cd0f08fb598ccb5f65a9edc4377b79f1-6ea00cf6a3b5b7ebd9599df33e16ea71e51b161c.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 5e82311..f53624e 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1775822092-7381cbaea463fe2758230cf4a84bf0d0096229d2-123ee915f2e081733b59fad9eeb7c59e899597c4.profdata
+chrome-win64-main-1775843980-2bb9a4599d10ba689fb25ef927a18c036ddef7c9-6ea00cf6a3b5b7ebd9599df33e16ea71e51b161c.profdata
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc
index cec3baf..9afef32 100644
--- a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc
+++ b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.cc
@@ -859,6 +859,10 @@
 }
 
 void ReadAnythingAppController::RecordNumSelections() {
+  if (IsHidden()) {
+    return;
+  }
+
   ukm::builders::Accessibility_ReadAnything_EmptyState(model_.GetUkmSourceId())
       .SetTotalNumSelections(model_.GetNumSelections())
       .Record(ukm_recorder_.get());
@@ -866,6 +870,10 @@
 }
 
 void ReadAnythingAppController::RecordEstimatedWordsSeen() {
+  if (IsHidden()) {
+    return;
+  }
+
   VLOG(1) << "Words seen: " << model_.words_seen();
   base::UmaHistogramCustomCounts(kWordsSeenHistogramName, model_.words_seen(),
                                  1, kMaxWordsConsumed, kWordsConsumedBuckets);
@@ -873,6 +881,10 @@
 }
 
 void ReadAnythingAppController::RecordEstimatedWordsHeard() {
+  if (IsHidden()) {
+    return;
+  }
+
   VLOG(1) << "Words heard: " << model_.words_heard();
   base::UmaHistogramCustomCounts(kWordsHeardHistogramName, model_.words_heard(),
                                  1, kMaxWordsConsumed, kWordsConsumedBuckets);
@@ -1076,7 +1088,7 @@
   // AXNode's language code is BCP 47. Only the base language is needed to
   // record the metric.
   std::string language = model_.GetActiveTree()->root()->GetLanguage();
-  if (!language.empty()) {
+  if (!language.empty() && !IsHidden()) {
     base::UmaHistogramSparse(
         "Accessibility.ReadAnything.Language",
         base::HashMetricName(language::ExtractBaseLanguage(language)));
@@ -1172,6 +1184,9 @@
 }
 
 void ReadAnythingAppController::LogEmptyState() {
+  if (IsHidden()) {
+    return;
+  }
   base::UmaHistogramEnumeration(ReadAnythingAppModel::kEmptyStateHistogramName,
                                 ReadAnythingAppModel::EmptyState::kShown);
 }
@@ -2717,7 +2732,7 @@
 
 void ReadAnythingAppController::LogLineFocusSession() {
   if (IsLineFocusEnabled() &&
-      model_.line_focus_session_start_time().has_value()) {
+      model_.line_focus_session_start_time().has_value() && !IsHidden()) {
     base::UmaHistogramLongTimes(
         "Accessibility.ReadAnything.LineFocusSessionLength",
         base::TimeTicks::Now() -
@@ -2943,3 +2958,9 @@
     ExecuteJavaScript("chrome.readingMode.onAnchorsReadyForReadability();");
   }
 }
+
+bool ReadAnythingAppController::IsHidden() {
+  return IsImmersiveEnabled() &&
+         model_.active_presentation_state() ==
+             read_anything::mojom::ReadAnythingPresentationState::kInactive;
+}
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.h b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.h
index 241c6ff8..46bfc9bd 100644
--- a/chrome/renderer/accessibility/read_anything/read_anything_app_controller.h
+++ b/chrome/renderer/accessibility/read_anything/read_anything_app_controller.h
@@ -536,6 +536,8 @@
   // between the training protos and the screenshot if it runs more than once.
   void DistillAndScreenshot();
 
+  bool IsHidden();
+
   std::unique_ptr<AXTreeDistiller> distiller_;
   mojo::Remote<read_anything::mojom::UntrustedPageHandlerFactory>
       page_handler_factory_;
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc b/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc
index cd63f46..5a49419 100644
--- a/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc
+++ b/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc
@@ -841,6 +841,12 @@
 
 void ReadAnythingAppModel::OnScroll(bool on_selection,
                                     bool from_reading_mode) const {
+  // Scroll events shouldn't be logged when reading mode is inactive.
+  if (features::IsImmersiveReadAnythingEnabled() &&
+      active_presentation_state_ ==
+          read_anything::mojom::ReadAnythingPresentationState::kInactive) {
+    return;
+  }
   // Enum for logging how a scroll occurs.
   // These values are persisted to logs. Entries should not be renumbered and
   // numeric values should never be reused.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 93ccfd374..18ce662 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2721,6 +2721,9 @@
       "//chrome/browser/profiles:profile_util",
       "//chrome/browser/profiles/keep_alive",
       "//chrome/browser/profiling_host:profiling_browsertests",
+      "//chrome/browser/push_messaging",
+      "//chrome/browser/push_messaging:browser_tests",
+      "//chrome/browser/push_messaging:service_impl_public",
       "//chrome/browser/reading_list",
       "//chrome/browser/reduce_accept_language:browser_tests",
       "//chrome/browser/regional_capabilities:browser_tests",
@@ -3671,7 +3674,6 @@
       "../browser/profiles/profile_keyed_service_browsertest.cc",
       "../browser/profiles/profile_list_desktop_browsertest.cc",
       "../browser/profiles/profile_manager_browsertest.cc",
-      "../browser/push_messaging/push_messaging_browsertest.cc",
       "../browser/referrer_policy_browsertest.cc",
       "../browser/renderer_host/javascript_optimizer_feature_browsertest.cc",
       "../browser/renderer_host/render_process_host_chrome_browsertest.cc",
@@ -4072,7 +4074,6 @@
     }
 
     if (is_chrome_for_testing) {
-      sources += [ "../browser/chrome_for_testing/chrome_for_testing_info_bar_browsertest.cc" ]
       deps += [ "//chrome/browser/chrome_for_testing:browser_tests" ]
     }
 
@@ -6712,12 +6713,6 @@
     "../browser/profiles/profile_selections_unittest.cc",
     "../browser/profiles/profiles_state_unittest.cc",
     "../browser/profiling_host/chrome_client_connection_manager_unittest.cc",
-    "../browser/push_messaging/budget_database_unittest.cc",
-    "../browser/push_messaging/push_messaging_app_identifier_unittest.cc",
-    "../browser/push_messaging/push_messaging_notification_manager_unittest.cc",
-    "../browser/push_messaging/push_messaging_refresher_unittest.cc",
-    "../browser/push_messaging/push_messaging_service_unittest.cc",
-    "../browser/push_messaging/push_messaging_unsubscribed_entry_unittest.cc",
     "../browser/renderer_preferences_util_unittest.cc",
     "../browser/resource_coordinator/tab_load_tracker_unittest.cc",
     "../browser/resources_util_unittest.cc",
@@ -7121,7 +7116,8 @@
     "//chrome/browser/profiles:unit_tests",
     "//chrome/browser/profiles/keep_alive",
     "//chrome/browser/profiling_host",
-    "//chrome/browser/push_messaging:budget_proto",
+    "//chrome/browser/push_messaging",
+    "//chrome/browser/push_messaging:unit_tests",
     "//chrome/browser/reading_list",
     "//chrome/browser/reduce_accept_language:unit_tests",
     "//chrome/browser/regional_capabilities:unit_tests",
@@ -9193,6 +9189,8 @@
       "//chrome/browser/enterprise/connectors/device_trust/attestation/ash",
       "//chrome/browser/extensions/api/terminal:startup_status",
       "//chrome/browser/media_galleries/fileapi:unit_tests",
+      "//chrome/browser/nearby_sharing",
+      "//chrome/browser/nearby_sharing:impl",
       "//chrome/browser/nearby_sharing:share_target",
       "//chrome/browser/nearby_sharing/certificates",
       "//chrome/browser/nearby_sharing/certificates:test_support",
@@ -12483,10 +12481,7 @@
       "../browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc",
     ]
 
-    deps += [
-      "//components/on_device_translation:public",
-      "//components/soda",
-    ]
+    deps += [ "//components/soda" ]
 
     if (safe_browsing_mode == 1) {
       sources += [ "../browser/safe_browsing/safe_browsing_navigation_observer_interactive_test.cc" ]
diff --git a/chrome/test/data/webui/cr_components/composebox/file_carousel_test.ts b/chrome/test/data/webui/cr_components/composebox/file_carousel_test.ts
index ef284b7..7f9800f 100644
--- a/chrome/test/data/webui/cr_components/composebox/file_carousel_test.ts
+++ b/chrome/test/data/webui/cr_components/composebox/file_carousel_test.ts
@@ -71,6 +71,25 @@
     assertEquals(2, thumbnails.length);
   });
 
+  test('renders tab chip with title tooltip', async () => {
+    const tabFile: ComposeboxFile = {
+      ...createFile(1),
+      type: 'tab',
+      url: 'https://example.com' as any,
+      name: 'Example Tab Title',
+    };
+    fileCarousel.files = [tabFile];
+    await microtasksFinished();
+
+    const thumbnail =
+        fileCarousel.shadowRoot.querySelector('cr-composebox-file-thumbnail');
+    assertTrue(!!thumbnail);
+
+    const tabChip = thumbnail.shadowRoot.querySelector('#tabChip');
+    assertTrue(!!tabChip);
+    assertEquals('Example Tab Title', tabChip.getAttribute('title'));
+  });
+
   test('getThumbnailElementByUuid returns correct element', async () => {
     const uuid1 = {high: 0n, low: 1n} as any;
     const uuid2 = {high: 0n, low: 2n} as any;
diff --git a/chrome/test/data/webui/cr_components/searchbox/searchbox_ntp_test.ts b/chrome/test/data/webui/cr_components/searchbox/searchbox_ntp_test.ts
index a303317..a4622b0 100644
--- a/chrome/test/data/webui/cr_components/searchbox/searchbox_ntp_test.ts
+++ b/chrome/test/data/webui/cr_components/searchbox/searchbox_ntp_test.ts
@@ -260,23 +260,13 @@
       description: 'theming refresh disabled',
       properties: {
         searchboxChromeRefreshTheming: false,
-        colorSourceIsBaseline: true,
       },
       shouldUseWebkit: false,
     },
     {
-      description: 'theming refresh with baseline color',
+      description: 'theming refresh enabled',
       properties: {
         searchboxChromeRefreshTheming: true,
-        colorSourceIsBaseline: true,
-      },
-      shouldUseWebkit: false,
-    },
-    {
-      description: 'theming refresh with non-baseline color',
-      properties: {
-        searchboxChromeRefreshTheming: true,
-        colorSourceIsBaseline: false,
       },
       shouldUseWebkit: true,
     },
diff --git a/chrome/test/data/webui/cr_components/searchbox/test_searchbox_browser_proxy.ts b/chrome/test/data/webui/cr_components/searchbox/test_searchbox_browser_proxy.ts
index 3d1e827..b3b1485d 100644
--- a/chrome/test/data/webui/cr_components/searchbox/test_searchbox_browser_proxy.ts
+++ b/chrome/test/data/webui/cr_components/searchbox/test_searchbox_browser_proxy.ts
@@ -53,7 +53,6 @@
       'recordToolSelectionAction',
       'setActiveModelMode',
       'recordModelSelectionAction',
-      'setPage',
       'getInputState',
       'activateMetricsFunnel',
       'setPopupSelection',
@@ -67,10 +66,6 @@
     this.results_.set(methodName, result);
   }
 
-  setPage(page: PageRemote) {
-    this.methodCalled('setPage', page);
-  }
-
   onFocusChanged(focused: boolean) {
     this.methodCalled('onFocusChanged', {focused});
   }
diff --git a/chrome/test/data/webui/guest_view_shared/slim_webview_test.ts b/chrome/test/data/webui/guest_view_shared/slim_webview_test.ts
index 1dfce1ca..c9c3d7a 100644
--- a/chrome/test/data/webui/guest_view_shared/slim_webview_test.ts
+++ b/chrome/test/data/webui/guest_view_shared/slim_webview_test.ts
@@ -147,6 +147,60 @@
         loadAbortEvent.reason));
     assertEquals(webviewUrl, loadCommitEvent.url);
   });
+
+  // NOTE: We do not test navigation to a disallowed origin here.
+  // In slim_web_view_page_handler.cc, a disallowed navigation triggers
+  // mojo::ReportBadMessage, which terminates the renderer process.
+  // In WebUI unit tests, this would cause the test itself to fail due to
+  // process crash, making it difficult to test directly in this suite.
+
+  test('AllowedOriginNavigation', async function() {
+    const webviewUrl = getTestUrl('/simple.html');
+    const origin = getOrigin(webviewUrl);
+
+    const webview = document.createElement('webview');
+    webview.allowedOrigins = [origin];
+    document.body.appendChild(webview);
+
+    const loadStartPromise = eventToPromise<LoadEvent>('loadstart', webview);
+    const loadCommitPromise = eventToPromise<LoadEvent>('loadcommit', webview);
+    const contentLoadPromise = eventToPromise('contentload', webview);
+    const loadStopPromise = eventToPromise('loadstop', webview);
+
+    webview.src = webviewUrl;
+
+    const [loadStartEvent, loadCommitEvent] = await Promise.all([
+      loadStartPromise,
+      loadCommitPromise,
+      contentLoadPromise,
+      loadStopPromise,
+    ]);
+
+    assertEquals(webviewUrl, loadStartEvent.url);
+    assertEquals(webviewUrl, loadCommitEvent.url);
+  });
+
+  test('InvalidAllowedOriginPatternFailsCreation', async function() {
+    const webview = document.createElement('webview');
+    // An invalid pattern that should fail parsing in SimpleUrlPatternMatcher.
+    webview.allowedOrigins = ['invalid pattern'];
+
+    const failurePromise = new Promise<void>((resolve) => {
+      window.addEventListener('unhandledrejection', function listener(e) {
+        if (e.reason.message &&
+            e.reason.message.includes('Failed to create guest')) {
+          e.preventDefault();
+          window.removeEventListener('unhandledrejection', listener);
+          resolve();
+        }
+      });
+    });
+
+    document.body.appendChild(webview);
+    webview.src = getTestUrl('/simple.html');
+
+    await failurePromise;
+  });
 });
 
 suite('Operations', function() {
@@ -219,7 +273,30 @@
   });
 });
 
-suite('Headers', function() {
+suite('Requests', function() {
+  test('NavigateToDisallowedOriginFails', async function() {
+    const webview = document.createElement('webview');
+    const origin = getOrigin(getTestUrl('/'));
+    // Only allow the test origin.
+    webview.allowedOrigins = [origin];
+    document.body.appendChild(webview);
+
+    await navigateAndWaitForContentLoad(
+        webview, getTestUrl('/webui/guest_view_shared/eval_post_message.html'));
+
+    const loadAbortPromise =
+        eventToPromise<LoadAbortEvent>('loadabort', webview);
+
+    // Trigger a navigation to a DISALLOWED origin via window.location.
+    evalOnWebview(webview, () => {
+      window.location.href = 'https://www.google.com/';
+    });
+
+    const loadAbortEvent = await loadAbortPromise;
+
+    assertEquals('ERR_BLOCKED_BY_CLIENT', loadAbortEvent.reason);
+  });
+
   test('HeadersConfigured', async function() {
     const webviewUrl =
         getTestUrl('/webui/guest_view_shared/eval_post_message.html');
@@ -251,7 +328,7 @@
     assertEquals('test-value', fetchHeaders['X-Test-Header']);
   });
 
-  test('UnsecureHeadersFail', async function() {
+  test('InsecureHeadersFail', async function() {
     const webview = document.createElement('webview');
 
     webview.onBeforeSendHeadersParams = new OnBeforeSendHeadersParams(
diff --git a/chrome/test/data/webui/settings/glic_subpage_test.ts b/chrome/test/data/webui/settings/glic_subpage_test.ts
index 0c9e90b9..f244209 100644
--- a/chrome/test/data/webui/settings/glic_subpage_test.ts
+++ b/chrome/test/data/webui/settings/glic_subpage_test.ts
@@ -770,15 +770,15 @@
       assertTrue(isVisible(webActuationToggle));
     });
 
-    test('ToggleEnabled', () => {
-      page.setPrefValue(PrefName.WEB_ACTUATION_ENABLED, true);
-
+    test('ToggleEnabled', async () => {
+      page.set('webActuationEnabledPref_.value', true);
+      await flushTasks();
       assertTrue($<SettingsToggleButtonElement>('webActuationToggle')!.checked);
     });
 
-    test('ToggleDisabled', () => {
-      page.setPrefValue(PrefName.WEB_ACTUATION_ENABLED, false);
-
+    test('ToggleDisabled', async () => {
+      page.set('webActuationEnabledPref_.value', false);
+      await flushTasks();
       assertFalse(
           $<SettingsToggleButtonElement>('webActuationToggle')!.checked);
     });
@@ -787,7 +787,8 @@
       const webActuationToggle =
           $<SettingsToggleButtonElement>('webActuationToggle')!;
       const infoCard = $<CrCollapseElement>('webActuationInfoCollapse')!;
-      page.setPrefValue(PrefName.WEB_ACTUATION_ENABLED, false);
+      page.set('webActuationEnabledPref_.value', false);
+      await flushTasks();
 
       assertFalse(infoCard.opened);
 
@@ -796,27 +797,33 @@
       webActuationToggle.click();
       await flushTasks();
       assertTrue(infoCard.opened);
-      assertFalse(page.getPref(PrefName.WEB_ACTUATION_ENABLED).value);
+      assertFalse(webActuationToggle.checked);
 
       // Clicking the host element again collapses the info card.
       webActuationToggle.click();
       await flushTasks();
       assertFalse(infoCard.opened);
-      assertFalse(page.getPref(PrefName.WEB_ACTUATION_ENABLED).value);
+      assertFalse(webActuationToggle.checked);
 
       // Toggling the setting to on expands the info card.
       webActuationToggle.$.control.click();
       await flushTasks();
-      assertTrue(page.getPref(PrefName.WEB_ACTUATION_ENABLED).value);
+      assertTrue(webActuationToggle.checked);
       assertTrue(infoCard.opened);
+      assertEquals(1, glicBrowserProxy.getCallCount('setWebActuationEnabled'));
+      assertTrue(glicBrowserProxy.getArgs('setWebActuationEnabled')[0]);
       await verifyUserAction('Glic.Settings.WebActuation.Enabled');
+      glicBrowserProxy.reset();
 
       // Toggling the setting off collapses the info card.
       webActuationToggle.$.control.click();
       await flushTasks();
-      assertFalse(page.getPref(PrefName.WEB_ACTUATION_ENABLED).value);
+      assertFalse(webActuationToggle.checked);
       assertFalse(infoCard.opened);
+      assertEquals(1, glicBrowserProxy.getCallCount('setWebActuationEnabled'));
+      assertFalse(glicBrowserProxy.getArgs('setWebActuationEnabled')[0]);
       await verifyUserAction('Glic.Settings.WebActuation.Disabled');
+      glicBrowserProxy.reset();
 
       // Toggling the setting to on while the info card is open leaves it open.
       webActuationToggle.click();
@@ -824,7 +831,7 @@
       assertTrue(infoCard.opened);
       webActuationToggle.$.control.click();
       await flushTasks();
-      assertTrue(page.getPref(PrefName.WEB_ACTUATION_ENABLED).value);
+      assertTrue(webActuationToggle.checked);
       assertTrue(infoCard.opened);
 
       // Toggling the setting to off while the info card is closed leaves it
@@ -834,7 +841,7 @@
       assertFalse(infoCard.opened);
       webActuationToggle.$.control.click();
       await flushTasks();
-      assertFalse(page.getPref(PrefName.WEB_ACTUATION_ENABLED).value);
+      assertFalse(webActuationToggle.checked);
       assertFalse(infoCard.opened);
     });
   });
@@ -969,7 +976,7 @@
     }
 
     test('ToggleDisabledWhenCanActOnWebFalse', async () => {
-      page.setPrefValue(PrefName.WEB_ACTUATION_ENABLED, true);
+      page.set('webActuationEnabledPref_.value', true);
       await flushTasks();
 
       // Verify initial state (enabled).
@@ -995,12 +1002,12 @@
       const webActuationToggle =
           $<SettingsToggleButtonElement>('webActuationToggle')!;
       let infoCard = $<CrCollapseElement>('webActuationInfoCollapse')!;
-      page.setPrefValue(PrefName.WEB_ACTUATION_ENABLED, false);
+      page.set('webActuationEnabledPref_.value', false);
+      await flushTasks();
       assertFalse(infoCard.opened);
       webActuationToggle.click();
       await flushTasks();
       assertTrue(infoCard.opened);
-      assertFalse(page.getPref(PrefName.WEB_ACTUATION_ENABLED).value);
 
       // Simulate enterprise DISABLING the feature.
       await setWebActuationCapability(false);
@@ -1020,7 +1027,7 @@
       assertFalse(infoCard.opened);  // Starts closed.
 
       // Try to enable it via pref (e.g. from sync).
-      page.setPrefValue(PrefName.WEB_ACTUATION_ENABLED, true);
+      page.set('webActuationEnabledPref_.value', true);
       await flushTasks();
 
       // Should still be closed because webActuationEnabledExpanded_
diff --git a/chrome/test/data/webui/settings/settings_browsertest.cc b/chrome/test/data/webui/settings/settings_browsertest.cc
index b2ac037..126ae63f 100644
--- a/chrome/test/data/webui/settings/settings_browsertest.cc
+++ b/chrome/test/data/webui/settings/settings_browsertest.cc
@@ -10,6 +10,8 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/enterprise/browser_management/management_service_factory.h"
 #include "chrome/browser/glic/glic_pref_names.h"
+#include "chrome/browser/glic/public/glic_enabling.h"
+#include "chrome/browser/glic/public/glic_keyed_service.h"
 #include "chrome/browser/glic/test_support/glic_test_environment.h"
 #include "chrome/browser/glic/test_support/glic_test_util.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
@@ -439,6 +441,11 @@
           "runMochaSuite('GlicSubpage ExperimentalTriggeringToggle')");
 }
 
+IN_PROC_BROWSER_TEST_F(SettingsTest, GlicSubpageWebActuation) {
+  RunTest("settings/glic_subpage_test.js",
+          "runMochaSuite('GlicSubpage WebActuationSettingFeatureEnabled')");
+}
+
 IN_PROC_BROWSER_TEST_F(SettingsTest, GlicLoginPermissionsPage) {
   RunTest("settings/glic_login_permissions_page_test.js", "mocha.run()");
 }
@@ -743,12 +750,9 @@
 
     SetUserTier(p.user_tier);
     if (p.consent_pref_set) {
-      GetProfile()->GetPrefs()->SetBoolean(
-          glic::prefs::kGlicUserEnabledActuationOnWeb, true);
-    } else {
-      // For "No Pref" branch, clear the pref so IsDefaultValue() returns true.
-      GetProfile()->GetPrefs()->ClearPref(
-          glic::prefs::kGlicUserEnabledActuationOnWeb);
+      glic::GlicKeyedService::Get(GetProfile())
+          ->enabling()
+          .SetUserEnabledActuationOnWeb(true);
     }
     if (p.is_dogfooder) {
       auto* variations_service = g_browser_process->variations_service();
diff --git a/chrome/test/data/webui/settings/test_glic_browser_proxy.ts b/chrome/test/data/webui/settings/test_glic_browser_proxy.ts
index a02d461..9afb96e 100644
--- a/chrome/test/data/webui/settings/test_glic_browser_proxy.ts
+++ b/chrome/test/data/webui/settings/test_glic_browser_proxy.ts
@@ -16,6 +16,7 @@
   private glicFocusToggleShortcutResponse_: string = '';
   private glicDisallowedByAdmin_: boolean = false;
   private webActuationToggleVisibilityResponse_: boolean = false;
+  private webActuationEnabledResponse_: boolean = false;
   private actorLoginPermissions_: LoginPermission[] = [];
 
   constructor() {
@@ -32,6 +33,8 @@
       'getWebActuationToggleVisibility',
       'getGlicSelectionShortcut',
       'setGlicSelectionShortcut',
+      'getWebActuationEnabled',
+      'setWebActuationEnabled',
     ]);
   }
 
@@ -109,6 +112,19 @@
   setWebActuationToggleVisibilityResponse(visible: boolean) {
     this.webActuationToggleVisibilityResponse_ = visible;
   }
+
+  getWebActuationEnabled() {
+    this.methodCalled('getWebActuationEnabled');
+    return Promise.resolve(this.webActuationEnabledResponse_);
+  }
+
+  setWebActuationEnabled(enabled: boolean) {
+    this.methodCalled('setWebActuationEnabled', enabled);
+  }
+
+  setWebActuationEnabledResponse(enabled: boolean) {
+    this.webActuationEnabledResponse_ = enabled;
+  }
   getActorLoginPermissions() {
     this.methodCalled('getActorLoginPermissions');
     return Promise.resolve(this.actorLoginPermissions_);
diff --git a/chrome/test/webapps/file_reading.py b/chrome/test/webapps/file_reading.py
index de07a95..078f68b 100755
--- a/chrome/test/webapps/file_reading.py
+++ b/chrome/test/webapps/file_reading.py
@@ -8,23 +8,20 @@
 from collections import defaultdict
 import logging
 import os
-from posixpath import split
 import re
-from typing import Dict, List, Set, Tuple, Optional
+from typing import Dict, List, Optional, Set, Tuple
 
-from numpy import append
-
-from models import Action, TestId
-from models import ArgEnum
+from models import Action
 from models import ActionType
 from models import ActionsByName
+from models import ArgEnum
 from models import CoverageTest
 from models import CoverageTestsByPlatformSet
 from models import EnumsByType
 from models import PartialAndFullCoverageByBaseName
+from models import TestIdTestNameTuple
 from models import TestIdsTestNamesByPlatform
 from models import TestIdsTestNamesByPlatformSet
-from models import TestIdTestNameTuple
 from models import TestPartitionDescription
 from models import TestPlatform
 
@@ -494,7 +491,7 @@
 
 def get_and_maybe_delete_tests_in_browsertest(
     filename: str,
-    required_tests: Set[TestIdTestNameTuple] = {},
+    required_tests: Optional[Set[TestIdTestNameTuple]] = None,
     delete_in_place: bool = False
 ) -> Dict[TestIdTestNameTuple, Set[TestPlatform]]:
     """
@@ -528,7 +525,10 @@
     `TestPlatform.WINDOWS` and thus enabled on {`TestPlatform.MAC`,
     `TestPlatform.CHROME_OS`, and `TestPlatform.LINUX`}.
     """
+    if required_tests is None:
+        required_tests = set()
     tests: Dict[TestIdTestNameTuple, Set[TestPlatform]] = {}
+    required_tests_ids = {test_id for test_id, _ in required_tests}
 
     with open(filename, 'r') as fp:
         file = fp.read()
@@ -551,9 +551,6 @@
             test_name = match.group(1)
             tests[TestIdTestNameTuple(test_id, test_name)] = set(TestPlatform)
             browser_test_name = f"{CoverageTest.TEST_ID_PREFIX}{test_name}"
-            required_tests_ids = []
-            for t in required_tests:
-                required_tests_ids.append(t[0])
             if f"DISABLED_{browser_test_name}" not in file:
                 if delete_in_place and test_id not in required_tests_ids:
                     del tests[TestIdTestNameTuple(test_id, test_name)]
@@ -579,7 +576,7 @@
                 enabled_platforms.clear()
     if delete_in_place:
         with open(filename, 'w') as fp:
-            fp.write(result_file)
+            fp.write(result_file.rstrip("\n") + "\n")
     return tests
 
 
@@ -592,8 +589,8 @@
     Returns a dictionary of platform set to test id, and a dictionary of
     platform to disabled test ids.
     """
-    existing_tests: TestIdsNamesByPlatformSet = defaultdict(lambda: set())
-    disabled_tests: TestIdsNamesByPlatform = defaultdict(lambda: set())
+    existing_tests: TestIdsTestNamesByPlatformSet = defaultdict(lambda: set())
+    disabled_tests: TestIdsTestNamesByPlatform = defaultdict(lambda: set())
     for partition in test_partitions:
         for file in os.listdir(partition.browsertest_dir):
             if not file.startswith(partition.test_file_prefix):
@@ -606,7 +603,7 @@
                 for i in required_coverage_by_platform_set.get(platforms, []))
             tests = get_and_maybe_delete_tests_in_browsertest(
                 filename, required_tests, delete_in_place)
-            for test_id, test_name in tests.keys():
+            for test_id, test_name in tests:
                 if test_id in existing_tests[platforms]:
                     raise ValueError(f"Already found test {test_name}. "
                                      f"Duplicate test in {filename}")
@@ -617,7 +614,7 @@
                     if platform not in enabled_platforms:
                         disabled_tests[platform].add(
                             TestIdTestNameTuple(test_id, test_name))
-            test_names = [test_name for (test_id, test_name) in tests.keys()]
+            test_names = [test_name for (_, test_name) in tests]
             logging.info(f"Found tests in {filename}:\n{test_names}")
     return (existing_tests, disabled_tests)
 
diff --git a/chrome/test/webapps/file_reading_unittest.py b/chrome/test/webapps/file_reading_unittest.py
index 3d74987..210ff02 100755
--- a/chrome/test/webapps/file_reading_unittest.py
+++ b/chrome/test/webapps/file_reading_unittest.py
@@ -3,24 +3,24 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import os
 import csv
-import tempfile
-from typing import List
+import os
 import shutil
+import tempfile
+from typing import Dict, List
 import unittest
 
 from file_reading import enumerate_all_argument_combinations
-from file_reading import expand_tests_from_action_parameter_wildcards
 from file_reading import enumerate_markdown_file_lines_to_table_rows
-from file_reading import human_friendly_name_to_canonical_action_name
+from file_reading import expand_tests_from_action_parameter_wildcards
 from file_reading import generate_test_id_from_test_steps
 from file_reading import get_and_maybe_delete_tests_in_browsertest
+from file_reading import human_friendly_name_to_canonical_action_name
 from file_reading import read_actions_file
 from file_reading import read_enums_file
 from file_reading import read_platform_supported_actions
-from file_reading import resolve_bash_style_replacement
 from file_reading import read_unprocessed_coverage_tests_file
+from file_reading import resolve_bash_style_replacement
 from models import ActionsByName
 from models import ArgEnum
 from models import CoverageTest
@@ -31,7 +31,7 @@
                              "test_data")
 
 
-class TestAnalysisTest(unittest.TestCase):
+class FileReadingTest(unittest.TestCase):
     def test_markdown_file_mapping(self):
         test_input = [
             "Test", "# Hello", "| #test |", "| ------- | Value |",
@@ -42,9 +42,10 @@
         self.assertEqual(expected, output)
 
     def test_argument_combinations(self):
-        argument_types: List[ArgEnum] = []
-        argument_types.append(ArgEnum("T1", ["T1V1", "T1V2"], None))
-        argument_types.append(ArgEnum("T2", ["T2V1", "T2V2"], None))
+        argument_types: List[ArgEnum] = [
+            ArgEnum("T1", ["T1V1", "T1V2"], None),
+            ArgEnum("T2", ["T2V1", "T2V2"], None),
+        ]
 
         combinations = enumerate_all_argument_combinations(argument_types)
 
@@ -127,7 +128,7 @@
         with open(actions_filename, "r", encoding="utf-8") as f, \
                 open(supported_actions_filename, "r", encoding="utf-8") \
                     as supported_actions, \
-                open (enums_filename, "r", encoding="utf-8") as enums:
+                open(enums_filename, "r", encoding="utf-8") as enums:
             supported_actions = read_platform_supported_actions(
                 csv.reader(supported_actions, delimiter=','))
             actions_tsv = f.readlines()
@@ -146,7 +147,7 @@
             # Check parameterized action state.
             self.assertIn('changes_Chicken', actions)
             self.assertIn('changes_Dog', actions)
-            self.assertTrue('checks' in actions)
+            self.assertIn('checks', actions)
             checks_output_actions = actions['checks'].output_actions
             self.assertEqual(len(checks_output_actions), 2)
             self.assertCountEqual(
@@ -161,7 +162,7 @@
 
         actions: ActionsByName = {}
         action_base_name_to_default_param = {}
-        with open(actions_filename) as f, \
+        with open(actions_filename, "r", encoding="utf-8") as f, \
                 open(supported_actions_filename, "r", encoding="utf-8") \
                     as supported_actions, \
                 open(enums_filename, "r", encoding="utf-8") as enums:
@@ -175,7 +176,7 @@
         coverage_filename = os.path.join(TEST_DATA_DIR,
                                          "test_unprocessed_coverage.md")
         coverage_tests: List[CoverageTest] = []
-        with open(coverage_filename) as f:
+        with open(coverage_filename, "r", encoding="utf-8") as f:
             coverage_tsv = f.readlines()
             coverage_tests = read_unprocessed_coverage_tests_file(
                 coverage_tsv, actions, enums,
@@ -197,7 +198,8 @@
 
     def test_browertest_in_place_deletion(self):
         input_file = os.path.join(TEST_DATA_DIR, "tests_for_deletion.cc")
-        after_deletion_file = os.path.join(TEST_DATA_DIR, "tests_default.cc")
+        after_deletion_file = os.path.join(TEST_DATA_DIR,
+                                           "tests_after_in_place_deletion.cc")
         with tempfile.TemporaryDirectory(dir=TEST_DATA_DIR) as tmpdirname:
             output_file = os.path.join(tmpdirname, "output.cc")
             shutil.copyfile(input_file, output_file)
@@ -209,9 +211,9 @@
                 },
                 delete_in_place=True)
 
-            with open(output_file, 'r') as f, open(after_deletion_file,
-                                                   'r') as f2:
-                self.assertTrue(f.read(), f2.read())
+            with open(output_file, 'r', encoding="utf-8") as f, \
+                    open(after_deletion_file, 'r', encoding="utf-8") as f2:
+                self.assertEqual(f.read(), f2.read())
 
             tests_and_platforms = tests_and_platforms[TestIdTestNameTuple(
                 "state_change_a_Chicken_check_a_Chicken_check_b_Chicken_Green",
diff --git a/chrome/test/webapps/test_analysis.py b/chrome/test/webapps/test_analysis.py
index 06e3672..a5a519a 100755
--- a/chrome/test/webapps/test_analysis.py
+++ b/chrome/test/webapps/test_analysis.py
@@ -6,20 +6,18 @@
 """
 
 from collections import defaultdict
-import datetime
 import logging
-import re
 import os
-from typing import List, Set
+import re
+from typing import Dict, List, Set
 
 from models import Action
+from models import ActionType
 from models import CoverageTest
 from models import CoverageTestsByPlatform
-from models import ActionType
 from models import CoverageTestsByPlatformSet
 from models import TestId
 from models import TestIdsTestNamesByPlatformSet
-from models import TestIdTestNameTuple
 from models import TestPartitionDescription
 from models import TestPlatform
 
@@ -27,15 +25,12 @@
 def filter_tests_for_partition(tests: List[CoverageTest],
                                partition: TestPartitionDescription
                                ) -> List[CoverageTest]:
-    def DoesTestHaveActionWithPrefixes(test: CoverageTest):
-        """Returns if the given tests has any actions with the given prefixes"""
-        nonlocal partition
-        for action in test.actions:
-            for prefix in partition.action_name_prefixes:
-                if action.name.startswith(prefix):
-                    return True
-
-    return list(filter(DoesTestHaveActionWithPrefixes, tests))
+    """Returns tests whose actions match any prefix assigned to `partition`."""
+    return [
+        test for test in tests if any(
+            action.name.startswith(prefix) for action in test.actions
+            for prefix in partition.action_name_prefixes)
+    ]
 
 
 def compare_and_print_tests_to_remove_and_add(
@@ -51,15 +46,14 @@
     Note: This does NOT support moving tests between partition files. If a test
     was found in any partition file, then it is ignored.
     """
-    def print_tests(filename: str, tests: List[CoverageTest],
-                    partition: TestPartitionDescription, add_to_file: bool):
-        new_test_str: str = ""
-        for test in tests:
-            new_test_str += ("\n" + test.generate_browsertest(partition) +
-                             "\n")
+
+    def write_tests(filename: str, tests: List[CoverageTest],
+                    partition: TestPartitionDescription):
+        new_test_str = "".join("\n" + test.generate_browsertest(partition) +
+                               "\n" for test in tests)
         if add_to_file:
             if os.path.exists(filename):
-                with open(filename, "r") as f:
+                with open(filename, "r", encoding="utf-8") as f:
                     test_file = f.read()
                 # Find the last test in the test file
                 matches = list(
@@ -77,10 +71,9 @@
                             '\n', 0, last_brace_index) + 1
                     else:
                         last_test_end_index = len(test_file)
-                new_content = (test_file[:last_test_end_index] + new_test_str +
-                               test_file[last_test_end_index:])
-                with open(filename, "w") as f:
-                    f.write(new_content)
+                with open(filename, "w", encoding="utf-8") as f:
+                    f.write(test_file[:last_test_end_index] + new_test_str +
+                            test_file[last_test_end_index:])
             else:
                 print(f"\n\nCreate a new test file: {filename}\n"
                       "Remember to add the new test file to the BUILD file.\n"
@@ -90,13 +83,16 @@
             print(f"\n\nAdd the following tests to {filename}:\n"
                   f"{new_test_str}")
 
-    test_ids_to_keep: TestIdsByPlatformSet = defaultdict(lambda: set())
+    test_ids_to_keep: Dict[frozenset[TestPlatform],
+                           Set[TestId]] = defaultdict(set)
     for platforms, tests in required_tests.items():
         tests_to_add: List[CoverageTest] = []
         for test in tests:
             if platforms in existing_tests:
-                existing_test_set = set(
-                    [test_id for (test_id, _) in existing_tests[platforms]])
+                existing_test_set = {
+                    test_id
+                    for (test_id, _) in existing_tests[platforms]
+                }
                 if test.id not in existing_test_set:
                     tests_to_add.append(test)
                 else:
@@ -117,8 +113,7 @@
                         "Cannot have a test written to multiple test files.")
                 tests_added_to_partition.add(test.id)
             filename = partition.generate_browsertest_filepath(platforms)
-            print_tests(filename, tests_to_add_partition, partition,
-                        add_to_file)
+            write_tests(filename, tests_to_add_partition, partition)
 
         # All remaining tests go into the default partition
         default_tests: List[CoverageTest] = [
@@ -128,7 +123,7 @@
         if not default_tests:
             continue
         filename = default_partition.generate_browsertest_filepath(platforms)
-        print_tests(filename, default_tests, default_partition, add_to_file)
+        write_tests(filename, default_tests, default_partition)
     # Print out all tests to remove. To keep the algorithm simple the partition
     # is not kept track of.
     for platforms, test_ids_names in existing_tests.items():
@@ -212,10 +207,7 @@
 def filter_coverage_tests_for_platform(tests: List[CoverageTest],
                                        platform: TestPlatform
                                        ) -> List[CoverageTest]:
-    def IsSupportedOnPlatform(test: CoverageTest):
-        return platform in test.platforms
-
-    return list(filter(IsSupportedOnPlatform, tests))
+    return [test for test in tests if platform in test.platforms]
 
 
 def partition_framework_tests_per_platform_combination(
diff --git a/chrome/test/webapps/test_analysis_unittest.py b/chrome/test/webapps/test_analysis_unittest.py
index dc81280..bb99df4 100755
--- a/chrome/test/webapps/test_analysis_unittest.py
+++ b/chrome/test/webapps/test_analysis_unittest.py
@@ -4,13 +4,13 @@
 # found in the LICENSE file.
 
 from collections import defaultdict
+from contextlib import redirect_stdout
 import csv
 from io import StringIO
 import os
+import shutil
 import tempfile
 from typing import List, Set
-import shutil
-import sys
 import unittest
 
 from file_reading import get_and_maybe_delete_tests_in_browsertest
@@ -18,14 +18,14 @@
 from file_reading import read_platform_supported_actions
 from file_reading import read_unprocessed_coverage_tests_file
 from models import Action
-from models import EnumsByType
 from models import ActionsByName
 from models import ActionType
 from models import CoverageTest
 from models import CoverageTestsByPlatform
 from models import CoverageTestsByPlatformSet
-from models import TestIdsTestNamesByPlatformSet
+from models import EnumsByType
 from models import TestIdTestNameTuple
+from models import TestIdsTestNamesByPlatformSet
 from models import TestPartitionDescription
 from models import TestPlatform
 from test_analysis import compare_and_print_tests_to_remove_and_add
@@ -36,28 +36,29 @@
                              "test_data")
 
 
-def CreateDummyAction(id: str):
+def create_dummy_action(id: str) -> Action:
     return Action(id, id, id, id, ActionType.STATE_CHANGE, TestPlatform,
                   TestPlatform)
 
 
-def CreateCoverageTest(id: str, platforms: Set[TestPlatform]):
-    return CoverageTest([CreateDummyAction(id)], platforms)
+def create_coverage_test(id: str,
+                         platforms: Set[TestPlatform]) -> CoverageTest:
+    return CoverageTest([create_dummy_action(id)], platforms)
 
 
-def CreateNewDummyTestByPlatformSet(
+def create_new_dummy_test_by_platform_set(
         platforms: Set[TestPlatform]) -> CoverageTestsByPlatformSet:
     new_test_by_platform: CoverageTestsByPlatform = {}
     for platform in platforms:
         # Add the simple dummy test of an action "a" to all platforms.
-        new_test_by_platform[platform] = ([
-            CreateCoverageTest("a", {platform})
-        ])
+        new_test_by_platform[platform] = [
+            create_coverage_test("a", {platform})
+        ]
     return partition_framework_tests_per_platform_combination(
         new_test_by_platform)
 
 
-def GetExistingTestIdsTestNamesByPlatformSet(
+def get_existing_test_ids_test_names_by_platform_set(
         filename: str, required_tests: Set[TestIdTestNameTuple],
         delete_in_place: bool) -> TestIdsTestNamesByPlatformSet:
     # Read in existing tests from a file.
@@ -75,31 +76,32 @@
 
 class TestAnalysisTest(unittest.TestCase):
     def test_partition_framework_tests_per_platform_combination(self):
-        tests_by_platform: CoverageTestsByPlatform = {}
-        windows_tests = []
-        windows_tests.append(CreateCoverageTest("a", {TestPlatform.WINDOWS}))
-        windows_tests.append(CreateCoverageTest("b", {TestPlatform.WINDOWS}))
-        windows_tests.append(CreateCoverageTest("c", {TestPlatform.WINDOWS}))
-        tests_by_platform[TestPlatform.WINDOWS] = windows_tests
-        mac_tests = []
-        mac_tests.append(CreateCoverageTest("a", {TestPlatform.MAC}))
-        mac_tests.append(CreateCoverageTest("c", {TestPlatform.MAC}))
-        tests_by_platform[TestPlatform.MAC] = mac_tests
-        linux_tests = []
-        linux_tests.append(CreateCoverageTest("a", {TestPlatform.LINUX}))
-        tests_by_platform[TestPlatform.LINUX] = linux_tests
+        tests_by_platform: CoverageTestsByPlatform = {
+            TestPlatform.WINDOWS: [
+                create_coverage_test("a", {TestPlatform.WINDOWS}),
+                create_coverage_test("b", {TestPlatform.WINDOWS}),
+                create_coverage_test("c", {TestPlatform.WINDOWS}),
+            ],
+            TestPlatform.MAC: [
+                create_coverage_test("a", {TestPlatform.MAC}),
+                create_coverage_test("c", {TestPlatform.MAC}),
+            ],
+            TestPlatform.LINUX: [
+                create_coverage_test("a", {TestPlatform.LINUX}),
+            ],
+        }
 
         partitions = partition_framework_tests_per_platform_combination(
             tests_by_platform)
         self.assertEqual(len(partitions), 3)
 
-        self.assertTrue(frozenset({TestPlatform.WINDOWS}) in partitions)
+        self.assertIn(frozenset({TestPlatform.WINDOWS}), partitions)
         windows_tests = partitions[frozenset({TestPlatform.WINDOWS})]
         self.assertEqual(len(windows_tests), 1)
         self.assertEqual(windows_tests[0].id, "b")
 
-        self.assertTrue(
-            frozenset({TestPlatform.MAC, TestPlatform.WINDOWS}) in partitions)
+        self.assertIn(frozenset({TestPlatform.MAC, TestPlatform.WINDOWS}),
+                      partitions)
         mac_win_tests = partitions[frozenset(
             {TestPlatform.MAC, TestPlatform.WINDOWS})]
         self.assertEqual(len(mac_win_tests), 1)
@@ -107,7 +109,7 @@
 
         mac_win_linux_key = frozenset(
             {TestPlatform.MAC, TestPlatform.WINDOWS, TestPlatform.LINUX})
-        self.assertTrue(mac_win_linux_key in partitions)
+        self.assertIn(mac_win_linux_key, partitions)
         mac_win_linux_tests = partitions[mac_win_linux_key]
         self.assertEqual(len(mac_win_linux_tests), 1)
         self.assertEqual(mac_win_linux_tests[0].id, "a")
@@ -173,9 +175,9 @@
                 TestPlatform.CHROME_OS,
             }
             new_test_required_by_platform_set: CoverageTestsByPlatformSet = (
-                CreateNewDummyTestByPlatformSet(test_platforms))
+                create_new_dummy_test_by_platform_set(test_platforms))
             existing_tests: TestIdsTestNamesByPlatformSet = (
-                GetExistingTestIdsTestNamesByPlatformSet(
+                get_existing_test_ids_test_names_by_platform_set(
                     filename=test_file,
                     required_tests={
                         TestIdTestNameTuple(
@@ -196,7 +198,10 @@
                 add_to_file=True)
             expected_file = os.path.join(TEST_DATA_DIR, "expected_test_txt",
                                          "tests_change_for_adding_test.cc")
-            with open(expected_file, "r") as f, open(test_file, "r") as f2:
+            with open(expected_file, "r",
+                      encoding="utf-8") as f, open(test_file,
+                                                   "r",
+                                                   encoding="utf-8") as f2:
                 self.assertEqual(f.read(), f2.read())
 
     def test_compare_and_print_tests_with_same_name_diff_check_actions_only(
@@ -208,7 +213,7 @@
 
         actions: ActionsByName = {}
         action_base_name_to_default_param = {}
-        with open(actions_filename) as f, \
+        with open(actions_filename, "r", encoding="utf-8") as f, \
                 open(supported_actions_filename, "r", encoding="utf-8") \
                     as supported_actions, \
                 open(enums_filename, "r", encoding="utf-8") as enums:
@@ -222,7 +227,7 @@
         coverage_filename = os.path.join(TEST_DATA_DIR,
                                          "test_addition_coverage.md")
         generated_coverage_tests: List[CoverageTest] = []
-        with open(coverage_filename) as f:
+        with open(coverage_filename, "r", encoding="utf-8") as f:
             coverage_tsv = f.readlines()
             generated_coverage_tests = read_unprocessed_coverage_tests_file(
                 coverage_tsv, actions, enums,
@@ -250,7 +255,7 @@
                 "tests_change_for_replacing_test_same_test_name.cc")
             shutil.copyfile(original_file, test_file)
             existing_tests: TestIdsTestNamesByPlatformSet = (
-                GetExistingTestIdsTestNamesByPlatformSet(
+                get_existing_test_ids_test_names_by_platform_set(
                     filename=test_file,
                     required_tests={
                         TestIdTestNameTuple(
@@ -275,7 +280,10 @@
             expected_file = os.path.join(
                 TEST_DATA_DIR, "expected_test_txt",
                 "tests_change_for_replacing_test_same_test_name.cc")
-            with open(expected_file, "r") as f, open(test_file, "r") as f2:
+            with open(expected_file, "r",
+                      encoding="utf-8") as f, open(test_file,
+                                                   "r",
+                                                   encoding="utf-8") as f2:
                 self.assertEqual(f.read(), f2.read())
 
     def test_compare_and_print_tests_to_remove_and_add_delete_and_add_to_file(
@@ -294,11 +302,12 @@
                 TestPlatform.CHROME_OS,
             }
             new_test_required_by_platform_set: CoverageTestsByPlatformSet = (
-                CreateNewDummyTestByPlatformSet(test_platforms))
-            existing_tests: TestIdsByPlatformSet = (
-                GetExistingTestIdsTestNamesByPlatformSet(filename=test_file,
-                                                         required_tests={},
-                                                         delete_in_place=True))
+                create_new_dummy_test_by_platform_set(test_platforms))
+            existing_tests: TestIdsTestNamesByPlatformSet = (
+                get_existing_test_ids_test_names_by_platform_set(
+                    filename=test_file,
+                    required_tests={},
+                    delete_in_place=True))
 
             default_partition = TestPartitionDescription(
                 action_name_prefixes=set(),
@@ -315,7 +324,10 @@
             expected_file = os.path.join(
                 TEST_DATA_DIR, "expected_test_txt",
                 "tests_change_for_deleting_adding_test.cc")
-            with open(expected_file, "r") as f, open(test_file, "r") as f2:
+            with open(expected_file, "r",
+                      encoding="utf-8") as f, open(test_file,
+                                                   "r",
+                                                   encoding="utf-8") as f2:
                 self.assertEqual(f.read(), f2.read())
 
     def test_compare_and_print_tests_to_remove_and_add_add_to_new_file(self):
@@ -330,9 +342,10 @@
                 TestPlatform.WINDOWS, TestPlatform.MAC
             }
             new_test_required_by_platform_set: CoverageTestsByPlatformSet = (
-                CreateNewDummyTestByPlatformSet(test_platforms))
-            existing_tests: TestIdsByPlatformSet = (
-                GetExistingTestIdsTestNamesByPlatformSet(test_file, {}, True))
+                create_new_dummy_test_by_platform_set(test_platforms))
+            existing_tests: TestIdsTestNamesByPlatformSet = (
+                get_existing_test_ids_test_names_by_platform_set(
+                    test_file, {}, True))
 
             default_partition = TestPartitionDescription(
                 action_name_prefixes=set(),
@@ -341,22 +354,21 @@
                 test_fixture="WebAppIntegration")
 
             captured_output = StringIO()
-            sys.stdout = captured_output
-            compare_and_print_tests_to_remove_and_add(
-                existing_tests,
-                new_test_required_by_platform_set,
-                test_partitions=[],
-                default_partition=default_partition,
-                add_to_file=True)
+            with redirect_stdout(captured_output):
+                compare_and_print_tests_to_remove_and_add(
+                    existing_tests,
+                    new_test_required_by_platform_set,
+                    test_partitions=[],
+                    default_partition=default_partition,
+                    add_to_file=True)
             console_output_str = captured_output.getvalue()
-            sys.stdout = sys.__stdout__
 
             expected_file = os.path.join(
                 TEST_DATA_DIR, "expected_test_txt",
                 "tests_change_for_deletion_addition_mac_win.txt")
             test_output_file = os.path.join(
                     tmpdirname, "tests_for_deletion_addition_mac_win.cc")
-            with open(expected_file, "r") as f:
+            with open(expected_file, "r", encoding="utf-8") as f:
                 self.assertEqual(f.read() % test_output_file,
                                  console_output_str)
 
diff --git a/chrome/test/webapps/test_data/expected_test_txt/tests_change_for_deleting_adding_test.cc b/chrome/test/webapps/test_data/expected_test_txt/tests_change_for_deleting_adding_test.cc
index ddb7c51c..6e3b41c6 100644
--- a/chrome/test/webapps/test_data/expected_test_txt/tests_change_for_deleting_adding_test.cc
+++ b/chrome/test/webapps/test_data/expected_test_txt/tests_change_for_deleting_adding_test.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-
-
 IN_PROC_BROWSER_TEST_F(TestName, WAI_A) {
   // Test contents are generated by script. Please do not modify!
   // See `docs/webapps/why-is-this-test-failing.md` or
diff --git a/chrome/test/webapps/test_data/expected_test_txt/tests_change_for_replacing_test_same_test_name.cc b/chrome/test/webapps/test_data/expected_test_txt/tests_change_for_replacing_test_same_test_name.cc
index 5b77697..710486bf 100644
--- a/chrome/test/webapps/test_data/expected_test_txt/tests_change_for_replacing_test_same_test_name.cc
+++ b/chrome/test/webapps/test_data/expected_test_txt/tests_change_for_replacing_test_same_test_name.cc
@@ -23,5 +23,3 @@
   helper_.CheckA(Animal::kDog);
   helper_.StateChangeA(Animal::kChicken);
 }
-
-
diff --git a/chrome/test/webapps/test_data/tests_after_in_place_deletion.cc b/chrome/test/webapps/test_data/tests_after_in_place_deletion.cc
new file mode 100644
index 0000000..f82c8e3
--- /dev/null
+++ b/chrome/test/webapps/test_data/tests_after_in_place_deletion.cc
@@ -0,0 +1,20 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_WAI_3Chicken_1Chicken_2ChickenGreen \
+  DISABLED_WAI_3Chicken_1Chicken_2ChickenGreen
+#else
+#define MAYBE_WAI_3Chicken_1Chicken_2ChickenGreen \
+  WAI_3Chicken_1Chicken_2ChickenGreen
+#endif
+IN_PROC_BROWSER_TEST_F(TestName, MAYBE_WAI_3Chicken_1Chicken_2ChickenGreen) {
+  // Test contents are generated by script. Please do not modify!
+  // See `docs/webapps/why-is-this-test-failing.md` or
+  // `docs/webapps/integration-testing-framework` for more info.
+  // Gardeners: Disabling this test is supported.
+  helper_.StateChangeA(Animal::kChicken);
+  helper_.CheckA(Animal::kChicken);
+  helper_.CheckB(Animal::kChicken, Color::kGreen);
+}
diff --git a/chrome/updater/app/app_install_win.cc b/chrome/updater/app/app_install_win.cc
index 72ca744..a7c241e 100644
--- a/chrome/updater/app/app_install_win.cc
+++ b/chrome/updater/app/app_install_win.cc
@@ -47,6 +47,7 @@
 #include "base/win/elevation_util.h"
 #include "base/win/registry.h"
 #include "base/win/scoped_com_initializer.h"
+#include "base/win/win_util.h"
 #include "chrome/updater/app/app_install_progress.h"
 #include "chrome/updater/app/app_install_util_win.h"
 #include "chrome/updater/app/app_install_win_internal.h"
@@ -1098,6 +1099,10 @@
 }
 
 scoped_refptr<App> MakeAppInstall(bool is_silent_install) {
+  if (!is_silent_install) {
+    base::win::EnableHighDPISupport();
+  }
+
   if (IsSystemInstall()) {
     if (base::CommandLine::ForCurrentProcess()->HasSwitch(kOemSwitch)) {
       const bool success = SetOemInstallState();
diff --git a/chrome/updater/app/server/win/com_classes_legacy.cc b/chrome/updater/app/server/win/com_classes_legacy.cc
index 608c6097..42ecc0a 100644
--- a/chrome/updater/app/server/win/com_classes_legacy.cc
+++ b/chrome/updater/app/server/win/com_classes_legacy.cc
@@ -65,6 +65,8 @@
 #include "components/update_client/protocol_definition.h"
 #include "components/update_client/update_client.h"
 
+namespace updater {
+
 namespace {
 
 HRESULT OpenCallerProcessHandle(DWORD proc_id,
@@ -87,9 +89,42 @@
   return {};
 }
 
-}  // namespace
+// Holds the result of the IPC to retrieve PolicyService data.
+template <typename T>
+class PolicyStatusResult
+    : public base::RefCountedThreadSafe<PolicyStatusResult<T>> {
+ public:
+  using ValueGetter = base::RepeatingCallback<PolicyStatus<T>()>;
 
-namespace updater {
+  static auto Get(ValueGetter value_getter) {
+    auto result = base::WrapRefCounted(new PolicyStatusResult<T>(value_getter));
+    AppServerWin::PostRpcTask(
+        base::BindOnce(&PolicyStatusResult::GetValueOnSequence, result));
+    result->completion_event.TimedWait(base::Seconds(60));
+    return result->value;
+  }
+
+ private:
+  friend base::RefCountedThreadSafe<PolicyStatusResult<T>>;
+  virtual ~PolicyStatusResult() = default;
+
+  explicit PolicyStatusResult(ValueGetter value_getter)
+      : value_getter(value_getter) {}
+
+  void GetValueOnSequence() {
+    PolicyStatus<T> policy_status = value_getter.Run();
+    if (policy_status) {
+      value = policy_status;
+    }
+    completion_event.Signal();
+  }
+
+  ValueGetter value_getter;
+  std::optional<PolicyStatus<T>> value;
+  base::WaitableEvent completion_event;
+};
+
+}  // namespace
 
 // Implements `IAppVersionWeb`.
 class AppVersionWebImpl : public IDispatchImpl<IAppVersionWeb> {
@@ -1398,12 +1433,13 @@
     return E_INVALIDARG;
   }
 
-  PolicyStatus<base::TimeDelta> period = policy_service_->GetLastCheckPeriod();
+  auto period = PolicyStatusResult<base::TimeDelta>::Get(
+      base::BindRepeating(&PolicyService::GetLastCheckPeriod, policy_service_));
   if (!period) {
     return E_FAIL;
   }
 
-  *minutes = period.policy().InMinutes();
+  *minutes = period->policy().InMinutes();
   return S_OK;
 }
 
@@ -1420,17 +1456,22 @@
     return E_INVALIDARG;
   }
 
-  PolicyStatus<UpdatesSuppressedTimes> updates_suppressed_times =
-      policy_service_->GetUpdatesSuppressedTimes();
-  if (!updates_suppressed_times || !updates_suppressed_times.policy().valid()) {
+  auto updates_suppressed_times =
+      PolicyStatusResult<UpdatesSuppressedTimes>::Get(base::BindRepeating(
+          &PolicyService::GetUpdatesSuppressedTimes, policy_service_));
+  if (!updates_suppressed_times ||
+      !updates_suppressed_times->policy().valid()) {
     return E_FAIL;
   }
 
-  *start_hour = updates_suppressed_times.policy().start_hour_;
-  *start_min = updates_suppressed_times.policy().start_minute_;
-  *duration_min = updates_suppressed_times.policy().duration_minute_;
+  *start_hour = updates_suppressed_times->policy().start_hour_;
+  *start_min = updates_suppressed_times->policy().start_minute_;
+  *duration_min = updates_suppressed_times->policy().duration_minute_;
   *are_updates_suppressed =
-      policy_service_->AreUpdatesSuppressedNow() ? VARIANT_TRUE : VARIANT_FALSE;
+      AreUpdatesSuppressed(updates_suppressed_times->policy(),
+                           base::Time::Now())
+          ? VARIANT_TRUE
+          : VARIANT_FALSE;
 
   return S_OK;
 }
@@ -1440,13 +1481,14 @@
     return E_INVALIDARG;
   }
 
-  PolicyStatus<std::string> download_preference =
-      policy_service_->GetDownloadPreference();
+  auto download_preference =
+      PolicyStatusResult<std::string>::Get(base::BindRepeating(
+          &PolicyService::GetDownloadPreference, policy_service_));
   if (!download_preference) {
     return E_FAIL;
   }
 
-  *pref = base::win::ScopedBstr(base::UTF8ToWide(download_preference.policy()))
+  *pref = base::win::ScopedBstr(base::UTF8ToWide(download_preference->policy()))
               .Release();
   return S_OK;
 }
@@ -1456,13 +1498,13 @@
     return E_INVALIDARG;
   }
 
-  PolicyStatus<int> cache_size_limit =
-      policy_service_->GetPackageCacheSizeLimitMBytes();
+  auto cache_size_limit = PolicyStatusResult<int>::Get(base::BindRepeating(
+      &PolicyService::GetPackageCacheSizeLimitMBytes, policy_service_));
   if (!cache_size_limit) {
     return E_FAIL;
   }
 
-  *limit = cache_size_limit.policy();
+  *limit = cache_size_limit->policy();
   return S_OK;
 }
 
@@ -1471,13 +1513,13 @@
     return E_INVALIDARG;
   }
 
-  PolicyStatus<int> cache_life_limit =
-      policy_service_->GetPackageCacheExpirationTimeDays();
+  auto cache_life_limit = PolicyStatusResult<int>::Get(base::BindRepeating(
+      &PolicyService::GetPackageCacheExpirationTimeDays, policy_service_));
   if (!cache_life_limit) {
     return E_FAIL;
   }
 
-  *days = cache_life_limit.policy();
+  *days = cache_life_limit->policy();
   return S_OK;
 }
 
@@ -1488,13 +1530,14 @@
     return E_INVALIDARG;
   }
 
-  PolicyStatus<int> install_policy =
-      policy_service_->GetPolicyForAppInstalls(base::WideToUTF8(app_id));
+  auto install_policy = PolicyStatusResult<int>::Get(
+      base::BindRepeating(&PolicyService::GetPolicyForAppInstalls,
+                          policy_service_, base::WideToUTF8(app_id)));
   if (!install_policy) {
     return E_FAIL;
   }
 
-  *policy = install_policy.policy();
+  *policy = install_policy->policy();
   return S_OK;
 }
 
@@ -1504,13 +1547,14 @@
     return E_INVALIDARG;
   }
 
-  PolicyStatus<int> update_policy =
-      policy_service_->GetPolicyForAppUpdates(base::WideToUTF8(app_id));
+  auto update_policy = PolicyStatusResult<int>::Get(
+      base::BindRepeating(&PolicyService::GetPolicyForAppUpdates,
+                          policy_service_, base::WideToUTF8(app_id)));
   if (!update_policy) {
     return E_FAIL;
   }
 
-  *policy = update_policy.policy();
+  *policy = update_policy->policy();
   return S_OK;
 }
 
@@ -1520,14 +1564,15 @@
     return E_INVALIDARG;
   }
 
-  PolicyStatus<std::string> target_version_prefix =
-      policy_service_->GetTargetVersionPrefix(base::WideToUTF8(app_id));
+  auto target_version_prefix = PolicyStatusResult<std::string>::Get(
+      base::BindRepeating(&PolicyService::GetTargetVersionPrefix,
+                          policy_service_, base::WideToUTF8(app_id)));
   if (!target_version_prefix) {
     return E_FAIL;
   }
 
   *prefix =
-      base::win::ScopedBstr(base::UTF8ToWide(target_version_prefix.policy()))
+      base::win::ScopedBstr(base::UTF8ToWide(target_version_prefix->policy()))
           .Release();
   return S_OK;
 }
@@ -1539,15 +1584,15 @@
     return E_INVALIDARG;
   }
 
-  PolicyStatus<bool> is_rollback_allowed =
-      policy_service_->IsRollbackToTargetVersionAllowed(
-          base::WideToUTF8(app_id));
+  auto is_rollback_allowed = PolicyStatusResult<bool>::Get(
+      base::BindRepeating(&PolicyService::IsRollbackToTargetVersionAllowed,
+                          policy_service_, base::WideToUTF8(app_id)));
   if (!is_rollback_allowed) {
     return E_FAIL;
   }
 
   *rollback_allowed =
-      is_rollback_allowed.policy() ? VARIANT_TRUE : VARIANT_FALSE;
+      is_rollback_allowed->policy() ? VARIANT_TRUE : VARIANT_FALSE;
   return S_OK;
 }
 
@@ -1573,41 +1618,6 @@
   virtual ~LastCheckedTimeResult() = default;
 };
 
-// Holds the result of the IPC to retrieve PolicyService data.
-template <typename T>
-class PolicyStatusResult
-    : public base::RefCountedThreadSafe<PolicyStatusResult<T>> {
- public:
-  using ValueGetter = base::RepeatingCallback<PolicyStatus<T>()>;
-
-  static auto Get(ValueGetter value_getter) {
-    auto result = base::WrapRefCounted(new PolicyStatusResult<T>(value_getter));
-    AppServerWin::PostRpcTask(
-        base::BindOnce(&PolicyStatusResult::GetValueOnSequence, result));
-    result->completion_event.TimedWait(base::Seconds(60));
-    return result->value;
-  }
-
- private:
-  friend base::RefCountedThreadSafe<PolicyStatusResult<T>>;
-  virtual ~PolicyStatusResult() = default;
-
-  explicit PolicyStatusResult(ValueGetter value_getter)
-      : value_getter(value_getter) {}
-
-  void GetValueOnSequence() {
-    PolicyStatus<T> policy_status = value_getter.Run();
-    if (policy_status) {
-      value = policy_status;
-    }
-    completion_event.Signal();
-  }
-
-  ValueGetter value_getter;
-  std::optional<PolicyStatus<T>> value;
-  base::WaitableEvent completion_event;
-};
-
 }  // namespace
 
 STDMETHODIMP PolicyStatusImpl::get_lastCheckedTime(DATE* last_checked) {
@@ -1704,12 +1714,14 @@
     return E_FAIL;
   }
   const UpdatesSuppressedTimes updates_suppressed_times =
-      policy_status->effective_policy()->policy;
+      policy_status->policy();
   if (!updates_suppressed_times.valid()) {
     return E_FAIL;
   }
   *are_updates_suppressed =
-      policy_service_->AreUpdatesSuppressedNow() ? VARIANT_TRUE : VARIANT_FALSE;
+      AreUpdatesSuppressed(updates_suppressed_times, base::Time::Now())
+          ? VARIANT_TRUE
+          : VARIANT_FALSE;
   return PolicyStatusValueImpl::Create(*policy_status, value);
 }
 
diff --git a/chrome/updater/check_for_updates_task.cc b/chrome/updater/check_for_updates_task.cc
index 6b4eadd7..4c68dfb 100644
--- a/chrome/updater/check_for_updates_task.cc
+++ b/chrome/updater/check_for_updates_task.cc
@@ -55,7 +55,7 @@
   }
 
   // Skip if the updater is in the update suppression period.
-  return config->GetPolicyService()->AreUpdatesSuppressedNow();
+  return config->GetPolicyService()->AreUpdatesSuppressed(base::Time::Now());
 }
 
 }  // namespace
diff --git a/chrome/updater/policy/service.cc b/chrome/updater/policy/service.cc
index 298041e..58d23b23 100644
--- a/chrome/updater/policy/service.cc
+++ b/chrome/updater/policy/service.cc
@@ -429,25 +429,14 @@
                             base::JoinString(policies, "\n  ").c_str());
 }
 
-bool PolicyService::AreUpdatesSuppressedNow(base::Time now) const {
+bool PolicyService::AreUpdatesSuppressed(base::Time time) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   const PolicyStatus<UpdatesSuppressedTimes> suppression =
       GetUpdatesSuppressedTimes();
-  if (!suppression || !suppression.policy().valid()) {
-    return false;
-  }
-  base::Time::Exploded now_local;
-  now.LocalExplode(&now_local);
-  const bool are_updates_suppressed =
-      suppression.policy().contains(now_local.hour, now_local.minute);
-  VLOG(0) << __func__ << ": Updates are "
-          << (are_updates_suppressed ? "" : "not ") << "suppressed: now=" << now
-          << ": UpdatesSuppressedTimes: start_hour_:"
-          << suppression.policy().start_hour_
-          << ": start_minute_:" << suppression.policy().start_minute_
-          << ": duration_minute_:" << suppression.policy().duration_minute_;
-  return are_updates_suppressed;
+  return suppression
+             ? ::updater::AreUpdatesSuppressed(suppression.policy(), time)
+             : false;
 }
 
 template <typename T, typename U>
@@ -564,4 +553,23 @@
                          !dm_storage->IsDeviceDeregistered()));
 }
 
+bool AreUpdatesSuppressed(UpdatesSuppressedTimes updates_suppressed_times,
+                          base::Time time) {
+  if (!updates_suppressed_times.valid()) {
+    return false;
+  }
+  base::Time::Exploded time_local;
+  time.LocalExplode(&time_local);
+  const bool are_updates_suppressed =
+      updates_suppressed_times.contains(time_local.hour, time_local.minute);
+  VLOG(0) << __func__ << ": Updates are "
+          << (are_updates_suppressed ? "" : "not ")
+          << "suppressed: time=" << time
+          << ": UpdatesSuppressedTimes: start_hour_:"
+          << updates_suppressed_times.start_hour_
+          << ": start_minute_:" << updates_suppressed_times.start_minute_
+          << ": duration_minute_:" << updates_suppressed_times.duration_minute_;
+  return are_updates_suppressed;
+}
+
 }  // namespace updater
diff --git a/chrome/updater/policy/service.h b/chrome/updater/policy/service.h
index a4889d0..b5d157f 100644
--- a/chrome/updater/policy/service.h
+++ b/chrome/updater/policy/service.h
@@ -272,7 +272,7 @@
   }
 
   std::string GetAllPoliciesAsString() const;
-  bool AreUpdatesSuppressedNow(base::Time now = base::Time::Now()) const;
+  bool AreUpdatesSuppressed(base::Time time) const;
 
   void SetManagersForTesting(
       std::vector<scoped_refptr<PolicyManagerInterface>> managers);
@@ -360,6 +360,11 @@
 // blocking IO.
 bool IsCloudManaged();
 
+// Determines whether `updates_suppressed_times` disallows updates from
+// occurring at the specified time.
+bool AreUpdatesSuppressed(UpdatesSuppressedTimes updates_suppressed_times,
+                          base::Time time);
+
 }  // namespace updater
 
 #endif  // CHROME_UPDATER_POLICY_SERVICE_H_
diff --git a/chrome/updater/policy/service_unittest.cc b/chrome/updater/policy/service_unittest.cc
index 59a6ad1c..e3661c1 100644
--- a/chrome/updater/policy/service_unittest.cc
+++ b/chrome/updater/policy/service_unittest.cc
@@ -867,21 +867,21 @@
             "}\n");
 }
 
-struct PolicyServiceAreUpdatesSuppressedNowTestCase {
+struct PolicyServiceAreUpdatesSuppressedTestCase {
   const UpdatesSuppressedTimes updates_suppressed_times;
-  const std::string now_string;
+  const std::string time_string;
   const bool expect_updates_suppressed;
 };
 
-class PolicyServiceAreUpdatesSuppressedNowTest
+class PolicyServiceAreUpdatesSuppressedTest
     : public ::testing::WithParamInterface<
-          PolicyServiceAreUpdatesSuppressedNowTestCase>,
+          PolicyServiceAreUpdatesSuppressedTestCase>,
       public PolicyServiceTest {};
 
 INSTANTIATE_TEST_SUITE_P(
-    PolicyServiceAreUpdatesSuppressedNowTestCases,
-    PolicyServiceAreUpdatesSuppressedNowTest,
-    ValuesIn(std::vector<PolicyServiceAreUpdatesSuppressedNowTestCase>{
+    PolicyServiceAreUpdatesSuppressedTestCases,
+    PolicyServiceAreUpdatesSuppressedTest,
+    ValuesIn(std::vector<PolicyServiceAreUpdatesSuppressedTestCase>{
         // Suppress starting 12:00 for 959 minutes.
         {{12, 00, 959}, "Sat, 01 July 2023 01:15:00", true},
 
@@ -901,15 +901,15 @@
         {{18, 00, 12 * 60}, "Sat, 01 July 2023 06:15:00", false},
     }));
 
-TEST_P(PolicyServiceAreUpdatesSuppressedNowTest, TestCases) {
+TEST_P(PolicyServiceAreUpdatesSuppressedTest, TestCases) {
   auto manager = base::MakeRefCounted<FakePolicyManager>(true, "Group Policy");
   manager->SetUpdatesSuppressedTimes(GetParam().updates_suppressed_times);
 
-  base::Time now;
-  ASSERT_TRUE(base::Time::FromString(GetParam().now_string.c_str(), &now));
+  base::Time time;
+  ASSERT_TRUE(base::Time::FromString(GetParam().time_string.c_str(), &time));
   EXPECT_EQ(GetParam().expect_updates_suppressed,
             CreatePolicyServiceForTesting({std::move(manager)})
-                ->AreUpdatesSuppressedNow(now));
+                ->AreUpdatesSuppressed(time));
 }
 
 TEST_F(PolicyServiceTest, PolicyServiceProxyConfiguration_Get) {
diff --git a/chrome/updater/win/ui/owner_draw_controls.cc b/chrome/updater/win/ui/owner_draw_controls.cc
index 88085d9..70c860b7 100644
--- a/chrome/updater/win/ui/owner_draw_controls.cc
+++ b/chrome/updater/win/ui/owner_draw_controls.cc
@@ -335,6 +335,17 @@
   return 1;
 }
 
+LRESULT OwnerDrawTitleBarWindow::OnSize(UINT, WPARAM, LPARAM, BOOL& handled) {
+  // Recalculate button positions based on new height/width.
+  RecalcLayout();
+
+  // Force a redraw to clear artifacts.
+  Invalidate();
+
+  handled = false;
+  return 0;
+}
+
 LRESULT OwnerDrawTitleBarWindow::OnClose(WORD, WORD, HWND, BOOL& handled) {
   handled = false;
 
@@ -360,8 +371,12 @@
   close_button_.set_bk_color(bk_color_);
   minimize_button_.set_bk_color(bk_color_);
 
-  CRect button_rect(0, 0, ::GetSystemMetrics(SM_CXSIZE),
-                    ::GetSystemMetrics(SM_CYSIZE));
+  // Get the DPI for this specific window
+  const int dpi = GetDpiForWindow(m_hWnd);
+
+  // Use the DPI-aware version of system metrics.
+  CRect button_rect(0, 0, ::GetSystemMetricsForDpi(SM_CXSIZE, dpi),
+                    ::GetSystemMetricsForDpi(SM_CYSIZE, dpi));
 
   minimize_button_.Create(m_hWnd, button_rect, nullptr,
                           WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 0,
@@ -470,8 +485,22 @@
       WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
 }
 
-void OwnerDrawTitleBar::RecalcLayout() {
-  CHECK(title_bar_window_.IsWindow());
+void OwnerDrawTitleBar::RecalcLayout(HWND parent_hwnd,
+                                     HWND title_bar_spacer_hwnd) {
+  if (!title_bar_window_.IsWindow()) {
+    return;
+  }
+
+  // Re-compute where the title bar window should be based on the spacer. The
+  // spacer was already resized by the OS Dialog Manager.
+  CRect new_rect =
+      ComputeTitleBarClientRect(parent_hwnd, title_bar_spacer_hwnd);
+
+  // Resize the title bar window itself.
+  title_bar_window_.SetWindowPos(nullptr, &new_rect,
+                                 SWP_NOZORDER | SWP_NOACTIVATE);
+
+  // Now tell the window to reposition its internal buttons.
   title_bar_window_.RecalcLayout();
 }
 
diff --git a/chrome/updater/win/ui/owner_draw_controls.h b/chrome/updater/win/ui/owner_draw_controls.h
index b742387..6b94644 100644
--- a/chrome/updater/win/ui/owner_draw_controls.h
+++ b/chrome/updater/win/ui/owner_draw_controls.h
@@ -149,6 +149,7 @@
     MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
     MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
     MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
+    MESSAGE_HANDLER(WM_SIZE, OnSize)
     COMMAND_ID_HANDLER(kButtonClose, OnClose)
     COMMAND_ID_HANDLER(kButtonMaximize, OnMaximize)
     COMMAND_ID_HANDLER(kButtonMinimize, OnMinimize)
@@ -198,6 +199,7 @@
                        WPARAM wparam,
                        LPARAM lparam,
                        BOOL& handled);  // NOLINT
+  LRESULT OnSize(UINT, WPARAM, LPARAM, BOOL& handled);  // NOLINT
   LRESULT OnClose(WORD notify_code,
                   WORD id,
                   HWND hwnd_ctrl,
@@ -229,7 +231,7 @@
                                HWND title_bar_spacer_hwnd,
                                COLORREF bk_color);
 
-  void RecalcLayout();
+  void RecalcLayout(HWND parent_hwnd, HWND title_bar_spacer_hwnd);
 
   BEGIN_MSG_MAP(OwnerDrawTitleBar)
   END_MSG_MAP()
diff --git a/chrome/updater/win/ui/progress_wnd.cc b/chrome/updater/win/ui/progress_wnd.cc
index bb1fb83..832abba5 100644
--- a/chrome/updater/win/ui/progress_wnd.cc
+++ b/chrome/updater/win/ui/progress_wnd.cc
@@ -149,6 +149,17 @@
                                   WPARAM wparam,
                                   LPARAM lparam,
                                   BOOL& handled) {
+  const HDC hdc = reinterpret_cast<HDC>(wparam);
+  CRect rect;
+  GetClientRect(&rect);
+
+  // Fill the entire client area with solid white first to clear any previous
+  // artifacts.
+  ::FillRect(hdc, &rect, static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH)));
+
+  const int width = rect.Width();
+  const int height = rect.Height();
+
   // Configuration for the rainbow geometry.
   static constexpr size_t kNumStops = 7;
   static constexpr size_t kNumSegments = kNumStops - 1;
@@ -173,24 +184,12 @@
       RGB(220, 255, 255)   // Light Aqua
   };
 
-  const HDC hdc = reinterpret_cast<HDC>(wparam);
-  CRect rect;
-  GetClientRect(&rect);
-
-  const int width = rect.right - rect.left;
-  const int height = rect.bottom - rect.top;
-
   // Define the curve parameters:
   // y_edge: The height where the rainbow starts at the left/right edges.
   // y_center: The height where the rainbow is thinnest at the center.
   const int y_edge = static_cast<int>(height * kYEdgeRatio);
   const int y_center = static_cast<int>(height * kYCenterRatio);
 
-  // Fill the top area with solid white.
-  CRect top_rect = {rect.left, rect.top, rect.right, y_edge};
-  ::FillRect(hdc, &top_rect,
-             static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH)));
-
   // Define the rainbow mesh vertices.
   std::array<TRIVERTEX, kNumVertices> vertices;
   auto v_span = base::span(vertices);
@@ -208,7 +207,10 @@
 
   for (size_t i = 0; i < kNumStops; ++i) {
     const double stop = kStops[i];
-    const int x = static_cast<int>(width * stop);
+
+    // Use the width of the rect to ensure we hit the right edge perfectly.
+    const int x =
+        (i == kNumStops - 1) ? rect.right : static_cast<int>(width * stop);
 
     // Calculate the concave (U-shaped) boundary using a parabola.
     const double factor = (2.0 * stop - 1.0);
@@ -218,8 +220,9 @@
     // Top row of the mesh (White boundary following the curve).
     set_vertex(v_span, i, x, y_boundary, RGB(255, 255, 255));
 
-    // Bottom row of the mesh (Light rainbow colors).
-    set_vertex(v_span, i + kNumStops, x, height, kColors[i]);
+    // Bottom row of the mesh (Light rainbow colors). Stretch to the very
+    // bottom.
+    set_vertex(v_span, i + kNumStops, x, rect.bottom, kColors[i]);
   }
 
   // Create the triangles, 2 triangles per segment.
diff --git a/chrome/updater/win/ui/ui.cc b/chrome/updater/win/ui/ui.cc
index a6ee4c6..897bef6e 100644
--- a/chrome/updater/win/ui/ui.cc
+++ b/chrome/updater/win/ui/ui.cc
@@ -160,6 +160,26 @@
   return 0;
 }
 
+LRESULT OmahaWnd::OnDpiChanged(UINT msg,
+                               WPARAM wparam,
+                               LPARAM lparam,
+                               BOOL& handled) {
+  // Resize window to the OS-suggested rect.
+  SetWindowPos(NULL, /*new_rect=*/reinterpret_cast<RECT*>(lparam),
+               SWP_NOZORDER | SWP_NOACTIVATE);
+
+  // Re-render text/graphics for the new DPI.
+  ApplyDpiScaling(/*new_dpi=*/HIWORD(wparam));
+
+  // 3. Resize the title bar.
+  RecalcLayout(m_hWnd, GetDlgItem(IDC_TITLE_BAR_SPACER));
+
+  // Force a full redraw of everything.
+  RedrawWindow(nullptr, nullptr,
+               RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN | RDW_UPDATENOW);
+  return 0;
+}
+
 // Called when ESC key is pressed.
 LRESULT OmahaWnd::OnCancel(WORD, WORD id, HWND, BOOL& handled) {
   CHECK_EQ(id, IDCANCEL);
@@ -187,6 +207,41 @@
   }
 }
 
+void OmahaWnd::ApplyDpiScaling(int dpi) {
+  // Calculate new font height: (DesiredPointSize * dpi) / 72.
+  // Standard formula for font height in pixels:
+  //   Height = -(PointSize * DPI / 72).
+  // Use a negative number for height to request the character height.
+  const int font_height = -::MulDiv(9, dpi, 72);
+
+  if (default_font_.m_hFont) {
+    default_font_.DeleteObject();
+  }
+
+  // Recreate the WTL font. Using `CreateFont` gives explicit control over the
+  // height calculated from the per-monitor DPI.
+  default_font_.CreateFont(font_height,          // nHeight
+                           0,                    // nWidth
+                           0,                    // nEscapement
+                           0,                    // nOrientation
+                           FW_NORMAL,            // nWeight
+                           FALSE,                // bItalic
+                           FALSE,                // bUnderline
+                           0,                    // cStrikeOut
+                           DEFAULT_CHARSET,      // nCharSet
+                           OUT_DEFAULT_PRECIS,   // nOutPrecision
+                           CLIP_DEFAULT_PRECIS,  // nClipPrecision
+                           CLEARTYPE_QUALITY,    // nQuality (Forces ClearType)
+                           DEFAULT_PITCH | FF_DONTCARE,  // nPitchAndFamily
+                           kDialogFont                   // lpszFacename
+  );
+
+  // Tell all child controls to use the new font.
+  SendMessageToDescendants(
+      WM_SETFONT, reinterpret_cast<WPARAM>(static_cast<HFONT>(default_font_)),
+      TRUE);
+}
+
 bool OmahaWnd::OnComplete() {
   if (!IsWindow()) {
     RequestExitProcess();
@@ -235,7 +290,7 @@
   }
   ::EnableMenuItem(menu, SC_CLOSE,
                    MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED));
-  RecalcLayout();
+  RecalcLayout(m_hWnd, GetDlgItem(IDC_TITLE_BAR_SPACER));
   return S_OK;
 }
 
diff --git a/chrome/updater/win/ui/ui.h b/chrome/updater/win/ui/ui.h
index 6500766a..e4a7d896 100644
--- a/chrome/updater/win/ui/ui.h
+++ b/chrome/updater/win/ui/ui.h
@@ -59,6 +59,7 @@
   BEGIN_MSG_MAP(OmahaWnd)
     MESSAGE_HANDLER(WM_CLOSE, OnClose)
     MESSAGE_HANDLER(WM_NCDESTROY, OnNCDestroy)
+    MESSAGE_HANDLER(WM_DPICHANGED, OnDpiChanged)
     COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
     CHAIN_MSG_MAP(Base)
     CHAIN_MSG_MAP(OwnerDrawTitleBar)
@@ -88,11 +89,17 @@
                       WPARAM wparam,
                       LPARAM lparam,
                       BOOL& handled);  // NOLINT
+  LRESULT OnDpiChanged(UINT msg,
+                       WPARAM wparam,
+                       LPARAM lparam,
+                       BOOL& handled);  // NOLINT
   LRESULT OnCancel(WORD notify_code,
                    WORD id,
                    HWND wnd_ctl,
                    BOOL& handled);  // NOLINT
 
+  void ApplyDpiScaling(int dpi);
+
   // Returns true if the window is closed.
   virtual bool MaybeCloseWindow() = 0;
 
diff --git a/clank b/clank
index acdf8f8..0492202 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit acdf8f87c64a7f4c6a2031b2f1d30c1d5d139591
+Subproject commit 0492202fbff318e4b8fe5079af3ab87672fabded
diff --git a/codelabs/mojo_examples/process_bootstrapper.h b/codelabs/mojo_examples/process_bootstrapper.h
index 6e4b71f..64abf1ff 100644
--- a/codelabs/mojo_examples/process_bootstrapper.h
+++ b/codelabs/mojo_examples/process_bootstrapper.h
@@ -36,11 +36,10 @@
             base::sequence_manager::SequenceManager::Settings::Builder()
                 .SetMessagePumpType(type)
                 .Build());
-    default_tq = std::make_unique<base::sequence_manager::TaskQueue::Handle>(
-        sequence_manager->CreateTaskQueue(
-            base::sequence_manager::TaskQueue::Spec(
-                base::sequence_manager::QueueName::DEFAULT_TQ)));
-    sequence_manager->SetDefaultTaskRunner((*default_tq)->task_runner());
+    default_tq = sequence_manager->CreateTaskQueue(
+        base::sequence_manager::TaskQueue::Spec(
+            base::sequence_manager::QueueName::DEFAULT_TQ));
+    sequence_manager->SetDefaultTaskQueue(default_tq.get());
 
     if (type == base::MessagePumpType::DEFAULT) {
       InitDedicatedIOThread();
@@ -75,11 +74,11 @@
     // be the IO thread. This means preferring `io_task_runner` when it is
     // non-null, and the default task runner otherwise.
     mojo::core::ScopedIPCSupport ipc_support(
-        io_task_runner ? io_task_runner : (*default_tq)->task_runner(),
+        io_task_runner ? io_task_runner : default_tq->task_runner(),
         mojo::core::ScopedIPCSupport::ShutdownPolicy::FAST);
   }
 
-  std::unique_ptr<base::sequence_manager::TaskQueue::Handle> default_tq;
+  base::sequence_manager::TaskQueue::Handle default_tq;
   std::unique_ptr<base::sequence_manager::SequenceManager> sequence_manager;
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner;
 
diff --git a/codelabs/threading_and_scheduling/01-single-task-queue.cc b/codelabs/threading_and_scheduling/01-single-task-queue.cc
index a0ff041..2e59a53 100644
--- a/codelabs/threading_and_scheduling/01-single-task-queue.cc
+++ b/codelabs/threading_and_scheduling/01-single-task-queue.cc
@@ -64,7 +64,8 @@
   // and get other interesting information about them.
   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
       default_task_queue->CreateTaskRunner(static_cast<int>(TaskType::kMain));
-  sequence_manager->SetDefaultTaskRunner(task_runner);
+  sequence_manager->SetDefaultTaskRunner(
+      task_runner, default_task_queue->GetQueuePriority());
 
   // Now that this thread has a bound sequence manager set up, we can:
   //   1.) Start posting tasks to its queues which are not yet being processed
diff --git a/codelabs/threading_and_scheduling/02-task-queue-priorities.cc b/codelabs/threading_and_scheduling/02-task-queue-priorities.cc
index 276cd88..9ce3e03 100644
--- a/codelabs/threading_and_scheduling/02-task-queue-priorities.cc
+++ b/codelabs/threading_and_scheduling/02-task-queue-priorities.cc
@@ -73,7 +73,7 @@
   // Get TaskRunners for both TaskQueues.
   scoped_refptr<base::SingleThreadTaskRunner> a_runner_1 =
       tq_a->CreateTaskRunner(static_cast<int>(TaskType::kSource1));
-  sequence_manager->SetDefaultTaskRunner(a_runner_1);
+  sequence_manager->SetDefaultTaskRunner(a_runner_1, tq_a->GetQueuePriority());
   scoped_refptr<base::SingleThreadTaskRunner> a_runner_2 =
       tq_a->CreateTaskRunner(static_cast<int>(TaskType::kSource2));
   scoped_refptr<base::SingleThreadTaskRunner> b_runner =
diff --git a/codelabs/threading_and_scheduling/03-randomized-task-queues.cc b/codelabs/threading_and_scheduling/03-randomized-task-queues.cc
index 5840e1c8..913a852 100644
--- a/codelabs/threading_and_scheduling/03-randomized-task-queues.cc
+++ b/codelabs/threading_and_scheduling/03-randomized-task-queues.cc
@@ -57,7 +57,7 @@
   // Get TaskRunners for both TaskQueues.
   scoped_refptr<base::SingleThreadTaskRunner> a_runner_1 =
       tq_a->CreateTaskRunner(static_cast<int>(TaskType::kSource1));
-  sequence_manager->SetDefaultTaskRunner(a_runner_1);
+  sequence_manager->SetDefaultTaskRunner(a_runner_1, tq_a->GetQueuePriority());
   scoped_refptr<base::SingleThreadTaskRunner> b_runner =
       tq_b->CreateTaskRunner(static_cast<int>(TaskType::kSource1));
 
diff --git a/codelabs/threading_and_scheduling/04-multiple-threads.cc b/codelabs/threading_and_scheduling/04-multiple-threads.cc
index 3487d003..f7cdf8d 100644
--- a/codelabs/threading_and_scheduling/04-multiple-threads.cc
+++ b/codelabs/threading_and_scheduling/04-multiple-threads.cc
@@ -48,7 +48,7 @@
             base::sequence_manager::TaskQueue::Spec(
                 base::sequence_manager::QueueName::IO_DEFAULT_TQ)));
     default_task_runner_ = task_queue_->task_runner();
-    owned_sequence_manager_->SetDefaultTaskRunner(default_task_runner_);
+    owned_sequence_manager_->SetDefaultTaskQueue(task_queue_.get());
     // Set the global TaskRunner-to-this-thread, so that the main thread can
     // post tasks to the IO thread.
     g_io_thread_task_runner = default_task_runner_;
@@ -61,7 +61,7 @@
   void BindToCurrentThread() override {
     owned_sequence_manager_->BindToMessagePump(
         base::MessagePump::Create(base::MessagePumpType::IO));
-    owned_sequence_manager_->SetDefaultTaskRunner(GetDefaultTaskRunner());
+    owned_sequence_manager_->SetDefaultTaskQueue(task_queue_.get());
   }
   scoped_refptr<base::SingleThreadTaskRunner> GetDefaultTaskRunner() override {
     return default_task_runner_;
@@ -93,7 +93,7 @@
   // Get a default TaskRunner for the main (UI) thread.
   scoped_refptr<base::SingleThreadTaskRunner> default_task_runner =
       main_task_queue->task_runner();
-  sequence_manager->SetDefaultTaskRunner(default_task_runner);
+  sequence_manager->SetDefaultTaskQueue(main_task_queue.get());
 
   // Set the global TaskRunner-to-this-thread, so that the IO thread can post
   // tasks to the main thread.
diff --git a/components/BUILD.gn b/components/BUILD.gn
index dabeaf1..8f4f7135 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -676,7 +676,6 @@
       "//components/android_autofill/browser:unit_tests",
       "//components/browser_ui/accessibility/android:unit_tests",
       "//components/browser_ui/site_settings/android:unit_tests",
-      "//components/browser_ui/sms/android:unit_tests",
       "//components/cdm/browser:unit_tests",
       "//components/collaboration/internal:comments_native_java_unittests_java",
       "//components/collaboration/internal:messaging_native_java_unittests_java",
diff --git a/components/accessibility_annotator/core/storage/accessibility_annotator_backend.h b/components/accessibility_annotator/core/storage/accessibility_annotator_backend.h
index 621685ab..42a01fc 100644
--- a/components/accessibility_annotator/core/storage/accessibility_annotator_backend.h
+++ b/components/accessibility_annotator/core/storage/accessibility_annotator_backend.h
@@ -37,10 +37,15 @@
 
 class AccessibilityAnnotatorBackend : public KeyedService {
  public:
+  struct ContentAnnotationsData;
+
   class Observer : public base::CheckedObserver {
+    // TODO(crbug.com/501107222): Add observer method for when annotations are
+    // deleted.
    public:
     // Called when content annotations are added.
-    virtual void OnContentAnnotationsAdded() = 0;
+    virtual void OnContentAnnotationsAdded(
+        const ContentAnnotationsData& annotation_data) = 0;
   };
 
   // TODO(crbug.com/501429617): Move this struct out of backend class.
@@ -61,6 +66,8 @@
     base::DictValue classifier_results;
     base::Time navigation_timestamp;
     history::VisitID visit_id;
+    // TODO(crbug.com/501092664): Add URL field to prepare for keying cache by
+    // visit_id and pass to observers for data ingestion.
   };
 
   ~AccessibilityAnnotatorBackend() override = default;
diff --git a/components/accessibility_annotator/core/storage/accessibility_annotator_backend_impl.cc b/components/accessibility_annotator/core/storage/accessibility_annotator_backend_impl.cc
index d4712c1..a59007f3 100644
--- a/components/accessibility_annotator/core/storage/accessibility_annotator_backend_impl.cc
+++ b/components/accessibility_annotator/core/storage/accessibility_annotator_backend_impl.cc
@@ -4,6 +4,7 @@
 
 #include "components/accessibility_annotator/core/storage/accessibility_annotator_backend_impl.h"
 
+#include "base/containers/lru_cache.h"
 #include "base/containers/map_util.h"
 #include "base/files/file_path.h"
 #include "base/functional/bind.h"
@@ -11,6 +12,7 @@
 #include "base/json/json_reader.h"
 #include "base/metrics/histogram_macros_local.h"
 #include "base/notimplemented.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/task/thread_pool.h"
 #include "components/accessibility_annotator/core/accessibility_annotator_features.h"
 #include "components/accessibility_annotator/core/storage/accessibility_annotation_sync_bridge.h"
@@ -123,10 +125,12 @@
 void AccessibilityAnnotatorBackendImpl::SetContentAnnotationsCacheData(
     const GURL& url,
     ContentAnnotationsData data) {
-  // This automatically handles eviction of the oldest entries if full.
-  content_annotations_cache_.Put(url, std::move(data));
+  base::LRUCache<GURL, ContentAnnotationsData>::iterator it =
+      content_annotations_cache_.Put(url, std::move(data));
+
   observers_.Notify(
-      &AccessibilityAnnotatorBackend::Observer::OnContentAnnotationsAdded);
+      &AccessibilityAnnotatorBackend::Observer::OnContentAnnotationsAdded,
+      it->second);
 }
 
 void AccessibilityAnnotatorBackendImpl::RemoveContentAnnotationsCacheData(
diff --git a/components/accessibility_annotator/core/storage/accessibility_annotator_backend_unittest.cc b/components/accessibility_annotator/core/storage/accessibility_annotator_backend_unittest.cc
index 5098a61..263dcd50 100644
--- a/components/accessibility_annotator/core/storage/accessibility_annotator_backend_unittest.cc
+++ b/components/accessibility_annotator/core/storage/accessibility_annotator_backend_unittest.cc
@@ -21,6 +21,11 @@
 namespace {
 
 using ::base::test::DictionaryHasValues;
+using ::testing::AllOf;
+using ::testing::Eq;
+using ::testing::Field;
+using ::testing::Optional;
+using ::testing::Property;
 
 AccessibilityAnnotatorBackend::ContentAnnotationsData
 CreateContentAnnotationsData(std::string_view page_title) {
@@ -34,7 +39,10 @@
 
 class MockBackendObserver : public AccessibilityAnnotatorBackend::Observer {
  public:
-  MOCK_METHOD(void, OnContentAnnotationsAdded, (), (override));
+  MOCK_METHOD(void,
+              OnContentAnnotationsAdded,
+              (const AccessibilityAnnotatorBackend::ContentAnnotationsData&),
+              (override));
 };
 
 class AccessibilityAnnotatorBackendTest : public testing::Test {
@@ -252,11 +260,25 @@
   backend_->AddObserver(&observer);
 
   GURL url("https://example.com/");
-  AccessibilityAnnotatorBackend::ContentAnnotationsData data;
-  data.page_title = "Test Page Title";
+  AccessibilityAnnotatorBackend::ContentAnnotationsData data =
+      CreateContentAnnotationsData("Test Page Title");
+  data.content_annotation = optimization_guide::proto::ContentAnnotation();
+  data.content_annotation->set_description("Test Description");
 
-  // Setting data should notify.
-  EXPECT_CALL(observer, OnContentAnnotationsAdded);
+  EXPECT_CALL(
+      observer,
+      OnContentAnnotationsAdded(AllOf(
+          // Match the plain struct member
+          Field(&AccessibilityAnnotatorBackend::ContentAnnotationsData::
+                    page_title,
+                Eq("Test Page Title")),
+
+          // Match the std::optional field
+          Field(&AccessibilityAnnotatorBackend::ContentAnnotationsData::
+                    content_annotation,
+                Optional(Property(
+                    &optimization_guide::proto::ContentAnnotation::description,
+                    Eq("Test Description")))))));
   backend_->SetContentAnnotationsCacheData(url, std::move(data));
   testing::Mock::VerifyAndClearExpectations(&observer);
 
diff --git a/components/autofill/android/java/src/org/chromium/components/autofill/autofill_ai/EntityType.java b/components/autofill/android/java/src/org/chromium/components/autofill/autofill_ai/EntityType.java
index 5f2eb4b..023a92d 100644
--- a/components/autofill/android/java/src/org/chromium/components/autofill/autofill_ai/EntityType.java
+++ b/components/autofill/android/java/src/org/chromium/components/autofill/autofill_ai/EntityType.java
@@ -32,6 +32,8 @@
     // Whether this entity type can be stored in Google Wallet.
     // Note, this is currently behind `kAutofillAiWalletPrivatePasses` feature flag.
     private final boolean mIsEligibleForWalletStorage;
+    // Whether this entity type supports masked storage.
+    private final boolean mIsMaskedStorageSupported;
     private final String mTypeNameAsString;
     // Used for histogram recording.
     private final String mTypeNameAsMetricsString;
@@ -52,6 +54,7 @@
             boolean isReadOnly,
             boolean isEnabled,
             boolean isEligibleForWalletStorage,
+            boolean isMaskedStorageSupported,
             @JniType("std::u16string") String typeNameAsString,
             @JniType("std::string") String typeNameAsMetricsString,
             @JniType("std::string") String addEntityTypeString,
@@ -65,6 +68,7 @@
         mIsReadOnly = isReadOnly;
         mIsEnabled = isEnabled;
         mIsEligibleForWalletStorage = isEligibleForWalletStorage;
+        mIsMaskedStorageSupported = isMaskedStorageSupported;
         mTypeNameAsString = typeNameAsString;
         mTypeNameAsMetricsString = typeNameAsMetricsString;
         mAddEntityTypeString = addEntityTypeString;
@@ -94,6 +98,11 @@
         return mIsEligibleForWalletStorage;
     }
 
+    @CalledByNative
+    public boolean isMaskedStorageSupported() {
+        return mIsMaskedStorageSupported;
+    }
+
     public String getTypeNameAsString() {
         return mTypeNameAsString;
     }
diff --git a/components/autofill/android/java/src/org/chromium/components/autofill/autofill_ai/utils/TestUtils.java b/components/autofill/android/java/src/org/chromium/components/autofill/autofill_ai/utils/TestUtils.java
index 7146f6c..9c79de0 100644
--- a/components/autofill/android/java/src/org/chromium/components/autofill/autofill_ai/utils/TestUtils.java
+++ b/components/autofill/android/java/src/org/chromium/components/autofill/autofill_ai/utils/TestUtils.java
@@ -26,6 +26,7 @@
                 isReadOnly,
                 isEnabled,
                 isEligibleForWalletStorage,
+                /* isMaskedStorageSupported= */ false,
                 /* typeNameAsString= */ "Vehicle",
                 /* typeNameAsMetricsString= */ "Vehicle",
                 /* addEntityTypeString= */ "Add Vehicle",
@@ -49,6 +50,7 @@
                 isReadOnly,
                 isEnabled,
                 isEligibleForWalletStorage,
+                /* isMaskedStorageSupported= */ true,
                 /* typeNameAsString= */ "Passport",
                 /* typeNameAsMetricsString= */ "Passport",
                 /* addEntityTypeString= */ "Add passport",
@@ -72,6 +74,7 @@
                 isReadOnly,
                 isEnabled,
                 isEligibleForWalletStorage,
+                /* isMaskedStorageSupported= */ true,
                 /* typeNameAsString= */ "National ID",
                 /* typeNameAsMetricsString= */ "NationalId",
                 /* addEntityTypeString= */ "Add National ID",
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index 45ce2a8a..d87b44b 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -1324,7 +1324,7 @@
     <message name="IDS_AUTOFILL_AI_SAVE_OR_UPDATE_LOCAL_ENTITY_SOURCE_NOTICE" desc="A description that appears beneath the entity details notifying the user that their data is going to be stored locally on device.">
       Your info is saved to your device
     </message>
-    <message name="IDS_AUTOFILL_AI_SAVE_OR_UPDATE_ENTITY_IN_WALLET_SOURCE_NOTICE" desc="A description that appears beneath the entity details notifying the user that their data is going to be stored in Google Wallet.">
+    <message name="IDS_AUTOFILL_AI_SAVE_OR_UPDATE_ENTITY_IN_WALLET_SOURCE_NOTICE" desc="A description that appears beneath the entity details notifying the user that their data is going to be stored in Google Wallet." formatter_data="android_java">
       Save your info in <ph name="IDS_AUTOFILL_GOOGLE_WALLET_TITLE_1">$1<ex>Google Wallet</ex></ph> and get things done faster, like filling forms across Google products. You can <ph name="BEGIN_LINK">&lt;link&gt;</ph>manage your info<ph name="END_LINK">&lt;/link&gt;</ph> in <ph name="IDS_AUTOFILL_GOOGLE_WALLET_TITLE_2">$2<ex>Google Wallet</ex></ph> for <ph name="ACCOUNT">$3<ex>alexpark@gmail.com</ex></ph>
     </message>
     <message name="IDS_AUTOFILL_AI_SAVE_OR_UPDATE_ENTITY_NEW_ATTRIBUTE_ACCESSIBILITY_NAME" desc="Used in entity update dialogs, as the accessible names for rows with new attributes" formatter_data="android_java">
@@ -1355,9 +1355,6 @@
     <message name="IDS_AUTOFILL_AI_LOCAL_ENTITY_EDITOR_SOURCE_NOTICE" desc="The footer message in the editor dialog informing the user that their data will be stored locally on their device. 'To your device': helps the user understand that information is saved locally, not in the Cloud." formatter_data="android_java">
       Your info is saved only to your device. You'll have the chance to save it to Wallet the next time you fill a relevant form.
     </message>
-    <message name="IDS_AUTOFILL_AI_WALLET_ENTITY_EDITOR_SOURCE_NOTICE" desc="The footer message in the editor dialog informing the user that their data will be stored in the Google Wallet." formatter_data="android_java">
-      Your info gets saved to Google Wallet for <ph name="ACCOUNT">$1<ex>elisa.beckett@gmail.com</ex></ph>. You can use it across Google services to fill forms faster.
-    </message>
     <message name="IDS_AUTOFILL_AI_ENTITY_EDITOR_DATE_FIELD_MONTH_LABEL" desc="The default value in the date field month picker in the editor dialog." formatter_data="android_java">
       Month
     </message>
diff --git a/components/autofill_strings_grdp/IDS_AUTOFILL_AI_SAVE_ENTITY_TO_WALLET_DIALOG_SUBTITLE_NEW.png.sha1 b/components/autofill_strings_grdp/IDS_AUTOFILL_AI_SAVE_ENTITY_TO_WALLET_DIALOG_SUBTITLE_NEW.png.sha1
index a5c1e3d..fb198fd 100644
--- a/components/autofill_strings_grdp/IDS_AUTOFILL_AI_SAVE_ENTITY_TO_WALLET_DIALOG_SUBTITLE_NEW.png.sha1
+++ b/components/autofill_strings_grdp/IDS_AUTOFILL_AI_SAVE_ENTITY_TO_WALLET_DIALOG_SUBTITLE_NEW.png.sha1
@@ -1 +1 @@
-a72185deba8f352c24766a54df580e10e14832a8
\ No newline at end of file
+3ddf620aa5c2a52fc41057fc096355caf8e02551
\ No newline at end of file
diff --git a/components/autofill_strings_grdp/IDS_AUTOFILL_AI_WALLET_ENTITY_EDITOR_SOURCE_NOTICE.png.sha1 b/components/autofill_strings_grdp/IDS_AUTOFILL_AI_WALLET_ENTITY_EDITOR_SOURCE_NOTICE.png.sha1
deleted file mode 100644
index 7609468..0000000
--- a/components/autofill_strings_grdp/IDS_AUTOFILL_AI_WALLET_ENTITY_EDITOR_SOURCE_NOTICE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4677859c1719d32a96f532dea5f75d0ed5d805ef
\ No newline at end of file
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java
index c90380c5..e8dd4fa9 100644
--- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java
+++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java
@@ -556,11 +556,9 @@
             return content == mBottomSheet.getCurrentSheetContent();
         }
 
-        boolean shouldSwapForPriorityContent =
+        boolean shouldSwapContent =
                 mBottomSheet.getCurrentSheetContent() != null
-                        && content.getPriority()
-                                < mBottomSheet.getCurrentSheetContent().getPriority()
-                        && canBottomSheetSwitchContent();
+                        && canBottomSheetSwitchContent(content);
 
         // Always add the content to the queue, it will be handled after the sheet closes if
         // necessary. If already hidden, |showNextContent| will handle the request.
@@ -569,7 +567,7 @@
         if (mBottomSheet.getCurrentSheetContent() == null && !mSuppressionTokens.hasTokens()) {
             showNextContent(animate);
             return true;
-        } else if (shouldSwapForPriorityContent) {
+        } else if (shouldSwapContent) {
             mIsSuppressingCurrentContent = true;
             mContentQueue.add(mBottomSheet.getCurrentSheetContent());
             if (!mSuppressionTokens.hasTokens()) {
@@ -780,14 +778,14 @@
      *
      * @return Whether the sheet currently supports switching its content.
      */
-    private boolean canBottomSheetSwitchContent() {
+    private boolean canBottomSheetSwitchContent(BottomSheetContent nextContent) {
         BottomSheetContent currentContent = assumeNonNull(mBottomSheet).getCurrentSheetContent();
-        if (!mBottomSheet.isSheetOpen()) {
+        if (nextContent.getPriority() < assumeNonNull(currentContent).getPriority()
+                && !mBottomSheet.isSheetOpen()) {
             return true;
         }
 
-        if (currentContent != null && currentContent.canSuppressInAnyState()) {
-            assert currentContent.getPriority() == BottomSheetContent.ContentPriority.LOW;
+        if (assumeNonNull(currentContent).canBeSuppressed(nextContent)) {
             return true;
         }
 
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImplUnitTest.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImplUnitTest.java
index e673f9f..2c4d87e0 100644
--- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImplUnitTest.java
+++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImplUnitTest.java
@@ -33,6 +33,7 @@
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.supplier.SettableMonotonicObservableSupplier;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason;
 import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
 import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateManager;
@@ -212,4 +213,88 @@
         // Verify that the request was blocked.
         assertFalse("requestShowContent should return false when the root view is null.", result);
     }
+
+    @Test
+    public void testRequestShowContent_currentLow_newHigh_canBeSuppressed_returnsFalse() {
+        mController.runSheetInitializerForTesting();
+
+        BottomSheetContent newContent = mock(BottomSheetContent.class);
+        when(newContent.getPriority()).thenReturn(BottomSheetContent.ContentPriority.HIGH);
+        BottomSheetContent currentContent = mock(BottomSheetContent.class);
+        when(currentContent.getPriority()).thenReturn(BottomSheetContent.ContentPriority.LOW);
+        when(currentContent.canBeSuppressed(newContent)).thenReturn(false);
+        when(currentContent.getBackPressStateChangedSupplier())
+                .thenReturn(ObservableSuppliers.alwaysFalse());
+
+        when(mBottomSheet.getCurrentSheetContent()).thenReturn(currentContent);
+        when(mBottomSheet.isSheetOpen()).thenReturn(true);
+
+        boolean result = mController.requestShowContent(newContent, /* animate= */ true);
+
+        assertFalse("Request should return false as content cannot be suppressed", result);
+        verify(mBottomSheet, times(0)).setSheetState(SheetState.HIDDEN, true);
+    }
+
+    @Test
+    public void testRequestShowContent_currentLow_newHigh_canBeSuppressed_returnsTrue() {
+        mController.runSheetInitializerForTesting();
+
+        BottomSheetContent newContent = mock(BottomSheetContent.class);
+        when(newContent.getPriority()).thenReturn(BottomSheetContent.ContentPriority.HIGH);
+        BottomSheetContent currentContent = mock(BottomSheetContent.class);
+        when(currentContent.getPriority()).thenReturn(BottomSheetContent.ContentPriority.LOW);
+        when(currentContent.canBeSuppressed(newContent)).thenReturn(true);
+        when(currentContent.getBackPressStateChangedSupplier())
+                .thenReturn(ObservableSuppliers.alwaysFalse());
+
+        when(mBottomSheet.getCurrentSheetContent()).thenReturn(currentContent);
+        when(mBottomSheet.isSheetOpen()).thenReturn(true);
+
+        boolean result = mController.requestShowContent(newContent, /* animate= */ true);
+
+        assertTrue("Request should return true as content can be suppressed", result);
+        verify(mBottomSheet).setSheetState(SheetState.HIDDEN, true);
+    }
+
+    @Test
+    public void testRequestShowContent_currentHigh_newLow_canBeSuppressed_returnsTrue() {
+        mController.runSheetInitializerForTesting();
+
+        BottomSheetContent newContent = mock(BottomSheetContent.class);
+        when(newContent.getPriority()).thenReturn(BottomSheetContent.ContentPriority.LOW);
+        BottomSheetContent currentContent = mock(BottomSheetContent.class);
+        when(currentContent.getPriority()).thenReturn(BottomSheetContent.ContentPriority.HIGH);
+        when(currentContent.canBeSuppressed(newContent)).thenReturn(true);
+        when(currentContent.getBackPressStateChangedSupplier())
+                .thenReturn(ObservableSuppliers.alwaysFalse());
+
+        when(mBottomSheet.getCurrentSheetContent()).thenReturn(currentContent);
+        when(mBottomSheet.isSheetOpen()).thenReturn(true);
+
+        boolean result = mController.requestShowContent(newContent, /* animate= */ true);
+
+        assertTrue("Request should return true as content can be suppressed", result);
+        verify(mBottomSheet).setSheetState(SheetState.HIDDEN, true);
+    }
+
+    @Test
+    public void testRequestShowContent_currentHigh_newHigh_canBeSuppressed_returnsTrue() {
+        mController.runSheetInitializerForTesting();
+
+        BottomSheetContent newContent = mock(BottomSheetContent.class);
+        when(newContent.getPriority()).thenReturn(BottomSheetContent.ContentPriority.HIGH);
+        BottomSheetContent currentContent = mock(BottomSheetContent.class);
+        when(currentContent.getPriority()).thenReturn(BottomSheetContent.ContentPriority.HIGH);
+        when(currentContent.canBeSuppressed(newContent)).thenReturn(true);
+        when(currentContent.getBackPressStateChangedSupplier())
+                .thenReturn(ObservableSuppliers.alwaysFalse());
+
+        when(mBottomSheet.getCurrentSheetContent()).thenReturn(currentContent);
+        when(mBottomSheet.isSheetOpen()).thenReturn(true);
+
+        boolean result = mController.requestShowContent(newContent, /* animate= */ true);
+
+        assertTrue("Request should return true as high priority content can be suppressed", result);
+        verify(mBottomSheet).setSheetState(SheetState.HIDDEN, true);
+    }
 }
diff --git a/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetContent.java b/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetContent.java
index 91bb5c68..0d7b4ab 100644
--- a/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetContent.java
+++ b/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetContent.java
@@ -301,11 +301,10 @@
     int getSheetClosedAccessibilityStringId();
 
     /**
-     * @return True if this content should hide when higher-priority content is requested to be
-     *     shown, even if the sheet is expanded. Otherwise the new content will only be shown after
-     *     the sheet is dismissed. If returning true here, this content's priority should be LOW.
+     * @param nextContent The content that is requesting to be shown.
+     * @return True if this content should hide when another content is requested to be shown.
      */
-    default boolean canSuppressInAnyState() {
+    default boolean canBeSuppressed(BottomSheetContent nextContent) {
         return false;
     }
 
diff --git a/components/browser_ui/bottomsheet/android/test/java/src/org/chromium/components/browser_ui/bottomsheet/TestBottomSheetContent.java b/components/browser_ui/bottomsheet/android/test/java/src/org/chromium/components/browser_ui/bottomsheet/TestBottomSheetContent.java
index f4ec7bb..42c881d 100644
--- a/components/browser_ui/bottomsheet/android/test/java/src/org/chromium/components/browser_ui/bottomsheet/TestBottomSheetContent.java
+++ b/components/browser_ui/bottomsheet/android/test/java/src/org/chromium/components/browser_ui/bottomsheet/TestBottomSheetContent.java
@@ -239,11 +239,11 @@
     }
 
     @Override
-    public boolean canSuppressInAnyState() {
+    public boolean canBeSuppressed(BottomSheetContent nextContent) {
         return mCanSuppressInAnyState;
     }
 
-    public void setCanSuppressInAnyState(boolean value) {
+    public void setCanBeSuppressed(boolean value) {
         mCanSuppressInAnyState = value;
     }
 }
diff --git a/components/browser_ui/sms/android/BUILD.gn b/components/browser_ui/sms/android/BUILD.gn
deleted file mode 100644
index 6c158a1..0000000
--- a/components/browser_ui/sms/android/BUILD.gn
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright 2020 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/android/rules.gni")
-import("//third_party/jni_zero/jni_zero.gni")
-
-source_set("android") {
-  sources = [
-    "sms_infobar.cc",
-    "sms_infobar.h",
-    "sms_infobar_delegate.cc",
-    "sms_infobar_delegate.h",
-  ]
-  deps = [
-    ":jni_headers",
-    "//base",
-    "//components/infobars/android",
-    "//components/infobars/core",
-    "//components/resources:android_resources",
-    "//components/strings",
-    "//components/url_formatter",
-    "//content/public/browser",
-    "//ui/android",
-    "//url",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [ "sms_infobar_delegate_unittest.cc" ]
-  deps = [
-    ":android",
-    "//base/test:test_support",
-    "//components/content_settings/browser",
-    "//components/content_settings/browser:test_support",
-    "//components/content_settings/core/browser",
-    "//components/infobars/content",
-    "//content/test:test_support",
-    "//testing/gtest",
-  ]
-}
-
-generate_jni("jni_headers") {
-  sources = [
-    "java/src/org/chromium/components/browser_ui/sms/WebOTPServiceInfoBar.java",
-  ]
-}
-
-android_library("java") {
-  resources_package = "org.chromium.components.browser_ui.sms"
-  sources = [
-    "java/src/org/chromium/components/browser_ui/sms/WebOTPServiceInfoBar.java",
-    "java/src/org/chromium/components/browser_ui/sms/WebOTPServiceUma.java",
-  ]
-
-  deps = [
-    "//base:base_java",
-    "//components/browser_ui/styles/android:java_resources",
-    "//components/infobars/android:java",
-    "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/jni_zero:jni_zero_java",
-    "//ui/android:ui_java",
-    "//url:gurl_java",
-  ]
-}
diff --git a/components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/WebOTPServiceInfoBar.java b/components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/WebOTPServiceInfoBar.java
deleted file mode 100644
index c643f69..0000000
--- a/components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/WebOTPServiceInfoBar.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.browser_ui.sms;
-
-import android.app.Activity;
-import android.os.SystemClock;
-import android.view.View;
-
-import androidx.annotation.VisibleForTesting;
-
-import org.jni_zero.CalledByNative;
-
-import org.chromium.base.Log;
-import org.chromium.build.annotations.NullMarked;
-import org.chromium.build.annotations.Nullable;
-import org.chromium.components.infobars.ConfirmInfoBar;
-import org.chromium.components.infobars.InfoBarControlLayout;
-import org.chromium.components.infobars.InfoBarLayout;
-import org.chromium.ui.KeyboardVisibilityDelegate;
-import org.chromium.ui.base.WindowAndroid;
-
-/** An InfoBar that asks for the user's permission to share the SMS with the page. */
-@NullMarked
-public class WebOTPServiceInfoBar extends ConfirmInfoBar {
-    private static final String TAG = "WebOTPServiceInfoBar";
-    private static final boolean DEBUG = false;
-    private final String mMessage;
-    private final WindowAndroid mWindowAndroid;
-    private @Nullable Long mKeyboardDismissedTime;
-
-    @VisibleForTesting
-    @CalledByNative
-    public static WebOTPServiceInfoBar create(
-            WindowAndroid windowAndroid,
-            int iconId,
-            String title,
-            String message,
-            String okButtonLabel) {
-        if (DEBUG) Log.d(TAG, "WebOTPServiceInfoBar.create()");
-        return new WebOTPServiceInfoBar(windowAndroid, iconId, title, message, okButtonLabel);
-    }
-
-    private WebOTPServiceInfoBar(
-            WindowAndroid windowAndroid,
-            int iconId,
-            String title,
-            String message,
-            String okButtonLabel) {
-        super(
-                iconId,
-                R.color.infobar_icon_drawable_color,
-                /* iconBitmap= */ null,
-                /* message= */ title,
-                /* linkText= */ null,
-                okButtonLabel,
-                /* secondaryButtonText= */ null);
-        mMessage = message;
-        mWindowAndroid = windowAndroid;
-    }
-
-    @Override
-    public int getPriority() {
-        return InfoBarPriority.USER_TRIGGERED;
-    }
-
-    @Override
-    public void createContent(InfoBarLayout layout) {
-        super.createContent(layout);
-        WebOTPServiceUma.recordInfobarAction(WebOTPServiceUma.InfobarAction.SHOWN);
-
-        Activity activity = mWindowAndroid.getActivity().get();
-        if (activity != null) {
-            View focusedView = activity.getCurrentFocus();
-            KeyboardVisibilityDelegate keyboardVisibilityDelegate =
-                    KeyboardVisibilityDelegate.getInstance();
-            if (focusedView != null && keyboardVisibilityDelegate.isKeyboardShowing(focusedView)) {
-                keyboardVisibilityDelegate.hideKeyboard(focusedView);
-                WebOTPServiceUma.recordInfobarAction(
-                        WebOTPServiceUma.InfobarAction.KEYBOARD_DISMISSED);
-                mKeyboardDismissedTime = SystemClock.uptimeMillis();
-            }
-        }
-
-        InfoBarControlLayout control = layout.addControlLayout();
-        control.addDescription(mMessage);
-    }
-
-    @Override
-    public void onCloseButtonClicked() {
-        super.onCloseButtonClicked();
-
-        if (mKeyboardDismissedTime != null) {
-            WebOTPServiceUma.recordCancelTimeAfterKeyboardDismissal(
-                    SystemClock.uptimeMillis() - mKeyboardDismissedTime);
-        }
-    }
-}
diff --git a/components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/WebOTPServiceUma.java b/components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/WebOTPServiceUma.java
deleted file mode 100644
index 982d018..0000000
--- a/components/browser_ui/sms/android/java/src/org/chromium/components/browser_ui/sms/WebOTPServiceUma.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.browser_ui.sms;
-
-import androidx.annotation.IntDef;
-
-import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.build.annotations.NullMarked;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/** Helper Class for WebOTP Service UMA Collection. */
-@NullMarked
-public final class WebOTPServiceUma {
-    // Note: these values must match the WebOTPServiceInfobar enum in enums.xml.
-    // Only add new values at the end, right before NUM_ENTRIES.
-    @IntDef({InfobarAction.SHOWN, InfobarAction.KEYBOARD_DISMISSED})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface InfobarAction {
-        int SHOWN = 0;
-        int KEYBOARD_DISMISSED = 1;
-        int NUM_ENTRIES = 2;
-    }
-
-    static void recordInfobarAction(int action) {
-        RecordHistogram.recordEnumeratedHistogram(
-                "Blink.Sms.Receive.Infobar", action, InfobarAction.NUM_ENTRIES);
-    }
-
-    static void recordCancelTimeAfterKeyboardDismissal(long durationMs) {
-        RecordHistogram.deprecatedRecordMediumTimesHistogram(
-                "Blink.Sms.Receive.TimeCancelOnKeyboardDismissal", durationMs);
-    }
-}
diff --git a/components/browser_ui/sms/android/sms_infobar.cc b/components/browser_ui/sms/android/sms_infobar.cc
deleted file mode 100644
index df746c2c..0000000
--- a/components/browser_ui/sms/android/sms_infobar.cc
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/browser_ui/sms/android/sms_infobar.h"
-
-#include "base/android/jni_string.h"
-#include "components/browser_ui/sms/android/sms_infobar_delegate.h"
-#include "content/public/browser/web_contents.h"
-#include "ui/android/window_android.h"
-#include "url/origin.h"
-
-// Must come after all headers that specialize FromJniType() / ToJniType().
-#include "components/browser_ui/sms/android/jni_headers/WebOTPServiceInfoBar_jni.h"
-
-using base::android::ConvertUTF16ToJavaString;
-using base::android::ScopedJavaLocalRef;
-using infobars::InfoBarDelegate;
-
-namespace sms {
-
-// static
-void SmsInfoBar::Create(content::WebContents* web_contents,
-                        infobars::InfoBarManager* manager,
-                        const std::vector<url::Origin>& origin_list,
-                        const std::string& one_time_code,
-                        base::OnceClosure on_confirm,
-                        base::OnceClosure on_cancel) {
-  auto delegate = std::make_unique<SmsInfoBarDelegate>(
-      origin_list, one_time_code, std::move(on_confirm), std::move(on_cancel));
-  auto infobar =
-      std::make_unique<SmsInfoBar>(web_contents, std::move(delegate));
-  manager->AddInfoBar(std::move(infobar));
-}
-
-SmsInfoBar::SmsInfoBar(content::WebContents* web_contents,
-                       std::unique_ptr<SmsInfoBarDelegate> delegate)
-    : infobars::ConfirmInfoBar(std::move(delegate)),
-      web_contents_(web_contents) {}
-
-SmsInfoBar::~SmsInfoBar() = default;
-
-ScopedJavaLocalRef<jobject> SmsInfoBar::CreateRenderInfoBar(
-    JNIEnv* env,
-    const ResourceIdMapper& resource_id_mapper) {
-  SmsInfoBarDelegate* delegate =
-      static_cast<SmsInfoBarDelegate*>(GetDelegate());
-
-  auto title = ConvertUTF16ToJavaString(env, delegate->GetTitle());
-  auto message = ConvertUTF16ToJavaString(env, delegate->GetMessageText());
-  auto button = ConvertUTF16ToJavaString(
-      env, GetTextFor(ConfirmInfoBarDelegate::BUTTON_OK));
-
-  base::android::ScopedJavaLocalRef<jobject> window_android =
-      web_contents_->GetNativeView()->GetWindowAndroid()->GetJavaObject();
-
-  return Java_WebOTPServiceInfoBar_create(
-      env, window_android, resource_id_mapper.Run(delegate->GetIconId()), title,
-      message, button);
-}
-
-}  // namespace sms
-
-DEFINE_JNI(WebOTPServiceInfoBar)
diff --git a/components/browser_ui/sms/android/sms_infobar.h b/components/browser_ui/sms/android/sms_infobar.h
deleted file mode 100644
index 0f08d1f..0000000
--- a/components/browser_ui/sms/android/sms_infobar.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_BROWSER_UI_SMS_ANDROID_SMS_INFOBAR_H_
-#define COMPONENTS_BROWSER_UI_SMS_ANDROID_SMS_INFOBAR_H_
-
-#include <memory>
-
-#include "base/functional/callback_forward.h"
-#include "base/memory/raw_ptr.h"
-#include "components/infobars/android/confirm_infobar.h"
-
-namespace content {
-class WebContents;
-}  // namespace content
-
-namespace url {
-class Origin;
-}  // namespace url
-
-namespace sms {
-
-class SmsInfoBarDelegate;
-
-class SmsInfoBar : public infobars::ConfirmInfoBar {
- public:
-  SmsInfoBar(content::WebContents* web_contents,
-             std::unique_ptr<SmsInfoBarDelegate> delegate);
-
-  SmsInfoBar(const SmsInfoBar&) = delete;
-  SmsInfoBar& operator=(const SmsInfoBar&) = delete;
-
-  ~SmsInfoBar() override;
-
-  // Creates an SMS receiver infobar and delegate and adds it to
-  // |infobar_manager|.
-  static void Create(content::WebContents* web_contents,
-                     infobars::InfoBarManager* manager,
-                     const std::vector<url::Origin>& origin_list,
-                     const std::string& one_time_code,
-                     base::OnceClosure on_confirm,
-                     base::OnceClosure on_cancel);
-
- private:
-  // ConfirmInfoBar:
-  base::android::ScopedJavaLocalRef<jobject> CreateRenderInfoBar(
-      JNIEnv* env,
-      const ResourceIdMapper& resource_id_mapper) override;
-
-  raw_ptr<content::WebContents> web_contents_;
-};
-
-}  // namespace sms
-
-#endif  // COMPONENTS_BROWSER_UI_SMS_ANDROID_SMS_INFOBAR_H_
diff --git a/components/browser_ui/sms/android/sms_infobar_delegate.cc b/components/browser_ui/sms/android/sms_infobar_delegate.cc
deleted file mode 100644
index 9c4b5d1e..0000000
--- a/components/browser_ui/sms/android/sms_infobar_delegate.cc
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/browser_ui/sms/android/sms_infobar_delegate.h"
-
-#include "base/check_op.h"
-#include "base/strings/utf_string_conversions.h"
-#include "build/build_config.h"
-#include "components/infobars/core/infobar.h"
-#include "components/resources/android/theme_resources.h"
-#include "components/strings/grit/components_strings.h"
-#include "components/url_formatter/elide_url.h"
-#include "content/public/browser/web_contents.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "url/origin.h"
-
-namespace sms {
-
-SmsInfoBarDelegate::SmsInfoBarDelegate(const OriginList& origin_list,
-                                       const std::string& one_time_code,
-                                       base::OnceClosure on_confirm,
-                                       base::OnceClosure on_cancel)
-    : ConfirmInfoBarDelegate(),
-      origin_list_(origin_list),
-      one_time_code_(one_time_code),
-      on_confirm_(std::move(on_confirm)),
-      on_cancel_(std::move(on_cancel)) {}
-
-SmsInfoBarDelegate::~SmsInfoBarDelegate() = default;
-
-infobars::InfoBarDelegate::InfoBarIdentifier SmsInfoBarDelegate::GetIdentifier()
-    const {
-  return WEBOTP_SERVICE_INFOBAR_DELEGATE;
-}
-
-int SmsInfoBarDelegate::GetIconId() const {
-  return IDR_ANDROID_INFOBAR_PHONE_ICON;
-}
-
-std::u16string SmsInfoBarDelegate::GetMessageText() const {
-  if (origin_list_.size() == 1) {
-    std::u16string origin = url_formatter::FormatOriginForSecurityDisplay(
-        origin_list_[0], url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS);
-    return l10n_util::GetStringFUTF16(IDS_SMS_INFOBAR_STATUS_SMS_RECEIVED,
-                                      base::UTF8ToUTF16(one_time_code_),
-                                      origin);
-  }
-
-  // Only one cross-origin iframe is allowed.
-  DCHECK_EQ(origin_list_.size(), 2u);
-
-  std::u16string embedded_origin =
-      url_formatter::FormatOriginForSecurityDisplay(
-          origin_list_[0], url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS);
-  std::u16string top_origin = url_formatter::FormatOriginForSecurityDisplay(
-      origin_list_[1], url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS);
-  return l10n_util::GetStringFUTF16(
-      IDS_SMS_INFOBAR_STATUS_SMS_RECEIVED_FROM_EMBEDDED_FRAME,
-      base::UTF8ToUTF16(one_time_code_), top_origin, embedded_origin);
-}
-
-int SmsInfoBarDelegate::GetButtons() const {
-  return BUTTON_OK;
-}
-
-std::u16string SmsInfoBarDelegate::GetButtonLabel(InfoBarButton button) const {
-  DCHECK_EQ(BUTTON_OK, button);
-  return l10n_util::GetStringUTF16(IDS_SMS_INFOBAR_BUTTON_OK);
-}
-
-bool SmsInfoBarDelegate::Accept() {
-  std::move(on_confirm_).Run();
-  return true;
-}
-
-void SmsInfoBarDelegate::InfoBarDismissed() {
-  std::move(on_cancel_).Run();
-}
-
-std::u16string SmsInfoBarDelegate::GetTitle() const {
-  return l10n_util::GetStringUTF16(IDS_SMS_INFOBAR_TITLE);
-}
-
-}  // namespace sms
diff --git a/components/browser_ui/sms/android/sms_infobar_delegate.h b/components/browser_ui/sms/android/sms_infobar_delegate.h
deleted file mode 100644
index aed535b..0000000
--- a/components/browser_ui/sms/android/sms_infobar_delegate.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_BROWSER_UI_SMS_ANDROID_SMS_INFOBAR_DELEGATE_H_
-#define COMPONENTS_BROWSER_UI_SMS_ANDROID_SMS_INFOBAR_DELEGATE_H_
-
-#include "base/functional/callback.h"
-#include "components/infobars/core/confirm_infobar_delegate.h"
-#include "url/origin.h"
-
-namespace sms {
-
-// This class configures an infobar shown when an SMS is received and the user
-// is asked for confirmation that it should be shared with the site (WebOTP).
-class SmsInfoBarDelegate : public ConfirmInfoBarDelegate {
- public:
-  using OriginList = std::vector<url::Origin>;
-
-  SmsInfoBarDelegate(const OriginList& origin_list,
-                     const std::string& one_time_code,
-                     base::OnceClosure on_confirm,
-                     base::OnceClosure on_cancel);
-
-  SmsInfoBarDelegate(const SmsInfoBarDelegate&) = delete;
-  SmsInfoBarDelegate& operator=(const SmsInfoBarDelegate&) = delete;
-
-  ~SmsInfoBarDelegate() override;
-
-  // ConfirmInfoBarDelegate:
-  infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
-  int GetIconId() const override;
-  std::u16string GetMessageText() const override;
-  int GetButtons() const override;
-  std::u16string GetButtonLabel(InfoBarButton button) const override;
-  bool Accept() override;
-  void InfoBarDismissed() override;
-
-  std::u16string GetTitle() const;
-
- private:
-  const OriginList origin_list_;
-  const std::string one_time_code_;
-  base::OnceClosure on_confirm_;
-  base::OnceClosure on_cancel_;
-};
-
-}  // namespace sms
-
-#endif  // COMPONENTS_BROWSER_UI_SMS_ANDROID_SMS_INFOBAR_DELEGATE_H_
diff --git a/components/browser_ui/sms/android/sms_infobar_delegate_unittest.cc b/components/browser_ui/sms/android/sms_infobar_delegate_unittest.cc
deleted file mode 100644
index a29995c..0000000
--- a/components/browser_ui/sms/android/sms_infobar_delegate_unittest.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/browser_ui/sms/android/sms_infobar_delegate.h"
-
-#include "base/functional/callback.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/bind.h"
-#include "components/browser_ui/sms/android/sms_infobar.h"
-#include "components/infobars/content/content_infobar_manager.h"
-#include "components/infobars/core/infobar.h"
-#include "content/public/test/test_renderer_host.h"
-
-namespace sms {
-
-class SmsInfoBarDelegateTest : public content::RenderViewHostTestHarness {
- public:
-  // content::RenderViewHostTestHarness:
-  void SetUp() override {
-    content::RenderViewHostTestHarness::SetUp();
-    infobar_manager_ =
-        std::make_unique<infobars::ContentInfoBarManager>(web_contents());
-  }
-
-  infobars::ContentInfoBarManager* infobar_manager() {
-    return infobar_manager_.get();
-  }
-
- private:
-  std::unique_ptr<infobars::ContentInfoBarManager> infobar_manager_;
-};
-
-TEST_F(SmsInfoBarDelegateTest, InfoBarForSingleFrame) {
-  std::string url = "https://example.com";
-  url::Origin origin = url::Origin::Create(GURL(url));
-  std::vector<url::Origin> origin_list{origin};
-  SmsInfoBar::Create(web_contents(), infobar_manager(), origin_list, "1234",
-                     base::OnceClosure(), base::OnceClosure());
-  EXPECT_EQ(infobar_manager()->infobars().size(), 1u);
-  std::string expected_message = "1234 is your code for example.com";
-
-  EXPECT_EQ(base::UTF16ToUTF8(infobar_manager()
-                                  ->infobars()[0]
-                                  ->delegate()
-                                  ->AsConfirmInfoBarDelegate()
-                                  ->GetMessageText()),
-            expected_message);
-}
-
-TEST_F(SmsInfoBarDelegateTest, InfoBarForEmbeddedFrame) {
-  std::string top_url = "https://top.com";
-  std::string embedded_url = "https://embedded.com";
-  url::Origin top_origin = url::Origin::Create(GURL(top_url));
-  url::Origin embedded_origin = url::Origin::Create(GURL(embedded_url));
-  std::vector<url::Origin> origin_list{embedded_origin, top_origin};
-  SmsInfoBar::Create(web_contents(), infobar_manager(), origin_list, "1234",
-                     base::OnceClosure(), base::OnceClosure());
-  EXPECT_EQ(infobar_manager()->infobars().size(), 1u);
-  std::string expected_message =
-      "1234 is your code for embedded.com to continue on top.com";
-  EXPECT_EQ(base::UTF16ToUTF8(infobar_manager()
-                                  ->infobars()[0]
-                                  ->delegate()
-                                  ->AsConfirmInfoBarDelegate()
-                                  ->GetMessageText()),
-            expected_message);
-}
-
-}  // namespace sms
diff --git a/components/contextual_search/BUILD.gn b/components/contextual_search/BUILD.gn
index 4aa740c2..2f85642 100644
--- a/components/contextual_search/BUILD.gn
+++ b/components/contextual_search/BUILD.gn
@@ -150,6 +150,7 @@
     "contextual_search_metrics_recorder_unittest.cc",
     "contextual_search_service_unittest.cc",
     "contextual_search_session_entry_unittest.cc",
+    "contextual_search_session_handle_unittest.cc",
     "input_state_model_unittest.cc",
   ]
 
diff --git a/components/contextual_search/contextual_search_session_handle.cc b/components/contextual_search/contextual_search_session_handle.cc
index de8a027c..9cdd163 100644
--- a/components/contextual_search/contextual_search_session_handle.cc
+++ b/components/contextual_search/contextual_search_session_handle.cc
@@ -168,7 +168,7 @@
     } else if (mime_type_has_image) {
       mime_type = lens::MimeType::kImage;
     } else {
-      NOTREACHED();
+      mime_type = lens::MimeType::kUnknown;
     }
   }
 
diff --git a/components/contextual_search/contextual_search_session_handle_unittest.cc b/components/contextual_search/contextual_search_session_handle_unittest.cc
new file mode 100644
index 0000000..832799c
--- /dev/null
+++ b/components/contextual_search/contextual_search_session_handle_unittest.cc
@@ -0,0 +1,73 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/contextual_search/contextual_search_session_handle.h"
+
+#include <memory>
+
+#include "base/test/scoped_feature_list.h"
+#include "base/unguessable_token.h"
+#include "components/contextual_search/contextual_search_metrics_recorder.h"
+#include "components/contextual_search/contextual_search_service.h"
+#include "components/contextual_search/mock_contextual_search_context_controller.h"
+#include "components/lens/contextual_input.h"
+#include "components/lens/lens_features.h"
+#include "components/prefs/testing_pref_service.h"
+#include "mojo/public/cpp/base/big_buffer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+
+namespace contextual_search {
+
+class ContextualSearchSessionHandleTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    auto mock_controller =
+        std::make_unique<MockContextualSearchContextController>();
+    mock_controller_ptr_ = mock_controller.get();
+
+    auto metrics_recorder = std::make_unique<ContextualSearchMetricsRecorder>(
+        ContextualSearchSource::kUnknown);
+
+    service_ = std::make_unique<ContextualSearchService>(
+        nullptr, nullptr, nullptr, nullptr, version_info::Channel::UNKNOWN, "");
+
+    handle_ = service_->CreateSessionForTesting(std::move(mock_controller),
+                                                std::move(metrics_recorder));
+
+    ContextualSearchService::RegisterProfilePrefs(prefs_.registry());
+    handle_->CheckSearchContentSharingSettings(&prefs_);
+  }
+
+  TestingPrefServiceSimple prefs_;
+  std::unique_ptr<ContextualSearchService> service_;
+  std::unique_ptr<ContextualSearchSessionHandle> handle_;
+  raw_ptr<MockContextualSearchContextController> mock_controller_ptr_;
+  base::test::ScopedFeatureList feature_list_;
+};
+
+TEST_F(ContextualSearchSessionHandleTest,
+       StartFileContextUploadFlow_FallbackToUnknown) {
+  // Ensure the feature is disabled.
+  feature_list_.InitAndDisableFeature(
+      lens::features::kLensSendRawFileMediaTypes);
+
+  base::UnguessableToken token = handle_->CreateContextToken();
+
+  // Expect StartFileUploadFlow to be called.
+  EXPECT_CALL(*mock_controller_ptr_, StartFileUploadFlow(token, _, _))
+      .WillOnce([](const base::UnguessableToken& file_token,
+                   std::unique_ptr<lens::ContextualInputData> input_data,
+                   std::optional<lens::ImageEncodingOptions> image_options) {
+        EXPECT_EQ(input_data->primary_content_type, lens::MimeType::kUnknown);
+      });
+
+  mojo_base::BigBuffer buffer;
+  handle_->StartFileContextUploadFlow(token, "test.txt", "text/plain",
+                                      std::move(buffer), std::nullopt);
+}
+
+}  // namespace contextual_search
diff --git a/components/cronet/android/test/cronet_test_util.cc b/components/cronet/android/test/cronet_test_util.cc
index cd2fdc52..e9129ff1 100644
--- a/components/cronet/android/test/cronet_test_util.cc
+++ b/components/cronet/android/test/cronet_test_util.cc
@@ -157,7 +157,10 @@
               .Build())
           .release();
   g_sequence_manager->SetDefaultTaskRunner(
-      TestUtil::GetTaskRunner(jcontext_adapter));
+      TestUtil::GetTaskRunner(jcontext_adapter),
+      static_cast<base::sequence_manager::TaskQueue::QueuePriority>(
+          base::sequence_manager::TaskQueue::DefaultQueuePriority::
+              kNormalPriority));
 }
 
 // Tests need to call into libcronet.so code on libcronet.so threads.
diff --git a/components/facilitated_payments/core/validation/pix_code_validator_unittest.cc b/components/facilitated_payments/core/validation/pix_code_validator_unittest.cc
index 048a02d..c6182f5 100644
--- a/components/facilitated_payments/core/validation/pix_code_validator_unittest.cc
+++ b/components/facilitated_payments/core/validation/pix_code_validator_unittest.cc
@@ -211,6 +211,15 @@
                            PixQrCodeResult::InvalidMerchantPresentedCode));
 }
 
+TEST_P(PixCodeValidatorTest, LengthWithLeadingPlus) {
+  // Code is invalid because the length of the static code section has a
+  // leading `+`. This is a regression test for the Rust validator, which uses
+  // `str::parse::<usize>()`, which allows a leading `+`.
+  EXPECT_TRUE(
+      CheckPixQrCodeResult("00020126270014br.gov.bcb.pix01+5ABCDE63041D3D",
+                           PixQrCodeResult::InvalidMerchantPresentedCode));
+}
+
 TEST(PixCodeValidatorCxxTest, ContainsPixCodeIdentifier) {
   constexpr char kPixCodeIndicatorLowercase[] = "0014br.gov.bcb.pix";
   EXPECT_TRUE(PixCodeValidator::ContainsPixIdentifier(
diff --git a/components/facilitated_payments/core/validation/pix_validator.rs b/components/facilitated_payments/core/validation/pix_validator.rs
index 0a95f67..22a1799 100644
--- a/components/facilitated_payments/core/validation/pix_validator.rs
+++ b/components/facilitated_payments/core/validation/pix_validator.rs
@@ -121,6 +121,9 @@
     //   99 characters.
     let (id, rest) = input.split_at_checked(2)?;
     let (length, rest) = rest.split_at_checked(2)?;
+    if length.starts_with('+') {
+        return None;
+    }
     let length: usize = length.parse().ok()?;
     let (value, rest) = rest.split_at_checked(length)?;
     Some((Section { id, value }, rest))
diff --git a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
index bac7504..ee62a0e 100644
--- a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
+++ b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
@@ -110,9 +110,9 @@
     /** The user clicked on the extensions menu button in the toolbar. */
     public static final String EXTENSIONS_MENU_BUTTON_CLICKED = "extensions_menu_button_clicked";
 
-    /** The user clicked on the extensions row in the Chrome main menu button. */
-    public static final String EXTENSIONS_ROW_IN_MAIN_MENU_CLICKED =
-            "extensions_row_in_main_menu_clicked";
+    /** The user clicked on the extensions row in the Chrome app menu button. */
+    public static final String EXTENSIONS_ROW_IN_APP_MENU_CLICKED =
+            "extensions_row_in_app_menu_clicked";
 
     /** The feed swipe refresh event. */
     public static final String FEED_SWIPE_REFRESHED = "feed_swipe_refresh_shown";
diff --git a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java
index 90f2124..2add80d 100644
--- a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java
+++ b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java
@@ -70,7 +70,7 @@
     FeatureConstants.GLIC_PROMO_ANDROID_FEATURE,
     FeatureConstants.IDENTITY_DISC_FEATURE,
     FeatureConstants.INSTANCE_SWITCHER,
-    FeatureConstants.IPH_EXTENSIONS_MANAGE_MAIN_MENU_FEATURE,
+    FeatureConstants.IPH_EXTENSIONS_MANAGE_APP_MENU_FEATURE,
     FeatureConstants.IPH_EXTENSIONS_MANAGE_TOOLBAR_FEATURE,
     FeatureConstants.IPH_MIC_TOOLBAR_FEATURE,
     FeatureConstants.IPH_PDF_PAGE_DOWNLOAD,
@@ -294,9 +294,9 @@
 
     /**
      * An IPH feature that shows after the extensions menu is uninstalled to inform users to manage
-     * their extensions in the main menu.
+     * their extensions in the app menu.
      */
-    String IPH_EXTENSIONS_MANAGE_MAIN_MENU_FEATURE = "IPH_ExtensionsManageMainMenu";
+    String IPH_EXTENSIONS_MANAGE_APP_MENU_FEATURE = "IPH_ExtensionsManageAppMenu";
 
     /**
      * An IPH feature that shows after the first extension is installed to inform users to manage
diff --git a/components/feature_engagement/public/feature_configurations.cc b/components/feature_engagement/public/feature_configurations.cc
index ee8d0b2..d468067 100644
--- a/components/feature_engagement/public/feature_configurations.cc
+++ b/components/feature_engagement/public/feature_configurations.cc
@@ -753,12 +753,12 @@
     return config;
   }
 
-  if (kIPHExtensionsManageMainMenuFeature.name == feature->name) {
+  if (kIPHExtensionsManageAppMenuFeature.name == feature->name) {
     // Allows an IPH to be shown after a user unpins the extensions menu to
     // inform them where it can be managed.
     // Constraints:
     // - Show at most once per year (360 days).
-    // - Only show if the user hasn't already clicked open the Chrome main menu
+    // - Only show if the user hasn't already clicked open the Chrome app menu
     //   button on their own.
     // - session_rate is set to EQUAL, 0 to ensure we don't show this if another
     //   IPH was already shown in the same session.
@@ -766,11 +766,11 @@
     config->valid = true;
     config->availability = Comparator(ANY, 0);
     config->session_rate = Comparator(EQUAL, 0);
-    config->trigger = EventConfig("manage_extensions_main_menu_iph_triggered",
+    config->trigger = EventConfig("manage_extensions_app_menu_iph_triggered",
                                   Comparator(LESS_THAN, 1), 360, 360);
     // Only show if the user hasn't already clicked the "Extensions" section in
-    // the Chrome main menu.
-    config->used = EventConfig("extensions_row_in_main_menu_clicked",
+    // the Chrome app menu.
+    config->used = EventConfig("extensions_row_in_app_menu_clicked",
                                Comparator(EQUAL, 0), 360, 360);
     return config;
   }
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc
index 49b9350..c15f6f9 100644
--- a/components/feature_engagement/public/feature_constants.cc
+++ b/components/feature_engagement/public/feature_constants.cc
@@ -232,6 +232,9 @@
 BASE_FEATURE(kIPHSideBySideTabSwitchFeature,
              "IPH_SideBySideTabSwitchFeature",
              base::FEATURE_ENABLED_BY_DEFAULT);
+BASE_FEATURE(kIPHVerticalTabsExpandOnHoverFeature,
+             "IPH_VerticalTabsExpandOnHoverFeature",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 BASE_FEATURE(kIPHVerticalTabstripTutorialFeature,
              "IPH_VerticalTabstripTutorialFeature",
              base::FEATURE_DISABLED_BY_DEFAULT);
@@ -414,8 +417,8 @@
 BASE_FEATURE(kIPHExtensionsManageToolbarFeature,
              "IPH_ExtensionsManageToolbar",
              base::FEATURE_ENABLED_BY_DEFAULT);
-BASE_FEATURE(kIPHExtensionsManageMainMenuFeature,
-             "IPH_ExtensionsManageMainMenu",
+BASE_FEATURE(kIPHExtensionsManageAppMenuFeature,
+             "IPH_ExtensionsManageAppMenu",
              base::FEATURE_ENABLED_BY_DEFAULT);
 BASE_FEATURE(kIPHReadAloudAppMenuFeature,
              "IPH_ReadAloudAppMenuFeature",
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h
index e2f279c..285e920 100644
--- a/components/feature_engagement/public/feature_constants.h
+++ b/components/feature_engagement/public/feature_constants.h
@@ -130,6 +130,7 @@
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHShoppingCollectionFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHSideBySidePinnableFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHSideBySideTabSwitchFeature);
+FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHVerticalTabsExpandOnHoverFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHVerticalTabstripTutorialFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHSidePanelGenericPinnableFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHSidePanelLensOverlayPinnableFeature);
@@ -202,7 +203,7 @@
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHDownloadSettingsFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHEphemeralTabFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHExploreSitesTileFeature);
-FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHExtensionsManageMainMenuFeature);
+FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHExtensionsManageAppMenuFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHExtensionsManageToolbarFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHFeedCardMenuFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHFeedHeaderMenuFeature);
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc
index 1d02c729..7cd469b 100644
--- a/components/feature_engagement/public/feature_list.cc
+++ b/components/feature_engagement/public/feature_list.cc
@@ -62,7 +62,7 @@
     &kIPHDownloadSettingsFeature,
     &kIPHEphemeralTabFeature,
     &kIPHExploreSitesTileFeature,
-    &kIPHExtensionsManageMainMenuFeature,
+    &kIPHExtensionsManageAppMenuFeature,
     &kIPHExtensionsManageToolbarFeature,
     &kIPHFeedCardMenuFeature,
     &kIPHFeedHeaderMenuFeature,
@@ -312,6 +312,7 @@
     &kIPHTabGroupsSharedTabFeedbackFeature,
     &kIPHTabSearchComboButtonFeature,
     &kIPHTabSearchToolbarButtonFeature,
+    &kIPHVerticalTabsExpandOnHoverFeature,
     &kIPHVerticalTabstripTutorialFeature,
     &kIPHWebUiHelpBubbleTestFeature,
 // keep-sorted end
diff --git a/components/feature_engagement/public/feature_list.h b/components/feature_engagement/public/feature_list.h
index 98039ee..10a93c6 100644
--- a/components/feature_engagement/public/feature_list.h
+++ b/components/feature_engagement/public/feature_list.h
@@ -121,8 +121,8 @@
 DEFINE_VARIATION_PARAM(kIPHDownloadInfoBarDownloadsAreFasterFeature,
                        "IPH_DownloadInfoBarDownloadsAreFaster");
 DEFINE_VARIATION_PARAM(kIPHEphemeralTabFeature, "IPH_EphemeralTab");
-DEFINE_VARIATION_PARAM(kIPHExtensionsManageMainMenuFeature,
-                       "IPH_ExtensionsManageMainMenu");
+DEFINE_VARIATION_PARAM(kIPHExtensionsManageAppMenuFeature,
+                       "IPH_ExtensionsManageAppMenu");
 DEFINE_VARIATION_PARAM(kIPHExtensionsManageToolbarFeature,
                        "IPH_ExtensionsManageToolbar");
 DEFINE_VARIATION_PARAM(kIPHFeedCardMenuFeature, "IPH_FeedCardMenu");
@@ -483,6 +483,8 @@
 DEFINE_VARIATION_PARAM(kIPHSideSearchPageActionLabelFeature,
                        "IPH_SideSearchPageActionLabel");
 
+DEFINE_VARIATION_PARAM(kIPHVerticalTabsExpandOnHoverFeature,
+                       "IPH_VerticalTabsExpandOnHoverFeature");
 DEFINE_VARIATION_PARAM(kIPHVerticalTabstripTutorialFeature,
                        "IPH_VerticalTabstripTutorialFeature");
 
@@ -635,7 +637,7 @@
         VARIATION_ENTRY(kIPHDownloadSettingsFeature),
         VARIATION_ENTRY(kIPHEphemeralTabFeature),
         VARIATION_ENTRY(kIPHExploreSitesTileFeature),
-        VARIATION_ENTRY(kIPHExtensionsManageMainMenuFeature),
+        VARIATION_ENTRY(kIPHExtensionsManageAppMenuFeature),
         VARIATION_ENTRY(kIPHExtensionsManageToolbarFeature),
         VARIATION_ENTRY(kIPHFeedCardMenuFeature),
         VARIATION_ENTRY(kIPHFeedHeaderMenuFeature),
diff --git a/components/guest_view/browser/slim_web_view/BUILD.gn b/components/guest_view/browser/slim_web_view/BUILD.gn
index b76e01c..948c2df0 100644
--- a/components/guest_view/browser/slim_web_view/BUILD.gn
+++ b/components/guest_view/browser/slim_web_view/BUILD.gn
@@ -29,11 +29,11 @@
     ":strings",
     "//base",
     "//components/guest_view/browser",
-    "//components/url_pattern",
     "//content/public/browser",
     "//content/public/common",
     "//net",
     "//third_party/blink/public:blink_headers",
+    "//url",
   ]
 }
 
diff --git a/components/guest_view/browser/slim_web_view/slim_web_view_guest.cc b/components/guest_view/browser/slim_web_view/slim_web_view_guest.cc
index e89e5e7..cb43029 100644
--- a/components/guest_view/browser/slim_web_view/slim_web_view_guest.cc
+++ b/components/guest_view/browser/slim_web_view/slim_web_view_guest.cc
@@ -19,7 +19,6 @@
 #include "components/guest_view/browser/slim_web_view/grit/slim_web_view_strings.h"
 #include "components/guest_view/browser/slim_web_view/request_utils.h"
 #include "components/guest_view/browser/slim_web_view/slim_web_view_constants.h"
-#include "components/url_pattern/simple_url_pattern_matcher.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_process_host.h"
@@ -30,6 +29,7 @@
 #include "net/http/http_util.h"
 #include "third_party/blink/public/mojom/window_features/window_features.mojom.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 #include "url/url_constants.h"
 
 namespace guest_view {
@@ -215,22 +215,21 @@
 void SlimWebViewGuest::Navigate(const GURL& url) {
   TRACE_EVENT_INSTANT("content", "SlimWebViewGuest::Navigate",
                       perfetto::Flow::FromPointer(this));
-  // TODO(acondor): Implement other security and navigation params, such as
-  // header overrides.
   content::NavigationController::LoadURLParams load_url_params(url);
   GetController().LoadURLWithParams(load_url_params);
 }
 
 bool SlimWebViewGuest::HasAllowedOrigins() const {
-  return !allowed_origin_matchers_.empty();
+  return !allowed_origins_.empty();
 }
 
 bool SlimWebViewGuest::IsUrlAllowed(const GURL& url) const {
-  if (allowed_origin_matchers_.empty()) {
+  if (allowed_origins_.empty()) {
     return true;
   }
-  for (const auto& matcher : allowed_origin_matchers_) {
-    if (matcher->Match(url)) {
+  url::Origin candidate_origin = url::Origin::Create(url);
+  for (const auto& origin : allowed_origins_) {
+    if (origin.IsSameOriginWith(candidate_origin)) {
       return true;
     }
   }
@@ -462,13 +461,12 @@
         RejectGuestCreation(std::move(owned_this), std::move(callback));
         return;
       }
-      auto matcher = url_pattern::SimpleUrlPatternMatcher::Create(
-          origin_value.GetString(), /*base_url=*/nullptr);
-      if (!matcher.has_value()) {
+      GURL allowed_origin_url(origin_value.GetString());
+      if (!allowed_origin_url.is_valid()) {
         RejectGuestCreation(std::move(owned_this), std::move(callback));
         return;
       }
-      allowed_origin_matchers_.push_back(std::move(matcher.value()));
+      allowed_origins_.push_back(url::Origin::Create(allowed_origin_url));
     }
   }
 
diff --git a/components/guest_view/browser/slim_web_view/slim_web_view_guest.h b/components/guest_view/browser/slim_web_view/slim_web_view_guest.h
index f3abb81..c83765d 100644
--- a/components/guest_view/browser/slim_web_view/slim_web_view_guest.h
+++ b/components/guest_view/browser/slim_web_view/slim_web_view_guest.h
@@ -15,13 +15,10 @@
 #include "components/guest_view/browser/slim_web_view/request_utils.h"
 #include "components/guest_view/browser/slim_web_view/slim_web_view_permission_helper.h"
 #include "net/base/net_errors.h"
+#include "url/origin.h"
 
 class GURL;
 
-namespace url_pattern {
-class SimpleUrlPatternMatcher;
-}
-
 namespace guest_view {
 
 // A minimal implementation of GuestView for embedding web content in Chrome.
@@ -126,8 +123,7 @@
 
   SlimWebViewPermissionHelper permission_helper_{this};
 
-  std::vector<std::unique_ptr<url_pattern::SimpleUrlPatternMatcher>>
-      allowed_origin_matchers_;
+  std::vector<url::Origin> allowed_origins_;
 
   base::WeakPtrFactory<SlimWebViewGuest> weak_ptr_factory_{this};
 };
diff --git a/components/infobars/core/infobar_delegate.h b/components/infobars/core/infobar_delegate.h
index 5fe307d..7cfb5a0 100644
--- a/components/infobars/core/infobar_delegate.h
+++ b/components/infobars/core/infobar_delegate.h
@@ -176,7 +176,7 @@
     SEND_TAB_TO_SELF_INFOBAR_DELEGATE = 92,
     TAB_SHARING_INFOBAR_DELEGATE = 93,
     // Removed SAFETY_TIP_INFOBAR_DELEGATE = 94,
-    WEBOTP_SERVICE_INFOBAR_DELEGATE = 95,
+    // Removed: WEBOTP_SERVICE_INFOBAR_DELEGATE = 95,
     KNOWN_INTERCEPTION_DISCLOSURE_INFOBAR_DELEGATE = 96,
     // Removed: SYNC_ERROR_INFOBAR_DELEGATE_ANDROID = 97,
     // Removed: INSECURE_DOWNLOAD_INFOBAR_DELEGATE_ANDROID = 98,
diff --git a/components/live_caption/BUILD.gn b/components/live_caption/BUILD.gn
index 331b4d54..37bbabe0 100644
--- a/components/live_caption/BUILD.gn
+++ b/components/live_caption/BUILD.gn
@@ -57,7 +57,6 @@
 
       deps += [
         "//components/language/core/common",
-        "//components/on_device_translation/buildflags",
         "//components/strings",
         "//components/translate/core/browser",
         "//components/vector_icons",
@@ -70,10 +69,6 @@
         "//ui/strings:ui_strings_grit",
         "//ui/views",
       ]
-
-      if (enable_on_device_translation) {
-        deps += [ "//components/on_device_translation:installer" ]
-      }
     }  # toolkit_views
   }
 
diff --git a/components/live_caption/views/caption_bubble_controller_views.cc b/components/live_caption/views/caption_bubble_controller_views.cc
index de60a753..3eec982b 100644
--- a/components/live_caption/views/caption_bubble_controller_views.cc
+++ b/components/live_caption/views/caption_bubble_controller_views.cc
@@ -11,7 +11,6 @@
 
 #include "base/functional/bind.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/live_caption/caption_bubble_context.h"
 #include "components/live_caption/caption_bubble_settings.h"
@@ -25,11 +24,6 @@
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
-#if BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-#include "components/on_device_translation/public/language_pack.h"
-#include "components/on_device_translation/public/supported_languages.h"
-#endif
-
 namespace captions {
 
 // Static
@@ -62,20 +56,11 @@
     soda_installer->AddObserver(this);
   }
 #endif  // BUILDFLAG(IS_CHROMEOS)
-
-#if BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-  auto* translation_installer =
-      on_device_translation::OnDeviceTranslationInstaller::GetInstance();
-  if (translation_installer) {
-    translation_installer->AddObserver(this);
-  }
-#endif  // BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
 }
 
 CaptionBubbleControllerViews::~CaptionBubbleControllerViews() {
-  if (caption_widget_) {
+  if (caption_widget_)
     caption_widget_->CloseNow();
-  }
 
 #if !BUILDFLAG(IS_CHROMEOS)
   speech::SodaInstaller* soda_installer = speech::SodaInstaller::GetInstance();
@@ -86,14 +71,6 @@
     soda_installer->RemoveObserver(this);
   }
 #endif  // BUILDFLAG(IS_CHROMEOS)
-
-#if BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-  auto* translation_installer =
-      on_device_translation::OnDeviceTranslationInstaller::GetInstance();
-  if (translation_installer) {
-    translation_installer->RemoveObserver(this);
-  }
-#endif  // BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
 }
 
 void CaptionBubbleControllerViews::OnCaptionBubbleDestroyed() {
@@ -105,18 +82,15 @@
     content::RenderFrameHost* rfh,
     CaptionBubbleContext* caption_bubble_context,
     const media::SpeechRecognitionResult& result) {
-  if (!caption_bubble_) {
+  if (!caption_bubble_)
     return false;
-  }
   SetActiveModel(caption_bubble_context);
-  if (active_model_->IsClosed()) {
+  if (active_model_->IsClosed())
     return false;
-  }
 
   active_model_->SetPartialText(result.transcription);
-  if (result.is_final) {
+  if (result.is_final)
     active_model_->CommitPartialText();
-  }
 
   return true;
 }
@@ -126,13 +100,11 @@
     CaptionBubbleErrorType error_type,
     OnErrorClickedCallback error_clicked_callback,
     OnDoNotShowAgainClickedCallback error_silenced_callback) {
-  if (!caption_bubble_) {
+  if (!caption_bubble_)
     return;
-  }
   SetActiveModel(caption_bubble_context);
-  if (active_model_->IsClosed()) {
+  if (active_model_->IsClosed())
     return;
-  }
   active_model_->OnError(error_type, std::move(error_clicked_callback),
                          std::move(error_silenced_callback));
 }
@@ -140,9 +112,8 @@
 void CaptionBubbleControllerViews::OnAudioStreamEnd(
     content::RenderFrameHost* rfh,
     CaptionBubbleContext* caption_bubble_context) {
-  if (!caption_bubble_) {
+  if (!caption_bubble_)
     return;
-  }
 
   auto caption_bubble_model_it =
       caption_bubble_models_.find(caption_bubble_context);
@@ -246,9 +217,8 @@
 }
 
 void CaptionBubbleControllerViews::CloseActiveModelForTesting() {
-  if (active_model_) {
+  if (active_model_)
     active_model_->Close();
-  }
 }
 
 views::Widget* CaptionBubbleControllerViews::GetCaptionWidgetForTesting() {
@@ -279,12 +249,8 @@
 
 void CaptionBubbleControllerViews::OnSodaInstalled(
     speech::LanguageCode language_code) {
-  if (language_code != speech::LanguageCode::kNone) {
-    soda_progress_ = std::nullopt;
-    soda_language_code_ = speech::LanguageCode::kNone;
-    if (active_model_) {
-      UpdateCombinedProgressText();
-    }
+  if (active_model_ && language_code != speech::LanguageCode::kNone) {
+    active_model_->OnLanguagePackInstalled();
   }
 }
 
@@ -302,130 +268,12 @@
 void CaptionBubbleControllerViews::OnSodaProgress(
     speech::LanguageCode language_code,
     int progress) {
-  if (language_code != speech::LanguageCode::kNone) {
-    soda_progress_ = progress;
-    soda_language_code_ = language_code;
-    UpdateCombinedProgressText();
-  }
-}
-
-#if BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-void CaptionBubbleControllerViews::OnLanguagePackProgress(
-    const on_device_translation::LanguagePackKey lang_pack,
-    int progress) {
-  translation_progress_[lang_pack] = progress;
-  UpdateCombinedProgressText();
-}
-
-void CaptionBubbleControllerViews::OnLanguagePackInstalled(
-    const on_device_translation::LanguagePackKey lang_pack) {
-  translation_progress_.erase(lang_pack);
-  UpdateCombinedProgressText();
-}
-
-void CaptionBubbleControllerViews::OnLanguagePackInstallationChanged(
-    const on_device_translation::LanguagePackKey lang_pack) {
-  auto* installer =
-      on_device_translation::OnDeviceTranslationInstaller::GetInstance();
-  if (installer && !installer->RegisteredLanguagePacks().contains(lang_pack) &&
-      !installer->InstalledLanguagePacks().contains(lang_pack)) {
-    translation_progress_.erase(lang_pack);
-    UpdateCombinedProgressText();
-  }
-}
-#endif  // BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-
-void CaptionBubbleControllerViews::UpdateCombinedProgressText() {
-  if (!active_model_) {
-    return;
-  }
-
-  bool soda_is_downloading = soda_progress_ && *soda_progress_ < 100;
-  bool soda_is_installing = soda_progress_ && *soda_progress_ >= 100;
-  int downloading_translation_count = 0;
-  int installing_count = soda_is_installing ? 1 : 0;
-  int sum_progress = 0;
-
-  if (soda_is_downloading) {
-    sum_progress += *soda_progress_;
-  }
-
-#if BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-  for (const auto& [lang_pack, progress] : translation_progress_) {
-    if (progress < 100) {
-      downloading_translation_count++;
-      sum_progress += progress;
-    } else {
-      installing_count++;
-    }
-  }
-#endif
-
-  int total_downloading =
-      (soda_is_downloading ? 1 : 0) + downloading_translation_count;
-
-  if (total_downloading > 1) {
-    int average_progress = sum_progress / total_downloading;
+  if (active_model_ && language_code != speech::LanguageCode::kNone) {
     active_model_->SetDownloadProgressText(l10n_util::GetStringFUTF16(
-        IDS_LIVE_CAPTION_TRANSLATION_DOWNLOADING_LANGUAGE_PACKAGES_WITH_PROGRESS,
-        base::UTF8ToUTF16(base::NumberToString(average_progress))));
-    return;
-  }
-
-  std::vector<std::u16string> parts;
-
-  if (soda_is_downloading) {
-    std::u16string language_name = speech::GetLanguageDisplayName(
-        speech::GetLanguageName(soda_language_code_), application_locale_);
-    parts.push_back(l10n_util::GetStringFUTF16(
-        IDS_LIVE_CAPTION_DOWNLOAD_PROGRESS, language_name,
-        base::UTF8ToUTF16(base::NumberToString(*soda_progress_))));
-  }
-
-  std::u16string installing_language_name;
-  if (soda_is_installing) {
-    installing_language_name = speech::GetLanguageDisplayName(
-        speech::GetLanguageName(soda_language_code_), application_locale_);
-  }
-
-#if BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-  for (const auto& [lang_pack, progress] : translation_progress_) {
-    if (progress < 0) {
-      continue;
-    }
-
-    auto get_language_name = [&]() {
-      return l10n_util::GetDisplayNameForLocale(
-          std::string(on_device_translation::ToLanguageCode(
-              on_device_translation::
-                  NonEnglishSupportedLanguageFromLanguagePackKey(lang_pack))),
-          application_locale_, /*is_for_ui=*/true);
-    };
-
-    if (progress < 100) {
-      parts.push_back(l10n_util::GetStringFUTF16(
-          IDS_LIVE_CAPTION_DOWNLOAD_PROGRESS, get_language_name(),
-          base::UTF8ToUTF16(base::NumberToString(progress))));
-    } else {
-      installing_language_name = get_language_name();
-    }
-  }
-#endif  // BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-
-  if (total_downloading == 0 && installing_count > 0) {
-    if (installing_count > 1) {
-      parts.push_back(l10n_util::GetStringUTF16(
-          IDS_LIVE_CAPTION_INSTALLING_LANGUAGE_PACKAGES));
-    } else {
-      parts.push_back(l10n_util::GetStringFUTF16(IDS_LIVE_CAPTION_INSTALLING,
-                                                 installing_language_name));
-    }
-  }
-
-  if (parts.empty()) {
-    active_model_->OnLanguagePackInstalled();
-  } else {
-    active_model_->SetDownloadProgressText(base::JoinString(parts, u", "));
+        IDS_LIVE_CAPTION_DOWNLOAD_PROGRESS,
+        speech::GetLanguageDisplayName(speech::GetLanguageName(language_code),
+                                       application_locale_),
+        base::UTF8ToUTF16(base::NumberToString(progress))));
   }
 }
 
diff --git a/components/live_caption/views/caption_bubble_controller_views.h b/components/live_caption/views/caption_bubble_controller_views.h
index 88c095bf..853f81a1 100644
--- a/components/live_caption/views/caption_bubble_controller_views.h
+++ b/components/live_caption/views/caption_bubble_controller_views.h
@@ -6,24 +6,17 @@
 #define COMPONENTS_LIVE_CAPTION_VIEWS_CAPTION_BUBBLE_CONTROLLER_VIEWS_H_
 
 #include <memory>
-#include <optional>
 #include <set>
 #include <string>
 #include <unordered_map>
 
-#include "base/containers/flat_map.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "components/live_caption/caption_bubble_controller.h"
 #include "components/live_caption/views/caption_bubble.h"
-#include "components/on_device_translation/buildflags/buildflags.h"
 #include "components/soda/soda_installer.h"
 #include "media/mojo/mojom/speech_recognition.mojom.h"
 
-#if BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-#include "components/on_device_translation/installer.h"
-#endif
-
 namespace views {
 class Widget;
 }
@@ -41,14 +34,8 @@
 //
 //  The implementation of the caption bubble controller for Views.
 //
-class CaptionBubbleControllerViews
-    : public CaptionBubbleController,
-      public speech::SodaInstaller::Observer
-#if BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-    ,
-      public on_device_translation::OnDeviceTranslationInstaller::Observer
-#endif
-{
+class CaptionBubbleControllerViews : public CaptionBubbleController,
+                                     public speech::SodaInstaller::Observer {
  public:
   CaptionBubbleControllerViews(
       CaptionBubbleSettings* caption_bubble_settings,
@@ -103,18 +90,6 @@
   void OnSodaProgress(speech::LanguageCode language_code,
                       int progress) override;
 
-#if BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-  // OnDeviceTranslationInstaller::Observer overrides:
-  void OnLanguagePackInstalled(
-      const on_device_translation::LanguagePackKey lang_pack) override;
-  void OnLanguagePackInstallationChanged(
-      const on_device_translation::LanguagePackKey lang_pack) override;
-  void OnInstallationChanged() override {}
-  void OnLanguagePackProgress(
-      const on_device_translation::LanguagePackKey lang_pack,
-      int progress) override;
-#endif
-
   // A callback passed to the CaptionBubble which is called when the
   // CaptionBubble is destroyed.
   void OnCaptionBubbleDestroyed();
@@ -154,15 +129,6 @@
 
   std::string application_locale_;
 
-  std::optional<int> soda_progress_;
-  speech::LanguageCode soda_language_code_ = speech::LanguageCode::kNone;
-#if BUILDFLAG(ENABLE_ON_DEVICE_TRANSLATION)
-  base::flat_map<on_device_translation::LanguagePackKey, int>
-      translation_progress_;
-#endif
-
-  void UpdateCombinedProgressText();
-
   base::WeakPtrFactory<CaptionBubbleControllerViews> weak_factory_{this};
 };
 }  // namespace captions
diff --git a/components/live_caption_strings.grdp b/components/live_caption_strings.grdp
index 9628dc4..a5ac16e 100644
--- a/components/live_caption_strings.grdp
+++ b/components/live_caption_strings.grdp
@@ -58,15 +58,6 @@
   <message name="IDS_LIVE_CAPTION_DOWNLOAD_PROGRESS" desc="Download progress indicator after Live Caption is enabled. The user needs to download certain files for the feature to work.">
    Downloading <ph name="LANGUAGE">$1<ex>Spanish</ex></ph> language pack... <ph name="PERCENT">$2<ex>17</ex></ph>%
   </message>
-  <message name="IDS_LIVE_CAPTION_TRANSLATION_DOWNLOADING_LANGUAGE_PACKAGES_WITH_PROGRESS" desc="Progress indicator when multiple language or translation packages are downloading.">
-   Downloading language packages... <ph name="PERCENT">$1<ex>30</ex></ph>%
-  </message>
-  <message name="IDS_LIVE_CAPTION_INSTALLING" desc="Progress indicator when the language pack is installing.">
-   Installing <ph name="LANGUAGE">$1<ex>Spanish</ex></ph>...
-  </message>
-  <message name="IDS_LIVE_CAPTION_INSTALLING_LANGUAGE_PACKAGES" desc="Progress indicator when multiple language packages are installing.">
-   Installing language packages...
-  </message>
   <message name="IDS_LIVE_CAPTION_LANGUAGE_DOWNLOAD_FAILED" desc="TODO.">
    Failed to download <ph name="LANGUAGE">$1<ex>Spanish</ex></ph>
   </message>
diff --git a/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_INSTALLING.png.sha1 b/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_INSTALLING.png.sha1
deleted file mode 100644
index 33888f0..0000000
--- a/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_INSTALLING.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-c552731a546e8a7d628da2b31e956bb086bb898b
\ No newline at end of file
diff --git a/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_INSTALLING_LANGUAGE_PACKAGES.png.sha1 b/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_INSTALLING_LANGUAGE_PACKAGES.png.sha1
deleted file mode 100644
index 82b2ecad..0000000
--- a/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_INSTALLING_LANGUAGE_PACKAGES.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-e771c0ded28bb241ff80d12e9587ceddf2cfdf82
\ No newline at end of file
diff --git a/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_TRANSLATION_DOWNLOADING_LANGUAGE_PACKAGES_WITH_PROGRESS.png.sha1 b/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_TRANSLATION_DOWNLOADING_LANGUAGE_PACKAGES_WITH_PROGRESS.png.sha1
deleted file mode 100644
index 5a069ae..0000000
--- a/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_TRANSLATION_DOWNLOADING_LANGUAGE_PACKAGES_WITH_PROGRESS.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-32f9621a282de5a8cf0ccf86a6fffe22669fa904
\ No newline at end of file
diff --git a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteInput.java b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteInput.java
index b6ca9df..74ad22c 100644
--- a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteInput.java
+++ b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteInput.java
@@ -313,7 +313,7 @@
     }
 
     /**
-     * Set the Initial Input - the one the session started with.
+     * Set the Initial Input - the default value to fall back to if the input is reset.
      *
      * <p>This is the default "revert-to" value.
      */
@@ -322,6 +322,7 @@
         return this;
     }
 
+    /** Returns the Initial Input - the default value to fall back to if the input is reset. */
     public String getInitialUserText() {
         return mInitialUserText;
     }
@@ -396,9 +397,10 @@
         return mAllowUserTextAutocompletion;
     }
 
-    /** Returns whether current context represents zero-prefix context. */
+    /** Returns whether the current context includes user-typed text. */
     public boolean isInZeroPrefixContext() {
-        return TextUtils.isEmpty(mUserText.get());
+        return TextUtils.isEmpty(mUserText.get())
+                || TextUtils.equals(mUserText.get(), mInitialUserText);
     }
 
     /** Returns whether current context enables suggestions caching. */
diff --git a/components/omnibox/browser/searchbox.mojom b/components/omnibox/browser/searchbox.mojom
index 815c109..e6625028 100644
--- a/components/omnibox/browser/searchbox.mojom
+++ b/components/omnibox/browser/searchbox.mojom
@@ -239,13 +239,17 @@
   mojo_base.mojom.TimeDelta fade_text_animation_duration;
 };
 
+// Used by the WebUI page to bootstrap bidirectional communication.
+interface PageHandlerFactory {
+  // The SearchboxBrowserProxy singleton calls this when it's first initialized.
+  CreatePageHandler(pending_remote<Page> page,
+                    pending_receiver<PageHandler> handler);
+};
+
 // Browser-side handler for requests from WebUI page. The web pages that embed
 // searchboxes which communicate with this interface are chrome://new-tab-page
 // and two chrome-untrusted://lens pages.
 interface PageHandler {
-  // The SearchboxBrowserProxy singleton calls this when it's first initialized.
-  SetPage(pending_remote<Page> page);
-
   // Informs the handler and model about focus state changes.
   OnFocusChanged(bool focused);
 
diff --git a/components/on_device_translation/BUILD.gn b/components/on_device_translation/BUILD.gn
index 55f37a0..ae7148f 100644
--- a/components/on_device_translation/BUILD.gn
+++ b/components/on_device_translation/BUILD.gn
@@ -31,21 +31,14 @@
   ]
 }
 
-source_set("installer") {
-  sources = [
-    "installer.cc",
-    "installer.h",
-  ]
-  public_deps = [ ":public" ]
-  deps = [ "//base" ]
-}
-
 source_set("on_device_translation") {
   sources = [
     "component_manager.cc",
     "component_manager.h",
     "file_operation_proxy_impl.cc",
     "file_operation_proxy_impl.h",
+    "installer.cc",
+    "installer.h",
     "service_controller.cc",
     "service_controller.h",
     "service_controller_manager.cc",
@@ -57,7 +50,6 @@
   ]
   public_deps = [
     ":features",
-    ":installer",
     ":metrics",
     ":public",
     "//components/prefs",
diff --git a/components/on_device_translation/test/fake_installer.h b/components/on_device_translation/test/fake_installer.h
index ebfa9b6..b0ebce4a 100644
--- a/components/on_device_translation/test/fake_installer.h
+++ b/components/on_device_translation/test/fake_installer.h
@@ -28,16 +28,16 @@
       LanguagePackKey language_pack) const override;
 
   void Init(base::RepeatingClosure on_ready_callback) override;
+  // Forces initialization to happen right away.
+  void InitNow(base::RepeatingClosure on_ready_callback);
   void InstallLanguagePack(LanguagePackKey language_pack) override;
+  // Forces installation to happen right away.
+  void InstallLanguagePackNow(LanguagePackKey language_pack);
   void UnInstallLanguagePack(LanguagePackKey language_pack) override;
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
 
  private:
-  // Forces initialization to happen right away.
-  void InitNow(base::RepeatingClosure on_ready_callback);
-  // Forces installation to happen right away.
-  void InstallLanguagePackNow(LanguagePackKey language_pack);
   base::FilePath fake_install_dir_;
   bool is_init_ = false;
   std::set<LanguagePackKey> installed_lang_packs_;
diff --git a/components/optimization_guide/core/model_execution/android/model_broker_android.cc b/components/optimization_guide/core/model_execution/android/model_broker_android.cc
index 7cef37e9..b98d13e 100644
--- a/components/optimization_guide/core/model_execution/android/model_broker_android.cc
+++ b/components/optimization_guide/core/model_execution/android/model_broker_android.cc
@@ -227,8 +227,8 @@
   parent_->usage_tracker_.AddObserver(this);
   // Start model downloads for recently used features
   for (auto feature : OnDeviceFeatureSet::All()) {
-    if (parent_->usage_tracker_.WasOnDeviceEligibleFeatureRecentlyUsed(
-            feature)) {
+    if (parent_->usage_tracker_.WasUseCaseRecentlyUsed(
+            ToUseCaseName(feature))) {
       MaybeStartDownload(feature);
     }
   }
@@ -286,8 +286,7 @@
     base_model_specs_.insert_or_assign(feature, spec);
     loader_map_.MaybeRegisterModelDownload(
         feature, spec,
-        parent_->usage_tracker_.WasOnDeviceEligibleFeatureRecentlyUsed(
-            feature));
+        parent_->usage_tracker_.WasUseCaseRecentlyUsed(ToUseCaseName(feature)));
   } else {
     MaybeUpdateModelAdaptation(
         feature, base::unexpected(AdaptationUnavailability::kNotSupported));
diff --git a/components/optimization_guide/core/model_execution/model_broker_client.cc b/components/optimization_guide/core/model_execution/model_broker_client.cc
index 2cae423..b0b575feab 100644
--- a/components/optimization_guide/core/model_execution/model_broker_client.cc
+++ b/components/optimization_guide/core/model_execution/model_broker_client.cc
@@ -263,36 +263,60 @@
     : remote_(std::move(remote)), logger_(logger) {}
 ModelBrokerClient::~ModelBrokerClient() = default;
 
-ModelSubscriber& ModelBrokerClient::GetSubscriber(
-    mojom::OnDeviceFeature feature) {
-  std::unique_ptr<ModelSubscriber>& ptr = subscribers_[feature];
+ModelSubscriber& ModelBrokerClient::GetSubscriber(const std::string& use_case) {
+  std::unique_ptr<ModelSubscriber>& ptr = subscribers_[use_case];
   if (!ptr) {
     TRACE_EVENT("optimization_guide", "ModelBrokerClient::CreateSubscriber",
-                "feature", base::ToString(feature));
+                "use_case", use_case);
     mojo::PendingRemote<mojom::ModelSubscriber> pending;
     ptr = std::make_unique<ModelSubscriber>(
         pending.InitWithNewPipeAndPassReceiver());
-    remote_->Subscribe(mojom::ModelSubscriptionOptions::New(feature),
+    remote_->Subscribe(mojom::ModelSubscriptionOptions::New(use_case),
                        std::move(pending));
   }
   return *ptr;
 }
 
+ModelSubscriber& ModelBrokerClient::GetSubscriber(
+    mojom::OnDeviceFeature feature) {
+  return GetSubscriber(ToUseCaseName(feature));
+}
+
+void ModelBrokerClient::RequestAssetsFor(const std::string& use_case) {
+  remote_->RequestAssetsFor(use_case);
+}
+
 void ModelBrokerClient::RequestAssetsFor(mojom::OnDeviceFeature feature) {
   TRACE_EVENT("optimization_guide", "ModelBrokerClient::RequestAssetsFor");
-  remote_->RequestAssetsFor(feature);
+  RequestAssetsFor(ToUseCaseName(feature));
+}
+
+bool ModelBrokerClient::HasSubscriber(const std::string& use_case) {
+  return subscribers_.contains(use_case);
 }
 
 bool ModelBrokerClient::HasSubscriber(mojom::OnDeviceFeature feature) {
-  return subscribers_.contains(feature);
+  return HasSubscriber(ToUseCaseName(feature));
+}
+
+void ModelBrokerClient::CreateSession(const std::string& use_case,
+                                      const SessionConfigParams& config_params,
+                                      CreateSessionCallback callback) {
+  RequestAssetsFor(use_case);
+  GetSubscriber(use_case).CreateSession(std::move(config_params),
+                                        std::move(callback), logger_);
 }
 
 void ModelBrokerClient::CreateSession(mojom::OnDeviceFeature feature,
                                       const SessionConfigParams& config_params,
                                       CreateSessionCallback callback) {
-  RequestAssetsFor(feature);
-  GetSubscriber(feature).CreateSession(std::move(config_params),
-                                       std::move(callback), logger_);
+  CreateSession(ToUseCaseName(feature), std::move(config_params),
+                std::move(callback));
+}
+
+void ModelBrokerClient::GetConfig(mojom::OnDeviceFeature feature,
+                                  GetConfigCallback callback) {
+  remote_->GetConfig(feature, std::move(callback));
 }
 
 void ModelBrokerClient::AddModelDownloadProgressObserver(
diff --git a/components/optimization_guide/core/model_execution/model_broker_client.h b/components/optimization_guide/core/model_execution/model_broker_client.h
index 429ed64..fd4c04d 100644
--- a/components/optimization_guide/core/model_execution/model_broker_client.h
+++ b/components/optimization_guide/core/model_execution/model_broker_client.h
@@ -175,19 +175,29 @@
   using CreateSessionCallback = ModelSubscriber::CreateSessionCallback;
 
   // Get or create the subscriber for the given key.
+  ModelSubscriber& GetSubscriber(const std::string& use_case);
   ModelSubscriber& GetSubscriber(mojom::OnDeviceFeature feature);
 
   // Request that the model assets for this feature be made available.
+  void RequestAssetsFor(const std::string& use_case);
   void RequestAssetsFor(mojom::OnDeviceFeature feature);
 
   // Whether the subscriber for this key already exists.
+  bool HasSubscriber(const std::string& use_case);
   bool HasSubscriber(mojom::OnDeviceFeature feature);
 
   // Async session creation.
+  void CreateSession(const std::string& use_case,
+                     const SessionConfigParams& config_params,
+                     CreateSessionCallback callback);
   void CreateSession(mojom::OnDeviceFeature feature,
                      const SessionConfigParams& config_params,
                      CreateSessionCallback callback);
 
+  using GetConfigCallback =
+      base::OnceCallback<void(std::optional<mojo_base::ProtoWrapper>)>;
+  void GetConfig(mojom::OnDeviceFeature feature, GetConfigCallback callback);
+
   // Add DownloadProgressObserver.
   void AddModelDownloadProgressObserver(
       mojo::PendingRemote<on_device_model::mojom::DownloadObserver> observer);
@@ -196,7 +206,7 @@
   mojo::Remote<mojom::ModelBroker> remote_;
   base::WeakPtr<OptimizationGuideLogger> logger_;
 
-  absl::flat_hash_map<mojom::OnDeviceFeature, std::unique_ptr<ModelSubscriber>>
+  absl::flat_hash_map<std::string, std::unique_ptr<ModelSubscriber>>
       subscribers_;
 };
 
diff --git a/components/optimization_guide/core/model_execution/model_broker_client_unittest.cc b/components/optimization_guide/core/model_execution/model_broker_client_unittest.cc
index 062a6f5..2082a4c 100644
--- a/components/optimization_guide/core/model_execution/model_broker_client_unittest.cc
+++ b/components/optimization_guide/core/model_execution/model_broker_client_unittest.cc
@@ -93,6 +93,46 @@
   ASSERT_TRUE(future.Take());
 }
 
+// Verify that CreateSession works when all the assets are provided using a
+// custom use case string.
+TEST(ModelBrokerClientTest, ReadyWithUseCaseClient) {
+  base::test::TaskEnvironment task_environment_;
+  OptimizationGuideLogger logger;
+  FakeAdaptationAsset fake_asset({
+      .config =
+          []() {
+            auto config = SimpleComposeConfig();
+            config.set_feature(proto::MODEL_EXECUTION_FEATURE_TEST);
+            config.set_can_skip_text_safety(true);
+            return config;
+          }(),
+  });
+  FakeModelBroker fake_broker({});
+  fake_broker.UpdateModelAdaptation(fake_asset);
+
+  ModelBrokerClient client(fake_broker.BindAndPassRemote(),
+                           logger.GetWeakPtr());
+  base::test::TestFuture<ModelBrokerClient::CreateSessionResult> future;
+
+  // Requesting the feature via its use case name "test" should succeed.
+  client.CreateSession("test", SessionConfigParams{}, future.GetCallback());
+  ASSERT_TRUE(future.Take());
+}
+
+TEST(ModelBrokerClientTest, GetConfigNulloptWhenNotSet) {
+  base::test::TaskEnvironment task_environment_;
+  OptimizationGuideLogger logger;
+  FakeModelBroker fake_broker({});
+
+  ModelBrokerClient client(fake_broker.BindAndPassRemote(),
+                           logger.GetWeakPtr());
+
+  base::test::TestFuture<std::optional<mojo_base::ProtoWrapper>> future;
+  client.GetConfig(mojom::OnDeviceFeature::kTest, future.GetCallback());
+  auto result = future.Take();
+  EXPECT_FALSE(result.has_value());
+}
+
 // Sometimes a feature is not supported for certain base models (e.g. EE model).
 // Attempts to create a Session for such features should fully resolve as
 // unavailable.
diff --git a/components/optimization_guide/core/model_execution/model_broker_impl.cc b/components/optimization_guide/core/model_execution/model_broker_impl.cc
index ba4f15af..3d5905c 100644
--- a/components/optimization_guide/core/model_execution/model_broker_impl.cc
+++ b/components/optimization_guide/core/model_execution/model_broker_impl.cc
@@ -10,6 +10,7 @@
 #include "base/strings/to_string.h"
 #include "base/trace_event/trace_event.h"
 #include "components/optimization_guide/core/model_execution/on_device_features.h"
+#include "mojo/public/cpp/bindings/message.h"
 #include "components/optimization_guide/core/model_execution/usage_tracker.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/optimization_guide/public/mojom/model_broker.mojom.h"
@@ -44,38 +45,89 @@
               perfetto::Flow::FromPointer(this));
   ensure_init_callback_.Run(base::BindOnce(
       &ModelBrokerImpl::SubscribeInternal, weak_ptr_factory_.GetWeakPtr(),
-      std::move(options), std::move(subscriber)));
+      std::move(options), std::move(subscriber),
+      mojo::GetBadMessageCallback()));
 }
 
 void ModelBrokerImpl::SubscribeInternal(
     mojom::ModelSubscriptionOptionsPtr options,
     mojo::PendingRemote<mojom::ModelSubscriber> subscriber,
+    mojo::ReportBadMessageCallback bad_message_callback,
     const on_device_model::Capabilities& capabilities) {
   TRACE_EVENT("optimization_guide", "ModelBrokerImpl::SubscribeInternal",
               perfetto::Flow::FromPointer(this));
-  GetSolutionProvider(options->feature)
+  const std::string& use_case = options->use_case;
+  if (!solution_providers_.contains(use_case) &&
+      !GetFeatureForUseCase(use_case)) {
+    std::move(bad_message_callback).Run("Unsupported use case");
+    return;
+  }
+  GetSolutionProvider(use_case)
       .AddSubscriber(std::move(subscriber), capabilities);
 }
 
-void ModelBrokerImpl::RequestAssetsFor(mojom::OnDeviceFeature feature) {
+void ModelBrokerImpl::GetConfig(mojom::OnDeviceFeature feature,
+                                GetConfigCallback callback) {
+  TRACE_EVENT("optimization_guide", "ModelBrokerImpl::GetConfig");
+  ensure_init_callback_.Run(base::BindOnce(&ModelBrokerImpl::GetConfigInternal,
+                                           weak_ptr_factory_.GetWeakPtr(),
+                                           feature, std::move(callback)));
+}
+
+void ModelBrokerImpl::GetConfigInternal(
+    mojom::OnDeviceFeature feature,
+    GetConfigCallback callback,
+    const on_device_model::Capabilities& capabilities) {
+  TRACE_EVENT("optimization_guide", "ModelBrokerImpl::GetConfigInternal");
+  auto it = feature_configs_.find(feature);
+  if (it == feature_configs_.end()) {
+    std::move(callback).Run(std::nullopt);
+    return;
+  }
+  std::move(callback).Run(mojo_base::ProtoWrapper(it->second));
+}
+
+void ModelBrokerImpl::RequestAssetsFor(const std::string& use_case) {
   TRACE_EVENT("optimization_guide", "ModelBrokerImpl::RequestAssetsFor",
               perfetto::Flow::FromPointer(this));
-  ensure_init_callback_.Run(
-      base::BindOnce(&ModelBrokerImpl::RequestAssetsForInternal,
-                     weak_ptr_factory_.GetWeakPtr(), feature));
+  ensure_init_callback_.Run(base::BindOnce(
+      &ModelBrokerImpl::RequestAssetsForInternal,
+      weak_ptr_factory_.GetWeakPtr(), use_case, mojo::GetBadMessageCallback()));
 }
 
 void ModelBrokerImpl::RequestAssetsForInternal(
-    mojom::OnDeviceFeature feature,
+    const std::string& use_case,
+    mojo::ReportBadMessageCallback bad_message_callback,
     const on_device_model::Capabilities& capabilities) {
   TRACE_EVENT("optimization_guide", "ModelBrokerImpl::RequestAssetsForInternal",
               perfetto::Flow::FromPointer(this));
-  usage_tracker_->OnDeviceEligibleFeatureUsed(feature);
+  if (!solution_providers_.contains(use_case) &&
+      !GetFeatureForUseCase(use_case)) {
+    std::move(bad_message_callback).Run("Unsupported use case");
+    return;
+  }
+  usage_tracker_->OnDeviceEligibleUseCaseUsed(use_case);
+}
+
+ModelBrokerImpl::SolutionProvider& ModelBrokerImpl::GetSolutionProvider(
+    const std::string& use_case) {
+  auto it = solution_providers_.find(use_case);
+  if (it == solution_providers_.end()) {
+    // TODO(crbug.com/500382778): Avoid unbounded map growth by not adding
+    // entries for unsupported use-cases.
+    it = solution_providers_.emplace(use_case, use_case).first;
+  }
+  return it->second;
 }
 
 ModelBrokerImpl::SolutionProvider& ModelBrokerImpl::GetSolutionProvider(
     mojom::OnDeviceFeature feature) {
-  return solution_providers_.emplace(feature, feature).first->second;
+  return GetSolutionProvider(ToUseCaseName(feature));
+}
+
+void ModelBrokerImpl::SetFeatureConfigs(
+    base::flat_map<mojom::OnDeviceFeature, proto::Any> feature_configs) {
+  feature_configs_ = std::move(feature_configs);
 }
 
 #if !BUILDFLAG(IS_ANDROID)
@@ -91,9 +143,8 @@
 ModelBrokerImpl::Solution::Solution() = default;
 ModelBrokerImpl::Solution::~Solution() = default;
 
-ModelBrokerImpl::SolutionProvider::SolutionProvider(
-    mojom::OnDeviceFeature feature)
-    : feature_(feature) {}
+ModelBrokerImpl::SolutionProvider::SolutionProvider(const std::string& use_case)
+    : use_case_(use_case) {}
 
 ModelBrokerImpl::SolutionProvider::~SolutionProvider() {
   TRACE_EVENT("optimization_guide",
@@ -127,8 +178,8 @@
 
 void ModelBrokerImpl::SolutionProvider::Update(MaybeSolution solution) {
   TRACE_EVENT("optimization_guide", "ModelBrokerImpl::SolutionProvider::Update",
-              perfetto::Flow::FromPointer(this), "feature",
-              base::ToString(feature_));
+              perfetto::Flow::FromPointer(this), "use_case",
+              use_case_);
   CHECK(!solution.has_value() || solution.value());
   if (solution.has_value()) {
     if (solution_.has_value() && solution_.value()->IsValid()) {
@@ -156,8 +207,8 @@
     mojom::ModelSubscriber& subscriber) {
   TRACE_EVENT("optimization_guide",
               "ModelBrokerImpl::SolutionProvider::UpdateSubscriber",
-              perfetto::Flow::FromPointer(this), "feature",
-              base::ToString(feature_));
+              perfetto::Flow::FromPointer(this), "use_case",
+              use_case_);
   if (!solution_.has_value()) {
     subscriber.Unavailable(
         *AvailabilityFromEligibilityReason(solution_.error()),
@@ -177,13 +228,17 @@
 }
 
 void ModelBrokerImpl::SolutionProvider::UpdateObservers() {
+  auto feature = GetFeatureForUseCase(use_case_);
+  if (!feature) {
+    return;
+  }
   TRACE_EVENT("optimization_guide",
               "ModelBrokerImpl::SolutionProvider::UpdateObservers",
               perfetto::Flow::FromPointer(this), "feature",
-              base::ToString(feature_));
+              base::ToString(*feature));
   for (auto& observer : observers_) {
     observer.OnDeviceModelAvailabilityChanged(
-        feature_, solution_.error_or(OnDeviceModelEligibilityReason::kSuccess));
+        *feature, solution_.error_or(OnDeviceModelEligibilityReason::kSuccess));
   }
 }
 
@@ -192,8 +247,8 @@
     const on_device_model::Capabilities& capabilities) {
   TRACE_EVENT("optimization_guide",
               "ModelBrokerImpl::SolutionProvider::UpdatePossibleCapabilities",
-              perfetto::Flow::FromPointer(this), "feature",
-              base::ToString(feature_));
+              perfetto::Flow::FromPointer(this), "use_case",
+              use_case_);
   subscriber.CapabilitiesUpdated(capabilities);
 }
 
diff --git a/components/optimization_guide/core/model_execution/model_broker_impl.h b/components/optimization_guide/core/model_execution/model_broker_impl.h
index ba911bd..f513cdd 100644
--- a/components/optimization_guide/core/model_execution/model_broker_impl.h
+++ b/components/optimization_guide/core/model_execution/model_broker_impl.h
@@ -55,7 +55,7 @@
   // Keeps subscribers updated with the current solution.
   class SolutionProvider final {
    public:
-    explicit SolutionProvider(mojom::OnDeviceFeature feature);
+    explicit SolutionProvider(const std::string& use_case);
     ~SolutionProvider();
 
     void AddSubscriber(mojo::PendingRemote<mojom::ModelSubscriber> pending,
@@ -78,7 +78,7 @@
         mojom::ModelSubscriber& subscriber,
         const on_device_model::Capabilities& capabilities);
 
-    mojom::OnDeviceFeature feature_;
+    std::string use_case_;
     mojo::RemoteSet<mojom::ModelSubscriber> subscribers_;
     base::ObserverList<OnDeviceModelAvailabilityObserver> observers_;
     ModelSubscriberImpl local_subscriber_;
@@ -96,14 +96,21 @@
   void BindBroker(mojo::PendingReceiver<mojom::ModelBroker> receiver);
 
   // Get (or construct) the solution provider for the feature.
+  SolutionProvider& GetSolutionProvider(const std::string& use_case);
   SolutionProvider& GetSolutionProvider(mojom::OnDeviceFeature feature);
 
+  // Set the feature configs for the current manifest.
+  void SetFeatureConfigs(
+      base::flat_map<mojom::OnDeviceFeature, proto::Any> feature_configs);
+
  private:
   // mojom::ModelBroker:
   void Subscribe(
       mojom::ModelSubscriptionOptionsPtr options,
       mojo::PendingRemote<mojom::ModelSubscriber> subscriber) override;
-  void RequestAssetsFor(mojom::OnDeviceFeature feature) override;
+  void GetConfig(mojom::OnDeviceFeature feature,
+                 GetConfigCallback callback) override;
+  void RequestAssetsFor(const std::string& use_case) override;
 
 #if !BUILDFLAG(IS_ANDROID)
   void AddModelDownloadProgressObserver(
@@ -114,16 +121,24 @@
   // Finishes Subscribe after initialization is finished.
   void SubscribeInternal(mojom::ModelSubscriptionOptionsPtr options,
                          mojo::PendingRemote<mojom::ModelSubscriber> subscriber,
+                         mojo::ReportBadMessageCallback bad_message_callback,
                          const on_device_model::Capabilities& capabilities);
   // Finishes `RequestAssetsFor` after initialization is finished.
   void RequestAssetsForInternal(
-      mojom::OnDeviceFeature feature,
+      const std::string& use_case,
+      mojo::ReportBadMessageCallback bad_message_callback,
       const on_device_model::Capabilities& capabilities);
 
+  // Finishes `GetConfig` after initialization is finished.
+  void GetConfigInternal(mojom::OnDeviceFeature feature,
+                         GetConfigCallback callback,
+                         const on_device_model::Capabilities& capabilities);
+
   raw_ref<UsageTracker> usage_tracker_;
   EnsureInitCallback ensure_init_callback_;
   AddDownloadProgressObserverCallback add_download_progress_observer_callback_;
-  std::map<mojom::OnDeviceFeature, SolutionProvider> solution_providers_;
+  base::flat_map<mojom::OnDeviceFeature, proto::Any> feature_configs_;
+  std::map<std::string, SolutionProvider> solution_providers_;
   mojo::ReceiverSet<mojom::ModelBroker> receivers_;
   base::WeakPtrFactory<ModelBrokerImpl> weak_ptr_factory_{this};
 };
diff --git a/components/optimization_guide/core/model_execution/on_device_asset_manager.cc b/components/optimization_guide/core/model_execution/on_device_asset_manager.cc
index 203b7c16..2467972 100644
--- a/components/optimization_guide/core/model_execution/on_device_asset_manager.cc
+++ b/components/optimization_guide/core/model_execution/on_device_asset_manager.cc
@@ -131,7 +131,7 @@
   for (auto feature : OnDeviceFeatureSet::All()) {
     adaptation_loaders_.MaybeRegisterModelDownload(
         feature, new_spec,
-        usage_tracker_->WasOnDeviceEligibleFeatureRecentlyUsed(feature));
+        usage_tracker_->WasUseCaseRecentlyUsed(ToUseCaseName(feature)));
   }
 }
 
@@ -152,7 +152,7 @@
       state ? std::make_optional(state->GetBaseModelSpec()) : std::nullopt;
   adaptation_loaders_.MaybeRegisterModelDownload(
       *feature, new_spec,
-      usage_tracker_->WasOnDeviceEligibleFeatureRecentlyUsed(*feature));
+      usage_tracker_->WasUseCaseRecentlyUsed(use_case_name));
 }
 
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.cc b/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.cc
index 939dd6b..aa88030 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_classifier_controller.cc
@@ -262,8 +262,8 @@
     return base::unexpected(error);
   }
 
-  if (!usage_tracker_->WasOnDeviceEligibleFeatureRecentlyUsed(
-          mojom::OnDeviceFeature::kClassifier)) {
+  if (!usage_tracker_->WasUseCaseRecentlyUsed(
+          ToUseCaseName(mojom::OnDeviceFeature::kClassifier))) {
     return base::unexpected(
         OnDeviceModelEligibilityReason::kNoOnDeviceFeatureUsed);
   }
diff --git a/components/optimization_guide/core/model_execution/on_device_model_component.cc b/components/optimization_guide/core/model_execution/on_device_model_component.cc
index b272b1f..2eebd49 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_component.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_component.cc
@@ -190,7 +190,7 @@
   return std::ranges::any_of(
       OnDeviceFeatureSet::All(), [&](mojom::OnDeviceFeature feature) {
         return GetOnDeviceModelType(feature) == model_type &&
-               usage_tracker->WasOnDeviceEligibleFeatureRecentlyUsed(feature);
+               usage_tracker->WasUseCaseRecentlyUsed(ToUseCaseName(feature));
       });
 }
 
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc b/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc
index d49008b..9d9b792d 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc
@@ -258,7 +258,7 @@
   bool is_background_download_enabled_for_feature =
       features::IsOnDeviceModelBackgroundDownloadEnabledForFeature(feature);
 
-  if (!usage_tracker_->WasOnDeviceEligibleFeatureRecentlyUsed(feature) &&
+  if (!usage_tracker_->WasUseCaseRecentlyUsed(ToUseCaseName(feature)) &&
       !is_background_download_enabled_for_feature) {
     return base::unexpected(
         OnDeviceModelEligibilityReason::kNoOnDeviceFeatureUsed);
diff --git a/components/optimization_guide/public/mojom/model_broker.mojom b/components/optimization_guide/public/mojom/model_broker.mojom
index c3c6ed8..5907d7d 100644
--- a/components/optimization_guide/public/mojom/model_broker.mojom
+++ b/components/optimization_guide/public/mojom/model_broker.mojom
@@ -117,7 +117,11 @@
 
 // Options for ModelBroker::Subscribe
 struct ModelSubscriptionOptions {
-  OnDeviceFeature feature;
+  // The optimization target use case.
+  // A valid use case is either the string representation of an OnDeviceFeature,
+  // or a use case defined in the on-device model manifest. Invalid use cases
+  // will be rejected via mojo::ReportBadMessage.
+  string use_case;
 };
 
 // Broker for using the on-device models. The remote end may be held by any
@@ -127,13 +131,20 @@
   Subscribe(ModelSubscriptionOptions options,
             pending_remote<ModelSubscriber> subscriber);
 
+  // Fetches a feature-specific opaque configuration from the manifest. Returns
+  // nullopt if the feature is not found in the manifest.
+  GetConfig(OnDeviceFeature feature) => (mojo_base.mojom.ProtoWrapper? config);
+
   // Start listening for download progress updates for models.
   // TODO: crbug.com/474999857 Implement this method on Android.
   [EnableIfNot=is_android]
   AddModelDownloadProgressObserver(
       pending_remote<on_device_model.mojom.DownloadObserver> observer);
 
-  // Request model assets of the provided feature. Must be called before a model
-  // solution can be available.
-  RequestAssetsFor(OnDeviceFeature feature);
+  // Request model assets of the provided use case. Must be called before a
+  // model solution can be available.
+  // A valid use case is either the string representation of an OnDeviceFeature
+  // or a use case defined in the on-device model manifest. Invalid use cases
+  // will be rejected via mojo::ReportBadMessage.
+  RequestAssetsFor(string use_case);
 };
diff --git a/components/security_interstitials/content/BUILD.gn b/components/security_interstitials/content/BUILD.gn
index 9a77345c..b80788f 100644
--- a/components/security_interstitials/content/BUILD.gn
+++ b/components/security_interstitials/content/BUILD.gn
@@ -30,6 +30,8 @@
     "insecure_form_tab_storage.h",
     "known_interception_disclosure_ui.cc",
     "known_interception_disclosure_ui.h",
+    "local_self_signed_blocking_page.cc",
+    "local_self_signed_blocking_page.h",
     "mitm_software_blocking_page.cc",
     "mitm_software_blocking_page.h",
     "security_blocking_page_factory.h",
diff --git a/components/security_interstitials/content/local_self_signed_blocking_page.cc b/components/security_interstitials/content/local_self_signed_blocking_page.cc
new file mode 100644
index 0000000..ddf36903
--- /dev/null
+++ b/components/security_interstitials/content/local_self_signed_blocking_page.cc
@@ -0,0 +1,44 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/security_interstitials/content/local_self_signed_blocking_page.h"
+
+#include "base/values.h"
+#include "components/security_interstitials/content/security_interstitial_controller_client.h"
+
+LocalSelfSignedBlockingPage::LocalSelfSignedBlockingPage(
+    content::WebContents* web_contents,
+    net::Error cert_error,
+    const net::SSLInfo& ssl_info,
+    const GURL& request_url,
+    int options_mask,
+    const base::Time& time_triggered,
+    const GURL& support_url,
+    bool overrideable,
+    bool can_show_enhanced_protection_message,
+    std::unique_ptr<
+        security_interstitials::SecurityInterstitialControllerClient>
+        controller_client)
+    : SSLBlockingPage(web_contents,
+                      cert_error,
+                      ssl_info,
+                      request_url,
+                      options_mask,
+                      time_triggered,
+                      support_url,
+                      overrideable,
+                      can_show_enhanced_protection_message,
+                      std::move(controller_client)) {}
+
+LocalSelfSignedBlockingPage::~LocalSelfSignedBlockingPage() = default;
+
+void LocalSelfSignedBlockingPage::PopulateInterstitialStrings(
+    base::DictValue& load_time_data) {
+  // TODO(crbug.com/394119724): For now this is just the SSL interstitial with a
+  // different title and heading. This will eventually be replaced with a
+  // different UI.
+  SSLBlockingPage::PopulateInterstitialStrings(load_time_data);
+  load_time_data.Set("tabTitle", "Local Self-Signed Site");
+  load_time_data.Set("heading", "Local Self-Signed Site");
+}
diff --git a/components/security_interstitials/content/local_self_signed_blocking_page.h b/components/security_interstitials/content/local_self_signed_blocking_page.h
new file mode 100644
index 0000000..97ef90ae
--- /dev/null
+++ b/components/security_interstitials/content/local_self_signed_blocking_page.h
@@ -0,0 +1,37 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_LOCAL_SELF_SIGNED_BLOCKING_PAGE_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_LOCAL_SELF_SIGNED_BLOCKING_PAGE_H_
+
+#include "components/security_interstitials/content/ssl_blocking_page.h"
+
+class LocalSelfSignedBlockingPage : public SSLBlockingPage {
+ public:
+  LocalSelfSignedBlockingPage(
+      content::WebContents* web_contents,
+      net::Error cert_error,
+      const net::SSLInfo& ssl_info,
+      const GURL& request_url,
+      int options_mask,
+      const base::Time& time_triggered,
+      const GURL& support_url,
+      bool overrideable,
+      bool can_show_enhanced_protection_message,
+      std::unique_ptr<
+          security_interstitials::SecurityInterstitialControllerClient>
+          controller_client);
+
+  LocalSelfSignedBlockingPage(const LocalSelfSignedBlockingPage&) = delete;
+  LocalSelfSignedBlockingPage& operator=(const LocalSelfSignedBlockingPage&) =
+      delete;
+
+  ~LocalSelfSignedBlockingPage() override;
+
+ protected:
+  // SSLBlockingPageBase implementation:
+  void PopulateInterstitialStrings(base::DictValue& load_time_data) override;
+};
+
+#endif  // COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_LOCAL_SELF_SIGNED_BLOCKING_PAGE_H_
diff --git a/components/security_interstitials/content/security_blocking_page_factory.h b/components/security_interstitials/content/security_blocking_page_factory.h
index a95b004..cb4057393 100644
--- a/components/security_interstitials/content/security_blocking_page_factory.h
+++ b/components/security_interstitials/content/security_blocking_page_factory.h
@@ -14,6 +14,7 @@
 #include "components/security_interstitials/content/captive_portal_blocking_page.h"
 #include "components/security_interstitials/content/https_only_mode_blocking_page.h"
 #include "components/security_interstitials/content/insecure_form_blocking_page.h"
+#include "components/security_interstitials/content/local_self_signed_blocking_page.h"
 #include "components/security_interstitials/content/mitm_software_blocking_page.h"
 #include "components/security_interstitials/content/ssl_blocking_page.h"
 #include "components/security_interstitials/content/ssl_blocking_page_base.h"
@@ -44,6 +45,16 @@
       const base::Time& time_triggered,
       const GURL& support_url) = 0;
 
+  // Creates a local self-signed blocking page.
+  virtual std::unique_ptr<LocalSelfSignedBlockingPage>
+  CreateLocalSelfSignedBlockingPage(content::WebContents* web_contents,
+                                    net::Error cert_error,
+                                    const net::SSLInfo& ssl_info,
+                                    const GURL& request_url,
+                                    int options_mask,
+                                    const base::Time& time_triggered,
+                                    const GURL& support_url) = 0;
+
   // Creates a captive portal blocking page.
   virtual std::unique_ptr<CaptivePortalBlockingPage>
   CreateCaptivePortalBlockingPage(content::WebContents* web_contents,
diff --git a/components/security_interstitials/content/ssl_error_handler.cc b/components/security_interstitials/content/ssl_error_handler.cc
index fe3e44c..2db2bca 100644
--- a/components/security_interstitials/content/ssl_error_handler.cc
+++ b/components/security_interstitials/content/ssl_error_handler.cc
@@ -387,6 +387,7 @@
   void ShowBadClockInterstitial(const base::Time& now,
                                 ssl_errors::ClockState clock_state) override;
   void ShowBlockedInterceptionInterstitial() override;
+  void ShowLocalSelfSignedInterstitial() override;
   void ReportNetworkConnectivity(base::OnceClosure callback) override;
   bool HasBlockedInterception() const override;
 
@@ -502,6 +503,12 @@
           web_contents_, cert_error_, request_url_, ssl_info_));
 }
 
+void SSLErrorHandlerDelegateImpl::ShowLocalSelfSignedInterstitial() {
+  OnBlockingPageReady(blocking_page_factory_->CreateLocalSelfSignedBlockingPage(
+      web_contents_, cert_error_, ssl_info_, request_url_, options_mask_,
+      base::Time::NowFromSystemTime(), GURL()));
+}
+
 void SSLErrorHandlerDelegateImpl::ReportNetworkConnectivity(
     base::OnceClosure callback) {
 #if BUILDFLAG(IS_ANDROID)
@@ -671,6 +678,10 @@
     return ShowBlockedInterceptionInterstitial();
   }
 
+  if (cert_error_ == net::ERR_CERT_SELF_SIGNED_LOCAL_NETWORK) {
+    return ShowLocalSelfSignedInterstitial();
+  }
+
   if (ssl_errors::ErrorInfo::NetErrorToErrorType(cert_error_) ==
       ssl_errors::ErrorInfo::CERT_DATE_INVALID) {
     HandleCertDateInvalidError();
@@ -901,6 +912,14 @@
   web_contents()->RemoveUserData(UserDataKey());
 }
 
+void SSLErrorHandler::ShowLocalSelfSignedInterstitial() {
+  RecordUMA(SHOW_LOCAL_SELF_SIGNED_INTERSTITIAL);
+  delegate_->ShowLocalSelfSignedInterstitial();
+  // Once an interstitial is displayed, no need to keep the handler around.
+  // This is the equivalent of "delete this".
+  web_contents()->RemoveUserData(UserDataKey());
+}
+
 void SSLErrorHandler::CommonNameMismatchHandlerCallback(
     CommonNameMismatchHandler::SuggestedUrlCheckResult result,
     const GURL& suggested_url) {
diff --git a/components/security_interstitials/content/ssl_error_handler.h b/components/security_interstitials/content/ssl_error_handler.h
index 4895a1f8..36efee9 100644
--- a/components/security_interstitials/content/ssl_error_handler.h
+++ b/components/security_interstitials/content/ssl_error_handler.h
@@ -104,6 +104,7 @@
     OS_REPORTS_CAPTIVE_PORTAL = 12,
     SHOW_BLOCKED_INTERCEPTION_INTERSTITIAL = 13,
     SHOW_LEGACY_TLS_INTERSTITIAL = 14,  // Deprecated in M98.
+    SHOW_LOCAL_SELF_SIGNED_INTERSTITIAL = 15,
     SSL_ERROR_HANDLER_EVENT_COUNT
   };
 
@@ -129,6 +130,7 @@
         const base::Time& now,
         ssl_errors::ClockState clock_state) = 0;
     virtual void ShowBlockedInterceptionInterstitial() = 0;
+    virtual void ShowLocalSelfSignedInterstitial() = 0;
     virtual void ReportNetworkConnectivity(base::OnceClosure callback) = 0;
     virtual bool HasBlockedInterception() const = 0;
   };
@@ -219,6 +221,7 @@
                                 ssl_errors::ClockState clock_state);
   void ShowDynamicInterstitial(const DynamicInterstitialInfo interstitial);
   void ShowBlockedInterceptionInterstitial();
+  void ShowLocalSelfSignedInterstitial();
 
   // Gets the result of whether the suggested URL is valid. Displays
   // common name mismatch interstitial or ssl interstitial accordingly.
diff --git a/components/security_interstitials/content/ssl_error_handler_unittest.cc b/components/security_interstitials/content/ssl_error_handler_unittest.cc
index 4007142b..48df5e4e 100644
--- a/components/security_interstitials/content/ssl_error_handler_unittest.cc
+++ b/components/security_interstitials/content/ssl_error_handler_unittest.cc
@@ -152,19 +152,7 @@
 class TestSSLErrorHandlerDelegate : public SSLErrorHandler::Delegate {
  public:
   TestSSLErrorHandlerDelegate(content::WebContents* web_contents,
-                              const net::SSLInfo& ssl_info)
-      : captive_portal_checked_(false),
-        os_reports_captive_portal_(false),
-        suggested_url_exists_(false),
-        suggested_url_checked_(false),
-        ssl_interstitial_shown_(false),
-        bad_clock_interstitial_shown_(false),
-        captive_portal_interstitial_shown_(false),
-        mitm_software_interstitial_shown_(false),
-        blocked_interception_interstitial_shown_(false),
-        redirected_to_suggested_url_(false),
-        is_overridable_error_(true),
-        has_blocked_interception_(false) {}
+                              const net::SSLInfo& ssl_info) {}
 
   TestSSLErrorHandlerDelegate(const TestSSLErrorHandlerDelegate&) = delete;
   TestSSLErrorHandlerDelegate& operator=(const TestSSLErrorHandlerDelegate&) =
@@ -190,6 +178,9 @@
   bool blocked_interception_interstitial_shown() const {
     return blocked_interception_interstitial_shown_;
   }
+  bool local_self_signed_interstitial_shown() const {
+    return local_self_signed_interstitial_shown_;
+  }
   bool suggested_url_checked() const { return suggested_url_checked_; }
   bool redirected_to_suggested_url() const {
     return redirected_to_suggested_url_;
@@ -209,6 +200,7 @@
     bad_clock_interstitial_shown_ = false;
     captive_portal_interstitial_shown_ = false;
     mitm_software_interstitial_shown_ = false;
+    local_self_signed_interstitial_shown_ = false;
     redirected_to_suggested_url_ = false;
     has_blocked_interception_ = false;
   }
@@ -250,6 +242,10 @@
     blocked_interception_interstitial_shown_ = true;
   }
 
+  void ShowLocalSelfSignedInterstitial() override {
+    local_self_signed_interstitial_shown_ = true;
+  }
+
   void CheckSuggestedUrl(
       const GURL& suggested_url,
       CommonNameMismatchHandler::CheckUrlCallback callback) override {
@@ -270,18 +266,19 @@
     return has_blocked_interception_;
   }
 
-  bool captive_portal_checked_;
-  bool os_reports_captive_portal_;
-  bool suggested_url_exists_;
-  bool suggested_url_checked_;
-  bool ssl_interstitial_shown_;
-  bool bad_clock_interstitial_shown_;
-  bool captive_portal_interstitial_shown_;
-  bool mitm_software_interstitial_shown_;
-  bool blocked_interception_interstitial_shown_;
-  bool redirected_to_suggested_url_;
-  bool is_overridable_error_;
-  bool has_blocked_interception_;
+  bool captive_portal_checked_ = false;
+  bool os_reports_captive_portal_ = false;
+  bool suggested_url_exists_ = false;
+  bool suggested_url_checked_ = false;
+  bool ssl_interstitial_shown_ = false;
+  bool bad_clock_interstitial_shown_ = false;
+  bool captive_portal_interstitial_shown_ = false;
+  bool mitm_software_interstitial_shown_ = false;
+  bool blocked_interception_interstitial_shown_ = false;
+  bool local_self_signed_interstitial_shown_ = false;
+  bool redirected_to_suggested_url_ = false;
+  bool is_overridable_error_ = true;
+  bool has_blocked_interception_ = false;
   CommonNameMismatchHandler::CheckUrlCallback suggested_url_callback_;
 };
 
@@ -1483,3 +1480,32 @@
   // Make sure that the |SSLErrorHandler| is deleted.
   EXPECT_FALSE(SSLErrorHandler::FromWebContents(web_contents()));
 }
+
+TEST_F(SSLErrorHandlerTest, LocalSelfSignedInterstitial) {
+  base::HistogramTester histograms;
+  net::SSLInfo ssl_info;
+  ssl_info.cert = net::ImportCertFromFile(net::GetTestCertsDirectory(),
+                                          "subjectAltName_www_example_com.pem");
+  ssl_info.cert_status = net::CERT_STATUS_COMMON_NAME_INVALID;
+  ssl_info.public_key_hashes.push_back(kCertPublicKeyHashValue);
+
+  // Recreate error handler with the specific error.
+  std::unique_ptr<TestSSLErrorHandlerDelegate> delegate(
+      new TestSSLErrorHandlerDelegate(web_contents(), ssl_info));
+  TestSSLErrorHandlerDelegate* delegate_ptr = delegate.get();
+
+  auto error_handler = std::make_unique<TestSSLErrorHandler>(
+      std::move(delegate), web_contents(),
+      net::ERR_CERT_SELF_SIGNED_LOCAL_NETWORK, ssl_info,
+      /*network_time_tracker=*/nullptr, GURL() /*request_url*/, nullptr);
+
+  error_handler->StartHandlingError();
+  EXPECT_FALSE(delegate_ptr->ssl_interstitial_shown());
+  EXPECT_TRUE(delegate_ptr->local_self_signed_interstitial_shown());
+  histograms.ExpectTotalCount(SSLErrorHandler::GetHistogramNameForTesting(), 2);
+  histograms.ExpectBucketCount(SSLErrorHandler::GetHistogramNameForTesting(),
+                               SSLErrorHandler::HANDLE_ALL, 1);
+  histograms.ExpectBucketCount(
+      SSLErrorHandler::GetHistogramNameForTesting(),
+      SSLErrorHandler::SHOW_LOCAL_SELF_SIGNED_INTERSTITIAL, 1);
+}
diff --git a/components/sharing_message/proto/glic_experimental_triggering.proto b/components/sharing_message/proto/glic_experimental_triggering.proto
index d60a25a..8df39c6 100644
--- a/components/sharing_message/proto/glic_experimental_triggering.proto
+++ b/components/sharing_message/proto/glic_experimental_triggering.proto
@@ -25,10 +25,10 @@
     // Monotonically increasing sequence number to ensure we can sort messages.
     // Since messages can arrive out of order, the sequence number enables an
     // ordered reconstruction of the stream.
-    optional uint64 sender_sequence_number = 3;
+    optional int64 sender_sequence_number = 3;
 
     // For this message, this was the last previously seen sequence number.
-    optional uint64 last_seen_sequence_number = 4;
+    optional int64 last_seen_sequence_number = 4;
   }
   // If the task is already running, this contains the metadata about it.
   optional TaskMetadata task_metadata = 1;
@@ -39,8 +39,15 @@
       optional string initial_prompt = 1;
     }
 
+    message StopActuationRequest {
+      // The reason for stopping. This may be surfaced to the user, so it should
+      // be human readable.
+      optional string stop_reason = 1;
+    }
+
     oneof payload {
       TriggerActuationRequest trigger_actuation_request = 1;
+      StopActuationRequest stop_actuation_request = 2;
     }
   }
 
diff --git a/components/skills/internal/BUILD.gn b/components/skills/internal/BUILD.gn
index 31789b3d..231696a2 100644
--- a/components/skills/internal/BUILD.gn
+++ b/components/skills/internal/BUILD.gn
@@ -45,6 +45,7 @@
     "//base",
     "//components/skills/proto",
     "//components/skills/public:metrics",
+    "//components/skills/public:types",
     "//net",
     "//services/network/public/cpp",
   ]
diff --git a/components/skills/internal/skills_downloader.cc b/components/skills/internal/skills_downloader.cc
index f6d4ccd..a240490 100644
--- a/components/skills/internal/skills_downloader.cc
+++ b/components/skills/internal/skills_downloader.cc
@@ -7,6 +7,7 @@
 #include "base/functional/bind.h"
 #include "components/skills/proto/skill.pb.h"
 #include "components/skills/public/skills_metrics.h"
+#include "components/skills/public/skills_types.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_request_headers.h"
@@ -138,7 +139,7 @@
     last_modified_header_ = last_modified_value;
   }
 
-  auto skills_map = std::make_unique<SkillsMap>();
+  auto skills_map = std::make_unique<SkillIdToProtoMap>();
   for (auto& skill : *skills_list->mutable_skills()) {
     skills_map->insert_or_assign(skill.id(), std::move(skill));
   }
diff --git a/components/skills/internal/skills_downloader.h b/components/skills/internal/skills_downloader.h
index 92d4dc4..ea64a5b5 100644
--- a/components/skills/internal/skills_downloader.h
+++ b/components/skills/internal/skills_downloader.h
@@ -12,6 +12,7 @@
 #include "base/functional/callback_forward.h"
 #include "base/memory/scoped_refptr.h"
 #include "components/skills/proto/skill.pb.h"
+#include "components/skills/public/skills_types.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
@@ -25,15 +26,12 @@
 // users via chrome://skills/discover-skills.
 class SkillsDownloader {
  public:
-  // Map of id to skill.
-  using SkillsMap = absl::flat_hash_map<std::string, skills::proto::Skill>;
-
   explicit SkillsDownloader(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
   ~SkillsDownloader();
 
   using OnFetchCompleteCallback =
-      base::OnceCallback<void(std::unique_ptr<SkillsMap>)>;
+      base::OnceCallback<void(std::unique_ptr<SkillIdToProtoMap>)>;
 
   // Initiates async download process that calls callback on fetch complete.
   // Called on chrome://skills/discover-skills page load or attempt to save a
diff --git a/components/skills/internal/skills_downloader_unittest.cc b/components/skills/internal/skills_downloader_unittest.cc
index 7215ca0..31d295d 100644
--- a/components/skills/internal/skills_downloader_unittest.cc
+++ b/components/skills/internal/skills_downloader_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/test/task_environment.h"
 #include "components/skills/proto/skill.pb.h"
 #include "components/skills/public/skills_metrics.h"
+#include "components/skills/public/skills_types.h"
 #include "net/http/http_status_code.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
@@ -47,11 +48,11 @@
 
  protected:
   // Helper to execute the async fetch synchronously for testing.
-  std::unique_ptr<SkillsDownloader::SkillsMap> FetchDiscoverySkillsSync() {
-    std::unique_ptr<SkillsDownloader::SkillsMap> result_map;
+  std::unique_ptr<SkillIdToProtoMap> FetchDiscoverySkillsSync() {
+    std::unique_ptr<SkillIdToProtoMap> result_map;
     base::RunLoop run_loop;
     downloader_.FetchDiscoverySkills(base::BindLambdaForTesting(
-        [&](std::unique_ptr<SkillsDownloader::SkillsMap> skills_map) {
+        [&](std::unique_ptr<SkillIdToProtoMap> skills_map) {
           result_map = std::move(skills_map);
           run_loop.Quit();
         }));
diff --git a/components/skills/internal/skills_service_impl.cc b/components/skills/internal/skills_service_impl.cc
index ea9acc51..d2928800 100644
--- a/components/skills/internal/skills_service_impl.cc
+++ b/components/skills/internal/skills_service_impl.cc
@@ -184,7 +184,7 @@
   return skills_;
 }
 
-const SkillsService::SkillsMap& SkillsServiceImpl::Get1PSkills() const {
+const SkillIdToProtoMap& SkillsServiceImpl::Get1PSkills() const {
   return first_party_skills_map_;
 }
 
@@ -275,9 +275,9 @@
 }
 
 void SkillsServiceImpl::Handle1pSkillsMap(
-    std::unique_ptr<SkillsMap> skills_map) {
+    std::unique_ptr<SkillIdToProtoMap> skills_map) {
   last_discovery_skills_fetch_time_ = base::Time::Now();
-  SkillsMap* notification_ptr = nullptr;
+  SkillIdToProtoMap* notification_ptr = nullptr;
   // If skills_map is null, this means we don't have an updated value so we
   // shouldn't modify the stored 1p map.
   if (skills_map) {
diff --git a/components/skills/internal/skills_service_impl.h b/components/skills/internal/skills_service_impl.h
index 1c53d4bf..924a716 100644
--- a/components/skills/internal/skills_service_impl.h
+++ b/components/skills/internal/skills_service_impl.h
@@ -16,6 +16,7 @@
 #include "components/skills/internal/skills_downloader.h"
 #include "components/skills/public/skill.h"
 #include "components/skills/public/skills_service.h"
+#include "components/skills/public/skills_types.h"
 #include "components/sync/model/data_type_store.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
@@ -79,8 +80,9 @@
   const Skill* GetSkillById(std::string_view skill_id) const override;
   void RefreshDiscoverySkills() override;
   void FetchDiscoverySkills() override;
-  void Handle1pSkillsMap(std::unique_ptr<SkillsMap> skills_map) override;
-  const SkillsMap& Get1PSkills() const override;
+  void Handle1pSkillsMap(
+      std::unique_ptr<SkillIdToProtoMap> skills_map) override;
+  const SkillIdToProtoMap& Get1PSkills() const override;
   const std::vector<std::unique_ptr<Skill>>& GetSkills() const override;
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
@@ -129,10 +131,10 @@
   std::vector<std::unique_ptr<Skill>> skills_;
 
   // The map of loaded 1p discovery skill protos.
-  SkillsMap first_party_skills_map_;
+  SkillIdToProtoMap first_party_skills_map_;
 
   // The map of loaded 1p discovery skill objects.
-  SkillObjectsMap first_party_skill_objects_map_;
+  SkillIdToSkillMap first_party_skill_objects_map_;
 
   // The list of observers to be notified on changes.
   base::ObserverList<Observer,
diff --git a/components/skills/internal/skills_service_impl_unittest.cc b/components/skills/internal/skills_service_impl_unittest.cc
index f83d2c3..4dfc237c 100644
--- a/components/skills/internal/skills_service_impl_unittest.cc
+++ b/components/skills/internal/skills_service_impl_unittest.cc
@@ -57,7 +57,7 @@
 
   MOCK_METHOD(void,
               Handle1pSkillsMap,
-              (std::unique_ptr<SkillsMap> skills_map),
+              (std::unique_ptr<SkillIdToProtoMap> skills_map),
               (override));
 };
 
@@ -287,7 +287,7 @@
   proto_skill.set_prompt("1P Skill Prompt");
   proto_skill.set_description("1P Skill Description");
 
-  auto skills_map = std::make_unique<SkillsService::SkillsMap>();
+  auto skills_map = std::make_unique<SkillIdToProtoMap>();
   skills_map->insert({"1p_skill_id", proto_skill});
 
   service().Handle1pSkillsMap(std::move(skills_map));
@@ -482,7 +482,7 @@
 
   base::RunLoop run_loop;
   EXPECT_CALL(mock_service, Handle1pSkillsMap(_))
-      .WillOnce([&](std::unique_ptr<SkillsService::SkillsMap> skills_map) {
+      .WillOnce([&](std::unique_ptr<SkillIdToProtoMap> skills_map) {
         EXPECT_EQ(1u, skills_map->size());
         run_loop.Quit();
       });
@@ -503,7 +503,7 @@
 
   base::RunLoop run_loop;
   EXPECT_CALL(mock_service, Handle1pSkillsMap(testing::IsNull()))
-      .WillOnce([&](std::unique_ptr<SkillsService::SkillsMap> skills_map) {
+      .WillOnce([&](std::unique_ptr<SkillIdToProtoMap> skills_map) {
         EXPECT_FALSE(skills_map);
         run_loop.Quit();
       });
diff --git a/components/skills/mocks/mock_skills_service.h b/components/skills/mocks/mock_skills_service.h
index a37bec4..baab340 100644
--- a/components/skills/mocks/mock_skills_service.h
+++ b/components/skills/mocks/mock_skills_service.h
@@ -24,7 +24,7 @@
               GetSkills,
               (),
               (const));
-  MOCK_METHOD(const SkillsMap&, Get1PSkills, (), (const));
+  MOCK_METHOD(const SkillIdToProtoMap&, Get1PSkills, (), (const, override));
   MOCK_METHOD(const Skill*,
               AddSkill,
               (const std::string&,
@@ -51,7 +51,10 @@
   MOCK_METHOD(void, DeleteSkill, (std::string_view, UpdateSource));
   MOCK_METHOD(void, FetchDiscoverySkills, ());
   MOCK_METHOD(void, RefreshDiscoverySkills, ());
-  MOCK_METHOD(void, Handle1pSkillsMap, (std::unique_ptr<SkillsMap>));
+  MOCK_METHOD(void,
+              Handle1pSkillsMap,
+              (std::unique_ptr<SkillIdToProtoMap>),
+              (override));
   MOCK_METHOD(void, AddObserver, (Observer*));
   MOCK_METHOD(void, RemoveObserver, (Observer*));
   MOCK_METHOD(base::WeakPtr<syncer::DataTypeControllerDelegate>,
diff --git a/components/skills/public/BUILD.gn b/components/skills/public/BUILD.gn
index 4de3b67..6048543 100644
--- a/components/skills/public/BUILD.gn
+++ b/components/skills/public/BUILD.gn
@@ -40,6 +40,15 @@
   ]
 }
 
+source_set("types") {
+  sources = [ "skills_types.h" ]
+  public_deps = [
+    ":skill",
+    "//base",
+    "//components/skills/proto",
+  ]
+}
+
 source_set("public") {
   sources = [
     "skills_service.cc",
@@ -49,6 +58,7 @@
   public_deps = [
     ":metrics",
     ":skill",
+    ":types",
     "//base",
     "//components/keyed_service/core",
     "//components/skills/internal:downloader",
diff --git a/components/skills/public/skills_service.h b/components/skills/public/skills_service.h
index 3bddf75..ca671a5 100644
--- a/components/skills/public/skills_service.h
+++ b/components/skills/public/skills_service.h
@@ -18,6 +18,7 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/skills/internal/skills_downloader.h"
 #include "components/skills/proto/skill.pb.h"
+#include "components/skills/public/skills_types.h"
 #include "components/sync/protocol/skill_specifics.pb.h"
 #include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
 
@@ -68,10 +69,6 @@
     kReshown,
   };
 
-  // Map of id to skill.
-  using SkillsMap = absl::flat_hash_map<std::string, skills::proto::Skill>;
-  using SkillObjectsMap = absl::flat_hash_map<std::string, Skill>;
-
   // Observer for the service notifications.
   class Observer : public base::CheckedObserver {
    public:
@@ -95,8 +92,8 @@
 
     // Called when the service has completed a download of 1P skills. Receives
     // new map or nullptr if map has not changed.
-    virtual void OnDiscoverySkillsUpdated(
-        const SkillsService::SkillsMap* skills_map) {}
+    virtual void OnDiscoverySkillsUpdated(const SkillIdToProtoMap* skills_map) {
+    }
 
     // Called when the service is shutting down. Observers should remove
     // themselves.
@@ -165,7 +162,7 @@
   // Returns a const reference to the currently loaded 1p skills. If skills have
   // not been loaded yet, returns an empty map. The service does not have to be
   // in a kReady state since these skills are loaded from a SCS file.
-  virtual const SkillsMap& Get1PSkills() const = 0;
+  virtual const SkillIdToProtoMap& Get1PSkills() const = 0;
 
   // Registers an observer for the service notifications.
   virtual void AddObserver(Observer* observer) = 0;
@@ -186,7 +183,8 @@
 
   // Called on download complete of 1p skills. If the download fails or the file
   // has not been modified skills_map is null. Notifies observers.
-  virtual void Handle1pSkillsMap(std::unique_ptr<SkillsMap> skills_map) = 0;
+  virtual void Handle1pSkillsMap(
+      std::unique_ptr<SkillIdToProtoMap> skills_map) = 0;
 
   // Returns controller delegate for the sync service.
   virtual base::WeakPtr<syncer::DataTypeControllerDelegate>
diff --git a/components/skills/public/skills_types.h b/components/skills/public/skills_types.h
new file mode 100644
index 0000000..ca5cc43
--- /dev/null
+++ b/components/skills/public/skills_types.h
@@ -0,0 +1,30 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SKILLS_PUBLIC_SKILLS_TYPES_H_
+#define COMPONENTS_SKILLS_PUBLIC_SKILLS_TYPES_H_
+
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "components/skills/proto/skill.pb.h"
+#include "components/skills/public/skill.h"
+#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
+
+namespace skills {
+
+// Map of Skill IDs to its proto representation.
+using SkillIdToProtoMap =
+    absl::flat_hash_map<std::string, skills::proto::Skill>;
+
+// Map of Skill categories to a vector of Skill structs.
+using SkillCategoryToSkillMap = base::flat_map<std::string, std::vector<Skill>>;
+
+// Map of Skill IDs to Skill structs.
+using SkillIdToSkillMap = absl::flat_hash_map<std::string, Skill>;
+
+}  // namespace skills
+
+#endif  // COMPONENTS_SKILLS_PUBLIC_SKILLS_TYPES_H_
diff --git a/components/subresource_filter/core/common/unindexed_ruleset.cc b/components/subresource_filter/core/common/unindexed_ruleset.cc
index 1522a097d..bcf59bd 100644
--- a/components/subresource_filter/core/common/unindexed_ruleset.cc
+++ b/components/subresource_filter/core/common/unindexed_ruleset.cc
@@ -45,7 +45,7 @@
 
 UnindexedRulesetWriter::~UnindexedRulesetWriter() {
   CHECK_EQ(pending_chunk_.url_rules_size(), 0);
-  CHECK_EQ(pending_chunk_.css_rules_size(), 0);
+  CHECK_EQ(pending_chunk_.style_rules_size(), 0);
 }
 
 bool UnindexedRulesetWriter::AddUrlRule(const proto::UrlRule& rule) {
diff --git a/components/subresource_filter/core/common/unindexed_ruleset.h b/components/subresource_filter/core/common/unindexed_ruleset.h
index d923103..232b4d0 100644
--- a/components/subresource_filter/core/common/unindexed_ruleset.h
+++ b/components/subresource_filter/core/common/unindexed_ruleset.h
@@ -80,7 +80,7 @@
   // Places the |rule| to the current chunk, and serializes the chunk if it has
   // grown up to |max_rules_per_chunk|.
   bool AddUrlRule(const url_pattern_index::proto::UrlRule& rule);
-  // TODO(pkalinnikov): Implement AddCssRule when needed.
+  // TODO(pkalinnikov): Implement AddStyleRule when needed.
 
   // Finalizes the serialization of the unindexed ruleset, i.e., writes the
   // final chunk of rules, if there are any still pending. This method *should*
diff --git a/components/subresource_filter/tools/rule_parser/rule.cc b/components/subresource_filter/tools/rule_parser/rule.cc
index 582a025..47eb53b0 100644
--- a/components/subresource_filter/tools/rule_parser/rule.cc
+++ b/components/subresource_filter/tools/rule_parser/rule.cc
@@ -140,16 +140,16 @@
   CanonicalizeDomainList(&domains);
 }
 
-CssRule::CssRule() = default;
+StyleRule::StyleRule() = default;
 
-CssRule::CssRule(const CssRule&) = default;
+StyleRule::StyleRule(const StyleRule&) = default;
 
-CssRule::~CssRule() = default;
+StyleRule::~StyleRule() = default;
 
-CssRule& CssRule::operator=(const CssRule&) = default;
+StyleRule& StyleRule::operator=(const StyleRule&) = default;
 
-url_pattern_index::proto::CssRule CssRule::ToProtobuf() const {
-  url_pattern_index::proto::CssRule result;
+url_pattern_index::proto::StyleRule StyleRule::ToProtobuf() const {
+  url_pattern_index::proto::StyleRule result;
 
   result.set_semantics(
       is_allowlist ? url_pattern_index::proto::RULE_SEMANTICS_ALLOWLIST
@@ -168,14 +168,14 @@
     }
   }
 
-  if (!css_selector.empty()) {
-    result.set_css_selector(css_selector);
+  if (!style_selector.empty()) {
+    result.set_style_selector(style_selector);
   }
 
   return result;
 }
 
-void CssRule::Canonicalize() {
+void StyleRule::Canonicalize() {
   CanonicalizeDomainList(&domains);
 }
 
@@ -330,7 +330,7 @@
   return result;
 }
 
-std::string ToString(const url_pattern_index::proto::CssRule& rule) {
+std::string ToString(const url_pattern_index::proto::StyleRule& rule) {
   std::string result;
   if (rule.domains_size()) {
     DomainListJoin(rule.domains(), ',', &result);
@@ -347,15 +347,15 @@
       LOG(FATAL);
   }
 
-  return result += rule.css_selector();
+  return result += rule.style_selector();
 }
 
 std::ostream& operator<<(std::ostream& os, const UrlRule& rule) {
   return os << "UrlRule(" << ToString(rule.ToProtobuf()) << ")";
 }
 
-std::ostream& operator<<(std::ostream& os, const CssRule& rule) {
-  return os << "CssRule(" << ToString(rule.ToProtobuf()) << ")";
+std::ostream& operator<<(std::ostream& os, const StyleRule& rule) {
+  return os << "StyleRule(" << ToString(rule.ToProtobuf()) << ")";
 }
 
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/tools/rule_parser/rule.h b/components/subresource_filter/tools/rule_parser/rule.h
index 38f2521..3d7cea1 100644
--- a/components/subresource_filter/tools/rule_parser/rule.h
+++ b/components/subresource_filter/tools/rule_parser/rule.h
@@ -93,18 +93,18 @@
 };
 
 // A single CSS element hiding rule as parsed from EasyList.
-struct CssRule {
+struct StyleRule {
   // Constructs a default empty blocklist rule (no domains, empty selector).
-  CssRule();
+  StyleRule();
 
-  CssRule(const CssRule&);
-  ~CssRule();
-  CssRule& operator=(const CssRule&);
+  StyleRule(const StyleRule&);
+  ~StyleRule();
+  StyleRule& operator=(const StyleRule&);
 
-  friend bool operator==(const CssRule&, const CssRule&) = default;
+  friend bool operator==(const StyleRule&, const StyleRule&) = default;
 
   // Returns a protobuf representation of the rule.
-  url_pattern_index::proto::CssRule ToProtobuf() const;
+  url_pattern_index::proto::StyleRule ToProtobuf() const;
 
   // Canonicalizes the rule, i.e. orders the |domains| list properly.
   void Canonicalize();
@@ -114,7 +114,7 @@
   // The list of domains, same as UrlRule::domains.
   std::vector<std::string> domains;
   // A CSS selector as specified in http://www.w3.org/TR/css3-selectors.
-  std::string css_selector;
+  std::string style_selector;
 };
 
 // Sorts domain patterns in decreasing order of length (and alphabetically
@@ -123,11 +123,11 @@
 
 // Converts protobuf |rule| into its canonical EasyList string representation.
 std::string ToString(const url_pattern_index::proto::UrlRule& rule);
-std::string ToString(const url_pattern_index::proto::CssRule& rule);
+std::string ToString(const url_pattern_index::proto::StyleRule& rule);
 
 // For testing.
 std::ostream& operator<<(std::ostream& os, const UrlRule& rule);
-std::ostream& operator<<(std::ostream& os, const CssRule& rule);
+std::ostream& operator<<(std::ostream& os, const StyleRule& rule);
 
 }  // namespace subresource_filter
 
diff --git a/components/subresource_filter/tools/rule_parser/rule_parser.cc b/components/subresource_filter/tools/rule_parser/rule_parser.cc
index 6c54f8e..32d60823 100644
--- a/components/subresource_filter/tools/rule_parser/rule_parser.cc
+++ b/components/subresource_filter/tools/rule_parser/rule_parser.cc
@@ -254,7 +254,7 @@
   }
 
   if (css_separator_pos != std::string_view::npos) {
-    return rule_type_ = ParseCssRule(line, part, css_separator_pos);
+    return rule_type_ = ParseStyleRule(line, part, css_separator_pos);
   }
   // Else assume we read a URL filtering rule.
   return rule_type_ = ParseUrlRule(line, part);
@@ -420,11 +420,11 @@
   return true;
 }
 
-RuleType RuleParser::ParseCssRule(std::string_view origin,
-                                  std::string_view part,
-                                  size_t css_section_start) {
+RuleType RuleParser::ParseStyleRule(std::string_view origin,
+                                    std::string_view part,
+                                    size_t css_section_start) {
   CHECK(part.data() >= origin.data());
-  css_rule_ = CssRule();
+  style_rule_ = StyleRule();
 
   // Check for a list of domains.
   if (css_section_start) {
@@ -434,7 +434,7 @@
                                          base::SPLIT_WANT_NONEMPTY);
     for (std::string_view domain : pieces) {
       CHECK(!domain.empty());
-      css_rule_.domains.push_back(std::string(domain));
+      style_rule_.domains.push_back(std::string(domain));
     }
   }
 
@@ -444,7 +444,7 @@
     return url_pattern_index::proto::RULE_TYPE_UNSPECIFIED;
   }
   if (part[0] == '@') {
-    css_rule_.is_allowlist = true;
+    style_rule_.is_allowlist = true;
     part.remove_prefix(1);
   }
   if (part.empty() || part[0] != '#') {
@@ -458,9 +458,9 @@
     return url_pattern_index::proto::RULE_TYPE_UNSPECIFIED;
   }
 
-  css_rule_.css_selector = std::string(part);
-  css_rule_.Canonicalize();
-  return url_pattern_index::proto::RULE_TYPE_CSS;
+  style_rule_.style_selector = std::string(part);
+  style_rule_.Canonicalize();
+  return url_pattern_index::proto::RULE_TYPE_STYLE;
 }
 
 void RuleParser::SetParseError(ParseError::ErrorCode code,
diff --git a/components/subresource_filter/tools/rule_parser/rule_parser.h b/components/subresource_filter/tools/rule_parser/rule_parser.h
index 8760d84c..6574b4a 100644
--- a/components/subresource_filter/tools/rule_parser/rule_parser.h
+++ b/components/subresource_filter/tools/rule_parser/rule_parser.h
@@ -89,8 +89,8 @@
   const UrlRule& url_rule() const { return url_rule_; }
 
   // Gets the last parsed CSS element hiding rule. The result is undefined if
-  // rule_type() != RULE_TYPE_CSS.
-  const CssRule& css_rule() const { return css_rule_; }
+  // rule_type() != RULE_TYPE_STYLE.
+  const StyleRule& style_rule() const { return style_rule_; }
 
  private:
   // Parses the |part| and saves parsed URL filtering rule to the |url_rule_|
@@ -104,13 +104,13 @@
   // correctly. Otherwise sets an error in |parse_error_| and returns false.
   bool ParseUrlRuleOptions(std::string_view origin, std::string_view options);
 
-  // Parses the |part| and saves parsed CSS rule to the |css_rule_| member.
+  // Parses the |part| and saves parsed CSS rule to the |style_rule_| member.
   // |css_section_start| denotes a position of '#' in the |part|, used to
   // separate a CSS selector. Returns true iff the line is a well-formed CSS
   // rule. Sets |parse_error_| on error.
-  RuleType ParseCssRule(std::string_view origin,
-                        std::string_view part,
-                        size_t css_section_start);
+  RuleType ParseStyleRule(std::string_view origin,
+                          std::string_view part,
+                          size_t css_section_start);
 
   // Sets |parse_error_| to contain specific error, starting at |error_begin|.
   void SetParseError(ParseError::ErrorCode code,
@@ -120,7 +120,7 @@
   ParseError parse_error_;
   RuleType rule_type_;
   UrlRule url_rule_;
-  CssRule css_rule_;
+  StyleRule style_rule_;
 };
 
 // Pretty-prints the parsing |error| to |out|, e.g. like this:
diff --git a/components/subresource_filter/tools/rule_parser/rule_parser_unittest.cc b/components/subresource_filter/tools/rule_parser/rule_parser_unittest.cc
index fcaaaf0..ed38044 100644
--- a/components/subresource_filter/tools/rule_parser/rule_parser_unittest.cc
+++ b/components/subresource_filter/tools/rule_parser/rule_parser_unittest.cc
@@ -34,9 +34,9 @@
   EXPECT_EQ(canonicalized_rule, parser.url_rule());
 }
 
-void ParseAndExpectCssRule(std::string_view line,
-                           const CssRule& expected_rule) {
-  CssRule canonicalized_rule = expected_rule;
+void ParseAndExpectStyleRule(std::string_view line,
+                             const StyleRule& expected_rule) {
+  StyleRule canonicalized_rule = expected_rule;
   canonicalized_rule.Canonicalize();
 
   RuleParser parser;
@@ -46,8 +46,8 @@
   EXPECT_TRUE(parser.parse_error().line.empty());
   EXPECT_EQ(std::string::npos, parser.parse_error().error_index);
 
-  EXPECT_EQ(url_pattern_index::proto::RULE_TYPE_CSS, parser.rule_type());
-  EXPECT_EQ(canonicalized_rule, parser.css_rule());
+  EXPECT_EQ(url_pattern_index::proto::RULE_TYPE_STYLE, parser.rule_type());
+  EXPECT_EQ(canonicalized_rule, parser.style_rule());
 }
 
 }  // namespace
@@ -314,31 +314,31 @@
   }
 }
 
-TEST(RuleParserTest, ParseCssRules) {
+TEST(RuleParserTest, ParseStyleRules) {
   static const std::string kCssSelector = "div.blocked_class";
   static const std::string kTestDomain = "example.com";
   static const std::string kTestSubDomain = "however.example.com";
 
-  CssRule expected_rule;
-  expected_rule.css_selector = kCssSelector;
+  StyleRule expected_rule;
+  expected_rule.style_selector = kCssSelector;
 
-  ParseAndExpectCssRule("##" + kCssSelector, expected_rule);
-  ParseAndExpectCssRule(" ##" + kCssSelector, expected_rule);
-  ParseAndExpectCssRule(" \t\t##" + kCssSelector, expected_rule);
+  ParseAndExpectStyleRule("##" + kCssSelector, expected_rule);
+  ParseAndExpectStyleRule(" ##" + kCssSelector, expected_rule);
+  ParseAndExpectStyleRule(" \t\t##" + kCssSelector, expected_rule);
 
   expected_rule.domains.push_back(kTestDomain);
-  ParseAndExpectCssRule(kTestDomain + "##" + kCssSelector, expected_rule);
+  ParseAndExpectStyleRule(kTestDomain + "##" + kCssSelector, expected_rule);
 
   expected_rule.is_allowlist = true;
-  ParseAndExpectCssRule(kTestDomain + "#@#" + kCssSelector, expected_rule);
+  ParseAndExpectStyleRule(kTestDomain + "#@#" + kCssSelector, expected_rule);
 
   expected_rule.is_allowlist = false;
   expected_rule.domains.push_back("~" + kTestSubDomain);
-  ParseAndExpectCssRule(
+  ParseAndExpectStyleRule(
       kTestDomain + ",~" + kTestSubDomain + "##" + kCssSelector, expected_rule);
 
   expected_rule.is_allowlist = true;
-  ParseAndExpectCssRule(
+  ParseAndExpectStyleRule(
       kTestDomain + ",~" + kTestSubDomain + "#@#" + kCssSelector,
       expected_rule);
 }
diff --git a/components/subresource_filter/tools/rule_parser/rule_unittest.cc b/components/subresource_filter/tools/rule_parser/rule_unittest.cc
index b0b9446..e6f73fc07 100644
--- a/components/subresource_filter/tools/rule_parser/rule_unittest.cc
+++ b/components/subresource_filter/tools/rule_parser/rule_unittest.cc
@@ -178,11 +178,11 @@
   EXPECT_EQ("example.com$image,~image", ToString(rule.ToProtobuf()));
 }
 
-TEST(RuleTest, CssRuleToString) {
-  CssRule rule;
+TEST(RuleTest, StyleRuleToString) {
+  StyleRule rule;
   rule.is_allowlist = true;
   rule.domains = {"example.com", "~exception.example.com"};
-  rule.css_selector = "#example-id";
+  rule.style_selector = "#example-id";
 
   EXPECT_EQ("example.com,~exception.example.com#@##example-id",
             ToString(rule.ToProtobuf()));
diff --git a/components/subresource_filter/tools/ruleset_converter/rule_stream.cc b/components/subresource_filter/tools/ruleset_converter/rule_stream.cc
index 04e916f..82a876b 100644
--- a/components/subresource_filter/tools/ruleset_converter/rule_stream.cc
+++ b/components/subresource_filter/tools/ruleset_converter/rule_stream.cc
@@ -45,11 +45,11 @@
       is_reading_url_rules_ = false;
       rule_index_ = 0;
     }
-    if (!is_reading_url_rules_ && rule_index_ >= rules_.css_rules_size()) {
+    if (!is_reading_url_rules_ && rule_index_ >= rules_.style_rules_size()) {
       return url_pattern_index::proto::RULE_TYPE_UNSPECIFIED;
     }
     return is_reading_url_rules_ ? url_pattern_index::proto::RULE_TYPE_URL
-                                 : url_pattern_index::proto::RULE_TYPE_CSS;
+                                 : url_pattern_index::proto::RULE_TYPE_STYLE;
   }
 
   const url_pattern_index::proto::UrlRule& GetUrlRule() {
@@ -57,9 +57,9 @@
     return rules_.url_rules(rule_index_);
   }
 
-  const url_pattern_index::proto::CssRule& GetCssRule() {
-    CHECK(!is_reading_url_rules_ && rule_index_ < rules_.css_rules_size());
-    return rules_.css_rules(rule_index_);
+  const url_pattern_index::proto::StyleRule& GetStyleRule() {
+    CHECK(!is_reading_url_rules_ && rule_index_ < rules_.style_rules_size());
+    return rules_.style_rules(rule_index_);
   }
 
  private:
@@ -100,9 +100,9 @@
     return parser_.url_rule().ToProtobuf();
   }
 
-  url_pattern_index::proto::CssRule GetCssRule() override {
-    CHECK_EQ(url_pattern_index::proto::RULE_TYPE_CSS, parser_.rule_type());
-    return parser_.css_rule().ToProtobuf();
+  url_pattern_index::proto::StyleRule GetStyleRule() override {
+    CHECK_EQ(url_pattern_index::proto::RULE_TYPE_STYLE, parser_.rule_type());
+    return parser_.style_rule().ToProtobuf();
   }
 
  private:
@@ -125,7 +125,7 @@
     return !output_->bad();
   }
 
-  bool PutCssRule(const url_pattern_index::proto::CssRule& rule) override {
+  bool PutStyleRule(const url_pattern_index::proto::StyleRule& rule) override {
     std::string line = ToString(rule) + '\n';
     output_->write(line.data(), line.size());
     return !output_->bad();
@@ -160,8 +160,8 @@
   url_pattern_index::proto::UrlRule GetUrlRule() override {
     return impl_->GetUrlRule();
   }
-  url_pattern_index::proto::CssRule GetCssRule() override {
-    return impl_->GetCssRule();
+  url_pattern_index::proto::StyleRule GetStyleRule() override {
+    return impl_->GetStyleRule();
   }
 
  private:
@@ -184,8 +184,8 @@
     return true;
   }
 
-  bool PutCssRule(const url_pattern_index::proto::CssRule& rule) override {
-    *all_rules_.add_css_rules() = rule;
+  bool PutStyleRule(const url_pattern_index::proto::StyleRule& rule) override {
+    *all_rules_.add_style_rules() = rule;
     return true;
   }
 
@@ -236,8 +236,8 @@
   url_pattern_index::proto::UrlRule GetUrlRule() override {
     return impl_->GetUrlRule();
   }
-  url_pattern_index::proto::CssRule GetCssRule() override {
-    return impl_->GetCssRule();
+  url_pattern_index::proto::StyleRule GetStyleRule() override {
+    return impl_->GetStyleRule();
   }
 
  private:
@@ -275,7 +275,7 @@
     return ruleset_writer_.AddUrlRule(rule);
   }
 
-  bool PutCssRule(const url_pattern_index::proto::CssRule& rule) override {
+  bool PutStyleRule(const url_pattern_index::proto::StyleRule& rule) override {
     return true;
   }
 
@@ -379,7 +379,7 @@
 
 bool TransferRules(RuleInputStream* input,
                    RuleOutputStream* url_rules_output,
-                   RuleOutputStream* css_rules_output,
+                   RuleOutputStream* style_rules_output,
                    int chrome_version) {
   while (true) {
     auto rule_type = input->FetchNextRule();
@@ -397,9 +397,9 @@
         }
         break;
       }
-      case url_pattern_index::proto::RULE_TYPE_CSS:
-        if (css_rules_output) {
-          css_rules_output->PutCssRule(input->GetCssRule());
+      case url_pattern_index::proto::RULE_TYPE_STYLE:
+        if (style_rules_output) {
+          style_rules_output->PutStyleRule(input->GetStyleRule());
         }
         break;
       case url_pattern_index::proto::RULE_TYPE_COMMENT:
diff --git a/components/subresource_filter/tools/ruleset_converter/rule_stream.h b/components/subresource_filter/tools/ruleset_converter/rule_stream.h
index 4dd70f3..e486f9c0 100644
--- a/components/subresource_filter/tools/ruleset_converter/rule_stream.h
+++ b/components/subresource_filter/tools/ruleset_converter/rule_stream.h
@@ -33,7 +33,7 @@
   virtual url_pattern_index::proto::UrlRule GetUrlRule() = 0;
 
   // Same as above, but for CSS rules.
-  virtual url_pattern_index::proto::CssRule GetCssRule() = 0;
+  virtual url_pattern_index::proto::StyleRule GetStyleRule() = 0;
 
   // Factory method to produce a RuleInputStream reading rules from |input| in
   // the specified |format|. If the |format| is not supported, then returns
@@ -51,7 +51,8 @@
   // The following methods are used to write rules into the stream. Return false
   // iff an error occurred.
   virtual bool PutUrlRule(const url_pattern_index::proto::UrlRule& rule) = 0;
-  virtual bool PutCssRule(const url_pattern_index::proto::CssRule& rule) = 0;
+  virtual bool PutStyleRule(
+      const url_pattern_index::proto::StyleRule& rule) = 0;
 
   // Finalizes the serialization. Returns false on error.
   virtual bool Finish() = 0;
@@ -64,7 +65,7 @@
 };
 
 // Reads rules from the |input| stream, puts URL rules to |url_rules_output|,
-// and CSS rules to |css_rules_output|. Returns false iff an error occurred
+// and CSS rules to |style_rules_output|. Returns false iff an error occurred
 // either in the input or one of the output streams.
 //
 // If one of the output streams is nullptr, the corresponding rules are
@@ -79,7 +80,7 @@
 // ruleset should remain intact.
 bool TransferRules(RuleInputStream* input,
                    RuleOutputStream* url_rules_output,
-                   RuleOutputStream* css_rules_output,
+                   RuleOutputStream* style_rules_output,
                    int chrome_version = 0);
 
 // This function is used by TransferRules to amend a stream of UrlRules
diff --git a/components/subresource_filter/tools/ruleset_converter/rule_stream_unittest.cc b/components/subresource_filter/tools/ruleset_converter/rule_stream_unittest.cc
index 4923f908..be9676b5 100644
--- a/components/subresource_filter/tools/ruleset_converter/rule_stream_unittest.cc
+++ b/components/subresource_filter/tools/ruleset_converter/rule_stream_unittest.cc
@@ -65,7 +65,7 @@
 // Generates and returns many rules in text format.
 std::vector<std::string> GetManyRules() {
   constexpr size_t kNumberOfUrlRules = 10123;
-  constexpr size_t kNumberOfCssRules = 5321;
+  constexpr size_t kNumberOfStyleRules = 5321;
 
   std::vector<std::string> text_rules;
 
@@ -86,7 +86,7 @@
     text_rules.push_back(text_rule);
   }
 
-  for (size_t i = 0; i != kNumberOfCssRules; ++i) {
+  for (size_t i = 0; i != kNumberOfStyleRules; ++i) {
     std::string text_rule = "domain.com";
     if (i & 1) {
       text_rule += ",~but_not.domain.com";
@@ -110,7 +110,7 @@
   TestRulesetContents contents;
 
   bool take_url_rule = true;
-  bool take_css_rule = true;
+  bool take_style_rule = true;
   url_pattern_index::proto::RuleType rule_type =
       url_pattern_index::proto::RULE_TYPE_UNSPECIFIED;
   while ((rule_type = input->FetchNextRule()) !=
@@ -121,11 +121,11 @@
       }
       take_url_rule = !take_url_rule;
     } else {
-      ASSERT_EQ(url_pattern_index::proto::RULE_TYPE_CSS, rule_type);
-      if (take_css_rule) {
-        contents.css_rules.push_back(input->GetCssRule());
+      ASSERT_EQ(url_pattern_index::proto::RULE_TYPE_STYLE, rule_type);
+      if (take_style_rule) {
+        contents.style_rules.push_back(input->GetStyleRule());
       }
-      take_css_rule = !take_css_rule;
+      take_style_rule = !take_style_rule;
     }
   }
 
@@ -166,8 +166,8 @@
   for (size_t i = 0, size = contents.url_rules.size(); i < size; i += 2) {
     half_contents.url_rules.push_back(contents.url_rules[i]);
   }
-  for (size_t i = 0, size = contents.css_rules.size(); i < size; i += 2) {
-    half_contents.css_rules.push_back(contents.css_rules[i]);
+  for (size_t i = 0, size = contents.style_rules.size(); i < size; i += 2) {
+    half_contents.style_rules.push_back(contents.style_rules[i]);
   }
 
   TestRulesetContents half_url_rules;
@@ -218,11 +218,11 @@
   input.reset();
   output.reset();
 
-  contents.css_rules.clear();
+  contents.style_rules.clear();
   EXPECT_EQ(target_ruleset.ReadContents(), contents);
 }
 
-TEST(RuleStreamTest, TransferCssRulesToOneStream) {
+TEST(RuleStreamTest, TransferStyleRulesToOneStream) {
   TestRulesetContents contents;
   contents.AppendRules(GetManyRules());
 
@@ -290,7 +290,7 @@
                   return rule.url_pattern_type() ==
                          url_pattern_index::proto::URL_PATTERN_TYPE_REGEXP;
                 });
-  contents.css_rules.clear();
+  contents.style_rules.clear();
   EXPECT_EQ(target_ruleset.ReadContents(), contents);
 }
 
@@ -353,7 +353,7 @@
   }
 
   EXPECT_EQ(number_of_correct_rules,
-            contents.url_rules.size() + contents.css_rules.size());
+            contents.url_rules.size() + contents.style_rules.size());
   EXPECT_EQ(target_ruleset.ReadContents(), contents);
 }
 
diff --git a/components/subresource_filter/tools/ruleset_converter/ruleset_converter.cc b/components/subresource_filter/tools/ruleset_converter/ruleset_converter.cc
index 4bd99952..3d4b808 100644
--- a/components/subresource_filter/tools/ruleset_converter/ruleset_converter.cc
+++ b/components/subresource_filter/tools/ruleset_converter/ruleset_converter.cc
@@ -31,7 +31,7 @@
 
   std::unique_ptr<RuleOutputStream> primary_output;
   std::unique_ptr<RuleOutputStream> secondary_output;
-  subresource_filter::RuleOutputStream* css_rules_output = nullptr;
+  subresource_filter::RuleOutputStream* style_rules_output = nullptr;
 
   auto make_output = [](const base::FilePath& path, RulesetFormat format) {
     return RuleOutputStream::Create(
@@ -42,16 +42,16 @@
 
   if (!output_file_.empty()) {
     primary_output = make_output(output_file_, output_format_);
-    css_rules_output = primary_output.get();
+    style_rules_output = primary_output.get();
   } else {
     if (!output_url_.empty()) {
       primary_output = make_output(output_url_, output_format_);
     }
     if (output_css_ == output_url_) {
-      css_rules_output = primary_output.get();
+      style_rules_output = primary_output.get();
     } else if (!output_css_.empty()) {
       secondary_output = make_output(output_css_, output_format_);
-      css_rules_output = secondary_output.get();
+      style_rules_output = secondary_output.get();
     }
   }
 
@@ -70,7 +70,7 @@
         input_format_);
     CHECK(input_stream);
     CHECK(TransferRules(input_stream.get(), primary_output.get(),
-                        css_rules_output, chrome_version_));
+                        style_rules_output, chrome_version_));
   }
 
   if (primary_output) {
diff --git a/components/subresource_filter/tools/ruleset_converter/ruleset_converter_unittest.cc b/components/subresource_filter/tools/ruleset_converter/ruleset_converter_unittest.cc
index d9234ee..776c449 100644
--- a/components/subresource_filter/tools/ruleset_converter/ruleset_converter_unittest.cc
+++ b/components/subresource_filter/tools/ruleset_converter/ruleset_converter_unittest.cc
@@ -158,7 +158,7 @@
   TestRulesetContents expected_url_output;
   expected_url_output.url_rules = test_content_.url_rules;
   TestRulesetContents expected_css_output;
-  expected_url_output.css_rules = test_content_.css_rules;
+  expected_url_output.style_rules = test_content_.style_rules;
 
   EXPECT_EQ(expected_url_output, output_url.ReadContents());
   EXPECT_EQ(expected_css_output, output_css.ReadContents());
@@ -206,7 +206,7 @@
   TestRulesetContents expected_url_output;
 
   TestRulesetContents expected_css_output;
-  expected_css_output.css_rules = test_content_.css_rules;
+  expected_css_output.style_rules = test_content_.style_rules;
 
   EXPECT_EQ(expected_url_output, output_url.ReadContents());
   EXPECT_EQ(expected_css_output, output_css.ReadContents());
diff --git a/components/subresource_filter/tools/ruleset_converter/ruleset_test_util.cc b/components/subresource_filter/tools/ruleset_converter/ruleset_test_util.cc
index a590cc46..30529c7 100644
--- a/components/subresource_filter/tools/ruleset_converter/ruleset_test_util.cc
+++ b/components/subresource_filter/tools/ruleset_converter/ruleset_test_util.cc
@@ -18,7 +18,7 @@
 TestRulesetContents::~TestRulesetContents() = default;
 
 TestRulesetContents::TestRulesetContents(const TestRulesetContents& other)
-    : url_rules(other.url_rules), css_rules(other.css_rules) {}
+    : url_rules(other.url_rules), style_rules(other.style_rules) {}
 
 void TestRulesetContents::AppendRules(
     const std::vector<std::string>& text_rules,
@@ -30,8 +30,8 @@
       case url_pattern_index::proto::RULE_TYPE_URL:
         url_rules.push_back(parser.url_rule().ToProtobuf());
         break;
-      case url_pattern_index::proto::RULE_TYPE_CSS:
-        css_rules.push_back(parser.css_rule().ToProtobuf());
+      case url_pattern_index::proto::RULE_TYPE_STYLE:
+        style_rules.push_back(parser.style_rule().ToProtobuf());
         break;
       case url_pattern_index::proto::RULE_TYPE_UNSPECIFIED:
         ASSERT_TRUE(allow_errors);
@@ -45,13 +45,13 @@
 void TestRulesetContents::AppendParsedRules(const TestRulesetContents& other) {
   url_rules.insert(url_rules.end(), other.url_rules.begin(),
                    other.url_rules.end());
-  css_rules.insert(css_rules.end(), other.css_rules.begin(),
-                   other.css_rules.end());
+  style_rules.insert(style_rules.end(), other.style_rules.begin(),
+                     other.style_rules.end());
 }
 
 bool TestRulesetContents::operator==(const TestRulesetContents& other) const {
   if (url_rules.size() != other.url_rules.size() ||
-      css_rules.size() != other.css_rules.size()) {
+      style_rules.size() != other.style_rules.size()) {
     return false;
   }
   for (size_t i = 0; i < url_rules.size(); ++i) {
@@ -59,8 +59,8 @@
       return false;
     }
   }
-  for (size_t i = 0; i < css_rules.size(); ++i) {
-    if (!AreCssRulesEqual(css_rules[i], other.css_rules[i])) {
+  for (size_t i = 0; i < style_rules.size(); ++i) {
+    if (!AreStyleRulesEqual(style_rules[i], other.style_rules[i])) {
       return false;
     }
   }
@@ -72,7 +72,7 @@
   for (const auto& rule : contents.url_rules) {
     out << ToString(rule) << "\n";
   }
-  for (const auto& rule : contents.css_rules) {
+  for (const auto& rule : contents.style_rules) {
     out << ToString(rule) << "\n";
   }
   return out;
@@ -111,8 +111,8 @@
   for (const auto& rule : contents.url_rules) {
     EXPECT_TRUE(output->PutUrlRule(rule));
   }
-  for (const auto& rule : contents.css_rules) {
-    EXPECT_TRUE(output->PutCssRule(rule));
+  for (const auto& rule : contents.style_rules) {
+    EXPECT_TRUE(output->PutStyleRule(rule));
   }
   EXPECT_TRUE(output->Finish());
 }
@@ -127,8 +127,8 @@
     if (rule_type == url_pattern_index::proto::RULE_TYPE_URL) {
       contents.url_rules.push_back(input->GetUrlRule());
     } else {
-      CHECK_EQ(url_pattern_index::proto::RULE_TYPE_CSS, rule_type);
-      contents.css_rules.push_back(input->GetCssRule());
+      CHECK_EQ(url_pattern_index::proto::RULE_TYPE_STYLE, rule_type);
+      contents.style_rules.push_back(input->GetStyleRule());
     }
   }
   return contents;
@@ -139,8 +139,8 @@
   return first.SerializeAsString() == second.SerializeAsString();
 }
 
-bool AreCssRulesEqual(const url_pattern_index::proto::CssRule& first,
-                      const url_pattern_index::proto::CssRule& second) {
+bool AreStyleRulesEqual(const url_pattern_index::proto::StyleRule& first,
+                        const url_pattern_index::proto::StyleRule& second) {
   return first.SerializeAsString() == second.SerializeAsString();
 }
 
diff --git a/components/subresource_filter/tools/ruleset_converter/ruleset_test_util.h b/components/subresource_filter/tools/ruleset_converter/ruleset_test_util.h
index a2ce371..8bd01dd6 100644
--- a/components/subresource_filter/tools/ruleset_converter/ruleset_test_util.h
+++ b/components/subresource_filter/tools/ruleset_converter/ruleset_test_util.h
@@ -25,7 +25,7 @@
   ~TestRulesetContents();
 
   std::vector<url_pattern_index::proto::UrlRule> url_rules;
-  std::vector<url_pattern_index::proto::CssRule> css_rules;
+  std::vector<url_pattern_index::proto::StyleRule> style_rules;
 
   // Parses |text_rules| and appends them to the |ruleset|.
   void AppendRules(const std::vector<std::string>& text_rules,
@@ -77,8 +77,8 @@
 bool AreUrlRulesEqual(const url_pattern_index::proto::UrlRule& first,
                       const url_pattern_index::proto::UrlRule& second);
 
-bool AreCssRulesEqual(const url_pattern_index::proto::CssRule& first,
-                      const url_pattern_index::proto::CssRule& second);
+bool AreStyleRulesEqual(const url_pattern_index::proto::StyleRule& first,
+                        const url_pattern_index::proto::StyleRule& second);
 
 }  // namespace subresource_filter
 
diff --git a/components/tab_groups/tab_group_color.cc b/components/tab_groups/tab_group_color.cc
index ab52982..b2ff90b 100644
--- a/components/tab_groups/tab_group_color.cc
+++ b/components/tab_groups/tab_group_color.cc
@@ -8,6 +8,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/no_destructor.h"
+#include "base/notreached.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/tab_groups/tab_group_id.h"
@@ -38,26 +39,4 @@
   return *kTabGroupColors;
 }
 
-TabGroupColorId GetNextColor(const std::vector<TabGroupColorId>& used_colors) {
-  // Count the number of times each available color is used.
-  std::map<TabGroupColorId, int> color_usage_counts;
-  for (const auto& id_color_pair : GetTabGroupColorLabelMap()) {
-    color_usage_counts[id_color_pair.first] = 0;
-  }
-  for (const auto& color : used_colors) {
-    color_usage_counts[color]++;
-  }
-
-  // Find the next least-used color.
-  TabGroupColorId next_color = color_usage_counts.begin()->first;
-  int min_usage_count = color_usage_counts.begin()->second;
-  for (const auto& color_usage_pair : color_usage_counts) {
-    if (color_usage_pair.second < min_usage_count) {
-      next_color = color_usage_pair.first;
-      min_usage_count = color_usage_pair.second;
-    }
-  }
-  return next_color;
-}
-
 }  // namespace tab_groups
diff --git a/components/tab_groups/tab_group_color.h b/components/tab_groups/tab_group_color.h
index 890740a..05b4a6a0 100644
--- a/components/tab_groups/tab_group_color.h
+++ b/components/tab_groups/tab_group_color.h
@@ -59,12 +59,6 @@
 COMPONENT_EXPORT(TAB_GROUPS)
 const ColorLabelMap& GetTabGroupColorLabelMap();
 
-// Returns the least-used color in the color set, breaking ties toward the first
-// color in the set. Used to initialize a new group's color, which should be as
-// distinct from the other groups as possible.
-COMPONENT_EXPORT(TAB_GROUPS)
-TabGroupColorId GetNextColor(const std::vector<TabGroupColorId>& used_colors);
-
 }  // namespace tab_groups
 
 #endif  // COMPONENTS_TAB_GROUPS_TAB_GROUP_COLOR_H_
diff --git a/components/tab_groups_strings.grdp b/components/tab_groups_strings.grdp
index 6609fca..643e1acd 100644
--- a/components/tab_groups_strings.grdp
+++ b/components/tab_groups_strings.grdp
@@ -14,12 +14,18 @@
       <message name="IDS_TAB_GROUP_COLOR_YELLOW" desc="The accessibility label for a button in a color picker that is used to choose the color yellow." formatter_data="android_java">
         Yellow
       </message>
+      <message name="IDS_TAB_GROUP_COLOR_MAGENTA" desc="The accessibility label for a button in a color picker that is used to choose the color magenta.">
+        Magenta
+      </message>
       <message name="IDS_TAB_GROUP_COLOR_GREEN" desc="The accessibility label for a button in a color picker that is used to choose the color green." formatter_data="android_java">
         Green
       </message>
       <message name="IDS_TAB_GROUP_COLOR_PINK" desc="The accessibility label for a button in a color picker that is used to choose the color pink." formatter_data="android_java">
         Pink
       </message>
+      <message name="IDS_TAB_GROUP_COLOR_LIME" desc="The accessibility label for a button in a color picker that is used to choose the color lime.">
+        Lime
+      </message>
       <message name="IDS_TAB_GROUP_COLOR_PURPLE" desc="The accessibility label for a button in a color picker that is used to choose the color purple." formatter_data="android_java">
         Purple
       </message>
diff --git a/components/tab_groups_strings_grdp/IDS_TAB_GROUP_COLOR_LIME.png.sha1 b/components/tab_groups_strings_grdp/IDS_TAB_GROUP_COLOR_LIME.png.sha1
new file mode 100644
index 0000000..b3571f2
--- /dev/null
+++ b/components/tab_groups_strings_grdp/IDS_TAB_GROUP_COLOR_LIME.png.sha1
@@ -0,0 +1 @@
+55468f7194119fd75c3706888c7c4b94a040d09a
\ No newline at end of file
diff --git a/components/tab_groups_strings_grdp/IDS_TAB_GROUP_COLOR_MAGENTA.png.sha1 b/components/tab_groups_strings_grdp/IDS_TAB_GROUP_COLOR_MAGENTA.png.sha1
new file mode 100644
index 0000000..9ad045f
--- /dev/null
+++ b/components/tab_groups_strings_grdp/IDS_TAB_GROUP_COLOR_MAGENTA.png.sha1
@@ -0,0 +1 @@
+cfa885d15860fed01c059e1f01d86df9cb461efb
\ No newline at end of file
diff --git a/components/unexportable_keys/BUILD.gn b/components/unexportable_keys/BUILD.gn
index c00f04a..cdc12c9 100644
--- a/components/unexportable_keys/BUILD.gn
+++ b/components/unexportable_keys/BUILD.gn
@@ -69,6 +69,7 @@
 
   sources = [
     "background_long_task_scheduler_unittest.cc",
+    "unexportable_key_id_unittest.cc",
     "unexportable_key_loader_unittest.cc",
     "unexportable_key_service_impl_unittest.cc",
     "unexportable_key_task_manager_unittest.cc",
diff --git a/components/unexportable_keys/unexportable_key_id.h b/components/unexportable_keys/unexportable_key_id.h
index d66fe95..1eaef33b 100644
--- a/components/unexportable_keys/unexportable_key_id.h
+++ b/components/unexportable_keys/unexportable_key_id.h
@@ -9,10 +9,27 @@
 
 namespace unexportable_keys {
 
-// Strongly typed id for identifying unexportable signing keys.
+// Strongly typed id for identifying unexportable keys.
 // Default constructor creates a new, unique key ID.
 using UnexportableKeyId = base::TokenType<class UnexportableKeyIdMarker>;
 
+// A subclass of `UnexportableKeyId` that represents a signing key specifically.
+//
+// Inheritance is used here instead of a distinct tag to allow implicit
+// conversion to the base `UnexportableKeyId` for type-agnostic APIs,
+// while preventing accidental interchange with other specific key types.
+//
+// TODO(b/501307307): Replace existing usages of `UnexportableKeyId` that should
+// be `UnexportableSigningKeyId`.
+class UnexportableSigningKeyId : public UnexportableKeyId {
+ public:
+  using UnexportableKeyId::UnexportableKeyId;
+
+  // Allows explicit conversion from the base class.
+  explicit UnexportableSigningKeyId(UnexportableKeyId key_id)
+      : UnexportableKeyId(key_id) {}
+};
+
 }  // namespace unexportable_keys
 
 #endif  // COMPONENTS_UNEXPORTABLE_KEYS_UNEXPORTABLE_KEY_ID_H_
diff --git a/components/unexportable_keys/unexportable_key_id_unittest.cc b/components/unexportable_keys/unexportable_key_id_unittest.cc
new file mode 100644
index 0000000..3f042c1
--- /dev/null
+++ b/components/unexportable_keys/unexportable_key_id_unittest.cc
@@ -0,0 +1,35 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/unexportable_keys/unexportable_key_id.h"
+
+#include <concepts>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace unexportable_keys {
+
+TEST(UnexportableSigningKeyIdTest, DefaultConstructor) {
+  UnexportableSigningKeyId signing_key_id;
+  EXPECT_FALSE(signing_key_id.value().is_empty());
+}
+
+TEST(UnexportableSigningKeyIdTest, ExplicitConversionFromBase) {
+  static_assert(
+      !std::convertible_to<UnexportableKeyId, UnexportableSigningKeyId>,
+      "Implicit conversion from UnexportableKeyId to "
+      "UnexportableSigningKeyId should not be allowed");
+
+  UnexportableKeyId base_id;
+  UnexportableSigningKeyId derived_id(base_id);
+  EXPECT_EQ(derived_id, base_id);
+}
+
+TEST(UnexportableSigningKeyIdTest, ImplicitConversionToBase) {
+  UnexportableSigningKeyId derived_id;
+  UnexportableKeyId base_id = derived_id;
+  EXPECT_EQ(derived_id, base_id);
+}
+
+}  // namespace unexportable_keys
diff --git a/components/url_pattern_index/proto/rules.proto b/components/url_pattern_index/proto/rules.proto
index 881f34f8..55247ae 100644
--- a/components/url_pattern_index/proto/rules.proto
+++ b/components/url_pattern_index/proto/rules.proto
@@ -14,7 +14,7 @@
 
   RULE_TYPE_COMMENT = 1;  // Comment rule.
   RULE_TYPE_URL = 2;      // Network level filtering rule based on URL pattern.
-  RULE_TYPE_CSS = 3;      // Element hiding rule based on a CSS selector.
+  RULE_TYPE_STYLE = 3;    // Element hiding rule based on a CSS selector.
 };
 
 // The format of a URL pattern.
@@ -174,8 +174,8 @@
   repeated DomainListItem request_domains = 11;
 }
 
-// Element hiding rule based on a CSS selector. Corresponds to RULE_TYPE_CSS.
-message CssRule {
+// Element hiding rule based on a CSS selector. Corresponds to RULE_TYPE_STYLE.
+message StyleRule {
   // The semantics of the rule.
   optional RuleSemantics semantics = 1;
 
@@ -183,7 +183,7 @@
   repeated DomainListItem domains = 2;
 
   // A CSS selector as specified in http://www.w3.org/TR/css3-selectors.
-  optional string css_selector = 3;
+  optional string style_selector = 3;
 }
 
 // A comment line.
@@ -199,5 +199,5 @@
 // A container for lists of non-comment rules collated by RuleType.
 message FilteringRules {
   repeated UrlRule url_rules = 1;
-  repeated CssRule css_rules = 2;
+  repeated StyleRule style_rules = 2;
 }
diff --git a/content/browser/browser_thread_unittest.cc b/content/browser/browser_thread_unittest.cc
index 3dcbe9dd..09b5fab 100644
--- a/content/browser/browser_thread_unittest.cc
+++ b/content/browser/browser_thread_unittest.cc
@@ -49,7 +49,8 @@
     default_task_runner_ =
         browser_ui_thread_scheduler->GetHandle()->GetDefaultTaskRunner();
 
-    ui_sequence_manager_->SetDefaultTaskRunner(default_task_runner_);
+    ui_sequence_manager_->SetDefaultTaskQueue(
+        browser_ui_thread_scheduler->GetDefaultTaskQueue());
 
     BrowserTaskExecutor::CreateForTesting(
         std::move(browser_ui_thread_scheduler),
@@ -267,8 +268,7 @@
       std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler =
           BrowserUIThreadScheduler::CreateForTesting(sequence_manager());
       DeferredInitFromSubclass(
-          browser_ui_thread_scheduler->GetHandle()->GetBrowserTaskRunner(
-              QueueType::kDefault));
+          browser_ui_thread_scheduler->GetDefaultTaskQueue());
       BrowserTaskExecutor::CreateForTesting(
           std::move(browser_ui_thread_scheduler),
           std::make_unique<BrowserIOThreadDelegate>());
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index 0f535e5..685d437 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -1193,7 +1193,7 @@
                   StoragePartitionConfig::CreateDefault(browser_context),
                   WebExposedIsolationInfo::CreateNonIsolated(),
                   /*cross_origin_isolation_key=*/std::nullopt,
-                  browser_context->UniqueId()));
+                  browser_context->UniqueToken()));
 }
 
 void ChildProcessSecurityPolicyImpl::Remove(ChildProcessId child_id) {
diff --git a/content/browser/child_process_security_policy_unittest.cc b/content/browser/child_process_security_policy_unittest.cc
index 3e5c087..e367d3a 100644
--- a/content/browser/child_process_security_policy_unittest.cc
+++ b/content/browser/child_process_security_policy_unittest.cc
@@ -3296,13 +3296,13 @@
     // This is important when this test is run with other tests, as then
     // BrowsingInstanceId will not be '1' in general.
     p->Add(kRendererProcess, &context);
-    p->LockProcess(
-        foo_instance->GetIsolationContext(), kRendererProcess,
-        /*is_process_used=*/false,
-        ProcessLock::CreateAllowAnySite(
-            StoragePartitionConfig::CreateDefault(&context),
-            WebExposedIsolationInfo::CreateNonIsolated(),
-            /*cross_origin_isolation_key=*/std::nullopt, context.UniqueId()));
+    p->LockProcess(foo_instance->GetIsolationContext(), kRendererProcess,
+                   /*is_process_used=*/false,
+                   ProcessLock::CreateAllowAnySite(
+                       StoragePartitionConfig::CreateDefault(&context),
+                       WebExposedIsolationInfo::CreateNonIsolated(),
+                       /*cross_origin_isolation_key=*/std::nullopt,
+                       context.UniqueToken()));
 
     EXPECT_TRUE(foo_instance->HasSite());
     if (ShouldUseDefaultSiteInstanceGroup()) {
@@ -3354,7 +3354,7 @@
       ProcessLock::CreateAllowAnySite(
           StoragePartitionConfig::CreateDefault(&context),
           WebExposedIsolationInfo::CreateNonIsolated(),
-          /*cross_origin_isolation_key=*/std::nullopt, context.UniqueId()));
+          /*cross_origin_isolation_key=*/std::nullopt, context.UniqueToken()));
   EXPECT_TRUE(p->GetProcessLock(kRendererProcess).AllowsAnySite());
   EXPECT_FALSE(p->GetProcessLock(kRendererProcess).IsLockedToSite());
 
diff --git a/content/browser/compositor/viz_process_transport_factory.cc b/content/browser/compositor/viz_process_transport_factory.cc
index 59a7d484..3b6170d 100644
--- a/content/browser/compositor/viz_process_transport_factory.cc
+++ b/content/browser/compositor/viz_process_transport_factory.cc
@@ -78,8 +78,7 @@
   GURL url("chrome://gpu/VizProcessTransportFactory::CreateContextProvider");
   return viz::ContextProviderCommandBuffer::CreateForRaster(
       std::move(gpu_channel_host), kGpuStreamIdDefault, kGpuStreamPriorityUI,
-      std::move(url), kAutomaticFlushes, supports_locking, memory_limits, type,
-      /*lose_context_when_out_of_memory=*/true);
+      std::move(url), kAutomaticFlushes, supports_locking, memory_limits, type);
 }
 
 bool IsContextLost(viz::RasterContextProvider* context_provider) {
diff --git a/content/browser/isolated_origin_browsertest.cc b/content/browser/isolated_origin_browsertest.cc
index ff4bbfa..456f8e0 100644
--- a/content/browser/isolated_origin_browsertest.cc
+++ b/content/browser/isolated_origin_browsertest.cc
@@ -150,7 +150,7 @@
         WebExposedIsolationLevel::kNotIsolated, /*is_guest=*/false,
         /*does_site_request_dedicated_process_for_coop=*/false,
         /*is_jit_disabled=*/false, /*are_v8_optimizations_disabled=*/false,
-        /*is_pdf=*/false, /*is_fenced=*/false, browser_context->UniqueId()));
+        /*is_pdf=*/false, /*is_fenced=*/false, browser_context->UniqueToken()));
   }
 
   WebContentsImpl* web_contents() const {
@@ -180,7 +180,7 @@
         WebExposedIsolationLevel::kNotIsolated, /*is_guest=*/false,
         /*does_site_request_dedicated_process_for_coop=*/false,
         /*is_jit_disabled=*/false, /*are_v8_optimizations_disabled=*/false,
-        /*is_pdf=*/false, /*is_fenced=*/false, browser_context->UniqueId()));
+        /*is_pdf=*/false, /*is_fenced=*/false, browser_context->UniqueToken()));
   }
 
  protected:
@@ -1738,7 +1738,7 @@
       WebExposedIsolationLevel::kNotIsolated, /*is_guest=*/false,
       /*does_site_request_dedicated_process_for_coop=*/false,
       /*is_jit_disabled=*/false, /*are_v8_optimizations_disabled=*/false,
-      /*is_pdf=*/false, /*is_fenced=*/false, browser_context->UniqueId()));
+      /*is_pdf=*/false, /*is_fenced=*/false, browser_context->UniqueToken()));
   EXPECT_TRUE(NavigateToURL(shell(), test_url));
   EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
 
diff --git a/content/browser/media/android/browser_gpu_video_accelerator_factories.cc b/content/browser/media/android/browser_gpu_video_accelerator_factories.cc
index 83f57048..a8059687 100644
--- a/content/browser/media/android/browser_gpu_video_accelerator_factories.cc
+++ b/content/browser/media/android/browser_gpu_video_accelerator_factories.cc
@@ -36,8 +36,7 @@
            "CreateGpuVideoAcceleratorFactories"),
       automatic_flushes, support_locking,
       gpu::SharedMemoryLimits::ForMailboxContext(),
-      viz::command_buffer_metrics::ContextType::UNKNOWN,
-      /*lose_context_when_out_of_memory=*/false);
+      viz::command_buffer_metrics::ContextType::UNKNOWN);
   context_provider->BindToCurrentSequence();
 
   auto gpu_factories = std::make_unique<BrowserGpuVideoAcceleratorFactories>(
diff --git a/content/browser/process_lock.cc b/content/browser/process_lock.cc
index 87f0356..9d33f94 100644
--- a/content/browser/process_lock.cc
+++ b/content/browser/process_lock.cc
@@ -20,7 +20,7 @@
     const WebExposedIsolationInfo& web_exposed_isolation_info,
     const std::optional<AgentClusterKey::CrossOriginIsolationKey>&
         cross_origin_isolation_key,
-    const std::string& browser_context_id) {
+    const base::UnguessableToken& browser_context_id) {
   WebExposedIsolationLevel web_exposed_isolation_level =
       SiteInfo::ComputeWebExposedIsolationLevelForEmptySite(
           web_exposed_isolation_info);
diff --git a/content/browser/process_lock.h b/content/browser/process_lock.h
index c32b2d3..8a4512f 100644
--- a/content/browser/process_lock.h
+++ b/content/browser/process_lock.h
@@ -52,7 +52,7 @@
       const WebExposedIsolationInfo& web_exposed_isolation_info,
       const std::optional<AgentClusterKey::CrossOriginIsolationKey>&
           cross_origin_isolation_key,
-      const std::string& browser_context_id);
+      const base::UnguessableToken& browser_context_id);
 
   // Create a lock for a specific UrlInfo. This method can be called from both
   // the UI and IO threads. Locks created with the same parameters must always
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 128da9eb..7cc0c5e 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -514,10 +514,7 @@
       GURL("chrome://gpu/CompositorImpl::CompositorContextProvider"),
       automatic_flushes, support_locking,
       GetCompositorContextSharedMemoryLimits(root_window_),
-      viz::command_buffer_metrics::ContextType::BROWSER_COMPOSITOR,
-      /*lose_context_when_out_of_memory=*/false
-
-  );
+      viz::command_buffer_metrics::ContextType::BROWSER_COMPOSITOR);
   auto result = context_provider->BindToCurrentSequence();
 
   if (result == gpu::ContextResult::kFatalFailure) {
diff --git a/content/browser/scheduler/browser_io_thread_delegate.cc b/content/browser/scheduler/browser_io_thread_delegate.cc
index eef7e18c..58d3c04 100644
--- a/content/browser/scheduler/browser_io_thread_delegate.cc
+++ b/content/browser/scheduler/browser_io_thread_delegate.cc
@@ -53,7 +53,7 @@
   DCHECK(sequence_manager_);
   sequence_manager_->BindToMessagePump(
       base::MessagePump::Create(base::MessagePumpType::IO));
-  sequence_manager_->SetDefaultTaskRunner(GetDefaultTaskRunner());
+  sequence_manager_->SetDefaultTaskQueue(task_queues_->GetDefaultTaskQueue());
   sequence_manager_->EnableCrashKeys("io_scheduler_async_stack");
 }
 
diff --git a/content/browser/scheduler/browser_task_executor_unittest.cc b/content/browser/scheduler/browser_task_executor_unittest.cc
index 37044022..fbd6f71 100644
--- a/content/browser/scheduler/browser_task_executor_unittest.cc
+++ b/content/browser/scheduler/browser_task_executor_unittest.cc
@@ -171,8 +171,7 @@
           BrowserUIThreadScheduler::CreateForTesting(sequence_manager());
 
       DeferredInitFromSubclass(
-          browser_ui_thread_scheduler->GetHandle()->GetBrowserTaskRunner(
-              QueueType::kDefault));
+          browser_ui_thread_scheduler->GetDefaultTaskQueue());
       BrowserTaskExecutor::CreateForTesting(
           std::move(browser_ui_thread_scheduler),
           BrowserIOThreadDelegate::CreateForTesting(sequence_manager()));
@@ -267,8 +266,7 @@
       std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler =
           BrowserUIThreadScheduler::CreateForTesting(sequence_manager());
       DeferredInitFromSubclass(
-          browser_ui_thread_scheduler->GetHandle()->GetBrowserTaskRunner(
-              QueueType::kDefault));
+          browser_ui_thread_scheduler->GetDefaultTaskQueue());
       BrowserTaskExecutor::CreateForTesting(
           std::move(browser_ui_thread_scheduler),
           BrowserIOThreadDelegate::CreateForTesting(sequence_manager()));
diff --git a/content/browser/scheduler/browser_task_queues.h b/content/browser/scheduler/browser_task_queues.h
index 2466cc7..814f18d8 100644
--- a/content/browser/scheduler/browser_task_queues.h
+++ b/content/browser/scheduler/browser_task_queues.h
@@ -176,6 +176,10 @@
 
   void AddTaskObserver(base::TaskObserver* task_observer);
 
+  base::sequence_manager::TaskQueue* GetDefaultTaskQueue() const {
+    return GetBrowserTaskQueue(QueueType::kDefault);
+  }
+
  private:
   struct QueueData {
    public:
@@ -202,10 +206,6 @@
     return queue_data_[static_cast<size_t>(type)].task_queue.get();
   }
 
-  base::sequence_manager::TaskQueue* GetDefaultTaskQueue() const {
-    return GetBrowserTaskQueue(QueueType::kDefault);
-  }
-
   std::array<scoped_refptr<base::SingleThreadTaskRunner>, kNumQueueTypes>
   CreateBrowserTaskRunners() const;
 
diff --git a/content/browser/scheduler/browser_task_queues_unittest.cc b/content/browser/scheduler/browser_task_queues_unittest.cc
index 9cbf98a3..5abd2d0 100644
--- a/content/browser/scheduler/browser_task_queues_unittest.cc
+++ b/content/browser/scheduler/browser_task_queues_unittest.cc
@@ -45,7 +45,7 @@
         queues_(std::make_unique<BrowserTaskQueues>(BrowserThread::UI,
                                                     sequence_manager_.get())),
         handle_(queues_->GetHandle()) {
-    sequence_manager_->SetDefaultTaskRunner(handle_->GetDefaultTaskRunner());
+    sequence_manager_->SetDefaultTaskQueue(queues_->GetDefaultTaskQueue());
   }
 
   std::unique_ptr<SequenceManager> sequence_manager_;
diff --git a/content/browser/scheduler/browser_ui_thread_scheduler.cc b/content/browser/scheduler/browser_ui_thread_scheduler.cc
index 54ee46a..50696eb 100644
--- a/content/browser/scheduler/browser_ui_thread_scheduler.cc
+++ b/content/browser/scheduler/browser_ui_thread_scheduler.cc
@@ -73,8 +73,8 @@
   task_queues_.SetOnTaskCompletedHandler(base::BindRepeating(
       &BrowserUIThreadScheduler::OnTaskCompleted, base::Unretained(this)));
   CommonSequenceManagerSetup(owned_sequence_manager_.get());
-  owned_sequence_manager_->SetDefaultTaskRunner(
-      handle_->GetDefaultTaskRunner());
+  owned_sequence_manager_->SetDefaultTaskQueue(
+      task_queues_.GetDefaultTaskQueue());
 
   owned_sequence_manager_->BindToMessagePump(
       base::MessagePump::Create(base::MessagePumpType::UI));
@@ -89,6 +89,11 @@
   g_browser_ui_thread_scheduler = this;
 }
 
+base::sequence_manager::TaskQueue*
+BrowserUIThreadScheduler::GetDefaultTaskQueue() const {
+  return task_queues_.GetDefaultTaskQueue();
+}
+
 void BrowserUIThreadScheduler::CommonSequenceManagerSetup(
     base::sequence_manager::SequenceManager* sequence_manager) {
   DCHECK_EQ(static_cast<size_t>(sequence_manager->GetPriorityCount()),
diff --git a/content/browser/scheduler/browser_ui_thread_scheduler.h b/content/browser/scheduler/browser_ui_thread_scheduler.h
index 45d35acc..9ade139e 100644
--- a/content/browser/scheduler/browser_ui_thread_scheduler.h
+++ b/content/browser/scheduler/browser_ui_thread_scheduler.h
@@ -50,6 +50,8 @@
 
   scoped_refptr<Handle> GetHandle() const { return handle_; }
 
+  base::sequence_manager::TaskQueue* GetDefaultTaskQueue() const;
+
  private:
   friend class BrowserTaskExecutor;
 
diff --git a/content/browser/site_info.cc b/content/browser/site_info.cc
index f7efa11c..c423a1ec 100644
--- a/content/browser/site_info.cc
+++ b/content/browser/site_info.cc
@@ -167,7 +167,7 @@
     WebExposedIsolationLevel web_exposed_isolation_level,
     const std::optional<AgentClusterKey::CrossOriginIsolationKey>&
         cross_origin_isolation_key,
-    const std::string& browser_context_id) {
+    const base::UnguessableToken& browser_context_id) {
   AgentClusterKey agent_cluster_key;
   if (cross_origin_isolation_key.has_value()) {
     agent_cluster_key = AgentClusterKey::CreateWithCrossOriginIsolationKey(
@@ -228,7 +228,7 @@
                   /*does_site_request_dedicated_process_for_coop=*/false,
                   is_jit_disabled, are_v8_optimizations_disabled,
                   /*is_pdf=*/false, isolation_context.is_fenced(),
-                  browser_context->UniqueId());
+                  browser_context->UniqueToken());
 }
 
 // static
@@ -251,7 +251,7 @@
       /*is_guest=*/true,
       /*does_site_request_dedicated_process_for_coop=*/false,
       /*is_jit_disabled=*/false, /*are_v8_optimizations_disabled=*/false,
-      /*is_pdf=*/false, /*is_fenced=*/false, browser_context->UniqueId());
+      /*is_pdf=*/false, /*is_fenced=*/false, browser_context->UniqueToken());
 }
 
 // static
@@ -353,7 +353,7 @@
         /*is_guest=*/isolation_context.is_guest(),
         /*is_fenced=*/isolation_context.is_fenced(), web_exposed_isolation_info,
         web_exposed_isolation_level, url_info.cross_origin_isolation_key,
-        isolation_context.browser_context()->UniqueId());
+        isolation_context.browser_context()->UniqueToken());
   }
 
   // If there is a COOP isolation request, propagate it to SiteInfo.
@@ -369,7 +369,7 @@
                   does_site_request_dedicated_process_for_coop, is_jitless,
                   are_v8_optimizations_disabled, url_info.is_pdf,
                   isolation_context.is_fenced(),
-                  isolation_context.browser_context()->UniqueId());
+                  isolation_context.browser_context()->UniqueToken());
 }
 
 // static
@@ -391,7 +391,7 @@
                    bool are_v8_optimizations_disabled,
                    bool is_pdf,
                    bool is_fenced,
-                   const std::string& browser_context_id)
+                   const base::UnguessableToken& browser_context_id)
     : site_url_(site_url),
       agent_cluster_key_(agent_cluster_key),
       is_sandboxed_(is_sandboxed),
@@ -431,7 +431,7 @@
                /*are_v8_optimizations_disabled=*/false,
                /*is_pdf=*/false,
                /*is_fenced=*/false,
-               browser_context->UniqueId()) {}
+               browser_context->UniqueToken()) {}
 
 // static
 auto SiteInfo::MakeSecurityPrincipalKey(const SiteInfo& site_info) {
diff --git a/content/browser/site_info.h b/content/browser/site_info.h
index 8145aac..29a9f94 100644
--- a/content/browser/site_info.h
+++ b/content/browser/site_info.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_BROWSER_SITE_INFO_H_
 #define CONTENT_BROWSER_SITE_INFO_H_
 
+#include "base/unguessable_token.h"
 #include "content/browser/agent_cluster_key.h"
 #include "content/browser/url_info.h"
 #include "content/browser/web_exposed_isolation_info.h"
@@ -72,7 +73,7 @@
       WebExposedIsolationLevel web_exposed_isolation_level,
       const std::optional<AgentClusterKey::CrossOriginIsolationKey>&
           cross_origin_isolation_key,
-      const std::string& browser_context_id);
+      const base::UnguessableToken& browser_context_id);
 
   // Helper to create a SiteInfo for default SiteInstances.  Default
   // SiteInstances are used for non-isolated sites on platforms without strict
@@ -203,7 +204,7 @@
            bool are_v8_optimizations_disabled,
            bool is_pdf,
            bool is_fenced,
-           const std::string& browser_context_id);
+           const base::UnguessableToken& browser_context_id);
   SiteInfo() = delete;
   SiteInfo(const SiteInfo& rhs);
 
@@ -544,10 +545,7 @@
 
   // Unique id of the BrowserContext. SiteInfos associated with different
   // BrowserContexts should be considered distinct security principals.
-  // This is a string, because it is initialized from
-  // BrowserContext::UniqueId() which returns string.
-  // TODO(crbug.com/466132514): use UnguessableToken instead.
-  std::string browser_context_id_;
+  base::UnguessableToken browser_context_id_;
 };
 
 CONTENT_EXPORT std::ostream& operator<<(std::ostream& out,
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index 9392365..0eaabdc1 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -1504,7 +1504,7 @@
       auto new_process_lock = ProcessLock::CreateAllowAnySite(
           storage_partition->GetConfig(), GetWebExposedIsolationInfo(),
           /*cross_origin_isolation_key=*/std::nullopt,
-          GetBrowserContext()->UniqueId());
+          GetBrowserContext()->UniqueToken());
       process->SetProcessLock(GetIsolationContext(), new_process_lock);
     } else {
       CHECK(process_lock.AllowsAnySite())
@@ -1562,7 +1562,7 @@
       auto new_process_lock = ProcessLock::CreateAllowAnySite(
           storage_partition->GetConfig(), GetWebExposedIsolationInfo(),
           /*cross_origin_isolation_key=*/std::nullopt,
-          GetBrowserContext()->UniqueId());
+          GetBrowserContext()->UniqueToken());
       process->SetProcessLock(GetIsolationContext(), new_process_lock);
     } else {
       CHECK(process_lock.AllowsAnySite())
diff --git a/content/browser/site_instance_impl_unittest.cc b/content/browser/site_instance_impl_unittest.cc
index b33ffa4..02d1aca 100644
--- a/content/browser/site_instance_impl_unittest.cc
+++ b/content/browser/site_instance_impl_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/test/mock_log.h"
 #include "base/test/scoped_command_line.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/unguessable_token.h"
 #include "content/browser/browsing_instance.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/isolated_origin_util.h"
@@ -68,9 +69,10 @@
       .RequiresDedicatedProcess(isolation_context);
 }
 
-SiteInfo CreateSimpleSiteInfo(const GURL& process_lock_url,
-                              bool requires_origin_keyed_process,
-                              const std::string& browser_context_id) {
+SiteInfo CreateSimpleSiteInfo(
+    const GURL& process_lock_url,
+    bool requires_origin_keyed_process,
+    const base::UnguessableToken& browser_context_id) {
   AgentClusterKey agent_cluster_key =
       requires_origin_keyed_process
           ? AgentClusterKey::CreateOriginKeyed(
@@ -95,7 +97,8 @@
 
 const char kPrivilegedScheme[] = "privileged";
 const char kCustomStandardScheme[] = "custom-standard";
-const char kBrowserContextId[] = "browser_context_id";
+const base::UnguessableToken kBrowserContextId =
+    base::UnguessableToken::CreateForTesting(0, 1);
 
 class SiteInstanceTestBrowserClient : public TestContentBrowserClient {
  public:
@@ -278,7 +281,7 @@
 
   auto site_info_1_other_context = CreateSimpleSiteInfo(
       GURL("https://foo.com"), false /* requires_origin_keyed_process */,
-      "other_context");
+      base::UnguessableToken::CreateForTesting(0, 2));
 
   // Test IsSamePrincipalWith.
   EXPECT_TRUE(site_info_1.IsSamePrincipalWith(site_info_1));
@@ -913,7 +916,7 @@
       /*is_guest=*/false, /*is_fenced=*/false,
       WebExposedIsolationInfo::CreateNonIsolated(),
       WebExposedIsolationLevel::kNotIsolated,
-      /*cross_origin_isolation_key=*/std::nullopt, context.UniqueId());
+      /*cross_origin_isolation_key=*/std::nullopt, context.UniqueToken());
   test_url = GURL(kUnreachableWebDataURL);
   site_url = GetSiteForURL(test_url);
   EXPECT_EQ(error_site_info.site_url(), site_url);
@@ -980,7 +983,7 @@
       WebExposedIsolationLevel::kNotIsolated, /*is_guest=*/false,
       /*does_site_request_dedicated_process_for_coop=*/false,
       /*is_jit_disabled=*/false, /*are_v8_optimizations_disabled=*/false,
-      /*is_pdf=*/false, /*is_fenced=*/false, browser_context->UniqueId());
+      /*is_pdf=*/false, /*is_fenced=*/false, browser_context->UniqueToken());
 
   // New SiteInstance in a new BrowsingInstance with a predetermined URL.
   {
@@ -1784,7 +1787,7 @@
       WebExposedIsolationLevel::kNotIsolated, /*is_guest=*/false,
       /*does_site_request_dedicated_process_for_coop=*/false,
       /*is_jit_disabled=*/false, /*are_v8_optimizations_disabled=*/false,
-      /*is_pdf=*/false, /*is_fenced=*/false, browser_context->UniqueId());
+      /*is_pdf=*/false, /*is_fenced=*/false, browser_context->UniqueToken());
 
   // New SiteInstance in a new BrowsingInstance with a predetermined URL.  In
   // this and subsequent cases, the site URL should consist of the effective
@@ -2221,7 +2224,7 @@
       /*is_guest=*/false, /*is_fenced=*/false,
       WebExposedIsolationInfo::CreateNonIsolated(),
       WebExposedIsolationLevel::kNotIsolated,
-      /*cross_origin_isolation_key=*/std::nullopt, context()->UniqueId());
+      /*cross_origin_isolation_key=*/std::nullopt, context()->UniqueToken());
   EXPECT_TRUE(error_site_info.is_error_page());
   EXPECT_FALSE(error_site_info.web_exposed_isolation_info().is_isolated());
   EXPECT_FALSE(error_site_info.IsGuest());
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index b1998f14..5901f6c 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -7397,10 +7397,20 @@
   // upstream contents. Drop that WebContents out of fullscreen if it does. This
   // is theoretically quadratic-ish (fullscreen contentses x each one's opener
   // length) but neither of those is expected to ever be a large number.
-  auto fullscreen_set_copy = *FullscreenContentsSet(GetBrowserContext());
-  for (WebContentsImpl* fullscreen_contents : fullscreen_set_copy) {
-    if (is_fullscreen(fullscreen_contents, display_id)) {
-      auto opener_contentses = GetAllOpeningWebContents(fullscreen_contents);
+  std::vector<base::WeakPtr<WebContentsImpl>> fullscreen_contents_list;
+  for (WebContentsImpl* fullscreen_contents :
+       *FullscreenContentsSet(GetBrowserContext())) {
+    fullscreen_contents_list.push_back(
+        fullscreen_contents->weak_factory_.GetWeakPtr());
+  }
+
+  for (auto& fullscreen_contents : fullscreen_contents_list) {
+    if (!fullscreen_contents) {
+      continue;
+    }
+    if (is_fullscreen(fullscreen_contents.get(), display_id)) {
+      auto opener_contentses =
+          GetAllOpeningWebContents(fullscreen_contents.get());
       if (opener_contentses.count(this)) {
         fullscreen_contents->ExitFullscreen(true);
       }
@@ -7414,29 +7424,41 @@
   // any request to enter fullscreen will have the upstream of the WebContents
   // checked. (See CanEnterFullscreenMode().)
 
-  std::vector<base::WeakPtr<WebContentsImpl>> blocked_contentses;
+  std::vector<base::WeakPtr<WebContentsImpl>> blocked_contents_list;
+  std::vector<base::WeakPtr<WebContentsImpl>> openers;
+  for (WebContentsImpl* opener : GetAllOpeningWebContents(this)) {
+    openers.push_back(opener->weak_factory_.GetWeakPtr());
+  }
 
-  for (auto opener : GetAllOpeningWebContents(this)) {
-    if (is_fullscreen(opener, display_id)) {
+  for (auto& opener : openers) {
+    if (!opener) {
+      continue;
+    }
+
+    if (is_fullscreen(opener.get(), display_id)) {
       opener->ExitFullscreen(true);
     }
 
+    if (!opener) {
+      continue;
+    }
+
     // ...block the WebContents from entering fullscreen until further notice.
     ++opener->fullscreen_blocker_count_;
-    blocked_contentses.push_back(opener->weak_factory_.GetWeakPtr());
+    blocked_contents_list.push_back(opener);
   }
 
   return base::ScopedClosureRunner(base::BindOnce(
-      [](std::vector<base::WeakPtr<WebContentsImpl>> blocked_contentses) {
+      [](std::vector<base::WeakPtr<WebContentsImpl>> blocked_contents_list) {
         for (base::WeakPtr<WebContentsImpl>& web_contents :
-             blocked_contentses) {
+             blocked_contents_list) {
           if (web_contents) {
             DCHECK_GT(web_contents->fullscreen_blocker_count_, 0);
             --web_contents->fullscreen_blocker_count_;
           }
         }
       },
-      std::move(blocked_contentses)));
+      std::move(blocked_contents_list)));
 }
 
 void WebContentsImpl::ResumeLoadingCreatedWebContents() {
diff --git a/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
index 29625668..8e1406204 100644
--- a/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
@@ -13,7 +13,6 @@
 import org.jni_zero.JNINamespace;
 import org.jni_zero.NativeMethods;
 
-import org.chromium.base.Log;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
@@ -39,8 +38,6 @@
 //                package whose visibility will be enforced via DEPS.
 @NullMarked
 /* package */ class NavigationControllerImpl implements NavigationController {
-    private static final String TAG = "NavigationController";
-
     // Using ScopedJavaGlobalRef in the owning C++ object to keep the Java object alive consumes an
     // entry per instance in the finite global ref table. This scales poorly with a large number of
     // WebContents. As a workaround, the C++ owner uses a JavaObjectWeakGlobalRef and an entry is
@@ -299,14 +296,6 @@
     public void setUseDesktopUserAgent(
             boolean override, boolean reloadOnChange, boolean skipOnInitialNavigation) {
         if (mNativeNavigationControllerAndroid != 0) {
-            Log.i(
-                    TAG,
-                    "Thread dump for debugging, override: "
-                            + override
-                            + " reloadOnChange: "
-                            + reloadOnChange);
-            Thread.dumpStack();
-
             NavigationControllerImplJni.get()
                     .setUseDesktopUserAgent(
                             mNativeNavigationControllerAndroid,
diff --git a/content/public/test/browser_task_environment.cc b/content/public/test/browser_task_environment.cc
index cf45957c..aac5744 100644
--- a/content/public/test/browser_task_environment.cc
+++ b/content/public/test/browser_task_environment.cc
@@ -181,8 +181,8 @@
 
   auto browser_ui_thread_scheduler =
       BrowserUIThreadScheduler::CreateForTesting(sequence_manager());
-  auto default_ui_task_runner =
-      browser_ui_thread_scheduler->GetHandle()->GetDefaultTaskRunner();
+  auto* default_ui_task_queue =
+      browser_ui_thread_scheduler->GetDefaultTaskQueue();
   auto browser_io_thread_delegate =
       real_io_thread_
           ? std::make_unique<BrowserIOThreadDelegate>()
@@ -191,7 +191,7 @@
 
   BrowserTaskExecutor::CreateForTesting(std::move(browser_ui_thread_scheduler),
                                         std::move(browser_io_thread_delegate));
-  DeferredInitFromSubclass(std::move(default_ui_task_runner));
+  DeferredInitFromSubclass(default_ui_task_queue);
 
   if (HasIOMainLoop()) {
     CHECK(base::CurrentIOThread::IsSet());
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index eea2b2f..06b8dd0 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -279,8 +279,7 @@
       std::make_unique<RendererBlinkPlatformImplTestOverrideImpl>(
           main_thread_scheduler_.get());
 
-  DeferredInitFromSubclass(
-      main_thread_scheduler_->DeprecatedDefaultTaskRunner());
+  DeferredInitFromSubclass(nullptr);
 }
 
 void RenderViewTest::CustomTaskEnvironment::TearDown() {
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 40f7d3d..8d193858 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -930,8 +930,7 @@
   auto media_context_provider = viz::ContextProviderCommandBuffer::CreateForGL(
       gpu_channel_host, kGpuStreamIdMedia, kGpuStreamPriorityMedia,
       GURL("chrome://gpu/RenderThreadImpl::CreateOffscreenContext/Media"),
-      viz::command_buffer_metrics::ContextType::MEDIA,
-      /*lose_context_when_out_of_memory=*/true);
+      viz::command_buffer_metrics::ContextType::MEDIA);
 
   const bool enable_video_decode_accelerator =
 #if BUILDFLAG(IS_LINUX)
@@ -1015,9 +1014,7 @@
           GURL("chrome://gpu/RenderThreadImpl::CreateOffscreenContext/"
                "RenderCompositor"),
           /*automatic_flushes=*/false, /*support_locking=*/false, limits,
-          viz::command_buffer_metrics::ContextType::RENDERER_COMPOSITOR,
-          /*lose_context_when_out_of_memory=*/true);
-
+          viz::command_buffer_metrics::ContextType::RENDERER_COMPOSITOR);
   return video_frame_compositor_context_provider_;
 }
 
@@ -1081,8 +1078,7 @@
                "RendererMainThread"),
           /*automatic_flushes=*/true, /*support_locking=*/false,
           gpu::SharedMemoryLimits(),
-          viz::command_buffer_metrics::ContextType::RENDERER_MAIN_THREAD,
-          /*lose_context_when_out_of_memory=*/true);
+          viz::command_buffer_metrics::ContextType::RENDERER_MAIN_THREAD);
 
   auto result = shared_main_thread_contexts_->BindToCurrentSequence();
   if (result != gpu::ContextResult::kSuccess) {
@@ -1537,8 +1533,7 @@
                "RenderWorker"),
           /*automatic_flushes=*/false, /*support_locking=*/true,
           shared_memory_limits,
-          viz::command_buffer_metrics::ContextType::RENDERER_RASTER_WORKER,
-          /*lose_context_when_out_of_memory=*/true);
+          viz::command_buffer_metrics::ContextType::RENDERER_RASTER_WORKER);
 
   auto result = shared_worker_context_provider_->BindToCurrentSequence();
   if (result != gpu::ContextResult::kSuccess) {
@@ -1580,8 +1575,7 @@
            "MediaWorker"),
       /*automatic_flushes=*/false, /*support_locking=*/true,
       shared_memory_limits,
-      viz::command_buffer_metrics::ContextType::RENDERER_MEDIA_WORKER,
-      /*lose_context_when_out_of_memory=*/true);
+      viz::command_buffer_metrics::ContextType::RENDERER_MEDIA_WORKER);
 
   GetMediaSequencedTaskRunner()->PostTaskAndReplyWithResult(
       FROM_HERE,
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 020b881..e5108355 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -781,7 +781,6 @@
 
   constexpr bool automatic_flushes = true;
   constexpr bool support_locking = false;
-  constexpr bool lose_context_when_out_of_memory = false;
 
   gpu::SchedulingPriority stream_priority =
       (base::FeatureList::IsEnabled(features::kInitialWebUI) &&
@@ -795,8 +794,7 @@
       viz::ContextProviderCommandBuffer::CreateForRaster(
           std::move(gpu_channel_host), kGpuStreamIdDefault, stream_priority,
           GURL(document_url), automatic_flushes, support_locking,
-          gpu::SharedMemoryLimits(), ToVizContextType(context_type),
-          lose_context_when_out_of_memory));
+          gpu::SharedMemoryLimits(), ToVizContextType(context_type)));
 }
 
 //------------------------------------------------------------------------------
diff --git a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
index 722828a..fb0b29384 100644
--- a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
@@ -309,6 +309,9 @@
 [ mac amd-0x7340 debug ] WebGLCanvasCaptureTraceTest_VideoStreamFrom2DAlphaCanvas [ Slow ]
 [ mac amd-0x7340 debug ] WebGLCanvasCaptureTraceTest_VideoStreamFromWebGLAlphaCanvas [ Slow ]
 
+# Copies from RGB WebGL Canvas to YUV Video Frame is unsupported due to EGL image creation errors on Linux NVIDIA.
+crbug.com/447709687 [ linux nvidia ] WebGLCanvasCaptureTraceTest_VideoStreamFromWebGLCanvas_TwoCopy_Accelerated [ Failure ]
+crbug.com/447709687 [ linux nvidia ] WebGLCanvasCaptureTraceTest_VideoStreamFromWebGLCanvas_OneCopy [ Failure ]
 #######################################################################
 # Automated Entries After This Point - Do Not Manually Add Below Here #
 #######################################################################
diff --git a/content/test/gpu_browsertest_helpers.cc b/content/test/gpu_browsertest_helpers.cc
index 1f01434..f471faf7 100644
--- a/content/test/gpu_browsertest_helpers.cc
+++ b/content/test/gpu_browsertest_helpers.cc
@@ -56,8 +56,7 @@
         content::kGpuStreamPriorityDefault, GURL(),
         /*automatic_flushes=*/false, /*support_locking=*/false,
         gpu::SharedMemoryLimits(),
-        viz::command_buffer_metrics::ContextType::FOR_TESTING,
-        /*lose_context_when_out_of_memory=*/false);
+        viz::command_buffer_metrics::ContextType::FOR_TESTING);
   }
 
   return viz::ContextProviderCommandBuffer::CreateForGL(
diff --git a/extensions/browser/api/declarative_net_request/filter_list_converter/converter.cc b/extensions/browser/api/declarative_net_request/filter_list_converter/converter.cc
index 33fb4bb..a2bda996 100644
--- a/extensions/browser/api/declarative_net_request/filter_list_converter/converter.cc
+++ b/extensions/browser/api/declarative_net_request/filter_list_converter/converter.cc
@@ -494,8 +494,8 @@
     return true;
   }
 
-  bool PutCssRule(const proto::CssRule& rule) override {
-    // Ignore CSS rules.
+  bool PutStyleRule(const proto::StyleRule& rule) override {
+    // Ignore style rules.
     return true;
   }
 
@@ -544,7 +544,7 @@
     CHECK(rule_input_stream);
     CHECK(subresource_filter::TransferRules(rule_input_stream.get(),
                                             &rule_output_stream,
-                                            nullptr /* css_rule_output */));
+                                            nullptr /* style_rule_output */));
   }
 
   return rule_output_stream.Finish();
diff --git a/extensions/browser/extension_registrar_unittest.cc b/extensions/browser/extension_registrar_unittest.cc
index 11211b9..5cf31f0f 100644
--- a/extensions/browser/extension_registrar_unittest.cc
+++ b/extensions/browser/extension_registrar_unittest.cc
@@ -30,7 +30,6 @@
 
 #if BUILDFLAG(IS_CHROMEOS)
 #include "base/test/scoped_feature_list.h"
-#include "chrome/common/pref_names.h"  // nogncheck
 #include "components/account_id/account_id.h"  // nogncheck
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
diff --git a/extensions/common/features/simple_feature.cc b/extensions/common/features/simple_feature.cc
index 7333581..e8c1f9c7 100644
--- a/extensions/common/features/simple_feature.cc
+++ b/extensions/common/features/simple_feature.cc
@@ -226,9 +226,7 @@
 }
 
 SimpleFeature::SimpleFeature()
-    : component_extensions_auto_granted_(true),
-      is_internal_(false),
-      disallow_for_service_workers_(false) {}
+    : is_internal_(false), disallow_for_service_workers_(false) {}
 
 SimpleFeature::~SimpleFeature() = default;
 
diff --git a/extensions/common/features/simple_feature.h b/extensions/common/features/simple_feature.h
index 76e5c212..db0080d 100644
--- a/extensions/common/features/simple_feature.h
+++ b/extensions/common/features/simple_feature.h
@@ -323,7 +323,10 @@
   // to perform the override availability check.
   DelegatedAvailabilityCheckHandler delegated_availability_check_handler_;
 
-  bool component_extensions_auto_granted_{false};
+  // Whether access to the feature is automatically granted to component
+  // extensions. This defaults to true to maintain backward compatibility and
+  // the expectation that component extensions are trusted.
+  bool component_extensions_auto_granted_{true};
   bool is_internal_;
   bool requires_delegated_availability_check_{false};
   bool developer_mode_only_{false};
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
index 0947f6c..4388292 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -1315,9 +1315,16 @@
   caps.msaa_is_slow = MSAAIsSlow(feature_info_->workarounds());
   caps.avoid_stencil_buffers =
       feature_info_->workarounds().avoid_stencil_buffers;
-  caps.supports_rgb_to_yuv_conversion = true;
-  // Technically, YUV readback is handled on the client side, but enable it here
-  // so that clients can use this to detect support.
+
+  if (base::FeatureList::IsEnabled(
+          features::kNvidiaWaylandYuvHardwareConversionWorkaround) &&
+      feature_info_->workarounds().disable_rgb_to_yuv_conversion) {
+    caps.supports_rgb_to_yuv_conversion = false;
+  } else {
+    caps.supports_rgb_to_yuv_conversion = true;
+  }
+  // Technically, YUV readback is handled on the client side, but enable it
+  // here so that clients can use this to detect support.
   caps.supports_yuv_readback = true;
   caps.mesa_framebuffer_flip_y =
       feature_info_->feature_flags().mesa_framebuffer_flip_y;
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 4424789b..f89bec1 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -1211,6 +1211,12 @@
     caps.supports_yuv_readback = true;
   }
 
+  if (base::FeatureList::IsEnabled(
+          features::kNvidiaWaylandYuvHardwareConversionWorkaround) &&
+      feature_info()->workarounds().disable_rgb_to_yuv_conversion) {
+    caps.supports_rgb_to_yuv_conversion = false;
+  }
+
 #if BUILDFLAG(IS_CHROMEOS)
   if (shared_context_state_->GrContextIsGL()) {
     PopulateDRMCapabilities(&caps, feature_info());
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index 699e7e3..7c96271 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -3761,6 +3761,18 @@
       "features": [
         "disable_vaapi"
       ]
+    },
+    {
+      "id": 474,
+      "description": "Disable RGB to YUV hardware conversion on NVIDIA Linux",
+      "cr_bugs": [447709687],
+      "os": {
+        "type": "linux"
+      },
+      "gl_vendor": "NVIDIA.*",
+      "features": [
+        "disable_rgb_to_yuv_conversion"
+      ]
     }
   ]
 }
diff --git a/gpu/config/gpu_finch_features.cc b/gpu/config/gpu_finch_features.cc
index 89763eb1..8a78842 100644
--- a/gpu/config/gpu_finch_features.cc
+++ b/gpu/config/gpu_finch_features.cc
@@ -116,6 +116,16 @@
 BASE_FEATURE(kSharedImageStubHighPriority, base::FEATURE_DISABLED_BY_DEFAULT);
 #endif
 
+// Disables hardware YUV conversion on NVIDIA + Wayland to workaround a driver
+// bug.
+BASE_FEATURE(kNvidiaWaylandYuvHardwareConversionWorkaround,
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
+             base::FEATURE_ENABLED_BY_DEFAULT
+#else
+             base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+);
+
 // Enable GPU Rasterization by default. This can still be overridden by
 // --enable-gpu-rasterization or --disable-gpu-rasterization.
 // DefaultEnableGpuRasterization has launched on Mac, Windows, ChromeOS,
diff --git a/gpu/config/gpu_finch_features.h b/gpu/config/gpu_finch_features.h
index 5f2f97e..a822d500 100644
--- a/gpu/config/gpu_finch_features.h
+++ b/gpu/config/gpu_finch_features.h
@@ -111,6 +111,8 @@
 GPU_CONFIG_EXPORT BASE_DECLARE_FEATURE(kSharedImageStubHighPriority);
 #endif
 
+GPU_CONFIG_EXPORT BASE_DECLARE_FEATURE(
+    kNvidiaWaylandYuvHardwareConversionWorkaround);
 GPU_CONFIG_EXPORT BASE_DECLARE_FEATURE(kWebGPUService);
 GPU_CONFIG_EXPORT BASE_DECLARE_FEATURE(kAAPMBlocksWebGPU);
 GPU_CONFIG_EXPORT BASE_DECLARE_FEATURE(kWebGPUBlobCache);
diff --git a/gpu/config/gpu_workaround_list.txt b/gpu/config/gpu_workaround_list.txt
index 8f60279f..21d2f05 100644
--- a/gpu/config/gpu_workaround_list.txt
+++ b/gpu/config/gpu_workaround_list.txt
@@ -57,6 +57,7 @@
 disable_program_caching_for_transform_feedback
 disable_program_disk_cache
 disable_rendering_to_rgb_external_texture
+disable_rgb_to_yuv_conversion
 disable_sharing_nv12_from_d3d11_to_d3d12
 disable_skia_reduce_ops_task_splitting
 disable_software_to_accelerated_canvas_upgrade
diff --git a/gpu/ipc/common/gpu_channel.mojom b/gpu/ipc/common/gpu_channel.mojom
index 661d954..e94b354 100644
--- a/gpu/ipc/common/gpu_channel.mojom
+++ b/gpu/ipc/common/gpu_channel.mojom
@@ -40,13 +40,11 @@
 };
 
 struct RasterCreationAttribs {
-  bool lose_context_when_out_of_memory = false;
 };
 
 struct GLESCreationAttribs {
   gl.mojom.GpuPreference gpu_preference = kLowPower;
   bool fail_if_major_perf_caveat = false;
-  bool lose_context_when_out_of_memory = false;
   ContextType context_type = kOpenGLES2;
 };
 
diff --git a/gpu/ipc/gl_in_process_context.cc b/gpu/ipc/gl_in_process_context.cc
index ccc527de..b92d9bc 100644
--- a/gpu/ipc/gl_in_process_context.cc
+++ b/gpu/ipc/gl_in_process_context.cc
@@ -88,7 +88,7 @@
   // Create the object exposing the OpenGL API.
   gles2_implementation_ = std::make_unique<gles2::GLES2Implementation>(
       gles2_helper_.get(), /*share_group=*/nullptr, transfer_buffer_.get(),
-      /*lose_context_when_out_of_memory=*/false, command_buffer_.get());
+      /*lose_context_when_out_of_memory=*/true, command_buffer_.get());
 
   result = gles2_implementation_->Initialize(mem_limits);
   return result;
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc
index e99a578..115e4d67 100644
--- a/gpu/ipc/in_process_command_buffer.cc
+++ b/gpu/ipc/in_process_command_buffer.cc
@@ -362,9 +362,8 @@
               task_executor_->shared_image_manager(), context_state_,
               /*is_privileged=*/true);
 
-      const auto& attribs = params.attribs->get_raster();
       auto result =
-          raster_decoder->Initialize(attribs->lose_context_when_out_of_memory);
+          raster_decoder->Initialize(/*lose_context_when_out_of_memory=*/true);
       if (result != gpu::ContextResult::kSuccess) {
         DestroyOnGpuThread();
         DLOG(ERROR) << "Failed to initialize decoder.";
@@ -451,7 +450,7 @@
       }
       auto result = gles2_decoder->Initialize(
           surface, context_, /*offscreen=*/true, attribs->context_type,
-          attribs->lose_context_when_out_of_memory);
+          /*lose_context_when_out_of_memory=*/true);
       if (result != gpu::ContextResult::kSuccess) {
         DestroyOnGpuThread();
         DLOG(ERROR) << "Failed to initialize decoder.";
diff --git a/gpu/ipc/raster_in_process_context.cc b/gpu/ipc/raster_in_process_context.cc
index 926a50fa..166e6cd 100644
--- a/gpu/ipc/raster_in_process_context.cc
+++ b/gpu/ipc/raster_in_process_context.cc
@@ -45,10 +45,8 @@
     CommandBufferTaskExecutor* task_executor,
     gpu::raster::GrShaderCache* gr_shader_cache,
     GpuProcessShmCount* use_shader_cache_shm_count) {
-  constexpr bool lose_context_when_out_of_memory = false;
   auto attribs = mojom::ContextCreationAttribs::NewRaster(
-      mojom::RasterCreationAttribs::New(
-          /*lose_context_when_out_of_memory=*/lose_context_when_out_of_memory));
+      mojom::RasterCreationAttribs::New());
 
   command_buffer_ =
       std::make_unique<InProcessCommandBuffer>(task_executor, GURL());
@@ -74,8 +72,7 @@
 
   raster_implementation_ = std::make_unique<raster::RasterImplementation>(
       raster_helper.get(), transfer_buffer_.get(),
-      /*lose_context_when_out_of_memory=*/lose_context_when_out_of_memory,
-      command_buffer_.get());
+      /*lose_context_when_out_of_memory=*/true, command_buffer_.get());
   result = raster_implementation_->Initialize(memory_limits);
   raster_implementation_->SetLostContextCallback(base::BindOnce(
       []() { EXPECT_TRUE(false) << "Unexpected lost context."; }));
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.cc b/gpu/ipc/service/gles2_command_buffer_stub.cc
index b71b3ce..53410f5 100644
--- a/gpu/ipc/service/gles2_command_buffer_stub.cc
+++ b/gpu/ipc/service/gles2_command_buffer_stub.cc
@@ -297,7 +297,7 @@
   // Initialize the decoder with either the view or pbuffer GLContext.
   auto result = gles2_decoder_->Initialize(
       surface_, context, /*offscreen=*/true, attribs.context_type,
-      attribs.lose_context_when_out_of_memory);
+      /*lose_context_when_out_of_memory=*/true);
   if (result != gpu::ContextResult::kSuccess) {
     DLOG(ERROR) << "Failed to initialize decoder.";
     return result;
diff --git a/gpu/ipc/service/gpu_channel.cc b/gpu/ipc/service/gpu_channel.cc
index 0002bd02..6224c3d7 100644
--- a/gpu/ipc/service/gpu_channel.cc
+++ b/gpu/ipc/service/gpu_channel.cc
@@ -994,6 +994,11 @@
     return;
   }
 
+  if (route_id <= static_cast<int32_t>(GpuChannelReservedRoutes::kMaxValue)) {
+    LOG(ERROR) << "ContextResult::kFatalFailure: using reserved route";
+    return;
+  }
+
   int32_t stream_id = init_params->stream_id;
   CommandBufferId command_buffer_id =
       CommandBufferIdFromChannelAndRoute(client_id_, route_id);
@@ -1055,6 +1060,10 @@
   TRACE_EVENT1("gpu", "GpuChannel::OnDestroyCommandBuffer", "route_id",
                route_id);
 
+  if (route_id <= static_cast<int32_t>(GpuChannelReservedRoutes::kMaxValue)) {
+    return;
+  }
+
   std::unique_ptr<CommandBufferStub> stub;
   auto it = stubs_.find(route_id);
   if (it != stubs_.end()) {
diff --git a/gpu/ipc/service/raster_command_buffer_stub.cc b/gpu/ipc/service/raster_command_buffer_stub.cc
index 4afc11a..603158bb 100644
--- a/gpu/ipc/service/raster_command_buffer_stub.cc
+++ b/gpu/ipc/service/raster_command_buffer_stub.cc
@@ -59,8 +59,6 @@
   TRACE_EVENT0("gpu", "RasterBufferStub::Initialize");
   UpdateActiveUrl();
 
-  const auto& attribs = *init_params.attribs->get_raster();
-
   GpuChannelManager* manager = channel_->gpu_channel_manager();
   DCHECK(manager);
 
@@ -107,7 +105,7 @@
   }
 
   // Initialize the decoder with either the view or pbuffer GLContext.
-  result = decoder->Initialize(attribs.lose_context_when_out_of_memory);
+  result = decoder->Initialize(/*lose_context_when_out_of_memory=*/true);
   if (result != gpu::ContextResult::kSuccess) {
     DLOG(ERROR) << "Failed to initialize decoder.";
     return result;
diff --git a/internal b/internal
index 2552620..f38946a 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 25526209ef1e7ff29da6a5ff5bc8a82667dd7e41
+Subproject commit f38946afef7fe36048db6bc80509c2e23475f82e
diff --git a/ios/chrome/browser/ai_prototyping/coordinator/ai_prototyping_mediator.mm b/ios/chrome/browser/ai_prototyping/coordinator/ai_prototyping_mediator.mm
index 2ff8047..183cedff 100644
--- a/ios/chrome/browser/ai_prototyping/coordinator/ai_prototyping_mediator.mm
+++ b/ios/chrome/browser/ai_prototyping/coordinator/ai_prototyping_mediator.mm
@@ -554,7 +554,6 @@
     return;
   }
 
-  optimization_guide::proto::Action action;
   NSString* jsonString = params[@"json"];
   if (jsonString.length == 0) {
     [self.consumer updateQueryResult:@"Error: No JSON provided."
@@ -564,37 +563,85 @@
   std::optional<base::Value> jsonVal = base::JSONReader::Read(
       base::SysNSStringToUTF8(jsonString), base::JSON_ALLOW_TRAILING_COMMAS);
 
-  if (!jsonVal || !jsonVal->is_dict()) {
+  if (!jsonVal) {
     [self.consumer updateQueryResult:@"Error: Invalid JSON."
                           forFeature:AIPrototypingFeature::kActorTools];
     return;
   }
 
-  // Try to parse the JSON to a known action optimization_guide::proto::Action.
-  if (!ai_prototyping::ParseActionFromDict(jsonVal->GetDict(), &action)) {
-    [self.consumer updateQueryResult:@"Error: Unknown action type in JSON."
-                          forFeature:AIPrototypingFeature::kActorTools];
+  std::vector<optimization_guide::proto::Action> actions;
+  if (jsonVal->is_dict()) {
+    optimization_guide::proto::Action action;
+    if (!ai_prototyping::ParseActionFromDict(jsonVal->GetDict(), &action)) {
+      [self.consumer updateQueryResult:@"Error: Unknown action type in JSON."
+                            forFeature:AIPrototypingFeature::kActorTools];
+      return;
+    }
+    actions.push_back(std::move(action));
+  } else if (jsonVal->is_list()) {
+    for (const auto& item : jsonVal->GetList()) {
+      if (!item.is_dict()) {
+        [self.consumer updateQueryResult:@"Error: Invalid JSON array element."
+                              forFeature:AIPrototypingFeature::kActorTools];
+        return;
+      }
+      optimization_guide::proto::Action action;
+      if (!ai_prototyping::ParseActionFromDict(item.GetDict(), &action)) {
+        [self.consumer
+            updateQueryResult:@"Error: Unknown action type in JSON array."
+                   forFeature:AIPrototypingFeature::kActorTools];
+        return;
+      }
+      actions.push_back(std::move(action));
+    }
+  } else {
+    [self.consumer
+        updateQueryResult:@"Error: JSON must be a dictionary or list."
+               forFeature:AIPrototypingFeature::kActorTools];
     return;
   }
 
   __weak __typeof(self) weakSelf = self;
-  actorService->ExecuteAction(
-      action, base::BindOnce(^(actor::ActorTool::ToolExecutionResult result) {
-        NSLog(@"[AIPrototypingMediator] Actor callback executed.");
-        if (result.has_value()) {
-          [weakSelf.consumer
-              updateQueryResult:@"Action executed successfully."
-                     forFeature:AIPrototypingFeature::kActorTools];
-        } else {
-          NSString* errorMsg = base::SysUTF8ToNSString(base::StringPrintf(
-              "Action failed: %s",
-              actor::GetActorToolErrorMessage(result.error()).c_str()));
-          NSLog(@"[AIPrototypingMediator] %@", errorMsg);
-          [weakSelf.consumer
-              updateQueryResult:errorMsg
-                     forFeature:AIPrototypingFeature::kActorTools];
-        }
-      }));
+  auto remaining_actions =
+      std::make_shared<std::vector<optimization_guide::proto::Action>>(
+          std::move(actions));
+  __block void (^executeNextAction)(void) = nil;
+
+  void (^executeNextActionBlock)(void) = ^void() {
+    if (remaining_actions->empty()) {
+      [weakSelf.consumer updateQueryResult:@"Action(s) executed successfully."
+                                forFeature:AIPrototypingFeature::kActorTools];
+      executeNextAction = nil;
+      return;
+    }
+    optimization_guide::proto::Action next_action =
+        std::move(remaining_actions->front());
+    remaining_actions->erase(remaining_actions->begin());
+
+    actorService->ExecuteAction(
+        next_action,
+        base::BindOnce(^(actor::ActorTool::ToolExecutionResult result) {
+          NSLog(@"[AIPrototypingMediator] Actor callback executed.");
+          if (result.has_value()) {
+            dispatch_async(dispatch_get_main_queue(), ^{
+              if (executeNextAction) {
+                executeNextAction();
+              }
+            });
+          } else {
+            NSString* errorMsg = base::SysUTF8ToNSString(base::StringPrintf(
+                "Action failed: %s",
+                actor::GetActorToolErrorMessage(result.error()).c_str()));
+            NSLog(@"[AIPrototypingMediator] %@", errorMsg);
+            [weakSelf.consumer
+                updateQueryResult:errorMsg
+                       forFeature:AIPrototypingFeature::kActorTools];
+            executeNextAction = nil;
+          }
+        }));
+  };
+  executeNextAction = executeNextActionBlock;
+  executeNextAction();
 }
 
 - (void)listTabs {
diff --git a/ios/chrome/browser/ai_prototyping/ui/ai_prototyping_actor_view_controller.mm b/ios/chrome/browser/ai_prototyping/ui/ai_prototyping_actor_view_controller.mm
index 0029132c..d853690 100644
--- a/ios/chrome/browser/ai_prototyping/ui/ai_prototyping_actor_view_controller.mm
+++ b/ios/chrome/browser/ai_prototyping/ui/ai_prototyping_actor_view_controller.mm
@@ -20,6 +20,7 @@
 NSString* const kToolHistoryForward = @"History Forward";
 NSString* const kToolType = @"Type";
 NSString* const kToolWait = @"Wait";
+NSString* const kToolMultiTool = @"Multi-tool";
 
 // Placeholder macro for tab ID.
 NSString* const kTabIdMacro = @"{{tab_id}}";
@@ -28,9 +29,10 @@
 // purposes, more tab related information will be exposed and available for
 // modification for these tools for debugging.
 bool IsWebActuationTool(NSString* tool) {
-  return [tool isEqualToString:kToolClick] || [tool isEqualToString:kToolType];
+  return [tool isEqualToString:kToolClick] ||
+         [tool isEqualToString:kToolType] ||
+         [tool isEqualToString:kToolMultiTool];
 }
-
 }  // namespace
 
 @interface AIPrototypingActorViewController () <UITextViewDelegate> {
@@ -93,7 +95,7 @@
   [self setupToolConfigs];
 
   // Default tool
-  [self selectTool:kToolNavigate];
+  [self selectTool:kToolMultiTool];
 }
 
 - (void)viewWillAppear:(BOOL)animated {
@@ -397,6 +399,27 @@
 // coordinate based approach.
 - (void)setupToolConfigs {
   _toolConfigs = @{
+    kToolMultiTool : @{
+      @"ui" : @[ _tabIdContainer, _frameIdContainer, _jsonContainer ],
+      @"template" : @[
+        @{
+          @"click" : @{
+            @"tab_id" : @(0),
+            @"target" : @{@"coordinate" : @{@"x" : @(100), @"y" : @(100)}},
+            @"click_type" : @(1),
+            @"click_count" : @(1)
+          }
+        },
+        @{@"wait" : @{@"wait_time_ms" : @(3000), @"observe_tab_id" : @(0)}}, @{
+          @"click" : @{
+            @"tab_id" : @(0),
+            @"target" : @{@"coordinate" : @{@"x" : @(200), @"y" : @(200)}},
+            @"click_type" : @(1),
+            @"click_count" : @(1)
+          }
+        }
+      ]
+    },
     kToolNavigate : @{
       @"ui" : @[ _tabIdContainer, _jsonContainer ],
       @"template" : @{
@@ -415,14 +438,6 @@
         }
       }
     },
-    kToolHistoryBack : @{
-      @"ui" : @[ _tabIdContainer, _jsonContainer ],
-      @"template" : @{@"back" : @{@"tab_id" : kTabIdMacro}}
-    },
-    kToolHistoryForward : @{
-      @"ui" : @[ _tabIdContainer, _jsonContainer ],
-      @"template" : @{@"forward" : @{@"tab_id" : kTabIdMacro}}
-    },
     kToolType : @{
       @"ui" : @[ _tabIdContainer, _frameIdContainer, _jsonContainer ],
       @"template" : @{
@@ -435,6 +450,14 @@
         }
       }
     },
+    kToolHistoryBack : @{
+      @"ui" : @[ _tabIdContainer, _jsonContainer ],
+      @"template" : @{@"back" : @{@"tab_id" : kTabIdMacro}}
+    },
+    kToolHistoryForward : @{
+      @"ui" : @[ _tabIdContainer, _jsonContainer ],
+      @"template" : @{@"forward" : @{@"tab_id" : kTabIdMacro}}
+    },
     kToolWait : @{
       @"ui" : @[ _tabIdContainer, _jsonContainer ],
       @"template" : @{
@@ -464,6 +487,32 @@
                                    children:@[ deferredElement ]];
 }
 
+// Helper to update a tool dictionary with tab ID and frame ID.
+- (void)updateToolDict:(NSMutableDictionary*)toolDict
+        configTemplate:(NSDictionary*)configTemplate
+                 tabId:(NSString*)tabId
+               frameId:(NSString*)frameId {
+  if (![toolDict isKindOfClass:[NSMutableDictionary class]]) {
+    return;
+  }
+  // 2. Update Tab ID.
+  if (tabId) {
+    toolDict[@"tab_id"] = @([tabId intValue]);
+  }
+
+  // 3. Update Target (Frame).
+  if (toolDict[@"target"]) {
+    if (frameId) {
+      toolDict[@"target"] =
+          [@{@"document_identifier" : frameId, @"content_node_id" : @(1)}
+              mutableCopy];
+    } else if (configTemplate && configTemplate[@"target"]) {
+      // Revert to template default if frame deselected.
+      toolDict[@"target"] = [configTemplate[@"target"] mutableCopy];
+    }
+  }
+}
+
 /**
  * Updates the JSON template for the specified tool with the given params.
  *
@@ -483,14 +532,11 @@
   if (!config) {
     return;
   }
-  NSDictionary<NSString*, NSDictionary*>* toolTemplate =
-      base::apple::ObjCCastStrict<NSDictionary<NSString*, NSDictionary*>>(
-          config[@"template"]);
 
-  // 1. Loads input data from  `_jsonInputView` or, if empty, the template.
+  // 1. Loads input data from `_jsonInputView` or, if empty, the template.
   NSData* inputData;
   if (_jsonInputView.text.length == 0) {
-    inputData = [NSJSONSerialization dataWithJSONObject:toolTemplate
+    inputData = [NSJSONSerialization dataWithJSONObject:config[@"template"]
                                                 options:0
                                                   error:nil];
   } else {
@@ -513,41 +559,86 @@
 
   // 3. Parse the data into JSON format.
   NSError* error = nil;
-  NSMutableDictionary* jsonDict =
+  id jsonRoot =
       [NSJSONSerialization JSONObjectWithData:inputData
                                       options:NSJSONReadingMutableContainers
                                         error:&error];
-  if (!jsonDict) {
+
+  if (!jsonRoot) {
     _responseContainer.text =
         [NSString stringWithFormat:@"Failed to parse JSON: %@",
                                    error.localizedDescription];
     return;
   }
 
-  // 4. Update Target (Frame).
-  NSMutableDictionary* toolDict =
-      base::apple::ObjCCastStrict<NSMutableDictionary>(
-          jsonDict.allValues.firstObject);
-  if (toolDict && toolDict[@"target"]) {
-    if (frameId) {
-      toolDict[@"target"] =
-          @{@"document_identifier" : frameId, @"content_node_id" : @(1)};
-    } else if (toolDict[@"target"][@"document_identifier"]) {
-      // Revert to template default if frame deselected.
-      NSDictionary* templateDefault = base::apple::ObjCCastStrict<NSDictionary>(
-          toolTemplate.allValues.firstObject);
-      toolDict[@"target"] = [templateDefault[@"target"] mutableCopy];
+  NSString* toolKey = [toolName lowercaseString];
+
+  if (![toolName isEqualToString:kToolMultiTool]) {
+    if (![jsonRoot isKindOfClass:[NSMutableDictionary class]] ||
+        ![jsonRoot count]) {
+      _responseContainer.text = [NSString
+          stringWithFormat:@"JSON mismatch: missing key '%@'", toolKey];
+      return;
+    }
+
+    // Attempt to find the tool dictionary. Use the toolKey if present,
+    // otherwise fallback to the first object.
+    NSMutableDictionary* toolDict = jsonRoot[toolKey];
+    if (!toolDict) {
+      toolDict = [jsonRoot allValues].firstObject;
+    }
+
+    // Find the config template for target reversion.
+    NSDictionary* configTemplate = config[@"template"][toolKey];
+    if (!configTemplate &&
+        [config[@"template"] isKindOfClass:[NSDictionary class]]) {
+      configTemplate = [config[@"template"] allValues].firstObject;
+    }
+
+    [self updateToolDict:toolDict
+          configTemplate:configTemplate
+                   tabId:tabId
+                 frameId:frameId];
+  } else {
+    if (![jsonRoot isKindOfClass:[NSMutableArray class]]) {
+      _responseContainer.text =
+          @"JSON mismatch: Multi-tool requires an array of actions.";
+      return;
+    }
+
+    for (id action in (NSMutableArray*)jsonRoot) {
+      if (![action isKindOfClass:[NSMutableDictionary class]]) {
+        continue;
+      }
+      NSMutableDictionary* actionDict = (NSMutableDictionary*)action;
+      NSString* actionKey = [[actionDict allKeys] firstObject];
+      if (actionKey) {
+        // Find default template for this action type.
+        NSDictionary* defaultTemplate = nil;
+        for (NSString* key in _toolConfigs) {
+          NSDictionary* toolConfig = _toolConfigs[key];
+          if ([toolConfig[@"template"] isKindOfClass:[NSDictionary class]] &&
+              toolConfig[@"template"][actionKey]) {
+            defaultTemplate = toolConfig[@"template"][actionKey];
+            break;
+          }
+        }
+        [self updateToolDict:actionDict[actionKey]
+              configTemplate:defaultTemplate
+                       tabId:tabId
+                     frameId:frameId];
+      }
     }
   }
 
-  // 5. Write Back to UI.
-  NSData* output = [NSJSONSerialization
-      dataWithJSONObject:jsonDict
+  // 4. Serialize the JSON back to the input view.
+  NSData* updatedData = [NSJSONSerialization
+      dataWithJSONObject:jsonRoot
                  options:NSJSONWritingPrettyPrinted |
                          NSJSONWritingWithoutEscapingSlashes
                    error:nil];
-  if (output) {
-    _jsonInputView.text = [[NSString alloc] initWithData:output
+  if (updatedData) {
+    _jsonInputView.text = [[NSString alloc] initWithData:updatedData
                                                 encoding:NSUTF8StringEncoding];
   }
 }
@@ -586,7 +677,14 @@
 - (UIMenu*)createToolPickerMenu {
   NSMutableArray<UIAction*>* actions = [NSMutableArray array];
   __weak __typeof(self) weakSelf = self;
-  for (NSString* toolName in _toolConfigs) {
+
+  // Define the explicit order for the dropdown menu.
+  NSArray<NSString*>* orderedTools = @[
+    kToolMultiTool, kToolNavigate, kToolClick, kToolType, kToolHistoryBack,
+    kToolHistoryForward, kToolWait
+  ];
+
+  for (NSString* toolName in orderedTools) {
     UIAction* action = [UIAction actionWithTitle:toolName
                                            image:nil
                                       identifier:nil
diff --git a/ios/chrome/browser/autofill/form_input_accessory/coordinator/form_input_accessory_mediator.mm b/ios/chrome/browser/autofill/form_input_accessory/coordinator/form_input_accessory_mediator.mm
index 537bcce..f219e0a 100644
--- a/ios/chrome/browser/autofill/form_input_accessory/coordinator/form_input_accessory_mediator.mm
+++ b/ios/chrome/browser/autofill/form_input_accessory/coordinator/form_input_accessory_mediator.mm
@@ -829,7 +829,8 @@
 // Handles the selection of a suggestion. `index` indicates the position of the
 // suggestion among the available suggestions.
 - (void)handleSuggestion:(FormSuggestion*)formSuggestion
-                 atIndex:(NSInteger)index {
+                 atIndex:(NSInteger)index
+              completion:(ProceduralBlock)completion {
   if ([self getProviderTypeFromSuggestion:formSuggestion] ==
       SuggestionProviderTypePassword) {
     default_browser::NotifyPasswordAutofillSuggestionUsed(
@@ -840,7 +841,9 @@
         notifyAutofillSuggestionWithIPHSelectedFor:formSuggestion
                                                        .featureForIPH];
   }
-  [self.currentProvider didSelectSuggestion:formSuggestion atIndex:index];
+  [self.currentProvider didSelectSuggestion:formSuggestion
+                                    atIndex:index
+                                 completion:completion];
 }
 
 // Sets the last focused form activity web frame ID with the given `frame`.
@@ -861,7 +864,8 @@
 #pragma mark - FormSuggestionClient
 
 - (void)didSelectSuggestion:(FormSuggestion*)formSuggestion
-                    atIndex:(NSInteger)index {
+                    atIndex:(NSInteger)index
+                 completion:(ProceduralBlock)completion {
   if (IsStateless()) {
     // When using the stateless FormSuggestionsController, ensure the params
     // attached to the suggestion are the same as the ones held by this mediator
@@ -882,19 +886,25 @@
   if (!formSuggestion.requiresReauth) {
     [self logReauthenticationEvent:ReauthenticationEvent::kSuccess
                      forSuggestion:formSuggestion];
-    [self handleSuggestion:formSuggestion atIndex:index];
+    [self handleSuggestion:formSuggestion atIndex:index completion:completion];
     return;
   }
   if ([self.reauthenticationModule canAttemptReauth]) {
     NSString* reason = l10n_util::GetNSString(IDS_IOS_AUTOFILL_REAUTH_REASON);
+    __weak __typeof(self) weakSelf = self;
     auto completionHandler = ^(ReauthenticationResult result) {
       if (result != ReauthenticationResult::kFailure) {
-        [self logReauthenticationEvent:ReauthenticationEvent::kSuccess
-                         forSuggestion:formSuggestion];
-        [self handleSuggestion:formSuggestion atIndex:index];
+        [weakSelf logReauthenticationEvent:ReauthenticationEvent::kSuccess
+                             forSuggestion:formSuggestion];
+        [weakSelf handleSuggestion:formSuggestion
+                           atIndex:index
+                        completion:completion];
       } else {
-        [self logReauthenticationEvent:ReauthenticationEvent::kFailure
-                         forSuggestion:formSuggestion];
+        [weakSelf logReauthenticationEvent:ReauthenticationEvent::kFailure
+                             forSuggestion:formSuggestion];
+        if (completion) {
+          completion();
+        }
       }
     };
 
@@ -905,15 +915,16 @@
   } else {
     [self logReauthenticationEvent:ReauthenticationEvent::kMissingPasscode
                      forSuggestion:formSuggestion];
-    [self handleSuggestion:formSuggestion atIndex:index];
+    [self handleSuggestion:formSuggestion atIndex:index completion:completion];
   }
 }
 
 - (void)didSelectSuggestion:(FormSuggestion*)formSuggestion
                     atIndex:(NSInteger)index
-                     params:(const autofill::FormActivityParams&)params {
+                     params:(const autofill::FormActivityParams&)params
+                 completion:(ProceduralBlock)completion {
   CHECK_EQ(_lastSeenParams, params);
-  [self didSelectSuggestion:formSuggestion atIndex:index];
+  [self didSelectSuggestion:formSuggestion atIndex:index completion:completion];
 }
 
 #pragma mark - PasswordCounterObserver
diff --git a/ios/chrome/browser/autofill/form_input_accessory/coordinator/form_input_accessory_mediator_unittest.mm b/ios/chrome/browser/autofill/form_input_accessory/coordinator/form_input_accessory_mediator_unittest.mm
index d6a0f76..e021db3e 100644
--- a/ios/chrome/browser/autofill/form_input_accessory/coordinator/form_input_accessory_mediator_unittest.mm
+++ b/ios/chrome/browser/autofill/form_input_accessory/coordinator/form_input_accessory_mediator_unittest.mm
@@ -429,11 +429,13 @@
 
   const NSInteger suggestionIndex = 0;
 
-  OCMExpect([formInputSuggestionProviderMock
-      didSelectSuggestion:[OCMArg any]
-                  atIndex:suggestionIndex]);
+  OCMExpect([formInputSuggestionProviderMock didSelectSuggestion:[OCMArg any]
+                                                         atIndex:suggestionIndex
+                                                      completion:[OCMArg any]]);
 
-  [mediator_ didSelectSuggestion:suggestion atIndex:suggestionIndex];
+  [mediator_ didSelectSuggestion:suggestion
+                         atIndex:suggestionIndex
+                      completion:nil];
 
   // Look the authentication metrics associated with the type of the selected
   // provided are correctly recorded.
diff --git a/ios/chrome/browser/autofill/form_input_accessory/ui/form_input_accessory_view_controller.mm b/ios/chrome/browser/autofill/form_input_accessory/ui/form_input_accessory_view_controller.mm
index 015e0d5..7cc65a0a 100644
--- a/ios/chrome/browser/autofill/form_input_accessory/ui/form_input_accessory_view_controller.mm
+++ b/ios/chrome/browser/autofill/form_input_accessory/ui/form_input_accessory_view_controller.mm
@@ -364,7 +364,6 @@
 
 // Resets this view to its original state. Can be animated.
 - (void)resetAnimated:(BOOL)animated {
-  [self resetLoadingStates];
   self.formInputAccessoryView.hidden = NO;
 
   [self.formSuggestionView resetContentInsetAndDelegateAnimated:animated];
@@ -664,7 +663,13 @@
   if ([self isSuggestionAutofillAsync:suggestion]) {
     [formSuggestionView setActivityIndicatorEnabled:YES];
   }
-  [self.formSuggestionClient didSelectSuggestion:suggestion atIndex:index];
+  __weak FormSuggestionView* weakFormSuggestionView = formSuggestionView;
+  [self.formSuggestionClient
+      didSelectSuggestion:suggestion
+                  atIndex:index
+               completion:^{
+                 [weakFormSuggestionView setActivityIndicatorEnabled:NO];
+               }];
 }
 
 @end
diff --git a/ios/chrome/browser/autofill/model/form_suggestion_client.h b/ios/chrome/browser/autofill/model/form_suggestion_client.h
index 8cc17f5..0c0f7aba 100644
--- a/ios/chrome/browser/autofill/model/form_suggestion_client.h
+++ b/ios/chrome/browser/autofill/model/form_suggestion_client.h
@@ -7,6 +7,8 @@
 
 #import <Foundation/Foundation.h>
 
+#import "base/ios/block_types.h"
+
 namespace autofill {
 struct FormActivityParams;
 }  // namespace autofill
@@ -20,9 +22,11 @@
 // selected suggestion among the available suggestions. The client needs to get
 // the form activity params from somewhere, so if the client is stateless the
 // params need to be set in the `suggestion` itself, otherwise the client needs
-// to provide the params by tracking them.
+// to provide the params by tracking them. `completion` is called when the
+// operation completes (e.g. after reauth or early return).
 - (void)didSelectSuggestion:(FormSuggestion*)suggestion
-                    atIndex:(NSInteger)index;
+                    atIndex:(NSInteger)index
+                 completion:(ProceduralBlock)completion;
 
 // Called when a suggestion is selected. Provides the parameters required to
 // fill the form, so this version of 'didSelectSuggestion' can be used without
@@ -30,9 +34,12 @@
 // If the parameters have already been provided by a previous call, then the
 // 'didSelectSuggestion' overload above should be used. `index` indicates the
 // position of the selected suggestion among the available suggestions.
+// `completion` is called when the operation completes (e.g. after reauth or
+// early return).
 - (void)didSelectSuggestion:(FormSuggestion*)suggestion
                     atIndex:(NSInteger)index
-                     params:(const autofill::FormActivityParams&)params;
+                     params:(const autofill::FormActivityParams&)params
+                 completion:(ProceduralBlock)completion;
 
 @end
 
diff --git a/ios/chrome/browser/autofill/model/form_suggestion_controller.mm b/ios/chrome/browser/autofill/model/form_suggestion_controller.mm
index e7d2222..59dc4feb 100644
--- a/ios/chrome/browser/autofill/model/form_suggestion_controller.mm
+++ b/ios/chrome/browser/autofill/model/form_suggestion_controller.mm
@@ -461,7 +461,8 @@
 #pragma mark - FormSuggestionClient
 
 - (void)didSelectSuggestion:(FormSuggestion*)suggestion
-                    atIndex:(NSInteger)index {
+                    atIndex:(NSInteger)index
+                 completion:(ProceduralBlock)completion {
   if (IsStateless()) {
     // Check that there are always params attached to the suggestion when no
     // params are provided by the -didSelectSuggestion caller itself.
@@ -469,24 +470,35 @@
     if (!suggestion.params) {
       // Just skip if the check isn't triggered. This is to handle the absence
       // of params when the CHECK isn't fatal.
+      if (completion) {
+        completion();
+      }
       return;
     }
 
     [self didSelectSuggestion:suggestion
                       atIndex:index
-                        state:AutofillSuggestionState(*suggestion.params)];
+                        state:AutofillSuggestionState(*suggestion.params)
+                   completion:completion];
   } else if (_suggestionState) {
     [self didSelectSuggestion:suggestion
                       atIndex:index
-                        state:(*_suggestionState)];
+                        state:*_suggestionState
+                   completion:completion];
+  } else if (completion) {
+    completion();
   }
 }
 
 - (void)didSelectSuggestion:(FormSuggestion*)suggestion
                     atIndex:(NSInteger)index
-                     params:(const autofill::FormActivityParams&)params {
+                     params:(const autofill::FormActivityParams&)params
+                 completion:(ProceduralBlock)completion {
   AutofillSuggestionState suggestionState(params);
-  [self didSelectSuggestion:suggestion atIndex:index state:suggestionState];
+  [self didSelectSuggestion:suggestion
+                    atIndex:index
+                      state:suggestionState
+                 completion:completion];
 }
 
 #pragma mark - FormInputSuggestionsProvider
@@ -550,7 +562,8 @@
 // provided `suggestionState`.
 - (void)didSelectSuggestion:(FormSuggestion*)suggestion
                     atIndex:(NSInteger)index
-                      state:(const AutofillSuggestionState&)suggestionState {
+                      state:(const AutofillSuggestionState&)suggestionState
+                 completion:(ProceduralBlock)completion {
   id<FormSuggestionProvider> provider = suggestion.provider ?: _provider;
 
   // If a password related suggestion was selected, reset the credential bottom
@@ -574,6 +587,9 @@
                               suggestionState.frame_identifier)
         completionHandler:^{
           [[weakSelf formInputNavigator] closeKeyboardWithoutButtonPress];
+          if (completion) {
+            completion();
+          }
         }];
 }
 
diff --git a/ios/chrome/browser/autofill/model/form_suggestion_controller_unittest.mm b/ios/chrome/browser/autofill/model/form_suggestion_controller_unittest.mm
index b86c11e..78cb4ef6 100644
--- a/ios/chrome/browser/autofill/model/form_suggestion_controller_unittest.mm
+++ b/ios/chrome/browser/autofill/model/form_suggestion_controller_unittest.mm
@@ -810,7 +810,9 @@
                                                         params);
 
   // Selecting a suggestion should notify the delegate.
-  [suggestion_controller_ didSelectSuggestion:suggestions[0] atIndex:0];
+  [suggestion_controller_ didSelectSuggestion:suggestions[0]
+                                      atIndex:0
+                                   completion:nil];
   EXPECT_TRUE([provider selected]);
   EXPECT_NSEQ(@"form", [provider formName]);
   EXPECT_NSEQ(@"field_id", [provider fieldIdentifier]);
diff --git a/ios/chrome/browser/autofill/scan_save_and_fill/coordinator/payments_scan_save_and_fill_offer_bottom_sheet_mediator.mm b/ios/chrome/browser/autofill/scan_save_and_fill/coordinator/payments_scan_save_and_fill_offer_bottom_sheet_mediator.mm
index fb43e21..6f99249 100644
--- a/ios/chrome/browser/autofill/scan_save_and_fill/coordinator/payments_scan_save_and_fill_offer_bottom_sheet_mediator.mm
+++ b/ios/chrome/browser/autofill/scan_save_and_fill/coordinator/payments_scan_save_and_fill_offer_bottom_sheet_mediator.mm
@@ -52,7 +52,10 @@
                    requiresReauth:NO
        acceptanceA11yAnnouncement:nil];
 
-  [_provider didSelectSuggestion:suggestion atIndex:0 params:_params];
+  [_provider didSelectSuggestion:suggestion
+                         atIndex:0
+                          params:_params
+                      completion:nil];
 }
 
 - (void)disconnect {
diff --git a/ios/chrome/browser/autofill/scan_save_and_fill/coordinator/payments_scan_save_and_fill_offer_bottom_sheet_mediator_unittest.mm b/ios/chrome/browser/autofill/scan_save_and_fill/coordinator/payments_scan_save_and_fill_offer_bottom_sheet_mediator_unittest.mm
index 14a7cea..33ea3a8 100644
--- a/ios/chrome/browser/autofill/scan_save_and_fill/coordinator/payments_scan_save_and_fill_offer_bottom_sheet_mediator_unittest.mm
+++ b/ios/chrome/browser/autofill/scan_save_and_fill/coordinator/payments_scan_save_and_fill_offer_bottom_sheet_mediator_unittest.mm
@@ -76,7 +76,8 @@
                autofill::SuggestionType::kSaveAndFillCreditCardEntry;
       }]
                   atIndex:0
-                   params:dummy_params];
+                   params:dummy_params
+               completion:nil];
 
   [mediator_ didAcceptScanCardSuggestion];
 
diff --git a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm
index 80b5d59e..0210208 100644
--- a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm
@@ -332,7 +332,10 @@
                          andSetParams:_params
                              provider:autofillProvider];
   }
-  [crossProvider didSelectSuggestion:suggestion atIndex:index params:_params];
+  [crossProvider didSelectSuggestion:suggestion
+                             atIndex:index
+                              params:_params
+                          completion:nil];
 }
 
 - (void)disableBottomSheetAndRefocus:(BOOL)shouldRefocus {
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_injection_handler.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_injection_handler.mm
index 6c75ca9b..d3b7393 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_injection_handler.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_injection_handler.mm
@@ -226,10 +226,12 @@
                 andSetParams:std::nullopt
                     provider:[self providerForSuggestion:formSuggestion]];
     [self.formSuggestionClient didSelectSuggestion:decoratedSuggestion
-                                           atIndex:index];
+                                           atIndex:index
+                                        completion:nil];
   } else {
     [self.formSuggestionClient didSelectSuggestion:formSuggestion
-                                           atIndex:index];
+                                           atIndex:index
+                                        completion:nil];
   }
 }
 
diff --git a/ios/chrome/browser/bubble/ui_bundled/bubble_view.mm b/ios/chrome/browser/bubble/ui_bundled/bubble_view.mm
index 6f2603c..729f33d 100644
--- a/ios/chrome/browser/bubble/ui_bundled/bubble_view.mm
+++ b/ios/chrome/browser/bubble/ui_bundled/bubble_view.mm
@@ -267,9 +267,10 @@
         initWithImage:DefaultSymbolWithConfiguration(kCircleBadgeFill,
                                                      symbolConfiguration)];
     BOOL shouldBeHighlighted = i == page - 1;
-    circleImageView.tintColor = shouldBeHighlighted
-                                    ? [UIColor whiteColor]
-                                    : [UIColor colorWithWhite:1 alpha:0.45];
+    UIColor* baseCircleColor = [UIColor colorNamed:kSolidButtonTextColor];
+    circleImageView.tintColor =
+        shouldBeHighlighted ? baseCircleColor
+                            : [baseCircleColor colorWithAlphaComponent:0.45];
     [container addArrangedSubview:circleImageView];
   }
   return container;
diff --git a/ios/chrome/browser/composebox/coordinator/BUILD.gn b/ios/chrome/browser/composebox/coordinator/BUILD.gn
index e921561..eb05faf 100644
--- a/ios/chrome/browser/composebox/coordinator/BUILD.gn
+++ b/ios/chrome/browser/composebox/coordinator/BUILD.gn
@@ -91,6 +91,7 @@
     ":url_loading_deps",
     "//components/application_locale_storage",
     "//components/contextual_search:public",
+    "//components/contextual_tasks/public:query_contextualizer",
     "//components/favicon/ios",
     "//components/feature_engagement/public",
     "//components/prefs",
@@ -99,6 +100,7 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/autocomplete/model",
     "//ios/chrome/browser/cobrowse/model",
+    "//ios/chrome/browser/cobrowse/model:factory",
     "//ios/chrome/browser/composebox/debugger",
     "//ios/chrome/browser/composebox/model",
     "//ios/chrome/browser/composebox/public",
@@ -289,6 +291,7 @@
     "//components/contextual_search:test_support",
     "//components/contextual_search/internal:test_support",
     "//components/contextual_search/internal/ios",
+    "//components/contextual_tasks/public:query_contextualizer",
     "//components/omnibox/browser",
     "//components/omnibox/browser:test_support",
     "//components/search_engines",
@@ -296,6 +299,7 @@
     "//components/signin/public/identity_manager",
     "//components/variations",
     "//components/version_info:channel",
+    "//ios/chrome/browser/cobrowse/model:factory",
     "//ios/chrome/browser/composebox/public",
     "//ios/chrome/browser/composebox/ui",
     "//ios/chrome/browser/shared/model/profile/test",
diff --git a/ios/chrome/browser/composebox/coordinator/DEPS b/ios/chrome/browser/composebox/coordinator/DEPS
index 7ba41a7e..02ff01c 100644
--- a/ios/chrome/browser/composebox/coordinator/DEPS
+++ b/ios/chrome/browser/composebox/coordinator/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   # keep-sorted start
   "+components/contextual_search",
+  "+components/contextual_tasks/public",
   "+components/omnibox/composebox",
   "+components/open_from_clipboard",
   "+ios/chrome/browser/aim/model",
@@ -8,6 +9,7 @@
   "+ios/chrome/browser/bookmarks/model",
   "+ios/chrome/browser/cobrowse/model/cobrowse_browser_agent.h",
   "+ios/chrome/browser/cobrowse/model/cobrowse_context.h",
+  "+ios/chrome/browser/cobrowse/model/ios_contextual_tasks_service_factory.h",
   "+ios/chrome/browser/context_menu/ui_bundled",
   "+ios/chrome/browser/default_browser/model",
   "+ios/chrome/browser/drag_and_drop/model",
diff --git a/ios/chrome/browser/composebox/coordinator/composebox_input_plate_coordinator.mm b/ios/chrome/browser/composebox/coordinator/composebox_input_plate_coordinator.mm
index 4e289b9..42dc757d 100644
--- a/ios/chrome/browser/composebox/coordinator/composebox_input_plate_coordinator.mm
+++ b/ios/chrome/browser/composebox/coordinator/composebox_input_plate_coordinator.mm
@@ -216,6 +216,7 @@
                    templateURLService:templateURLService
                 aimEligibilityService:_aimEligibilityService
                           prefService:self.profile->GetPrefs()
+                              profile:self.profile
                  cobrowseBrowserAgent:CobrowseBrowserAgent::FromBrowser(
                                           self.browser)
             browserCoordinatorHandler:HandlerForProtocol(
diff --git a/ios/chrome/browser/composebox/coordinator/composebox_input_plate_mediator.h b/ios/chrome/browser/composebox/coordinator/composebox_input_plate_mediator.h
index 7d7e56b0e..d2f05ebd6 100644
--- a/ios/chrome/browser/composebox/coordinator/composebox_input_plate_mediator.h
+++ b/ios/chrome/browser/composebox/coordinator/composebox_input_plate_mediator.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #import "components/contextual_search/internal/ios/composebox_context_upload_observer_bridge.h"
+#import "components/contextual_tasks/public/query_contextualizer.h"
 #import "ios/chrome/browser/composebox/coordinator/composebox_entrypoint.h"
 #import "ios/chrome/browser/composebox/coordinator/composebox_mode_holder.h"
 #import "ios/chrome/browser/composebox/coordinator/composebox_omnibox_client_delegate.h"
@@ -32,6 +33,7 @@
 class FaviconLoader;
 class PersistTabContextBrowserAgent;
 class PrefService;
+class ProfileIOS;
 class TemplateURLService;
 class WebStateList;
 
@@ -85,6 +87,7 @@
               aimEligibilityService:
                   (AimEligibilityService*)aimEligibilityService
                         prefService:(PrefService*)prefService
+                            profile:(ProfileIOS*)profile
                cobrowseBrowserAgent:(CobrowseBrowserAgent*)cobrowseBrowserAgent
           browserCoordinatorHandler:
               (id<BrowserCoordinatorCommands>)browserCoordinatorHandler
diff --git a/ios/chrome/browser/composebox/coordinator/composebox_input_plate_mediator.mm b/ios/chrome/browser/composebox/coordinator/composebox_input_plate_mediator.mm
index 138778c..aeb9714f 100644
--- a/ios/chrome/browser/composebox/coordinator/composebox_input_plate_mediator.mm
+++ b/ios/chrome/browser/composebox/coordinator/composebox_input_plate_mediator.mm
@@ -36,6 +36,7 @@
 #import "components/contextual_search/input_state_model.h"
 #import "components/contextual_search/internal/ios/composebox_context_upload_observer_bridge.h"
 #import "components/contextual_search/internal/ios/composebox_query_controller_ios.h"
+#import "components/contextual_tasks/public/query_contextualizer.h"
 #import "components/lens/contextual_input.h"
 #import "components/lens/lens_bitmap_processing.h"
 #import "components/lens/lens_overlay_mime_type.h"
@@ -51,6 +52,7 @@
 #import "components/search_engines/util.h"
 #import "ios/chrome/browser/cobrowse/model/cobrowse_browser_agent.h"
 #import "ios/chrome/browser/cobrowse/model/cobrowse_context.h"
+#import "ios/chrome/browser/cobrowse/model/ios_contextual_tasks_service_factory.h"
 #import "ios/chrome/browser/composebox/coordinator/composebox_constants.h"
 #import "ios/chrome/browser/composebox/coordinator/composebox_url_loader.h"
 #import "ios/chrome/browser/composebox/debugger/composebox_debugger_logger.h"
@@ -68,6 +70,7 @@
 #import "ios/chrome/browser/lens/ui_bundled/lens_availability.h"
 #import "ios/chrome/browser/lens/ui_bundled/lens_entrypoint.h"
 #import "ios/chrome/browser/search_engines/model/search_engine_observer_bridge.h"
+#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/model/url/url_util.h"
 #import "ios/chrome/browser/shared/model/utils/mime_type_util.h"
 #import "ios/chrome/browser/shared/model/utils/web_state_deferred_executor.h"
@@ -281,8 +284,67 @@
     SearchEngineObserving,
     ComposeboxInputItemCollectionDelegate,
     WebStateDeferredExecutorDelegate>
+
+// Delegate methods for QueryContextualizer.
+- (GURL)GetTabUrl:(contextual_tasks::QueryContextualizer::TabId)id;
+- (SessionID)GetTabSessionId:(contextual_tasks::QueryContextualizer::TabId)id;
+- (void)GetPageContext:(contextual_tasks::QueryContextualizer::TabId)id
+              callback:
+                  (base::OnceCallback<void(
+                       std::unique_ptr<lens::ContextualInputData>)>)callback;
+- (bool)IsTabValid:(contextual_tasks::QueryContextualizer::TabId)id;
+- (std::optional<lens::ImageEncodingOptions>)
+    GetTabViewportEncodingOptionsForQueryContextualizer;
+- (contextual_search::ContextualSearchSessionHandle*)
+    GetOrCreateSessionHandleForQueryContextualizer;
+
 @end
 
+namespace {
+
+class QueryContextualizerDelegateBridge
+    : public contextual_tasks::QueryContextualizer::Delegate {
+ public:
+  explicit QueryContextualizerDelegateBridge(
+      ComposeboxInputPlateMediator* mediator)
+      : mediator_(mediator) {}
+
+  GURL GetTabUrl(contextual_tasks::QueryContextualizer::TabId id) override {
+    return [mediator_ GetTabUrl:id];
+  }
+
+  SessionID GetTabSessionId(
+      contextual_tasks::QueryContextualizer::TabId id) override {
+    return [mediator_ GetTabSessionId:id];
+  }
+
+  void GetPageContext(
+      contextual_tasks::QueryContextualizer::TabId id,
+      base::OnceCallback<void(std::unique_ptr<lens::ContextualInputData>)>
+          callback) override {
+    [mediator_ GetPageContext:id callback:std::move(callback)];
+  }
+
+  bool IsTabValid(contextual_tasks::QueryContextualizer::TabId id) override {
+    return [mediator_ IsTabValid:id];
+  }
+
+  std::optional<lens::ImageEncodingOptions>
+  GetTabViewportEncodingOptionsForQueryContextualizer() override {
+    return [mediator_ GetTabViewportEncodingOptionsForQueryContextualizer];
+  }
+
+  contextual_search::ContextualSearchSessionHandle*
+  GetOrCreateSessionHandleForQueryContextualizer() override {
+    return [mediator_ GetOrCreateSessionHandleForQueryContextualizer];
+  }
+
+ private:
+  __weak ComposeboxInputPlateMediator* mediator_;
+};
+
+}  // namespace
+
 @implementation ComposeboxInputPlateMediator {
   // The ordered list of items for display.
   ComposeboxInputItemCollection* _items;
@@ -308,6 +370,8 @@
   raw_ptr<AimEligibilityService> _aimEligibilityService;
   // The preference service.
   raw_ptr<PrefService> _prefService;
+  // The profile.
+  raw_ptr<ProfileIOS> _profile;
   // Browser agent to manage the cobrowse context.
   raw_ptr<CobrowseBrowserAgent> _cobrowseBrowserAgent;
 
@@ -318,6 +382,12 @@
                      base::UnguessableTokenHash>
       _latestTabSelectionMapping;
 
+  // Delegate for the query contextualizer.
+  std::unique_ptr<QueryContextualizerDelegateBridge>
+      _queryContextualizerDelegate;
+  // Orchestrator to contextualize tabs before search.
+  std::unique_ptr<contextual_tasks::QueryContextualizer> _queryContextualizer;
+
   // Utilitary to delay execution until the web state is loaded.
   WebStateDeferredExecutor* _webStateDeferredExecutor;
 
@@ -377,6 +447,7 @@
               aimEligibilityService:
                   (AimEligibilityService*)aimEligibilityService
                         prefService:(PrefService*)prefService
+                            profile:(ProfileIOS*)profile
                cobrowseBrowserAgent:(CobrowseBrowserAgent*)cobrowseBrowserAgent
           browserCoordinatorHandler:
               (id<BrowserCoordinatorCommands>)browserCoordinatorHandler
@@ -388,6 +459,7 @@
     _browserCoordinatorHandler = browserCoordinatorHandler;
     _sceneHandler = sceneHandler;
     _prefService = prefService;
+    _profile = profile;
     _cobrowseBrowserAgent = cobrowseBrowserAgent;
     _contextualSearchSession = std::move(contextualSearchSession);
     if (_contextualSearchSession) {
@@ -423,6 +495,16 @@
     _items = [[ComposeboxInputItemCollection alloc] init];
     _items.delegate = self;
 
+    contextual_tasks::ContextualTasksService* tasksService =
+        IOSContextualTasksServiceFactory::GetForProfile(_profile);
+    if (tasksService) {
+      _queryContextualizerDelegate =
+          std::make_unique<QueryContextualizerDelegateBridge>(self);
+      _queryContextualizer =
+          std::make_unique<contextual_tasks::QueryContextualizer>(
+              tasksService, _queryContextualizerDelegate.get());
+    }
+
     if (_entrypoint == ComposeboxEntrypoint::kCobrowse) {
       CHECK([self isEligibleToAIM])
           << "The Cobrowse entry point requires AIM eligibility. Accessing it "
@@ -446,6 +528,8 @@
   _aimEligibilityService = nullptr;
   _cobrowseBrowserAgent = nullptr;
   _inputStateModel = nullptr;
+  _queryContextualizer.reset();
+  _queryContextualizerDelegate.reset();
   _composeboxObserverBridge.reset();
   if (_contextualSearchSession) {
     if (!_inNavigation) {
@@ -459,6 +543,7 @@
   _URLLoader = nil;
   _consumer = nil;
   _prefService = nullptr;
+  _profile = nullptr;
   [[NSNotificationCenter defaultCenter] removeObserver:self];
 }
 
@@ -603,8 +688,25 @@
         [weakSelf didCreateSearchURL:URL];
       }));
 
-  _contextualSearchSession->CreateSearchUrl(std::move(search_url_request_info),
-                                            std::move(callback));
+  auto createSearchUrlCallback = base::BindOnce(
+      &contextual_search::ContextualSearchSessionHandle::CreateSearchUrl,
+      _contextualSearchSession->AsWeakPtr(), std::move(search_url_request_info),
+      std::move(callback));
+
+  if (_queryContextualizer) {
+    _queryContextualizer->Contextualize(
+        /*task_id=*/std::nullopt, base::SysNSStringToUTF8(text),
+        /*tabs_to_recontextualize=*/{}, /*tabs_to_force_contextualize=*/{},
+        /*on_ineligible_callback=*/base::DoNothing(),
+        /*on_processed_callback=*/base::DoNothing(),
+        base::BindOnce(
+            [](base::OnceClosure closure,
+               base::WeakPtr<contextual_search::ContextualSearchSessionHandle>
+                   ignored_handle) { std::move(closure).Run(); },
+            std::move(createSearchUrlCallback)));
+  } else {
+    std::move(createSearchUrlCallback).Run();
+  }
 }
 
 - (void)processTab:(web::WebState*)webState
@@ -2792,6 +2894,49 @@
   [self sendText:query];
 }
 
+#pragma mark - contextual_tasks::QueryContextualizer::Delegate
+
+- (GURL)GetTabUrl:(contextual_tasks::QueryContextualizer::TabId)id {
+  // No-op. Recontextualization is only needed for the contextual-tasks
+  // composebox handler.
+  return GURL();
+}
+
+- (SessionID)GetTabSessionId:(contextual_tasks::QueryContextualizer::TabId)id {
+  // No-op. Recontextualization is only needed for the contextual-tasks
+  // composebox handler.
+  return SessionID::InvalidValue();
+}
+
+- (void)GetPageContext:(contextual_tasks::QueryContextualizer::TabId)id
+              callback:
+                  (base::OnceCallback<void(
+                       std::unique_ptr<lens::ContextualInputData>)>)callback {
+  // No-op. Recontextualization is only needed for the contextual-tasks
+  // composebox handler.
+  if (callback) {
+    std::move(callback).Run(nullptr);
+  }
+}
+
+- (bool)IsTabValid:(contextual_tasks::QueryContextualizer::TabId)id {
+  // No-op. Recontextualization is only needed for the contextual-tasks
+  // composebox handler.
+  return false;
+}
+
+- (std::optional<lens::ImageEncodingOptions>)
+    GetTabViewportEncodingOptionsForQueryContextualizer {
+  // No-op. Recontextualization is only needed for the contextual-tasks
+  // composebox handler.
+  return std::nullopt;
+}
+
+- (contextual_search::ContextualSearchSessionHandle*)
+    GetOrCreateSessionHandleForQueryContextualizer {
+  return _contextualSearchSession.get();
+}
+
 #pragma mark - WebStateDeferredExecutorDelegate
 
 - (void)webStateDeferredExecutor:(WebStateDeferredExecutor*)executor
diff --git a/ios/chrome/browser/composebox/coordinator/composebox_input_plate_mediator_unittest.mm b/ios/chrome/browser/composebox/coordinator/composebox_input_plate_mediator_unittest.mm
index abcc2205..397b77b 100644
--- a/ios/chrome/browser/composebox/coordinator/composebox_input_plate_mediator_unittest.mm
+++ b/ios/chrome/browser/composebox/coordinator/composebox_input_plate_mediator_unittest.mm
@@ -205,6 +205,7 @@
                      templateURLService:template_url_service()
                   aimEligibilityService:aim_eligibility_service_.get()
                             prefService:&pref_service_
+                                profile:profile_.get()
                    cobrowseBrowserAgent:nil
               browserCoordinatorHandler:nil
                            sceneHandler:nil
@@ -657,6 +658,7 @@
                        templateURLService:template_url_service()
                     aimEligibilityService:aim_eligibility_service_.get()
                               prefService:&pref_service_
+                                  profile:profile_.get()
                      cobrowseBrowserAgent:nil
                 browserCoordinatorHandler:nil
                              sceneHandler:nil
diff --git a/ios/chrome/browser/fullscreen/coordinator/fullscreen_mediator.mm b/ios/chrome/browser/fullscreen/coordinator/fullscreen_mediator.mm
index 85f07b2..8ed49f8 100644
--- a/ios/chrome/browser/fullscreen/coordinator/fullscreen_mediator.mm
+++ b/ios/chrome/browser/fullscreen/coordinator/fullscreen_mediator.mm
@@ -232,23 +232,34 @@
 
 #pragma mark - CRWWebViewScrollViewProxyObserver
 
-- (void)webViewScrollViewDidScroll:
-    (CRWWebViewScrollViewProxy*)webViewScrollViewProxy {
+- (void)webViewScrollViewDidScroll:(CRWWebViewScrollViewProxy*)scrollView {
   // Ignore programmatic scrolls (e.g. from inset updates). Only process scroll
   // events that are actively driven by the user's touch or residual momentum.
-  if (!webViewScrollViewProxy.isDragging &&
-      !webViewScrollViewProxy.isDecelerating) {
+  if (!scrollView.isDragging && !scrollView.isDecelerating) {
     return;
   }
 
-  CGFloat currentContentOffset = webViewScrollViewProxy.contentOffset.y;
-  CGFloat delta = currentContentOffset - _lastContentOffset;
-  _lastContentOffset = currentContentOffset;
+  CGFloat contentOffset = scrollView.contentOffset.y;
+  CGFloat delta = contentOffset - _lastContentOffset;
+  _lastContentOffset = contentOffset;
 
   if (_updatingInsets) {
     return;
   }
 
+  // Check if content is scrolled past the top.
+  CGFloat topInsetRemaining =
+      _browserAgent->max_insets().top - _browserAgent->insets().top;
+  if (contentOffset + topInsetRemaining <= -scrollView.contentInset.top) {
+    return;
+  }
+  // Check if content is scrolled past the bottom.
+  CGFloat scrollViewHeight = CGRectGetHeight(scrollView.frame);
+  CGFloat contentHeight = scrollView.contentSize.height;
+  if (contentOffset + scrollViewHeight > contentHeight) {
+    return;
+  }
+
   if (delta != 0) {
     // If the direction changed, reset the _scrollTotal.
     if ((delta > 0 && _scrollTotal < 0) || (delta < 0 && _scrollTotal > 0)) {
diff --git a/ios/chrome/browser/intelligence/actor/model/BUILD.gn b/ios/chrome/browser/intelligence/actor/model/BUILD.gn
index d156474..e9922bef 100644
--- a/ios/chrome/browser/intelligence/actor/model/BUILD.gn
+++ b/ios/chrome/browser/intelligence/actor/model/BUILD.gn
@@ -19,7 +19,7 @@
     "//base",
     "//components/keyed_service/core",
     "//components/optimization_guide/proto:optimization_guide_proto",
-    "//ios/chrome/browser/intelligence/actor/public:actor_task_ui_delegate",
+    "//ios/chrome/browser/intelligence/actor/public:actor_task_ui_protocols",
     "//ios/chrome/browser/intelligence/actor/public:actor_types",
     "//ios/chrome/browser/intelligence/actor/tools/model:actor_interface",
     "//ios/chrome/browser/intelligence/actor/tools/model:actor_tool_error",
diff --git a/ios/chrome/browser/intelligence/actor/model/actor_service.h b/ios/chrome/browser/intelligence/actor/model/actor_service.h
index 3635bda..f447110 100644
--- a/ios/chrome/browser/intelligence/actor/model/actor_service.h
+++ b/ios/chrome/browser/intelligence/actor/model/actor_service.h
@@ -19,8 +19,6 @@
 
 class ProfileIOS;
 
-@protocol ActorTaskUIDelegate;
-
 namespace actor {
 
 class ActorTask;
@@ -48,7 +46,6 @@
 
   // Creates a new task.
   ActorTaskId CreateTask(const std::string& title,
-                         id<ActorTaskUIDelegate> delegate,
                          bool allow_incognito_web_states);
 
   // Submits tools to an active task with a task update string (a short blurb
@@ -84,6 +81,9 @@
   // Map of active tasks, keyed by their task ID.
   std::map<ActorTaskId, std::unique_ptr<ActorTask>> active_tasks_;
 
+  // Generator for unique task IDs.
+  ActorTaskId::Generator next_task_id_;
+
   // Weak pointer factory.
   base::WeakPtrFactory<ActorService> weak_ptr_factory_{this};
 };
diff --git a/ios/chrome/browser/intelligence/actor/model/actor_service.mm b/ios/chrome/browser/intelligence/actor/model/actor_service.mm
index 9b5e214c..ccc42e9 100644
--- a/ios/chrome/browser/intelligence/actor/model/actor_service.mm
+++ b/ios/chrome/browser/intelligence/actor/model/actor_service.mm
@@ -12,7 +12,6 @@
 #import "components/optimization_guide/proto/features/actions_data.pb.h"
 #import "ios/chrome/browser/intelligence/actor/model/actor_task.h"
 #import "ios/chrome/browser/intelligence/actor/model/aggregated_journal.h"
-#import "ios/chrome/browser/intelligence/actor/public/actor_task_ui_delegate.h"
 #import "ios/chrome/browser/intelligence/actor/tools/model/actor_tool.h"
 #import "ios/chrome/browser/intelligence/actor/tools/model/actor_tool_error.h"
 #import "ios/chrome/browser/intelligence/actor/tools/model/actor_tool_factory.h"
@@ -116,14 +115,9 @@
 }
 
 ActorTaskId ActorService::CreateTask(const std::string& title,
-                                     id<ActorTaskUIDelegate> delegate,
                                      bool allow_incognito_web_states) {
-  ActorTaskId task_id(base::Token::CreateRandom());
-  while (active_tasks_.find(task_id) != active_tasks_.end()) {
-    task_id = ActorTaskId(base::Token::CreateRandom());
-  }
-  active_tasks_[task_id] =
-      std::make_unique<ActorTask>(task_id, title, delegate);
+  const ActorTaskId task_id = next_task_id_.GenerateNextId();
+  active_tasks_[task_id] = std::make_unique<ActorTask>(task_id, title);
   return task_id;
 }
 
diff --git a/ios/chrome/browser/intelligence/actor/model/actor_service_unittest.mm b/ios/chrome/browser/intelligence/actor/model/actor_service_unittest.mm
index 6851fb4..d30cd0f 100644
--- a/ios/chrome/browser/intelligence/actor/model/actor_service_unittest.mm
+++ b/ios/chrome/browser/intelligence/actor/model/actor_service_unittest.mm
@@ -57,9 +57,9 @@
   std::set<ActorTaskId> task_ids;
   for (int i = 0; i < 100; ++i) {
     ActorTaskId task_id =
-        service->CreateTask("Test Task", /*delegate=*/nil,
+        service->CreateTask("Test Task",
                             /*allow_incognito_web_states=*/false);
-    EXPECT_FALSE(task_id.value().is_zero());
+    EXPECT_FALSE(task_id.is_null());
     EXPECT_TRUE(task_ids.insert(task_id).second);
   }
 }
diff --git a/ios/chrome/browser/intelligence/actor/model/actor_task.h b/ios/chrome/browser/intelligence/actor/model/actor_task.h
index e8a477f..f879942 100644
--- a/ios/chrome/browser/intelligence/actor/model/actor_task.h
+++ b/ios/chrome/browser/intelligence/actor/model/actor_task.h
@@ -13,8 +13,6 @@
 #import "base/memory/weak_ptr.h"
 #import "ios/chrome/browser/intelligence/actor/public/actor_types.h"
 
-@protocol ActorTaskUIDelegate;
-
 namespace web {
 class WebState;
 }
@@ -29,31 +27,7 @@
 // sequentially.
 class ActorTask {
  public:
-  // Represents the high-level orchestration state of the task.
-  enum class State {
-    // Task is initialized but has not started executing tools.
-    kInit = 0,
-    // Task is actively executing through its engine.
-    kActing = 1,
-    // Task is waiting for AI provider to reflect on next actions to execute.
-    kReflecting = 2,
-    // Task execution was paused by the actor.
-    kPausedByActor = 3,
-    // Task execution was paused by the user.
-    kPausedByUser = 4,
-    // Task execution was cancelled or aborted.
-    kCancelled = 5,
-    // Task successfully completed.
-    kFinished = 6,
-    // Task is currently waiting for input or confirmation from the user.
-    kWaitingOnUser = 7,
-    // Task execution encountered a terminal failure.
-    kFailed = 8
-  };
-
-  ActorTask(ActorTaskId task_id,
-            const std::string& title,
-            id<ActorTaskUIDelegate> delegate);
+  ActorTask(ActorTaskId task_id, const std::string& title);
   ~ActorTask();
 
   ActorTask(const ActorTask&) = delete;
@@ -63,10 +37,9 @@
   // in ActorTask, this is to fix compilation.
   ActorTaskId task_id() const { return task_id_; }
   const std::string& title() const { return title_; }
-  id<ActorTaskUIDelegate> delegate() const { return delegate_; }
 
   // Returns the current execution state of the task.
-  State GetState() const;
+  ActorTaskState GetState() const;
 
   // Begins executing the given sequence of tools on the underlying execution
   // engine with a string update blurb in plain language about what the actor is
@@ -92,7 +65,7 @@
   friend class ActorTaskTest;
 
   // The task state.
-  State state_ = State::kInit;
+  ActorTaskState state_ = ActorTaskState::kInit;
 
   // The task's ID.
   const ActorTaskId task_id_;
@@ -100,8 +73,6 @@
   // The task's title.
   const std::string title_;
 
-  // The task's UI delegate.
-  __weak id<ActorTaskUIDelegate> delegate_;
 
   // The execution engine for this task.
   std::unique_ptr<ActorEngine> engine_;
diff --git a/ios/chrome/browser/intelligence/actor/model/actor_task.mm b/ios/chrome/browser/intelligence/actor/model/actor_task.mm
index 5cc8f83..cae29cb 100644
--- a/ios/chrome/browser/intelligence/actor/model/actor_task.mm
+++ b/ios/chrome/browser/intelligence/actor/model/actor_task.mm
@@ -10,16 +10,14 @@
 
 namespace actor {
 
-ActorTask::ActorTask(ActorTaskId task_id,
-                     const std::string& title,
-                     id<ActorTaskUIDelegate> delegate)
-    : task_id_(task_id), title_(title), delegate_(delegate) {
+ActorTask::ActorTask(ActorTaskId task_id, const std::string& title)
+    : task_id_(task_id), title_(title) {
   engine_ = std::make_unique<ActorEngine>();
 }
 
 ActorTask::~ActorTask() = default;
 
-ActorTask::State ActorTask::GetState() const {
+ActorTaskState ActorTask::GetState() const {
   return state_;
 }
 
diff --git a/ios/chrome/browser/intelligence/actor/model/actor_task_unittest.mm b/ios/chrome/browser/intelligence/actor/model/actor_task_unittest.mm
index 2e38ecc4..a7fc9277 100644
--- a/ios/chrome/browser/intelligence/actor/model/actor_task_unittest.mm
+++ b/ios/chrome/browser/intelligence/actor/model/actor_task_unittest.mm
@@ -17,8 +17,7 @@
  protected:
   void SetUp() override {
     PlatformTest::SetUp();
-    task_ = std::make_unique<ActorTask>(
-        ActorTaskId(base::Token::CreateRandom()), "Test Task", nil);
+    task_ = std::make_unique<ActorTask>(ActorTaskId(1), "Test Task");
   }
 
   void TearDown() override {
diff --git a/ios/chrome/browser/intelligence/actor/model/aggregated_journal.mm b/ios/chrome/browser/intelligence/actor/model/aggregated_journal.mm
index 33b7a32..2912449f 100644
--- a/ios/chrome/browser/intelligence/actor/model/aggregated_journal.mm
+++ b/ios/chrome/browser/intelligence/actor/model/aggregated_journal.mm
@@ -157,7 +157,7 @@
         dict.Set("type", "Instant");
         break;
     }
-    dict.Set("task_id", entry.task_id.value().ToString());
+    dict.Set("task_id", base::NumberToString(entry.task_id.value()));
     dict.Set("event", entry.event);
     dict.Set("timestamp", entry.timestamp.InSecondsFSinceUnixEpoch());
     dict.Set("track_uuid", base::NumberToString(entry.track_uuid));
diff --git a/ios/chrome/browser/intelligence/actor/model/aggregated_journal_unittest.mm b/ios/chrome/browser/intelligence/actor/model/aggregated_journal_unittest.mm
index 9ecca39..0ed3b5282 100644
--- a/ios/chrome/browser/intelligence/actor/model/aggregated_journal_unittest.mm
+++ b/ios/chrome/browser/intelligence/actor/model/aggregated_journal_unittest.mm
@@ -22,7 +22,7 @@
 
 TEST_F(AggregatedJournalTest, LogInstant) {
   AggregatedJournal journal;
-  ActorTaskId task_id(base::Token::CreateRandom());
+  ActorTaskId task_id(1);
   journal.Log(GURL("https://example.com"), task_id, "TestEvent",
               {{"key", "value"}});
 
@@ -38,7 +38,7 @@
 
 TEST_F(AggregatedJournalTest, GetLogsAsJson) {
   AggregatedJournal journal;
-  ActorTaskId task_id(base::Token::CreateRandom());
+  ActorTaskId task_id(1);
   journal.Log(GURL("https://example.com"), task_id, "TestEvent",
               {{"key", "value"}});
 
@@ -52,7 +52,7 @@
 
 TEST_F(AggregatedJournalTest, AsyncEntry) {
   AggregatedJournal journal;
-  ActorTaskId task_id(base::Token::CreateRandom());
+  ActorTaskId task_id(1);
   {
     auto pending =
         journal.CreatePendingAsyncEntry(GURL("https://example.com"), task_id,
@@ -77,7 +77,7 @@
 
 TEST_F(AggregatedJournalTest, RingBuffer) {
   AggregatedJournal journal;
-  ActorTaskId task_id(base::Token::CreateRandom());
+  ActorTaskId task_id(1);
   for (int i = 0; i < 30; ++i) {
     journal.Log(GURL(), task_id, "Event " + base::NumberToString(i), {});
   }
diff --git a/ios/chrome/browser/intelligence/actor/public/BUILD.gn b/ios/chrome/browser/intelligence/actor/public/BUILD.gn
index 7f2feb8..7fd9444d 100644
--- a/ios/chrome/browser/intelligence/actor/public/BUILD.gn
+++ b/ios/chrome/browser/intelligence/actor/public/BUILD.gn
@@ -7,6 +7,18 @@
   deps = [ "//base" ]
 }
 
-source_set("actor_task_ui_delegate") {
-  sources = [ "actor_task_ui_delegate.h" ]
+source_set("actor_task_ui_protocols") {
+  sources = [
+    "actor_task_intervention_delegate.h",
+    "actor_task_updates_observer.h",
+  ]
+  deps = [
+    ":actor_types",
+    "//ios/web/public",
+  ]
+}
+
+source_set("actor_task_controller") {
+  sources = [ "actor_task_controller.h" ]
+  deps = [ ":actor_types" ]
 }
diff --git a/ios/chrome/browser/intelligence/actor/public/actor_task_controller.h b/ios/chrome/browser/intelligence/actor/public/actor_task_controller.h
new file mode 100644
index 0000000..6b50701
--- /dev/null
+++ b/ios/chrome/browser/intelligence/actor/public/actor_task_controller.h
@@ -0,0 +1,30 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_INTELLIGENCE_ACTOR_PUBLIC_ACTOR_TASK_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_INTELLIGENCE_ACTOR_PUBLIC_ACTOR_TASK_CONTROLLER_H_
+
+#import <Foundation/Foundation.h>
+
+#import "ios/chrome/browser/intelligence/actor/public/actor_types.h"
+
+// The handle the Actor layer provides to the UI for simple ActorTask lifecycle
+// management.
+@protocol ActorTaskController <NSObject>
+
+// TODO(crbug.com/501043031): Remove @optional when API stabilizes.
+@optional
+
+// Pause the active task.
+- (void)pauseTask;
+
+// Resume a paused task.
+- (void)resumeTask;
+
+// Stop the task completely with a reason.
+- (void)stopTaskWithReason:(actor::ActorTaskStoppedReason)reason;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_INTELLIGENCE_ACTOR_PUBLIC_ACTOR_TASK_CONTROLLER_H_
diff --git a/ios/chrome/browser/intelligence/actor/public/actor_task_intervention_delegate.h b/ios/chrome/browser/intelligence/actor/public/actor_task_intervention_delegate.h
new file mode 100644
index 0000000..42d4d6d
--- /dev/null
+++ b/ios/chrome/browser/intelligence/actor/public/actor_task_intervention_delegate.h
@@ -0,0 +1,20 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_INTELLIGENCE_ACTOR_PUBLIC_ACTOR_TASK_INTERVENTION_DELEGATE_H_
+#define IOS_CHROME_BROWSER_INTELLIGENCE_ACTOR_PUBLIC_ACTOR_TASK_INTERVENTION_DELEGATE_H_
+
+#import <Foundation/Foundation.h>
+
+// The ActorTask intervention delegate protocol (1-to-1). Used for blocking
+// interventions (likely user facing prompts, but can be programmatic in
+// headless Actor mode).
+@protocol ActorTaskInterventionDelegate <NSObject>
+
+// TODO(crbug.com/501043031): Remove @optional when API stabilizes.
+@optional
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_INTELLIGENCE_ACTOR_PUBLIC_ACTOR_TASK_INTERVENTION_DELEGATE_H_
diff --git a/ios/chrome/browser/intelligence/actor/public/actor_task_ui_delegate.h b/ios/chrome/browser/intelligence/actor/public/actor_task_ui_delegate.h
deleted file mode 100644
index aa0eeb0..0000000
--- a/ios/chrome/browser/intelligence/actor/public/actor_task_ui_delegate.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2026 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_INTELLIGENCE_ACTOR_PUBLIC_ACTOR_TASK_UI_DELEGATE_H_
-#define IOS_CHROME_BROWSER_INTELLIGENCE_ACTOR_PUBLIC_ACTOR_TASK_UI_DELEGATE_H_
-
-#import <Foundation/Foundation.h>
-
-// UI fulfillment protocol for handling task updates and blocking prompts.
-// TODO(crbug.com/496163970): Add methods.
-@protocol ActorTaskUIDelegate <NSObject>
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_INTELLIGENCE_ACTOR_PUBLIC_ACTOR_TASK_UI_DELEGATE_H_
diff --git a/ios/chrome/browser/intelligence/actor/public/actor_task_updates_observer.h b/ios/chrome/browser/intelligence/actor/public/actor_task_updates_observer.h
new file mode 100644
index 0000000..4f435ff5
--- /dev/null
+++ b/ios/chrome/browser/intelligence/actor/public/actor_task_updates_observer.h
@@ -0,0 +1,52 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_INTELLIGENCE_ACTOR_PUBLIC_ACTOR_TASK_UPDATES_OBSERVER_H_
+#define IOS_CHROME_BROWSER_INTELLIGENCE_ACTOR_PUBLIC_ACTOR_TASK_UPDATES_OBSERVER_H_
+
+#import <Foundation/Foundation.h>
+
+#import "ios/chrome/browser/intelligence/actor/public/actor_types.h"
+
+namespace web {
+class WebStateID;
+}
+
+// The ActorTask updates observer protocol (1-to-N). Used for passive
+// state broadcasts.
+@protocol ActorTaskUpdatesObserver <NSObject>
+
+// TODO(crbug.com/501043031): Remove @optional when API stabilizes.
+@optional
+
+// Fired immediately after the UI successfully registers as an observer.
+// Provides the initial status so the UI knows what to draw right away.
+- (void)didRegisterAsObserverForTaskID:(actor::ActorTaskId)taskID
+                             taskTitle:(NSString*)taskTitle
+                            taskUpdate:(NSString*)taskUpdate
+                          currentState:(actor::ActorTaskState)state
+                             webStates:(NSArray<NSNumber*>*)webStatesIDs;
+
+// Called when a WebState is added to an active task.
+- (void)actorTaskWithID:(actor::ActorTaskId)taskID
+         didAddWebState:(web::WebStateID)webStateID;
+
+// Called when an existing task's state changes.
+- (void)actorTaskWithID:(actor::ActorTaskId)taskID
+         didChangeState:(actor::ActorTaskState)newState
+              fromState:(actor::ActorTaskState)oldState;
+
+// Called just before a tool is about to be executed.
+- (void)actorTaskWithID:(actor::ActorTaskId)taskID
+        willExecuteTool:(NSString*)toolString
+             taskUpdate:(NSString*)taskUpdate
+             onWebState:(web::WebStateID)webStateID;
+
+// Called when a task stops.
+- (void)actorTaskDidStopWithID:(actor::ActorTaskId)taskID
+                    finalState:(actor::ActorTaskState)finalState;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_INTELLIGENCE_ACTOR_PUBLIC_ACTOR_TASK_UPDATES_OBSERVER_H_
diff --git a/ios/chrome/browser/intelligence/actor/public/actor_types.h b/ios/chrome/browser/intelligence/actor/public/actor_types.h
index acab1ce..24c4446 100644
--- a/ios/chrome/browser/intelligence/actor/public/actor_types.h
+++ b/ios/chrome/browser/intelligence/actor/public/actor_types.h
@@ -6,13 +6,35 @@
 #define IOS_CHROME_BROWSER_INTELLIGENCE_ACTOR_PUBLIC_ACTOR_TYPES_H_
 
 #import "base/functional/callback_forward.h"
-#import "base/token.h"
-#import "base/types/strong_alias.h"
+#import "base/types/id_type.h"
 
 namespace actor {
 
-// Strongly typed, performant unique token representing an ActorTask.
-using ActorTaskId = base::StrongAlias<class ActorTaskIdMarker, base::Token>;
+// Strongly typed, performant unique ID representing an ActorTask.
+using ActorTaskId = base::IdType32<class ActorTaskIdMarker>;
+static_assert(ActorTaskId(0).is_null(), "0 must be a null ActorTaskId");
+
+// Represents the high-level orchestration state of an ActorTask.
+enum class ActorTaskState {
+  // Task is initialized but has not started executing tools.
+  kInit = 0,
+  // Task is actively executing through its engine.
+  kActing = 1,
+  // Task is waiting for AI provider to reflect on next actions to execute.
+  kReflecting = 2,
+  // Task execution was paused by the actor.
+  kPausedByActor = 3,
+  // Task execution was paused by the user.
+  kPausedByUser = 4,
+  // Task execution was cancelled or aborted.
+  kCancelled = 5,
+  // Task successfully completed.
+  kFinished = 6,
+  // Task is currently waiting for input or confirmation from the user.
+  kWaitingOnUser = 7,
+  // Task execution encountered a terminal failure.
+  kFailed = 8
+};
 
 // Reasons why an ActorTask was stopped.
 enum class ActorTaskStoppedReason {
diff --git a/ios/chrome/browser/intelligence/bwg/coordinator/BUILD.gn b/ios/chrome/browser/intelligence/bwg/coordinator/BUILD.gn
index 82a1566..ba937111 100644
--- a/ios/chrome/browser/intelligence/bwg/coordinator/BUILD.gn
+++ b/ios/chrome/browser/intelligence/bwg/coordinator/BUILD.gn
@@ -61,6 +61,7 @@
     "//base/test:test_support",
     "//components/feature_engagement/public",
     "//components/feature_engagement/test:test_support",
+    "//components/prefs",
     "//components/prefs:test_support",
     "//components/sync_preferences:test_support",
     "//ios/chrome/app:app_internal",
diff --git a/ios/chrome/browser/intelligence/bwg/model/BUILD.gn b/ios/chrome/browser/intelligence/bwg/model/BUILD.gn
index 5f201171..6632d19 100644
--- a/ios/chrome/browser/intelligence/bwg/model/BUILD.gn
+++ b/ios/chrome/browser/intelligence/bwg/model/BUILD.gn
@@ -9,6 +9,9 @@
     "bwg_link_opening_handler.mm",
     "bwg_session_handler.h",
     "bwg_session_handler.mm",
+    "gemini_actuation_delegate.h",
+    "gemini_actuation_handler.h",
+    "gemini_actuation_handler.mm",
     "gemini_camera_delegate.h",
     "gemini_camera_handler.h",
     "gemini_camera_handler.mm",
@@ -26,6 +29,8 @@
   deps = [
     ":tab_helper",
     "//ios/chrome/app/strings:ios_strings",
+    "//ios/chrome/browser/intelligence/actor/model",
+    "//ios/chrome/browser/intelligence/actor/public:actor_types",
     "//ios/chrome/browser/intelligence/bwg/metrics",
     "//ios/chrome/browser/intelligence/bwg/utils:constants",
     "//ios/chrome/browser/intelligence/features",
@@ -62,6 +67,7 @@
     "//components/prefs",
     "//ios/chrome/browser/fullscreen/ui_bundled",
     "//ios/chrome/browser/fullscreen/ui_bundled:ui",
+    "//ios/chrome/browser/intelligence/actor/model",
     "//ios/chrome/browser/intelligence/bwg/metrics",
     "//ios/chrome/browser/intelligence/bwg/utils:constants",
     "//ios/chrome/browser/intelligence/bwg/utils:prefs",
@@ -216,6 +222,7 @@
     "bwg_service_impl_unittest.mm",
     "bwg_session_handler_unittest.mm",
     "bwg_tab_helper_unittest.mm",
+    "gemini_actuation_handler_unittest.mm",
     "gemini_browser_agent_unittest.mm",
     "gemini_camera_handler_unittest.mm",
   ]
@@ -241,6 +248,7 @@
     "//components/unified_consent",
     "//ios/chrome/browser/favicon/model",
     "//ios/chrome/browser/feature_engagement/model",
+    "//ios/chrome/browser/intelligence/actor/model",
     "//ios/chrome/browser/intelligence/bwg/metrics",
     "//ios/chrome/browser/intelligence/bwg/metrics:unit_tests",
     "//ios/chrome/browser/intelligence/bwg/utils:constants",
diff --git a/ios/chrome/browser/intelligence/bwg/model/gemini_actuation_delegate.h b/ios/chrome/browser/intelligence/bwg/model/gemini_actuation_delegate.h
new file mode 100644
index 0000000..5dbbf90
--- /dev/null
+++ b/ios/chrome/browser/intelligence/bwg/model/gemini_actuation_delegate.h
@@ -0,0 +1,50 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_INTELLIGENCE_BWG_MODEL_GEMINI_ACTUATION_DELEGATE_H_
+#define IOS_CHROME_BROWSER_INTELLIGENCE_BWG_MODEL_GEMINI_ACTUATION_DELEGATE_H_
+
+#import <Foundation/Foundation.h>
+
+#import <vector>
+
+#import "ios/chrome/browser/intelligence/actor/public/actor_types.h"
+
+@protocol ActorTaskUpdatesObserver;
+@protocol ActorTaskInterventionDelegate;
+
+// Protocol for the Gemini actor delegate. This protocol is implemented by a
+// Gemini actuation handler passed downstream to enable it to communicate with
+// the upstream ActorService.
+@protocol GeminiActuationDelegate <NSObject>
+
+// Creates a new task with the given title.
+- (actor::ActorTaskId)createTaskWithTitle:(NSString*)title;
+
+// Asynchronously register an updates observer (1-to-N).
+- (void)addTaskUpdatesObserver:(id<ActorTaskUpdatesObserver>)observer
+                     forTaskID:(actor::ActorTaskId)taskID;
+
+// Asynchronously register an intervention delegate (1-to-1).
+- (void)setTaskInterventionDelegate:(id<ActorTaskInterventionDelegate>)delegate
+                          forTaskID:(actor::ActorTaskId)taskID;
+
+// Request to perform actions.
+- (void)performActionsWithTaskID:(actor::ActorTaskId)taskID
+                      taskUpdate:(NSString*)taskUpdate
+          serializedActionProtos:(NSArray<NSData*>*)serializedActionProtos
+                      completion:
+                          (void (^)(BOOL success,
+                                    std::vector<bool> results))completionBlock;
+
+// Request to pause the task.
+- (void)pauseTaskWithID:(actor::ActorTaskId)taskID;
+
+// Request to stop the task.
+- (void)stopTaskWithID:(actor::ActorTaskId)taskID
+                reason:(actor::ActorTaskStoppedReason)reason;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_INTELLIGENCE_BWG_MODEL_GEMINI_ACTUATION_DELEGATE_H_
diff --git a/ios/chrome/browser/intelligence/bwg/model/gemini_actuation_handler.h b/ios/chrome/browser/intelligence/bwg/model/gemini_actuation_handler.h
new file mode 100644
index 0000000..ad7558f
--- /dev/null
+++ b/ios/chrome/browser/intelligence/bwg/model/gemini_actuation_handler.h
@@ -0,0 +1,26 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_INTELLIGENCE_BWG_MODEL_GEMINI_ACTUATION_HANDLER_H_
+#define IOS_CHROME_BROWSER_INTELLIGENCE_BWG_MODEL_GEMINI_ACTUATION_HANDLER_H_
+
+#import <Foundation/Foundation.h>
+
+#import "ios/chrome/browser/intelligence/bwg/model/gemini_actuation_delegate.h"
+
+namespace actor {
+class ActorService;
+}
+
+// The handler for Gemini actuations, bridging to the Actor orchestration layer.
+@interface GeminiActuationHandler : NSObject <GeminiActuationDelegate>
+
+// Initialize the handler with the ActorService.
+- (instancetype)initWithActorService:(actor::ActorService*)actorService
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_INTELLIGENCE_BWG_MODEL_GEMINI_ACTUATION_HANDLER_H_
diff --git a/ios/chrome/browser/intelligence/bwg/model/gemini_actuation_handler.mm b/ios/chrome/browser/intelligence/bwg/model/gemini_actuation_handler.mm
new file mode 100644
index 0000000..677022b
--- /dev/null
+++ b/ios/chrome/browser/intelligence/bwg/model/gemini_actuation_handler.mm
@@ -0,0 +1,60 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/intelligence/bwg/model/gemini_actuation_handler.h"
+
+#import <vector>
+
+#import "base/memory/raw_ptr.h"
+#import "ios/chrome/browser/intelligence/actor/model/actor_service.h"
+
+@implementation GeminiActuationHandler {
+  // The ActorService to use for actuating tasks.
+  raw_ptr<actor::ActorService> _actorService;
+}
+
+- (instancetype)initWithActorService:(actor::ActorService*)actorService {
+  self = [super init];
+  if (self) {
+    _actorService = actorService;
+  }
+  return self;
+}
+
+#pragma mark - GeminiActuationDelegate
+
+- (actor::ActorTaskId)createTaskWithTitle:(NSString*)title {
+  // TODO(crbug.com/496163970): Implement and test.
+  return actor::ActorTaskId();
+}
+
+- (void)addTaskUpdatesObserver:(id<ActorTaskUpdatesObserver>)observer
+                     forTaskID:(actor::ActorTaskId)taskID {
+  // TODO(crbug.com/496163970): Implement and test.
+}
+
+- (void)setTaskInterventionDelegate:(id<ActorTaskInterventionDelegate>)delegate
+                          forTaskID:(actor::ActorTaskId)taskID {
+  // TODO(crbug.com/496163970): Implement and test.
+}
+
+- (void)performActionsWithTaskID:(actor::ActorTaskId)taskID
+                      taskUpdate:(NSString*)taskUpdate
+          serializedActionProtos:(NSArray<NSData*>*)serializedActionProtos
+                      completion:
+                          (void (^)(BOOL success,
+                                    std::vector<bool> results))completionBlock {
+  // TODO(crbug.com/496163970): Implement and test.
+}
+
+- (void)pauseTaskWithID:(actor::ActorTaskId)taskID {
+  // TODO(crbug.com/496163970): Implement and test.
+}
+
+- (void)stopTaskWithID:(actor::ActorTaskId)taskID
+                reason:(actor::ActorTaskStoppedReason)reason {
+  // TODO(crbug.com/496163970): Implement and test.
+}
+
+@end
diff --git a/ios/chrome/browser/intelligence/bwg/model/gemini_actuation_handler_unittest.mm b/ios/chrome/browser/intelligence/bwg/model/gemini_actuation_handler_unittest.mm
new file mode 100644
index 0000000..93fe934
--- /dev/null
+++ b/ios/chrome/browser/intelligence/bwg/model/gemini_actuation_handler_unittest.mm
@@ -0,0 +1,41 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/intelligence/bwg/model/gemini_actuation_handler.h"
+
+#import "base/test/scoped_feature_list.h"
+#import "base/test/task_environment.h"
+#import "ios/chrome/browser/intelligence/actor/model/actor_service.h"
+#import "ios/chrome/browser/intelligence/actor/model/actor_service_factory.h"
+#import "ios/chrome/browser/intelligence/features/features.h"
+#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
+#import "testing/gtest/include/gtest/gtest.h"
+#import "testing/platform_test.h"
+
+namespace {
+
+class GeminiActuationHandlerTest : public PlatformTest {
+ public:
+  GeminiActuationHandlerTest() {
+    scoped_feature_list_.InitAndEnableFeature(kActorTools);
+    profile_ = TestProfileIOS::Builder().Build();
+    actor_service_ = actor::ActorServiceFactory::GetForProfile(profile_.get());
+  }
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+  std::unique_ptr<TestProfileIOS> profile_;
+  raw_ptr<actor::ActorService> actor_service_;
+};
+
+// Tests that a GeminiActuationHandler can be initialized.
+TEST_F(GeminiActuationHandlerTest, Initialization) {
+  ASSERT_NE(nullptr, actor_service_);
+  GeminiActuationHandler* handler =
+      [[GeminiActuationHandler alloc] initWithActorService:actor_service_];
+  EXPECT_NE(nil, handler);
+}
+
+}  // namespace
diff --git a/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent.mm b/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent.mm
index 4d45e75..00cd3b5 100644
--- a/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent.mm
+++ b/ios/chrome/browser/intelligence/bwg/model/gemini_browser_agent.mm
@@ -21,6 +21,7 @@
 #import "ios/chrome/browser/fullscreen/ui_bundled/fullscreen_controller.h"
 #import "ios/chrome/browser/fullscreen/ui_bundled/fullscreen_controller_observer.h"
 #import "ios/chrome/browser/fullscreen/ui_bundled/scoped_fullscreen_disabler.h"
+#import "ios/chrome/browser/intelligence/actor/model/actor_service_factory.h"
 #import "ios/chrome/browser/intelligence/bwg/metrics/gemini_metrics.h"
 #import "ios/chrome/browser/intelligence/bwg/model/bwg_link_opening_delegate.h"
 #import "ios/chrome/browser/intelligence/bwg/model/bwg_link_opening_handler.h"
diff --git a/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/BUILD.gn b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/BUILD.gn
index bfd60da9..9d2c2c2 100644
--- a/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/BUILD.gn
+++ b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/BUILD.gn
@@ -26,3 +26,23 @@
     "//ios/web/public",
   ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [ "enhanced_calendar_mediator_unittest.mm" ]
+  deps = [
+    ":coordinator",
+    "//base",
+    "//base/test:test_support",
+    "//components/optimization_guide/proto:optimization_guide_proto",
+    "//ios/chrome/browser/intelligence/enhanced_calendar/constants",
+    "//ios/chrome/browser/intelligence/enhanced_calendar/model:config",
+    "//ios/chrome/browser/optimization_guide/model",
+    "//ios/chrome/browser/optimization_guide/mojom",
+    "//ios/chrome/browser/shared/model/profile/test",
+    "//ios/chrome/test:test_support",
+    "//ios/web/public/test",
+    "//ios/web/public/test/fakes",
+    "//testing/gtest",
+  ]
+}
diff --git a/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator_unittest.mm b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator_unittest.mm
new file mode 100644
index 0000000..aef79019
--- /dev/null
+++ b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator_unittest.mm
@@ -0,0 +1,218 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator.h"
+
+#import "base/strings/sys_string_conversions.h"
+#import "components/optimization_guide/proto/features/enhanced_calendar.pb.h"
+#import "ios/chrome/browser/intelligence/enhanced_calendar/constants/error_strings.h"
+#import "ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator_delegate.h"
+#import "ios/chrome/browser/intelligence/enhanced_calendar/model/enhanced_calendar_configuration.h"
+#import "ios/chrome/browser/optimization_guide/model/optimization_guide_service_factory.h"
+#import "ios/chrome/browser/optimization_guide/mojom/enhanced_calendar_service.mojom.h"
+#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
+#import "ios/web/public/test/fakes/fake_web_state.h"
+#import "ios/web/public/test/web_task_environment.h"
+#import "testing/gtest_mac.h"
+#import "testing/platform_test.h"
+
+// A fake delegate to capture calls from the mediator and verify behavior.
+@interface FakeEnhancedCalendarMediatorDelegate
+    : NSObject <EnhancedCalendarMediatorDelegate>
+@property(nonatomic, assign) BOOL cancelCalled;
+@property(nonatomic, assign) BOOL presentCalled;
+@property(nonatomic, strong) EnhancedCalendarConfiguration* lastConfig;
+@end
+
+@implementation FakeEnhancedCalendarMediatorDelegate
+- (void)cancelRequestsAndDismissViewController:
+    (EnhancedCalendarMediator*)mediator {
+  _cancelCalled = YES;
+}
+- (void)presentAddToCalendar:(EnhancedCalendarMediator*)mediator
+                      config:(EnhancedCalendarConfiguration*)config {
+  _presentCalled = YES;
+  _lastConfig = config;
+}
+@end
+
+// Expose private methods for testing.
+@interface EnhancedCalendarMediator (Testing)
+- (void)handleEnhancedCalendarResponseResult:
+    (ai::mojom::EnhancedCalendarResponseResultPtr)responseResult;
+- (void)updateConfigWithEnhancedCalendarResponse:
+    (optimization_guide::proto::EnhancedCalendarResponse)
+        enhancedCalendarResponse;
+@end
+
+namespace {
+
+// Test fixture for EnhancedCalendarMediator.
+class EnhancedCalendarMediatorTest : public PlatformTest {
+ protected:
+  EnhancedCalendarMediatorTest()
+      : task_environment_(web::WebTaskEnvironment::MainThreadType::UI) {}
+
+  void SetUp() override {
+    PlatformTest::SetUp();
+
+    // Pin timezone to UTC to avoid flakiness.
+    original_timezone_ = [NSTimeZone defaultTimeZone];
+    [NSTimeZone
+        setDefaultTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
+
+    // Set up a fake profile with the OptimizationGuideService.
+    TestProfileIOS::Builder builder;
+    builder.AddTestingFactory(
+        OptimizationGuideServiceFactory::GetInstance(),
+        OptimizationGuideServiceFactory::GetDefaultFactory());
+    profile_ = std::move(builder).Build();
+
+    // Set up a fake web state.
+    web_state_ = std::make_unique<web::FakeWebState>();
+    web_state_->SetBrowserState(profile_.get());
+
+    // Initialize configuration.
+    config_ = [[EnhancedCalendarConfiguration alloc] init];
+    config_.calendarEventConfig = [[CalendarEventConfiguration alloc] init];
+    config_.URL = "https://example.com";
+
+    // Create the mediator.
+    mediator_ =
+        [[EnhancedCalendarMediator alloc] initWithWebState:web_state_.get()
+                                    enhancedCalendarConfig:config_];
+  }
+
+  void TearDown() override {
+    [mediator_ disconnect];
+    mediator_ = nil;
+    config_ = nil;
+    web_state_.reset();
+    profile_.reset();
+
+    // Restore the original timezone.
+    [NSTimeZone setDefaultTimeZone:original_timezone_];
+
+    PlatformTest::TearDown();
+  }
+
+  web::WebTaskEnvironment task_environment_;
+  std::unique_ptr<TestProfileIOS> profile_;
+  std::unique_ptr<web::FakeWebState> web_state_;
+  EnhancedCalendarConfiguration* config_;
+  EnhancedCalendarMediator* mediator_;
+  NSTimeZone* original_timezone_;
+};
+
+// Tests that cancelling the request correctly notifies the delegate.
+TEST_F(EnhancedCalendarMediatorTest, CancelCallsDelegate) {
+  FakeEnhancedCalendarMediatorDelegate* delegate =
+      [[FakeEnhancedCalendarMediatorDelegate alloc] init];
+  mediator_.delegate = delegate;
+
+  [mediator_ cancelEnhancedCalendarRequestAndDismissBottomSheet];
+
+  EXPECT_TRUE(delegate.cancelCalled);
+}
+
+// Tests that a primary account change error triggers a cancellation.
+TEST_F(EnhancedCalendarMediatorTest, HandleResponseAccountChangeError) {
+  FakeEnhancedCalendarMediatorDelegate* delegate =
+      [[FakeEnhancedCalendarMediatorDelegate alloc] init];
+  mediator_.delegate = delegate;
+
+  std::string error = ai::GetEnhancedCalendarErrorString(
+      ai::EnhancedCalendarError::kPrimaryAccountChangeError);
+  ai::mojom::EnhancedCalendarResponseResultPtr result =
+      ai::mojom::EnhancedCalendarResponseResult::NewError(error);
+
+  [mediator_ handleEnhancedCalendarResponseResult:std::move(result)];
+
+  EXPECT_TRUE(delegate.cancelCalled);
+  EXPECT_FALSE(delegate.presentCalled);
+}
+
+// Tests that other errors result in presenting the current configuration as a
+// fallback.
+TEST_F(EnhancedCalendarMediatorTest, HandleResponseOtherError) {
+  FakeEnhancedCalendarMediatorDelegate* delegate =
+      [[FakeEnhancedCalendarMediatorDelegate alloc] init];
+  mediator_.delegate = delegate;
+
+  ai::mojom::EnhancedCalendarResponseResultPtr result =
+      ai::mojom::EnhancedCalendarResponseResult::NewError("some other error");
+
+  [mediator_ handleEnhancedCalendarResponseResult:std::move(result)];
+
+  EXPECT_FALSE(delegate.cancelCalled);
+  EXPECT_TRUE(delegate.presentCalled);
+  EXPECT_EQ(delegate.lastConfig, config_);
+}
+
+// Tests that the configuration is correctly populated from the proto response.
+TEST_F(EnhancedCalendarMediatorTest, UpdateConfigPopulatesFields) {
+  optimization_guide::proto::EnhancedCalendarResponse response;
+  response.set_event_title("Test Title");
+  response.set_event_summary("Test Summary");
+  response.set_event_location("Test Location");
+  response.set_event_confirmation_code("CONF123");
+  response.set_is_event_booked(true);
+  response.set_is_all_day(true);
+  response.set_start_date("10/04/2026");
+  response.set_start_time("15:30");
+  response.set_end_date("10/04/2026");
+  response.set_end_time("16:30");
+
+  [mediator_ updateConfigWithEnhancedCalendarResponse:response];
+
+  // Verify title prefixing for booked events.
+  EXPECT_NSEQ(@"[BOOKED] Test Title", config_.calendarEventConfig.eventTitle);
+  EXPECT_TRUE(config_.calendarEventConfig.isAllDay);
+
+  // Verify date and time parsing.
+  NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
+  [dateFormatter setDateFormat:@"dd/MM/yyyy HH:mm"];
+  NSDate* expectedStartDate =
+      [dateFormatter dateFromString:@"10/04/2026 15:30"];
+  NSDate* expectedEndDate = [dateFormatter dateFromString:@"10/04/2026 16:30"];
+
+  EXPECT_TRUE(
+      [config_.calendarEventConfig.startDateTime isEqual:expectedStartDate]);
+  EXPECT_TRUE(
+      [config_.calendarEventConfig.endDateTime isEqual:expectedEndDate]);
+
+  // Verify description construction.
+  NSString* expectedDescription =
+      @"Test Summary\n\nLocation: Test Location\nConfirmation code: "
+       "CONF123\nURL: https://example.com";
+  EXPECT_NSEQ(expectedDescription,
+              config_.calendarEventConfig.eventDescription);
+}
+
+// Tests that the prefix is not applied when is_event_booked is false.
+TEST_F(EnhancedCalendarMediatorTest, UpdateConfigUnbookedEvent) {
+  optimization_guide::proto::EnhancedCalendarResponse response;
+  response.set_event_title("Test Title");
+  response.set_is_event_booked(false);
+
+  [mediator_ updateConfigWithEnhancedCalendarResponse:response];
+
+  EXPECT_NSEQ(@"Test Title", config_.calendarEventConfig.eventTitle);
+}
+
+// Tests that optional fields are omitted from description when missing in
+// proto.
+TEST_F(EnhancedCalendarMediatorTest, UpdateConfigHandlesMissingFields) {
+  optimization_guide::proto::EnhancedCalendarResponse response;
+  response.set_event_title("Test Title");
+  response.set_event_summary("Test Summary");
+
+  [mediator_ updateConfigWithEnhancedCalendarResponse:response];
+
+  NSString* expectedDescription = @"Test Summary\n\nURL: https://example.com";
+  EXPECT_NSEQ(expectedDescription,
+              config_.calendarEventConfig.eventDescription);
+}
+
+}  // namespace
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index bc1f48d..8af0e755 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -366,6 +366,7 @@
     "//ios/chrome/browser/intelligence/bwg/coordinator:unit_tests",
     "//ios/chrome/browser/intelligence/bwg/model:unit_tests",
     "//ios/chrome/browser/intelligence/bwg/ui:unit_tests",
+    "//ios/chrome/browser/intelligence/enhanced_calendar/coordinator:unit_tests",
     "//ios/chrome/browser/intelligence/features:unit_tests",
     "//ios/chrome/browser/intelligence/page_action_menu/coordinator:unit_tests",
     "//ios/chrome/browser/intelligence/persist_tab_context/model:unit_tests",
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
index 97a83cc..1af5a7a 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-8bb604ed3f96ba957797208cd671e304aa5acd7a
\ No newline at end of file
+9c99f55e2acb2289b36f0268b48f3bd9249aa507
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
index 3032ae97..ce6ace1 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-8b05a283f74f6d672eba9fc513fd0623bf656181
\ No newline at end of file
+1409b154b384b4d201d2df494390e75f27451a9d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
index a42e0eb..46e70ad 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-afc8f6d5f8ca17fc9975c7a9532cbd580a34571a
\ No newline at end of file
+7adc7b14c4e0d298b27a2bc581114c1950e25c5e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
index 52207d7..0547b4c3 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-b212764ea848f275460500de0a2e983bde88aab8
\ No newline at end of file
+96e4837011bda9d64aadbd3749e04ad7f8839a3d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
index 6b2f616..7567d24 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-c396711bcdab0500b7e990a1e3a8af3ceddb5954
\ No newline at end of file
+6c67a9860dacfbbffe7c25d4f8633ad9162c60e2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
index 47cedf3..36feeb5d 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-99cd8b6bcd65bddfb40c30c53e3094fec78ebde2
\ No newline at end of file
+0fd6e52adc5510738acdc94b8206d1b81af7c7e0
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
index d67e8b7..49d1fb0 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-f2b99a442453d542b5316d57a1e0483930da530d
\ No newline at end of file
+2f04f90b3e27e522ad4e8ca2f73dcfcbb5ccf6f5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
index 31579d3..57c27243 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-59ed2bfba826a80ac04b70eacb9e6f4a4e8f7385
\ No newline at end of file
+58f155b30cd68c3fbdecd57a2c617f3cda6b2bd4
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
index 49290946..c701110 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-16381d4bf053f72590b05cb476940b36b68fe11e
\ No newline at end of file
+5d3191c9c96b59cfdebbdbc5cd5786a052cbaa20
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
index f656654..6e3fcded 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-e10de77161a519f1e92d465fb78908fba2a5dcdc
\ No newline at end of file
+7af4f75996f55cdd4e8fa6f30ab9e9faa314fda1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 09930f8..21b8ebe5 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-c756370e7ac140d67a5064ec28447f0e110998a3
\ No newline at end of file
+192a564cd3159dec64266215714dade79586790d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
index 350bee4..40943264 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-bf3c71581037cf85d620fdae9b66caebcbf5ea92
\ No newline at end of file
+70e499a7d9f02c62c3ac23d0cf87421125d374b5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 578b027..a967e45 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-f282d2a3f9099906cd11dc58dbc274e2429ac6da
\ No newline at end of file
+c8e64ce123456cb452a7cbad2439d13523a3514d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 09c3623..81a5c95 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-5054dd57fd988522192008badd2b7b8816943ed1
\ No newline at end of file
+14e6e4d382f1acc8930f5ee39274b980659be748
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 84a8a20..1fb72e71 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-bfa26770867e62de4ded72b15beeec3cbad7b3bf
\ No newline at end of file
+6dc00ab02a1e20eada2e56ec94d723a5152cf095
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
index 92000f8..996c35e 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-080a1ec2935c5aa8db2ca59348345035423be03a
\ No newline at end of file
+58f6d2a0b3bff86d8c9145f30c8ad1b7d2106d15
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 2437b54..1872faf81 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-2198a4ad91aa651f2bb39df9a5270df2eb1b8ad9
\ No newline at end of file
+99a5bccf194c0b43fd6c374846aa21d3cfe90ac2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 4436303..7aec78f 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-230a6381d0fe12e66ecdafd4e790a4c55687bb7a
\ No newline at end of file
+0c3aa32a3944acd97309b8fc0d4f29c0058408f6
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
index c4bed07..37678242 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-a51b6bf525d6f7768c74465729aec9d34f87debc
\ No newline at end of file
+0203ed6d8b0a483b5e60f992029b83d90938d9cb
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
index 1177a2b..3a7798be 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-0245ba692976d20eb441a9682c3537a05ed0ec50
\ No newline at end of file
+b7c0e311bf156a065bed6780e843feefc4db056f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index eaa18fc..b066e10 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-4039afab267af16b821ada9ab788b8d734594c0d
\ No newline at end of file
+761dd4d888ca805e4a35da863813d883d5381cf2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
index 42728df..f120cc0 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-895e831411a1b248ca09a2405e3ec2c12f26f618
\ No newline at end of file
+d9fd335dc7f9decd3385029a4ee63b51b233f78b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 9db298d..55ff3d77 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-bf15f443938c84b1fe68afe2255db156b155391c
\ No newline at end of file
+f182b6e3ea7b2057c3af18871647e24707be8a8a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
index e3b55bb..91c25a4 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-4eca0215038a1a541279c65468bfb3892bc486f6
\ No newline at end of file
+c7124dfaa3e25020ff8daff5af1723582e2217a7
\ No newline at end of file
diff --git a/ios_internal b/ios_internal
index 36408e5..a5d123d 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 36408e5ec3cc81882de29fc10a5fd8d6f4ab2477
+Subproject commit a5d123d0b0d078b82424468650a010cbf04f34b7
diff --git a/media/base/demuxer_memory_limit_android.cc b/media/base/demuxer_memory_limit_android.cc
index 5f74ba2..5b68e59 100644
--- a/media/base/demuxer_memory_limit_android.cc
+++ b/media/base/demuxer_memory_limit_android.cc
@@ -23,9 +23,7 @@
                : default_limit;
   }
   // Use very low limit on 512MiB Android Go devices only.
-  if (base::android::android_info::sdk_int() >=
-          base::android::android_info::SDK_VERSION_OREO &&
-      base::SysInfo::AmountOfPhysicalMemory().InMiB() <= 512) {
+  if (base::SysInfo::AmountOfPhysicalMemory().InMiB() <= 512) {
     return very_low_limit;
   }
   return low_limit;
diff --git a/media/filters/symphonia_audio_decoder.cc b/media/filters/symphonia_audio_decoder.cc
index 581b9820..d006794 100644
--- a/media/filters/symphonia_audio_decoder.cc
+++ b/media/filters/symphonia_audio_decoder.cc
@@ -19,6 +19,7 @@
 #include "base/memory/aligned_memory.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/rand_util.h"
 #include "base/task/bind_post_task.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
@@ -45,6 +46,17 @@
 // value covers up to ~85ms of audio per chunk.
 constexpr int kDefaultMaxFramesPerPcmPacket = 4096;
 
+// We sample 1% of Symphonia related errors for dumping.
+static constexpr double kSampleRate = 0.01;
+
+void MaybeDumpError(const DecoderStatus& status) {
+  // TODO(crbug.com/491162892): remove temporary DUMP_WILL_BE_CHECK once
+  // Symphonia is sufficiently stable.
+  if (base::RandDouble() < kSampleRate) {
+    DUMP_WILL_BE_CHECK(false) << status << ": " << status.message();
+  }
+}
+
 SymphoniaAudioCodec ToSymphoniaCodec(AudioCodec codec,
                                      SampleFormat sample_format) {
   switch (codec) {
@@ -176,6 +188,76 @@
   NOTREACHED();
 }
 
+constexpr DecoderStatus::Codes ToStatusCode(SymphoniaInitStatus status) {
+  switch (status) {
+    case SymphoniaInitStatus::Ok:
+      return DecoderStatus::Codes::kOk;
+    case SymphoniaInitStatus::InvalidConfig:
+      return DecoderStatus::Codes::kUnsupportedConfig;
+    case SymphoniaInitStatus::DecoderError:
+      return DecoderStatus::Codes::kFailedToCreateDecoder;
+    case SymphoniaInitStatus::UnsupportedCodec:
+      return DecoderStatus::Codes::kUnsupportedCodec;
+    case SymphoniaInitStatus::XiphVorbisUnpackError:
+      return DecoderStatus::Codes::kUnsupportedConfig;
+    case SymphoniaInitStatus::SymphoniaUnsupported:
+      return DecoderStatus::Codes::kUnsupportedCodec;
+    case SymphoniaInitStatus::SymphoniaDecodeError:
+      return DecoderStatus::Codes::kMalformedBitstream;
+    case SymphoniaInitStatus::SymphoniaIoError:
+      return DecoderStatus::Codes::kDecoderStreamDemuxerError;
+    case SymphoniaInitStatus::SymphoniaLimitError:
+      return DecoderStatus::Codes::kFailed;
+    case SymphoniaInitStatus::kMaxValue:
+      NOTREACHED();
+  }
+}
+
+DecoderStatus ToDecoderStatus(SymphoniaInitResult& result) {
+  DecoderStatus status(ToStatusCode(result.status), result.error_str.c_str());
+  if (result.status != SymphoniaInitStatus::Ok) {
+    MaybeDumpError(status);
+  }
+  return status;
+}
+
+constexpr DecoderStatus::Codes ToStatusCode(SymphoniaDecodeStatus status) {
+  switch (status) {
+    case SymphoniaDecodeStatus::Ok:
+      return DecoderStatus::Codes::kOk;
+    case SymphoniaDecodeStatus::UnexpectedEndOfStream:
+      return DecoderStatus::Codes::kFailed;
+    case SymphoniaDecodeStatus::InvalidDecoderState:
+      return DecoderStatus::Codes::kNotInitialized;
+    case SymphoniaDecodeStatus::Error:
+      return DecoderStatus::Codes::kFailed;
+    case SymphoniaDecodeStatus::DecodeError:
+      return DecoderStatus::Codes::kMalformedBitstream;
+    case SymphoniaDecodeStatus::IoError:
+      return DecoderStatus::Codes::kDecoderStreamDemuxerError;
+    case SymphoniaDecodeStatus::ResetRequired:
+      return DecoderStatus::Codes::kFailed;
+    case SymphoniaDecodeStatus::SeekError:
+      return DecoderStatus::Codes::kFailed;
+    case SymphoniaDecodeStatus::Unsupported:
+      return DecoderStatus::Codes::kUnsupportedCodec;
+    case SymphoniaDecodeStatus::InsufficentData:
+      return DecoderStatus::Codes::kFailed;
+    case SymphoniaDecodeStatus::InvalidDecodedBufferSampleFormat:
+      return DecoderStatus::Codes::kFailed;
+    case SymphoniaDecodeStatus::kMaxValue:
+      NOTREACHED();
+  }
+}
+
+DecoderStatus ToDecoderStatus(SymphoniaDecodeResult& result) {
+  DecoderStatus status(ToStatusCode(result.status), result.error_str.c_str());
+  if (result.status != SymphoniaDecodeStatus::Ok) {
+    MaybeDumpError(status);
+  }
+  return status;
+}
+
 }  // namespace
 
 SymphoniaAudioDecoder::SymphoniaAudioDecoder(
@@ -229,9 +311,9 @@
     return;
   }
 
-  if (!ConfigureDecoder(config)) {
-    // ConfigureDecoder logs the specific error.
-    std::move(bound_init_cb).Run(DecoderStatus::Codes::kUnsupportedConfig);
+  const auto configure_result = ConfigureDecoder(config);
+  if (!configure_result.is_ok()) {
+    std::move(bound_init_cb).Run(std::move(configure_result));
     return;
   }
 
@@ -306,10 +388,10 @@
   }
 
   // Pass the buffer to the Symphonia decoder.
-  if (!SymphoniaDecode(*buffer)) {
-    // SymphoniaDecode logs the error.
+  const DecoderStatus status = SymphoniaDecode(*buffer);
+  if (!status.is_ok()) {
     state_ = DecoderState::kError;
-    std::move(decode_cb_bound).Run(DecoderStatus::Codes::kFailed);
+    std::move(decode_cb_bound).Run(std::move(status));
     return;
   }
 
@@ -354,7 +436,8 @@
   return false;
 }
 
-bool SymphoniaAudioDecoder::SymphoniaDecode(const DecoderBuffer& buffer) {
+DecoderStatus SymphoniaAudioDecoder::SymphoniaDecode(
+    const DecoderBuffer& buffer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // The first frame only has a valid timestamp if it is not EOS.
@@ -387,7 +470,7 @@
       DCHECK(!processed);
     }
 
-    return true;
+    return DecoderStatus::Codes::kOk;
   }
   // Sanity check: if Symphonia thinks things are OK and returned a valid
   // buffer, then the input buffer should definitely not have been end of
@@ -397,7 +480,7 @@
   if (result.status != SymphoniaDecodeStatus::Ok) {
     MEDIA_LOG(ERROR, media_log_)
         << "Symphonia error occurred: " << result.error_str.c_str();
-    return false;
+    return ToDecoderStatus(result);
   }
 
   // TODO(crbug.com/40074653): similar to FFMPEG audio decoder, add support
@@ -421,7 +504,7 @@
     output_cb_.Run(std::move(decoded_audio));
   }
 
-  return true;
+  return DecoderStatus::Codes::kOk;
 }
 
 scoped_refptr<AudioBuffer> SymphoniaAudioDecoder::ToMediaAudioBuffer(
@@ -451,7 +534,8 @@
   symphonia_decoder_.reset();
 }
 
-bool SymphoniaAudioDecoder::ConfigureDecoder(const AudioDecoderConfig& config) {
+DecoderStatus SymphoniaAudioDecoder::ConfigureDecoder(
+    const AudioDecoderConfig& config) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK(config.IsValidConfig());
   CHECK(!config.is_encrypted());
@@ -471,12 +555,12 @@
         << "Could not initialize Symphonia audio decoder: "
         << result.error_str.c_str();
     state_ = DecoderState::kUninitialized;
-    return false;
+    return ToDecoderStatus(result);
   }
 
   ResetTimestampState(config);
   symphonia_decoder_ = std::move(result.decoder);
-  return true;
+  return DecoderStatus::Codes::kOk;
 }
 
 // The Symphonia audio decoder implementation currently needs the same discard
diff --git a/media/filters/symphonia_audio_decoder.h b/media/filters/symphonia_audio_decoder.h
index b96988f..0981f11 100644
--- a/media/filters/symphonia_audio_decoder.h
+++ b/media/filters/symphonia_audio_decoder.h
@@ -95,10 +95,10 @@
   void DecodeBuffer(scoped_refptr<DecoderBuffer> buffer,
                     DecodeCB decode_cb_bound);
 
-  // Passes the encoded buffer to the Symphonia decoder instance. Returns true
-  // on success, false otherwise. May result in zero or more calls to
-  // output_cb_.
-  bool SymphoniaDecode(const DecoderBuffer& buffer);
+  // Passes the encoded buffer to the Symphonia decoder instance. Returns
+  // DecoderStatus::Codes::kOk on success, or an error code otherwise.
+  // May result in zero or more calls to output_cb_.
+  DecoderStatus SymphoniaDecode(const DecoderBuffer& buffer);
 
   // Creates a media::AudioBuffer from the decoded SymphoniaAudioBuffer.
   scoped_refptr<AudioBuffer> ToMediaAudioBuffer(
@@ -106,8 +106,8 @@
       base::TimeDelta timestamp);
 
   // Handles (re-)initializing the decoder with a (new) config.
-  // Returns true if initialization was successful.
-  bool ConfigureDecoder(const AudioDecoderConfig& config);
+  // Returns DecoderStatus::Codes::kOk if initialization was successful.
+  DecoderStatus ConfigureDecoder(const AudioDecoderConfig& config);
 
   // Releases resources associated with |symphonia_decoder_|.
   void ReleaseSymphoniaResources();
diff --git a/media/remoting/stream_provider.cc b/media/remoting/stream_provider.cc
index 6d06c0c8..741f04e3 100644
--- a/media/remoting/stream_provider.cc
+++ b/media/remoting/stream_provider.cc
@@ -546,6 +546,11 @@
   DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(message->has_acquire_demuxer_rpc());
 
+  if (audio_stream_ || video_stream_) {
+    VLOG(1) << __func__ << " Demuxer streams already acquired, ignoring.";
+    return;
+  }
+
   int32_t audio_demuxer_handle =
       message->acquire_demuxer_rpc().audio_demuxer_handle();
   int32_t video_demuxer_handle =
diff --git a/media/remoting/stream_provider_unittest.cc b/media/remoting/stream_provider_unittest.cc
index 6413abf..ded1ac96 100644
--- a/media/remoting/stream_provider_unittest.cc
+++ b/media/remoting/stream_provider_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "media/remoting/stream_provider.h"
 
+#include "base/functional/callback_helpers.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/task/single_thread_task_runner.h"
@@ -331,5 +332,27 @@
   EXPECT_EQ(GetVideoCurrentFrameCount(), flush_video_count);
 }
 
+TEST_F(StreamProviderTest, DuplicateAcquireDemuxer) {
+  InitializeDemuxer();
+  SendRpcAcquireDemuxer();
+  task_environment_.RunUntilIdle();
+  EXPECT_TRUE(stream_provider_initialized_);
+
+  // Cache raw pointers.
+  std::vector<DemuxerStream*> streams = stream_provider_->GetAllStreams();
+  ASSERT_EQ(streams.size(), 2u);
+  DemuxerStream* cached_audio =
+      streams[0]->type() == DemuxerStream::AUDIO ? streams[0] : streams[1];
+
+  // Second acquisition.
+  SendRpcAcquireDemuxer();
+  task_environment_.RunUntilIdle();
+
+  // The first streams should still be valid and not destroyed.
+  // If they were destroyed, this call would trigger a UAF.
+  cached_audio->Read(1, base::DoNothing());
+  task_environment_.RunUntilIdle();
+}
+
 }  // namespace remoting
 }  // namespace media
diff --git a/remoting/host/ftl_echo_message_listener.cc b/remoting/host/ftl_echo_message_listener.cc
index ba5cf94..877befc 100644
--- a/remoting/host/ftl_echo_message_listener.cc
+++ b/remoting/host/ftl_echo_message_listener.cc
@@ -5,6 +5,7 @@
 #include "remoting/host/ftl_echo_message_listener.h"
 
 #include <string>
+#include <string_view>
 
 #include "base/logging.h"
 #include "remoting/base/logging.h"
@@ -51,10 +52,10 @@
     return false;
   }
 
-  std::string request_message_payload(message.echo().message());
+  std::string_view request_message_payload = message.echo().message();
   HOST_LOG << "Handling echo message: '" << request_message_payload << "'";
 
-  std::string response_message_payload =
+  std::string_view response_message_payload =
       request_message_payload.substr(0, kMaxEchoMessageLength);
   ftl::ChromotingMessage response_message;
   response_message.mutable_echo()->set_message(response_message_payload);
diff --git a/remoting/protocol/session_authz_authenticator.cc b/remoting/protocol/session_authz_authenticator.cc
index 1b7edd3..adda926e 100644
--- a/remoting/protocol/session_authz_authenticator.cc
+++ b/remoting/protocol/session_authz_authenticator.cc
@@ -137,9 +137,15 @@
   DCHECK_EQ(state(), MESSAGE_READY);
 
   JingleAuthentication message;
+  auto self = weak_factory_.GetWeakPtr();
   if (underlying_ && underlying_->state() == MESSAGE_READY) {
     message = underlying_->GetNextMessage();
-    StartReauthorizerIfNecessary();
+    if (self) {
+      StartReauthorizerIfNecessary();
+    }
+  }
+  if (!self) {
+    return message;
   }
 
   if (session_authz_state_ == SessionAuthzState::READY_TO_SEND_HOST_TOKEN) {
@@ -296,7 +302,7 @@
       verify_token_response_->session_reauth_token,
       verify_token_response_->session_reauth_token_lifetime,
       base::BindOnce(&SessionAuthzAuthenticator::OnReauthorizationFailed,
-                     base::Unretained(this)));
+                     weak_factory_.GetWeakPtr()));
   reauthorizer_->Start();
   verify_token_response_.reset();
 }
diff --git a/remoting/protocol/session_authz_authenticator_unittest.cc b/remoting/protocol/session_authz_authenticator_unittest.cc
index ee49dcc..7ca4afb 100644
--- a/remoting/protocol/session_authz_authenticator_unittest.cc
+++ b/remoting/protocol/session_authz_authenticator_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/notreached.h"
 #include "base/run_loop.h"
+#include "base/test/bind.h"
 #include "base/test/gmock_callback_support.h"
 #include "base/test/gmock_move_support.h"
 #include "base/test/mock_callback.h"
@@ -530,6 +531,8 @@
   base::test::TestFuture<void> start_future;
   host_authenticator_ptr->Start(start_future.GetCallback());
   EXPECT_TRUE(start_future.Wait());
+  // We're done with this pointer so clear it.
+  host_authenticator_ptr = nullptr;
 
   // Transition to WAITING_FOR_SESSION_TOKEN state.
   std::ignore = host_->GetNextMessage();
@@ -560,7 +563,6 @@
 
 TEST_F(SessionAuthzAuthenticatorTeardownTest,
        UnderlyingAuthenticator_SubsequentMessage_SynchronousTeardown) {
-  // Use a mock underlying authenticator.
   auto mock_underlying_owned = std::make_unique<NiceMock<MockAuthenticator>>();
   MockAuthenticator* mock_underlying = mock_underlying_owned.get();
 
@@ -578,7 +580,7 @@
   auto mock_underlying_holder = base::MakeRefCounted<
       base::RefCountedData<std::unique_ptr<Authenticator>>>(
       std::move(mock_underlying_owned));
-  host_ = std::make_unique<SessionAuthzAuthenticator>(
+  auto host_authenticator = std::make_unique<SessionAuthzAuthenticator>(
       CredentialsType::CORP_SESSION_AUTHZ, std::move(mock_service_client),
       base::BindRepeating(
           [](scoped_refptr<base::RefCountedData<std::unique_ptr<Authenticator>>>
@@ -586,19 +588,21 @@
              const std::string& secret,
              Authenticator::State state) { return std::move(holder->data); },
           mock_underlying_holder));
+  SessionAuthzAuthenticator* host_authenticator_ptr = host_authenticator.get();
+  host_ = std::move(host_authenticator);
 
   base::test::TestFuture<void> start_future;
-  static_cast<SessionAuthzAuthenticator*>(host_.get())
-      ->Start(start_future.GetCallback());
+  host_authenticator_ptr->Start(start_future.GetCallback());
   EXPECT_TRUE(start_future.Wait());
+  // We're done with this pointer so clear it.
+  host_authenticator_ptr = nullptr;
 
   std::ignore = host_->GetNextMessage();
   JingleAuthentication message;
   message.session_authz_session_token = std::string(kFakeSessionToken);
 
   // First ProcessMessage triggers VerifySessionToken, which then triggers
-  // underlying_->ProcessMessage.
-  // This time we DON'T destroy it yet.
+  // underlying_->ProcessMessage but ensure we don't destroy it yet.
   EXPECT_CALL(*mock_underlying, ProcessMessage(_, _))
       .WillOnce([&](const JingleAuthentication&, base::OnceClosure callback) {
         std::move(callback).Run();
@@ -608,21 +612,97 @@
 
   host_->ProcessMessage(message, base::DoNothing());
 
-  // Now the state should be SHARED_SECRET_FETCHED.
-  // Call ProcessMessage again with a callback that destroys host_.
+  // Now the state should be SHARED_SECRET_FETCHED so we call ProcessMessage
+  // again with a callback that destroys |host_|.
   EXPECT_CALL(*mock_underlying, ProcessMessage(_, _))
       .WillOnce([&](const JingleAuthentication&, base::OnceClosure callback) {
         std::move(callback).Run();
       });
 
-  host_->ProcessMessage(message,
-                        base::BindOnce(
-                            [](std::unique_ptr<Authenticator>* host,
-                               MockSessionAuthzServiceClient** client_ptr) {
-                              *client_ptr = nullptr;
-                              host->reset();
-                            },
-                            &host_, &mock_service_client_ptr));
+  auto reset_host_callback = base::BindOnce(
+      [](std::unique_ptr<Authenticator>* host,
+         MockSessionAuthzServiceClient** client_ptr) {
+        *client_ptr = nullptr;
+        host->reset();
+      },
+      &host_, &mock_service_client_ptr);
+  host_->ProcessMessage(message, std::move(reset_host_callback));
+
+  ASSERT_EQ(host_, nullptr);
+}
+
+TEST_F(SessionAuthzAuthenticatorTeardownTest,
+       GetNextMessage_SynchronousTeardown) {
+  // Use a mock underlying authenticator.
+  auto mock_underlying_owned = std::make_unique<NiceMock<MockAuthenticator>>();
+  MockAuthenticator* mock_underlying = mock_underlying_owned.get();
+
+  auto mock_service_client = std::make_unique<MockSessionAuthzServiceClient>();
+  MockSessionAuthzServiceClient* mock_service_client_ptr =
+      mock_service_client.get();
+
+  EXPECT_CALL(*mock_service_client_ptr, GenerateHostToken(_))
+      .WillOnce(RespondGenerateHostToken());
+  EXPECT_CALL(*mock_service_client_ptr, VerifySessionToken(_, _))
+      .WillOnce(RespondVerifySessionTokenWithoutReauthFields());
+
+  // Use a ref-counted holder to allow the repeating callback to return the
+  // unique_ptr once.
+  auto mock_underlying_holder = base::MakeRefCounted<
+      base::RefCountedData<std::unique_ptr<Authenticator>>>(
+      std::move(mock_underlying_owned));
+  auto host_authenticator = std::make_unique<SessionAuthzAuthenticator>(
+      CredentialsType::CORP_SESSION_AUTHZ, std::move(mock_service_client),
+      base::BindRepeating(
+          [](scoped_refptr<base::RefCountedData<std::unique_ptr<Authenticator>>>
+                 holder,
+             const std::string& secret,
+             Authenticator::State state) { return std::move(holder->data); },
+          mock_underlying_holder));
+  SessionAuthzAuthenticator* host_authenticator_ptr = host_authenticator.get();
+  host_ = std::move(host_authenticator);
+
+  base::test::TestFuture<void> start_future;
+  host_authenticator_ptr->Start(start_future.GetCallback());
+  EXPECT_TRUE(start_future.Wait());
+
+  // Transition to WAITING_FOR_SESSION_TOKEN state by calling GetNextMessage.
+  // The first GetNextMessage will just return the host token.
+  std::ignore = host_->GetNextMessage();
+
+  JingleAuthentication message;
+  message.session_authz_session_token = std::string(kFakeSessionToken);
+
+  // ProcessMessage will call underlying_->ProcessMessage.
+  EXPECT_CALL(*mock_underlying, ProcessMessage(_, _))
+      .WillOnce([&](const JingleAuthentication&, base::OnceClosure callback) {
+        std::move(callback).Run();
+      });
+
+  // Initially, underlying is WAITING_MESSAGE.
+  EXPECT_CALL(*mock_underlying, state())
+      .WillRepeatedly(Return(Authenticator::WAITING_MESSAGE));
+
+  host_->ProcessMessage(message, base::DoNothing());
+
+  // Now underlying_->state() should be MESSAGE_READY.
+  EXPECT_CALL(*mock_underlying, state())
+      .WillRepeatedly(Return(Authenticator::MESSAGE_READY));
+
+  // When GetNextMessage() is called on mock_underlying, we transition its state
+  // to ACCEPTED.
+  EXPECT_CALL(*mock_underlying, GetNextMessage()).WillOnce([&]() {
+    EXPECT_CALL(*mock_underlying, state())
+        .WillRepeatedly(Return(Authenticator::ACCEPTED));
+    return JingleAuthentication();
+  });
+
+  // Set the state change callback to destroy |host_|.
+  host_->set_state_change_after_accepted_callback(
+      base::BindLambdaForTesting([&]() { host_.reset(); }));
+
+  // Verify calling GetNextMessage() does not crash.
+  std::ignore = host_->GetNextMessage();
 
   ASSERT_EQ(host_, nullptr);
 }
diff --git a/services/network/scheduler/network_service_task_queues.h b/services/network/scheduler/network_service_task_queues.h
index 8f0f6bc..458477a3 100644
--- a/services/network/scheduler/network_service_task_queues.h
+++ b/services/network/scheduler/network_service_task_queues.h
@@ -51,6 +51,9 @@
       const {
     return GetTaskRunner(net::RequestPriority::DEFAULT_PRIORITY);
   }
+  base::sequence_manager::TaskQueue* GetDefaultTaskQueue() const {
+    return GetTaskQueue(net::RequestPriority::DEFAULT_PRIORITY);
+  }
 
   // Returns the task runner for the specified `RequestPriority`.
   const scoped_refptr<base::SingleThreadTaskRunner>& GetTaskRunner(
diff --git a/services/network/scheduler/network_service_task_queues_unittest.cc b/services/network/scheduler/network_service_task_queues_unittest.cc
index bdab1a0..031ff6a 100644
--- a/services/network/scheduler/network_service_task_queues_unittest.cc
+++ b/services/network/scheduler/network_service_task_queues_unittest.cc
@@ -35,7 +35,7 @@
                             CreateNetworkServiceTaskPrioritySettings())
                         .Build())),
         queues_(sequence_manager_.get()) {
-    sequence_manager_->SetDefaultTaskRunner(queues_.GetDefaultTaskRunner());
+    sequence_manager_->SetDefaultTaskQueue(queues_.GetDefaultTaskQueue());
   }
 
   std::unique_ptr<base::sequence_manager::SequenceManager> sequence_manager_;
diff --git a/services/network/scheduler/network_service_task_scheduler.cc b/services/network/scheduler/network_service_task_scheduler.cc
index 622f32b..16733fd 100644
--- a/services/network/scheduler/network_service_task_scheduler.cc
+++ b/services/network/scheduler/network_service_task_scheduler.cc
@@ -82,7 +82,7 @@
   // crashes.
   sequence_manager->EnableCrashKeys("network_service_scheduler_async_stack");
   // Set the default task runner for the current thread.
-  sequence_manager->SetDefaultTaskRunner(GetDefaultTaskRunner());
+  sequence_manager->SetDefaultTaskQueue(task_queues_.GetDefaultTaskQueue());
 }
 
 void NetworkServiceTaskScheduler::OnTaskCompleted(
@@ -142,4 +142,9 @@
   return task_queues_.GetDefaultTaskRunner();
 }
 
+base::sequence_manager::TaskQueue*
+NetworkServiceTaskScheduler::GetDefaultTaskQueue() {
+  return task_queues_.GetDefaultTaskQueue();
+}
+
 }  // namespace network
diff --git a/services/network/scheduler/network_service_task_scheduler.h b/services/network/scheduler/network_service_task_scheduler.h
index b9b570b..63d8b07 100644
--- a/services/network/scheduler/network_service_task_scheduler.h
+++ b/services/network/scheduler/network_service_task_scheduler.h
@@ -67,6 +67,8 @@
   const scoped_refptr<base::SingleThreadTaskRunner>& GetDefaultTaskRunner()
       const;
 
+  base::sequence_manager::TaskQueue* GetDefaultTaskQueue();
+
   // Sets up the global `net` task runners to point to this scheduler's task
   // runners. This test-only version saves the original global task runners
   // and restores them upon this scheduler's destruction to prevent side-effects
diff --git a/services/network/scheduler/network_service_task_scheduler_unittest.cc b/services/network/scheduler/network_service_task_scheduler_unittest.cc
index 28979794..8949a8f 100644
--- a/services/network/scheduler/network_service_task_scheduler_unittest.cc
+++ b/services/network/scheduler/network_service_task_scheduler_unittest.cc
@@ -47,7 +47,7 @@
       : base::test::TaskEnvironment(std::move(scoped_task_environment)) {
     scheduler_ =
         NetworkServiceTaskScheduler::CreateForTesting(sequence_manager());
-    DeferredInitFromSubclass(scheduler_->GetDefaultTaskRunner());
+    DeferredInitFromSubclass(scheduler_->GetDefaultTaskQueue());
   }
 
   std::unique_ptr<NetworkServiceTaskScheduler> scheduler_;
diff --git a/services/viz/public/cpp/gpu/context_provider_command_buffer.cc b/services/viz/public/cpp/gpu/context_provider_command_buffer.cc
index ce86afe4..fc497204 100644
--- a/services/viz/public/cpp/gpu/context_provider_command_buffer.cc
+++ b/services/viz/public/cpp/gpu/context_provider_command_buffer.cc
@@ -102,10 +102,8 @@
     int32_t stream_id,
     gpu::SchedulingPriority stream_priority,
     const GURL& active_url,
-    command_buffer_metrics::ContextType type,
-    bool lose_context_when_out_of_memory) {
+    command_buffer_metrics::ContextType type) {
   auto attributes = gpu::mojom::GLESCreationAttribs::New();
-  attributes->lose_context_when_out_of_memory = lose_context_when_out_of_memory;
 
   return base::MakeRefCounted<ContextProviderCommandBuffer>(
       base::PassKey<ContextProviderCommandBuffer>(), std::move(channel),
@@ -176,10 +174,8 @@
     bool automatic_flushes,
     bool support_locking,
     const gpu::SharedMemoryLimits& memory_limits,
-    command_buffer_metrics::ContextType type,
-    bool lose_context_when_out_of_memory) {
+    command_buffer_metrics::ContextType type) {
   auto attributes = gpu::mojom::RasterCreationAttribs::New();
-  attributes->lose_context_when_out_of_memory = lose_context_when_out_of_memory;
 
   return base::MakeRefCounted<ContextProviderCommandBuffer>(
       base::PassKey<ContextProviderCommandBuffer>(), std::move(channel),
@@ -330,8 +326,7 @@
       DCHECK(channel_);
       auto raster_impl = std::make_unique<gpu::raster::RasterImplementation>(
           raster_helper.get(), transfer_buffer.get(),
-          attributes_->get_raster()->lose_context_when_out_of_memory,
-          command_buffer_.get());
+          /*lose_context_when_out_of_memory=*/true, command_buffer_.get());
       bind_result_ = raster_impl->Initialize(memory_limits_);
       if (bind_result_ != gpu::ContextResult::kSuccess) {
         DLOG(ERROR) << "Failed to initialize RasterImplementation.";
@@ -373,8 +368,7 @@
       // gpu::ContextSupport interface.
       auto gles2_impl = std::make_unique<gpu::gles2::GLES2Implementation>(
           gles2_helper.get(), /*share_group=*/nullptr, transfer_buffer.get(),
-          attributes_->get_gles()->lose_context_when_out_of_memory,
-          command_buffer_.get());
+          /*lose_context_when_out_of_memory=*/true, command_buffer_.get());
       bind_result_ = gles2_impl->Initialize(memory_limits_);
       if (bind_result_ != gpu::ContextResult::kSuccess) {
         DLOG(ERROR) << "Failed to initialize GLES2Implementation.";
diff --git a/services/viz/public/cpp/gpu/context_provider_command_buffer.h b/services/viz/public/cpp/gpu/context_provider_command_buffer.h
index bdafbc6a..f79e4dea 100644
--- a/services/viz/public/cpp/gpu/context_provider_command_buffer.h
+++ b/services/viz/public/cpp/gpu/context_provider_command_buffer.h
@@ -75,8 +75,7 @@
       int32_t stream_id,
       gpu::SchedulingPriority stream_priority,
       const GURL& active_url,
-      command_buffer_metrics::ContextType type,
-      bool lose_context_when_out_of_memory = false);
+      command_buffer_metrics::ContextType type);
 
   static scoped_refptr<ContextProviderCommandBuffer> CreateForWebGL(
       scoped_refptr<gpu::GpuChannelHost> channel,
@@ -93,8 +92,7 @@
       bool automatic_flushes,
       bool support_locking,
       const gpu::SharedMemoryLimits& memory_limits,
-      command_buffer_metrics::ContextType type,
-      bool lose_context_when_out_of_memory);
+      command_buffer_metrics::ContextType type);
 
   static scoped_refptr<ContextProviderCommandBuffer> CreateForWebGPU(
       scoped_refptr<gpu::GpuChannelHost> channel,
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 5ce0360b..ef4787d0 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -365,7 +365,7 @@
           "hard_timeout": 14400,
           "io_timeout": 21600,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 10
+          "shards": 8
         },
         "test": "performance_test_suite_android_trichrome_chrome_google_64_32_bundle",
         "trigger_script": {
diff --git a/testing/buildbot/chromium.perf.pinpoint.json b/testing/buildbot/chromium.perf.pinpoint.json
index 0e7e87d20..f1d662b0 100644
--- a/testing/buildbot/chromium.perf.pinpoint.json
+++ b/testing/buildbot/chromium.perf.pinpoint.json
@@ -306,7 +306,7 @@
           "hard_timeout": 14400,
           "io_timeout": 21600,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 10
+          "shards": 8
         },
         "test": "performance_test_suite_android_trichrome_chrome_google_64_32_bundle",
         "trigger_script": {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 48fed2b8..eba584a8 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3811,8 +3811,8 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled",
-                    "enable_features": [
+                    "name": "Disabled",
+                    "disable_features": [
                         "BackForwardCachePauseMicrotasks"
                     ]
                 }
diff --git a/third_party/androidx/build.gradle b/third_party/androidx/build.gradle
index 787a7ac..0c6debaa 100644
--- a/third_party/androidx/build.gradle
+++ b/third_party/androidx/build.gradle
@@ -339,7 +339,7 @@
     google()
     maven {
         // This URL is generated by the fetch_all_androidx.py script.
-        url 'https://androidx.dev/snapshots/builds/15183400/artifacts/repository'
+        url 'https://androidx.dev/snapshots/builds/15186230/artifacts/repository'
     }
     mavenCentral()
 }
diff --git a/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium b/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium
index 18e3f49b..0ffac05 100644
--- a/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium
@@ -1,6 +1,6 @@
 Name: Activity
 Short Name: activity
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/activity/activity/1.14.0-SNAPSHOT/activity-1.14.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/activity/activity/1.14.0-SNAPSHOT/activity-1.14.0-20260410.210759-1.aar
 Version: 1.14.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium b/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium
index 56a5a5d..a207f01 100644
--- a/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium
@@ -1,6 +1,6 @@
 Name: Activity Compose
 Short Name: activity-compose
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/activity/activity-compose/1.14.0-SNAPSHOT/activity-compose-1.14.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/activity/activity-compose/1.14.0-SNAPSHOT/activity-compose-1.14.0-20260410.210759-1.aar
 Version: 1.14.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium
index a22a3aa..f01e2d7 100644
--- a/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Activity Kotlin Extensions
 Short Name: activity-ktx
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/activity/activity-ktx/1.14.0-SNAPSHOT/activity-ktx-1.14.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/activity/activity-ktx/1.14.0-SNAPSHOT/activity-ktx-1.14.0-20260410.210759-1.aar
 Version: 1.14.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium b/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium
index 91841eb..2c4d311 100644
--- a/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium
@@ -1,6 +1,6 @@
 Name: AppCompat
 Short Name: appcompat
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/appcompat/appcompat/1.8.0-SNAPSHOT/appcompat-1.8.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/appcompat/appcompat/1.8.0-SNAPSHOT/appcompat-1.8.0-20260410.210759-1.aar
 Version: 1.8.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium b/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium
index aae364e..7cec54bb 100644
--- a/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium
@@ -1,6 +1,6 @@
 Name: AppCompat Resources
 Short Name: appcompat-resources
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/appcompat/appcompat-resources/1.8.0-SNAPSHOT/appcompat-resources-1.8.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/appcompat/appcompat-resources/1.8.0-SNAPSHOT/appcompat-resources-1.8.0-20260410.210759-1.aar
 Version: 1.8.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium b/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium
index ce84cc3..fa5a417 100644
--- a/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium
@@ -1,6 +1,6 @@
 Name: AppSearch
 Short Name: appsearch
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/appsearch/appsearch/1.2.0-SNAPSHOT/appsearch-1.2.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/appsearch/appsearch/1.2.0-SNAPSHOT/appsearch-1.2.0-20260410.210759-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium
index c6a61ad..21fad34 100644
--- a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium
@@ -1,6 +1,6 @@
 Name: AppSearch Builtin Types
 Short Name: appsearch-builtin-types
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/appsearch/appsearch-builtin-types/1.2.0-SNAPSHOT/appsearch-builtin-types-1.2.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/appsearch/appsearch-builtin-types/1.2.0-SNAPSHOT/appsearch-builtin-types-1.2.0-20260410.210759-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium
index 1ebe581..cbcc5af6 100644
--- a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium
@@ -1,6 +1,6 @@
 Name: AppSearch Platform Storage
 Short Name: appsearch-platform-storage
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/appsearch/appsearch-platform-storage/1.2.0-SNAPSHOT/appsearch-platform-storage-1.2.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/appsearch/appsearch-platform-storage/1.2.0-SNAPSHOT/appsearch-platform-storage-1.2.0-20260410.210759-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium b/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium
index e12523ca..0a01141fe 100644
--- a/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium
@@ -1,6 +1,6 @@
 Name: Arch-Common
 Short Name: core-common
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/arch/core/core-common/2.3.0-SNAPSHOT/core-common-2.3.0-20260410.132134-1.jar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/arch/core/core-common/2.3.0-SNAPSHOT/core-common-2.3.0-20260410.210759-1.jar
 Version: 2.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium b/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium
index 0e056f7..1b1b02c 100644
--- a/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium
@@ -1,6 +1,6 @@
 Name: Arch-Runtime
 Short Name: core-runtime
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/arch/core/core-runtime/2.3.0-SNAPSHOT/core-runtime-2.3.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/arch/core/core-runtime/2.3.0-SNAPSHOT/core-runtime-2.3.0-20260410.210759-1.aar
 Version: 2.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium b/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium
index 70a8cb4..55931ce2 100644
--- a/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium
@@ -1,6 +1,6 @@
 Name: Autofill
 Short Name: autofill
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/autofill/autofill/1.4.0-SNAPSHOT/autofill-1.4.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/autofill/autofill/1.4.0-SNAPSHOT/autofill-1.4.0-20260410.210759-1.aar
 Version: 1.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium
index 8a5b9b1..03b425e 100644
--- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium
@@ -1,6 +1,6 @@
 Name: Benchmark - Common
 Short Name: benchmark-common
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/benchmark/benchmark-common/1.5.0-SNAPSHOT/benchmark-common-1.5.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/benchmark/benchmark-common/1.5.0-SNAPSHOT/benchmark-common-1.5.0-20260410.210759-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium
index 1914ebf..419ffc75c 100644
--- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium
@@ -1,6 +1,6 @@
 Name: Benchmark - JUnit4
 Short Name: benchmark-junit4
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/benchmark/benchmark-junit4/1.5.0-SNAPSHOT/benchmark-junit4-1.5.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/benchmark/benchmark-junit4/1.5.0-SNAPSHOT/benchmark-junit4-1.5.0-20260410.210759-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium
index e1f9023..5dcc848 100644
--- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium
@@ -1,6 +1,6 @@
 Name: Benchmark - Macrobenchmark
 Short Name: benchmark-macro
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/benchmark/benchmark-macro/1.5.0-SNAPSHOT/benchmark-macro-1.5.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/benchmark/benchmark-macro/1.5.0-SNAPSHOT/benchmark-macro-1.5.0-20260410.210759-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium
index 0f8a570d..367cc1b 100644
--- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium
@@ -1,6 +1,6 @@
 Name: Benchmark - Macrobenchmark JUnit4
 Short Name: benchmark-macro-junit4
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/benchmark/benchmark-macro-junit4/1.5.0-SNAPSHOT/benchmark-macro-junit4-1.5.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/benchmark/benchmark-macro-junit4/1.5.0-SNAPSHOT/benchmark-macro-junit4-1.5.0-20260410.210759-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium
index 9f50966..81a053e3 100644
--- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Benchmark TraceProcessor
 Short Name: benchmark-traceprocessor-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/benchmark/benchmark-traceprocessor-android/1.5.0-SNAPSHOT/benchmark-traceprocessor-android-1.5.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/benchmark/benchmark-traceprocessor-android/1.5.0-SNAPSHOT/benchmark-traceprocessor-android-1.5.0-20260410.210759-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium b/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium
index 19b714bd..754897e 100644
--- a/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium
@@ -1,6 +1,6 @@
 Name: Biometric
 Short Name: biometric
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/biometric/biometric/1.4.0-SNAPSHOT/biometric-1.4.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/biometric/biometric/1.4.0-SNAPSHOT/biometric-1.4.0-20260410.210759-1.aar
 Version: 1.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium b/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium
index acb1c00e..f080fa6 100644
--- a/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium
@@ -1,6 +1,6 @@
 Name: CardView
 Short Name: cardview
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/cardview/cardview/1.1.0-SNAPSHOT/cardview-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/cardview/cardview/1.1.0-SNAPSHOT/cardview-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium b/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium
index b913f3fb..7e6fb06 100644
--- a/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium
@@ -1,6 +1,6 @@
 Name: collections
 Short Name: collection-jvm
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/collection/collection-jvm/1.7.0-SNAPSHOT/collection-jvm-1.7.0-20260410.132134-1.jar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/collection/collection-jvm/1.7.0-SNAPSHOT/collection-jvm-1.7.0-20260410.210759-1.jar
 Version: 1.7.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium
index 69f63ed..c21374f6 100644
--- a/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Collections Kotlin Extensions
 Short Name: collection-ktx
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/collection/collection-ktx/1.7.0-SNAPSHOT/collection-ktx-1.7.0-20260410.132134-1.jar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/collection/collection-ktx/1.7.0-SNAPSHOT/collection-ktx-1.7.0-20260410.210759-1.jar
 Version: 1.7.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium
index 6bfe7bcf1..e010a86 100644
--- a/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Animation
 Short Name: animation-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/animation/animation-android/1.12.0-SNAPSHOT/animation-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/animation/animation-android/1.12.0-SNAPSHOT/animation-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium
index f6c89a6..028c6de 100644
--- a/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Animation Core
 Short Name: animation-core-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/animation/animation-core-android/1.12.0-SNAPSHOT/animation-core-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/animation/animation-core-android/1.12.0-SNAPSHOT/animation-core-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium
index 503a71f..5aab066 100644
--- a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Foundation
 Short Name: foundation-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/foundation/foundation-android/1.12.0-SNAPSHOT/foundation-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/foundation/foundation-android/1.12.0-SNAPSHOT/foundation-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium
index 6cec6d0..cbd563a 100644
--- a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Layouts
 Short Name: foundation-layout-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/foundation/foundation-layout-android/1.12.0-SNAPSHOT/foundation-layout-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/foundation/foundation-layout-android/1.12.0-SNAPSHOT/foundation-layout-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium
index a6056b4f..09f4aca 100644
--- a/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Material3 Components
 Short Name: material3-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/material3/material3-android/1.5.0-SNAPSHOT/material3-android-1.5.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/material3/material3-android/1.5.0-SNAPSHOT/material3-android-1.5.0-20260410.210759-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium
index fd3b08a6..ef02a8f 100644
--- a/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Material Ripple
 Short Name: material-ripple-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/material/material-ripple-android/1.12.0-SNAPSHOT/material-ripple-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/material/material-ripple-android/1.12.0-SNAPSHOT/material-ripple-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium
index 6c611ab4..3a8f452 100644
--- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Runtime
 Short Name: runtime-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/runtime/runtime-android/1.12.0-SNAPSHOT/runtime-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/runtime/runtime-android/1.12.0-SNAPSHOT/runtime-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium
index f5545200..aeb06fad 100644
--- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Runtime Annotation
 Short Name: runtime-annotation-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/runtime/runtime-annotation-android/1.12.0-SNAPSHOT/runtime-annotation-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/runtime/runtime-annotation-android/1.12.0-SNAPSHOT/runtime-annotation-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium
index 754a036..459571cc 100644
--- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Runtime Retain
 Short Name: runtime-retain-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/runtime/runtime-retain-android/1.12.0-SNAPSHOT/runtime-retain-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/runtime/runtime-retain-android/1.12.0-SNAPSHOT/runtime-retain-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium
index 519d6a3e..9e486510 100644
--- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Saveable
 Short Name: runtime-saveable-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/runtime/runtime-saveable-android/1.12.0-SNAPSHOT/runtime-saveable-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/runtime/runtime-saveable-android/1.12.0-SNAPSHOT/runtime-saveable-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium
index 4ea0f090..d9afe63f 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose UI
 Short Name: ui-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/ui/ui-android/1.12.0-SNAPSHOT/ui-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/ui/ui-android/1.12.0-SNAPSHOT/ui-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium
index d4b41326..aa447c2 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Geometry
 Short Name: ui-geometry-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/ui/ui-geometry-android/1.12.0-SNAPSHOT/ui-geometry-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/ui/ui-geometry-android/1.12.0-SNAPSHOT/ui-geometry-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium
index 2f7223a..99a1315 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Graphics
 Short Name: ui-graphics-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/ui/ui-graphics-android/1.12.0-SNAPSHOT/ui-graphics-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/ui/ui-graphics-android/1.12.0-SNAPSHOT/ui-graphics-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium
index 3ccac95..b4ed9fa 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Testing
 Short Name: ui-test-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/ui/ui-test-android/1.12.0-SNAPSHOT/ui-test-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/ui/ui-test-android/1.12.0-SNAPSHOT/ui-test-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium
index e47edc3..5f7fb33 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Testing for JUnit4
 Short Name: ui-test-junit4-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/ui/ui-test-junit4-android/1.12.0-SNAPSHOT/ui-test-junit4-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/ui/ui-test-junit4-android/1.12.0-SNAPSHOT/ui-test-junit4-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium
index 3ed0bf8..8735542 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Testing manifest dependency
 Short Name: ui-test-manifest
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/ui/ui-test-manifest/1.12.0-SNAPSHOT/ui-test-manifest-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/ui/ui-test-manifest/1.12.0-SNAPSHOT/ui-test-manifest-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium
index 7e696680..31d270fa 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose UI Text
 Short Name: ui-text-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/ui/ui-text-android/1.12.0-SNAPSHOT/ui-text-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/ui/ui-text-android/1.12.0-SNAPSHOT/ui-text-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium
index b6d1417c..f3377d96 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Google Fonts integration
 Short Name: ui-text-google-fonts
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/ui/ui-text-google-fonts/1.12.0-SNAPSHOT/ui-text-google-fonts-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/ui/ui-text-google-fonts/1.12.0-SNAPSHOT/ui-text-google-fonts-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium
index 9e6dd63..4ea8ae7 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Unit
 Short Name: ui-unit-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/ui/ui-unit-android/1.12.0-SNAPSHOT/ui-unit-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/ui/ui-unit-android/1.12.0-SNAPSHOT/ui-unit-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium
index 7a69701a..f51d72d 100644
--- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Util
 Short Name: ui-util-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/compose/ui/ui-util-android/1.12.0-SNAPSHOT/ui-util-android-1.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/compose/ui/ui-util-android/1.12.0-SNAPSHOT/ui-util-android-1.12.0-20260410.210759-1.aar
 Version: 1.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_concurrent_concurrent_futures/README.chromium b/third_party/androidx/committed/libs/androidx_concurrent_concurrent_futures/README.chromium
index 997604f..10391bfa 100644
--- a/third_party/androidx/committed/libs/androidx_concurrent_concurrent_futures/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_concurrent_concurrent_futures/README.chromium
@@ -1,6 +1,6 @@
 Name: Futures
 Short Name: concurrent-futures
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/concurrent/concurrent-futures/1.4.0-SNAPSHOT/concurrent-futures-1.4.0-20260410.132134-1.jar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/concurrent/concurrent-futures/1.4.0-SNAPSHOT/concurrent-futures-1.4.0-20260410.210759-1.jar
 Version: 1.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_concurrent_concurrent_futures_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_concurrent_concurrent_futures_ktx/README.chromium
index cd8ccfd..76afc64 100644
--- a/third_party/androidx/committed/libs/androidx_concurrent_concurrent_futures_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_concurrent_concurrent_futures_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Futures Kotlin Extensions
 Short Name: concurrent-futures-ktx
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/concurrent/concurrent-futures-ktx/1.4.0-SNAPSHOT/concurrent-futures-ktx-1.4.0-20260410.132134-1.jar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/concurrent/concurrent-futures-ktx/1.4.0-SNAPSHOT/concurrent-futures-ktx-1.4.0-20260410.210759-1.jar
 Version: 1.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium
index 5628ed0..7658e1e 100644
--- a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium
@@ -1,6 +1,6 @@
 Name: ConstraintLayout
 Short Name: constraintlayout
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/constraintlayout/constraintlayout/2.3.0-SNAPSHOT/constraintlayout-2.3.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/constraintlayout/constraintlayout/2.3.0-SNAPSHOT/constraintlayout-2.3.0-20260410.210759-1.aar
 Version: 2.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium
index 67a76bb72..e7fb995 100644
--- a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium
@@ -1,6 +1,6 @@
 Name: ConstraintLayout Core
 Short Name: constraintlayout-core
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/constraintlayout/constraintlayout-core/1.2.0-SNAPSHOT/constraintlayout-core-1.2.0-20260410.132134-1.jar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/constraintlayout/constraintlayout-core/1.2.0-SNAPSHOT/constraintlayout-core-1.2.0-20260410.210759-1.jar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_core_core/README.chromium b/third_party/androidx/committed/libs/androidx_core_core/README.chromium
index 841592a32..d1146cb 100644
--- a/third_party/androidx/committed/libs/androidx_core_core/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_core_core/README.chromium
@@ -1,6 +1,6 @@
 Name: Core
 Short Name: core
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/core/core/1.19.0-SNAPSHOT/core-1.19.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/core/core/1.19.0-SNAPSHOT/core-1.19.0-20260410.210759-1.aar
 Version: 1.19.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium
index 49fb199..1e62df6 100644
--- a/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Core Kotlin Extensions
 Short Name: core-ktx
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/core/core-ktx/1.19.0-SNAPSHOT/core-ktx-1.19.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/core/core-ktx/1.19.0-SNAPSHOT/core-ktx-1.19.0-20260410.210759-1.aar
 Version: 1.19.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_core_core_pip/README.chromium b/third_party/androidx/committed/libs/androidx_core_core_pip/README.chromium
index 49e547b7..34b82a1 100644
--- a/third_party/androidx/committed/libs/androidx_core_core_pip/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_core_core_pip/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.core:core-pip
 Short Name: core-pip
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/core/core-pip/1.0.0-SNAPSHOT/core-pip-1.0.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/core/core-pip/1.0.0-SNAPSHOT/core-pip-1.0.0-20260410.210759-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium b/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium
index 331601d..acd2eff 100644
--- a/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.core:core-viewtree
 Short Name: core-viewtree
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/core/core-viewtree/1.1.0-SNAPSHOT/core-viewtree-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/core/core-viewtree/1.1.0-SNAPSHOT/core-viewtree-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium
index b1bd858..1a32d457 100644
--- a/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium
@@ -1,6 +1,6 @@
 Name: Credentials
 Short Name: credentials
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/credentials/credentials/1.7.0-SNAPSHOT/credentials-1.7.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/credentials/credentials/1.7.0-SNAPSHOT/credentials-1.7.0-20260410.210759-1.aar
 Version: 1.7.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium
index 78cdfe8..bef61c8 100644
--- a/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium
@@ -1,6 +1,6 @@
 Name: Credentials Play Services Auth
 Short Name: credentials-play-services-auth
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/credentials/credentials-play-services-auth/1.7.0-SNAPSHOT/credentials-play-services-auth-1.7.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/credentials/credentials-play-services-auth/1.7.0-SNAPSHOT/credentials-play-services-auth-1.7.0-20260410.210759-1.aar
 Version: 1.7.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium
index 1a4fa93..1eca915 100644
--- a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.credentials.registry:registry-provider
 Short Name: registry-provider
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/credentials/registry/registry-provider/1.0.0-SNAPSHOT/registry-provider-1.0.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/credentials/registry/registry-provider/1.0.0-SNAPSHOT/registry-provider-1.0.0-20260410.210759-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium
index 996b2b3..b5e9b4f 100644
--- a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.credentials.registry:registry-provider-play-services
 Short Name: registry-provider-play-services
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/credentials/registry/registry-provider-play-services/1.0.0-SNAPSHOT/registry-provider-play-services-1.0.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/credentials/registry/registry-provider-play-services/1.0.0-SNAPSHOT/registry-provider-play-services-1.0.0-20260410.210759-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium b/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium
index 99e517e..4f20b998 100644
--- a/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium
@@ -1,6 +1,6 @@
 Name: Cursor Adapter
 Short Name: cursoradapter
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/cursoradapter/cursoradapter/1.1.0-SNAPSHOT/cursoradapter-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/cursoradapter/cursoradapter/1.1.0-SNAPSHOT/cursoradapter-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium
index 2627043..db0d9f30 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium
@@ -1,6 +1,6 @@
 Name: DataStore
 Short Name: datastore-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/datastore/datastore-android/1.3.0-SNAPSHOT/datastore-android-1.3.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/datastore/datastore-android/1.3.0-SNAPSHOT/datastore-android-1.3.0-20260410.210759-1.aar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium
index 1002abd1..04f999e 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium
@@ -1,6 +1,6 @@
 Name: DataStore Core
 Short Name: datastore-core-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/datastore/datastore-core-android/1.3.0-SNAPSHOT/datastore-core-android-1.3.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/datastore/datastore-core-android/1.3.0-SNAPSHOT/datastore-core-android-1.3.0-20260410.210759-1.aar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium
index 10ba20a..ccc8c9c 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium
@@ -1,6 +1,6 @@
 Name: DataStore Core Okio
 Short Name: datastore-core-okio-jvm
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/datastore/datastore-core-okio-jvm/1.3.0-SNAPSHOT/datastore-core-okio-jvm-1.3.0-20260410.132134-1.jar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/datastore/datastore-core-okio-jvm/1.3.0-SNAPSHOT/datastore-core-okio-jvm-1.3.0-20260410.210759-1.jar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium
index e11dda8..06d42695 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Preferences DataStore
 Short Name: datastore-preferences-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/datastore/datastore-preferences-android/1.3.0-SNAPSHOT/datastore-preferences-android-1.3.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/datastore/datastore-preferences-android/1.3.0-SNAPSHOT/datastore-preferences-android-1.3.0-20260410.210759-1.aar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium
index 30a0595c4..865da8b 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Preferences DataStore Core
 Short Name: datastore-preferences-core-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/datastore/datastore-preferences-core-android/1.3.0-SNAPSHOT/datastore-preferences-core-android-1.3.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/datastore/datastore-preferences-core-android/1.3.0-SNAPSHOT/datastore-preferences-core-android-1.3.0-20260410.210759-1.aar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium
index 9d7b29c..e4c686e 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium
@@ -1,6 +1,6 @@
 Name: Preferences External Protobuf
 Short Name: datastore-preferences-external-protobuf
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/datastore/datastore-preferences-external-protobuf/1.3.0-SNAPSHOT/datastore-preferences-external-protobuf-1.3.0-20260410.132134-1.jar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/datastore/datastore-preferences-external-protobuf/1.3.0-SNAPSHOT/datastore-preferences-external-protobuf-1.3.0-20260410.210759-1.jar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: BSD-3-Clause
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium
index 1f93a780..bb5167ee 100644
--- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium
@@ -1,6 +1,6 @@
 Name: Preferences DataStore Proto
 Short Name: datastore-preferences-proto
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/datastore/datastore-preferences-proto/1.3.0-SNAPSHOT/datastore-preferences-proto-1.3.0-20260410.132134-1.jar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/datastore/datastore-preferences-proto/1.3.0-SNAPSHOT/datastore-preferences-proto-1.3.0-20260410.210759-1.jar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium b/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium
index 5edf092..1bb19bfa 100644
--- a/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium
@@ -1,6 +1,6 @@
 Name: Drawer Layout
 Short Name: drawerlayout
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/drawerlayout/drawerlayout/1.3.0-SNAPSHOT/drawerlayout-1.3.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/drawerlayout/drawerlayout/1.3.0-SNAPSHOT/drawerlayout-1.3.0-20260410.210759-1.aar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_dynamicanimation_dynamicanimation/README.chromium b/third_party/androidx/committed/libs/androidx_dynamicanimation_dynamicanimation/README.chromium
index 3c37d594..dde8f83 100644
--- a/third_party/androidx/committed/libs/androidx_dynamicanimation_dynamicanimation/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_dynamicanimation_dynamicanimation/README.chromium
@@ -1,6 +1,6 @@
 Name: DynamicAnimation
 Short Name: dynamicanimation
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/dynamicanimation/dynamicanimation/1.2.0-SNAPSHOT/dynamicanimation-1.2.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/dynamicanimation/dynamicanimation/1.2.0-SNAPSHOT/dynamicanimation-1.2.0-20260410.210759-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium
index de0bff6..bd4b69b 100644
--- a/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium
@@ -1,6 +1,6 @@
 Name: fragment
 Short Name: fragment
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/fragment/fragment/1.9.0-SNAPSHOT/fragment-1.9.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/fragment/fragment/1.9.0-SNAPSHOT/fragment-1.9.0-20260410.210759-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium
index 84260ad..20d34263 100644
--- a/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium
@@ -1,6 +1,6 @@
 Name: Fragment Compose
 Short Name: fragment-compose
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/fragment/fragment-compose/1.9.0-SNAPSHOT/fragment-compose-1.9.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/fragment/fragment-compose/1.9.0-SNAPSHOT/fragment-compose-1.9.0-20260410.210759-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium
index 2bc8353..9c41e04 100644
--- a/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Fragment Kotlin Extensions
 Short Name: fragment-ktx
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/fragment/fragment-ktx/1.9.0-SNAPSHOT/fragment-ktx-1.9.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/fragment/fragment-ktx/1.9.0-SNAPSHOT/fragment-ktx-1.9.0-20260410.210759-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium
index 2d59a44..626d9d01 100644
--- a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium
@@ -1,6 +1,6 @@
 Name: Fragment Testing Extensions
 Short Name: fragment-testing
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/fragment/fragment-testing/1.9.0-SNAPSHOT/fragment-testing-1.9.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/fragment/fragment-testing/1.9.0-SNAPSHOT/fragment-testing-1.9.0-20260410.210759-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium
index 054b451..aec01214 100644
--- a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium
@@ -1,6 +1,6 @@
 Name: Fragment Testing Manifest dependency
 Short Name: fragment-testing-manifest
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/fragment/fragment-testing-manifest/1.9.0-SNAPSHOT/fragment-testing-manifest-1.9.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/fragment/fragment-testing-manifest/1.9.0-SNAPSHOT/fragment-testing-manifest-1.9.0-20260410.210759-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_graphics_graphics_core/README.chromium b/third_party/androidx/committed/libs/androidx_graphics_graphics_core/README.chromium
index 92e351b..932b0455 100644
--- a/third_party/androidx/committed/libs/androidx_graphics_graphics_core/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_graphics_graphics_core/README.chromium
@@ -1,6 +1,6 @@
 Name: Graphics Core
 Short Name: graphics-core
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/graphics/graphics-core/1.1.0-SNAPSHOT/graphics-core-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/graphics/graphics-core/1.1.0-SNAPSHOT/graphics-core-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium b/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium
index fa6bb6390..83ba096 100644
--- a/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium
@@ -1,6 +1,6 @@
 Name: Android Graphics Path
 Short Name: graphics-path
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/graphics/graphics-path/1.1.0-SNAPSHOT/graphics-path-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/graphics/graphics-path/1.1.0-SNAPSHOT/graphics-path-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_graphics_graphics_shapes_android/README.chromium b/third_party/androidx/committed/libs/androidx_graphics_graphics_shapes_android/README.chromium
index b813e12..0530bab 100644
--- a/third_party/androidx/committed/libs/androidx_graphics_graphics_shapes_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_graphics_graphics_shapes_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Graphics Shapes
 Short Name: graphics-shapes-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/graphics/graphics-shapes-android/1.2.0-SNAPSHOT/graphics-shapes-android-1.2.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/graphics/graphics-shapes-android/1.2.0-SNAPSHOT/graphics-shapes-android-1.2.0-20260410.210759-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_ink_ink_authoring_android/README.chromium b/third_party/androidx/committed/libs/androidx_ink_ink_authoring_android/README.chromium
index 00d45c90..8af5a8c 100644
--- a/third_party/androidx/committed/libs/androidx_ink_ink_authoring_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_ink_ink_authoring_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Ink Authoring
 Short Name: ink-authoring-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/ink/ink-authoring-android/1.1.0-SNAPSHOT/ink-authoring-android-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/ink/ink-authoring-android/1.1.0-SNAPSHOT/ink-authoring-android-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_ink_ink_brush_android/README.chromium b/third_party/androidx/committed/libs/androidx_ink_ink_brush_android/README.chromium
index fb2e4a7..7699ba4 100644
--- a/third_party/androidx/committed/libs/androidx_ink_ink_brush_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_ink_ink_brush_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Ink Brush
 Short Name: ink-brush-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/ink/ink-brush-android/1.1.0-SNAPSHOT/ink-brush-android-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/ink/ink-brush-android/1.1.0-SNAPSHOT/ink-brush-android-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_ink_ink_geometry_android/README.chromium b/third_party/androidx/committed/libs/androidx_ink_ink_geometry_android/README.chromium
index 870ce7c..1c2ce81a 100644
--- a/third_party/androidx/committed/libs/androidx_ink_ink_geometry_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_ink_ink_geometry_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Ink Geometry
 Short Name: ink-geometry-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/ink/ink-geometry-android/1.1.0-SNAPSHOT/ink-geometry-android-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/ink/ink-geometry-android/1.1.0-SNAPSHOT/ink-geometry-android-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_ink_ink_nativeloader_android/README.chromium b/third_party/androidx/committed/libs/androidx_ink_ink_nativeloader_android/README.chromium
index f785f96d..a9d3592 100644
--- a/third_party/androidx/committed/libs/androidx_ink_ink_nativeloader_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_ink_ink_nativeloader_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Ink Native Loader
 Short Name: ink-nativeloader-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/ink/ink-nativeloader-android/1.1.0-SNAPSHOT/ink-nativeloader-android-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/ink/ink-nativeloader-android/1.1.0-SNAPSHOT/ink-nativeloader-android-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_ink_ink_rendering_android/README.chromium b/third_party/androidx/committed/libs/androidx_ink_ink_rendering_android/README.chromium
index 3b5b97c..25679549 100644
--- a/third_party/androidx/committed/libs/androidx_ink_ink_rendering_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_ink_ink_rendering_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Ink Rendering
 Short Name: ink-rendering-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/ink/ink-rendering-android/1.1.0-SNAPSHOT/ink-rendering-android-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/ink/ink-rendering-android/1.1.0-SNAPSHOT/ink-rendering-android-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_ink_ink_strokes_android/README.chromium b/third_party/androidx/committed/libs/androidx_ink_ink_strokes_android/README.chromium
index c78fd6e..6f8f550 100644
--- a/third_party/androidx/committed/libs/androidx_ink_ink_strokes_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_ink_ink_strokes_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Ink Strokes
 Short Name: ink-strokes-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/ink/ink-strokes-android/1.1.0-SNAPSHOT/ink-strokes-android-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/ink/ink-strokes-android/1.1.0-SNAPSHOT/ink-strokes-android-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium b/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium
index 828ad08..6ee5744 100644
--- a/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium
@@ -1,6 +1,6 @@
 Name: Interpolators
 Short Name: interpolator
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/interpolator/interpolator/1.1.0-SNAPSHOT/interpolator-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/interpolator/interpolator/1.1.0-SNAPSHOT/interpolator-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_leanback_leanback/README.chromium b/third_party/androidx/committed/libs/androidx_leanback_leanback/README.chromium
index 8cad77c..ad63ada9 100644
--- a/third_party/androidx/committed/libs/androidx_leanback_leanback/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_leanback_leanback/README.chromium
@@ -1,6 +1,6 @@
 Name: Leanback
 Short Name: leanback
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/leanback/leanback/1.3.0-SNAPSHOT/leanback-1.3.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/leanback/leanback/1.3.0-SNAPSHOT/leanback-1.3.0-20260410.210759-1.aar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_leanback_leanback_grid/README.chromium b/third_party/androidx/committed/libs/androidx_leanback_leanback_grid/README.chromium
index 026ac1e..469da2e 100644
--- a/third_party/androidx/committed/libs/androidx_leanback_leanback_grid/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_leanback_leanback_grid/README.chromium
@@ -1,6 +1,6 @@
 Name: Leanback Grid
 Short Name: leanback-grid
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/leanback/leanback-grid/1.1.0-SNAPSHOT/leanback-grid-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/leanback/leanback-grid/1.1.0-SNAPSHOT/leanback-grid-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium
index 5ac7bb6..158d69f 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle-Common for Java 8
 Short Name: lifecycle-common-java8
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/lifecycle/lifecycle-common-java8/2.11.0-SNAPSHOT/lifecycle-common-java8-2.11.0-20260410.132134-1.jar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/lifecycle/lifecycle-common-java8/2.11.0-SNAPSHOT/lifecycle-common-java8-2.11.0-20260410.210759-1.jar
 Version: 2.11.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium
index 1211d15..575c6df 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle-Common
 Short Name: lifecycle-common-jvm
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/lifecycle/lifecycle-common-jvm/2.11.0-SNAPSHOT/lifecycle-common-jvm-2.11.0-20260410.132134-1.jar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/lifecycle/lifecycle-common-jvm/2.11.0-SNAPSHOT/lifecycle-common-jvm-2.11.0-20260410.210759-1.jar
 Version: 2.11.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium
index 9989518c..7a3fcd52 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle LiveData
 Short Name: lifecycle-livedata
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/lifecycle/lifecycle-livedata/2.11.0-SNAPSHOT/lifecycle-livedata-2.11.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/lifecycle/lifecycle-livedata/2.11.0-SNAPSHOT/lifecycle-livedata-2.11.0-20260410.210759-1.aar
 Version: 2.11.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium
index ff6ec18..577f8c79 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle LiveData Core
 Short Name: lifecycle-livedata-core
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core/2.11.0-SNAPSHOT/lifecycle-livedata-core-2.11.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core/2.11.0-SNAPSHOT/lifecycle-livedata-core-2.11.0-20260410.210759-1.aar
 Version: 2.11.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium
index 345a676..7b142f7 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: LiveData Core Kotlin Extensions
 Short Name: lifecycle-livedata-core-ktx
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core-ktx/2.11.0-SNAPSHOT/lifecycle-livedata-core-ktx-2.11.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core-ktx/2.11.0-SNAPSHOT/lifecycle-livedata-core-ktx-2.11.0-20260410.210759-1.aar
 Version: 2.11.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium
index cc1c5036..2b500b8 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: LiveData Kotlin Extensions
 Short Name: lifecycle-livedata-ktx
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/lifecycle/lifecycle-livedata-ktx/2.11.0-SNAPSHOT/lifecycle-livedata-ktx-2.11.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/lifecycle/lifecycle-livedata-ktx/2.11.0-SNAPSHOT/lifecycle-livedata-ktx-2.11.0-20260410.210759-1.aar
 Version: 2.11.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium
index ab409a8c..5fe820c2 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle Process
 Short Name: lifecycle-process
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/lifecycle/lifecycle-process/2.11.0-SNAPSHOT/lifecycle-process-2.11.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/lifecycle/lifecycle-process/2.11.0-SNAPSHOT/lifecycle-process-2.11.0-20260410.210759-1.aar
 Version: 2.11.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium
index 2b51690..e5a928f 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle Runtime
 Short Name: lifecycle-runtime-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/lifecycle/lifecycle-runtime-android/2.11.0-SNAPSHOT/lifecycle-runtime-android-2.11.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/lifecycle/lifecycle-runtime-android/2.11.0-SNAPSHOT/lifecycle-runtime-android-2.11.0-20260410.210759-1.aar
 Version: 2.11.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium
index f2b5969..583f132e 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle Runtime Compose
 Short Name: lifecycle-runtime-compose-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/lifecycle/lifecycle-runtime-compose-android/2.11.0-SNAPSHOT/lifecycle-runtime-compose-android-2.11.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/lifecycle/lifecycle-runtime-compose-android/2.11.0-SNAPSHOT/lifecycle-runtime-compose-android-2.11.0-20260410.210759-1.aar
 Version: 2.11.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium
index 1c7f711..c94bb1a6c 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle Kotlin Extensions
 Short Name: lifecycle-runtime-ktx-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/lifecycle/lifecycle-runtime-ktx-android/2.11.0-SNAPSHOT/lifecycle-runtime-ktx-android-2.11.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/lifecycle/lifecycle-runtime-ktx-android/2.11.0-SNAPSHOT/lifecycle-runtime-ktx-android-2.11.0-20260410.210759-1.aar
 Version: 2.11.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium
index 6dded24..7145b283 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle Service
 Short Name: lifecycle-service
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/lifecycle/lifecycle-service/2.11.0-SNAPSHOT/lifecycle-service-2.11.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/lifecycle/lifecycle-service/2.11.0-SNAPSHOT/lifecycle-service-2.11.0-20260410.210759-1.aar
 Version: 2.11.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium
index 5eaa4bf..880f31b 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle ViewModel
 Short Name: lifecycle-viewmodel-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-android/2.11.0-SNAPSHOT/lifecycle-viewmodel-android-2.11.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-android/2.11.0-SNAPSHOT/lifecycle-viewmodel-android-2.11.0-20260410.210759-1.aar
 Version: 2.11.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium
index 1ff8f74..ac5bcdc4 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle ViewModel Compose
 Short Name: lifecycle-viewmodel-compose-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-compose-android/2.11.0-SNAPSHOT/lifecycle-viewmodel-compose-android-2.11.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-compose-android/2.11.0-SNAPSHOT/lifecycle-viewmodel-compose-android-2.11.0-20260410.210759-1.aar
 Version: 2.11.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium
index 67e5cfe2..8fa3b79 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle ViewModel Kotlin Extensions
 Short Name: lifecycle-viewmodel-ktx
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-ktx/2.11.0-SNAPSHOT/lifecycle-viewmodel-ktx-2.11.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-ktx/2.11.0-SNAPSHOT/lifecycle-viewmodel-ktx-2.11.0-20260410.210759-1.aar
 Version: 2.11.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium
index 0de19b96..f7b7ca6 100644
--- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Lifecycle ViewModel with SavedState
 Short Name: lifecycle-viewmodel-savedstate-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-savedstate-android/2.11.0-SNAPSHOT/lifecycle-viewmodel-savedstate-android-2.11.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-savedstate-android/2.11.0-SNAPSHOT/lifecycle-viewmodel-savedstate-android-2.11.0-20260410.210759-1.aar
 Version: 2.11.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium b/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium
index 7691f57..28336225 100644
--- a/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium
@@ -1,6 +1,6 @@
 Name: loader
 Short Name: loader
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/loader/loader/1.2.0-SNAPSHOT/loader-1.2.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/loader/loader/1.2.0-SNAPSHOT/loader-1.2.0-20260410.210759-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_media_media/README.chromium b/third_party/androidx/committed/libs/androidx_media_media/README.chromium
index 1b66df04..00209011 100644
--- a/third_party/androidx/committed/libs/androidx_media_media/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_media_media/README.chromium
@@ -1,6 +1,6 @@
 Name: Media
 Short Name: media
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/media/media/1.8.0-SNAPSHOT/media-1.8.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/media/media/1.8.0-SNAPSHOT/media-1.8.0-20260410.210759-1.aar
 Version: 1.8.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_mediarouter_mediarouter/README.chromium b/third_party/androidx/committed/libs/androidx_mediarouter_mediarouter/README.chromium
index 34d363d..0ce22af 100644
--- a/third_party/androidx/committed/libs/androidx_mediarouter_mediarouter/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_mediarouter_mediarouter/README.chromium
@@ -1,6 +1,6 @@
 Name: MediaRouter
 Short Name: mediarouter
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/mediarouter/mediarouter/1.9.0-SNAPSHOT/mediarouter-1.9.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/mediarouter/mediarouter/1.9.0-SNAPSHOT/mediarouter-1.9.0-20260410.210759-1.aar
 Version: 1.9.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium
index e2d5027..c5d51708 100644
--- a/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Navigation Common
 Short Name: navigation-common-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/navigation/navigation-common-android/2.10.0-SNAPSHOT/navigation-common-android-2.10.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/navigation/navigation-common-android/2.10.0-SNAPSHOT/navigation-common-android-2.10.0-20260410.210759-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium
index aa9c7db1..9e02f73 100644
--- a/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Compose Navigation
 Short Name: navigation-compose-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/navigation/navigation-compose-android/2.10.0-SNAPSHOT/navigation-compose-android-2.10.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/navigation/navigation-compose-android/2.10.0-SNAPSHOT/navigation-compose-android-2.10.0-20260410.210759-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium
index 054d6a2..5fd4f38 100644
--- a/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Navigation Runtime
 Short Name: navigation-runtime-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/navigation/navigation-runtime-android/2.10.0-SNAPSHOT/navigation-runtime-android-2.10.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/navigation/navigation-runtime-android/2.10.0-SNAPSHOT/navigation-runtime-android-2.10.0-20260410.210759-1.aar
 Version: 2.10.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium
index 3e141809..3ba346e 100644
--- a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Navigation Event
 Short Name: navigationevent-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/navigationevent/navigationevent-android/1.1.0-SNAPSHOT/navigationevent-android-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/navigationevent/navigationevent-android/1.1.0-SNAPSHOT/navigationevent-android-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium
index 582f1e8..0a4dd33 100644
--- a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: NavigationEvent Compose
 Short Name: navigationevent-compose-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/navigationevent/navigationevent-compose-android/1.1.0-SNAPSHOT/navigationevent-compose-android-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/navigationevent/navigationevent-compose-android/1.1.0-SNAPSHOT/navigationevent-compose-android-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium
index b371a535..c50a04d5 100644
--- a/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Paging-Common
 Short Name: paging-common-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/paging/paging-common-android/3.5.0-SNAPSHOT/paging-common-android-3.5.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/paging/paging-common-android/3.5.0-SNAPSHOT/paging-common-android-3.5.0-20260410.210759-1.aar
 Version: 3.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium
index 2dc6a5c9..5430612 100644
--- a/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Paging-Common Kotlin Extensions
 Short Name: paging-common-ktx
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/paging/paging-common-ktx/3.5.0-SNAPSHOT/paging-common-ktx-3.5.0-20260410.132134-1.jar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/paging/paging-common-ktx/3.5.0-SNAPSHOT/paging-common-ktx-3.5.0-20260410.210759-1.jar
 Version: 3.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium
index 1b9bdfb..fb6309f 100644
--- a/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Paging-Compose
 Short Name: paging-compose-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/paging/paging-compose-android/3.5.0-SNAPSHOT/paging-compose-android-3.5.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/paging/paging-compose-android/3.5.0-SNAPSHOT/paging-compose-android-3.5.0-20260410.210759-1.aar
 Version: 3.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium
index 90fa9a2a..a58f61d 100644
--- a/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium
@@ -1,6 +1,6 @@
 Name: Paging-Runtime
 Short Name: paging-runtime
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/paging/paging-runtime/3.5.0-SNAPSHOT/paging-runtime-3.5.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/paging/paging-runtime/3.5.0-SNAPSHOT/paging-runtime-3.5.0-20260410.210759-1.aar
 Version: 3.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium b/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium
index 993f1ba..c3abb33 100644
--- a/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium
@@ -1,6 +1,6 @@
 Name: Palette
 Short Name: palette
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/palette/palette/1.1.0-SNAPSHOT/palette-1.1.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/palette/palette/1.1.0-SNAPSHOT/palette-1.1.0-20260410.210759-1.aar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_pdf_pdf_document_service/README.chromium b/third_party/androidx/committed/libs/androidx_pdf_pdf_document_service/README.chromium
index b33a74f..1cd4cca2 100644
--- a/third_party/androidx/committed/libs/androidx_pdf_pdf_document_service/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_pdf_pdf_document_service/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.pdf:pdf-document-service
 Short Name: pdf-document-service
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/pdf/pdf-document-service/1.0.0-SNAPSHOT/pdf-document-service-1.0.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/pdf/pdf-document-service/1.0.0-SNAPSHOT/pdf-document-service-1.0.0-20260410.210759-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_pdf_pdf_ink/README.chromium b/third_party/androidx/committed/libs/androidx_pdf_pdf_ink/README.chromium
index 095ae548..b13f16d4b 100644
--- a/third_party/androidx/committed/libs/androidx_pdf_pdf_ink/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_pdf_pdf_ink/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.pdf:pdf-ink
 Short Name: pdf-ink
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/pdf/pdf-ink/1.0.0-SNAPSHOT/pdf-ink-1.0.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/pdf/pdf-ink/1.0.0-SNAPSHOT/pdf-ink-1.0.0-20260410.210759-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer/README.chromium b/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer/README.chromium
index 392d2932..e524c43 100644
--- a/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.pdf:pdf-viewer
 Short Name: pdf-viewer
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/pdf/pdf-viewer/1.0.0-SNAPSHOT/pdf-viewer-1.0.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/pdf/pdf-viewer/1.0.0-SNAPSHOT/pdf-viewer-1.0.0-20260410.210759-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer_fragment/README.chromium b/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer_fragment/README.chromium
index 3365991..d431235 100644
--- a/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer_fragment/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer_fragment/README.chromium
@@ -1,6 +1,6 @@
 Name: androidx.pdf:pdf-viewer-fragment
 Short Name: pdf-viewer-fragment
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/pdf/pdf-viewer-fragment/1.0.0-SNAPSHOT/pdf-viewer-fragment-1.0.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/pdf/pdf-viewer-fragment/1.0.0-SNAPSHOT/pdf-viewer-fragment-1.0.0-20260410.210759-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium b/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium
index 703fe5b..b8795164 100644
--- a/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium
@@ -1,6 +1,6 @@
 Name: Preference
 Short Name: preference
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/preference/preference/1.3.0-SNAPSHOT/preference-1.3.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/preference/preference/1.3.0-SNAPSHOT/preference-1.3.0-20260410.210759-1.aar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium b/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium
index a3d0268..ee3c91f8 100644
--- a/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium
@@ -1,6 +1,6 @@
 Name: Profile Installer
 Short Name: profileinstaller
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/profileinstaller/profileinstaller/1.5.0-SNAPSHOT/profileinstaller-1.5.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/profileinstaller/profileinstaller/1.5.0-SNAPSHOT/profileinstaller-1.5.0-20260410.210759-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium b/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium
index d06bbb7..9d463ef 100644
--- a/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium
@@ -1,6 +1,6 @@
 Name: RecyclerView
 Short Name: recyclerview
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/recyclerview/recyclerview/1.5.0-SNAPSHOT/recyclerview-1.5.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/recyclerview/recyclerview/1.5.0-SNAPSHOT/recyclerview-1.5.0-20260410.210759-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium b/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium
index 1eb8f19..3280b5df 100644
--- a/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium
@@ -1,6 +1,6 @@
 Name: Resource Inspection - Annotations
 Short Name: resourceinspection-annotation
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/resourceinspection/resourceinspection-annotation/1.1.0-SNAPSHOT/resourceinspection-annotation-1.1.0-20260410.132134-1.jar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/resourceinspection/resourceinspection-annotation/1.1.0-SNAPSHOT/resourceinspection-annotation-1.1.0-20260410.210759-1.jar
 Version: 1.1.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium
index 52c4baa..e69fac5 100644
--- a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Saved State
 Short Name: savedstate-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/savedstate/savedstate-android/1.5.0-SNAPSHOT/savedstate-android-1.5.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/savedstate/savedstate-android/1.5.0-SNAPSHOT/savedstate-android-1.5.0-20260410.210759-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium
index bba9432..0dae8b30 100644
--- a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Saved State Compose
 Short Name: savedstate-compose-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/savedstate/savedstate-compose-android/1.5.0-SNAPSHOT/savedstate-compose-android-1.5.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/savedstate/savedstate-compose-android/1.5.0-SNAPSHOT/savedstate-compose-android-1.5.0-20260410.210759-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium
index fd0a85a..01b7fde 100644
--- a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: SavedState Kotlin Extensions
 Short Name: savedstate-ktx
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/savedstate/savedstate-ktx/1.5.0-SNAPSHOT/savedstate-ktx-1.5.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/savedstate/savedstate-ktx/1.5.0-SNAPSHOT/savedstate-ktx-1.5.0-20260410.210759-1.aar
 Version: 1.5.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium b/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium
index 61b16fb8..6bba4a0 100644
--- a/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium
@@ -1,6 +1,6 @@
 Name: Sliding Pane Layout
 Short Name: slidingpanelayout
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/slidingpanelayout/slidingpanelayout/1.3.0-SNAPSHOT/slidingpanelayout-1.3.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/slidingpanelayout/slidingpanelayout/1.3.0-SNAPSHOT/slidingpanelayout-1.3.0-20260410.210759-1.aar
 Version: 1.3.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_sqlite_sqlite_android/README.chromium b/third_party/androidx/committed/libs/androidx_sqlite_sqlite_android/README.chromium
index 5b18fbc7..bc86b759 100644
--- a/third_party/androidx/committed/libs/androidx_sqlite_sqlite_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_sqlite_sqlite_android/README.chromium
@@ -1,6 +1,6 @@
 Name: SQLite
 Short Name: sqlite-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/sqlite/sqlite-android/2.7.0-SNAPSHOT/sqlite-android-2.7.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/sqlite/sqlite-android/2.7.0-SNAPSHOT/sqlite-android-2.7.0-20260410.210759-1.aar
 Version: 2.7.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_sqlite_sqlite_framework_android/README.chromium b/third_party/androidx/committed/libs/androidx_sqlite_sqlite_framework_android/README.chromium
index 1662db8..98889c8f 100644
--- a/third_party/androidx/committed/libs/androidx_sqlite_sqlite_framework_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_sqlite_sqlite_framework_android/README.chromium
@@ -1,6 +1,6 @@
 Name: SQLite Framework Integration
 Short Name: sqlite-framework-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/sqlite/sqlite-framework-android/2.7.0-SNAPSHOT/sqlite-framework-android-2.7.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/sqlite/sqlite-framework-android/2.7.0-SNAPSHOT/sqlite-framework-android-2.7.0-20260410.210759-1.aar
 Version: 2.7.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium b/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium
index 9bdc7a3..b106d61 100644
--- a/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium
@@ -1,6 +1,6 @@
 Name: UIAutomator
 Short Name: uiautomator
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/test/uiautomator/uiautomator/2.4.0-SNAPSHOT/uiautomator-2.4.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/test/uiautomator/uiautomator/2.4.0-SNAPSHOT/uiautomator-2.4.0-20260410.210759-1.aar
 Version: 2.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator_shell_android/README.chromium b/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator_shell_android/README.chromium
index 08fbb0d..ce2ce76 100644
--- a/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator_shell_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator_shell_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Shell
 Short Name: uiautomator-shell-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/test/uiautomator/uiautomator-shell-android/2.4.0-SNAPSHOT/uiautomator-shell-android-2.4.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/test/uiautomator/uiautomator-shell-android/2.4.0-SNAPSHOT/uiautomator-shell-android-2.4.0-20260410.210759-1.aar
 Version: 2.4.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_tracing_tracing_android/README.chromium b/third_party/androidx/committed/libs/androidx_tracing_tracing_android/README.chromium
index 88dd2d82..701e6ee 100644
--- a/third_party/androidx/committed/libs/androidx_tracing_tracing_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_tracing_tracing_android/README.chromium
@@ -1,6 +1,6 @@
 Name: Tracing
 Short Name: tracing-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/tracing/tracing-android/2.0.0-SNAPSHOT/tracing-android-2.0.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/tracing/tracing-android/2.0.0-SNAPSHOT/tracing-android-2.0.0-20260410.210759-1.aar
 Version: 2.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_tracing_tracing_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_tracing_tracing_ktx/README.chromium
index 3344a7de..ee0d6a0b 100644
--- a/third_party/androidx/committed/libs/androidx_tracing_tracing_ktx/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_tracing_tracing_ktx/README.chromium
@@ -1,6 +1,6 @@
 Name: Tracing Kotlin Extensions
 Short Name: tracing-ktx
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/tracing/tracing-ktx/2.0.0-SNAPSHOT/tracing-ktx-2.0.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/tracing/tracing-ktx/2.0.0-SNAPSHOT/tracing-ktx-2.0.0-20260410.210759-1.aar
 Version: 2.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_transition_transition/README.chromium b/third_party/androidx/committed/libs/androidx_transition_transition/README.chromium
index b633ec3..c72fe10c 100644
--- a/third_party/androidx/committed/libs/androidx_transition_transition/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_transition_transition/README.chromium
@@ -1,6 +1,6 @@
 Name: Transition
 Short Name: transition
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/transition/transition/1.8.0-SNAPSHOT/transition-1.8.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/transition/transition/1.8.0-SNAPSHOT/transition-1.8.0-20260410.210759-1.aar
 Version: 1.8.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium b/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium
index 92fa037..0e53ff07 100644
--- a/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium
@@ -1,6 +1,6 @@
 Name: ViewPager2
 Short Name: viewpager2
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/viewpager2/viewpager2/1.2.0-SNAPSHOT/viewpager2-1.2.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/viewpager2/viewpager2/1.2.0-SNAPSHOT/viewpager2-1.2.0-20260410.210759-1.aar
 Version: 1.2.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium b/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium
index 2338ed7..eac06a6 100644
--- a/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium
@@ -1,6 +1,6 @@
 Name: Webkit
 Short Name: webkit
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/webkit/webkit/1.17.0-SNAPSHOT/webkit-1.17.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/webkit/webkit/1.17.0-SNAPSHOT/webkit-1.17.0-20260410.210759-1.aar
 Version: 1.17.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium b/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium
index a294bfb..bfacbe5 100644
--- a/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium
@@ -1,6 +1,6 @@
 Name: WindowManager Sidecar
 Short Name: sidecar
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/window/sidecar/sidecar/1.0.0-SNAPSHOT/sidecar-1.0.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/window/sidecar/sidecar/1.0.0-SNAPSHOT/sidecar-1.0.0-20260410.210759-1.aar
 Version: 1.0.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_window_window/README.chromium b/third_party/androidx/committed/libs/androidx_window_window/README.chromium
index 79d918a..53f2bc67 100644
--- a/third_party/androidx/committed/libs/androidx_window_window/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_window_window/README.chromium
@@ -1,6 +1,6 @@
 Name: WindowManager
 Short Name: window
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/window/window/1.6.0-SNAPSHOT/window-1.6.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/window/window/1.6.0-SNAPSHOT/window-1.6.0-20260410.210759-1.aar
 Version: 1.6.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_window_window_core_android/README.chromium b/third_party/androidx/committed/libs/androidx_window_window_core_android/README.chromium
index a5defa24..58195d3a 100644
--- a/third_party/androidx/committed/libs/androidx_window_window_core_android/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_window_window_core_android/README.chromium
@@ -1,6 +1,6 @@
 Name: WindowManager Core
 Short Name: window-core-android
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/window/window-core-android/1.6.0-SNAPSHOT/window-core-android-1.6.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/window/window-core-android/1.6.0-SNAPSHOT/window-core-android-1.6.0-20260410.210759-1.aar
 Version: 1.6.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_work_work_multiprocess/README.chromium b/third_party/androidx/committed/libs/androidx_work_work_multiprocess/README.chromium
index bf17a13d..2dd26033 100644
--- a/third_party/androidx/committed/libs/androidx_work_work_multiprocess/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_work_work_multiprocess/README.chromium
@@ -1,6 +1,6 @@
 Name: WorkManager Multiprocess
 Short Name: work-multiprocess
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/work/work-multiprocess/2.12.0-SNAPSHOT/work-multiprocess-2.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/work/work-multiprocess/2.12.0-SNAPSHOT/work-multiprocess-2.12.0-20260410.210759-1.aar
 Version: 2.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_work_work_runtime/README.chromium b/third_party/androidx/committed/libs/androidx_work_work_runtime/README.chromium
index 87f85ef5..dd864b4 100644
--- a/third_party/androidx/committed/libs/androidx_work_work_runtime/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_work_work_runtime/README.chromium
@@ -1,6 +1,6 @@
 Name: WorkManager Runtime
 Short Name: work-runtime
-URL: https://androidx.dev/snapshots/builds/15183400/artifacts/repository/androidx/work/work-runtime/2.12.0-SNAPSHOT/work-runtime-2.12.0-20260410.132134-1.aar
+URL: https://androidx.dev/snapshots/builds/15186230/artifacts/repository/androidx/work/work-runtime/2.12.0-SNAPSHOT/work-runtime-2.12.0-20260410.210759-1.aar
 Version: 2.12.0-SNAPSHOT
 Update Mechanism: Autoroll
 License: Apache-2.0
diff --git a/third_party/angle b/third_party/angle
index be2cb8a..1433dd4 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit be2cb8a0c5a71e8e0983a0222a292bf18d20d3d7
+Subproject commit 1433dd4e8a59659f8e16a96f62c9ccedd3ce2e92
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 58f9ae7..8f951dd 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -166,7 +166,7 @@
 // This is a kill switch for pausing microtask while the page is in the BFCache.
 // Remove by m148 if things go well.
 BASE_FEATURE(kBackForwardCachePauseMicrotasks,
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 // Enable background resource fetch in Blink. See https://crbug.com/1379780 for
 // more details.
diff --git a/third_party/blink/renderer/controller/BUILD.gn b/third_party/blink/renderer/controller/BUILD.gn
index b229de2..21ca0f0f5 100644
--- a/third_party/blink/renderer/controller/BUILD.gn
+++ b/third_party/blink/renderer/controller/BUILD.gn
@@ -192,6 +192,7 @@
 
   fuzztests = [
     "BlinkAttributeFuzzTestFixture.NoElementAttributeCrashes",
+    "AccessibilityDomScenarioRunner.AnimationAndAria",
     "AccessibilityDomScenarioRunner.HtmlAndAria",
     "AccessibilityDomScenarioRunner.CanvasFallbackContent",
     "AccessibilityDomScenarioRunner.SvgAndAria",
diff --git a/third_party/blink/renderer/core/html/forms/html_option_element.cc b/third_party/blink/renderer/core/html/forms/html_option_element.cc
index ceae28e..7443869 100644
--- a/third_party/blink/renderer/core/html/forms/html_option_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_option_element.cc
@@ -592,8 +592,7 @@
     CHECK(old_ancestor_select);
     const bool should_skip_option_removed =
         !parentNode() && insertion_point == old_ancestor_select;
-    if (!RuntimeEnabledFeatures::SelectChildrenRemovedFixEnabled() ||
-        !should_skip_option_removed) {
+    if (!should_skip_option_removed) {
       // If this option was removed from a select element as a direct child,
       // then let HTMLSelectElement::ChildrenChanged make the call to
       // OptionRemoved in order to avoid
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.cc b/third_party/blink/renderer/core/html/forms/html_select_element.cc
index ff757b0..4989fdd 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -1314,22 +1314,18 @@
       button_changed = true;
     } else if (auto* option =
                    DynamicTo<HTMLOptionElement>(change.sibling_changed)) {
-      if (RuntimeEnabledFeatures::SelectChildrenRemovedFixEnabled()) {
-        // OptionRemoved is normally called in HTMLOptionElement::RemovedFrom,
-        // but as a direct child we call OptionRemoved here in order to avoid
-        // https://issues.chromium.org/issues/444330901
-        OptionRemoved(*option);
-      }
+      // OptionRemoved is normally called in HTMLOptionElement::RemovedFrom,
+      // but as a direct child we call OptionRemoved here in order to avoid
+      // https://issues.chromium.org/issues/444330901
+      OptionRemoved(*option);
     }
   } else if (change.type == ChildrenChangeType::kAllChildrenRemoved) {
     for (Node* node : change.removed_nodes) {
       if (IsA<HTMLButtonElement>(node)) {
         button_changed = true;
       } else if (auto* option = DynamicTo<HTMLOptionElement>(node)) {
-        if (RuntimeEnabledFeatures::SelectChildrenRemovedFixEnabled()) {
-          // See comment in kElementRemoved case.
-          OptionRemoved(*option);
-        }
+        // See comment in kElementRemoved case.
+        OptionRemoved(*option);
       }
     }
   }
diff --git a/third_party/blink/renderer/core/html/resources/html.css b/third_party/blink/renderer/core/html/resources/html.css
index dd70bd1e..36bde9d 100644
--- a/third_party/blink/renderer/core/html/resources/html.css
+++ b/third_party/blink/renderer/core/html/resources/html.css
@@ -1041,7 +1041,7 @@
         block-size: -internal-auto-base(27px, auto);
         /* Same as native_theme_base when auto. */
         border: 1px solid -internal-auto-base(ButtonBorder, currentColor);
-        padding: -internal-auto-base(1px 2px, 0.25em 0.5em);
+        padding: -internal-auto-base(1px 2px, 0);
         cursor: -internal-auto-base(default, inherit);
         background-color: -internal-auto-base(ButtonFace, transparent);
         color: -internal-auto-base(ButtonText, inherit);
@@ -1052,6 +1052,7 @@
         display: -internal-auto-base(inline-block, inline-flex);
         gap: -internal-auto-base(unset, 0.5em);
         border-radius: -internal-auto-base(unset, 0.5em);
+        overflow: -internal-auto-base(revert-rule, clip);
     }
 }
 
@@ -1094,18 +1095,25 @@
     /* The swatch should not be affected by color scheme. */
     color-scheme: only light;
     background-color: #000000;
-    flex: 1;
     -webkit-user-modify: read-only !important;
     @supports not blink-feature(AppearanceBase) {
         border: 1px solid #777777;
         min-width: 0;
+        flex: 1;
     }
 
     @supports blink-feature(AppearanceBase) {
-        border: 1px solid -internal-auto-base(#777777, currentColor);
-        min-width: -internal-auto-base(0, initial);
-        inline-size: -internal-auto-base(unset, 2em);
-        box-sizing: -internal-auto-base(unset, border-box);
+        border: -internal-auto-base(1px solid #777777, revert-rule);
+        min-width: -internal-auto-base(0, revert-rule);
+        flex: -internal-auto-base(1, revert-rule);
+
+        color: -internal-auto-base(revert-rule, inherit);
+        font: -internal-auto-base(revert-rule, inherit);
+        box-sizing: -internal-auto-base(revert-rule, border-box);
+
+        inline-size: -internal-auto-base(revert-rule, stretch);
+        block-size: -internal-auto-base(revert-rule, stretch);
+        forced-color-adjust: -internal-auto-base(revert-rule, none);
     }
 }
 
diff --git a/third_party/blink/renderer/core/page/focus_controller.cc b/third_party/blink/renderer/core/page/focus_controller.cc
index 1415662b..42bae1f 100644
--- a/third_party/blink/renderer/core/page/focus_controller.cc
+++ b/third_party/blink/renderer/core/page/focus_controller.cc
@@ -2123,9 +2123,9 @@
 Element* FocusController::NextFocusableElementForIme(
     Element* element,
     const mojom::blink::FocusType focus_type) {
-  // TODO(ajith.v) Due to crbug.com/781026 when next/previous element is far
-  // from current element in terms of tabindex, then it's signalling CPU load.
-  // Will investigate further for a proper solution later.
+  // TODO(crbug.com/40551209): Due to crbug.com/781026 when next/previous
+  // element is far from current element in terms of tabindex, then it's
+  // signalling CPU load. Will investigate further for a proper solution later.
   static const int kFocusTraversalThreshold = 50;
   element->GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kFocus);
   auto* html_element = DynamicTo<HTMLElement>(element);
diff --git a/third_party/blink/renderer/modules/accessibility/testing/accessibility_dom_fuzztest.cc b/third_party/blink/renderer/modules/accessibility/testing/accessibility_dom_fuzztest.cc
index cec3d893..c9daea49 100644
--- a/third_party/blink/renderer/modules/accessibility/testing/accessibility_dom_fuzztest.cc
+++ b/third_party/blink/renderer/modules/accessibility/testing/accessibility_dom_fuzztest.cc
@@ -273,6 +273,44 @@
   }
 };
 
+class AnimationAndAria : public DomScenarioDomainSpecification {
+ public:
+  fuzztest::Domain<QualifiedName> AnyTag() override { return AnyHtmlTag(); }
+  fuzztest::Domain<std::pair<QualifiedName, std::string>>
+  AnyAttributeNameValuePair() override {
+    return fuzztest::OneOf(AnyHtmlAttributeNameValuePair(),
+                           AnyAriaAttributeNameValuePair());
+  }
+  fuzztest::Domain<std::string> AnyStyles() override {
+    auto animation_styles = fuzztest::Map(
+        [](const std::string& name, const std::string& duration,
+           const std::string& delay, const std::string& direction,
+           const std::string& fill_mode, const std::string& timing,
+           const std::string& count) {
+          return base::StrCat(
+              {"animation-name:", name, ";animation-duration:", duration,
+               ";animation-delay:", delay, ";animation-direction:", direction,
+               ";animation-fill-mode:", fill_mode,
+               ";animation-timing-function:", timing,
+               ";animation-iteration-count:", count});
+        },
+        AnyCSSAnimationNameValue(), AnyCSSAnimationDurationValue(),
+        AnyCSSAnimationDelayValue(), AnyCSSAnimationDirectionValue(),
+        AnyCSSAnimationFillModeValue(), AnyCSSAnimationTimingFunctionValue(),
+        AnyCSSAnimationIterationCountValue());
+    return animation_styles;
+  }
+  fuzztest::Domain<std::string> AnyText() override {
+    return fuzztest::PrintableAsciiString();
+  }
+  int GetMaxDomNodes() override { return 8; }
+  int GetMaxAttributesPerNode() override { return 3; }
+  fuzztest::Domain<QualifiedName> GetRootElementTag() override {
+    return fuzztest::Just<QualifiedName>(
+        html_names::TagToQualifiedName(html_names::HTMLTag::kBody));
+  }
+};
+
 class AccessibilityDomScenarioRunner : public DomScenarioRunner {
  public:
   AccessibilityDomScenarioRunner() = default;
@@ -283,6 +321,7 @@
   void MathMLAndAria(const DomScenario& input) { RunTest(input); }
   void ProblematicMarkup(const DomScenario& input) { RunTest(input); }
   void ValidTableAndAria(const DomScenario& input) { RunTest(input); }
+  void AnimationAndAria(const DomScenario& input) { RunTest(input); }
 
  protected:
   // Observer hooks to add accessibility tree printing.
@@ -327,4 +366,7 @@
 FUZZ_TEST_F(AccessibilityDomScenarioRunner, ValidTableAndAria)
     .WithDomains(BuildDomScenarios<ValidTableAndAria>());
 
+FUZZ_TEST_F(AccessibilityDomScenarioRunner, AnimationAndAria)
+    .WithDomains(BuildDomScenarios<AnimationAndAria>());
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/web_install/navigator_web_install.cc b/third_party/blink/renderer/modules/web_install/navigator_web_install.cc
index e0f0941..69b3b18 100644
--- a/third_party/blink/renderer/modules/web_install/navigator_web_install.cc
+++ b/third_party/blink/renderer/modules/web_install/navigator_web_install.cc
@@ -108,11 +108,10 @@
           script_state);
   ScriptPromise<WebInstallResult> promise = resolver->Promise();
 
-  CHECK(GetService());
-
   // `navigator.install()` was called.
   // Initiate installation of the current document.
   if (!manifest_id && !install_url) {
+    CHECK(GetService());
     GetService()->Install(
         /*options=*/nullptr,
         BindOnce(&blink::OnInstallResponse, WrapPersistent(resolver)));
@@ -141,6 +140,7 @@
     options->manifest_id = resolved_id;
   }
 
+  CHECK(GetService());
   GetService()->Install(std::move(options), BindOnce(&blink::OnInstallResponse,
                                                      WrapPersistent(resolver)));
   return promise;
diff --git a/third_party/blink/renderer/modules/web_install/navigator_web_install_test.cc b/third_party/blink/renderer/modules/web_install/navigator_web_install_test.cc
index e48bf955..b93df90a 100644
--- a/third_party/blink/renderer/modules/web_install/navigator_web_install_test.cc
+++ b/third_party/blink/renderer/modules/web_install/navigator_web_install_test.cc
@@ -243,4 +243,96 @@
   EXPECT_TRUE(promise.IsEmpty());
 }
 
+TEST_F(NavigatorWebInstallTest, EmptyInstallUrl) {
+  LocalFrame::NotifyUserActivation(
+      &GetFrame(), mojom::UserActivationNotificationType::kTest);
+
+  DummyExceptionStateForTesting exception_state;
+  auto promise = NavigatorWebInstall::install(GetScriptState(), *GetNavigator(),
+                                              String(""), exception_state);
+  ASSERT_FALSE(exception_state.HadException());
+
+  ScriptPromiseTester tester(GetScriptState(), promise);
+  tester.WaitUntilSettled();
+  EXPECT_TRUE(tester.IsRejected());
+}
+
+TEST_F(NavigatorWebInstallTest, InvalidInstallUrl) {
+  LocalFrame::NotifyUserActivation(
+      &GetFrame(), mojom::UserActivationNotificationType::kTest);
+
+  DummyExceptionStateForTesting exception_state;
+  auto promise = NavigatorWebInstall::install(
+      GetScriptState(), *GetNavigator(), String("://invalid"), exception_state);
+  ASSERT_FALSE(exception_state.HadException());
+
+  ScriptPromiseTester tester(GetScriptState(), promise);
+  tester.WaitUntilSettled();
+  EXPECT_TRUE(tester.IsRejected());
+}
+
+TEST_F(NavigatorWebInstallTest, WhitespaceOnlyInstallUrl) {
+  LocalFrame::NotifyUserActivation(
+      &GetFrame(), mojom::UserActivationNotificationType::kTest);
+
+  DummyExceptionStateForTesting exception_state;
+  auto promise = NavigatorWebInstall::install(GetScriptState(), *GetNavigator(),
+                                              String("   "), exception_state);
+  ASSERT_FALSE(exception_state.HadException());
+
+  ScriptPromiseTester tester(GetScriptState(), promise);
+  tester.WaitUntilSettled();
+  EXPECT_TRUE(tester.IsRejected());
+}
+
+TEST_F(NavigatorWebInstallTest, EmptyManifestId) {
+  LocalFrame::NotifyUserActivation(
+      &GetFrame(), mojom::UserActivationNotificationType::kTest);
+
+  DummyExceptionStateForTesting exception_state;
+  auto promise = NavigatorWebInstall::install(GetScriptState(), *GetNavigator(),
+                                              String("https://example.com"),
+                                              String(""), exception_state);
+  ASSERT_FALSE(exception_state.HadException());
+
+  ScriptPromiseTester tester(GetScriptState(), promise);
+  tester.WaitUntilSettled();
+  EXPECT_TRUE(tester.IsRejected());
+}
+
+TEST_F(NavigatorWebInstallTest, NullManifestId) {
+  LocalFrame::NotifyUserActivation(
+      &GetFrame(), mojom::UserActivationNotificationType::kTest);
+
+  DummyExceptionStateForTesting exception_state;
+  // When JavaScript passes null as a USVString argument, the WebIDL binding
+  // converts it via ToString(null) which produces the literal string "null".
+  auto promise = NavigatorWebInstall::install(GetScriptState(), *GetNavigator(),
+                                              String("https://example.com"),
+                                              String("null"), exception_state);
+  ASSERT_FALSE(exception_state.HadException());
+
+  ScriptPromiseTester tester(GetScriptState(), promise);
+  tester.WaitUntilSettled();
+  EXPECT_TRUE(tester.IsRejected());
+}
+
+TEST_F(NavigatorWebInstallTest, UndefinedManifestId) {
+  LocalFrame::NotifyUserActivation(
+      &GetFrame(), mojom::UserActivationNotificationType::kTest);
+
+  DummyExceptionStateForTesting exception_state;
+  // When JavaScript passes undefined as a USVString argument, the WebIDL
+  // binding converts it via ToString(undefined) which produces the literal
+  // string "undefined".
+  auto promise = NavigatorWebInstall::install(
+      GetScriptState(), *GetNavigator(), String("https://example.com"),
+      String("undefined"), exception_state);
+  ASSERT_FALSE(exception_state.HadException());
+
+  ScriptPromiseTester tester(GetScriptState(), promise);
+  tester.WaitUntilSettled();
+  EXPECT_TRUE(tester.IsRejected());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index abf574a..500d56a2 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -5059,14 +5059,6 @@
       },
     },
     {
-      // Replicates old behavior which removes selectedness from option
-      // elements when they are all removed from a select at the same time.
-      // https://issues.chromium.org/issues/444330901
-      // Enabled in M142, can be removed in M145.
-      name: "SelectChildrenRemovedFix",
-      status: "stable",
-    },
-    {
       // The selectedcontentelement attribute is for connecting select elements
       // to arbitrary <selectedcontent> elements.
       // https://github.com/openui/open-ui/issues/1063
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc
index a10304d..211e412 100644
--- a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.cc
@@ -26,7 +26,8 @@
   sequence_manager_->SetWorkBatchSize(4);
 }
 
-void SchedulerHelper::InitDefaultTaskRunner(
+void SchedulerHelper::InitDefaultTaskQueue(
+    base::sequence_manager::TaskQueue* queue,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
   default_task_runner_ = std::move(task_runner);
 
@@ -34,7 +35,8 @@
   // SchedulerHelper to a thread is fine. The default TaskRunner will be stored
   // in TLS by the ThreadController before tasks are executed.
   DCHECK(sequence_manager_);
-  sequence_manager_->SetDefaultTaskRunner(default_task_runner_);
+  sequence_manager_->SetDefaultTaskRunner(default_task_runner_,
+                                          queue->GetQueuePriority());
 }
 
 void SchedulerHelper::AttachToCurrentThread() {
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h
index dbb2c50..ae72ac3 100644
--- a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h
+++ b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h
@@ -37,7 +37,8 @@
   ~SchedulerHelper() override;
 
   // Must be called before invoking AttachToCurrentThread().
-  void InitDefaultTaskRunner(
+  void InitDefaultTaskQueue(
+      base::sequence_manager::TaskQueue* queue,
       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
 
   // Must be invoked before running any task from the scheduler, on the thread
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc
index fa6161b..418c50c 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc
@@ -27,8 +27,9 @@
                            MainThreadTaskQueue::QueueType::kControl)
                            .SetShouldNotifyObservers(false))) {
   control_task_queue_->SetQueuePriority(TaskPriority::kControlPriority);
-  InitDefaultTaskRunner(default_task_queue_->CreateTaskRunner(
-      TaskType::kMainThreadTaskQueueDefault));
+  InitDefaultTaskQueue(default_task_queue_->GetTaskQueue(),
+                       default_task_queue_->CreateTaskRunner(
+                           TaskType::kMainThreadTaskQueueDefault));
 
   sequence_manager_->EnableCrashKeys("blink_scheduler_async_stack");
 }
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
index da8e7215..8f0c481 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -313,6 +313,7 @@
   }
 
   scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner();
+  scoped_refptr<MainThreadTaskQueue> DefaultTaskQueue();
 
   scoped_refptr<SingleThreadIdleTaskRunner> IdleTaskRunner();
   base::TimeTicks NowTicks() const;
@@ -438,7 +439,6 @@
   Vector<base::OnceClosure>& GetOnTaskCompletionCallbacks() override;
 
   scoped_refptr<MainThreadTaskQueue> ControlTaskQueue();
-  scoped_refptr<MainThreadTaskQueue> DefaultTaskQueue();
   scoped_refptr<MainThreadTaskQueue> V8TaskQueue();
 
   virtual void PerformMicrotaskCheckpoint();
diff --git a/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_manager.cc b/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_manager.cc
index c3921592..79df05a 100644
--- a/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_manager.cc
+++ b/third_party/blink/renderer/platform/scheduler/test/fuzzer/thread_manager.cc
@@ -66,7 +66,7 @@
   TaskQueue::Spec spec = TaskQueue::Spec(QueueName::DEFAULT_TQ);
   task_queues_.emplace_back(
       MakeRefCounted<TaskQueueWithVoters>(manager_->CreateTaskQueue(spec)));
-  manager_->SetDefaultTaskRunner(task_queues_.back()->queue->task_runner());
+  manager_->SetDefaultTaskQueue(task_queues_.back()->queue.get());
 }
 
 ThreadManager::~ThreadManager() {
diff --git a/third_party/blink/renderer/platform/scheduler/test/task_environment.cc b/third_party/blink/renderer/platform/scheduler/test/task_environment.cc
index aa0b568..2a8e3ae4 100644
--- a/third_party/blink/renderer/platform/scheduler/test/task_environment.cc
+++ b/third_party/blink/renderer/platform/scheduler/test/task_environment.cc
@@ -18,7 +18,8 @@
   scheduler_helper_ = std::make_unique<scheduler::NonMainThreadSchedulerHelper>(
       sequence_manager(), nullptr, TaskType::kInternalTest);
   scheduler_helper_->AttachToCurrentThread();
-  DeferredInitFromSubclass(scheduler_helper_->DefaultTaskRunner());
+  DeferredInitFromSubclass(
+      scheduler_helper_->DefaultNonMainThreadTaskQueue()->GetTaskQueue());
 }
 
 }  // namespace blink::test
diff --git a/third_party/blink/renderer/platform/scheduler/test/task_environment.h b/third_party/blink/renderer/platform/scheduler/test/task_environment.h
index 757b03cc..22408821 100644
--- a/third_party/blink/renderer/platform/scheduler/test/task_environment.h
+++ b/third_party/blink/renderer/platform/scheduler/test/task_environment.h
@@ -45,7 +45,7 @@
     task_queue_ = sequence_manager()->CreateTaskQueue(
         base::sequence_manager::TaskQueue::Spec(
             base::sequence_manager::QueueName::TASK_ENVIRONMENT_DEFAULT_TQ));
-    DeferredInitFromSubclass(task_queue_->task_runner());
+    DeferredInitFromSubclass(task_queue_.get());
   }
 
   base::sequence_manager::TaskQueue::Handle CreateTaskQueue(
@@ -91,7 +91,7 @@
       : base::test::TaskEnvironment(std::move(scoped_task_environment)) {
     CHECK(IsMainThread());
     scheduler_ = std::make_unique<MainThreadScheduler>(sequence_manager());
-    DeferredInitFromSubclass(scheduler_->DefaultTaskRunner());
+    DeferredInitFromSubclass(scheduler_->DefaultTaskQueue()->GetTaskQueue());
   }
 
   std::unique_ptr<MainThreadScheduler> scheduler_;
diff --git a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_helper.cc b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_helper.cc
index b056a37..da54ab8 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_helper.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_helper.cc
@@ -32,7 +32,8 @@
   control_task_queue_->SetQueuePriority(TaskPriority::kControlPriority);
   input_task_queue_->SetQueuePriority(TaskPriority::kHighestPriority);
 
-  InitDefaultTaskRunner(
+  InitDefaultTaskQueue(
+      default_task_queue_->GetTaskQueue(),
       default_task_queue_->CreateTaskRunner(default_task_type));
 }
 
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc
index 82b7033f..5b675db3 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_impl_unittest.cc
@@ -141,7 +141,7 @@
         ThreadType::kTestThread, sequence_manager(), nullptr /* proxy */);
     scheduler_->Init();
     scheduler_->AttachToCurrentThread();
-    DeferredInitFromSubclass(scheduler_->DefaultTaskRunner());
+    DeferredInitFromSubclass(scheduler_->DefaultTaskQueue()->GetTaskQueue());
   }
 
   std::unique_ptr<WorkerThreadSchedulerForTest> scheduler_;
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc
index 3d11d96f..2b5f89ab 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc
@@ -150,7 +150,7 @@
         sequence_manager(), GetMockTickClock(), timeline_);
     scheduler_->Init();
     scheduler_->AttachToCurrentThread();
-    DeferredInitFromSubclass(scheduler_->DefaultTaskRunner());
+    DeferredInitFromSubclass(scheduler_->DefaultTaskQueue()->GetTaskQueue());
   }
 
   ~TaskEnvironmentWithWorkerThreadScheduler() override {
diff --git a/third_party/blink/renderer/platform/testing/task_environment.cc b/third_party/blink/renderer/platform/testing/task_environment.cc
index a5829930..f5990d03 100644
--- a/third_party/blink/renderer/platform/testing/task_environment.cc
+++ b/third_party/blink/renderer/platform/testing/task_environment.cc
@@ -36,7 +36,7 @@
   CHECK(IsMainThread());
   scheduler_ =
       std::make_unique<scheduler::MainThreadSchedulerImpl>(sequence_manager());
-  DeferredInitFromSubclass(scheduler_->DefaultTaskRunner());
+  DeferredInitFromSubclass(scheduler_->DefaultTaskQueue()->GetTaskQueue());
 
   main_thread_isolate_.emplace();
 
diff --git a/third_party/blink/renderer/platform/widget/widget_base.cc b/third_party/blink/renderer/platform/widget/widget_base.cc
index 3e76494..303e16d 100644
--- a/third_party/blink/renderer/platform/widget/widget_base.cc
+++ b/third_party/blink/renderer/platform/widget/widget_base.cc
@@ -869,13 +869,11 @@
 
   constexpr bool automatic_flushes = false;
   constexpr bool support_locking = false;
-  constexpr bool lose_context_when_out_of_memory = true;
 
   auto context_provider = viz::ContextProviderCommandBuffer::CreateForRaster(
       gpu_channel_host, kGpuStreamIdDefault, kGpuStreamPriorityDefault,
       GURL(params.url), automatic_flushes, support_locking, limits,
-      viz::command_buffer_metrics::ContextType::RENDERER_COMPOSITOR,
-      lose_context_when_out_of_memory);
+      viz::command_buffer_metrics::ContextType::RENDERER_COMPOSITOR);
 
 #if BUILDFLAG(IS_ANDROID)
   if (Platform::Current()->IsSynchronousCompositingEnabledForAndroidWebView() &&
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 2ed156543..87fe28f 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -5203,6 +5203,26 @@
     ],
     "expires": "Jan 1, 2027"
   },
+  {
+    "prefix": "web-install",
+    "platforms": [
+      "Linux",
+      "Mac",
+      "Win"
+    ],
+    "bases": [
+      "external/wpt/web-install"
+    ],
+    "exclusive_tests": "ALL",
+    "args": [
+      "--enable-features=WebAppInstallation"
+    ],
+    "owners": [
+      "pwa-team@google.com",
+      "liahiscock@microsoft.com"
+    ],
+    "expires": "Jan 1, 2028"
+  },
   "--webauthn-remote-desktop-support can be toggled with enterprise policies, ",
   "so we need to support this long term.",
   {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-forms/input-color-base-appearance-rendering-ref.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-forms/input-color-base-appearance-rendering-ref.html
similarity index 78%
rename from third_party/blink/web_tests/external/wpt/css/css-forms/input-color-base-appearance-rendering-ref.tentative.html
rename to third_party/blink/web_tests/external/wpt/css/css-forms/input-color-base-appearance-rendering-ref.html
index 41b2062..4e514b81 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-forms/input-color-base-appearance-rendering-ref.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-forms/input-color-base-appearance-rendering-ref.html
@@ -14,23 +14,21 @@
 
     border: 1px solid green;
 
-    padding-block: 0.25em;
-    padding-inline: 0.5em;
-
     border-radius: 0.5em;
 
     min-block-size: 24px;
     min-inline-size: 24px;
 
     display: inline-flex;
+
+    overflow: clip;
 }
 
 span {
     background-color: #ff0000;
-    flex: 1;
     display: block;
-    border: 1px solid green;
-    inline-size: 2em;
+    inline-size: stretch;
+    block-size: stretch;
     box-sizing: border-box;
 }
 </style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-forms/input-color-base-appearance-rendering.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-forms/input-color-base-appearance-rendering.html
similarity index 94%
rename from third_party/blink/web_tests/external/wpt/css/css-forms/input-color-base-appearance-rendering.tentative.html
rename to third_party/blink/web_tests/external/wpt/css/css-forms/input-color-base-appearance-rendering.html
index c124b6e..7961386 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-forms/input-color-base-appearance-rendering.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-forms/input-color-base-appearance-rendering.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <link rel=author href="mailto:lwarlow@igalia.com">
 <link rel=help href="https://drafts.csswg.org/css-forms-1">
-<link rel=match href="input-color-base-appearance-rendering-ref.tentative.html">
+<link rel=match href="input-color-base-appearance-rendering-ref.html">
 
 <input type="color" value="#ff0000" style="appearance:base">
 
diff --git a/third_party/blink/web_tests/external/wpt/web-install/navigator-install-bad-inputs.tentative.https.html b/third_party/blink/web_tests/external/wpt/web-install/navigator-install-bad-inputs.tentative.https.html
new file mode 100644
index 0000000..a5af5365
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-install/navigator-install-bad-inputs.tentative.https.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<title>navigator.install() rejects with TypeError for bad inputs</title>
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/WebInstall/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<body>
+<script>
+// These tests verify that navigator.install() rejects with TypeError for
+// malformed or invalid inputs. Each test requires a user activation via
+// test_driver.bless() since the activation is consumed by each call.
+
+promise_test(async function(t) {
+  await test_driver.bless('install with empty url');
+  return promise_rejects_js(t, TypeError, navigator.install(''),
+      'Empty install_url should reject with TypeError');
+}, 'navigator.install("") rejects with TypeError for empty install URL.');
+
+promise_test(async function(t) {
+  await test_driver.bless('install with invalid url');
+  return promise_rejects_js(t, TypeError, navigator.install('://invalid'),
+      'Invalid install_url should reject with TypeError');
+}, 'navigator.install("://invalid") rejects with TypeError for invalid URL.');
+
+promise_test(async function(t) {
+  await test_driver.bless('install with empty manifest_id');
+  return promise_rejects_js(t, TypeError,
+      navigator.install('https://example.com', ''),
+      'Empty manifest_id should reject with TypeError');
+}, 'navigator.install(url, "") rejects with TypeError for empty manifest ID.');
+
+promise_test(async function(t) {
+  await test_driver.bless('install with whitespace-only url');
+  return promise_rejects_js(t, TypeError, navigator.install('   '),
+      'Whitespace-only install_url should reject with TypeError');
+}, 'navigator.install("   ") rejects with TypeError for whitespace-only URL.');
+
+promise_test(async function(t) {
+  await test_driver.bless('install with null manifest_id');
+  return promise_rejects_js(t, TypeError,
+      navigator.install('https://example.com', null),
+      'Null manifest_id should reject with TypeError');
+}, 'navigator.install(url, null) rejects with TypeError for null manifest ID.');
+
+promise_test(async function(t) {
+  await test_driver.bless('install with undefined manifest_id');
+  var manifest_id;
+  return promise_rejects_js(t, TypeError,
+      navigator.install('https://example.com', manifest_id),
+      'Undefined manifest_id should reject with TypeError');
+}, 'navigator.install(url, undefined) rejects with TypeError for undefined ' +
+   'manifest ID.');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/web-install/navigator-install-defined.tentative.https.html b/third_party/blink/web_tests/external/wpt/web-install/navigator-install-defined.tentative.https.html
new file mode 100644
index 0000000..a085a9241
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-install/navigator-install-defined.tentative.https.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>navigator.install() is defined</title>
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/WebInstall/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+test(function() {
+  assert_true(typeof navigator.install === 'function',
+      'navigator.install should be defined');
+}, 'navigator.install() is defined.');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/web-install/navigator-install-sandboxed-iframe.tentative.https.html b/third_party/blink/web_tests/external/wpt/web-install/navigator-install-sandboxed-iframe.tentative.https.html
new file mode 100644
index 0000000..7296044
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-install/navigator-install-sandboxed-iframe.tentative.https.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<title>navigator.install() in sandboxed and non-sandboxed iframes</title>
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/WebInstall/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+var lastCallbackId = 0;
+var callbacks = {};
+
+function postMessageAndWaitResult(frame) {
+  return new Promise(function(resolve) {
+    var id = ++lastCallbackId;
+    callbacks[id] = resolve;
+    frame.contentWindow.postMessage({id: id}, '*');
+  });
+}
+
+window.onmessage = function(e) {
+  var message = e.data;
+  var id = message['id'];
+  var callback = callbacks[id];
+  delete callbacks[id];
+  callback(message);
+};
+
+function with_iframe(url) {
+  return new Promise(function(resolve) {
+    var frame = document.createElement('iframe');
+    frame.src = url;
+    frame.onload = function() { resolve(frame); };
+    document.body.appendChild(frame);
+  });
+}
+
+function with_sandboxed_iframe(url, sandbox) {
+  return new Promise(function(resolve) {
+    var frame = document.createElement('iframe');
+    frame.sandbox = sandbox;
+    frame.src = url;
+    frame.onload = function() { resolve(frame); };
+    document.body.appendChild(frame);
+  });
+}
+
+const helper_url = 'resources/navigator-install-iframe-helper.html';
+
+promise_test(function(t) {
+    return with_sandboxed_iframe(helper_url, 'allow-scripts')
+      .then(function(frame) {
+          t.add_cleanup(() => { frame.remove(); });
+          return postMessageAndWaitResult(frame);
+        })
+      .then(function(message) {
+          // In a sandboxed iframe without allow-same-origin, the origin is
+          // opaque. The permissions policy default allowlist of "self" does not
+          // include opaque origins, so the permissions policy check fails first
+          // with a SecurityError.
+          assert_equals(message.errorName, 'SecurityError',
+              'Should throw SecurityError in sandboxed iframe');
+        });
+  }, 'navigator.install() in sandboxed iframe (allow-scripts) should throw ' +
+     'SecurityError.');
+
+promise_test(function(t) {
+    return with_sandboxed_iframe(
+               helper_url, 'allow-scripts allow-same-origin')
+      .then(function(frame) {
+          t.add_cleanup(() => { frame.remove(); });
+          return postMessageAndWaitResult(frame);
+        })
+      .then(function(message) {
+          // With allow-same-origin, the iframe retains its real origin, so the
+          // permissions policy check passes. However, navigator.install()
+          // disallows ALL sandboxed contexts, so the sandbox flags check fails
+          // with a SecurityError.
+          assert_equals(message.errorName, 'SecurityError',
+              'Should throw SecurityError in sandboxed iframe even with ' +
+              'allow-same-origin');
+        });
+  }, 'navigator.install() in sandboxed iframe (allow-scripts ' +
+     'allow-same-origin) should throw SecurityError.');
+
+promise_test(function(t) {
+    return with_iframe(helper_url)
+      .then(function(frame) {
+          t.add_cleanup(() => { frame.remove(); });
+          return postMessageAndWaitResult(frame);
+        })
+      .then(function(message) {
+          // A regular same-origin iframe passes the permissions policy and
+          // sandbox checks, but fails the main-frame check with an
+          // InvalidStateError.
+          assert_equals(message.errorName, 'InvalidStateError',
+              'Should throw InvalidStateError in non-main-frame iframe');
+        });
+  }, 'navigator.install() in a same-origin iframe should throw ' +
+     'InvalidStateError.');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/web-install/navigator-install-user-activation.tentative.https.html b/third_party/blink/web_tests/external/wpt/web-install/navigator-install-user-activation.tentative.https.html
new file mode 100644
index 0000000..929c42f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-install/navigator-install-user-activation.tentative.https.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>navigator.install() requires user activation</title>
+<link rel="help" href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/WebInstall/explainer.md">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+promise_test(async function(t) {
+  await promise_rejects_dom(t, 'NotAllowedError', navigator.install(),
+      'navigator.install() without user activation should reject with ' +
+      'NotAllowedError');
+}, 'navigator.install() rejects with NotAllowedError without a user ' +
+   'activation.');
+
+promise_test(async function(t) {
+  await promise_rejects_dom(t, 'NotAllowedError',
+      navigator.install('https://example.com'),
+      'navigator.install(url) without user activation should reject with ' +
+      'NotAllowedError');
+}, 'navigator.install(url) rejects with NotAllowedError without a user ' +
+   'activation.');
+
+promise_test(async function(t) {
+  await promise_rejects_dom(t, 'NotAllowedError',
+      navigator.install('https://example.com', 'https://example.com'),
+      'navigator.install(url, id) without user activation should reject ' +
+      'with NotAllowedError');
+}, 'navigator.install(url, manifest_id) rejects with NotAllowedError ' +
+   'without a user activation.');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/web-install/resources/navigator-install-iframe-helper.html b/third_party/blink/web_tests/external/wpt/web-install/resources/navigator-install-iframe-helper.html
new file mode 100644
index 0000000..a08ebe50
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-install/resources/navigator-install-iframe-helper.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<script>
+// Helper page loaded inside iframes to test navigator.install() restrictions.
+// Receives a postMessage with {id}, attempts to call navigator.install(),
+// and reports the result back via postMessage.
+window.onmessage = function(e) {
+  const id = e.data['id'];
+
+  if (typeof navigator.install !== 'function') {
+    window.top.postMessage({
+        id: id,
+        result: 'navigator.install is not available'
+      }, '*');
+    return;
+  }
+
+  // navigator.install() returns a rejected promise for precondition failures
+  // (permissions policy, sandbox, main-frame, user activation).
+  navigator.install().then(function() {
+    window.top.postMessage({id: id, result: 'ok'}, '*');
+  }).catch(function(error) {
+    window.top.postMessage({
+        id: id,
+        result: 'navigator.install failed: ' + error.name,
+        errorName: error.name,
+        errorMessage: error.message
+      }, '*');
+  });
+};
+</script>
diff --git a/third_party/blink/web_tests/virtual/web-install/README.md b/third_party/blink/web_tests/virtual/web-install/README.md
new file mode 100644
index 0000000..83945074
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/web-install/README.md
@@ -0,0 +1,6 @@
+This virtual test suite runs web-install WPT tests with the
+WebAppInstallation feature enabled via --enable-features=WebAppInstallation.
+
+These tests cover navigator.install() precondition checks (sandbox, iframe,
+user activation restrictions) and input validation that can be tested without
+actually installing a web app.
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blend-mode-over-both-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blend-mode-over-both-ref.html
new file mode 100644
index 0000000..25ed726
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blend-mode-over-both-ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<head>
+  <title>drawElementImage applies CSS blend-mode correctly - ref</title>
+  <style>
+    #parent {
+      width: 100px;
+      height: 100px;
+      background: white;
+      border: 1px black solid;
+    }
+    #child1 {
+      margin: 0px;
+      width: 50px;
+      height: 50px;
+      background: blue;
+    }
+    #child2 {
+      margin-left: 10px;
+      margin-top: -40px;
+      width: 80px;
+      height: 80px;
+      background: green;
+      mix-blend-mode: difference;
+    }
+  </style>
+</head>
+<body>
+  <div id=parent>
+    <div id=child1></div>
+    <div id=child2></div>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blend-mode-over-both.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blend-mode-over-both.html
new file mode 100644
index 0000000..3b2e768
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blend-mode-over-both.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+  <title>drawElementImage applies CSS blend-mode correctly over canvas and dom content</title>
+  <link rel="help" href="https://github.com/WICG/html-in-canvas">
+  <link rel="author" href="mailto:schenney@chromium.org">
+  <link rel="match" href="css-blend-mode-over-both-ref.html">
+  <meta name="assert" value="CSS blend-mode is applied correctly over canvas and dom contentnt.">
+  <script src="/common/reftest-wait.js"></script>
+  <style>
+    canvas {
+      background: red;
+      border: 1px black solid;
+    }
+    #child1 {
+      width: 50px;
+      height: 50px;
+      background: blue;
+    }
+    #child2 {
+      width: 80px;
+      height: 80px;
+      background: green;
+      mix-blend-mode: difference;
+    }
+  </style>
+</head>
+<body>
+  <canvas id=canvas width="100" height="100" layoutsubtree>
+    <div id="child1">
+    </div>
+    <div id="child2">
+    </div>
+  </canvas>
+
+  <script>
+    function runTest() {
+      var context = canvas.getContext("2d");
+      context.fillStyle = 'white';
+      context.fillRect(0, 0, 100, 100);
+      context.drawElementImage(child1, 0, 0);
+      context.drawElementImage(child2, 10, 10);
+      takeScreenshot();
+    }
+    onload = () => requestAnimationFrame(() => setTimeout(runTest));
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blend-mode-over-content.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blend-mode-over-content.html
new file mode 100644
index 0000000..827e367
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blend-mode-over-content.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+  <title>drawElementImage applies CSS blend-mode correctly pver canvas content</title>
+  <link rel="help" href="https://github.com/WICG/html-in-canvas">
+  <link rel="author" href="mailto:schenney@chromium.org">
+  <link rel="match" href="css-blend-mode-ref.html">
+  <meta name="assert" value="CSS blend-mode is applied correctl over canvas content.">
+  <script src="/common/reftest-wait.js"></script>
+  <style>
+    canvas {
+      background: red;
+      border: 1px black solid;
+    }
+    div {
+      width: 80px;
+      height: 80px;
+      background: green;
+      mix-blend-mode: difference;
+    }
+  </style>
+</head>
+<body>
+  <canvas id=canvas width="100" height="100" layoutsubtree>
+    <div id="child">
+    </div>
+  </canvas>
+
+  <script>
+    function runTest() {
+      var context = canvas.getContext("2d");
+      context.fillStyle = 'white';
+      context.fillRect(0, 0, 100, 100);
+      context.drawElementImage(child, 10, 10);
+      takeScreenshot();
+    }
+    onload = () => requestAnimationFrame(() => setTimeout(runTest));
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blend-mode-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blend-mode-ref.html
new file mode 100644
index 0000000..c18f801
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blend-mode-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<head>
+  <title>drawElementImage applies CSS blend-mode correctly - ref</title>
+  <style>
+    div {
+      width: 100px;
+      height: 100px;
+      background: white;
+      border: 1px black solid;
+    }
+    div > div {
+      margin: 9px;
+      width: 80px;
+      height: 80px;
+      background: green;
+      mix-blend-mode: difference;
+    }
+  </style>
+</head>
+<body>
+  <div>
+    <div></div>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blend-mode.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blend-mode.html
new file mode 100644
index 0000000..76eb1f9
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blend-mode.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+  <title>drawElementImage applies CSS blend-mode correctly</title>
+  <link rel="help" href="https://github.com/WICG/html-in-canvas">
+  <link rel="author" href="mailto:schenney@chromium.org">
+  <link rel="match" href="css-blend-mode-ref.html">
+  <meta name="assert" value="CSS blend-mode is applied correctly.">
+  <script src="/common/reftest-wait.js"></script>
+  <style>
+    canvas {
+      background: red;
+      border: 1px black solid;
+    }
+    div {
+      width: 100px;
+      height: 100px;
+      background: white;
+    }
+    div > div {
+      margin: 10px;
+      width: 80px;
+      height: 80px;
+      background: green;
+      mix-blend-mode: difference;
+    }
+  </style>
+</head>
+<body>
+  <canvas id=canvas width="100" height="100" layoutsubtree>
+    <div id="child">
+      <div></div>
+    </div>
+  </canvas>
+
+  <script>
+    function runTest() {
+      var context = canvas.getContext("2d");
+      context.drawElementImage(child, 0, 0);
+      takeScreenshot();
+    }
+    onload = () => requestAnimationFrame(() => setTimeout(runTest));
+  </script>
+</body>
+</html>
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index c7e0068..2973377 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit c7e0068773af382ec75ce24a61e4c2b00062572c
+Subproject commit 29733770fe6d2dddcfba66a47c6578b3c800ddc5
diff --git a/third_party/fuzztest/BUILD.gn b/third_party/fuzztest/BUILD.gn
index a94ebc1..a506812 100644
--- a/third_party/fuzztest/BUILD.gn
+++ b/third_party/fuzztest/BUILD.gn
@@ -27,6 +27,7 @@
 
 config("fuzztest_internal_config") {
   cflags = [
+    "-Wno-unused-result",  #crbug.com/500385597
     "-Wno-unused-private-field",
     "-Wno-unreachable-code-return",
     "-Wno-unused-but-set-variable",
diff --git a/third_party/libyuv b/third_party/libyuv
index 5b5a2f6..5cfaa44 160000
--- a/third_party/libyuv
+++ b/third_party/libyuv
@@ -1 +1 @@
-Subproject commit 5b5a2f6b922bacdcd6dd2861381d91cd168edea6
+Subproject commit 5cfaa44d71e019363a715d55279abeeb14abd8f9
diff --git a/third_party/search_engines_data/resources_internal b/third_party/search_engines_data/resources_internal
index 76082ff..3ef61c2 160000
--- a/third_party/search_engines_data/resources_internal
+++ b/third_party/search_engines_data/resources_internal
@@ -1 +1 @@
-Subproject commit 76082ffbead660e13d1be3294881ce2ffb46fa94
+Subproject commit 3ef61c24de0bb0516d149c1b6821cd7e5192205f
diff --git a/third_party/skia b/third_party/skia
index 7c8b853..8cbf3db 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 7c8b85349a9ae1968fefd80d71e206ee608e3fe3
+Subproject commit 8cbf3db1a0dbd1f6e5e97811cd52daf678fc80c4
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index 2d1afb1..00e9e22 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit 2d1afb159766057defdd5d50f86e0fdc0f519456
+Subproject commit 00e9e22a37b18a1a7e913c394bc6c44b53da37cf
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src
index ba774d9..c89a5e4 160000
--- a/third_party/vulkan-validation-layers/src
+++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@
-Subproject commit ba774d998564105fa17460c1180f1ad1cbf55663
+Subproject commit c89a5e4faa2be7f66b8df40d310aeff9454aa1eb
diff --git a/third_party/wuffs/README.chromium b/third_party/wuffs/README.chromium
index fb398561..ad706728 100644
--- a/third_party/wuffs/README.chromium
+++ b/third_party/wuffs/README.chromium
@@ -1,9 +1,9 @@
 Name: Wuffs (Wrangling Untrusted File Formats Safely)
 Short name: Wuffs
 URL: https://github.com/google/wuffs-mirror-release-c
-Version: 0.3.3
-Revision: e3f919ccfe3ef542cfc983a82146070258fb57f8
-Update Mechanism: Manual
+Version: v0.3.4
+Revision: 50869df0ea703b4f41b238bfe26aec6ec9c86889
+Update Mechanism: Autoroll
 Security critical: yes
 License: Apache-2.0
 License File: src/LICENSE
diff --git a/third_party/wuffs/src b/third_party/wuffs/src
index e3f919c..50869df 160000
--- a/third_party/wuffs/src
+++ b/third_party/wuffs/src
@@ -1 +1 @@
-Subproject commit e3f919ccfe3ef542cfc983a82146070258fb57f8
+Subproject commit 50869df0ea703b4f41b238bfe26aec6ec9c86889
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index b2bbc8a5..d6dd3bf0 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -360,8 +360,8 @@
   <variant name="_ExplicitBrowserSigninPreferenceRemembered"
       summary="For Explicit Browser Signin Preference Remembered feature."/>
   <variant name="_ExploreSitesTile" summary="For Explore Sites feature."/>
-  <variant name="_ExtensionsManageMainMenuFeature"
-      summary="For managing your extensions from the main menu feature."/>
+  <variant name="_ExtensionsManageAppMenuFeature"
+      summary="For managing your extensions from the app menu feature."/>
   <variant name="_ExtensionsManageToolbarFeature"
       summary="For managing your extension feature in the toolbar."/>
   <variant name="_ExtensionsMenu" summary="For Extensions menu opening."/>
@@ -809,6 +809,9 @@
   <variant name="_TranslateMenuButton" summary="For translate menu button."/>
   <variant name="_UpdatedConnectionSecurityIndicators"
       summary="For updated omnibox connection security indicators."/>
+  <variant name="_VerticalTabsExpandOnHoverFeature"
+      summary="For showing the vertical tabs expand on hover feature when in
+               vertical tabs mode."/>
   <variant name="_VerticalTabstripTutorialFeature"
       summary="For showing the vertical tabs feature when the horizontal
                tabstrip is crowded."/>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index eb60cf1..72c10d8a 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -14007,6 +14007,7 @@
   <int value="-22506564" label="OptimizeDataUrls:disabled"/>
   <int value="-22311932"
       label="QuickIntensiveWakeUpThrottlingAfterLoading:disabled"/>
+  <int value="-21840927" label="TabGroupColorRefresh:disabled"/>
   <int value="-21771427" label="HistoryClustersPersistedClusters:disabled"/>
   <int value="-21693450" label="RetryGetVideoCaptureDeviceInfos:disabled"/>
   <int value="-20597579" label="UseMultiloginEndpoint:enabled"/>
@@ -20044,6 +20045,7 @@
   <int value="1982280338" label="IOSPromoAddressBubble:disabled"/>
   <int value="1982520858" label="XplatSyncedSetup:enabled"/>
   <int value="1982576646" label="PanelSelfRefresh2:disabled"/>
+  <int value="1983635890" label="TabGroupColorRefresh:enabled"/>
   <int value="1984029104" label="test-third-party-cookie-phaseout:enabled"/>
   <int value="1985040337" label="EnableFastTouchpadClick:enabled"/>
   <int value="1985567360" label="VulkanFromANGLE:enabled"/>
@@ -25849,6 +25851,7 @@
   <int value="12" label="OS_REPORTS_CAPTIVE_PORTAL"/>
   <int value="13" label="SHOW_BLOCKED_INTERCEPTION_INTERSTITIAL"/>
   <int value="14" label="SHOW_LEGACY_TLS_INTERSTITIAL"/>
+  <int value="15" label="SHOW_LOCAL_SELF_SIGNED_INTERSTITIAL"/>
 </enum>
 
 <enum name="SSLErrorTypes">
diff --git a/tools/metrics/histograms/metadata/android/enums.xml b/tools/metrics/histograms/metadata/android/enums.xml
index 5b75582c..be60390 100644
--- a/tools/metrics/histograms/metadata/android/enums.xml
+++ b/tools/metrics/histograms/metadata/android/enums.xml
@@ -1056,6 +1056,7 @@
   <int value="0" label="Messages Promo"/>
   <int value="1" label="Setting Card Promo"/>
   <int value="2" label="Educational Tip Promo"/>
+  <int value="3" label="FRE Promo"/>
 </enum>
 
 <!-- LINT.ThenChange(//chrome/browser/ui/android/default_browser_promo/java/src/org/chromium/chrome/browser/ui/default_browser_promo/DefaultBrowserPromoMetrics.java:DefaultBrowserPromoSourceType) -->
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 130b8ce..d2680259 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -1804,6 +1804,17 @@
   </summary>
 </histogram>
 
+<histogram name="Android.DefaultBrowserPromo.Outcome.FRE"
+    enum="AndroidDefaultBrowserPromoOutcomeType" expires_after="2027-04-09">
+  <owner>eleanorlee@google.com</owner>
+  <owner>twellington@chromium.org</owner>
+  <summary>
+    The final default browser state (outcome) after the user has been prompted
+    to set Chrome as the default via the First Run Experience. Recorded when the
+    Role Manager Dialog is dismissed.
+  </summary>
+</histogram>
+
 <histogram name="Android.DefaultBrowserPromo.Outcome.{CurrentState}"
     enum="AndroidDefaultBrowserPromoOutcomeType" expires_after="2026-08-30">
   <owner>lazzzis@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/browser/enums.xml b/tools/metrics/histograms/metadata/browser/enums.xml
index e4c6dfc34..70e35fb 100644
--- a/tools/metrics/histograms/metadata/browser/enums.xml
+++ b/tools/metrics/histograms/metadata/browser/enums.xml
@@ -321,7 +321,7 @@
   <int value="92" label="SEND_TAB_TO_SELF_INFOBAR_DELEGATE"/>
   <int value="93" label="TAB_SHARING_INFOBAR_DELEGATE"/>
   <int value="94" label="SAFETY_TIP_INFOBAR_DELEGATE"/>
-  <int value="95" label="SMS_RECEIVER_INFOBAR_DELEGATE"/>
+  <int value="95" label="SMS_RECEIVER_INFOBAR_DELEGATE (Obsolete)"/>
   <int value="96" label="KNOWN_INTERCEPTION_DISCLOSURE_INFOBAR_DELEGATE"/>
   <int value="97" label="SYNC_ERROR_INFOBAR_DELEGATE_ANDROID"/>
   <int value="98"
diff --git a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
index b081e91..95ec952 100644
--- a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
+++ b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
@@ -215,9 +215,9 @@
       summary="Confirmation message that the explicit browser signin
                preference was remembered"/>
   <variant name="IPH_ExploreSitesTile" summary="Explore Sites feature"/>
-  <variant name="IPH_ExtensionsManageMainMenuFeature"
+  <variant name="IPH_ExtensionsManageAppMenuFeature"
       summary="message shown informing users they can manage their extensions
-               in the main menu."/>
+               in the app menu."/>
   <variant name="IPH_ExtensionsManageToolbarFeature"
       summary="message shown informing users they can manage their extensions
                in the extensions menu found in the toolbar."/>
@@ -690,6 +690,8 @@
   <variant name="IPH_TranslateMenuButton" summary="translate menu button"/>
   <variant name="IPH_UpdatedConnectionSecurityIndicators"
       summary="updated omnibox connection security indicators"/>
+  <variant name="IPH_VerticalTabsExpandOnHoverFeature"
+      summary="The vertical tabstrip expand on hover IPH"/>
   <variant name="IPH_VerticalTabstripTutorialFeature"
       summary="The vertical tabstrip IPH"/>
   <variant name="IPH_VideoTutorial_NTP_ChromeIntro"
diff --git a/tools/metrics/histograms/metadata/glic/enums.xml b/tools/metrics/histograms/metadata/glic/enums.xml
index 373dd90..b71235f 100644
--- a/tools/metrics/histograms/metadata/glic/enums.xml
+++ b/tools/metrics/histograms/metadata/glic/enums.xml
@@ -215,6 +215,13 @@
 
 <!-- LINT.ThenChange(//chrome/browser/glic/glic_metrics.h:EntryPointStatus) -->
 
+<enum name="GlicFeatureDisabledReason">
+  <int value="0" label="Feature Flag Disabled"/>
+  <int value="1" label="Country Disabled"/>
+  <int value="2" label="Locale Disabled"/>
+  <int value="3" label="System Requirement Not Met"/>
+</enum>
+
 <!-- LINT.IfChange(GlicFilteringResult) -->
 
 <enum name="GlicFilteringResult">
diff --git a/tools/metrics/histograms/metadata/glic/histograms.xml b/tools/metrics/histograms/metadata/glic/histograms.xml
index 1316fb79..898f6a5b 100644
--- a/tools/metrics/histograms/metadata/glic/histograms.xml
+++ b/tools/metrics/histograms/metadata/glic/histograms.xml
@@ -696,6 +696,19 @@
   </summary>
 </histogram>
 
+<histogram name="Glic.GlobalEnabling.FeatureDisabledReason.{Suffix}"
+    enum="GlicFeatureDisabledReason" expires_after="2026-09-27">
+  <owner>cuianthony@chromium.org</owner>
+  <owner>glic@chromium.org</owner>
+  <summary>
+    Records specific reasons why the Glic feature is considered disabled. This
+    provides a granular breakdown for clients that would otherwise simply report
+    'Feature disabled' in Glic.ProfileEnablement.DisabledReason metrics.
+    Multiple reasons may be recorded per client. This is {Suffix}.
+  </summary>
+  <token key="Suffix" variants="ProfileEnablementSuffix"/>
+</histogram>
+
 <histogram name="Glic.Host.ClientSendMessageQueueLength" units="message count"
     expires_after="2027-02-06">
   <owner>harringtond@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/ios/enums.xml b/tools/metrics/histograms/metadata/ios/enums.xml
index e806c780..8f65168 100644
--- a/tools/metrics/histograms/metadata/ios/enums.xml
+++ b/tools/metrics/histograms/metadata/ios/enums.xml
@@ -1058,12 +1058,6 @@
 
 <!-- LINT.ThenChange(/ios/chrome/browser/default_browser/model/install_attribution/install_attribution_helper.h:InstallAttributionType) -->
 
-<enum name="IOSJavascriptContentBlockType">
-  <int value="0" label="Cookie"/>
-  <int value="1" label="Local Storage"/>
-  <int value="2" label="Session Storage"/>
-</enum>
-
 <!-- LINT.IfChange(IOSJavascriptErrorReportProcessorResult) -->
 
 <enum name="IOSJavascriptErrorReportProcessorResult">
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index 0eee72fa..f96e2f36 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -3511,17 +3511,6 @@
   </summary>
 </histogram>
 
-<histogram name="IOS.JavascriptContentBlockFailure"
-    enum="IOSJavascriptContentBlockType" expires_after="2020-12-31">
-  <owner>rkgibson@google.com</owner>
-  <owner>gambard@chromium.org</owner>
-  <summary>
-    IOS Content Blocking uses special injected Javascript to block access to
-    things like cookies and local storage. Future changes to iOS/WebKit could
-    cause these blocks to break. This logs that occurrence.
-  </summary>
-</histogram>
-
 <histogram name="IOS.LaunchSource" enum="IOSLaunchSource" expires_after="never">
 <!-- expires-never: Core metric to track how Chrome is launched -->
 
diff --git a/tools/metrics/histograms/metadata/mobile/enums.xml b/tools/metrics/histograms/metadata/mobile/enums.xml
index b8ae0325..ece2737d 100644
--- a/tools/metrics/histograms/metadata/mobile/enums.xml
+++ b/tools/metrics/histograms/metadata/mobile/enums.xml
@@ -402,6 +402,8 @@
       label="The user clicked on the |No thanks| button to decline history
              sync"/>
   <int value="15" label="Default browser promo shown"/>
+  <int value="16" label="The user accepted the default browser promo"/>
+  <int value="17" label="The user rejected the default browser promo"/>
 </enum>
 
 <!-- LINT.ThenChange(/chrome/browser/first_run/android/java/src/org/chromium/chrome/browser/firstrun/MobileFreProgress.java:MobileFreProgress) -->
diff --git a/tools/perf/core/bot_platforms.py b/tools/perf/core/bot_platforms.py
index 0bcc6b4..a44cec6 100644
--- a/tools/perf/core/bot_platforms.py
+++ b/tools/perf/core/bot_platforms.py
@@ -911,7 +911,7 @@
     },
     'android-pixel-fold-perf': {
         'description': 'Android U',
-        'num_shards': 10,
+        'num_shards': 8,
         'platform_os': 'android',
         'is_fyi': False
     },
diff --git a/tools/perf/core/shard_maps/android-pixel-fold-perf_map.json b/tools/perf/core/shard_maps/android-pixel-fold-perf_map.json
index 9bbac5f0..2cdbe15 100644
--- a/tools/perf/core/shard_maps/android-pixel-fold-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel-fold-perf_map.json
@@ -2,78 +2,80 @@
     "0": {
         "benchmarks": {
             "ad_frames.fencedframe": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "blink_perf.accessibility": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "blink_perf.bindings": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "blink_perf.css": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "blink_perf.dom": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "blink_perf.events": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "blink_perf.image_decoder": {
-                "end": 7,
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
+            },
+            "blink_perf.layout": {
+                "end": 56,
+                "abridged": false
             }
         }
     },
     "1": {
         "benchmarks": {
-            "blink_perf.image_decoder": {
-                "begin": 7,
-                "abridged": false,
-                "pageset_repeat": 1
-            },
             "blink_perf.layout": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "begin": 56,
+                "abridged": false
             },
             "blink_perf.owp_storage": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "blink_perf.parser": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "blink_perf.shadow_dom": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "blink_perf.webaudio": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "blink_perf.webcodecs": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "blink_perf.webgl": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "blink_perf.webgpu": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "blink_perf.webgpu_compat": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
+            },
+            "dummy_benchmark.noisy_benchmark_1": {
+                "abridged": false
+            },
+            "dummy_benchmark.stable_benchmark_1": {
+                "abridged": false
+            },
+            "jetstream2": {
+                "abridged": false
+            },
+            "media.mobile": {
+                "abridged": false
+            },
+            "rasterize_and_record_micro.top_25": {
+                "abridged": false
+            },
+            "rendering.mobile": {
+                "end": 15,
+                "abridged": false
             }
         },
         "executables": {
@@ -87,92 +89,59 @@
     },
     "2": {
         "benchmarks": {
-            "dummy_benchmark.noisy_benchmark_1": {
-                "abridged": false,
-                "pageset_repeat": 1
-            },
-            "dummy_benchmark.stable_benchmark_1": {
-                "abridged": false,
-                "pageset_repeat": 1
-            },
-            "jetstream2": {
-                "abridged": false,
-                "pageset_repeat": 1
-            },
-            "media.mobile": {
-                "abridged": false,
-                "pageset_repeat": 1
-            },
-            "rasterize_and_record_micro.top_25": {
-                "abridged": false,
-                "pageset_repeat": 1
-            },
             "rendering.mobile": {
-                "end": 72,
-                "abridged": false,
-                "pageset_repeat": 1
+                "begin": 15,
+                "end": 155,
+                "abridged": false
             }
         }
     },
     "3": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 72,
-                "end": 176,
-                "abridged": false,
-                "pageset_repeat": 1
+                "begin": 155,
+                "end": 286,
+                "abridged": false
             }
         }
     },
     "4": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 176,
-                "end": 287,
-                "abridged": false,
-                "pageset_repeat": 1
+                "begin": 286,
+                "abridged": false
+            },
+            "rendering.mobile.notracing": {
+                "abridged": false
             }
         }
     },
     "5": {
         "benchmarks": {
-            "rendering.mobile": {
-                "begin": 287,
-                "end": 382,
+            "speedometer3": {
+                "abridged": false
+            },
+            "speedometer3-future": {
+                "abridged": false
+            },
+            "startup.mobile": {
+                "abridged": false
+            },
+            "system_health.common_mobile": {
+                "abridged": false
+            },
+            "system_health.memory_mobile": {
+                "end": 17,
                 "abridged": false,
-                "pageset_repeat": 1
+                "pageset_repeat": 3
             }
         }
     },
     "6": {
         "benchmarks": {
-            "rendering.mobile": {
-                "begin": 382,
-                "abridged": false,
-                "pageset_repeat": 1
-            },
-            "rendering.mobile.notracing": {
-                "abridged": false,
-                "pageset_repeat": 1
-            },
-            "speedometer3": {
-                "abridged": false,
-                "pageset_repeat": 1
-            },
-            "speedometer3-future": {
-                "abridged": false,
-                "pageset_repeat": 1
-            },
-            "startup.mobile": {
-                "abridged": false,
-                "pageset_repeat": 1
-            },
-            "system_health.common_mobile": {
-                "abridged": false,
-                "pageset_repeat": 1
-            },
             "system_health.memory_mobile": {
-                "end": 2,
+                "begin": 17,
+                "end": 61,
                 "abridged": false,
                 "pageset_repeat": 3
             }
@@ -181,27 +150,7 @@
     "7": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 2,
-                "end": 35,
-                "abridged": false,
-                "pageset_repeat": 3
-            }
-        }
-    },
-    "8": {
-        "benchmarks": {
-            "system_health.memory_mobile": {
-                "begin": 35,
-                "end": 71,
-                "abridged": false,
-                "pageset_repeat": 3
-            }
-        }
-    },
-    "9": {
-        "benchmarks": {
-            "system_health.memory_mobile": {
-                "begin": 71,
+                "begin": 61,
                 "abridged": false,
                 "pageset_repeat": 3
             },
@@ -210,38 +159,32 @@
                 "pageset_repeat": 20
             },
             "v8.browsing_mobile": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "v8.browsing_mobile-future": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "wasmpspdfkit": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             },
             "webrtc": {
-                "abridged": false,
-                "pageset_repeat": 1
+                "abridged": false
             }
         }
     },
     "extra_infos": {
         "num_stories": 1117,
-        "predicted_min_shard_time": 2892.0,
-        "predicted_min_shard_index": 8,
-        "predicted_max_shard_time": 2964.0,
-        "predicted_max_shard_index": 9,
-        "shard #0": 2913.0,
-        "shard #1": 2924.0,
-        "shard #2": 2907.0,
-        "shard #3": 2917.0,
-        "shard #4": 2930.0,
-        "shard #5": 2901.0,
-        "shard #6": 2915.0,
-        "shard #7": 2893.0,
-        "shard #8": 2892.0,
-        "shard #9": 2964.0
+        "predicted_min_shard_time": 3553.0,
+        "predicted_min_shard_index": 5,
+        "predicted_max_shard_time": 3702.0,
+        "predicted_max_shard_index": 6,
+        "shard #0": 3641.0,
+        "shard #1": 3641.0,
+        "shard #2": 3622.0,
+        "shard #3": 3650.0,
+        "shard #4": 3651.0,
+        "shard #5": 3553.0,
+        "shard #6": 3702.0,
+        "shard #7": 3696.0
     }
 }
diff --git a/ui/android/javatests/src/org/chromium/ui/theme/ThemeResourceWrapperJavaUnitTest.java b/ui/android/javatests/src/org/chromium/ui/theme/ThemeResourceWrapperJavaUnitTest.java
index ddbbfad..6378586 100644
--- a/ui/android/javatests/src/org/chromium/ui/theme/ThemeResourceWrapperJavaUnitTest.java
+++ b/ui/android/javatests/src/org/chromium/ui/theme/ThemeResourceWrapperJavaUnitTest.java
@@ -28,13 +28,13 @@
 import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Batch;
-import org.chromium.base.test.util.DisableIf;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.ui.R;
-import org.chromium.ui.base.DeviceFormFactor;
 
 /** Java test show case how to use the wrapper. */
 @Batch(Batch.PER_CLASS)
 @RunWith(BaseJUnit4ClassRunner.class)
+@DisabledTest(message = "crbug.com/499024053")
 public class ThemeResourceWrapperJavaUnitTest {
 
     @Rule
@@ -55,7 +55,6 @@
 
     @Test
     @SmallTest
-    @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // https://crbug.com/499024053
     public void setThemeForOriginalActivity() {
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
@@ -74,7 +73,6 @@
 
     @Test
     @SmallTest
-    @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // https://crbug.com/499024053
     public void setThemeNoOpWithThemeResourceWrapper() {
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
@@ -100,7 +98,6 @@
 
     @Test
     @SmallTest
-    @DisableIf.Device(DeviceFormFactor.DESKTOP_FREEFORM) // https://crbug.com/499024053
     public void themeWrapperNoImpactOnOtherAttribute() {
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index e4b9053..fc7c0b7 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -1003,6 +1003,14 @@
   ]
 }
 
+if (is_win) {
+  executable("shell_test_helper") {
+    testonly = true
+    sources = [ "win/shell_test_helper.cc" ]
+    deps = [ "//base" ]
+  }
+}
+
 test("ui_base_unittests") {
   use_xvfb = use_xvfb_in_this_config
 
@@ -1244,6 +1252,7 @@
       "dragdrop/os_exchange_data_win_unittest.cc",
       "view_prop_unittest.cc",
       "win/hwnd_subclass_unittest.cc",
+      "win/shell_unittest.cc",
       "win/win_cursor_factory_unittest.cc",
     ]
     deps += [
@@ -1251,6 +1260,7 @@
       "//ui/base/cursor/mojom:cursor_type_shared",
       "//ui/base/idle:unit_tests",
     ]
+    data_deps += [ ":shell_test_helper" ]
   }
 
   if (is_android) {
diff --git a/ui/base/win/shell.cc b/ui/base/win/shell.cc
index 1354d5f..347558b 100644
--- a/ui/base/win/shell.cc
+++ b/ui/base/win/shell.cc
@@ -29,11 +29,11 @@
 
 namespace ui::win {
 
-namespace {
-
 BASE_FEATURE(kManuallyParsePathForShellExecute,
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+namespace {
+
 // Default ShellExecuteEx flags used with "openas", "explore", and default
 // verbs.
 //
diff --git a/ui/base/win/shell.h b/ui/base/win/shell.h
index f631169..0de77a7 100644
--- a/ui/base/win/shell.h
+++ b/ui/base/win/shell.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/component_export.h"
+#include "base/feature_list.h"
 #include "base/win/windows_types.h"
 
 namespace base {
@@ -16,6 +17,9 @@
 
 namespace ui::win {
 
+COMPONENT_EXPORT(UI_BASE)
+BASE_DECLARE_FEATURE(kManuallyParsePathForShellExecute);
+
 // Open the folder at |full_path| via the Windows shell. It is an error if
 // |full_path| does not refer to a folder.
 //
diff --git a/ui/base/win/shell_test_helper.cc b/ui/base/win/shell_test_helper.cc
new file mode 100644
index 0000000..a3f6c53a
--- /dev/null
+++ b/ui/base/win/shell_test_helper.cc
@@ -0,0 +1,17 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/containers/span.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+
+int main(int argc, char* argv[]) {
+  // Write a sentinel file to the current directory to indicate success.
+  if (!base::WriteFile(base::FilePath(L"executed.txt"),
+                       base::as_byte_span("EXECUTED"))) {
+    return 1;
+  }
+
+  return 0;
+}
diff --git a/ui/base/win/shell_unittest.cc b/ui/base/win/shell_unittest.cc
new file mode 100644
index 0000000..61e1a0b
--- /dev/null
+++ b/ui/base/win/shell_unittest.cc
@@ -0,0 +1,115 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/win/shell.h"
+
+#include <shlobj.h>
+
+#include "base/base_paths.h"
+#include "base/files/file_path_watcher.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/functional/bind.h"
+#include "base/functional/callback_helpers.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/task/thread_pool.h"
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_timeouts.h"
+#include "base/win/scoped_co_mem.h"
+#include "base/win/scoped_com_initializer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui::win {
+
+class ShellTest : public testing::TestWithParam<bool> {
+ public:
+  ShellTest() {
+    feature_list_.InitWithFeatureState(kManuallyParsePathForShellExecute,
+                                       GetParam());
+  }
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  base::test::ScopedFeatureList feature_list_;
+};
+
+TEST_P(ShellTest, OpenFileWithSpaceInExtension) {
+  base::win::ScopedCOMInitializer com_initializer;
+  ASSERT_TRUE(com_initializer.Succeeded());
+
+  const auto FeatureEnabled = GetParam;
+
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  // Create a file with a name that triggers legacy resolution (space in
+  // extension).
+  const base::FilePath file_path = temp_dir.GetPath().Append(L"test. txt");
+  ASSERT_TRUE(base::WriteFile(file_path, "test content"));
+
+  // Find the helper executable.
+  base::FilePath exe_path;
+  ASSERT_TRUE(base::PathService::Get(base::DIR_EXE, &exe_path));
+  exe_path = exe_path.Append(L"shell_test_helper.exe");
+  ASSERT_TRUE(base::PathExists(exe_path));
+
+  // Create the "legacy resolution" target that ShellExecute might mistakenly
+  // pick.
+  base::FilePath legacy_exe_path = temp_dir.GetPath().Append(L"test. txt.exe");
+  ASSERT_TRUE(base::CopyFile(exe_path, legacy_exe_path));
+
+  // The helper will create "executed.txt" in its current directory.
+  // ui::win::OpenFileViaShell sets the working directory to the file's DirName.
+  base::FilePath executed_sentinel = temp_dir.GetPath().Append(L"executed.txt");
+  ASSERT_FALSE(base::PathExists(executed_sentinel));
+
+  if (FeatureEnabled()) {
+    // When feature is enabled, ui::win uses SHParseDisplayName.
+    // Verify that SHParseDisplayName resolves to the correct file.
+    base::win::ScopedCoMem<ITEMIDLIST_ABSOLUTE> path_id_list;
+    ASSERT_TRUE(SUCCEEDED(::SHParseDisplayName(file_path.value().c_str(),
+                                               nullptr, &path_id_list,
+                                               SFGAO_FILESYSTEM, nullptr)));
+
+    wchar_t parsed_path[MAX_PATH];
+    ASSERT_TRUE(::SHGetPathFromIDList(path_id_list.get(), parsed_path));
+    base::FilePath parsed(parsed_path);
+    EXPECT_EQ(file_path, parsed);
+  }
+
+  base::RunLoop sentinel_run_loop;
+  base::FilePathWatcher watcher;
+  // Watch the directory for the creation of the sentinel file.
+  ASSERT_TRUE(watcher.Watch(
+      temp_dir.GetPath(), base::FilePathWatcher::Type::kNonRecursive,
+      base::BindLambdaForTesting([&](const base::FilePath& path, bool error) {
+        if (!error && base::PathExists(path.Append(L"executed.txt"))) {
+          sentinel_run_loop.Quit();
+        }
+      })));
+
+  ASSERT_TRUE(OpenFileViaShell(file_path));
+
+  if (FeatureEnabled()) {
+    // Feature enabled: it will not execute the exe, the openwith dialog will
+    // appear instead, which the test cannot easily check for. The runloop still
+    // needs to exit, so wait the time it might take to launch a process, then
+    // terminate it.
+    base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
+        FROM_HERE, sentinel_run_loop.QuitClosure(), base::Seconds(3));
+  }
+
+  sentinel_run_loop.Run();
+
+  EXPECT_EQ(FeatureEnabled(), !base::PathExists(executed_sentinel));
+}
+
+INSTANTIATE_TEST_SUITE_P(, ShellTest, testing::Bool(), [](const auto& info) {
+  return info.param ? "ManuallyParsed" : "AutomaticallyParsed";
+});
+
+}  // namespace ui::win
diff --git a/ui/webui/resources/cr_components/composebox/file_thumbnail.html.ts b/ui/webui/resources/cr_components/composebox/file_thumbnail.html.ts
index 3a253ba..7b71e79 100644
--- a/ui/webui/resources/cr_components/composebox/file_thumbnail.html.ts
+++ b/ui/webui/resources/cr_components/composebox/file_thumbnail.html.ts
@@ -88,7 +88,7 @@
           </cr-icon-button>`: ''}
         </div>
       ` : this.file.url ? html`
-        <div id="tabChip" class="chip">
+        <div id="tabChip" class="chip" title="${this.file.name}">
           <div id="tabThumbnail" class="thumbnail">
             <cr-composebox-tab-favicon .url="${this.file.url}" .size="${24}">
             </cr-composebox-tab-favicon>
diff --git a/ui/webui/resources/cr_components/searchbox/OWNERS b/ui/webui/resources/cr_components/searchbox/OWNERS
index ebb8737..13d86b94 100644
--- a/ui/webui/resources/cr_components/searchbox/OWNERS
+++ b/ui/webui/resources/cr_components/searchbox/OWNERS
@@ -8,6 +8,9 @@
 rtatum@google.com
 tiborg@chromium.org
 
+# For merges only
+jdonnelly@chromium.org
+
 # TODO(crbug.com/493909140): Remove these per-file entries and the
 # EXTENDED_OWNERS file.
 per-file *.css=file:EXTENDED_OWNERS
diff --git a/ui/webui/resources/cr_components/searchbox/searchbox.css b/ui/webui/resources/cr_components/searchbox/searchbox.css
index c80ad97..c7256bf 100644
--- a/ui/webui/resources/cr_components/searchbox/searchbox.css
+++ b/ui/webui/resources/cr_components/searchbox/searchbox.css
@@ -36,22 +36,6 @@
   --cr-searchbox-shadow: none;
 }
 
-:host-context([searchbox-width-behavior_='revert']):host([can-show-secondary-side]:not([dropdown-is-visible])) {
-  --cr-searchbox-width: var(--cr-searchbox-min-width);
-}
-
-/**
- * Show the secondary side if it can be shown and is currently available to be
- * shown.
- */
-:host([can-show-secondary-side][has-secondary-side]) {
-  --cr-searchbox-secondary-side-display: block;
-}
-
-:host([is-dark]) {
-  --cr-searchbox-shadow: 0 2px 6px 0 var(--color-searchbox-shadow);
-}
-
 :host([searchbox-voice-search-enabled_]) {
   --cr-searchbox-voice-search-button-width: var(--cr-searchbox-icon-width);
 }
diff --git a/ui/webui/resources/cr_components/searchbox/searchbox.ts b/ui/webui/resources/cr_components/searchbox/searchbox.ts
index ae8e057..aabcf3c 100644
--- a/ui/webui/resources/cr_components/searchbox/searchbox.ts
+++ b/ui/webui/resources/cr_components/searchbox/searchbox.ts
@@ -75,11 +75,6 @@
         reflect: true,
       },
 
-      colorSourceIsBaseline: {
-        type: Boolean,
-        reflect: true,
-      },
-
       /**
        * Whether the secondary side was at any point available to be shown.
        */
@@ -97,12 +92,6 @@
         reflect: true,
       },
 
-      /** Whether the theme is dark. */
-      isDark: {
-        type: Boolean,
-        reflect: true,
-      },
-
       searchboxChromeRefreshTheming: {
         type: Boolean,
         reflect: true,
@@ -113,11 +102,6 @@
         reflect: true,
       },
 
-      searchboxLayoutMode: {
-        type: String,
-        reflect: true,
-      },
-
       placeholderText: {
         type: String,
         reflect: true,
@@ -162,15 +146,12 @@
   // being needed for regular searchbox
   accessor showThumbnail: boolean = false;
   accessor canShowSecondarySide: boolean = false;
-  accessor colorSourceIsBaseline: boolean = false;
   accessor hadSecondarySide: boolean = false;
   accessor hasSecondarySide: boolean = false;
-  accessor isDark: boolean = false;
   accessor searchboxChromeRefreshTheming: boolean =
       loadTimeData.getBoolean('searchboxCr23Theming');
   accessor searchboxSteadyStateShadow: boolean =
       loadTimeData.getBoolean('searchboxCr23SteadyStateShadow');
-  accessor searchboxLayoutMode: string = '';
   accessor placeholderText: string = '';
   protected accessor enableThumbnailSizingTweaks_: boolean =
       loadTimeData.getBoolean('enableThumbnailSizingTweaks');
@@ -213,10 +194,8 @@
   override willUpdate(changedProperties: PropertyValues<this>) {
     super.willUpdate(changedProperties);
 
-    if (changedProperties.has('searchboxChromeRefreshTheming') ||
-        changedProperties.has('colorSourceIsBaseline')) {
-      this.useWebkitSearchIcons_ =
-          this.searchboxChromeRefreshTheming && !this.colorSourceIsBaseline;
+    if (changedProperties.has('searchboxChromeRefreshTheming')) {
+      this.useWebkitSearchIcons_ = this.searchboxChromeRefreshTheming;
     }
 
     const changedPrivateProperties =
@@ -400,10 +379,6 @@
     this.hasSecondarySide = e.detail.value;
   }
 
-  protected useCompactLayout_(): boolean {
-    return this.searchboxLayoutMode === 'Compact';
-  }
-
   protected processFiles_(
       files: FileList|null,
       contextAdditionMethod: ComposeboxContextAddedMethod) {
diff --git a/ui/webui/resources/cr_components/searchbox/searchbox_browser_proxy.ts b/ui/webui/resources/cr_components/searchbox/searchbox_browser_proxy.ts
index d6ebaf2..22442f8 100644
--- a/ui/webui/resources/cr_components/searchbox/searchbox_browser_proxy.ts
+++ b/ui/webui/resources/cr_components/searchbox/searchbox_browser_proxy.ts
@@ -13,7 +13,7 @@
  */
 
 import type {AutocompleteMatch, AutocompleteResult, PageHandlerInterface} from '//resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js';
-import {PageCallbackRouter, PageHandler} from '//resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js';
+import {PageCallbackRouter, PageHandlerFactory, PageHandlerRemote} from '//resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js';
 
 export function createAutocompleteMatch(
     modifiers: Partial<AutocompleteMatch> = {}): AutocompleteMatch {
@@ -95,10 +95,13 @@
   callbackRouter: PageCallbackRouter;
 
   constructor() {
-    this.handler = PageHandler.getRemote();
+    const handler = new PageHandlerRemote();
+    this.handler = handler;
     this.callbackRouter = new PageCallbackRouter();
 
-    this.handler.setPage(this.callbackRouter.$.bindNewPipeAndPassRemote());
+    PageHandlerFactory.getRemote().createPageHandler(
+        this.callbackRouter.$.bindNewPipeAndPassRemote(),
+        handler.$.bindNewPipeAndPassReceiver());
   }
 }
 
diff --git a/v8 b/v8
index 98b7076..013bfcb 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 98b707649f79997d3395530659718471d899b05c
+Subproject commit 013bfcbff17793f940a902dc26ea0b2e090550a9