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(¶ms); + 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"><link></ph>manage your info<ph name="END_LINK"></link></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