diff --git a/DEPS b/DEPS index 52c9cfb1..ee41be5 100644 --- a/DEPS +++ b/DEPS
@@ -303,7 +303,7 @@ # 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': 'b5ad54043bed93367fbf4a399424fca635d1d7a4', + 'skia_revision': 'a8e9b6aa2b406b35760cc3abd768a88c43ad783b', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -311,7 +311,7 @@ # 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': 'cdb34025f286a0ded80c34c4edae180539e24dc1', + 'angle_revision': '7e1b79c7156eff744ecc6073a90ab2612668fe83', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -379,7 +379,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling CrossBench # and whatever else without interference from each other. - 'crossbench_revision': 'ce39b8b26f68bc3abb5d75abf7b6358ec7d0d2ff', + 'crossbench_revision': '34be6a0627d00863c2bc46bcdba070d5cc9b05b2', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -395,7 +395,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': '4283539e427e270bad107202fbc752a5f23e8141', + 'devtools_frontend_revision': 'd5def0337ba13fa0b63937a94837bd0281f91751', # 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. @@ -419,7 +419,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': 'ffb09b2dee453a5121d21d79ae9a9dc91ceaf1fa', + 'dawn_revision': '0ef89959a16e947d4d73f33c2f33d39818d95459', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -1152,7 +1152,7 @@ }, 'src/chrome/release_scripts': { - 'url': Var('chrome_git') + '/chrome/tools/release/scripts' + '@' + '51e35b4bafb095a86c2108573a250e6fee9625a5', + 'url': Var('chrome_git') + '/chrome/tools/release/scripts' + '@' + 'b21f087218529baaa951d83544ec5d08cd54b3b8', 'condition': 'checkout_chrome_release_scripts', }, @@ -1495,7 +1495,7 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - 'd1eb01c07fa71d57ecd25f9d1bbe604bcff5fbc4', + '3138045236e6f126b9de2569f8f9132a1f399fed', 'condition': 'checkout_android and checkout_src_internal', }, @@ -1987,7 +1987,7 @@ 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'fa8fc854e1766b86f10c9a15902cf3cc23adaac2', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1fcc527019d786502b02f71b8b764ee674a40953', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), @@ -2535,7 +2535,7 @@ Var('pdfium_git') + '/pdfium.git' + '@' + Var('pdfium_revision'), 'src/third_party/perfetto': - Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + 'a54dd38d60593129ae56d400f1a72860670abea4', + Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '220d71b5a8e54a558cb10e70518c8b22b2c57b46', 'src/base/tracing/test/data': { 'bucket': 'perfetto', @@ -2784,10 +2784,10 @@ 'bucket': 'chromium-ads-detection', 'objects': [ { - 'object_name': '4723c6e42380c6a90a601c9bf6e4dd72136958516de05623dc8d342b6e05f00c', - 'sha256sum': '4723c6e42380c6a90a601c9bf6e4dd72136958516de05623dc8d342b6e05f00c', - 'size_bytes': 77095, - 'generation': 1739551787231704, + 'object_name': '2e9b747b519d133c2cb0ef7f10119b7b023533363db30ef45c93bdeaf6dbc57e', + 'sha256sum': '2e9b747b519d133c2cb0ef7f10119b7b023533363db30ef45c93bdeaf6dbc57e', + 'size_bytes': 79185, + 'generation': 1744292578626605, 'output_file': 'UnindexedRules', }, ], @@ -2903,7 +2903,7 @@ Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'), 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '7a56cbb05d89dee09ff67d00595bf9fb1ed78650', + Var('webrtc_git') + '/src.git' + '@' + 'dc428bd75f2ca847b165b2556970c9bcd5037c5b', # Wuffs' canonical repository is at github.com/google/wuffs, but we use # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file. @@ -3080,7 +3080,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/projector_app/app', - 'version': '-rvm94WxMnM68SquVNcXlOduHvzBni2VWApKdx5XlQYC', + 'version': 'idhv0InosQt0lvw6c07uT_wP3D0K1hyYVGdpOTIIDsgC', }, ], 'condition': 'checkout_chromeos and checkout_src_internal', @@ -3107,7 +3107,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_deps/autorolled', - 'version': 'yZGuDQ9pDcy816oWVPUC2zMwLtFUfGt4W2PmIYEWTcAC', + 'version': '_rzZP_RJ0Fx7bXb_R5WFX8yvYsF5YffSZ0p9pbI_Z64C', }, ], 'condition': 'checkout_android and non_git_source', @@ -4689,7 +4689,7 @@ 'src/ios_internal': { 'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' + - 'c5e56941476cea4381eff1c195f7d16edf3996d0', + 'e2dcc744d3e17a3e0736d9ceecba5dd4a69e7f31', 'condition': 'checkout_ios and checkout_src_internal', },
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc index 8ee099c8..2628f987 100644 --- a/ash/constants/ash_features.cc +++ b/ash/constants/ash_features.cc
@@ -2961,6 +2961,12 @@ "UseAndroidStagingSmds", base::FEATURE_DISABLED_BY_DEFAULT); +// If enabled, the new `TokenHandleStoreImpl` will be used instead of +// `TokenHandleUtil`. +BASE_FEATURE(kUseTokenHandleStore, + "UseTokenHandleStore", + base::FEATURE_DISABLED_BY_DEFAULT); + // Use the AnnotatedAccountId for mapping between User and BrowserContext // (a.k.a. browser's Profile). BASE_FEATURE(kUseAnnotatedAccountId, @@ -4831,4 +4837,8 @@ "mix_local_and_drive", false)); } +bool IsUseTokenHandleStoreEnabled() { + return base::FeatureList::IsEnabled(kUseTokenHandleStore); +} + } // namespace ash::features
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h index b07d427..3b2fc1a 100644 --- a/ash/constants/ash_features.h +++ b/ash/constants/ash_features.h
@@ -986,6 +986,7 @@ BASE_DECLARE_FEATURE(kUseStorkSmdsServerAddress); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kUseWallpaperStagingUrl); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kUserActivityPrediction); +COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kUseTokenHandleStore); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kLiveCaptionUserMicrophone); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kVcBackgroundReplace); @@ -1476,6 +1477,7 @@ bool IsSearchCustomizableShortcutsInLauncherEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFeatureAwareDeviceDemoModeEnabled(); +COMPONENT_EXPORT(ASH_CONSTANTS) bool IsUseTokenHandleStoreEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool UseMixedFileLauncherContinueSection(); // Keep alphabetized.
diff --git a/base/json/json_parser.cc b/base/json/json_parser.cc index 2681617..0873aa1 100644 --- a/base/json/json_parser.cc +++ b/base/json/json_parser.cc
@@ -7,6 +7,7 @@ #include <algorithm> #include <cmath> #include <iterator> +#include <memory> #include <string_view> #include <utility> #include <vector> @@ -24,6 +25,7 @@ #include "base/strings/utf_string_conversion_utils.h" #include "base/strings/utf_string_conversions.h" #include "base/third_party/icu/icu_utf.h" +#include "base/types/pass_key.h" namespace base::internal { @@ -373,7 +375,7 @@ return std::nullopt; } - std::vector<std::pair<std::string, Value>> values; + std::vector<std::pair<std::string, std::unique_ptr<Value>>> values; Token token = GetNextToken(); while (token != T_OBJECT_END) { @@ -403,7 +405,8 @@ return std::nullopt; } - values.emplace_back(std::move(*key), std::move(*value)); + values.emplace_back(std::move(*key), + std::make_unique<Value>(std::move(*value))); token = GetNextToken(); if (token == T_LIST_SEPARATOR) { @@ -423,8 +426,7 @@ // Reverse |dict_storage| to keep the last of elements with the same key in // the input. std::ranges::reverse(values); - return Value(Value::Dict(std::make_move_iterator(values.begin()), - std::make_move_iterator(values.end()))); + return Value(Value::Dict(PassKey<JSONParser>(), std::move(values))); } std::optional<Value> JSONParser::ConsumeList() {
diff --git a/base/json/json_reader.cc b/base/json/json_reader.cc index 3e3d6097..f32622d 100644 --- a/base/json/json_reader.cc +++ b/base/json/json_reader.cc
@@ -105,7 +105,6 @@ (options & base::JSON_REPLACE_INVALID_CHARACTERS) != 0, .allow_comments = (options & base::JSON_ALLOW_COMMENTS) != 0, .allow_newlines = (options & base::JSON_ALLOW_NEWLINES_IN_STRINGS) != 0, - .allow_control_chars = false, .allow_vert_tab = (options & base::JSON_ALLOW_VERT_TAB) != 0, .allow_x_escapes = (options & base::JSON_ALLOW_X_ESCAPES) != 0, .max_depth = max_depth,
diff --git a/base/nix/xdg_util.cc b/base/nix/xdg_util.cc index 6d23627..de66c5b 100644 --- a/base/nix/xdg_util.cc +++ b/base/nix/xdg_util.cc
@@ -259,10 +259,17 @@ } std::optional<std::string> ExtractXdgActivationTokenFromEnv(Environment& env) { - if (auto token = env.GetVar(kXdgActivationTokenEnvVar).value_or(""); + std::string token; + if (token = env.GetVar(kXdgActivationTokenEnvVar).value_or(""); !token.empty()) { GetXdgActivationToken() = std::move(token); env.UnSetVar(kXdgActivationTokenEnvVar); + } else if (token = env.GetVar(kDesktopStartupIdEnvVar).value_or(""); + !token.empty()) { + // X11 apps use DESKTOP_STARTUP_ID to pass the activation token. + // https://gitlab.freedesktop.org/wayland/wayland-protocols/-/blob/main/staging/xdg-activation/x11-interoperation.rst + GetXdgActivationToken() = std::move(token); + env.UnSetVar(kDesktopStartupIdEnvVar); } return GetXdgActivationToken(); }
diff --git a/base/nix/xdg_util.h b/base/nix/xdg_util.h index a8e47c0..85b012be 100644 --- a/base/nix/xdg_util.h +++ b/base/nix/xdg_util.h
@@ -79,6 +79,9 @@ // The XDG activation token environment variable. inline constexpr char kXdgActivationTokenEnvVar[] = "XDG_ACTIVATION_TOKEN"; +// Desktop startup ID environment variable. +inline constexpr char kDesktopStartupIdEnvVar[] = "DESKTOP_STARTUP_ID"; + // Internally used to communicate the activation token between a newly launched // process and an existing browser process. inline constexpr char kXdgActivationTokenSwitch[] = "xdg-activation-token";
diff --git a/base/nix/xdg_util_unittest.cc b/base/nix/xdg_util_unittest.cc index 156cdc2..926962da 100644 --- a/base/nix/xdg_util_unittest.cc +++ b/base/nix/xdg_util_unittest.cc
@@ -432,6 +432,17 @@ EXPECT_EQ(kXdgActivationTokenFromEnv, TakeXdgActivationToken()); // Should be cleared after the token is taken once. EXPECT_EQ(std::nullopt, TakeXdgActivationToken()); + + EXPECT_CALL(getter, GetVar(Eq("XDG_ACTIVATION_TOKEN"))) + .WillOnce(Return(std::nullopt)); + EXPECT_CALL(getter, GetVar(Eq("DESKTOP_STARTUP_ID"))) + .WillOnce(Return(kXdgActivationTokenFromEnv)); + EXPECT_CALL(getter, UnSetVar(Eq("DESKTOP_STARTUP_ID"))); + EXPECT_EQ(kXdgActivationTokenFromEnv, + ExtractXdgActivationTokenFromEnv(getter)); + EXPECT_EQ(kXdgActivationTokenFromEnv, TakeXdgActivationToken()); + // Should be cleared after the token is taken once. + EXPECT_EQ(std::nullopt, TakeXdgActivationToken()); } TEST(XDGUtilTest, ExtractXdgActivationTokenFromCmdLineNotSet) {
diff --git a/base/values.cc b/base/values.cc index 7332862..35bb39c 100644 --- a/base/values.cc +++ b/base/values.cc
@@ -29,6 +29,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/trace_event/base_tracing.h" #include "base/tracing_buildflags.h" +#include "base/types/pass_key.h" #include "base/types/to_address.h" #if BUILDFLAG(ENABLE_BASE_TRACING) @@ -356,6 +357,16 @@ DictValue::DictValue() = default; +DictValue::DictValue(flat_map<std::string, std::unique_ptr<Value>> storage) + : storage_(std::move(storage)) { + DCHECK(std::ranges::all_of(storage_, + [](const auto& entry) { return !!entry.second; })); +} + +DictValue::DictValue(PassKey<internal::JSONParser>, + flat_map<std::string, std::unique_ptr<Value>> storage) + : DictValue(std::move(storage)) {} + DictValue::DictValue(DictValue&&) noexcept = default; DictValue& DictValue::operator=(DictValue&&) noexcept = default; @@ -420,12 +431,10 @@ storage.emplace_back(key, std::make_unique<Value>(value->Clone())); } - DictValue result; // `storage` is already sorted and unique by construction, which allows us to // avoid an additional O(n log n) step. - result.storage_ = flat_map<std::string, std::unique_ptr<Value>>( - sorted_unique, std::move(storage)); - return result; + return DictValue(flat_map<std::string, std::unique_ptr<Value>>( + sorted_unique, std::move(storage))); } void DictValue::Merge(DictValue dict) {
diff --git a/base/values.h b/base/values.h index 24d34fc..cd2caf37 100644 --- a/base/values.h +++ b/base/values.h
@@ -30,6 +30,7 @@ #include "base/containers/flat_map.h" #include "base/containers/span.h" #include "base/trace_event/base_tracing_forward.h" +#include "base/types/pass_key.h" #include "base/value_iterators.h" namespace base { @@ -37,6 +38,10 @@ class DictValue; class Value; +namespace internal { +class JSONParser; +} // namespace internal + using BlobStorage = std::vector<uint8_t>; // Represents a list of Values. @@ -278,6 +283,9 @@ storage_ = flat_map<std::string, std::unique_ptr<Value>>(std::move(values)); } + DictValue(PassKey<internal::JSONParser>, + flat_map<std::string, std::unique_ptr<Value>>); + ~DictValue(); // Returns true if there are no entries in this dictionary and false @@ -567,6 +575,8 @@ BASE_EXPORT friend std::partial_ordering operator<=>(const DictValue& lhs, const DictValue& rhs); + explicit DictValue(flat_map<std::string, std::unique_ptr<Value>>); + // TODO(dcheng): Replace with `flat_map<std::string, Value>` once no caller // relies on stability of pointers anymore. flat_map<std::string, std::unique_ptr<Value>> storage_;
diff --git a/build/android/docs/build_config.md b/build/android/docs/build_config.md index 8f752a66..1e09f4e 100644 --- a/build/android/docs/build_config.md +++ b/build/android/docs/build_config.md
@@ -1,32 +1,30 @@ -# Introduction - This document describes the `.build_config.json` files that are used by the Chromium build system for Android-specific targets like APK, resources, and more. [TOC] -# I. Overview of .build_config.json files: +# Overview -The Android build requires performing computations about dependencies in -various targets, which are not possible with the GN build language. To address -this, `.build_config.json` files are written during the build to store the needed -per-target information as JSON files. +Instead of using GN's `metadata` system to propagate information between targets, +every Java target writes a `.params.json` and a `.build_config.json` file with +information needed by targets that depend on them. -They are always written to `$target_gen_dir/${target_name}.build_config.json`. +They are always written to `$target_gen_dir/$target_name.{build_config,params}.json`. -Many scripts under [`build/android/gyp/`](build/android_gyp/), which are used -during the build, can also accept parameter arguments using -`@FileArg references`, which look like: +`.params.json` files are written during "gn gen" with values available at that +time, while `.build_config.json` files are written during the build with values +that are derived from dependent `.json` files. - --some-param=@FileArg(<filename>:<key1>:<key2>:..<keyN>) +Build scripts, can accept parameter arguments using `@FileArg references`, +which look like: + + --some-param=@FileArg(foo.build_config.json:<key1>:<key2>:..<keyN>) This placeholder will ensure that `<filename>` is read as a JSON file, then return the value at `[key1][key2]...[keyN]` for the `--some-param` option. -Apart from that, the scripts do not need to know anything about the structure -of `.build_config.json` files (but the GN rules that invoke them do and select -which `@FileArg()` references to use). +Be sure to list the `.build_config.json` in the `action`'s `inputs`. For a concrete example, consider the following GN fragment: @@ -41,128 +39,32 @@ } ``` -This will end up generating the following JSON file under -`$CHROMIUM_OUTPUT_DIR/gen/ui/android/ui_java_resources.build_config.json`: +This will end up generating: +**`$CHROMIUM_OUTPUT_DIR/gen/ui/android/ui_java_resources.params.json`:** ```json { - "deps_info": { - "deps_configs": [ - "gen/ui/android/ui_strings_grd.build_config.json" - ], - "name": "ui_java_resources.build_config.json", - "package_name": "org.chromium.ui", - "path": "gen/ui/android/ui_java_resources.build_config.json", - "r_text": "gen/ui/android/ui_java_resources_R.txt", - "resources_dirs": [ - "../../ui/android/java/res" - ], - "resources_zip": "resource_zips/ui/android/ui_java_resources.resources.zip", - "srcjar": "gen/ui/android/ui_java_resources.srcjar", - "type": "android_resources" - }, - "gradle": {}, - "resources": { - "dependency_zips": [ - "resource_zips/ui/android/ui_strings_grd.resources.zip" - ], - "extra_package_names": [], - } + "chromium_code": true, + "deps_configs": [ + "gen/ui/android/ui_strings_grd.build_config.json", + "gen/third_party/android_sdk/android_sdk_java.build_config.json" + ], + "gn_target": "//ui/android:ui_java_resources", + "res_sources_path": "gen/ui/android/ui_java_resources.res.sources", + "resources_zip": "obj/ui/android/ui_java_resources.resources.zip", + "rtxt_path": "gen/ui/android/ui_java_resources_R.txt", + "type": "android_resources" } ``` -NOTE: All path values in `.build_config.json` files are relative to your -`$CHROMIUM_OUTPUT_DIR`. +**`$CHROMIUM_OUTPUT_DIR/gen/ui/android/ui_java_resources.build_config.json`:** -# II. Generation of .build_config.json files: - -They are generated by the GN [`write_build_config()`](gn_write_build_config) -internal template, which ends up invoking -[`write_build_config.py`](write_build_config_py). For our example above, this -is with the following parameters: - +```json +{ + "dependency_zip_overlays": [], + "dependency_zips": [ + "obj/ui/android/ui_strings_grd.resources.zip" + ], + "extra_package_names": [] +} ``` -python ../../build/android/gyp/write_build_config.py \ - --type=android_resources \ - --depfile gen/ui/android/ui_java_resources__build_config_crbug_908819.d \ - --deps-configs=\[\"gen/ui/android/ui_strings_grd.build_config.json\"\] \ - --build-config gen/ui/android/ui_java_resources.build_config.json \ - --resources-zip resource_zips/ui/android/ui_java_resources.resources.zip \ - --package-name org.chromium.ui \ - --r-text gen/ui/android/ui_java_resources_R.txt \ - --resource-dirs=\[\"../../ui/android/java/res\"\] \ - --srcjar gen/ui/android/ui_java_resources.srcjar -``` - -Note that *most* of the content of the JSON file comes from command-line -parameters, but not all of it. - -In particular, the `resources['dependency_zips']` entry was computed by -inspecting the content of all dependencies (here, only -`ui_string_grd.build_config.json`), and collecting their -`deps_configs['resources_zip']` values. - -Because a target's `.build_config.json` file will always be generated after -that of all of its dependencies, -[`write_build_config.py`](write_build_config_py) can traverse the -whole (transitive) set of direct *and* indirect dependencies for a given target -and extract useful information out of it. - -This is the kind of processing that cannot be done at the GN language level, -and is very powerful for Android builds. - - -# III. Usage of .build_config.json files: - -In addition to being parsed by `write_build_config.py`, when they are listed -in the `--deps-configs` of a given target, the `.build_config.json` files are used -by other scripts under [build/android/gyp/] to build stuff. - -For example, the GN `android_resources` template uses it to invoke the -[`process_resources.py`] script with the following command, in order to -generate various related files (e.g. `ui_java_resources_R.txt`): - -```sh -python ../../build/android/gyp/process_resources.py \ - --depfile gen/ui/android/ui_java_resources_1.d \ - --android-sdk-jar ../../third_party/android_sdk/public/platforms/android-29/android.jar \ - --aapt-path ../../third_party/android_sdk/public/build-tools/29.0.2/aapt \ - --dependencies-res-zips=@FileArg\(gen/ui/android/ui_java_resources.build_config.json:resources:dependency_zips\) \ - --extra-res-packages=@FileArg\(gen/ui/android/ui_java_resources.build_config.json:resources:extra_package_names\) \ - --resource-dirs=\[\"../../ui/android/java/res\"\] \ - --debuggable \ - --resource-zip-out resource_zips/ui/android/ui_java_resources.resources.zip \ - --r-text-out gen/ui/android/ui_java_resources_R.txt \ - --srcjar-out gen/ui/android/ui_java_resources.srcjar \ - --non-constant-id \ - --custom-package org.chromium.ui \ - --shared-resources -``` - -Note the use of `@FileArg()` references here, to tell the script where to find -the information it needs. - - -# IV. Format of .build_config.json files: - -Thanks to `@FileArg()` references, Python build scripts under -[`build/android/gyp/`](build/android/gyp/) do not need to know anything -about the internal format of `.build_config.json` files. - -This format is decided between internal GN build rules and -[`write_build_config.py`][write_build_config_py]. Since these changes rather -often, the format documentation is kept inside the Python script itself, but -can be extracted as a Markdown file and visualized with the following commands: - -```sh -# Extract .build_config.json format documentation -build/android/gyp/write_build_config.py \ - --generate-markdown-format-doc > /tmp/format.md - -# Launch a browser to visualize the format documentation. -python tools/md_browser/md_browser.py -d /tmp /tmp/format.md -``` - -[build/android/gyp/]: https://chromium.googlesource.com/chromium/src/build/+/main/android/gyp/ -[gn_write_build_config]: https://cs.chromium.org/chromium/src/build/config/android/internal_rules.gni?q=write_build_config&sq=package:chromium -[write_build_config_py]: https://chromium.googlesource.com/chromium/src/build/+/main/android/gyp/write_build_config.py
diff --git a/build/android/generate_vscode_project.py b/build/android/generate_vscode_project.py index e9dbd35e..f5e9b6e3 100755 --- a/build/android/generate_vscode_project.py +++ b/build/android/generate_vscode_project.py
@@ -67,15 +67,17 @@ logging.info('Processing build config: %s', build_config_path) - with open(os.path.join(output_dir, build_config_path)) as build_config_file: - build_config = json.load(build_config_file) + params_json = build_config_path.replace('.build_config.json', '.params.json') + with open(os.path.join(output_dir, params_json)) as f: + build_config = json.load(f) + with open(os.path.join(output_dir, build_config_path)) as f: + build_config.update(json.load(f)) - deps_info = build_config['deps_info'] - target_sources_file = deps_info.get('target_sources_file') + target_sources_file = build_config.get('target_sources_file') if target_sources_file is not None: _ProcessSourcesFile(output_dir, target_sources_file, source_dirs) else: - unprocessed_jar_path = deps_info.get('unprocessed_jar_path') + unprocessed_jar_path = build_config.get('unprocessed_jar_path') if unprocessed_jar_path is not None: lib_path = os.path.normpath(os.path.join(output_dir, unprocessed_jar_path)) @@ -107,7 +109,7 @@ os.pardir, os.pardir, 'build-tools', android_sdk_build_tools_version, 'core-lambda-stubs.jar'))) - for dep_config in deps_info['deps_configs']: + for dep_config in build_config.get('deps_configs', []): _ProcessBuildConfigFile(output_dir, dep_config, source_dirs, libs, already_processed_build_config_files, android_sdk_build_tools_version)
diff --git a/build/android/gradle/generate_gradle.py b/build/android/gradle/generate_gradle.py index 90df56b8..b14e0f0 100755 --- a/build/android/gradle/generate_gradle.py +++ b/build/android/gradle/generate_gradle.py
@@ -106,6 +106,11 @@ output_file.write(data) +def _ReadJson(path): + with open(path) as f: + return json.load(f) + + def _RunGnGen(output_dir, args=None): cmd = [os.path.join(_DEPOT_TOOLS_PATH, 'gn'), 'gen', output_dir] if args: @@ -176,6 +181,9 @@ def BuildConfigPath(self): return os.path.join('gen', self.GradleSubdir() + '.build_config.json') + def GetName(self): + return self._gn_target.split(':')[-1] + def GradleSubdir(self): """Returns the output subdirectory.""" ninja_target = self.NinjaTarget() @@ -195,13 +203,13 @@ def BuildConfig(self): """Reads and returns the project's .build_config.json JSON.""" if not self._build_config: - with open(_RebasePath(self.BuildConfigPath())) as jsonfile: - self._build_config = json.load(jsonfile) + path = self.BuildConfigPath() + config = _ReadJson(path.replace('.build_config.json', '.params.json')) + config.update(_ReadJson(path)) + config['path'] = path + self._build_config = config return self._build_config - def DepsInfo(self): - return self.BuildConfig()['deps_info'] - def Gradle(self): return self.BuildConfig()['gradle'] @@ -210,7 +218,7 @@ def GetType(self): """Returns the target type from its .build_config.""" - return self.DepsInfo()['type'] + return self.BuildConfig()['type'] def IsValid(self): return self.GetType() in ( @@ -223,11 +231,11 @@ ) def ResSources(self): - return self.DepsInfo().get('lint_resource_sources', []) + return self.BuildConfig().get('lint_resource_sources', []) def JavaFiles(self): if self._java_files is None: - target_sources_file = self.DepsInfo().get('target_sources_file') + target_sources_file = self.BuildConfig().get('target_sources_file') java_files = [] if target_sources_file: target_sources_file = _RebasePath(target_sources_file) @@ -331,7 +339,7 @@ return generated_inputs def GenerateManifest(self, root_entry): - android_manifest = root_entry.DepsInfo().get('android_manifest') + android_manifest = root_entry.BuildConfig().get('android_manifest') if not android_manifest: android_manifest = self._GenCustomManifest(root_entry) return self._Relativize(root_entry, android_manifest) @@ -550,31 +558,31 @@ def _GenerateGradleFile(entry, generator, build_vars, jinja_processor): """Returns the data for a project's build.gradle.""" - deps_info = entry.DepsInfo() + config = entry.BuildConfig() variables = _GenerateBaseVars(generator, build_vars) sourceSetName = 'main' - if deps_info['type'] == 'android_apk': + if config['type'] == 'android_apk': target_type = 'android_apk' - elif deps_info['type'] in ('java_library', 'java_annotation_processor'): - is_prebuilt = deps_info.get('is_prebuilt', False) - gradle_treat_as_prebuilt = deps_info.get('gradle_treat_as_prebuilt', False) + elif config['type'] in ('java_library', 'java_annotation_processor'): + is_prebuilt = config.get('is_prebuilt', False) + gradle_treat_as_prebuilt = config.get('gradle_treat_as_prebuilt', False) if is_prebuilt or gradle_treat_as_prebuilt: return None - if deps_info['requires_android']: + if config['requires_android']: target_type = 'android_library' else: target_type = 'java_library' - elif deps_info['type'] == 'java_binary': + elif config['type'] == 'java_binary': target_type = 'java_binary' - variables['main_class'] = deps_info.get('main_class') - elif deps_info['type'] == 'robolectric_binary': + variables['main_class'] = config.get('main_class') + elif config['type'] == 'robolectric_binary': target_type = 'android_junit' sourceSetName = 'test' else: return None - variables['target_name'] = os.path.splitext(deps_info['name'])[0] + variables['target_name'] = entry.GetName() variables['template_type'] = target_type variables['main'] = {} variables[sourceSetName] = generator.Generate(entry) @@ -718,7 +726,7 @@ if cur_entry in found: continue found.add(cur_entry) - sub_config_paths = cur_entry.DepsInfo()['deps_configs'] + sub_config_paths = cur_entry.BuildConfig()['deps_configs'] to_scan.extend( _ProjectEntry.FromBuildConfigPath(p) for p in sub_config_paths) return list(found) @@ -743,7 +751,7 @@ else: combined_entries.append(entry) for entry in combined_entries: - target_name = entry.DepsInfo()['name'] + target_name = entry.GetName() if target_name in android_test_entries: entry.android_test_entries = android_test_entries[target_name] del android_test_entries[target_name]
diff --git a/build/android/gyp/util/dep_utils.py b/build/android/gyp/util/dep_utils.py index 98869a1..4fff9c5 100644 --- a/build/android/gyp/util/dep_utils.py +++ b/build/android/gyp/util/dep_utils.py
@@ -1,7 +1,7 @@ # Copyright 2023 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""Methods for managing deps based on build_config.json files.""" +"""Methods for managing deps based on .params.json files.""" from __future__ import annotations import collections @@ -125,7 +125,7 @@ logging.debug('Running list_java_targets.py...') list_java_targets_command = [ 'build/android/list_java_targets.py', '--gn-labels', - '--print-build-config-paths', + '--print-params-paths', f'--output-directory={self._abs_build_output_dir}' ] if self._should_build: @@ -152,27 +152,27 @@ target_line_parts = target_line.split(': ') assert len(target_line_parts) == 2, target_line_parts - target_name, build_config_path = target_line_parts + target_name, params_path = target_line_parts - if not os.path.exists(build_config_path): + if not os.path.exists(params_path): assert not self._should_build continue - with open(build_config_path) as build_config_contents: - build_config_json: Dict = json.load(build_config_contents) - deps_info = build_config_json['deps_info'] + with open(params_path) as data: + params_json: Dict = json.load(data) # Checking the library type here instead of in list_java_targets.py avoids # reading each .build_config file twice. - if deps_info['type'] not in ('java_library', 'group'): + if params_json['type'] not in ('java_library', 'group'): continue - relpath = os.path.relpath(build_config_path, self._abs_build_output_dir) - preferred_dep = bool(deps_info.get('preferred_dep')) - is_group = bool(deps_info.get('type') == 'group') - dependent_config_paths = deps_info.get('deps_configs', []) + relpath = os.path.relpath(params_path, self._abs_build_output_dir) + preferred_dep = bool(params_json.get('preferred_dep')) + is_group = bool(params_json['type'] == 'group') + dependent_config_paths = (params_json.get('deps_configs', []) + + params_json.get('public_deps_configs', [])) full_class_names = self._compute_full_class_names_for_build_config( - deps_info) + params_json) build_config = BuildConfig(relpath=relpath, target_name=target_name, is_group=is_group, @@ -204,13 +204,12 @@ return class_index def _compute_full_class_names_for_build_config(self, - deps_info: Dict) -> Set[str]: + params_json: Dict) -> Set[str]: """Returns set of fully qualified class names for build config.""" - full_class_names = set() # Read the location of the target_sources_file from the build_config - sources_path = deps_info.get('target_sources_file') + sources_path = params_json.get('target_sources_file') if sources_path: # Read the target_sources_file, indexing the classes found with open(self._abs_build_output_dir / sources_path) as sources_contents: @@ -224,7 +223,7 @@ # android_aar_prebuilt()) # |unprocessed_jar_path| might be set but not exist if not all targets have # been built. - unprocessed_jar_path = deps_info.get('unprocessed_jar_path') + unprocessed_jar_path = params_json.get('unprocessed_jar_path') if unprocessed_jar_path: abs_unprocessed_jar_path = (self._abs_build_output_dir / unprocessed_jar_path)
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py index 19223e8..8b2a5ac 100755 --- a/build/android/gyp/write_build_config.py +++ b/build/android/gyp/write_build_config.py
@@ -4,558 +4,21 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""Writes a build_config file. +"""Writes a .build_config.json file. -The build_config file for a target is a json file containing information about -how to build that target based on the target's dependencies. This includes -things like: the javac classpath, the list of android resources dependencies, -etc. It also includes the information needed to create the build_config for -other targets that depend on that one. - -Android build scripts should not refer to the build_config directly, and the -build specification should instead pass information in using the special -file-arg syntax (see build_utils.py:ExpandFileArgs). That syntax allows passing -of values in a json dict in a file and looks like this: - --python-arg=@FileArg(build_config_path:javac:classpath) - -Note: If paths to input files are passed in this way, it is important that: - 1. inputs/deps of the action ensure that the files are available the first - time the action runs. - 2. Either (a) or (b) - a. inputs/deps ensure that the action runs whenever one of the files changes - b. the files are added to the action's depfile - -NOTE: All paths within .build_config files are relative to $OUTPUT_CHROMIUM_DIR. - -This is a technical note describing the format of .build_config files. -Please keep it updated when changing this script. For extraction and -visualization instructions, see build/android/docs/build_config.md - -------------- BEGIN_MARKDOWN --------------------------------------------------- -The .build_config file format -=== - -# Introduction - -This document tries to explain the format of `.build_config` generated during -the Android build of Chromium. For a higher-level explanation of these files, -please read -[build/android/docs/build_config.md](build/android/docs/build_config.md). - -# The `deps_info` top-level dictionary: - -All `.build_config` files have a required `'deps_info'` key, whose value is a -dictionary describing the target and its dependencies. The latter has the -following required keys: - -## Required keys in `deps_info`: - -* `deps_info['type']`: The target type as a string. - - The following types are known by the internal GN build rules and the - build scripts altogether: - - * [java_binary](#target_java_binary) - * [java_annotation_processor](#target_java_annotation_processor) - * [robolectric_binary](#target_robolectric_binary) - * [java_library](#target_java_library) - * [android_assets](#target_android_assets) - * [android_resources](#target_android_resources) - * [android_apk](#target_android_apk) - * [android_app_bundle_module](#target_android_app_bundle_module) - * [android_app_bundle](#target_android_app_bundle) - * [dist_jar](#target_dist_jar) - * [dist_aar](#target_dist_aar) - * [group](#target_group) - - See later sections for more details of some of these. - -* `deps_info['path']`: Path to the target's `.build_config` file. - -* `deps_info['name']`: Nothing more than the basename of `deps_info['path']` -at the moment. - -* `deps_info['deps_configs']`: List of paths to the `.build_config` files of -all *direct* dependencies of the current target. - - NOTE: Because the `.build_config` of a given target is always generated - after the `.build_config` of its dependencies, the `write_build_config.py` - script can use chains of `deps_configs` to compute transitive dependencies - for each target when needed. - -## Optional keys in `deps_info`: - -The following keys will only appear in the `.build_config` files of certain -target types: - -* `deps_info['requires_android']`: True to indicate that the corresponding -code uses Android-specific APIs, and thus cannot run on the host within a -regular JVM. May only appear in Java-related targets. - -* `deps_info['supports_android']`: -May appear in Java-related targets, and indicates that -the corresponding code doesn't use Java APIs that are not available on -Android. As such it may run either on the host or on an Android device. - -* `deps_info['assets']`: -Only seen for the [`android_assets`](#target_android_assets) type. See below. - -* `deps_info['package_name']`: Java package name associated with this target. - - NOTE: For `android_resources` targets, - this is the package name for the corresponding R class. For `android_apk` - targets, this is the corresponding package name. This does *not* appear for - other target types. - -* `deps_info['android_manifest']`: -Path to an AndroidManifest.xml file related to the current target. - -* `deps_info['base_module_config']`: -Only seen for the [`android_app_bundle`](#target_android_app_bundle) type. -Path to the base module for the bundle. - -* `deps_info['module_name']`: -Only seen for the -[`android_app_bundle_module`](#target_android_app_bundle_module) -type. The name of the feature module. - -* `deps_info['dependency_zips']`: -List of `deps_info['resources_zip']` entries for all `android_resources` -dependencies for the current target. - -* `deps_info['extra_package_names']`: -Always empty for `android_resources` types. Otherwise, -the list of `deps_info['package_name']` entries for all `android_resources` -dependencies for the current target. Computed automatically by -`write_build_config.py`. - -* `deps_info['dependency_r_txt_files']`: -Exists only on dist_aar. It is the list of deps_info['r_text_path'] from -transitive dependencies. Computed automatically. - - -# `.build_config` target types description: - -## <a name="target_group">Target type `group`</a>: - -This type corresponds to a simple target that is only used to group -dependencies. It matches the `java_group()` GN template. Its only top-level -`deps_info` keys are `supports_android` (always True), and `deps_configs`. - - -## <a name="target_android_resources">Target type `android_resources`</a>: - -This type corresponds to targets that are used to group Android resource files. -For example, all `android_resources` dependencies of an `android_apk` will -end up packaged into the final APK by the build system. - -It uses the following keys: - - -* `deps_info['res_sources_path']`: -Path to file containing a list of resource source files used by the -android_resources target. - -* `deps_info['resources_zip']`: -*Required*. Path to the `.resources.zip` file that contains all raw/uncompiled -resource files for this target (and also no `R.txt`, `R.java` or `R.class`). - - If `deps_info['res_sources_path']` is missing, this must point to a prebuilt - `.aar` archive containing resources. Otherwise, this will point to a zip - archive generated at build time, wrapping the sources listed in - `deps_info['res_sources_path']` into a single zip file. - -* `deps_info['package_name']`: -Java package name that the R class for this target belongs to. - -* `deps_info['android_manifest']`: -Optional. Path to the top-level Android manifest file associated with these -resources (if not provided, an empty manifest will be used to generate R.txt). - -* `deps_info['resource_overlay']`: -Optional. Whether the resources in resources_zip should override resources with -the same name. Does not affect the behaviour of any android_resources() -dependencies of this target. If a target with resource_overlay=true depends -on another target with resource_overlay=true the target with the dependency -overrides the other. - -* `deps_info['r_text_path']`: -Provide the path to the `R.txt` file that describes the resources wrapped by -this target. Normally this file is generated from the content of the resource -directories or zip file, but some targets can provide their own `R.txt` file -if they want. - -* `deps_info['srcjar_path']`: -Path to the `.srcjar` file that contains the auto-generated `R.java` source -file corresponding to the content of `deps_info['r_text_path']`. This is -*always* generated from the content of `deps_info['r_text_path']` by the -`build/android/gyp/process_resources.py` script. - -## <a name="target_android_assets">Target type `android_assets`</a>: - -This type corresponds to targets used to group Android assets, i.e. liberal -files that will be placed under `//assets/` within the final APK. - -These use an `deps_info['assets']` key to hold a dictionary of values related -to assets covered by this target. - -* `assets['sources']`: -The list of all asset source paths for this target. Each source path can -use an optional `:<zipPath>` suffix, where `<zipPath>` is the final location -of the assets (relative to `//assets/`) within the APK. - -* `assets['outputs']`: -Optional. Some of the sources might be renamed before being stored in the -final //assets/ sub-directory. When this happens, this contains a list of -all renamed output file paths - - NOTE: When not empty, the first items of `assets['sources']` must match - every item in this list. Extra sources correspond to non-renamed sources. - - NOTE: This comes from the `asset_renaming_destinations` parameter for the - `android_assets()` GN template. - -* `assets['disable_compression']`: -Optional. Will be True to indicate that these assets should be stored -uncompressed in the final APK. For example, this is necessary for locale -.pak files used by the System WebView feature. - -* `assets['treat_as_locale_paks']`: -Optional. Will be True to indicate that these assets are locale `.pak` files -(containing localized strings for C++). These are later processed to generate -a special ``.build_config`.java` source file, listing all supported Locales in -the current build. - - -## <a name="target_java_library">Target type `java_library`</a>: - -This type is used to describe target that wrap Java bytecode, either created -by compiling sources, or providing them with a prebuilt jar. - -* `deps_info['public_deps_configs']`: List of paths to the `.build_config` files -of *direct* dependencies of the current target which are exposed as part of the -current target's public API. - -* `deps_info['unprocessed_jar_path']`: -Path to the original .jar file for this target, before any kind of processing -through Proguard or other tools. For most targets this is generated -from sources, with a name like `$target_name.javac.jar`. However, when using -a prebuilt jar, this will point to the source archive directly. - -* `deps_info['processed_jar_path']`: -Path to a file that is the result of processing -`deps_info['unprocessed_jar_path']` with various tools (ready to be dexed). - -* `deps_info['interface_jar_path']: -Path to the interface jar generated for this library. This corresponds to -a jar file that only contains declarations. Generated by running the `ijar` on -`deps_info['unprocessed_jar_path']` or the `turbine` tool on source files. - -* `deps_info['dex_path']`: -Path to the `.dex` file generated for this target, from -`deps_info['processed_jar_path']` unless this comes from a prebuilt `.aar` archive. - -* `deps_info['is_prebuilt']`: -True to indicate that this target corresponds to a prebuilt `.jar` file. -In this case, `deps_info['unprocessed_jar_path']` will point to the source -`.jar` file. Otherwise, it will be point to a build-generated file. - -* `deps_info['target_sources_file']`: -Path to a single `.sources` file listing all the Java and Kotlin sources that -were used to generate the library (simple text format, one `.jar` path per -line). - -* `deps_info['lint_android_manifest']`: -Path to an AndroidManifest.xml file to use for this lint target. - -* `deps_info['lint_sources']`: -The list of all `deps_info['target_sources_file']` entries for all library -dependencies that are chromium code. Note: this is a list of files, where each -file contains a list of Java and Kotlin source files. This is used for lint. - -* `deps_info['lint_aars']`: -List of all aars from transitive java dependencies. This allows lint to collect -their custom annotations.zip and run checks like @IntDef on their annotations. - -* `deps_info['lint_srcjars']`: -List of all bundled srcjars of all transitive java library targets. Excludes -non-chromium java libraries. - -* `deps_info['lint_resource_sources']`: -List of all resource sources files belonging to all transitive resource -dependencies of this target. Excludes resources owned by non-chromium code. - -* `deps_info['lint_resource_zips']`: -List of all resource zip files belonging to all transitive resource dependencies -of this target. Excludes resources owned by non-chromium code. - -* `deps_info['javac']`: -A dictionary containing information about the way the sources in this library -are compiled. Appears also on other Java-related targets. See the [dedicated -section about this](#dict_javac) below for details. - -* `deps_info['javac_full_classpath']`: -The classpath used when performing bytecode processing. Essentially the -collection of all `deps_info['unprocessed_jar_path']` entries for the target -and all its dependencies. - -* `deps_info['javac_full_interface_classpath']`: -The classpath used when using the errorprone compiler. - -* `deps_info['proguard_enabled"]`: -True to indicate that ProGuard processing is enabled for this target. - -* `deps_info['proguard_configs"]`: -A list of paths to ProGuard configuration files related to this library. - -* `deps_info['extra_classpath_jars']: -For some Java related types, a list of extra `.jar` files to use at build time -but not at runtime. - -## <a name="target_java_binary">Target type `java_binary`</a>: - -This type corresponds to a Java binary, which is nothing more than a -`java_library` target that also provides a main class name. It thus inherits -all entries from the `java_library` type, and adds: - -* `deps_info['main_class']`: -Name of the main Java class that serves as an entry point for the binary. - -* `deps_info['device_classpath']`: -The classpath used when running a Java or Android binary. Essentially the -collection of all `deps_info['processed_jar_path']` entries for the target and all -its dependencies. - -* `deps_info['all_dex_files']`: -The list of paths to all `deps_info['dex_path']` entries for all libraries -that comprise this APK. Valid only for debug builds. - -* `deps_info['preferred_dep']`: -Whether the target should be the preferred dep. This is usually the case when we -have a java_group that depends on either the public or internal dep accordingly, -and it is better to depend on the group rather than the underlying dep. Another -case is for android_library_factory targets, the factory target should be -preferred instead of the actual implementation. - -## <a name="target_robolectric_binary">Target type `robolectric_binary`</a>: - -A target type for JUnit-specific binaries. Identical to -[`java_binary`](#target_java_binary) in the context of `.build_config` files, -except the name. - - -## <a name="target_java_annotation_processor">Target type \ -`java_annotation_processor`</a>: - -A target type for Java annotation processors. Identical to -[`java_binary`](#target_java_binary) in the context of `.build_config` files, -except the name, except that it requires a `deps_info['main_class']` entry. - - -## <a name="target_android_apk">Target type `android_apk`</a>: - -Corresponds to an Android APK. Inherits from the -[`java_binary`](#target_java_binary) type and adds: - -* `deps_info['apk_path']`: -Path to the raw, unsigned, APK generated by this target. - -* `deps_info['incremental_apk_path']`: -Path to the raw, unsigned, incremental APK generated by this target. - -* `deps_info['incremental_install_json_path']`: -Path to the JSON file with per-apk details for incremental install. -See `build/android/gyp/incremental/write_installer_json.py` for more -details about its content. - -* `deps_info['dist_jar']['all_interface_jars']`: -For `android_apk` and `dist_jar` targets, a list of all interface jar files -that will be merged into the final `.jar` file for distribution. - -* `deps_info['final_dex']['path']`: -Path to the final classes.dex file (or classes.zip in case of multi-dex) -for this APK - only used for proguarded builds. - -* `native['libraries']` -List of native libraries for the primary ABI to be embedded in this APK. -E.g. [ "libchrome.so" ] (i.e. this doesn't include any ABI sub-directory -prefix). - -* `native['java_libraries_list']` -The same list as `native['libraries']` as a string holding a Java source -fragment, e.g. `"{\"chrome\"}"`, without any `lib` prefix, and `.so` -suffix (as expected by `System.loadLibrary()`). - -* `native['second_abi_libraries']` -List of native libraries for the secondary ABI to be embedded in this APK. -Empty if only a single ABI is supported. - -* `native['loadable_modules']` -A list of native libraries to store within the APK, in addition to those from -`native['libraries']`. These correspond to things like the Chromium linker -or instrumentation libraries. - -* `native['secondary_abi_loadable_modules']` -Secondary ABI version of loadable_modules - -* `native['library_always_compress']` -A list of library files that we always compress. - -* `assets` -A list of assets stored compressed in the APK. Each entry has the format -`<source-path>:<destination-path>`, where `<source-path>` is relative to -`$CHROMIUM_OUTPUT_DIR`, and `<destination-path>` is relative to `//assets/` -within the APK. - -NOTE: Not to be confused with the `deps_info['assets']` dictionary that -belongs to `android_assets` targets only. - -* `uncompressed_assets` -A list of uncompressed assets stored in the APK. Each entry has the format -`<source-path>:<destination-path>` too. - -* `locales_java_list` -A string holding a Java source fragment that gives the list of locales stored -uncompressed as android assets. - -* `extra_android_manifests` -A list of `deps_configs['android_manifest]` entries, for all resource -dependencies for this target. I.e. a list of paths to manifest files for -all the resources in this APK. These will be merged with the root manifest -file to generate the final one used to build the APK. - -* `java_resources_jars` -This is a list of `.jar` files whose *Java* resources should be included in -the final APK. For example, this is used to copy the `.res` files from the -EMMA Coverage tool. The copy will omit any `.class` file and the top-level -`//meta-inf/` directory from the input jars. Everything else will be copied -into the final APK as-is. - -NOTE: This has nothing to do with *Android* resources. - -* `deps_info['proguard_all_configs']`: -The collection of all 'deps_info['proguard_configs']` values from this target -and all its dependencies. - -* `deps_info['proguard_classpath_jars']`: -The collection of all 'deps_info['extra_classpath_jars']` values from all -dependencies. - -* `deps_info['proguard_under_test_mapping']`: -Applicable to apks with proguard enabled that have an apk_under_test. This is -the path to the apk_under_test's output proguard .mapping file. - -## <a name="target_android_app_bundle_module">Target type \ -`android_app_bundle_module`</a>: - -Corresponds to an Android app bundle module. Very similar to an APK and -inherits the same fields, except that this does not generate an installable -file (see `android_app_bundle`), and for the following omitted fields: - -* `deps_info['apk_path']`, `deps_info['incremental_apk_path']` and - `deps_info['incremental_install_json_path']` are omitted. - -* top-level `dist_jar` is omitted as well. - -In addition to `android_apk` targets though come these new fields: - -* `deps_info['proto_resources_path']`: -The path of an zip archive containing the APK's resources compiled to the -protocol buffer format (instead of regular binary xml + resources.arsc). - -* `deps_info['module_rtxt_path']`: -The path of the R.txt file generated when compiling the resources for the bundle -module. - -* `deps_info['module_pathmap_path']`: -The path of the pathmap file generated when compiling the resources for the -bundle module, if resource path shortening is enabled. - -* `deps_info['base_allowlist_rtxt_path']`: -Optional path to an R.txt file used as a allowlist for base string resources. -This means that any string resource listed in this file *and* in -`deps_info['module_rtxt_path']` will end up in the base split APK of any -`android_app_bundle` target that uses this target as its base module. - -This ensures that such localized strings are available to all bundle installs, -even when language based splits are enabled (e.g. required for WebView strings -inside the Monochrome bundle). - - -## <a name="target_android_app_bundle">Target type `android_app_bundle`</a> - -This target type corresponds to an Android app bundle, and is built from one -or more `android_app_bundle_module` targets listed as dependencies. - - -## <a name="target_dist_aar">Target type `dist_aar`</a>: - -This type corresponds to a target used to generate an `.aar` archive for -distribution. The archive's content is determined by the target's dependencies. - -This always has the following entries: - - * `deps_info['supports_android']` (always True). - * `deps_info['requires_android']` (always True). - * `deps_info['proguard_configs']` (optional). - - -## <a name="target_dist_jar">Target type `dist_jar`</a>: - -This type is similar to [`dist_aar`](#target_dist_aar) but is not -Android-specific, and used to create a `.jar` file that can be later -redistributed. - -This always has the following entries: - - * `deps_info['proguard_enabled']` (False by default). - * `deps_info['proguard_configs']` (optional). - * `deps_info['supports_android']` (True by default). - * `deps_info['requires_android']` (False by default). - - - -## <a name="dict_javac">The `deps_info['javac']` dictionary</a>: - -This dictionary appears in Java-related targets (e.g. `java_library`, -`android_apk` and others), and contains information related to the compilation -of Java sources, class files, and jars. - -* `javac['classpath']` -The classpath used to compile this target when annotation processors are -present. - -* `javac['interface_classpath']` -The classpath used to compile this target when annotation processors are -not present. These are also always used to known when a target needs to be -rebuilt. - -* `javac['processor_classpath']` -The classpath listing the jars used for annotation processors. I.e. sent as -`-processorpath` when invoking `javac`. - -* `javac['processor_classes']` -The list of annotation processor main classes. I.e. sent as `-processor' when -invoking `javac`. - -## <a name="android_app_bundle">Target type `android_app_bundle`</a>: - -This type corresponds to an Android app bundle (`.aab` file). - ---------------- END_MARKDOWN --------------------------------------------------- +See //build/android/docs/build_config.md """ +import argparse import collections import itertools import json -import optparse import os import shutil import sys import xml.dom.minidom from util import build_utils -from util import resource_utils import action_helpers # build_utils adds //build to sys.path. @@ -592,26 +55,6 @@ self.remove(v) -def _ExtractMarkdownDocumentation(input_text): - """Extract Markdown documentation from a list of input strings lines. - - This generates a list of strings extracted from |input_text|, by looking - for '-- BEGIN_MARKDOWN --' and '-- END_MARKDOWN --' line markers.""" - in_markdown = False - result = [] - for line in input_text.splitlines(): - if in_markdown: - if '-- END_MARKDOWN --' in line: - in_markdown = False - else: - result.append(line) - else: - if '-- BEGIN_MARKDOWN --' in line: - in_markdown = True - - return result - - class AndroidManifest: def __init__(self, path): self.path = path @@ -642,15 +85,19 @@ return self.manifest.getAttribute('package') -def GetDepConfigRoot(path): - if not path in _dep_config_cache: - with open(path) as jsonfile: - _dep_config_cache[path] = json.load(jsonfile) - return _dep_config_cache[path] +def ReadJson(path): + with open(path) as f: + return json.load(f) def GetDepConfig(path): - return GetDepConfigRoot(path)['deps_info'] + if ret := _dep_config_cache.get(path): + return ret + config = ReadJson(path.replace('.build_config.json', '.params.json')) + config.update(ReadJson(path)) + config['path'] = path + _dep_config_cache[path] = config + return config def DepsOfType(wanted_type, configs): @@ -669,7 +116,8 @@ def discover(path): config = GetDepConfig(path) - all_deps = config['deps_configs'] + config.get('public_deps_configs', []) + all_deps = (config.get('deps_configs', []) + + config.get('public_deps_configs', [])) return apply_filter(all_deps) deps_config_paths = apply_filter(deps_config_paths) @@ -723,7 +171,7 @@ def helper(cur): for config in cur.Direct('java_library'): - if config['is_prebuilt'] or config['gradle_treat_as_prebuilt']: + if config['is_prebuilt'] or config.get('gradle_treat_as_prebuilt'): if config['unprocessed_jar_path'] not in ret: ret.append(config['unprocessed_jar_path']) @@ -737,8 +185,8 @@ for config in cur.Direct('java_library'): if config['is_prebuilt']: pass - elif config['gradle_treat_as_prebuilt']: - all_deps = config['deps_configs'] + config.get( + elif config.get('gradle_treat_as_prebuilt'): + all_deps = config.get('deps_configs', []) + config.get( 'public_deps_configs', []) helper(Deps(all_deps)) elif config not in ret: @@ -805,11 +253,11 @@ if config['type'] == 'group': # Groups combine public_deps with deps_configs, so no need to check # public_config_paths separately. - return config['deps_configs'] + return config.get('deps_configs', []) if config['type'] == 'android_resources': # android_resources targets do not support public_deps, but instead treat # all resource deps as public deps. - return DepPathsOfType('android_resources', config['deps_configs']) + return DepPathsOfType('android_resources', config.get('deps_configs', [])) return config.get('public_deps_configs', []) @@ -932,43 +380,19 @@ return '{%s}' % ','.join('"%s"' % l for l in sorted(locales)) -def _AddJarMapping(jar_to_target, configs): - for config in configs: - jar = config.get('unprocessed_jar_path') - if jar: - jar_to_target[jar] = config['gn_target'] - for jar in config.get('extra_classpath_jars', []): - jar_to_target[jar] = config['gn_target'] +def _AddJarMapping(jar_to_target, config): + if jar := config.get('unprocessed_jar_path'): + jar_to_target[jar] = config['gn_target'] + for jar in config.get('input_jars_paths', []): + jar_to_target[jar] = config['gn_target'] def _CompareClasspathPriority(dep): return 1 if dep.get('low_classpath_priority') else 0 -def _DedupFeatureModuleSharedCode(uses_split_arg, modules, +def _DedupFeatureModuleSharedCode(child_to_ancestors, modules, field_names_to_dedup): - child_to_ancestors = collections.defaultdict(list) - if uses_split_arg: - for split_pair in uses_split_arg: - child, parent = split_pair.split(':') - assert child in modules - assert parent in modules - child_to_ancestors[child] = [parent] - - # Create a full list of ancestors for each module. - for name in modules: - if name == 'base': - continue - curr_name = name - while curr_name in child_to_ancestors: - parent = child_to_ancestors[curr_name][0] - if parent not in child_to_ancestors[name]: - child_to_ancestors[name].append(parent) - curr_name = parent - - if curr_name != 'base': - child_to_ancestors[name].append('base') - # Strip out duplicates from ancestors. for name, module in modules.items(): if name == 'base': @@ -1036,333 +460,63 @@ print(f'Copied {len(_dep_config_cache)} .build_config.json into {debug_dir}') -def main(argv): - parser = optparse.OptionParser() +def _ListFromDeps(deps, key_name): + return [config[key_name] for config in deps if key_name in config] + + +def _SetFromDeps(deps, key_name): + combined = set() + for config in deps: + if value := config.get(key_name): + if isinstance(value, str): + combined.add(value) + else: + combined.update(value) + return combined + + +def main(): + parser = argparse.ArgumentParser() action_helpers.add_depfile_arg(parser) - parser.add_option('--build-config', help='Path to build_config output.') - parser.add_option('--store-deps-for-debugging-to', - help='Path to copy all transitive build config files to.') - parser.add_option( - '--type', - help='Type of this target (e.g. android_library).') - parser.add_option('--gn-target', help='GN label for this target') - parser.add_option( - '--deps-configs', - help='GN-list of dependent build_config files.') - parser.add_option( - '--annotation-processor-configs', - help='GN-list of build_config files for annotation processors.') + parser.add_argument('--params', help='Path to .params.json file.') + parser.add_argument('--store-deps-for-debugging-to', + help='Path to copy all transitive build config files to.') + options = parser.parse_args() - # android_resources options - parser.add_option('--srcjar', help='Path to target\'s resources srcjar.') - parser.add_option('--resources-zip', help='Path to target\'s resources zip.') - parser.add_option('--package-name', - help='Java package name for these resources.') - parser.add_option('--android-manifest', - help='Path to the root android manifest.') - parser.add_option('--merged-android-manifest', - help='Path to the merged android manifest.') - parser.add_option('--resource-dirs', action='append', default=[], - help='GYP-list of resource dirs') - parser.add_option( - '--res-sources-path', - help='Path to file containing a list of paths to resources.') - parser.add_option( - '--resource-overlay', - action='store_true', - help='Whether resources passed in via --resources-zip should override ' - 'resources with the same name') - parser.add_option( - '--recursive-resource-deps', - action='store_true', - help='Whether deps should be walked recursively to find resource deps.') + params = ReadJson(options.params) + output_path = options.params.replace('.params.json', '.build_config.json') - # android_assets options - parser.add_option('--asset-sources', help='List of asset sources.') - parser.add_option('--asset-renaming-sources', - help='List of asset sources with custom destinations.') - parser.add_option('--asset-renaming-destinations', - help='List of asset custom destinations.') - parser.add_option('--disable-asset-compression', action='store_true', - help='Whether to disable asset compression.') - parser.add_option('--treat-as-locale-paks', action='store_true', - help='Consider the assets as locale paks in BuildConfig.java') + if lines := params.get('fail'): + parser.error('\n'.join(lines)) - # java library and group options - parser.add_option('--preferred-dep', - action='store_true', - help='Whether the target should be preferred as a dep.') + target_type = params['type'] - # java library options - parser.add_option('--public-deps-configs', - help='GN list of config files of deps which are exposed as ' - 'part of the target\'s public API.') - parser.add_option('--aar-path', help='Path to containing .aar file.') - parser.add_option('--processed-jar-path', - help='Path to .jar to use at runtime.') - parser.add_option('--unprocessed-jar-path', - help='Path to the .jar to use for javac classpath purposes.') - parser.add_option( - '--interface-jar-path', - help='Path to the interface .jar to use for javac classpath purposes.') - parser.add_option('--is-prebuilt', action='store_true', - help='Whether the jar was compiled or pre-compiled.') - parser.add_option('--target-sources-file', help='Path to .sources file') - parser.add_option('--bundled-srcjars', - help='GYP-list of .srcjars that have been included in this java_library.') - parser.add_option('--supports-android', action='store_true', - help='Whether this library supports running on the Android platform.') - parser.add_option('--requires-android', action='store_true', - help='Whether this library requires running on the Android platform.') - parser.add_option('--bypass-platform-checks', action='store_true', - help='Bypass checks for support/require Android platform.') - parser.add_option('--extra-classpath-jars', - help='GYP-list of .jar files to include on the classpath when compiling, ' - 'but not to include in the final binary.') - parser.add_option( - '--low-classpath-priority', - action='store_true', - help='Indicates that the library should be placed at the end of the ' - 'classpath.') - parser.add_option( - '--mergeable-android-manifests', - help='GN-list of AndroidManifest.xml to include in manifest merging.') - parser.add_option('--gradle-treat-as-prebuilt', action='store_true', - help='Whether this library should be treated as a prebuilt library by ' - 'generate_gradle.py.') - parser.add_option('--main-class', - help='Main class for java_binary or java_annotation_processor targets.') - parser.add_option('--java-resources-jar-path', - help='Path to JAR that contains java resources. Everything ' - 'from this JAR except meta-inf/ content and .class files ' - 'will be added to the final APK.') - parser.add_option( - '--non-chromium-code', - action='store_true', - help='True if a java library is not chromium code, used for lint.') + is_bundle_module = target_type == 'android_app_bundle_module' + is_apk = target_type == 'android_apk' + is_apk_or_module = is_apk or is_bundle_module + is_bundle = target_type == 'android_app_bundle' - # robolectric_library options - parser.add_option('--is-robolectric', - action='store_true', - help='Whether this is a host side android test library.') + is_java_target = target_type in ('java_binary', 'robolectric_binary', + 'java_annotation_processor', 'java_library', + 'android_apk', 'dist_aar', 'dist_jar', + 'system_java_library', + 'android_app_bundle_module') - # android library options - parser.add_option('--dex-path', help='Path to target\'s dex output.') - - # native library options - parser.add_option('--shared-libraries-runtime-deps', - help='Path to file containing runtime deps for shared ' - 'libraries.') - parser.add_option( - '--loadable-modules', - action='append', - help='GN-list of native libraries for primary ' - 'android-abi. Can be specified multiple times.', - default=[]) - parser.add_option('--secondary-abi-shared-libraries-runtime-deps', - help='Path to file containing runtime deps for secondary ' - 'abi shared libraries.') - parser.add_option( - '--secondary-abi-loadable-modules', - action='append', - help='GN-list of native libraries for secondary ' - 'android-abi. Can be specified multiple times.', - default=[]) - parser.add_option( - '--native-lib-placeholders', - action='append', - help='GN-list of native library placeholders to add.', - default=[]) - parser.add_option( - '--secondary-native-lib-placeholders', - action='append', - help='GN-list of native library placeholders to add ' - 'for the secondary android-abi.', - default=[]) - parser.add_option('--uncompress-shared-libraries', default=False, - action='store_true', - help='Whether to store native libraries uncompressed') - parser.add_option( - '--library-always-compress', - help='The list of library files that we always compress.') - - # apk options - parser.add_option('--apk-path', help='Path to the target\'s apk output.') - parser.add_option('--incremental-apk-path', - help="Path to the target's incremental apk output.") - parser.add_option('--incremental-install-json-path', - help="Path to the target's generated incremental install " - "json.") - parser.add_option( - '--suffix-apk-assets-used-by', - help='Path to the build config of the apk whose asset list should be ' - 'suffixed.') - parser.add_option( - '--tested-apk-config', - help='Path to the build config of the tested apk (for an instrumentation ' - 'test apk).') - parser.add_option( - '--proguard-enabled', - action='store_true', - help='Whether proguard is enabled for this apk or bundle module.') - parser.add_option( - '--proguard-configs', - help='GN-list of proguard flag files to use in final apk.') - parser.add_option( - '--proguard-mapping-path', help='Path to jar created by ProGuard step') - - # apk options that are static library specific - parser.add_option( - '--static-library-dependent-configs', - help='GN list of .build_configs of targets that use this target as a ' - 'static library.') - - # options shared between android_resources and apk targets - parser.add_option('--r-text-path', help='Path to target\'s R.txt file.') - - parser.add_option('--fail', - help='GN-list of error message lines to fail with.') - - parser.add_option('--final-dex-path', - help='Path to final input classes.dex (or classes.zip) to ' - 'use in final apk.') - parser.add_option('--res-size-info', help='Path to .ap_.info') - parser.add_option('--apk-proto-resources', - help='Path to resources compiled in protocol buffer format ' - ' for this apk.') - parser.add_option( - '--module-pathmap-path', - help='Path to pathmap file for resource paths in a bundle module.') - parser.add_option( - '--base-allowlist-rtxt-path', - help='Path to R.txt file for the base resources allowlist.') - - parser.add_option('--generate-markdown-format-doc', action='store_true', - help='Dump the Markdown .build_config format documentation ' - 'then exit immediately.') - - parser.add_option('--module-name', help='The name of this feature module.') - parser.add_option( - '--base-module-build-config', - help='Path to the base module\'s build config ' - 'if this is a feature module.') - parser.add_option('--parent-module-build-config', - help='Path to the parent module\'s build config ' - 'when not using base module as parent.') - - parser.add_option( - '--module-build-configs', - help='For bundles, the paths of all non-async module .build_configs ' - 'for modules that are part of the bundle.') - parser.add_option( - '--uses-split', - action='append', - help='List of name pairs separated by : mapping a feature module to a ' - 'dependent feature module.') - - parser.add_option( - '--trace-events-jar-dir', - help='Directory of rewritten .jar files for trace event rewriting.') - - parser.add_option('--version-name', help='Version name for this APK.') - parser.add_option('--version-code', help='Version code for this APK.') - - # dist_jar options - parser.add_option('--use-interface-jars', action='store_true') - parser.add_option('--direct-deps-only', action='store_true') - - options, args = parser.parse_args(argv) - - if args: - parser.error('No positional arguments should be given.') - - if options.generate_markdown_format_doc: - doc_lines = _ExtractMarkdownDocumentation(__doc__) - for line in doc_lines: - print(line) - return 0 - - if options.fail: - parser.error('\n'.join(action_helpers.parse_gn_list(options.fail))) - - lib_options = ['unprocessed_jar_path', 'interface_jar_path'] - device_lib_options = ['processed_jar_path', 'dex_path'] - required_options_map = { - 'android_apk': ['build_config'] + lib_options + device_lib_options, - 'android_app_bundle_module': - ['build_config', 'res_size_info'] + lib_options + device_lib_options, - 'android_assets': ['build_config'], - 'android_resources': ['build_config', 'resources_zip'], - 'dist_aar': ['build_config'], - 'dist_jar': ['build_config'], - 'group': ['build_config'], - 'java_annotation_processor': ['build_config', 'main_class'], - 'java_binary': ['build_config'], - 'java_library': ['build_config', 'processed_jar_path'] + lib_options, - 'robolectric_binary': ['build_config'], - 'system_java_library': ['build_config', 'unprocessed_jar_path'], - 'android_app_bundle': ['build_config', 'module_build_configs'], - } - required_options = required_options_map.get(options.type) - if not required_options: - raise Exception('Unknown type: <%s>' % options.type) - - build_utils.CheckOptions(options, parser, required_options) - - if options.type != 'android_app_bundle_module': - if options.apk_proto_resources: - raise Exception('--apk-proto-resources can only be used with ' - '--type=android_app_bundle_module') - if options.module_pathmap_path: - raise Exception('--module-pathmap-path can only be used with ' - '--type=android_app_bundle_module') - if options.base_allowlist_rtxt_path: - raise Exception('--base-allowlist-rtxt-path can only be used with ' - '--type=android_app_bundle_module') - if options.module_name: - raise Exception('--module-name can only be used with ' - '--type=android_app_bundle_module') - - is_apk_or_module_target = options.type in ('android_apk', - 'android_app_bundle_module') - - if not is_apk_or_module_target: - if options.library_always_compress: - raise Exception( - '--library-always-compress can only be used with --type=android_apk ' - 'or --type=android_app_bundle_module') - - if any(getattr(options, x) for x in lib_options): - for attr in lib_options: - if not getattr(options, attr): - raise('Expected %s to be set.' % attr) - - if options.requires_android and not options.supports_android: - raise Exception( - '--supports-android is required when using --requires-android') - - is_java_target = options.type in ('java_binary', 'robolectric_binary', - 'java_annotation_processor', 'java_library', - 'android_apk', 'dist_aar', 'dist_jar', - 'system_java_library', - 'android_app_bundle_module') - - deps_configs_paths = action_helpers.parse_gn_list(options.deps_configs) - public_deps_configs_paths = action_helpers.parse_gn_list( - options.public_deps_configs) + deps_configs_paths = params.get('deps_configs', []) + public_deps_configs_paths = params.get('public_deps_configs', []) deps_configs_paths += public_deps_configs_paths - deps = _DepsFromPaths(deps_configs_paths, - options.type, - recursive_resource_deps=options.recursive_resource_deps) - public_deps = _DepsFromPaths(public_deps_configs_paths, options.type) - processor_deps = _DepsFromPaths(action_helpers.parse_gn_list( - options.annotation_processor_configs or ''), - options.type, + deps = _DepsFromPaths( + deps_configs_paths, + target_type, + recursive_resource_deps=params.get('recursive_resource_deps')) + public_deps = _DepsFromPaths(public_deps_configs_paths, target_type) + processor_deps = _DepsFromPaths(params.get('processor_configs', []), + target_type, filter_root_targets=False) - all_inputs = (deps.AllConfigPaths() + processor_deps.AllConfigPaths()) + all_inputs = deps.AllConfigPaths() + processor_deps.AllConfigPaths() - if options.recursive_resource_deps: + if params.get('recursive_resource_deps'): # Include java_library targets since changes to these targets can remove # resource deps from the build, which would require rebuilding this target's # build config file: crbug.com/1168655. @@ -1376,7 +530,7 @@ all_library_deps = deps.All('java_library') direct_resources_deps = deps.Direct('android_resources') - if options.type == 'java_library': + if target_type == 'java_library': # For Java libraries, restrict to resource targets that are direct deps, or # are indirect via other resource targets. # The indirect-through-other-targets ones are picked up because @@ -1386,7 +540,8 @@ else: all_resources_deps = deps.All('android_resources') - if options.type == 'android_resources' and options.recursive_resource_deps: + if target_type == 'android_resources' and params.get( + 'recursive_resource_deps'): # android_resources targets that want recursive resource deps also need to # collect package_names from all library deps. This ensures the R.java files # for these libraries will get pulled in along with the resources. @@ -1394,29 +549,13 @@ deps_configs_paths, allowlist=['java_library']).All('java_library') base_module_build_config = None - if options.base_module_build_config: - base_module_build_config = GetDepConfigRoot( - options.base_module_build_config) + if path := params.get('base_module_config'): + base_module_build_config = GetDepConfig(path) parent_module_build_config = base_module_build_config - if options.parent_module_build_config: - parent_module_build_config = GetDepConfigRoot( - options.parent_module_build_config) + if path := params.get('parent_module_config'): + parent_module_build_config = GetDepConfig(path) - # Initialize some common config. - # Any value that needs to be queryable by dependents must go within deps_info. - config = { - 'deps_info': { - 'name': os.path.basename(options.build_config), - 'path': options.build_config, - 'type': options.type, - 'gn_target': options.gn_target, - 'chromium_code': not options.non_chromium_code, - }, - # Info needed only by generate_gradle.py. - 'gradle': {} - } - deps_info = config['deps_info'] - gradle = config['gradle'] + config = collections.defaultdict(dict) # The paths we record as deps can differ from deps_config_paths: # 1) Paths can be removed when blocked by _ROOT_TYPES / _RESOURCE_TYPES. @@ -1429,50 +568,30 @@ # such an undertaking so far. public_deps_set = set() if public_deps_configs_paths: - deps_info['public_deps_configs'] = [d['path'] for d in public_deps.Direct()] - public_deps_set = set(deps_info['public_deps_configs']) + resolved_public_deps_configs = [d['path'] for d in public_deps.Direct()] + if resolved_public_deps_configs != params.get('public_deps_configs', []): + config['public_deps_configs'] = resolved_public_deps_configs + public_deps_set.update(resolved_public_deps_configs) - deps_info['deps_configs'] = [ + resolved_deps_configs = [ d['path'] for d in deps.Direct() if d['path'] not in public_deps_set ] + if resolved_deps_configs != params.get('deps_configs', []): + config['deps_configs'] = resolved_deps_configs - if options.type == 'android_apk' and options.tested_apk_config: - tested_apk_deps = Deps([options.tested_apk_config]) - tested_apk_config = tested_apk_deps.Direct()[0] - gradle['apk_under_test'] = tested_apk_config['name'] - - if options.type == 'android_app_bundle_module': - deps_info['module_name'] = options.module_name - - # Required for generating gradle files. - if options.type == 'java_library': - deps_info['is_prebuilt'] = bool(options.is_prebuilt) - deps_info['gradle_treat_as_prebuilt'] = options.gradle_treat_as_prebuilt - - if options.preferred_dep: - deps_info['preferred_dep'] = bool(options.preferred_dep) - - if options.android_manifest: - deps_info['android_manifest'] = options.android_manifest - - if options.merged_android_manifest: - deps_info['merged_android_manifest'] = options.merged_android_manifest - - if options.bundled_srcjars: - deps_info['bundled_srcjars'] = action_helpers.parse_gn_list( - options.bundled_srcjars) - - if options.target_sources_file: - deps_info['target_sources_file'] = options.target_sources_file + apk_under_test_config = None + if is_apk: + config['apk_path'] = params['apk_path'] + if path := params.get('apk_under_test_config'): + apk_under_test_config = GetDepConfig(path) + config['gradle']['apk_under_test'] = os.path.basename( + apk_under_test_config['apk_path']) if is_java_target: - if options.main_class: - deps_info['main_class'] = options.main_class - dependent_prebuilt_jars = deps.GradlePrebuiltJarPaths() dependent_prebuilt_jars.sort() if dependent_prebuilt_jars: - gradle['dependent_prebuilt_jars'] = dependent_prebuilt_jars + config['gradle']['dependent_prebuilt_jars'] = dependent_prebuilt_jars dependent_android_projects = [] dependent_java_projects = [] @@ -1482,42 +601,15 @@ else: dependent_java_projects.append(c['path']) - gradle['dependent_android_projects'] = dependent_android_projects - gradle['dependent_java_projects'] = dependent_java_projects - - if options.r_text_path: - deps_info['r_text_path'] = options.r_text_path - - if is_apk_or_module_target or options.type in ('group', 'java_library', - 'robolectric_binary'): - if options.apk_proto_resources: - deps_info['proto_resources_path'] = options.apk_proto_resources - - deps_info['version_name'] = options.version_name - deps_info['version_code'] = options.version_code - if options.module_pathmap_path: - deps_info['module_pathmap_path'] = options.module_pathmap_path - else: - # Ensure there is an entry, even if it is empty, for modules - # that have not enabled resource path shortening. Otherwise - # build_utils.ExpandFileArgs fails. - deps_info['module_pathmap_path'] = '' - - if options.base_allowlist_rtxt_path: - deps_info['base_allowlist_rtxt_path'] = options.base_allowlist_rtxt_path - else: - # Ensure there is an entry, even if it is empty, for modules - # that don't need such a allowlist. - deps_info['base_allowlist_rtxt_path'] = '' + config['gradle']['dependent_android_projects'] = dependent_android_projects + config['gradle']['dependent_java_projects'] = dependent_java_projects if is_java_target: - deps_info['requires_android'] = bool(options.requires_android) - deps_info['supports_android'] = bool(options.supports_android) - # robolectric is special in that its an android target that runs on host. # You are allowed to depend on both android |deps_require_android| and # non-android |deps_not_support_android| targets. - if not options.bypass_platform_checks and not options.is_robolectric: + if (not params.get('bypass_platform_checks') + and not params.get('requires_robolectric')): deps_require_android = direct_resources_deps + [ d for d in deps.Direct() if d.get('requires_android', False) ] @@ -1525,87 +617,39 @@ d for d in deps.Direct() if not d.get('supports_android', True) ] - if deps_require_android and not options.requires_android: + if deps_require_android and not params.get('requires_android'): raise Exception( 'Some deps require building for the Android platform:\n' + '\n'.join('* ' + d['gn_target'] for d in deps_require_android)) - if deps_not_support_android and options.supports_android: + if deps_not_support_android and params.get('supports_android'): raise Exception('Not all deps support the Android platform:\n' + '\n'.join('* ' + d['gn_target'] for d in deps_not_support_android)) - if is_apk_or_module_target or options.type == 'dist_jar': + if is_apk_or_module or target_type == 'dist_jar': all_dex_files = [c['dex_path'] for c in all_library_deps] - if is_java_target: - # Classpath values filled in below (after applying tested_apk_config). - config['javac'] = {} - if options.aar_path: - deps_info['aar_path'] = options.aar_path - if options.unprocessed_jar_path: - deps_info['unprocessed_jar_path'] = options.unprocessed_jar_path - deps_info['interface_jar_path'] = options.interface_jar_path - if options.processed_jar_path: - deps_info['processed_jar_path'] = options.processed_jar_path - if options.dex_path: - deps_info['dex_path'] = options.dex_path - if is_apk_or_module_target: - all_dex_files.append(options.dex_path) - if options.low_classpath_priority: - deps_info['low_classpath_priority'] = True - if options.type == 'android_apk': - deps_info['apk_path'] = options.apk_path - deps_info['incremental_apk_path'] = options.incremental_apk_path - deps_info['incremental_install_json_path'] = ( - options.incremental_install_json_path) + # Classpath values filled in below (after applying apk_under_test_config). + if is_apk_or_module: + all_dex_files.append(params['dex_path']) - if options.type == 'android_assets': - all_asset_sources = [] - if options.asset_renaming_sources: - all_asset_sources.extend( - action_helpers.parse_gn_list(options.asset_renaming_sources)) - if options.asset_sources: - all_asset_sources.extend( - action_helpers.parse_gn_list(options.asset_sources)) + if is_bundle_module: + config['unprocessed_jar_path'] = params['unprocessed_jar_path'] + config['res_size_info_path'] = params['res_size_info_path'] - deps_info['assets'] = { - 'sources': all_asset_sources - } - if options.asset_renaming_destinations: - deps_info['assets']['outputs'] = (action_helpers.parse_gn_list( - options.asset_renaming_destinations)) - if options.disable_asset_compression: - deps_info['assets']['disable_compression'] = True - if options.treat_as_locale_paks: - deps_info['assets']['treat_as_locale_paks'] = True + if target_type == 'android_resources': + if not params.get('package_name'): + if path := params.get('android_manifest'): + manifest = AndroidManifest(path) + config['package_name'] = manifest.GetPackageName() - if options.type == 'android_resources': - deps_info['resources_zip'] = options.resources_zip - if options.resource_overlay: - deps_info['resource_overlay'] = True - if options.srcjar: - deps_info['srcjar'] = options.srcjar - if options.android_manifest: - manifest = AndroidManifest(options.android_manifest) - deps_info['package_name'] = manifest.GetPackageName() - if options.package_name: - deps_info['package_name'] = options.package_name - deps_info['res_sources_path'] = '' - if options.res_sources_path: - deps_info['res_sources_path'] = options.res_sources_path - - if (options.requires_android - and options.type == 'java_library') or options.is_robolectric: - if options.package_name: - deps_info['package_name'] = options.package_name - - if options.type in ('android_resources', 'android_apk', 'robolectric_binary', - 'dist_aar', 'android_app_bundle_module', 'java_library'): + if target_type in ('android_resources', 'android_apk', 'robolectric_binary', + 'dist_aar', 'android_app_bundle_module', 'java_library'): dependency_zips = [] dependency_zip_overlays = [] for c in all_resources_deps: - if not c['resources_zip']: + if not c.get('resources_zip'): continue dependency_zips.append(c['resources_zip']) @@ -1614,12 +658,10 @@ extra_package_names = [] - if options.type != 'android_resources': - extra_package_names = [ - c['package_name'] for c in all_resources_deps if 'package_name' in c - ] - if options.package_name: - extra_package_names += [options.package_name] + if target_type != 'android_resources': + extra_package_names = _ListFromDeps(all_resources_deps, 'package_name') + if name := params.get('package_name'): + extra_package_names.append(name) # android_resources targets which specified recursive_resource_deps may # have extra_package_names. @@ -1629,64 +671,43 @@ # In final types (i.e. apks and modules) that create real R.java files, # they must collect package names from java_libraries as well. # https://crbug.com/1073476 - if options.type != 'java_library': - extra_package_names.extend([ - c['package_name'] for c in all_library_deps if 'package_name' in c - ]) - elif options.recursive_resource_deps: + if target_type != 'java_library': + extra_package_names += _ListFromDeps(all_library_deps, 'package_name') + + elif params.get('recursive_resource_deps'): # Pull extra_package_names from library deps if recursive resource deps # are required. - extra_package_names = [ - c['package_name'] for c in android_resources_library_deps - if 'package_name' in c - ] - config['deps_info']['includes_recursive_resources'] = True + extra_package_names = _ListFromDeps(android_resources_library_deps, + 'package_name') - if options.type in ('dist_aar', 'java_library'): - r_text_files = [ - c['r_text_path'] for c in all_resources_deps if 'r_text_path' in c - ] - deps_info['dependency_r_txt_files'] = r_text_files + if target_type in ('dist_aar', 'java_library'): + paths = _ListFromDeps(all_resources_deps, 'rtxt_path') + config['dependency_rtxt_files'] = paths - if options.type == 'android_apk' and options.tested_apk_config: - config['deps_info']['arsc_package_name'] = ( - tested_apk_config['package_name']) + if is_apk and apk_under_test_config: + config['arsc_package_name'] = apk_under_test_config['package_name'] # We should not shadow the actual R.java files of the apk_under_test by # creating new R.java files with the same package names in the tested apk. extra_package_names = [ package for package in extra_package_names - if package not in tested_apk_config['extra_package_names'] + if package not in apk_under_test_config['extra_package_names'] ] - if options.res_size_info: - config['deps_info']['res_size_info'] = options.res_size_info # Safe to sort: Build checks that non-overlay resource have no overlap. dependency_zips.sort() - config['deps_info']['dependency_zips'] = dependency_zips - config['deps_info']['dependency_zip_overlays'] = dependency_zip_overlays + config['dependency_zips'] = dependency_zips + config['dependency_zip_overlays'] = dependency_zip_overlays # Order doesn't matter, so make stable. extra_package_names.sort() - config['deps_info']['extra_package_names'] = extra_package_names + config['extra_package_names'] = extra_package_names - # These are .jars to add to javac classpath but not to runtime classpath. - extra_classpath_jars = action_helpers.parse_gn_list( - options.extra_classpath_jars) - if extra_classpath_jars: - extra_classpath_jars.sort() - deps_info['extra_classpath_jars'] = extra_classpath_jars - - mergeable_android_manifests = action_helpers.parse_gn_list( - options.mergeable_android_manifests) + mergeable_android_manifests = params.get('mergeable_android_manifests', []) mergeable_android_manifests.sort() if mergeable_android_manifests: - deps_info['mergeable_android_manifests'] = mergeable_android_manifests + config['mergeable_android_manifests'] = mergeable_android_manifests extra_proguard_classpath_jars = [] - proguard_configs = action_helpers.parse_gn_list(options.proguard_configs) - if proguard_configs: - # Make a copy of |proguard_configs| since it's mutated below. - deps_info['proguard_configs'] = list(proguard_configs) - + proguard_configs = params.get('proguard_configs', []) if is_java_target: classpath_direct_deps = deps.Direct() @@ -1694,13 +715,13 @@ # The classpath used to compile this target when annotation processors are # present. - javac_classpath = set(c['unprocessed_jar_path'] - for c in classpath_direct_library_deps) + javac_classpath = _SetFromDeps(classpath_direct_library_deps, + 'unprocessed_jar_path') # The classpath used to compile this target when annotation processors are # not present. These are also always used to know when a target needs to be # rebuilt. - javac_interface_classpath = set(c['interface_jar_path'] - for c in classpath_direct_library_deps) + javac_interface_classpath = _SetFromDeps(classpath_direct_library_deps, + 'interface_jar_path') # Preserve order of |all_library_deps|. Move low priority libraries to the # end of the classpath. @@ -1717,46 +738,43 @@ # Adding base module to classpath to compile against its R.java file if base_module_build_config: - javac_full_classpath.add( - base_module_build_config['deps_info']['unprocessed_jar_path']) + javac_full_classpath.add(base_module_build_config['unprocessed_jar_path']) javac_full_interface_classpath.add( - base_module_build_config['deps_info']['interface_jar_path']) + base_module_build_config['interface_jar_path']) # Turbine now compiles headers against only the direct classpath, so the # base module's interface jar must be on the direct interface classpath. javac_interface_classpath.add( - base_module_build_config['deps_info']['interface_jar_path']) + base_module_build_config['interface_jar_path']) for dep in classpath_direct_deps: - if 'extra_classpath_jars' in dep: - javac_classpath.update(dep['extra_classpath_jars']) - javac_interface_classpath.update(dep['extra_classpath_jars']) + if paths := dep.get('input_jars_paths'): + javac_classpath.update(paths) + javac_interface_classpath.update(paths) for dep in all_deps: - if 'extra_classpath_jars' in dep: - javac_full_classpath.update(dep['extra_classpath_jars']) - javac_full_interface_classpath.update(dep['extra_classpath_jars']) + if paths := dep.get('input_jars_paths'): + javac_full_classpath.update(paths) + javac_full_interface_classpath.update(paths) # TODO(agrieve): Might be less confusing to fold these into bootclasspath. # Deps to add to the compile-time classpath (but not the runtime classpath). # These are jars specified by input_jars_paths that almost never change. # Just add them directly to all the classpaths. - if options.extra_classpath_jars: - javac_classpath.update(extra_classpath_jars) - javac_interface_classpath.update(extra_classpath_jars) - javac_full_classpath.update(extra_classpath_jars) - javac_full_interface_classpath.update(extra_classpath_jars) + if paths := params.get('input_jars_paths'): + javac_classpath.update(paths) + javac_interface_classpath.update(paths) + javac_full_classpath.update(paths) + javac_full_interface_classpath.update(paths) - if is_java_target or options.type == 'android_app_bundle': + if is_java_target or is_bundle: # The classpath to use to run this target (or as an input to ProGuard). device_classpath = [] # dist_jar configs should not list their device jar in their own classpath # since the classpath is used to create the device jar itself. - if (is_java_target and options.processed_jar_path - and options.type != 'dist_jar'): - device_classpath.append(options.processed_jar_path) - device_classpath.extend( - c.get('processed_jar_path') for c in all_library_deps - if c.get('processed_jar_path')) - if options.type == 'android_app_bundle': + if is_java_target and target_type != 'dist_jar': + if path := params.get('processed_jar_path'): + device_classpath.append(path) + device_classpath += _ListFromDeps(all_library_deps, 'processed_jar_path') + if is_bundle: for d in deps.Direct('android_app_bundle_module'): device_classpath.extend(c for c in d.get('device_classpath', []) if c not in device_classpath) @@ -1770,7 +788,7 @@ # bundle includes. Different android_app_bundle targets may include different # android_app_bundle_module targets, so the bundle needs to be able to # de-duplicate these lint artifacts. - if options.type in ('android_app_bundle_module', 'android_apk'): + if is_apk_or_module: # Collect all sources and resources at the apk/bundle_module level. lint_aars = set() lint_srcjars = set() @@ -1778,50 +796,55 @@ lint_resource_sources = set() lint_resource_zips = set() - if options.target_sources_file: - lint_sources.add(options.target_sources_file) - if options.bundled_srcjars: - lint_srcjars.update(deps_info['bundled_srcjars']) + if path := params.get('target_sources_file'): + lint_sources.add(path) + if paths := params.get('bundled_srcjars'): + lint_srcjars.update(paths) for c in all_library_deps: - if c['chromium_code'] and c['requires_android']: - if 'target_sources_file' in c: - lint_sources.add(c['target_sources_file']) + if c.get('chromium_code', True) and c['requires_android']: + if path := c.get('target_sources_file'): + lint_sources.add(path) lint_srcjars.update(c['bundled_srcjars']) - if 'aar_path' in c: - lint_aars.add(c['aar_path']) + if path := c.get('aar_path'): + lint_aars.add(path) - if options.res_sources_path: - lint_resource_sources.add(options.res_sources_path) - if options.resources_zip: - lint_resource_zips.add(options.resources_zip) + if path := params.get('res_sources_path'): + lint_resource_sources.add(path) + if path := params.get('resources_zip'): + lint_resource_zips.add(path) for c in all_resources_deps: - if c['chromium_code']: + if c.get('chromium_code', True): # Prefer res_sources_path to resources_zips so that lint errors have # real paths and to avoid needing to extract during lint. - if c['res_sources_path']: - lint_resource_sources.add(c['res_sources_path']) + if path := c.get('res_sources_path'): + lint_resource_sources.add(path) else: lint_resource_zips.add(c['resources_zip']) - deps_info['lint_aars'] = sorted(lint_aars) - deps_info['lint_srcjars'] = sorted(lint_srcjars) - deps_info['lint_sources'] = sorted(lint_sources) - deps_info['lint_resource_sources'] = sorted(lint_resource_sources) - deps_info['lint_resource_zips'] = sorted(lint_resource_zips) - deps_info['lint_extra_android_manifests'] = [] + config['lint_aars'] = sorted(lint_aars) + config['lint_srcjars'] = sorted(lint_srcjars) + config['lint_sources'] = sorted(lint_sources) + config['lint_resource_sources'] = sorted(lint_resource_sources) + config['lint_resource_zips'] = sorted(lint_resource_zips) + config['lint_extra_android_manifests'] = [] - if options.type == 'android_apk': - assert options.android_manifest, 'Android APKs must define a manifest' - deps_info['lint_android_manifest'] = options.android_manifest + if is_bundle: + modules_by_name = {m['name']: m for m in params['modules']} + module_configs_by_name = { + m['name']: GetDepConfig(m['build_config']) + for m in params['modules'] + } + modules = {name: {} for name in modules_by_name} - if options.type == 'android_app_bundle': - module_config_paths = action_helpers.parse_gn_list( - options.module_build_configs) - module_configs = [GetDepConfig(c) for c in module_config_paths] - module_configs_by_name = {d['module_name']: d for d in module_configs} + child_to_ancestors = {n: ['base'] for n in modules_by_name} + for name, module in modules_by_name.items(): + while parent := module.get('uses_split'): + child_to_ancestors[name].append(parent) + module = modules_by_name[parent] + per_module_fields = [ 'device_classpath', 'trace_event_rewritten_device_classpath', - 'all_dex_files', 'assets' + 'all_dex_files', 'assets', 'uncompressed_assets' ] lint_aars = set() lint_srcjars = set() @@ -1829,17 +852,15 @@ lint_resource_sources = set() lint_resource_zips = set() lint_extra_android_manifests = set() - config['modules'] = {} - modules = config['modules'] for n, c in module_configs_by_name.items(): if n == 'base': - assert 'base_module_config' not in deps_info, ( + assert 'base_module_config' not in config, ( 'Must have exactly 1 base module!') - deps_info['package_name'] = c['package_name'] - deps_info['version_code'] = c['version_code'] - deps_info['version_name'] = c['version_name'] - deps_info['base_module_config'] = c['path'] - deps_info['lint_android_manifest'] = c['android_manifest'] + config['package_name'] = c['package_name'] + config['version_code'] = c['version_code'] + config['version_name'] = c['version_name'] + config['base_module_config'] = c['path'] + config['android_manifest'] = c['android_manifest'] else: # All manifests nodes are merged into the main manfiest by lint.py. lint_extra_android_manifests.add(c['android_manifest']) @@ -1850,176 +871,155 @@ lint_sources.update(c['lint_sources']) lint_resource_sources.update(c['lint_resource_sources']) lint_resource_zips.update(c['lint_resource_zips']) - module = modules[n] = {} + module = modules[n] + module['final_dex_path'] = c['final_dex_path'] for f in per_module_fields: if f in c: module[f] = c[f] - deps_info['lint_aars'] = sorted(lint_aars) - deps_info['lint_srcjars'] = sorted(lint_srcjars) - deps_info['lint_sources'] = sorted(lint_sources) - deps_info['lint_resource_sources'] = sorted(lint_resource_sources) - deps_info['lint_resource_zips'] = sorted(lint_resource_zips) - deps_info['lint_extra_android_manifests'] = sorted( + config['lint_aars'] = sorted(lint_aars) + config['lint_srcjars'] = sorted(lint_srcjars) + config['lint_sources'] = sorted(lint_sources) + config['lint_resource_sources'] = sorted(lint_resource_sources) + config['lint_resource_zips'] = sorted(lint_resource_zips) + config['lint_extra_android_manifests'] = sorted( lint_extra_android_manifests) - _DedupFeatureModuleSharedCode(options.uses_split, modules, + _DedupFeatureModuleSharedCode(child_to_ancestors, modules, per_module_fields) + config['modules'] = modules - system_jars = [c['unprocessed_jar_path'] for c in system_library_deps] - system_interface_jars = [c['interface_jar_path'] for c in system_library_deps] - if system_library_deps: - config['android'] = {} - config['android']['sdk_interface_jars'] = system_interface_jars - config['android']['sdk_jars'] = system_jars + if is_java_target or (is_bundle and params.get('proguard_enabled')): + config['sdk_jars'] = [ + c['unprocessed_jar_path'] for c in system_library_deps + ] + config['sdk_interface_jars'] = [ + c['interface_jar_path'] for c in system_library_deps + ] - if options.type in ('android_apk', 'dist_aar', - 'dist_jar', 'android_app_bundle_module', 'android_app_bundle'): + if target_type in ('android_apk', 'dist_aar', 'dist_jar', + 'android_app_bundle_module', 'android_app_bundle'): for c in all_deps: proguard_configs.extend(c.get('proguard_configs', [])) - extra_proguard_classpath_jars.extend(c.get('extra_classpath_jars', [])) - if options.type == 'android_app_bundle': + extra_proguard_classpath_jars.extend(c.get('input_jars_paths', [])) + if is_bundle: for c in deps.Direct('android_app_bundle_module'): - proguard_configs.extend(p for p in c.get('proguard_configs', [])) - if options.type == 'android_app_bundle': - for d in deps.Direct('android_app_bundle_module'): + proguard_configs.extend(c.get('proguard_configs', [])) extra_proguard_classpath_jars.extend( - c for c in d.get('proguard_classpath_jars', []) - if c not in extra_proguard_classpath_jars) + j for j in c.get('proguard_classpath_jars', []) + if j not in extra_proguard_classpath_jars) - if options.type == 'android_app_bundle': deps_proguard_enabled = [] deps_proguard_disabled = [] for d in deps.Direct('android_app_bundle_module'): if not d['device_classpath']: # We don't care about modules that have no Java code for proguarding. continue - if d['proguard_enabled']: - deps_proguard_enabled.append(d['name']) + if d.get('proguard_enabled'): + deps_proguard_enabled.append(d['module_name']) else: - deps_proguard_disabled.append(d['name']) + deps_proguard_disabled.append(d['module_name']) if deps_proguard_enabled and deps_proguard_disabled: raise Exception('Deps %s have proguard enabled while deps %s have ' 'proguard disabled' % (deps_proguard_enabled, deps_proguard_disabled)) - deps_info['proguard_enabled'] = bool(options.proguard_enabled) - - if options.proguard_mapping_path: - deps_info['proguard_mapping_path'] = options.proguard_mapping_path # The java code for an instrumentation test apk is assembled differently for - # ProGuard vs. non-ProGuard. + # R8 vs. non-R8. # - # Without ProGuard: Each library's jar is dexed separately and then combined + # Without R8: Each library's jar is dexed separately and then combined # into a single classes.dex. A test apk will include all dex files not already # present in the apk-under-test. At runtime all test code lives in the test # apk, and the program code lives in the apk-under-test. # - # With ProGuard: Each library's .jar file is fed into ProGuard, which outputs + # With R8: Each library's .jar file is fed into R8, which outputs # a single .jar, which is then dexed into a classes.dex. A test apk includes # all jar files from the program and the tests because having them separate - # doesn't work with ProGuard's whole-program optimizations. Although the + # doesn't work with R8's whole-program optimizations. Although the # apk-under-test still has all of its code in its classes.dex, none of it is # used at runtime because the copy of it within the test apk takes precidence. - if options.type == 'android_apk' and options.tested_apk_config: - if tested_apk_config['proguard_enabled']: - assert options.proguard_enabled, ('proguard must be enabled for ' + if is_apk and apk_under_test_config: + if apk_under_test_config['proguard_enabled']: + assert params.get('proguard_enabled'), ( + 'proguard must be enabled for ' 'instrumentation apks if it\'s enabled for the tested apk.') # Mutating lists, so no need to explicitly re-assign to dict. - proguard_configs.extend( - p for p in tested_apk_config['proguard_all_configs']) + proguard_configs.extend(apk_under_test_config['proguard_all_configs']) extra_proguard_classpath_jars.extend( - p for p in tested_apk_config['proguard_classpath_jars']) - tested_apk_config = GetDepConfig(options.tested_apk_config) - deps_info['proguard_under_test_mapping'] = ( - tested_apk_config['proguard_mapping_path']) - elif options.proguard_enabled: - # Not sure why you'd want to proguard the test apk when the under-test apk - # is not proguarded, but it's easy enough to support. - deps_info['proguard_under_test_mapping'] = '' + apk_under_test_config['proguard_classpath_jars']) # Add all tested classes to the test's classpath to ensure that the test's # java code is a superset of the tested apk's java code device_classpath_extended = list(device_classpath) device_classpath_extended.extend( - p for p in tested_apk_config['device_classpath'] + p for p in apk_under_test_config['device_classpath'] if p not in device_classpath) # Include in the classpath classes that are added directly to the apk under # test (those that are not a part of a java_library). - javac_classpath.add(tested_apk_config['unprocessed_jar_path']) - javac_interface_classpath.add(tested_apk_config['interface_jar_path']) - javac_full_classpath.add(tested_apk_config['unprocessed_jar_path']) - javac_full_interface_classpath.add(tested_apk_config['interface_jar_path']) - javac_full_classpath.update(tested_apk_config['javac_full_classpath']) + javac_classpath.add(apk_under_test_config['unprocessed_jar_path']) + javac_interface_classpath.add(apk_under_test_config['interface_jar_path']) + javac_full_classpath.add(apk_under_test_config['unprocessed_jar_path']) + javac_full_interface_classpath.add( + apk_under_test_config['interface_jar_path']) + javac_full_classpath.update(apk_under_test_config['javac_full_classpath']) javac_full_interface_classpath.update( - tested_apk_config['javac_full_interface_classpath']) + apk_under_test_config['javac_full_interface_classpath']) # Exclude .jar files from the test apk that exist within the apk under test. - tested_apk_library_deps = tested_apk_deps.All('java_library') - tested_apk_dex_files = {c['dex_path'] for c in tested_apk_library_deps} - all_dex_files = [p for p in all_dex_files if p not in tested_apk_dex_files] - tested_apk_jar_files = set(tested_apk_config['device_classpath']) + apk_under_test_deps = Deps([apk_under_test_config['path']]) + apk_under_test_library_deps = apk_under_test_deps.All('java_library') + apk_under_test_dex_files = { + c['dex_path'] + for c in apk_under_test_library_deps + } + all_dex_files = [ + p for p in all_dex_files if p not in apk_under_test_dex_files + ] + apk_under_test_jar_files = set(apk_under_test_config['device_classpath']) device_classpath = [ - p for p in device_classpath if p not in tested_apk_jar_files + p for p in device_classpath if p not in apk_under_test_jar_files ] - if options.type in ('android_apk', 'dist_aar', 'dist_jar', - 'android_app_bundle_module', 'android_app_bundle'): - deps_info['proguard_all_configs'] = sorted(set(proguard_configs)) - deps_info['proguard_classpath_jars'] = sorted( + if target_type in ('android_apk', 'dist_aar', 'dist_jar', + 'android_app_bundle_module', 'android_app_bundle'): + config['proguard_all_configs'] = sorted(set(proguard_configs)) + config['proguard_classpath_jars'] = sorted( set(extra_proguard_classpath_jars)) - if options.type in ('dist_jar', 'java_binary', 'robolectric_binary'): + if target_type in ('dist_jar', 'java_binary', 'robolectric_binary'): # The classpath to use to run this target. host_classpath = [] - if options.processed_jar_path: - host_classpath.append(options.processed_jar_path) + if path := params.get('processed_jar_path'): + host_classpath.append(path) host_classpath.extend(c['processed_jar_path'] for c in all_library_deps) # Collect all the dist_jar host jars. - dist_jar_host_jars = [ - c['processed_jar_path'] for c in all_dist_jar_deps - if 'processed_jar_path' in c - ] + dist_jar_host_jars = _ListFromDeps(all_dist_jar_deps, 'processed_jar_path') # Collect all the jars that went into the dist_jar host jars. - dist_jar_host_classpath = set() - for c in all_dist_jar_deps: - dist_jar_host_classpath.update(c['host_classpath']) + dist_jar_host_classpath = _SetFromDeps(all_dist_jar_deps, 'host_classpath') # Remove the jars that went into the dist_jar host jars. host_classpath = [ p for p in host_classpath if p not in dist_jar_host_classpath ] # Add the dist_jar host jars themselves instead. host_classpath += dist_jar_host_jars - deps_info['host_classpath'] = host_classpath + config['host_classpath'] = host_classpath if is_java_target: - - def _CollectListsFromDeps(deps, key_name): - combined = set() - for config in deps: - combined.update(config.get(key_name, [])) - return combined - - dist_jar_device_classpath = _CollectListsFromDeps(all_dist_jar_deps, - 'device_classpath') - dist_jar_javac_full_classpath = _CollectListsFromDeps( - all_dist_jar_deps, 'javac_full_classpath') - dist_jar_javac_full_interface_classpath = _CollectListsFromDeps( + dist_jar_device_classpath = _SetFromDeps(all_dist_jar_deps, + 'device_classpath') + dist_jar_javac_full_classpath = _SetFromDeps(all_dist_jar_deps, + 'javac_full_classpath') + dist_jar_javac_full_interface_classpath = _SetFromDeps( all_dist_jar_deps, 'javac_full_interface_classpath') - dist_jar_child_dex_files = _CollectListsFromDeps(all_dist_jar_deps, - 'all_dex_files') + dist_jar_child_dex_files = _SetFromDeps(all_dist_jar_deps, 'all_dex_files') - def _CollectSinglesFromDeps(deps, key_name): - return [config[key_name] for config in deps if key_name in config] - - dist_jar_device_jars = _CollectSinglesFromDeps(all_dist_jar_deps, - 'processed_jar_path') - dist_jar_combined_dex_files = _CollectSinglesFromDeps( - all_dist_jar_deps, 'dex_path') - dist_jar_interface_jars = _CollectSinglesFromDeps(all_dist_jar_deps, - 'interface_jar_path') - dist_jar_unprocessed_jars = _CollectSinglesFromDeps(all_dist_jar_deps, - 'unprocessed_jar_path') + dist_jar_device_jars = _ListFromDeps(all_dist_jar_deps, + 'processed_jar_path') + dist_jar_combined_dex_files = _ListFromDeps(all_dist_jar_deps, 'dex_path') + dist_jar_interface_jars = _ListFromDeps(all_dist_jar_deps, + 'interface_jar_path') + dist_jar_unprocessed_jars = _ListFromDeps(all_dist_jar_deps, + 'unprocessed_jar_path') device_classpath = [ p for p in device_classpath if p not in dist_jar_device_classpath @@ -2036,37 +1036,31 @@ javac_interface_classpath.update(dist_jar_interface_jars) javac_classpath.update(dist_jar_unprocessed_jars) - if is_apk_or_module_target or options.type == 'dist_jar': + if is_apk_or_module or target_type == 'dist_jar': all_dex_files = [ p for p in all_dex_files if p not in dist_jar_child_dex_files ] all_dex_files += dist_jar_combined_dex_files - if options.final_dex_path: - config['final_dex'] = {'path': options.final_dex_path} - if is_apk_or_module_target or options.type == 'dist_jar': + if is_apk_or_module or target_type == 'dist_jar': # Dependencies for the final dex file of an apk. - deps_info['all_dex_files'] = all_dex_files + config['all_dex_files'] = all_dex_files if is_java_target: - config['javac']['classpath'] = sorted(javac_classpath) - config['javac']['interface_classpath'] = sorted(javac_interface_classpath) + config['classpath'] = sorted(javac_classpath) + config['interface_classpath'] = sorted(javac_interface_classpath) # Direct() will be of type 'java_annotation_processor', and so not included # in All('java_library'). # Annotation processors run as part of the build, so need processed_jar_path. - config['javac']['processor_classpath'] = [ - c['processed_jar_path'] for c in processor_deps.Direct() - if c.get('processed_jar_path') - ] - config['javac']['processor_classpath'] += [ - c['processed_jar_path'] for c in processor_deps.All('java_library') - ] - config['javac']['processor_classes'] = sorted( - c['main_class'] for c in processor_deps.Direct()) - deps_info['javac_full_classpath'] = list(javac_full_classpath) - deps_info['javac_full_interface_classpath'] = list( + config['processor_classpath'] = _ListFromDeps( + processor_deps.Direct() + processor_deps.All('java_library'), + 'processed_jar_path') + config['processor_classes'] = sorted(c['main_class'] + for c in processor_deps.Direct()) + config['javac_full_classpath'] = list(javac_full_classpath) + config['javac_full_interface_classpath'] = list( javac_full_interface_classpath) - elif options.type == 'android_app_bundle': + elif is_bundle: # bundles require javac_full_classpath to create .aab.jar.info and require # javac_full_interface_classpath for lint. javac_full_classpath = OrderedSet() @@ -2076,66 +1070,74 @@ javac_full_interface_classpath.update(d['javac_full_interface_classpath']) javac_full_classpath.add(d['unprocessed_jar_path']) javac_full_interface_classpath.add(d['interface_jar_path']) - deps_info['javac_full_classpath'] = list(javac_full_classpath) - deps_info['javac_full_interface_classpath'] = list( + config['javac_full_classpath'] = list(javac_full_classpath) + config['javac_full_interface_classpath'] = list( javac_full_interface_classpath) - if options.type in ('android_apk', 'android_app_bundle', - 'android_app_bundle_module', 'dist_aar', 'dist_jar'): - deps_info['device_classpath'] = device_classpath - if options.trace_events_jar_dir: + if target_type in ('android_apk', 'android_app_bundle', + 'android_app_bundle_module', 'dist_aar', 'dist_jar'): + config['device_classpath'] = device_classpath + if trace_events_jar_dir := params.get('trace_events_jar_dir'): trace_event_rewritten_device_classpath = [] - for processed_jar_path in device_classpath: - file_path = processed_jar_path.replace('../', '') - file_path = file_path.replace('obj/', '') - file_path = file_path.replace('gen/', '') - file_path = file_path.replace('.jar', '.tracing_rewritten.jar') - rewritten_jar_path = os.path.join(options.trace_events_jar_dir, - file_path) + for jar_path in device_classpath: + jar_path = jar_path.replace('../', '') + jar_path = jar_path.replace('obj/', '') + jar_path = jar_path.replace('gen/', '') + jar_path = jar_path.replace('.jar', '.tracing_rewritten.jar') + rewritten_jar_path = os.path.join(trace_events_jar_dir, jar_path) trace_event_rewritten_device_classpath.append(rewritten_jar_path) - deps_info['trace_event_rewritten_device_classpath'] = ( + config['trace_event_rewritten_device_classpath'] = ( trace_event_rewritten_device_classpath) - if options.tested_apk_config: - deps_info['device_classpath_extended'] = device_classpath_extended + if apk_under_test_config: + config['device_classpath_extended'] = device_classpath_extended - if options.type == 'dist_jar': - if options.direct_deps_only: - if options.use_interface_jars: - dist_jars = config['javac']['interface_classpath'] + if target_type == 'dist_jar': + if params.get('direct_deps_only'): + if params.get('use_interface_jars'): + dist_jars = config['interface_classpath'] else: dist_jars = sorted(c['processed_jar_path'] for c in classpath_direct_library_deps) else: - if options.use_interface_jars: + if params.get('use_interface_jars'): dist_jars = [c['interface_jar_path'] for c in all_library_deps] else: - dist_jars = deps_info['device_classpath'] + dist_jars = config['device_classpath'] - config['dist_jar'] = { - 'jars': dist_jars, - } + config['dist_jar']['jars'] = dist_jars - if is_apk_or_module_target: - manifest = AndroidManifest(options.android_manifest) - deps_info['package_name'] = manifest.GetPackageName() - if not options.tested_apk_config and manifest.GetInstrumentationElements(): + if is_apk_or_module: + manifest = AndroidManifest(params['android_manifest']) + if not apk_under_test_config and manifest.GetInstrumentationElements(): # This must then have instrumentation only for itself. manifest.CheckInstrumentationElements(manifest.GetPackageName()) + config['package_name'] = manifest.GetPackageName() + config['android_manifest'] = params['android_manifest'] + config['merged_android_manifest'] = params['merged_android_manifest'] + + if is_apk: + config['version_code'] = params['version_code'] + config['version_name'] = params['version_name'] + + # TrichromeLibrary has no dex. + if final_dex_path := params.get('final_dex_path'): + config['final_dex_path'] = final_dex_path + library_paths = [] java_libraries_list = None - if options.shared_libraries_runtime_deps: - library_paths = _ExtractSharedLibsFromRuntimeDeps( - options.shared_libraries_runtime_deps) + if path := params.get('shared_libraries_runtime_deps_file'): + # GN does not add this input to avoid depending on the generated_file() target. + all_inputs.append(path) + library_paths = _ExtractSharedLibsFromRuntimeDeps(path) java_libraries_list = _CreateJavaLibrariesList(library_paths) - all_inputs.append(options.shared_libraries_runtime_deps) secondary_abi_library_paths = [] - if options.secondary_abi_shared_libraries_runtime_deps: - secondary_abi_library_paths = _ExtractSharedLibsFromRuntimeDeps( - options.secondary_abi_shared_libraries_runtime_deps) + if path := params.get('secondary_abi_shared_libraries_runtime_deps_file'): + all_inputs.append(path) + secondary_abi_library_paths = _ExtractSharedLibsFromRuntimeDeps(path) secondary_abi_library_paths.sort() paths_without_parent_dirs = [ p for p in secondary_abi_library_paths if os.path.sep not in p @@ -2149,60 +1151,55 @@ 'monochrome_secondary_abi_lib //base:base\n') sys.exit(1) - all_inputs.append(options.secondary_abi_shared_libraries_runtime_deps) + config['native']['libraries'] = library_paths + config['native']['secondary_abi_libraries'] = secondary_abi_library_paths + config['native']['java_libraries_list'] = java_libraries_list - native_library_placeholder_paths = action_helpers.parse_gn_list( - options.native_lib_placeholders) - native_library_placeholder_paths.sort() + if is_bundle_module: + loadable_modules = params.get('loadable_modules', []) + loadable_modules.sort() + secondary_abi_loadable_modules = params.get( + 'secondary_abi_loadable_modules', []) + secondary_abi_loadable_modules.sort() + placeholder_paths = params.get('native_lib_placeholders', []) + placeholder_paths.sort() + secondary_abi_placeholder_paths = params.get( + 'secondary_native_lib_placeholders', []) + secondary_abi_placeholder_paths.sort() - secondary_native_library_placeholder_paths = action_helpers.parse_gn_list( - options.secondary_native_lib_placeholders) - secondary_native_library_placeholder_paths.sort() - - loadable_modules = action_helpers.parse_gn_list(options.loadable_modules) - loadable_modules.sort() - secondary_abi_loadable_modules = action_helpers.parse_gn_list( - options.secondary_abi_loadable_modules) - secondary_abi_loadable_modules.sort() - - config['native'] = { - 'libraries': - library_paths, - 'native_library_placeholders': - native_library_placeholder_paths, - 'secondary_abi_libraries': - secondary_abi_library_paths, - 'secondary_native_library_placeholders': - secondary_native_library_placeholder_paths, - 'java_libraries_list': - java_libraries_list, - 'library_always_compress': - options.library_always_compress, - 'loadable_modules': - loadable_modules, - 'secondary_abi_loadable_modules': - secondary_abi_loadable_modules, - } + config['native']['loadable_modules'] = loadable_modules + config['native']['placeholders'] = placeholder_paths + config['native'][ + 'secondary_abi_loadable_modules'] = secondary_abi_loadable_modules + config['native'][ + 'secondary_abi_placeholders'] = secondary_abi_placeholder_paths + config['native']['library_always_compress'] = params.get( + 'library_always_compress') + config['proto_resources_path'] = params['proto_resources_path'] + config['base_allowlist_rtxt_path'] = params['base_allowlist_rtxt_path'] + config['rtxt_path'] = params['rtxt_path'] + config['module_pathmap_path'] = params['module_pathmap_path'] # Collect java resources - java_resources_jars = [d['java_resources_jar'] for d in all_library_deps - if 'java_resources_jar' in d] - if options.tested_apk_config: - tested_apk_resource_jars = [d['java_resources_jar'] - for d in tested_apk_library_deps - if 'java_resources_jar' in d] - java_resources_jars = [jar for jar in java_resources_jars - if jar not in tested_apk_resource_jars] + java_resources_jars = _ListFromDeps(all_library_deps, + 'java_resources_jar_path') + if apk_under_test_config: + apk_under_test_resource_jars = _SetFromDeps(apk_under_test_library_deps, + 'java_resources_jar_path') + java_resources_jars = [ + jar for jar in java_resources_jars + if jar not in apk_under_test_resource_jars + ] java_resources_jars.sort() config['java_resources_jars'] = java_resources_jars - if is_apk_or_module_target or options.type == 'robolectric_binary': + if is_apk_or_module or target_type == 'robolectric_binary': # android_resources deps which had recursive_resource_deps set should not # have the manifests from the recursively collected deps added to this # module. This keeps the manifest declarations in the child DFMs, since they # will have the Java implementations. def ExcludeRecursiveResourcesDeps(config): - return not config.get('includes_recursive_resources', False) + return not config.get('recursive_resource_deps', False) extra_manifest_deps = [ GetDepConfig(p) for p in GetAllDepsConfigsInOrder( @@ -2211,24 +1208,24 @@ # Manifests are listed from highest priority to lowest priority. # Ensure directly manfifests come first, and then sort the rest by name. # https://developer.android.com/build/manage-manifests#merge_priorities - deps_info['extra_android_manifests'] = list(mergeable_android_manifests) + config['extra_android_manifests'] = list(mergeable_android_manifests) manifests_from_deps = [] for c in extra_manifest_deps: manifests_from_deps += c.get('mergeable_android_manifests', []) manifests_from_deps.sort(key=lambda p: (os.path.basename(p), p)) - deps_info['extra_android_manifests'] += manifests_from_deps + config['extra_android_manifests'] += manifests_from_deps assets, uncompressed_assets, locale_paks = _MergeAssets( deps.All('android_assets')) - deps_info['assets'] = assets - deps_info['uncompressed_assets'] = uncompressed_assets - deps_info['locales_java_list'] = _CreateJavaLocaleListFromAssets( + config['assets'] = assets + config['uncompressed_assets'] = uncompressed_assets + config['locales_java_list'] = _CreateJavaLocaleListFromAssets( uncompressed_assets, locale_paks) - if options.suffix_apk_assets_used_by: - if options.suffix_apk_assets_used_by == options.build_config: - target_config = deps_info + if path := params.get('suffix_apk_assets_used_by_config'): + if path == output_path: + target_config = config else: - target_config = GetDepConfig(options.suffix_apk_assets_used_by) + target_config = GetDepConfig(path) all_assets = (target_config['assets'] + target_config['uncompressed_assets']) suffix = '+' + target_config['package_name'] + '+' @@ -2236,16 +1233,13 @@ x.split(':', 1)[1].replace(suffix, '') for x in all_assets } - deps_info['assets'] = _SuffixAssets(suffix_names, suffix, assets) - deps_info['uncompressed_assets'] = _SuffixAssets(suffix_names, suffix, - uncompressed_assets) + config['assets'] = _SuffixAssets(suffix_names, suffix, assets) + config['uncompressed_assets'] = _SuffixAssets(suffix_names, suffix, + uncompressed_assets) config['apk_assets_suffixed_list'] = ','.join( f'"assets/{x}"' for x in sorted(suffix_names)) config['apk_assets_suffix'] = suffix - if options.java_resources_jar_path: - deps_info['java_resources_jar'] = options.java_resources_jar_path - # DYNAMIC FEATURE MODULES: # There are two approaches to dealing with modules dependencies: # 1) Perform steps in android_apk_or_module(), with only the knowledge of @@ -2261,49 +1255,49 @@ # parent. # _DedupFeatureModuleSharedCode() implements this approach. if base_module_build_config: - ancestors = [base_module_build_config] + ancestor_configs = [base_module_build_config] if parent_module_build_config is not base_module_build_config: - ancestors += [parent_module_build_config] - for ancestor in ancestors: - RemoveObjDups(config, ancestor, 'deps_info', 'dependency_zips') - RemoveObjDups(config, ancestor, 'deps_info', 'dependency_zip_overlays') - RemoveObjDups(config, ancestor, 'deps_info', 'extra_android_manifests') - RemoveObjDups(config, ancestor, 'deps_info', 'extra_package_names') + ancestor_configs += [parent_module_build_config] + for c in ancestor_configs: + RemoveObjDups(config, c, 'dependency_zips') + RemoveObjDups(config, c, 'dependency_zip_overlays') + RemoveObjDups(config, c, 'extra_android_manifests') + RemoveObjDups(config, c, 'extra_package_names') if is_java_target: jar_to_target = {} - _AddJarMapping(jar_to_target, [deps_info]) - _AddJarMapping(jar_to_target, all_deps) + _AddJarMapping(jar_to_target, params) + for d in all_deps: + _AddJarMapping(jar_to_target, d) if base_module_build_config: - _AddJarMapping(jar_to_target, [base_module_build_config['deps_info']]) - if parent_module_build_config is not base_module_build_config: - _AddJarMapping(jar_to_target, [parent_module_build_config['deps_info']]) - if options.tested_apk_config: - _AddJarMapping(jar_to_target, [tested_apk_config]) - for jar, target in zip(tested_apk_config['javac_full_classpath'], - tested_apk_config['javac_full_classpath_targets']): - jar_to_target[jar] = target + for c in ancestor_configs: + _AddJarMapping(jar_to_target, c) + if apk_under_test_config: + _AddJarMapping(jar_to_target, apk_under_test_config) + jar_to_target.update( + zip(apk_under_test_config['javac_full_classpath'], + apk_under_test_config['javac_full_classpath_targets'])) # Used by check_for_missing_direct_deps.py to give better error message # when missing deps are found. Both javac_full_classpath_targets and # javac_full_classpath must be in identical orders, as they get passed as # separate arrays and then paired up based on index. - config['deps_info']['javac_full_classpath_targets'] = [ - jar_to_target[x] for x in deps_info['javac_full_classpath'] + config['javac_full_classpath_targets'] = [ + jar_to_target[x] for x in config['javac_full_classpath'] ] - build_utils.WriteJson(config, options.build_config, only_if_changed=True) + build_utils.WriteJson(config, output_path, only_if_changed=True) if options.depfile: - action_helpers.write_depfile(options.depfile, options.build_config, - sorted(set(all_inputs))) + all_inputs += [ + p.replace('.build_config.json', '.params.json') for p in all_inputs + ] + action_helpers.write_depfile(options.depfile, output_path, all_inputs) if options.store_deps_for_debugging_to: - GetDepConfig(options.build_config) # Add it to cache. + GetDepConfig(output_path) # Add it to cache. _CopyBuildConfigsForDebugging(options.store_deps_for_debugging_to) - return 0 - if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) + main()
diff --git a/build/android/gyp/write_build_config.pydeps b/build/android/gyp/write_build_config.pydeps index fa7209c2..ac0c355 100644 --- a/build/android/gyp/write_build_config.pydeps +++ b/build/android/gyp/write_build_config.pydeps
@@ -1,30 +1,7 @@ # Generated by running: # build/print_python_deps.py --root build/android/gyp --output build/android/gyp/write_build_config.pydeps build/android/gyp/write_build_config.py -../../../third_party/jinja2/__init__.py -../../../third_party/jinja2/_identifier.py -../../../third_party/jinja2/async_utils.py -../../../third_party/jinja2/bccache.py -../../../third_party/jinja2/compiler.py -../../../third_party/jinja2/defaults.py -../../../third_party/jinja2/environment.py -../../../third_party/jinja2/exceptions.py -../../../third_party/jinja2/filters.py -../../../third_party/jinja2/idtracking.py -../../../third_party/jinja2/lexer.py -../../../third_party/jinja2/loaders.py -../../../third_party/jinja2/nodes.py -../../../third_party/jinja2/optimizer.py -../../../third_party/jinja2/parser.py -../../../third_party/jinja2/runtime.py -../../../third_party/jinja2/tests.py -../../../third_party/jinja2/utils.py -../../../third_party/jinja2/visitor.py -../../../third_party/markupsafe/__init__.py -../../../third_party/markupsafe/_compat.py -../../../third_party/markupsafe/_native.py ../../action_helpers.py ../../gn_helpers.py util/__init__.py util/build_utils.py -util/resource_utils.py write_build_config.py
diff --git a/build/android/java/src/org/chromium/build/annotations/MockedInTests.java b/build/android/java/src/org/chromium/build/annotations/MockedInTests.java index 8d8db20..120864c 100644 --- a/build/android/java/src/org/chromium/build/annotations/MockedInTests.java +++ b/build/android/java/src/org/chromium/build/annotations/MockedInTests.java
@@ -8,7 +8,10 @@ import java.lang.annotation.Target; /** - * Prevents optimization. R8 supports Mockito now, so this should be rarely be needed. + * See b/147584922. Proguard and Mockito don't play nicely together, and proguard rules make it + * impossible to keep the base class/interface for a mocked class without providing additional + * explicit information, like this annotation. This annotation should only need to be used on a + * class/interface that is extended/implemented by another class/interface that is then mocked. */ @Target(ElementType.TYPE) public @interface MockedInTests {}
diff --git a/build/android/list_java_targets.py b/build/android/list_java_targets.py index 90ccf1d..edead38 100755 --- a/build/android/list_java_targets.py +++ b/build/android/list_java_targets.py
@@ -156,16 +156,24 @@ subpath = ninja_target.replace(':', os.path.sep) + '.build_config.json' return os.path.join(constants.GetOutDirectory(), 'gen', subpath) + @property + def params_path(self): + """Returns the filepath of the project's .params.json.""" + return self.build_config_path.replace('.build_config.json', '.params.json') + def build_config(self): """Reads and returns the project's .build_config.json JSON.""" if not self._build_config: - with open(self.build_config_path) as jsonfile: - self._build_config = json.load(jsonfile) + with open(self.params_path) as f: + config = json.load(f) + with open(self.build_config_path) as f: + config.update(json.load(f)) + self._build_config = config return self._build_config def get_type(self): """Returns the target type from its .build_config.json.""" - return self.build_config()['deps_info']['type'] + return self.build_config()['type'] def proguard_enabled(self): """Returns whether proguard runs for this target.""" @@ -173,7 +181,7 @@ # bundle level. if self.get_type() == 'android_app_bundle_module': return False - return self.build_config()['deps_info'].get('proguard_enabled', False) + return self.build_config().get('proguard_enabled', False) def main(): @@ -197,6 +205,9 @@ '--print-build-config-paths', action='store_true', help='Print path to the .build_config.json of each target') + parser.add_argument('--print-params-paths', + action='store_true', + help='Print path to the .params.json of each target') parser.add_argument('--build', action='store_true', help='Build all .build_config.json files.') @@ -213,9 +224,9 @@ parser.add_argument('--query', help='A dot separated string specifying a query for a ' 'build config json value of each target. Example: Use ' - '--query deps_info.unprocessed_jar_path to show a list ' - 'of all targets that have a non-empty deps_info dict and ' - 'non-empty "unprocessed_jar_path" value in that dict.') + '--query unprocessed_jar_path to show a list ' + 'of all targets that have a non-empty ' + '"unprocessed_jar_path" value in that dict.') parser.add_argument('-v', '--verbose', default=0, action='count') parser.add_argument('-q', '--quiet', default=0, action='count') args = parser.parse_args() @@ -272,6 +283,8 @@ to_print = f'{to_print}: {e.get_type()}' elif args.print_build_config_paths: to_print = f'{to_print}: {e.build_config_path}' + elif args.print_params_paths: + to_print = f'{to_print}: {e.params_path}' elif args.query: value = _query_json(json_dict=e.build_config(), query=args.query,
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index 16093f75..ee1051ec 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -120,12 +120,16 @@ # See build/android/gyp/write_build_config.py and # build/android/gyp/util/build_utils.py:ExpandFileArgs template("write_build_config") { - action_with_pydeps(target_name) { - forward_variables_from(invoker, [ "testonly" ]) + _parent_invoker = invoker.invoker + _target_label = + get_label_info(":${_parent_invoker.target_name}", "label_no_toolchain") + _params_file = + string_replace(invoker.build_config, ".build_config.json", ".params.json") + _inputs = [ _params_file ] + _deps = [] + + if (current_toolchain == default_toolchain) { _type = invoker.type - _parent_invoker = invoker.invoker - _target_label = - get_label_info(":${_parent_invoker.target_name}", "label_no_toolchain") # Ensure targets match naming patterns so that __assetres, __header, __host, # and __validate targets work properly. @@ -145,30 +149,39 @@ } else if (filter_exclude([ _type ], _java_leaf_types) != []) { assert(false, "This java type needs a category: $_type") } + } + if (defined(invoker.public_target_label)) { + _target_label = invoker.public_target_label + } - if (defined(invoker.public_target_label)) { - _target_label = invoker.public_target_label + if (current_toolchain != default_toolchain) { + not_needed(invoker, "*") + _params = { + # This has to be a build-time error rather than a GN assert because many + # packages have a mix of java and non-java targets. For example, the + # following would fail even though nothing depends on :bar(//baz): + # + # shared_library("foo") { + # } + # + # android_library("bar") { + # deps = [ ":foo(//baz)" ] + # assert(current_toolchain == default_toolchain) + # } + fail = [ + "Tried to build an Android target in a non-default toolchain.", + "target: $_target_label", + "current_toolchain: $current_toolchain", + "default_toolchain: $default_toolchain", + ] } - - deps = [] - if (defined(invoker.deps)) { - deps = invoker.deps - } - if (defined(invoker.android_manifest_dep)) { - deps += [ invoker.android_manifest_dep ] - } - - script = "//build/android/gyp/write_build_config.py" - depfile = "$target_gen_dir/$target_name.d" - inputs = [] - outputs = [ invoker.build_config ] - - _deps_configs = [] + } else { if (defined(invoker.possible_config_deps)) { + _deps_configs = [] foreach(_possible_dep, invoker.possible_config_deps) { _dep_label = get_label_info(_possible_dep, "label_no_toolchain") if (filter_exclude([ _dep_label ], java_target_patterns) == []) { - deps += [ "$_dep_label$build_config_target_suffix" ] + _deps += [ "$_dep_label$build_config_target_suffix" ] _dep_gen_dir = get_label_info(_possible_dep, "target_gen_dir") _dep_name = get_label_info(_possible_dep, "name") _dep_config = "$_dep_gen_dir/$_dep_name.build_config.json" @@ -176,9 +189,10 @@ _deps_configs += [ _dep_config ] } } + _inputs += _deps_configs } - _public_deps_configs = [] if (defined(invoker.possible_config_public_deps)) { + _public_deps_configs = [] foreach(_possible_dep, invoker.possible_config_public_deps) { _dep_label = get_label_info(_possible_dep, "label_no_toolchain") @@ -193,413 +207,69 @@ # Put the bug number in the target name so that false-positives # have a hint in the error message about non-existent dependencies. - deps += [ "$_dep_label$build_config_target_suffix" ] + _deps += [ "$_dep_label$build_config_target_suffix" ] _dep_gen_dir = get_label_info(_possible_dep, "target_gen_dir") _dep_name = get_label_info(_possible_dep, "name") _dep_config = "$_dep_gen_dir/$_dep_name.build_config.json" _public_deps_configs += [ _dep_config ] } - } - inputs += _deps_configs - inputs += _public_deps_configs - _rebased_deps_configs = rebase_path(_deps_configs, root_build_dir) - _rebased_public_deps_configs = - rebase_path(_public_deps_configs, root_build_dir) - - args = [ - "--type=$_type", - "--depfile", - rebase_path(depfile, root_build_dir), - "--deps-configs=$_rebased_deps_configs", - "--public-deps-configs=$_rebased_public_deps_configs", - "--build-config", - rebase_path(invoker.build_config, root_build_dir), - "--gn-target", - _target_label, - ] - - if (defined(invoker.preferred_dep) && invoker.preferred_dep) { - args += [ "--preferred-dep" ] - } - - if (defined(invoker.aar_path)) { - args += [ - "--aar-path", - rebase_path(invoker.aar_path, root_build_dir), - ] - } - - if (defined(invoker.chromium_code) && !invoker.chromium_code) { - # Default to chromium code if invoker did not pass anything. - args += [ "--non-chromium-code" ] - } - - if (defined(invoker.processed_jar_path)) { - args += [ - "--processed-jar-path", - rebase_path(invoker.processed_jar_path, root_build_dir), - ] - } - if (defined(invoker.unprocessed_jar_path)) { - args += [ - "--unprocessed-jar-path", - rebase_path(invoker.unprocessed_jar_path, root_build_dir), - ] - } - if (defined(invoker.ijar_path)) { - args += [ - "--interface-jar-path", - rebase_path(invoker.ijar_path, root_build_dir), - ] - } - if (defined(invoker.kotlinc_jar_path)) { - args += [ - "--kotlinc-jar-path", - rebase_path(invoker.kotlinc_jar_path, root_build_dir), - ] - } - if (defined(invoker.java_resources_jar)) { - args += [ - "--java-resources-jar-path", - rebase_path(invoker.java_resources_jar, root_build_dir), - ] + _inputs += _public_deps_configs } if (defined(invoker.annotation_processor_deps) && invoker.annotation_processor_deps != []) { _processor_configs = [] foreach(_dep_label, invoker.annotation_processor_deps) { - deps += [ "$_dep_label$build_config_target_suffix" ] + _deps += [ "$_dep_label$build_config_target_suffix" ] _dep_gen_dir = get_label_info(_dep_label, "target_gen_dir") _dep_name = get_label_info(_dep_label, "name") _dep_config = "$_dep_gen_dir/$_dep_name.build_config.json" _processor_configs += [ _dep_config ] } - _rebased_processor_configs = - rebase_path(_processor_configs, root_build_dir) - inputs += _processor_configs - args += [ "--annotation-processor-configs=$_rebased_processor_configs" ] + _inputs += _processor_configs } - - # Dex path for library targets, or the the intermediate library for apks. - if (defined(invoker.dex_path)) { - args += [ - "--dex-path", - rebase_path(invoker.dex_path, root_build_dir), - ] - } - - # Dex path for the final apk. - if (defined(invoker.final_dex_path)) { - args += [ - "--final-dex-path", - rebase_path(invoker.final_dex_path, root_build_dir), - ] - } - if (defined(invoker.supports_android) && invoker.supports_android) { - args += [ "--supports-android" ] - } - if (defined(invoker.requires_android) && invoker.requires_android) { - args += [ "--requires-android" ] - } - if (defined(invoker.is_prebuilt) && invoker.is_prebuilt) { - args += [ "--is-prebuilt" ] - } - if (defined(invoker.bypass_platform_checks) && - invoker.bypass_platform_checks) { - args += [ "--bypass-platform-checks" ] - } - if (defined(invoker.requires_robolectric) && invoker.requires_robolectric) { - args += [ "--is-robolectric" ] - } - if (defined(invoker.apk_under_test)) { _dep_label = invoker.apk_under_test _dep_gen_dir = get_label_info(_dep_label, "target_gen_dir") _dep_name = get_label_info(_dep_label, "name") - _dep_config = "$_dep_gen_dir/$_dep_name.build_config.json" - inputs += [ _dep_config ] - deps += [ "$_dep_label$build_config_target_suffix" ] - args += [ - "--tested-apk-config", - rebase_path(_dep_config, root_build_dir), - ] - } - - if (defined(invoker.asset_sources)) { - _rebased_asset_sources = - rebase_path(invoker.asset_sources, root_build_dir) - args += [ "--asset-sources=$_rebased_asset_sources" ] - } - if (defined(invoker.asset_renaming_sources)) { - _rebased_asset_renaming_sources = - rebase_path(invoker.asset_renaming_sources, root_build_dir) - args += [ "--asset-renaming-sources=$_rebased_asset_renaming_sources" ] - - # These are zip paths, so no need to rebase. - args += [ - "--asset-renaming-destinations=${invoker.asset_renaming_destinations}", - ] - } - if (defined(invoker.disable_compression) && invoker.disable_compression) { - args += [ "--disable-asset-compression" ] - } - if (defined(invoker.treat_as_locale_paks) && invoker.treat_as_locale_paks) { - args += [ "--treat-as-locale-paks" ] - } - if (defined(invoker.suffix_apk_assets_used_by)) { - _dep_label = invoker.suffix_apk_assets_used_by - _dep_gen_dir = get_label_info(_dep_label, "target_gen_dir") - _dep_name = get_label_info(_dep_label, "name") - _dep_config = "$_dep_gen_dir/$_dep_name.build_config.json" - if (_dep_config != invoker.build_config) { - inputs += [ _dep_config ] - deps += [ "$_dep_label$build_config_target_suffix" ] - } - args += [ - "--suffix-apk-assets-used-by", - rebase_path(_dep_config, root_build_dir), - ] - } - - if (defined(invoker.merged_android_manifest)) { - args += [ - "--merged-android-manifest", - rebase_path(invoker.merged_android_manifest, root_build_dir), - ] - } - if (defined(invoker.android_manifest)) { - inputs += [ invoker.android_manifest ] - args += [ - "--android-manifest", - rebase_path(invoker.android_manifest, root_build_dir), - ] - } - if (defined(invoker.resources_zip)) { - args += [ - "--resources-zip", - rebase_path(invoker.resources_zip, root_build_dir), - ] - } - - if (defined(invoker.resource_overlay) && invoker.resource_overlay) { - args += [ "--resource-overlay" ] - } - - if (defined(invoker.custom_package)) { - args += [ - "--package-name", - invoker.custom_package, - ] - } - if (defined(invoker.r_text)) { - args += [ - "--r-text-path", - rebase_path(invoker.r_text, root_build_dir), - ] - } - if (defined(invoker.res_size_info_path)) { - args += [ - "--res-size-info", - rebase_path(invoker.res_size_info_path, root_build_dir), - ] - } - if (defined(invoker.res_sources_path)) { - _res_sources_path = rebase_path(invoker.res_sources_path, root_build_dir) - args += [ "--res-sources-path=$_res_sources_path" ] - } - if (defined(invoker.proto_resources_path)) { - _rebased_proto_resources = - rebase_path(invoker.proto_resources_path, root_build_dir) - args += [ "--apk-proto-resources=$_rebased_proto_resources" ] - } - if (defined(invoker.r_text_path)) { - _rebased_rtxt_path = rebase_path(invoker.r_text_path, root_build_dir) - args += [ "--r-text-path=$_rebased_rtxt_path" ] - } - if (defined(invoker.module_pathmap_path)) { - _rebased_pathmap_path = - rebase_path(invoker.module_pathmap_path, root_build_dir) - args += [ "--module-pathmap-path=$_rebased_pathmap_path" ] - } - - if (defined(invoker.shared_libraries_runtime_deps_file)) { - # Don't list shared_libraries_runtime_deps_file as an input in order to - # avoid having to depend on the runtime_deps target. See comment in - # rules.gni for why we do this. - args += [ - "--shared-libraries-runtime-deps", - rebase_path(invoker.shared_libraries_runtime_deps_file, root_build_dir), - ] - } - - if (defined(invoker.base_allowlist_rtxt_path)) { - args += [ - "--base-allowlist-rtxt-path", - rebase_path(invoker.base_allowlist_rtxt_path, root_build_dir), - ] - } - - if (defined(invoker.loadable_modules)) { - _rebased_loadable_modules = - rebase_path(invoker.loadable_modules, root_build_dir) - args += [ "--loadable-modules=$_rebased_loadable_modules" ] - } - - if (defined(invoker.secondary_abi_shared_libraries_runtime_deps_file)) { - # Don't list secondary_abi_shared_libraries_runtime_deps_file as an - # input in order to avoid having to depend on the runtime_deps target. - # See comment in rules.gni for why we do this. - args += [ - "--secondary-abi-shared-libraries-runtime-deps", - rebase_path(invoker.secondary_abi_shared_libraries_runtime_deps_file, - root_build_dir), - ] - } - - if (defined(invoker.secondary_abi_loadable_modules) && - invoker.secondary_abi_loadable_modules != []) { - _rebased_secondary_abi_loadable_modules = - rebase_path(invoker.secondary_abi_loadable_modules, root_build_dir) - args += [ "--secondary-abi-loadable-modules=$_rebased_secondary_abi_loadable_modules" ] - } - - if (defined(invoker.native_lib_placeholders) && - invoker.native_lib_placeholders != []) { - args += [ "--native-lib-placeholders=${invoker.native_lib_placeholders}" ] - } - - if (defined(invoker.secondary_native_lib_placeholders) && - invoker.secondary_native_lib_placeholders != []) { - args += [ "--secondary-native-lib-placeholders=${invoker.secondary_native_lib_placeholders}" ] - } - - if (defined(invoker.library_always_compress)) { - args += [ "--library-always-compress=${invoker.library_always_compress}" ] - } - - if (defined(invoker.apk_path)) { - # TODO(tiborg): Remove APK path from build config and use - # install_artifacts from metadata instead. - _rebased_apk_path = rebase_path(invoker.apk_path, root_build_dir) - args += [ "--apk-path=$_rebased_apk_path" ] - if (defined(invoker.incremental_apk_path)) { - _rebased_incremental_apk_path = - rebase_path(invoker.incremental_apk_path, root_build_dir) - _rebased_incremental_install_json_path = - rebase_path(invoker.incremental_install_json_path, root_build_dir) - args += [ - "--incremental-install-json-path=$_rebased_incremental_install_json_path", - "--incremental-apk-path=$_rebased_incremental_apk_path", - ] - } - } - - if (defined(invoker.target_sources_file)) { - args += [ - "--target-sources-file", - rebase_path(invoker.target_sources_file, root_build_dir), - ] - } - if (defined(invoker.srcjar)) { - args += [ - "--srcjar", - rebase_path(invoker.srcjar, root_build_dir), - ] - } - if (defined(invoker.bundled_srcjars)) { - _rebased_bundled_srcjars = - rebase_path(invoker.bundled_srcjars, root_build_dir) - args += [ "--bundled-srcjars=$_rebased_bundled_srcjars" ] - } - if (defined(invoker.proguard_enabled) && invoker.proguard_enabled) { - args += [ "--proguard-enabled" ] - } - if (defined(invoker.proguard_mapping_path)) { - _rebased_proguard_mapping_path = - rebase_path(invoker.proguard_mapping_path, root_build_dir) - args += [ "--proguard-mapping-path=$_rebased_proguard_mapping_path" ] - } - if (defined(invoker.input_jars_paths)) { - _rebased_input_jars_paths = - rebase_path(invoker.input_jars_paths, root_build_dir) - args += [ "--extra-classpath-jars=$_rebased_input_jars_paths" ] - } - if (defined(invoker.low_classpath_priority) && - invoker.low_classpath_priority) { - args += [ "--low-classpath-priority" ] - } - if (defined(invoker.mergeable_android_manifests)) { - _rebased_mergeable_android_manifests = - rebase_path(invoker.mergeable_android_manifests, root_build_dir) - args += [ - "--mergeable-android-manifests=$_rebased_mergeable_android_manifests", - ] - } - if (defined(invoker.proguard_configs)) { - _rebased_proguard_configs = - rebase_path(invoker.proguard_configs, root_build_dir) - args += [ "--proguard-configs=$_rebased_proguard_configs" ] - } - if (defined(invoker.gradle_treat_as_prebuilt) && - invoker.gradle_treat_as_prebuilt) { - args += [ "--gradle-treat-as-prebuilt" ] - } - if (defined(invoker.main_class)) { - args += [ - "--main-class", - invoker.main_class, - ] - } - if (defined(invoker.direct_deps_only) && invoker.direct_deps_only) { - args += [ "--direct-deps-only" ] - } - if (defined(invoker.use_interface_jars) && invoker.use_interface_jars) { - args += [ "--use-interface-jars" ] + _apk_under_test_config = "$_dep_gen_dir/$_dep_name.build_config.json" + _inputs += [ _apk_under_test_config ] + _deps += [ "$_dep_label$build_config_target_suffix" ] } if (defined(invoker.base_module_target)) { _dep_label = invoker.base_module_target _dep_gen_dir = get_label_info(_dep_label, "target_gen_dir") _dep_name = get_label_info(_dep_label, "name") - _dep_config = "$_dep_gen_dir/$_dep_name.build_config.json" - deps += [ "$_dep_label$build_config_target_suffix" ] - inputs += [ _dep_config ] - args += [ - "--base-module-build-config", - rebase_path(_dep_config, root_build_dir), - ] + _base_module_config = "$_dep_gen_dir/$_dep_name.build_config.json" + _inputs += [ _base_module_config ] + _deps += [ "$_dep_label$build_config_target_suffix" ] } if (defined(invoker.parent_module_target)) { _dep_label = invoker.parent_module_target _dep_gen_dir = get_label_info(_dep_label, "target_gen_dir") _dep_name = get_label_info(_dep_label, "name") - _dep_config = "$_dep_gen_dir/$_dep_name.build_config.json" - deps += [ "$_dep_label$build_config_target_suffix" ] - inputs += [ _dep_config ] - args += [ - "--parent-module-build-config", - rebase_path(_dep_config, root_build_dir), - ] - } - if (defined(invoker.module_name)) { - args += [ - "--module-name", - invoker.module_name, - ] - } - if (defined(invoker.modules)) { - foreach(_module, invoker.modules) { - if (defined(_module.uses_split)) { - args += [ "--uses-split=${_module.name}:${_module.uses_split}" ] - } - } + _parent_module_config = "$_dep_gen_dir/$_dep_name.build_config.json" + _inputs += [ _parent_module_config ] + _deps += [ "$_dep_label$build_config_target_suffix" ] } if (defined(invoker.module_build_configs)) { - inputs += invoker.module_build_configs - _rebased_configs = - rebase_path(invoker.module_build_configs, root_build_dir) - args += [ "--module-build-configs=$_rebased_configs" ] + _inputs += invoker.module_build_configs } + if (defined(invoker.android_manifest)) { + _inputs += [ invoker.android_manifest ] + } + if (defined(invoker.suffix_apk_assets_used_by)) { + _dep_label = invoker.suffix_apk_assets_used_by + _dep_gen_dir = get_label_info(_dep_label, "target_gen_dir") + _dep_name = get_label_info(_dep_label, "name") + _suffix_apk_assets_used_by_config = + "$_dep_gen_dir/$_dep_name.build_config.json" + if (_suffix_apk_assets_used_by_config != invoker.build_config) { + _inputs += [ _suffix_apk_assets_used_by_config ] + _deps += [ "$_dep_label$build_config_target_suffix" ] + } + } + if (defined(invoker.add_view_trace_events) && invoker.add_view_trace_events) { # Adding trace events involves rewriting bytecode and generating a new set @@ -619,51 +289,251 @@ _grandparent_invoker = _parent_invoker.invoker _trace_events_target_name = _grandparent_invoker.target_name } + } - # FIXME: This should likely be using the base module's target_out_dir - # rather than the current target's. - args += [ - "--trace-events-jar-dir", - rebase_path("$target_out_dir/$_trace_events_target_name", - root_build_dir), - ] + if (defined(invoker.assets)) { + # Only sources need rebasing since outputs are zip paths. + _assets = invoker.assets + _rebased_asset_sources = rebase_path(_assets.sources, root_build_dir) } - if (defined(invoker.version_name)) { - args += [ - "--version-name", - invoker.version_name, - ] + + _params = { + type = _type + gn_target = _target_label + + if (defined(_deps_configs)) { + deps_configs = rebase_path(_deps_configs, root_build_dir) + } + if (defined(_public_deps_configs)) { + public_deps_configs = rebase_path(_public_deps_configs, root_build_dir) + } + if (defined(_processor_configs)) { + processor_configs = rebase_path(_processor_configs, root_build_dir) + } + if (defined(_apk_under_test_config)) { + apk_under_test_config = + rebase_path(_apk_under_test_config, root_build_dir) + } + if (defined(_base_module_config)) { + base_module_config = rebase_path(_base_module_config, root_build_dir) + } + if (defined(_parent_module_config)) { + parent_module_config = + rebase_path(_parent_module_config, root_build_dir) + } + if (defined(invoker.module_build_configs)) { + module_configs = + rebase_path(invoker.module_build_configs, root_build_dir) + } + if (defined(_suffix_apk_assets_used_by_config)) { + suffix_apk_assets_used_by_config = + rebase_path(_suffix_apk_assets_used_by_config, root_build_dir) + } + + forward_variables_from(invoker, + [ + "assets", + "bypass_platform_checks", + "chromium_code", + "direct_deps_only", + "disable_asset_compression", + "gradle_treat_as_prebuilt", + "is_prebuilt", + "library_always_compress", + "low_classpath_priority", + "main_class", + "module_name", + "native_lib_placeholders", + "preferred_dep", + "proguard_enabled", + "recursive_resource_deps", + "requires_android", + "requires_robolectric", + "resource_overlay", + "secondary_native_lib_placeholders", + "supports_android", + "use_interface_jars", + "version_code", + "version_name", + ]) + if (defined(invoker.custom_package)) { + package_name = invoker.custom_package + } + + if (defined(invoker.apk_path)) { + apk_path = rebase_path(invoker.apk_path, root_build_dir) + } + if (defined(invoker.incremental_apk_path)) { + incremental_apk_path = + rebase_path(invoker.incremental_apk_path, root_build_dir) + incremental_install_json_path = + rebase_path(invoker.incremental_install_json_path, root_build_dir) + } + if (defined(invoker.aar_path)) { + aar_path = rebase_path(invoker.aar_path, root_build_dir) + } + if (defined(invoker.processed_jar_path)) { + processed_jar_path = + rebase_path(invoker.processed_jar_path, root_build_dir) + } + if (defined(invoker.unprocessed_jar_path)) { + unprocessed_jar_path = + rebase_path(invoker.unprocessed_jar_path, root_build_dir) + } + if (defined(invoker.ijar_path)) { + interface_jar_path = rebase_path(invoker.ijar_path, root_build_dir) + } + if (defined(invoker.kotlinc_jar_path)) { + kotlinc_jar_path = rebase_path(invoker.kotlinc_jar_path, root_build_dir) + } + if (defined(invoker.java_resources_jar)) { + java_resources_jar_path = + rebase_path(invoker.java_resources_jar, root_build_dir) + } + if (defined(invoker.dex_path)) { + # Dex path for library targets, or the the intermediate library for apks. + dex_path = rebase_path(invoker.dex_path, root_build_dir) + } + if (defined(invoker.final_dex_path)) { + # Dex path for the final apk. + final_dex_path = rebase_path(invoker.final_dex_path, root_build_dir) + } + if (defined(invoker.merged_android_manifest)) { + merged_android_manifest = + rebase_path(invoker.merged_android_manifest, root_build_dir) + } + if (defined(invoker.android_manifest)) { + android_manifest = rebase_path(invoker.android_manifest, root_build_dir) + } + if (defined(invoker.resources_zip)) { + resources_zip = rebase_path(invoker.resources_zip, root_build_dir) + } + if (defined(invoker.rtxt_path)) { + rtxt_path = rebase_path(invoker.rtxt_path, root_build_dir) + } + if (defined(invoker.res_size_info_path)) { + res_size_info_path = + rebase_path(invoker.res_size_info_path, root_build_dir) + } + if (defined(invoker.res_sources_path)) { + res_sources_path = rebase_path(invoker.res_sources_path, root_build_dir) + } + if (defined(invoker.proto_resources_path)) { + proto_resources_path = + rebase_path(invoker.proto_resources_path, root_build_dir) + } + if (_type == "android_app_bundle_module") { + if (defined(invoker.module_pathmap_path)) { + module_pathmap_path = + rebase_path(invoker.module_pathmap_path, root_build_dir) + } else { + module_pathmap_path = "" + } + if (defined(invoker.base_allowlist_rtxt_path)) { + base_allowlist_rtxt_path = + rebase_path(invoker.base_allowlist_rtxt_path, root_build_dir) + } else { + base_allowlist_rtxt_path = "" + } + } + + if (defined(assets)) { + assets.sources = [] + assets.sources = _rebased_asset_sources + } + if (defined(invoker.shared_libraries_runtime_deps_file)) { + # Don't list shared_libraries_runtime_deps_file as an input in order to + # avoid having to depend on the runtime_deps target. See comment in + # rules.gni for why we do this. + shared_libraries_runtime_deps_file = + rebase_path(invoker.shared_libraries_runtime_deps_file, + root_build_dir) + } + if (defined(invoker.secondary_abi_shared_libraries_runtime_deps_file)) { + secondary_abi_shared_libraries_runtime_deps_file = + rebase_path( + invoker.secondary_abi_shared_libraries_runtime_deps_file, + root_build_dir) + } + if (defined(invoker.loadable_modules)) { + loadable_modules = rebase_path(invoker.loadable_modules, root_build_dir) + } + if (defined(invoker.secondary_abi_loadable_modules) && + invoker.secondary_abi_loadable_modules != []) { + secondary_abi_loadable_modules = + rebase_path(invoker.secondary_abi_loadable_modules, root_build_dir) + } + if (defined(invoker.target_sources_file)) { + target_sources_file = + rebase_path(invoker.target_sources_file, root_build_dir) + } + if (defined(invoker.srcjar)) { + srcjar_path = rebase_path(invoker.srcjar, root_build_dir) + } + if (defined(invoker.bundled_srcjars)) { + bundled_srcjars = rebase_path(invoker.bundled_srcjars, root_build_dir) + } + if (defined(invoker.proguard_mapping_path)) { + proguard_mapping_path = + rebase_path(invoker.proguard_mapping_path, root_build_dir) + } + if (defined(invoker.input_jars_paths)) { + input_jars_paths = rebase_path(invoker.input_jars_paths, root_build_dir) + } + if (defined(invoker.mergeable_android_manifests)) { + mergeable_android_manifests = + rebase_path(invoker.mergeable_android_manifests, root_build_dir) + } + if (defined(invoker.proguard_configs)) { + proguard_configs = rebase_path(invoker.proguard_configs, root_build_dir) + } + if (defined(_trace_events_target_name)) { + # FIXME: This should likely be using the base module's target_out_dir + # rather than the current target's. + trace_events_jar_dir = + rebase_path("$target_out_dir/$_trace_events_target_name", + root_build_dir) + } + if (defined(invoker.modules)) { + modules = [] + foreach(_module, invoker.modules) { + modules += [ + { + name = _module.name + build_config = rebase_path(_module.build_config, root_build_dir) + if (defined(_module.uses_split)) { + uses_split = _module.uses_split + } + }, + ] + } + } } - if (defined(invoker.version_code)) { - args += [ - "--version-code", - invoker.version_code, - ] + } + write_file(_params_file, _params, "json") + + action_with_pydeps(target_name) { + forward_variables_from(invoker, [ "testonly" ]) + script = "//build/android/gyp/write_build_config.py" + depfile = "$target_gen_dir/$target_name.d" + inputs = _inputs + outputs = [ invoker.build_config ] + + args = [ + "--depfile", + rebase_path(depfile, root_build_dir), + "--params", + rebase_path(_params_file, root_build_dir), + ] + + deps = [] + if (defined(invoker.deps)) { + deps += invoker.deps } - if (defined(invoker.recursive_resource_deps) && - invoker.recursive_resource_deps) { - args += [ "--recursive-resource-deps" ] + if (defined(invoker.android_manifest_dep)) { + deps += [ invoker.android_manifest_dep ] } - if (current_toolchain != default_toolchain) { - # This has to be a built-time error rather than a GN assert because many - # packages have a mix of java and non-java targets. For example, the - # following would fail even though nothing depends on :bar(//baz): - # - # shared_library("foo") { - # } - # - # android_library("bar") { - # deps = [ ":foo(//baz)" ] - # assert(current_toolchain == default_toolchain) - # } - _msg = [ - "Tried to build an Android target in a non-default toolchain.", - "target: $_target_label", - "current_toolchain: $current_toolchain", - "default_toolchain: $default_toolchain", - ] - args += [ "--fail=$_msg" ] - } + deps += _deps } } @@ -693,7 +563,7 @@ rebase_path(depfile, root_build_dir), "--srcjar-out", rebase_path(invoker.srcjar_path, root_build_dir), - "--deps-rtxts=@FileArg($_rebased_build_config:deps_info:dependency_r_txt_files)", + "--deps-rtxts=@FileArg($_rebased_build_config:dependency_rtxt_files)", "--r-package=${invoker.package}", ] } @@ -865,9 +735,9 @@ ] } } else if (_test_type == "instrumentation") { - _test_apk = "@WrappedPath(@FileArg($_rebased_apk_build_config:deps_info:apk_path))" + _test_apk = "@WrappedPath(@FileArg($_rebased_apk_build_config:apk_path))" if (_incremental_apk) { - _test_apk = "@WrappedPath(@FileArg($_rebased_apk_build_config:deps_info:incremental_apk_path))" + _test_apk = "@WrappedPath(@FileArg($_rebased_apk_build_config:incremental_apk_path))" } executable_args += [ "--test-apk", @@ -882,7 +752,7 @@ ".build_config.json" _rebased_apk_under_test_build_config = rebase_path(_apk_under_test_build_config, root_build_dir) - _apk_under_test = "@WrappedPath(@FileArg($_rebased_apk_under_test_build_config:deps_info:incremental_apk_path))" + _apk_under_test = "@WrappedPath(@FileArg($_rebased_apk_under_test_build_config:incremental_apk_path))" } else { deps += [ ":${_install_artifacts_target_name}" ] _rebased_install_artifacts_json = @@ -904,7 +774,7 @@ _rebased_build_config = rebase_path(_build_config, root_build_dir) executable_args += [ "--use-webview-provider", - "@WrappedPath(@FileArg($_rebased_build_config:deps_info:apk_path))", + "@WrappedPath(@FileArg($_rebased_build_config:apk_path))", ] } if (defined(invoker.proguard_mapping_path)) { @@ -978,7 +848,7 @@ } else if (_test_type == "linker") { executable_args += [ "--test-apk", - "@WrappedPath(@FileArg($_rebased_apk_build_config:deps_info:apk_path))", + "@WrappedPath(@FileArg($_rebased_apk_build_config:apk_path))", ] } else { assert(false, "Invalid test type: $_test_type.") @@ -1000,7 +870,7 @@ _rebased_build_config = rebase_path(_build_config, root_build_dir) executable_args += [ "--additional-apk", - "@WrappedPath(@FileArg($_rebased_build_config:deps_info:apk_path))", + "@WrappedPath(@FileArg($_rebased_build_config:apk_path))", ] } } @@ -1010,12 +880,12 @@ if (_incremental_apk) { executable_args += [ "--test-apk-incremental-install-json", - "@WrappedPath(@FileArg($_rebased_apk_build_config:deps_info:incremental_install_json_path))", + "@WrappedPath(@FileArg($_rebased_apk_build_config:incremental_install_json_path))", ] if (defined(invoker.apk_under_test)) { executable_args += [ "--apk-under-test-incremental-install-json", - "@WrappedPath(@FileArg($_rebased_apk_under_test_build_config:deps_info:incremental_install_json_path))", + "@WrappedPath(@FileArg($_rebased_apk_under_test_build_config:incremental_install_json_path))", ] } executable_args += [ "--fast-local-dev" ] @@ -1211,19 +1081,19 @@ rebase_path(invoker.build_config, root_build_dir) args += [ - "--manifest-path=@FileArg($_rebased_build_config:deps_info:lint_android_manifest)", - "--extra-manifest-paths=@FileArg($_rebased_build_config:deps_info:lint_extra_android_manifests)", + "--manifest-path=@FileArg($_rebased_build_config:android_manifest)", + "--extra-manifest-paths=@FileArg($_rebased_build_config:lint_extra_android_manifests)", # Lint requires all source and all resource files to be passed in the # same invocation for checks like UnusedResources. - "--sources=@FileArg($_rebased_build_config:deps_info:lint_sources)", - "--aars=@FileArg($_rebased_build_config:deps_info:lint_aars)", - "--srcjars=@FileArg($_rebased_build_config:deps_info:lint_srcjars)", - "--resource-sources=@FileArg($_rebased_build_config:deps_info:lint_resource_sources)", - "--resource-zips=@FileArg($_rebased_build_config:deps_info:lint_resource_zips)", + "--sources=@FileArg($_rebased_build_config:lint_sources)", + "--aars=@FileArg($_rebased_build_config:lint_aars)", + "--srcjars=@FileArg($_rebased_build_config:lint_srcjars)", + "--resource-sources=@FileArg($_rebased_build_config:lint_resource_sources)", + "--resource-zips=@FileArg($_rebased_build_config:lint_resource_zips)", # The full classpath is required for annotation checks like @IntDef. - "--classpath=@FileArg($_rebased_build_config:deps_info:javac_full_interface_classpath)", + "--classpath=@FileArg($_rebased_build_config:javac_full_interface_classpath)", ] } @@ -1287,8 +1157,8 @@ # This is generally the apk name, and serves to identify the mapping # file that would be required to deobfuscate a stacktrace. _mapping_basename = get_path_info(_mapping_path, "name") - _version_code = "@FileArg($_rebased_build_config:deps_info:version_code)" - _package_name = "@FileArg($_rebased_build_config:deps_info:package_name)" + _version_code = "@FileArg($_rebased_build_config:version_code)" + _package_name = "@FileArg($_rebased_build_config:package_name)" if (defined(invoker.package_name)) { _package_name = invoker.package_name } @@ -1306,9 +1176,9 @@ "--mapping-output", rebase_path(_mapping_path, root_build_dir), "--sdk-jars", - "@FileArg($_rebased_build_config:android:sdk_jars)", + "@FileArg($_rebased_build_config:sdk_jars)", "--sdk-extension-jars", - "@FileArg($_rebased_build_config:deps_info:proguard_classpath_jars)", + "@FileArg($_rebased_build_config:proguard_classpath_jars)", "--r8-path", rebase_path(_r8_path, root_build_dir), "--custom-r8-path", @@ -1316,7 +1186,7 @@ "--package-name=$_package_name", "--source-file", _source_file_template, - "--proguard-configs=@FileArg($_rebased_build_config:deps_info:proguard_all_configs)", + "--proguard-configs=@FileArg($_rebased_build_config:proguard_all_configs)", ] if (treat_warnings_as_errors) { _args += [ "--warnings-as-errors" ] @@ -1345,11 +1215,9 @@ if (defined(invoker.modules)) { foreach(_feature_module, invoker.modules) { - _rebased_module_build_config = - rebase_path(_feature_module.build_config, root_build_dir) _args += [ "--feature-name=${_feature_module.name}", - "--dex-dest=@FileArg($_rebased_module_build_config:final_dex:path)", + "--dex-dest=@FileArg($_rebased_build_config:modules:${_feature_module.name}:final_dex_path)", ] # The bundle's build config has the correct classpaths - the individual @@ -1374,9 +1242,11 @@ ] } else { if (defined(invoker.has_apk_under_test) && invoker.has_apk_under_test) { - _args += [ "--input-paths=@FileArg($_rebased_build_config:deps_info:device_classpath_extended)" ] + _args += [ "--input-paths=@FileArg($_rebased_build_config:device_classpath_extended)" ] } else { - _args += [ "--input-paths=@FileArg($_rebased_build_config:deps_info:device_classpath)" ] + _args += [ + "--input-paths=@FileArg($_rebased_build_config:device_classpath)", + ] } # We don't directly set the output arg on the _args variable since it is @@ -1543,7 +1413,7 @@ rebase_path(_java_script, root_build_dir), "--main-class", _main_class, - "--classpath=@FileArg($_rebased_build_config:deps_info:host_classpath)", + "--classpath=@FileArg($_rebased_build_config:host_classpath)", "--max-heap-size=$_max_heap_size", ] data = [] @@ -1764,7 +1634,7 @@ args += [ # Pass the full classpath to find new dependencies that are not in # the .desugardeps file. - "--classpath=@FileArg($_rebased_build_config:deps_info:javac_full_interface_classpath)", + "--classpath=@FileArg($_rebased_build_config:javac_full_interface_classpath)", ] } @@ -1775,7 +1645,7 @@ args += [ "--desugar-dependencies", rebase_path(_desugar_dependencies_path, root_build_dir), - "--bootclasspath=@FileArg($_rebased_build_config:android:sdk_jars)", + "--bootclasspath=@FileArg($_rebased_build_config:sdk_jars)", ] } } @@ -1900,9 +1770,9 @@ rebase_path(outputs[0], root_build_dir), "--chromium-output-dir", rebase_path(root_build_dir, root_build_dir), - "--direct-classpath-jars=@FileArg($_rebased_build_config:javac:interface_classpath)", - "--full-classpath-jars=@FileArg($_rebased_build_config:deps_info:javac_full_interface_classpath)", - "--full-classpath-gn-targets=@FileArg($_rebased_build_config:deps_info:javac_full_classpath_targets)", + "--direct-classpath-jars=@FileArg($_rebased_build_config:interface_classpath)", + "--full-classpath-jars=@FileArg($_rebased_build_config:javac_full_interface_classpath)", + "--full-classpath-gn-targets=@FileArg($_rebased_build_config:javac_full_classpath_targets)", ] if (defined(invoker.header_jar_path)) { _rebased_header_jar_path = @@ -1916,7 +1786,8 @@ args += [ "--use-build-server" ] } if (invoker.include_android_sdk) { - args += [ "--sdk-classpath-jars=@FileArg($_rebased_build_config:android:sdk_jars)" ] + args += + [ "--sdk-classpath-jars=@FileArg($_rebased_build_config:sdk_jars)" ] } if (treat_warnings_as_errors) { args += [ "--warnings-as-errors" ] @@ -1952,7 +1823,7 @@ "--output", rebase_path(invoker.output_manifest, root_build_dir), "--extras", - "@FileArg($_rebased_build_config:deps_info:extra_android_manifests)", + "@FileArg($_rebased_build_config:extra_android_manifests)", "--min-sdk-version=${invoker.min_sdk_version}", "--target-sdk-version=${invoker.target_sdk_version}", ] @@ -1990,7 +1861,7 @@ # If custom_package is not provided, path to an AndroidManifest.xml file # that is only used to extract a package name out of it. # - # r_text_in_path: (optional) + # rtxt_in_path: (optional) # Path to an input R.txt file to use to generate the R.java file. # The default is to use 'aapt' to generate the file from the content # of the resource directories. @@ -2000,7 +1871,7 @@ # Path to a .resources.zip that will simply contain all the # input resources, collected in a single archive. # - # r_text_out_path: Path for the generated R.txt file. + # rtxt_out_path: Path for the generated R.txt file. # template("prepare_resources") { action_with_pydeps(target_name) { @@ -2016,7 +1887,7 @@ outputs = [ invoker.resources_zip, invoker.resources_zip + ".info", - invoker.r_text_out_path, + invoker.rtxt_out_path, ] inputs = [ invoker.res_sources_path ] @@ -2031,15 +1902,15 @@ "--resource-zip-out", rebase_path(invoker.resources_zip, root_build_dir), "--r-text-out", - rebase_path(invoker.r_text_out_path, root_build_dir), + rebase_path(invoker.rtxt_out_path, root_build_dir), ] - if (defined(invoker.r_text_in_path)) { - _r_text_in_path = invoker.r_text_in_path - inputs += [ _r_text_in_path ] + if (defined(invoker.rtxt_in_path)) { + _rtxt_in_path = invoker.rtxt_in_path + inputs += [ _rtxt_in_path ] args += [ "--r-text-in", - rebase_path(_r_text_in_path, root_build_dir), + rebase_path(_rtxt_in_path, root_build_dir), ] } @@ -2142,7 +2013,7 @@ # # proto_output: Path to output .proto.ap_ file (optional). # - # r_text_out_path: (optional): + # rtxt_out_path: (optional): # Path for the corresponding generated R.txt file. # # proguard_file: (optional) @@ -2244,8 +2115,8 @@ "--include-resources=$_rebased_android_sdk_jar", "--aapt2-path", rebase_path(android_sdk_tools_bundle_aapt2, root_build_dir), - "--dependencies-res-zips=@FileArg($_rebased_build_config:deps_info:dependency_zips)", - "--extra-res-packages=@FileArg($_rebased_build_config:deps_info:extra_package_names)", + "--dependencies-res-zips=@FileArg($_rebased_build_config:dependency_zips)", + "--extra-res-packages=@FileArg($_rebased_build_config:extra_package_names)", "--min-sdk-version=${invoker.min_sdk_version}", "--target-sdk-version=${_target_sdk_version}", "--webp-cache-dir=obj/android-webp-cache", @@ -2306,11 +2177,11 @@ _args += [ "--debuggable" ] } - if (defined(invoker.r_text_out_path)) { - _outputs += [ invoker.r_text_out_path ] + if (defined(invoker.rtxt_out_path)) { + _outputs += [ invoker.rtxt_out_path ] _args += [ "--r-text-out", - rebase_path(invoker.r_text_out_path, root_build_dir), + rebase_path(invoker.rtxt_out_path, root_build_dir), ] } @@ -2364,9 +2235,9 @@ if (!defined(testonly) || !testonly || (defined(invoker.enforce_resource_overlays_in_tests) && invoker.enforce_resource_overlays_in_tests)) { - _args += [ "--dependencies-res-zip-overlays=@FileArg($_rebased_build_config:deps_info:dependency_zip_overlays)" ] + _args += [ "--dependencies-res-zip-overlays=@FileArg($_rebased_build_config:dependency_zip_overlays)" ] } else { - _args += [ "--dependencies-res-zip-overlays=@FileArg($_rebased_build_config:deps_info:dependency_zips)" ] + _args += [ "--dependencies-res-zip-overlays=@FileArg($_rebased_build_config:dependency_zips)" ] } if (defined(invoker.proguard_file)) { @@ -2548,14 +2419,14 @@ script = "//build/android/gyp/optimize_resources.py" inputs = [ android_sdk_tools_bundle_aapt2, - invoker.r_text_path, + invoker.rtxt_path, ] outputs = [] args = [ "--aapt2-path", rebase_path(android_sdk_tools_bundle_aapt2, root_build_dir), "--r-text-in", - rebase_path(invoker.r_text_path, root_build_dir), + rebase_path(invoker.rtxt_path, root_build_dir), ] if (defined(invoker.optimized_proto_output)) { inputs += [ invoker.proto_input_path ] @@ -2622,8 +2493,8 @@ rebase_path(_unused_resources_script, root_build_dir), "--output-config", rebase_path(invoker.output_config, root_build_dir), - "--r-text-in=@FileArg($_rebased_build_config:deps_info:r_text_path)", - "--dependencies-res-zips=@FileArg($_rebased_build_config:deps_info:dependency_zips)", + "--r-text-in=@FileArg($_rebased_build_config:rtxt_path)", + "--dependencies-res-zips=@FileArg($_rebased_build_config:dependency_zips)", "--depfile", rebase_path(depfile, root_build_dir), ] @@ -2636,11 +2507,11 @@ ] } - if (defined(invoker.output_r_txt)) { - outputs += [ invoker.output_r_txt ] + if (defined(invoker.output_rtxt_path)) { + outputs += [ invoker.output_rtxt_path ] args += [ "--r-text-out", - rebase_path(invoker.output_r_txt, root_build_dir), + rebase_path(invoker.output_rtxt_path, root_build_dir), ] } @@ -2650,14 +2521,14 @@ _rebased_module_build_config = rebase_path(_build_config, root_build_dir) args += [ - "--dexes=@FileArg($_rebased_module_build_config:final_dex:path)", - "--android-manifests=@FileArg($_rebased_module_build_config:deps_info:merged_android_manifest)", + "--dexes=@FileArg($_rebased_module_build_config:final_dex_path)", + "--android-manifests=@FileArg($_rebased_module_build_config:merged_android_manifest)", ] } } else { args += [ - "--dexes=@FileArg($_rebased_build_config:final_dex:path)", - "--android-manifests=@FileArg($_rebased_build_config:deps_info:merged_android_manifest)", + "--dexes=@FileArg($_rebased_build_config:final_dex_path)", + "--android-manifests=@FileArg($_rebased_build_config:merged_android_manifest)", ] } } @@ -2703,23 +2574,28 @@ _rebased_module_build_config = rebase_path(_module.build_config, root_build_dir) args += [ - "--jar-files=@FileArg($_rebased_module_build_config:deps_info:unprocessed_jar_path)", - "--jar-files=@FileArg($_rebased_module_build_config:deps_info:javac_full_classpath)", - "--in-res-info-path=@FileArg($_rebased_module_build_config:deps_info:res_size_info)", - "--uncompressed-assets=@FileArg($_rebased_module_build_config:deps_info:uncompressed_assets)", + # Look for .info files created by compile_java.py. + "--jar-files=@FileArg($_rebased_module_build_config:unprocessed_jar_path)", + "--jar-files=@FileArg($_rebased_module_build_config:javac_full_classpath)", + "--in-res-info-path=@FileArg($_rebased_module_build_config:res_size_info_path)", "--assets=@FileArg($_rebased_build_config:modules:${_module.name}:assets)", + "--uncompressed-assets=@FileArg($_rebased_build_config:modules:${_module.name}:uncompressed_assets)", ] } } else { - inputs += [ invoker.res_size_info_path ] + inputs += [ + invoker.res_size_info_path, + invoker.unprocessed_jar_path, + ] + _rebased_unprocessed_jar_path = + rebase_path(invoker.unprocessed_jar_path, root_build_dir) args += [ - # Look for .info files created by compile_java.py. - "--jar-files=@FileArg($_rebased_build_config:deps_info:unprocessed_jar_path)", - "--jar-files=@FileArg($_rebased_build_config:deps_info:javac_full_classpath)", + "--jar-files=$_rebased_unprocessed_jar_path", + "--jar-files=@FileArg($_rebased_build_config:javac_full_classpath)", "--in-res-info-path", rebase_path(invoker.res_size_info_path, root_build_dir), - "--assets=@FileArg($_rebased_build_config:deps_info:assets)", - "--uncompressed-assets=@FileArg($_rebased_build_config:deps_info:uncompressed_assets)", + "--assets=@FileArg($_rebased_build_config:assets)", + "--uncompressed-assets=@FileArg($_rebased_build_config:uncompressed_assets)", ] } } @@ -2791,8 +2667,8 @@ _inputs += [ invoker.build_config ] _rebased_build_config = rebase_path(invoker.build_config, root_build_dir) _args += [ - "--assets=@FileArg($_rebased_build_config:deps_info:assets)", - "--uncompressed-assets=@FileArg($_rebased_build_config:deps_info:uncompressed_assets)", + "--assets=@FileArg($_rebased_build_config:assets)", + "--uncompressed-assets=@FileArg($_rebased_build_config:uncompressed_assets)", ] if (!_is_robolectric_apk) { _args += [ "--java-resources=@FileArg($_rebased_build_config:java_resources_jars)" ] @@ -2956,7 +2832,7 @@ # # Note that the only way to specify custom annotation processors is # by using build_config to point to a file that corresponds to a java-related - # target that includes javac:processor_classes entries (i.e. there is no + # target that includes processor_classes entries (i.e. there is no # variable here that can be used for this purpose). # # Note also the peculiar use of source_files / target_sources_file. The content @@ -3074,17 +2950,16 @@ } _rebased_output_jar_path = rebase_path(invoker.output_jar_path, root_build_dir) - - # Error Prone does not run on srcjar, and they are not needed as inputs thanks to Turbine. - _rebased_java_srcjars = rebase_path(_java_srcjars, root_build_dir) - _rebased_generated_dir = rebase_path( - "$target_gen_dir/${invoker.main_target_name}/generated_java", - root_build_dir) - args += [ - "--jar-path=$_rebased_output_jar_path", - "--java-srcjars=$_rebased_java_srcjars", - ] + args += [ "--jar-path=$_rebased_output_jar_path" ] + if (_java_srcjars != []) { + _rebased_java_srcjars = rebase_path(_java_srcjars, root_build_dir) + args += [ "--java-srcjars=$_rebased_java_srcjars" ] + } if (!invoker.use_turbine) { + # Error Prone does not run on srcjar, and they are not needed as inputs thanks to Turbine. + _rebased_generated_dir = rebase_path( + "$target_gen_dir/${invoker.main_target_name}/generated_java", + root_build_dir) args += [ "--generated-dir=$_rebased_generated_dir" ] } inputs += _java_srcjars @@ -3109,7 +2984,9 @@ # SDK jar must be first on classpath. if (invoker.include_android_sdk) { - args += [ "--classpath=@FileArg($_rebased_build_config:android:sdk_interface_jars)" ] + args += [ + "--classpath=@FileArg($_rebased_build_config:sdk_interface_jars)", + ] } if (defined(invoker.header_jar_path)) { @@ -3142,14 +3019,14 @@ rebase_path(_turbine_jar_path, root_build_dir), "--generated-jar-path", rebase_path(invoker.generated_jar_path, root_build_dir), - "--processorpath=@FileArg($_rebased_build_config:javac:processor_classpath)", - "--processors=@FileArg($_rebased_build_config:javac:processor_classes)", + "--processorpath=@FileArg($_rebased_build_config:processor_classpath)", + "--processors=@FileArg($_rebased_build_config:processor_classes)", # Turbine needs only direct deps. - "--classpath=@FileArg($_rebased_build_config:javac:interface_classpath)", + "--classpath=@FileArg($_rebased_build_config:interface_classpath)", ] } else { - args += [ "--classpath=@FileArg($_rebased_build_config:deps_info:javac_full_interface_classpath)" ] + args += [ "--classpath=@FileArg($_rebased_build_config:javac_full_interface_classpath)" ] if (invoker.chromium_code) { args += [ "--chromium-code" ] } @@ -3190,7 +3067,7 @@ args += [ "--processorpath=$_rebased_nullaway_jar", "--enable-nullaway", - "--processorpath=@FileArg($_rebased_errorprone_buildconfig:deps_info:host_classpath)", + "--processorpath=@FileArg($_rebased_errorprone_buildconfig:host_classpath)", ] inputs += [ _nullaway_jar, @@ -3303,10 +3180,12 @@ # SDK jar must be first on classpath. if (invoker.include_android_sdk) { - args += [ "--classpath=@FileArg($_rebased_build_config:android:sdk_interface_jars)" ] + args += [ + "--classpath=@FileArg($_rebased_build_config:sdk_interface_jars)", + ] } - args += [ "--classpath=@FileArg($_rebased_build_config:deps_info:javac_full_interface_classpath)" ] + args += [ "--classpath=@FileArg($_rebased_build_config:javac_full_interface_classpath)" ] if (_chromium_code) { args += [ "--chromium-code" ] @@ -3461,7 +3340,7 @@ # proto_resources_path: The path of an zip archive containing the APK's # resources compiled to the protocol buffer format (instead of regular # binary xml + resources.arsc). - # r_text_path: The path of the R.txt file generated when compiling the + # rtxt_path: The path of the R.txt file generated when compiling the # resources for this target. # module_pathmap_path: The path of the pathmap file generated when compiling # the resources for the bundle module, if path shortening is enabled. @@ -3783,7 +3662,7 @@ "proguard_enabled", "proguard_mapping_path", "public_target_label", - "r_text_path", + "rtxt_path", "suffix_apk_assets_used_by", "type", "use_interface_jars", @@ -4005,7 +3884,6 @@ if (_enable_errorprone) { source_files = _errorprone_source_files } else { - main_target_name = _main_target_name source_files = _source_files srcjar_deps = _srcjar_deps srcjars = _srcjars @@ -4062,6 +3940,7 @@ "jar_excluded_patterns", "jar_included_patterns", ]) + main_target_name = _main_target_name output_jar_path = _javac_jar_path if (!_skip_header_jar) { deps = [ ":$_header_target_name" ] @@ -4133,9 +4012,9 @@ "--script", rebase_path(_rewriter_path, root_build_dir), "--classpath", - "@FileArg($_rebased_build_config:deps_info:javac_full_classpath)", + "@FileArg($_rebased_build_config:javac_full_classpath)", "--classpath", - "@FileArg($_rebased_build_config:android:sdk_jars)", + "@FileArg($_rebased_build_config:sdk_jars)", "--input-jar", rebase_path(_unprocessed_jar_path, root_build_dir), "--output-jar", @@ -4529,28 +4408,22 @@ "--format=bundle-module", "--output-apk", rebase_path(invoker.module_zip_path, root_build_dir), - "--resource-apk=@FileArg(" + - "$_rebased_build_config:deps_info:proto_resources_path)", + "--resource-apk=@FileArg(" + "$_rebased_build_config:proto_resources_path)", "--assets=@FileArg($_rebased_app_bundle_build_config:modules:${invoker.module_name}:assets)", - "--uncompressed-assets=@FileArg(" + - "$_rebased_build_config:deps_info:uncompressed_assets)", + "--uncompressed-assets=@FileArg($_rebased_app_bundle_build_config:modules:${invoker.module_name}:uncompressed_assets)", + "--android-abi=$android_app_abi", "--native-libs=@FileArg($_rebased_build_config:native:libraries)", "--native-libs=@FileArg($_rebased_build_config:native:loadable_modules)", - "--native-lib-placeholders=@FileArg($_rebased_build_config" + - ":native:native_library_placeholders)", - "--secondary-native-lib-placeholders=@FileArg($_rebased_build_config" + - ":native:secondary_native_library_placeholders)", - "--android-abi=$android_app_abi", + "--native-lib-placeholders=@FileArg($_rebased_build_config:native:placeholders)", "--min-sdk-version=${invoker.min_sdk_version}", "--library-always-compress=@FileArg($_rebased_build_config:native:library_always_compress)", ] if (defined(android_app_secondary_abi)) { _args += [ - "--secondary-native-libs=@FileArg(" + - "$_rebased_build_config:native:secondary_abi_libraries)", - "--secondary-native-libs=@FileArg(" + - "$_rebased_build_config:native:secondary_abi_loadable_modules)", "--secondary-android-abi=$android_app_secondary_abi", + "--secondary-native-libs=@FileArg($_rebased_build_config:native:secondary_abi_libraries)", + "--secondary-native-libs=@FileArg($_rebased_build_config:native:secondary_abi_loadable_modules)", + "--secondary-native-lib-placeholders=@FileArg($_rebased_build_config:native:secondary_abi_placeholders)", ] } if (defined(invoker.is_multi_abi) && invoker.is_multi_abi) { @@ -4569,7 +4442,7 @@ _rebased_dex_path = rebase_path(invoker.dex_path, root_build_dir) _args += [ "--dex-file=$_rebased_dex_path" ] } else { - _args += [ "--dex-file=@FileArg($_rebased_build_config:final_dex:path)" ] + _args += [ "--dex-file=@FileArg($_rebased_build_config:final_dex_path)" ] } if (treat_warnings_as_errors) {
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index ea9ab152..f43fcfa 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -530,7 +530,7 @@ if (defined(invoker.deps)) { possible_config_deps = invoker.deps } - r_text = _rtxt_out_path + rtxt_path = _rtxt_out_path } action_with_pydeps(target_name) { forward_variables_from(invoker, [ "visibility" ]) @@ -695,7 +695,7 @@ # resource_overlay=true depends on another target with # resource_overlay=true the target with the dependency overrides the # other. - # r_text_file: (optional) path to pre-generated R.txt to be used when + # rtxt_file: (optional) path to pre-generated R.txt to be used when # generating R.java instead of resource-based aapt-generated one. # recursive_resource_deps: (optional) whether deps should be walked # recursively to find resource deps. @@ -728,7 +728,7 @@ _res_sources_path = "$target_gen_dir/${invoker.target_name}.res.sources" _resources_zip = "$target_out_dir/$target_name.resources.zip" - _r_text_out_path = _base_path + "_R.txt" + _rtxt_out_path = _base_path + "_R.txt" _build_config = _base_path + ".build_config.json" _build_config_target_name = "$target_name$build_config_target_suffix" @@ -778,7 +778,7 @@ "recursive_resource_deps", ]) - r_text = _r_text_out_path + rtxt_path = _rtxt_out_path possible_config_deps = _deps + [ _android_sdk_dep ] # Always merge manifests from resources. @@ -821,10 +821,10 @@ sources = _resource_files resources_zip = _resources_zip - r_text_out_path = _r_text_out_path + rtxt_out_path = _rtxt_out_path - if (defined(invoker.r_text_file)) { - r_text_in_path = invoker.r_text_file + if (defined(invoker.rtxt_file)) { + rtxt_in_path = invoker.rtxt_file } } } @@ -882,38 +882,39 @@ if (defined(invoker.renaming_sources)) { _renaming_sources = invoker.renaming_sources } + if (_renaming_sources != []) { + assert(defined(invoker.renaming_destinations)) + _source_count = 0 + foreach(_, _renaming_sources) { + _source_count += 1 + } + _dest_count = 0 + foreach(_, invoker.renaming_destinations) { + _dest_count += 1 + } + assert( + _source_count == _dest_count, + "android_assets() renaming_sources.length != renaming_destinations.length") + } + write_build_config(_build_config_target_name) { type = "android_assets" build_config = _build_config - forward_variables_from(invoker, - [ - "disable_compression", - "treat_as_locale_paks", - ]) - if (defined(invoker.deps)) { possible_config_deps = invoker.deps } + assets = { + forward_variables_from(invoker, + [ + "disable_compression", + "treat_as_locale_paks", + ]) - if (_sources != []) { - asset_sources = _sources - } - if (_renaming_sources != []) { - assert(defined(invoker.renaming_destinations)) - _source_count = 0 - foreach(_, _renaming_sources) { - _source_count += 1 + sources = _renaming_sources + _sources + if (_renaming_sources != []) { + outputs = invoker.renaming_destinations } - _dest_count = 0 - foreach(_, invoker.renaming_destinations) { - _dest_count += 1 - } - assert( - _source_count == _dest_count, - "android_assets() renaming_sources.length != renaming_destinations.length") - asset_renaming_sources = _renaming_sources - asset_renaming_destinations = invoker.renaming_destinations } } @@ -1646,8 +1647,7 @@ ignore_desugar_missing_deps = true } else { _rebased_build_config = rebase_path(_build_config, root_build_dir) - input_dex_filearg = - "@FileArg(${_rebased_build_config}:deps_info:all_dex_files)" + input_dex_filearg = "@FileArg(${_rebased_build_config}:all_dex_files)" } } } @@ -1737,16 +1737,14 @@ rebase_path(depfile, root_build_dir), "--output", rebase_path(invoker.output, root_build_dir), - "--dependencies-res-zips=@FileArg($_rebased_build_config:deps_info:dependency_zips)", - "--r-text-files=@FileArg($_rebased_build_config:deps_info:dependency_r_txt_files)", - "--proguard-configs=@FileArg($_rebased_build_config:deps_info:proguard_all_configs)", + "--dependencies-res-zips=@FileArg($_rebased_build_config:dependency_zips)", + "--r-text-files=@FileArg($_rebased_build_config:dependency_rtxt_files)", + "--proguard-configs=@FileArg($_rebased_build_config:proguard_all_configs)", ] if (_direct_deps_only) { - args += [ "--jars=@FileArg($_rebased_build_config:javac:classpath)" ] + args += [ "--jars=@FileArg($_rebased_build_config:classpath)" ] } else { - args += [ - "--jars=@FileArg($_rebased_build_config:deps_info:device_classpath)", - ] + args += [ "--jars=@FileArg($_rebased_build_config:device_classpath)" ] } if (defined(invoker.android_manifest)) { @@ -2043,7 +2041,8 @@ forward_variables_from(invoker, TESTONLY_AND_VISIBILITY + [ "deps" ]) _rebased_build_config = rebase_path(invoker.build_config, root_build_dir) - defines += [ "LOCALE_LIST=@FileArg($_rebased_build_config:deps_info:locales_java_list)" ] + defines += + [ "LOCALE_LIST=@FileArg($_rebased_build_config:locales_java_list)" ] } } } @@ -2336,7 +2335,7 @@ # to their .build_config.json file only when proguarding. if (_proguard_enabled) { _final_dex_path = "$_base_path.r8dex.jar" - } else if (!_is_bundle_module) { + } else { _final_dex_path = "$_base_path.mergeddex.jar" } } @@ -2373,7 +2372,7 @@ get_label_info(_shared_resources_allowlist_target, "target_gen_dir") _allowlist_target_name = get_label_info(_shared_resources_allowlist_target, "name") - _allowlist_r_txt_path = + _allowlist_rtxt_path_path = "${_allowlist_gen_dir}/${_allowlist_target_name}" + "__compile_resources_R.txt" _allowlist_deps = @@ -2452,7 +2451,7 @@ if (defined(invoker.post_process_package_resources_script)) { post_process_script = invoker.post_process_package_resources_script } - r_text_out_path = _compile_resources_rtxt_out + rtxt_out_path = _compile_resources_rtxt_out emit_ids_out_path = _compile_resources_emit_ids_out size_info_path = _res_size_info_path proguard_file = _generated_proguard_config @@ -2471,8 +2470,7 @@ # So that test resources can references under_test resources via # @type/name syntax. r_java_root_package_name = "test" - arsc_package_name = - "@FileArg($_rebased_build_config:deps_info:arsc_package_name)" + arsc_package_name = "@FileArg($_rebased_build_config:arsc_package_name)" # Passing in the --emit-ids mapping will cause aapt2 to assign resources # IDs that do not conflict with those from apk_under_test. @@ -2505,7 +2503,7 @@ if (defined(_shared_resources_allowlist_target)) { # Used to ensure that the WebView resources are properly shared # (i.e. are non-final and with package ID 0). - shared_resources_allowlist = _allowlist_r_txt_path + shared_resources_allowlist = _allowlist_rtxt_path_path deps += [ _allowlist_deps ] } } @@ -2551,7 +2549,7 @@ if (_short_resource_paths) { resources_path_map_out_path = _resources_path_map_out_path } - r_text_path = _compile_resources_rtxt_out + rtxt_path = _compile_resources_rtxt_out if (_is_bundle_module) { proto_input_path = _proto_resources_path optimized_proto_output = _optimized_proto_resources_path @@ -2796,7 +2794,7 @@ } else { type = "android_apk" } - r_text_path = _compile_resources_rtxt_out + rtxt_path = _compile_resources_rtxt_out main_target_name = _template_name supports_android = true requires_android = true @@ -2853,9 +2851,9 @@ deps += _sanitizer_deps loadable_modules = _loadable_modules + _sanitizer_loadable_modules - if (defined(_allowlist_r_txt_path) && _is_bundle_module) { + if (defined(_allowlist_rtxt_path_path) && _is_bundle_module) { # Used to write the file path to the target's .build_config.json only. - base_allowlist_rtxt_path = _allowlist_r_txt_path + base_allowlist_rtxt_path = _allowlist_rtxt_path_path } } @@ -2909,13 +2907,13 @@ # Enable dex merging only when min_sdk_version is >= what the library # .dex files were created with. input_dex_filearg = - "@FileArg(${_rebased_build_config}:deps_info:all_dex_files)" + "@FileArg(${_rebased_build_config}:all_dex_files)" # Pure dex-merge. enable_desugar = false } else { input_classes_filearg = - "@FileArg($_rebased_build_config:deps_info:device_classpath)" + "@FileArg($_rebased_build_config:device_classpath)" } } @@ -2987,10 +2985,12 @@ name = "${invoker.name}.apk" build_config = _build_config res_size_info_path = _res_size_info_path + unprocessed_jar_path = "$target_out_dir/$_template_name.javac.jar" deps = [ ":$_build_config_target", ":$_compile_resources_target", ":$_java_target_name", + ":${_template_name}__compile_java", ] if (defined(invoker.asset_deps)) { deps += invoker.asset_deps @@ -3107,7 +3107,7 @@ args = [ "--apk-path=$_rebased_incremental_apk_path", "--output-path=$_rebased_incremental_install_json_path", - "--dex-file=@FileArg($_rebased_build_config:deps_info:all_dex_files)", + "--dex-file=@FileArg($_rebased_build_config:all_dex_files)", ] if (_proguard_enabled) { args += [ "--show-proguard-warning" ] @@ -3607,18 +3607,18 @@ "--script-output-path", rebase_path(_generated_script, root_build_dir), "--package-name", - "@FileArg($_rebased_apk_build_config:deps_info:package_name)", + "@FileArg($_rebased_apk_build_config:package_name)", ] deps += [ "${_apk_target}$build_config_target_suffix" ] if (_incremental_apk) { args += [ "--test-apk-incremental-install-json", - "@FileArg($_rebased_apk_build_config:deps_info:incremental_install_json_path)", + "@FileArg($_rebased_apk_build_config:incremental_install_json_path)", ] } else { args += [ "--test-apk", - "@FileArg($_rebased_apk_build_config:deps_info:apk_path)", + "@FileArg($_rebased_apk_build_config:apk_path)", ] } if (defined(invoker.proguard_mapping_path) && !_incremental_apk) { @@ -3636,7 +3636,7 @@ ".build_config.json" _rebased_apk_under_test_build_config = rebase_path(_apk_under_test_build_config, root_build_dir) - _apk_under_test = "@FileArg($_rebased_apk_under_test_build_config:deps_info:incremental_apk_path)" + _apk_under_test = "@FileArg($_rebased_apk_under_test_build_config:incremental_apk_path)" } else { deps += [ ":${_install_artifacts_target_name}" ] _rebased_install_artifacts_json = @@ -3657,7 +3657,7 @@ _rebased_build_config = rebase_path(_build_config, root_build_dir) args += [ "--additional-apk", - "@FileArg($_rebased_build_config:deps_info:apk_path)", + "@FileArg($_rebased_build_config:apk_path)", ] } deps += invoker.additional_apks @@ -4373,7 +4373,7 @@ sources = rebase_path(_scanned_files.resources, "", _output_path) if (_scanned_files.has_r_text_file) { - r_text_file = "${_output_path}/R.txt" + rtxt_file = "${_output_path}/R.txt" } } } else if (defined(invoker.strip_drawables)) { @@ -4685,12 +4685,10 @@ # Make build config, which is required for synchronized proguarding. _module_java_targets = [] - _module_build_configs = [] _module_targets = [] foreach(_module, _modules) { _module_targets += [ _module.module_target ] _module_java_targets += [ "${_module.module_target}__java" ] - _module_build_configs += [ _module.build_config ] } # Used to expose the module Java targets of the bundle. @@ -4725,7 +4723,6 @@ possible_config_deps = _module_targets + [ _android_sdk_dep ] build_config = _build_config proguard_enabled = _proguard_enabled - module_build_configs = _module_build_configs modules = _modules if (_proguard_enabled) { @@ -4757,11 +4754,11 @@ "--script", rebase_path(_rewriter_path, root_build_dir), "--classpath", - "@FileArg($_rebased_build_config:android:sdk_jars)", + "@FileArg($_rebased_build_config:sdk_jars)", "--input-jars", - "@FileArg($_rebased_build_config:deps_info:device_classpath)", + "@FileArg($_rebased_build_config:device_classpath)", "--output-jars", - "@FileArg($_rebased_build_config:deps_info:trace_event_rewritten_device_classpath)", + "@FileArg($_rebased_build_config:trace_event_rewritten_device_classpath)", ] deps = [ _build_config_target, @@ -4905,7 +4902,7 @@ _unused_resources_target = "${_base_target_name}__unused_resources" _unused_resources_config = "${_base_target_gen_dir}/${_base_target_name}_unused_resources.config" - _unused_resources_r_txt_out = + _unused_resources_rtxt_path_out = "${_base_target_gen_dir}/${_base_target_name}_unused_resources.R.txt" unused_resources(_unused_resources_target) { deps = _all_module_unused_resources_deps @@ -4915,7 +4912,7 @@ proguard_mapping_path = _proguard_mapping_path } output_config = _unused_resources_config - output_r_txt = _unused_resources_r_txt_out + output_rtxt_path = _unused_resources_rtxt_path_out } _unused_resources_final_path = "${_bundle_path}.unused_resources" _copy_unused_resources_target = @@ -4984,18 +4981,19 @@ } if (_enable_language_splits) { - args += [ "--base-allowlist-rtxt-path=@FileArg($_rebased_base_module_build_config:deps_info:base_allowlist_rtxt_path)" ] + args += [ "--base-allowlist-rtxt-path=@FileArg($_rebased_base_module_build_config:base_allowlist_rtxt_path)" ] if (_strip_unused_resources) { # Use the stripped out rtxt file to set resources that are pinned to # the default language split. - _rebased_unused_resources_r_txt_out = - rebase_path(_unused_resources_r_txt_out, root_build_dir) - inputs += [ _unused_resources_r_txt_out ] + _rebased_unused_resources_rtxt_path_out = + rebase_path(_unused_resources_rtxt_path_out, root_build_dir) + inputs += [ _unused_resources_rtxt_path_out ] deps += [ ":$_unused_resources_target" ] - args += - [ "--base-module-rtxt-path=$_rebased_unused_resources_r_txt_out" ] + args += [ + "--base-module-rtxt-path=$_rebased_unused_resources_rtxt_path_out", + ] } else { - args += [ "--base-module-rtxt-path=@FileArg($_rebased_base_module_build_config:deps_info:r_text_path)" ] + args += [ "--base-module-rtxt-path=@FileArg($_rebased_base_module_build_config:rtxt_path)" ] } } if (defined(invoker.validate_services) && invoker.validate_services) { @@ -5007,11 +5005,10 @@ rebase_path(_module.build_config, root_build_dir) args += [ "--uncompressed-assets=@FileArg(" + - "$_rebased_build_config:deps_info:uncompressed_assets)", - "--rtxt-in-paths=@FileArg(" + - "$_rebased_build_config:deps_info:r_text_path)", + "$_rebased_build_config:uncompressed_assets)", + "--rtxt-in-paths=@FileArg(" + "$_rebased_build_config:rtxt_path)", "--pathmap-in-paths=@FileArg(" + - "$_rebased_build_config:deps_info:module_pathmap_path)", + "$_rebased_build_config:module_pathmap_path)", "--module-name=" + _module.name, ] } @@ -5077,7 +5074,7 @@ args = [ "--script-output-path", rebase_path(_bundle_wrapper_script_path, root_build_dir), - "--package-name=@FileArg($_rebased_base_module_build_config:deps_info:package_name)", + "--package-name=@FileArg($_rebased_base_module_build_config:package_name)", "--aapt2", rebase_path(_android_aapt2_path, root_build_dir), "--bundle-path",
diff --git a/build/config/android/system_image.gni b/build/config/android/system_image.gni index 56a2355..88882d258 100644 --- a/build/config/android/system_image.gni +++ b/build/config/android/system_image.gni
@@ -63,7 +63,7 @@ if (defined(_apk_or_bundle_target)) { deps = [ "$_apk_or_bundle_target$build_config_target_suffix" ] inputs = [ _build_config ] - args += [ "--use-permissions-from=@FileArg($_rebased_build_config:deps_info:lint_android_manifest)" ] + args += [ "--use-permissions-from=@FileArg($_rebased_build_config:android_manifest)" ] } } @@ -165,9 +165,9 @@ } else { deps += [ "${_apk_or_bundle_target}$build_config_target_suffix" ] inputs += [ _build_config ] - _package_name = "@FileArg($_rebased_build_config:deps_info:package_name)" - _version_code = "@FileArg($_rebased_build_config:deps_info:version_code)" - _version_name = "@FileArg($_rebased_build_config:deps_info:version_name)" + _package_name = "@FileArg($_rebased_build_config:package_name)" + _version_code = "@FileArg($_rebased_build_config:version_code)" + _version_name = "@FileArg($_rebased_build_config:version_name)" # TODO(crbug.com/40253411): Make static_library_version mandatory. # Pass this through to ensure that the version code in the build config is
diff --git a/build/config/siso/android.star b/build/config/siso/android.star index 8b258bf..51e4dd5 100644 --- a/build/config/siso/android.star +++ b/build/config/siso/android.star
@@ -451,7 +451,7 @@ trace_event_adder_json = json.decode( str(ctx.fs.read(ctx.fs.canonpath("gen/build/android/bytecode/trace_event_adder.build_config.json"))), ) - for path in trace_event_adder_json.get("deps_info", {}).get("host_classpath", []): + for path in trace_event_adder_json.get("host_classpath", []): inputs.append(ctx.fs.canonpath(path)) ctx.actions.fix( @@ -560,19 +560,26 @@ inputs = cmd.inputs + inputs, ) -def __deps_configs(ctx, f, seen, inputs): - if f in seen: +def __deps_configs(ctx, build_config_path, seen, inputs): + if build_config_path in seen: return - seen[f] = True - inputs.append(f) - v = json.decode(str(ctx.fs.read(f))) - for f in v["deps_info"]["deps_configs"]: - f = ctx.fs.canonpath(f) - __deps_configs(ctx, f, seen, inputs) - if "public_deps_configs" in v["deps_info"]: - for f in v["deps_info"]["public_deps_configs"]: - f = ctx.fs.canonpath(f) - __deps_configs(ctx, f, seen, inputs) + seen[build_config_path] = True + params_path = build_config_path.replace(".build_config.json", ".params.json") + inputs.append(build_config_path) + inputs.append(params_path) + build_config_data = json.decode(str(ctx.fs.read(build_config_path))) + params_data = None + + # Entries can be in either .build_config.json or in .params.json. + for configs_key in ["deps_configs", "public_deps_configs"]: + sub_configs = build_config_data.get(configs_key) + if not sub_configs: + if not params_data: + params_data = json.decode(str(ctx.fs.read(params_path))) + sub_configs = params_data.get(configs_key, []) + + for f in sub_configs: + __deps_configs(ctx, ctx.fs.canonpath(f), seen, inputs) def __android_write_build_config_handler(ctx, cmd): # Script: @@ -580,35 +587,31 @@ # GN Config: # https://crsrc.org/c/build/config/android/internal_rules.gni;l=122;drc=99e4f79301e108ea3d27ec84320f430490382587 # Sample args: - # --type=java_library # --depfile gen/third_party/android_deps/org_jetbrains_kotlinx_kotlinx_metadata_jvm_java__build_config_crbug_908819.d - # --deps-configs=\[\"gen/third_party/kotlin_stdlib/kotlin_stdlib_java.build_config.json\"\] - # --public-deps-configs=\[\] - # --build-config gen/third_party/android_deps/org_jetbrains_kotlinx_kotlinx_metadata_jvm_java.build_config.json - # --gn-target //third_party/android_deps:org_jetbrains_kotlinx_kotlinx_metadata_jvm_java - # --non-chromium-code - # --host-jar-path lib.java/third_party/android_deps/org_jetbrains_kotlinx_kotlinx_metadata_jvm.jar - # --unprocessed-jar-path ../../third_party/android_deps/libs/org_jetbrains_kotlinx_kotlinx_metadata_jvm/kotlinx-metadata-jvm-0.1.0.jar - # --interface-jar-path obj/third_party/android_deps/org_jetbrains_kotlinx_kotlinx_metadata_jvm.ijar.jar - # --is-prebuilt - # --bundled-srcjars=\[\] + # --params gen/third_party/android_deps/org_jetbrains_kotlinx_kotlinx_metadata_jvm_java.params.json inputs = [] seen = {} for i, arg in enumerate(cmd.args): - if arg in ["--shared-libraries-runtime-deps", "--secondary-abi-shared-libraries-runtime-deps"]: - inputs.append(ctx.fs.canonpath(cmd.args[i + 1])) - continue - if arg == "--tested-apk-config": - f = ctx.fs.canonpath(cmd.args[i + 1]) - __deps_configs(ctx, f, seen, inputs) - continue - for k in ["--deps-configs=", "--public-deps-configs=", "--annotation-processor-configs="]: - if arg.startswith(k): - arg = arg.removeprefix(k) - v = json.decode(arg) - for f in v: - f = ctx.fs.canonpath(f) - __deps_configs(ctx, f, seen, inputs) + if arg == "--params": + params_path = ctx.fs.canonpath(cmd.args[i + 1]) + output_build_config_path = params_path.replace(".params.json", ".build_config.json") + v = json.decode(str(ctx.fs.read(params_path))) + path = v.get("shared_libraries_runtime_deps_file") + if path: + inputs.append(ctx.fs.canonpath(path)) + path = v.get("secondary_abi_shared_libraries_runtime_deps_file") + if path: + inputs.append(ctx.fs.canonpath(path)) + for k in ["apk_under_test_config", "base_module_config", "parent_module_config", "suffix_apk_assets_used_by_config"]: + path = v.get(k) + if path: + path = ctx.fs.canonpath(path) + if path != output_build_config_path: + __deps_configs(ctx, path, seen, inputs) + for k in ["deps_configs", "public_deps_configs", "processor_configs", "module_configs"]: + for path in v.get(k, []): + path = ctx.fs.canonpath(path) + __deps_configs(ctx, path, seen, inputs) ctx.actions.fix(inputs = cmd.inputs + inputs)
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn index dae1a63..97f843f 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn
@@ -307,11 +307,6 @@ # Needed to use the master_preferences functions "//chrome/installer/util:with_no_strings", "//content/public/app", - - # To handle stack overflow crashes. - # TODO(crbug.com/42310237): Remove this once pthread_create dependency - # gets added to a more appropriate place. - "//third_party/crashpad/crashpad/client:pthread_create", ] public_deps = [
diff --git a/chrome/VERSION b/chrome/VERSION index 142a8f12..8414778 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=137 MINOR=0 -BUILD=7150 +BUILD=7151 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index 7d60fecc..cae7cbbb 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -83,7 +83,6 @@ forward_variables_from(invoker, [ "assert_no_deps", - "add_view_trace_events", "expected_android_manifest", "is_64_bit_browser", "is_monochrome", @@ -94,6 +93,7 @@ "resource_ids_provider_dep", ]) target_type = "android_app_bundle_module" + add_view_trace_events = true srcjar_deps = [] foreach(_module_desc, _module_descs) { @@ -113,7 +113,6 @@ chrome_bundle(target_name) { forward_variables_from(invoker, [ - "add_view_trace_events", "bundle_name", "include_32_bit_webview", "include_64_bit_webview", @@ -129,6 +128,7 @@ manifest_package = chrome_public_manifest_package module_descs = _module_descs chrome_deps = [ ":delegate_public_impl_java" ] + add_view_trace_events = true if (target_name == _android_lint_target_name) { enable_lint = true @@ -2730,7 +2730,6 @@ chrome_public_bundle(_main_monochrome_public_bundle_target) { is_monochrome = true bundle_name = "MonochromePublic" - add_view_trace_events = true if (android_64bit_target_cpu) { is_64_bit_browser = false @@ -2748,7 +2747,6 @@ is_trichrome = true bundle_name = "TrichromeChrome" static_library_provider = ":$_main_trichrome_library_apk_target" - add_view_trace_events = true if (android_64bit_target_cpu) { is_64_bit_browser = false include_64_bit_webview = true
diff --git a/chrome/android/expectations/monochrome_64_32_public_bundle.proguard_flags.expected b/chrome/android/expectations/monochrome_64_32_public_bundle.proguard_flags.expected index 00748ac..0fb98ad 100644 --- a/chrome/android/expectations/monochrome_64_32_public_bundle.proguard_flags.expected +++ b/chrome/android/expectations/monochrome_64_32_public_bundle.proguard_flags.expected
@@ -1138,7 +1138,7 @@ boolean getRECOVER_STACK_TRACES() return false; } -# File: ../../third_party/android_deps/autorolled/cipd/libs/org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm/kotlinx-coroutines-core-jvm-1.7.3.jar:META-INF/com.android.tools/r8/coroutines.pro +# File: ../../third_party/android_deps/autorolled/cipd/libs/org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm/kotlinx-coroutines-core-jvm-1.8.1.jar:META-INF/com.android.tools/r8/coroutines.pro # When editing this file, update the following files as well: # - META-INF/proguard/coroutines.pro # - META-INF/com.android.tools/proguard/coroutines.pro @@ -1240,4 +1240,4 @@ } # File: //build/android/gyp/proguard.py (generated rules) --repackageclasses '' +-repackageclasses '' \ No newline at end of file
diff --git a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java index 395d6c7..82e66e9 100644 --- a/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java +++ b/chrome/android/features/keyboard_accessory/junit/src/org/chromium/chrome/browser/keyboard_accessory/ManualFillingControllerTest.java
@@ -82,6 +82,7 @@ import org.chromium.chrome.browser.keyboard_accessory.data.UserInfoField; import org.chromium.chrome.browser.keyboard_accessory.sheet_component.AccessorySheetCoordinator; import org.chromium.chrome.browser.keyboard_accessory.sheet_tabs.AccessorySheetTabCoordinator; +import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher; import org.chromium.chrome.browser.password_manager.ConfirmationDialogHelper; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.ProfileJni; @@ -136,6 +137,7 @@ @Mock private InsetObserver mInsetObserver; @Mock private BackPressManager mMockBackPressManager; @Mock private EdgeToEdgeController mMockEdgeToEdgeController; + @Mock private MultiWindowModeStateDispatcher mMockMultiWindowModeStateDispatcher; private final ManualFillingCoordinator mController = new ManualFillingCoordinator(); private final ManualFillingMediator mMediator = mController.getMediatorForTesting(); @@ -325,7 +327,7 @@ when(mMockActivity.getTabModelSelector()).thenReturn(mMockTabModelSelector); when(mMockActivity.getActivityTabProvider()).thenReturn(mActivityTabProvider); BrowserControlsManager browserControlsManager = - new BrowserControlsManager(mMockActivity, 0); + new BrowserControlsManager(mMockActivity, 0, mMockMultiWindowModeStateDispatcher); when(mMockActivity.getBrowserControlsManager()).thenReturn(browserControlsManager); when(mMockActivity.getFullscreenManager()).thenReturn(mMockFullscreenManager); doNothing().when(mMockFullscreenManager).addObserver(mFullscreenObserverCaptor.capture());
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediator.java index 4d916b7..bcc1182 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediator.java
@@ -14,6 +14,7 @@ import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter; import org.chromium.chrome.browser.tasks.tab_management.TabGroupFaviconCluster.ClusterData; import org.chromium.chrome.browser.tasks.tab_management.TabGroupListBottomSheetCoordinator.TabMovedCallback; +import org.chromium.chrome.browser.tasks.tab_management.TabGroupListBottomSheetMediator.TabGroupListBottomSheetRowMergeOperation; import org.chromium.chrome.browser.tasks.tab_management.TabGroupRowView.TabGroupRowViewTitleData; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.url.GURL; @@ -26,7 +27,7 @@ * TabGroupModelFilter} as its primary source of truth. */ @NullMarked -class LocalTabGroupListBottomSheetRowMediator { +class LocalTabGroupListBottomSheetRowMediator implements TabGroupListBottomSheetRowMergeOperation { private final Token mGroupId; private final TabGroupModelFilter mTabGroupModelFilter; private final @Nullable TabMovedCallback mTabMovedCallback; @@ -98,15 +99,7 @@ } @TabId int destTabId = mTabGroupModelFilter.getGroupLastShownTabId(mGroupId); - Tab destTab = mTabGroupModelFilter.getTabModel().getTabById(destTabId); - if (destTab == null) { - return; - } - - mTabGroupModelFilter.mergeListOfTabsToGroup(tabs, destTab, true); - if (mTabMovedCallback != null) { - mTabMovedCallback.onTabMoved(); - } + mergeTabsToDest(tabs, destTabId, mTabGroupModelFilter, mTabMovedCallback); } private boolean areTabsAlreadyInGroup(List<Tab> tabsToBeMoved) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediatorUnitTest.java index 9fd0029..85119f147 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediatorUnitTest.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediatorUnitTest.java
@@ -24,6 +24,7 @@ import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter; import org.chromium.chrome.browser.tabmodel.TabModel; +import org.chromium.chrome.browser.tabmodel.TabUngrouper; import org.chromium.chrome.browser.tasks.tab_management.TabGroupListBottomSheetCoordinator.TabMovedCallback; import org.chromium.chrome.browser.tasks.tab_management.TabGroupRowView.TabGroupRowViewTitleData; import org.chromium.ui.modelutil.PropertyModel; @@ -48,6 +49,7 @@ @Mock private Runnable mOnClickRunnable; @Mock private TabMovedCallback mTabMovedCallback; @Mock private TabModel mTabModel; + @Mock private TabUngrouper mTabUngrouper; @Mock private Tab mTab1; @Mock private Tab mTab2; @@ -62,6 +64,7 @@ mTabs.add(mTab1); when(mTabGroupModelFilter.getTabModel()).thenReturn(mTabModel); + when(mTabGroupModelFilter.getTabUngrouper()).thenReturn(mTabUngrouper); when(mTabGroupModelFilter.getTabsInGroup(mGroupId)).thenReturn(mTabs); when(mTabGroupModelFilter.getRootIdFromTabGroupId(mGroupId)).thenReturn(TEST_ROOT_ID); when(mTabGroupModelFilter.tabGroupExists(mGroupId)).thenReturn(true);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetMediator.java index 380945a..394c9d9 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetMediator.java
@@ -11,6 +11,7 @@ import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.TabId; import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter; import org.chromium.chrome.browser.tasks.tab_management.TabGroupListBottomSheetCoordinator.RowType; import org.chromium.chrome.browser.tasks.tab_management.TabGroupListBottomSheetCoordinator.TabGroupCreationCallback; @@ -38,6 +39,48 @@ */ @NullMarked public class TabGroupListBottomSheetMediator { + /** Represents a operation to merge tabs to a destination. */ + interface TabGroupListBottomSheetRowMergeOperation { + /** + * Merges tabs to a destination. + * + * @param tabs The tabs to be added to a tab group. + * @param destTabId The tab id of the destination tab group. + * @param tabGroupModelFilter Used to read current tab groups. + * @param tabMovedCallback Used to follow up on a tab being moved groups or ungrouped. + */ + default void mergeTabsToDest( + List<Tab> tabs, + @TabId int destTabId, + TabGroupModelFilter tabGroupModelFilter, + @Nullable TabMovedCallback tabMovedCallback) { + Tab destTab = tabGroupModelFilter.getTabModel().getTabById(destTabId); + if (destTab == null) { + return; + } + + // TODO(crbug.com/413724490): Replace the ungroup operation with a {@link + // TabGroupModelFilter}-based solution. + // + // Right now, it is only possible to move tabs in 2 ways: + // - Moving 1 tab within a group to another via the ctx menu 'Move to group' option + // - Selecting a whole group via the Tab List Editor + // In the first case, we want to ungroup, since the tab group card needs to be updated. + // In the second case, we do not want to since the card disappears. This means the + // 'undo' option will fully be able to undo this action. + if (tabs.size() == 1) { + tabGroupModelFilter + .getTabUngrouper() + .ungroupTabs(tabs, /* trailing= */ false, /* allowDialog= */ false); + } + + tabGroupModelFilter.mergeListOfTabsToGroup(tabs, destTab, true); + if (tabMovedCallback != null) { + tabMovedCallback.onTabMoved(); + } + } + } + private final BottomSheetController mBottomSheetController; private final TabGroupListBottomSheetCoordinatorDelegate mDelegate; private final ModelList mModelList;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetRowMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetRowMediator.java index facb2c2..20be193d 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetRowMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetRowMediator.java
@@ -12,6 +12,7 @@ import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter; import org.chromium.chrome.browser.tasks.tab_management.TabGroupFaviconCluster.ClusterData; import org.chromium.chrome.browser.tasks.tab_management.TabGroupListBottomSheetCoordinator.TabMovedCallback; +import org.chromium.chrome.browser.tasks.tab_management.TabGroupListBottomSheetMediator.TabGroupListBottomSheetRowMergeOperation; import org.chromium.chrome.browser.tasks.tab_management.TabGroupRowView.TabGroupRowViewTitleData; import org.chromium.chrome.browser.tasks.tab_management.TabGroupTimeAgo.TimestampEvent; import org.chromium.components.tab_group_sync.LocalTabGroupId; @@ -29,7 +30,7 @@ * TabGroupSyncService} as its primary source of truth. */ @NullMarked -class TabGroupListBottomSheetRowMediator { +class TabGroupListBottomSheetRowMediator implements TabGroupListBottomSheetRowMergeOperation { private final SavedTabGroup mSavedTabGroup; private final TabGroupModelFilter mTabGroupModelFilter; private final @Nullable TabGroupSyncService mTabGroupSyncService; @@ -116,21 +117,7 @@ return; } - Tab destTab = mTabGroupModelFilter.getTabModel().getTabById(localId); - if (destTab == null) { - return; - } - - if (tabs.size() == 1) { - mTabGroupModelFilter - .getTabUngrouper() - .ungroupTabs(tabs, /* trailing= */ false, /* allowDialog= */ false); - } - - mTabGroupModelFilter.mergeListOfTabsToGroup(tabs, destTab, true); - if (mTabMovedCallback != null) { - mTabMovedCallback.onTabMoved(); - } + mergeTabsToDest(tabs, localId, mTabGroupModelFilter, mTabMovedCallback); } private boolean areTabsAlreadyInGroup(List<Tab> tabsToBeMoved) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java index 1df1447c..16dedd9 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java
@@ -26,6 +26,7 @@ import org.chromium.ui.modelutil.MVCListAdapter.ModelList; import org.chromium.ui.widget.AnchoredPopupWindow; import org.chromium.ui.widget.RectProvider; +import org.chromium.ui.widget.ViewRectProvider; /** * A coordinator for the menu on tab group cards in GTS. It is responsible for creating a list of @@ -73,7 +74,12 @@ if (tabGroupId == null) return; mShouldShowIcons = false; - createAndShowMenu(view, tabGroupId, (Activity) view.getContext()); + createAndShowMenu( + new ViewRectProvider(view), + tabGroupId, + /* animStyle= */ R.style.EndIconMenuAnim, + /* verticalOverlapAnchor= */ true, + (Activity) view.getContext()); }; } @@ -89,16 +95,30 @@ createAndShowMenu( anchorViewRectProvider, tabGroupId, - /* horizontalOverlapAnchor= */ true, - /* verticalOverlapAnchor= */ false, /* animStyle= */ ResourcesCompat.ID_NULL, - AnchoredPopupWindow.HorizontalOrientation.LAYOUT_DIRECTION, + /* verticalOverlapAnchor= */ false, mActivity); } + private void createAndShowMenu( + RectProvider anchorRectProvider, + @NonNull Token tabGroupId, + int animStyle, + boolean verticalOverlapAnchor, + Activity activity) { + createAndShowMenu( + anchorRectProvider, + tabGroupId, + /* horizontalOverlapAnchor= */ true, + /* verticalOverlapAnchor= */ verticalOverlapAnchor, + /* animStyle= */ animStyle, + AnchoredPopupWindow.HorizontalOrientation.MAX_AVAILABLE_SPACE, + activity, + /* isIncognito= */ false); + } + @Override protected void buildMenuActionItems(ModelList itemList, Token tabGroupId) { - boolean isIncognito = mTabModelSupplier.get().isIncognitoBranded(); @Nullable String collaborationId = getCollaborationIdOrNull(tabGroupId); boolean hasCollaborationData = TabShareUtils.isCollaborationIdValid(collaborationId) @@ -110,8 +130,8 @@ mShouldShowIcons ? R.drawable.ic_tab_close_24dp : Resources.ID_NULL, /* iconTintColorStateList= */ Resources.ID_NULL, R.style.TextAppearance_TextLarge_Primary_Baseline_Light, - isIncognito, - true)); + /* isIncognito= */ false, + /* enabled= */ true)); itemList.add( BrowserUiListMenuUtils.buildMenuListItemWithIncognitoBranding( R.string.rename_tab_group_menu_item, @@ -119,8 +139,10 @@ mShouldShowIcons ? R.drawable.ic_edit_24dp : Resources.ID_NULL, /* iconTintColorStateList= */ Resources.ID_NULL, R.style.TextAppearance_TextLarge_Primary_Baseline_Light, - isIncognito, - true)); + /* isIncognito= */ false, + /* enabled= */ true)); + + boolean isIncognito = mTabModelSupplier.get().isIncognitoBranded(); if (!hasCollaborationData) { itemList.add( BrowserUiListMenuUtils.buildMenuListItemWithIncognitoBranding( @@ -129,8 +151,8 @@ mShouldShowIcons ? R.drawable.ic_ungroup_tabs_24dp : Resources.ID_NULL, /* iconTintColorStateList= */ Resources.ID_NULL, R.style.TextAppearance_TextLarge_Primary_Baseline_Light, - isIncognito, - true)); + /* isIncognito= */ false, + /* enabled= */ true)); if (!isIncognito && mCollaborationService.getServiceStatus().isAllowedToCreate()) { itemList.add(buildShareMenuItem(R.string.share_tab_group_menu_item)); } @@ -150,8 +172,8 @@ : Resources.ID_NULL, /* iconTintColorStateList= */ Resources.ID_NULL, R.style.TextAppearance_TextLarge_Primary_Baseline_Light, - isIncognito, - true)); + /* isIncognito= */ false, + /* enabled= */ true)); } } @@ -174,7 +196,9 @@ BrowserUiListMenuUtils.buildMenuListItemWithIncognitoBranding( R.string.leave_tab_group_menu_item, R.id.leave_group, - /* startIconId= */ Resources.ID_NULL, + mShouldShowIcons + ? R.drawable.material_ic_delete_24dp + : Resources.ID_NULL, mShouldShowIcons ? R.drawable.material_ic_delete_24dp : Resources.ID_NULL,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java index 3d4ff0e..fc296a1a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java
@@ -41,6 +41,8 @@ import org.chromium.chrome.browser.browserservices.intents.SessionHolder; import org.chromium.chrome.browser.browserservices.intents.WebappConstants; import org.chromium.chrome.browser.customtabs.CustomTabsConnection; +import org.chromium.chrome.browser.customtabs.EphemeralCustomTabIntentDataProvider; +import org.chromium.chrome.browser.customtabs.IncognitoCustomTabIntentDataProvider; import org.chromium.chrome.browser.document.ChromeLauncherActivity; import org.chromium.chrome.browser.externalnav.IntentWithRequestMetadataHandler; import org.chromium.chrome.browser.externalnav.IntentWithRequestMetadataHandler.RequestMetadata; @@ -269,8 +271,7 @@ public static final String EXTRA_TAB_GROUP_METADATA = "org.chromium.chrome.browser.tab_group_metadata"; - public static final String EXTRA_SKIP_PRECONNECT = - "org.chromium.chrome.browser.skip_preconnect"; + public static final String EXTRA_CCT_EARLY_NAV = "org.chromium.chrome.browser.cct_early_nav"; /** The package name for the Google Search App. */ public static final String PACKAGE_GSA = GSAUtils.GSA_PACKAGE_NAME; @@ -1620,6 +1621,17 @@ || IntentUtils.safeGetBoolean(extras, EXTRA_ENABLE_EPHEMERAL_BROWSING, false); } + public static boolean willLaunchIncognitoCustomTab(Intent intent) { + if (IncognitoCustomTabIntentDataProvider.isValidIncognitoIntent( + intent, /* recordMetrics= */ false)) { + return true; + } + if (EphemeralCustomTabIntentDataProvider.isValidEphemeralTabIntent(intent)) { + return true; + } + return false; + } + @NativeMethods interface Natives { boolean isCorsSafelistedHeader(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java index 8c1f318..2d985f3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java
@@ -40,7 +40,6 @@ import org.chromium.chrome.browser.customtabs.CustomTabActivity; import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider; import org.chromium.chrome.browser.customtabs.CustomTabsConnection; -import org.chromium.chrome.browser.customtabs.IncognitoCustomTabIntentDataProvider; import org.chromium.chrome.browser.customtabs.content.WebAppLaunchHandler; import org.chromium.chrome.browser.document.ChromeLauncherActivity; import org.chromium.chrome.browser.firstrun.FirstRunFlowSequencer; @@ -426,6 +425,8 @@ if (handled) return true; } + // Should not be set by external apps, remove if present. + mIntent.removeExtra(IntentHandler.EXTRA_CCT_EARLY_NAV); boolean startedNavigationEarly = maybeStartNavigation(); RecordHistogram.recordBooleanHistogram( "CustomTabs.Startup.StartedNavigationEarly", startedNavigationEarly); @@ -444,8 +445,6 @@ intent.putExtra(IntentHandler.EXTRA_CALLING_ACTIVITY_PACKAGE, packageName); } - if (startedNavigationEarly) intent.putExtra(IntentHandler.EXTRA_SKIP_PRECONNECT, true); - // Pass the package name obtained via identity sharing API separately from the one // obtained via startActivityForResult. boolean identityShared = packageNameIdentitySharing != null; @@ -473,10 +472,7 @@ private boolean maybeStartNavigation() { if (!WarmupManager.getInstance().isCctPrewarmTabFeatureEnabled(false)) return false; if (!ChromeFeatureList.isEnabled(ChromeFeatureList.CCT_EARLY_NAV)) return false; - if (IncognitoCustomTabIntentDataProvider.isValidIncognitoIntent( - mIntent, /* recordMetrics= */ false)) { - return false; - } + if (IntentHandler.willLaunchIncognitoCustomTab(mIntent)) return false; if (!ProfileManager.isInitialized()) return false; if (clearTopIntentsForCustomTabsEnabled(mIntent) && SessionDataHolder.getInstance()
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java index ef47b24..304b3fa 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -577,7 +577,9 @@ // BrowserControlsManager is ready immediately. mBrowserControlsManagerSupplier.set( new BrowserControlsManager( - this, BrowserControlsStateProvider.ControlsPosition.TOP)); + this, + BrowserControlsStateProvider.ControlsPosition.TOP, + getMultiWindowModeStateDispatcher())); } /** Subclasses must create a {@link RootUiCoordinator}. */ @@ -2247,7 +2249,6 @@ MultiWindowUtils.getInstance().isInMultiWindowMode(this)); } } - super.onMultiWindowModeChanged(isInMultiWindowMode); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostView.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostView.java index 33f1300..dc25447 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostView.java
@@ -25,7 +25,6 @@ import org.chromium.chrome.browser.ui.theme.BrandedColorScheme; import org.chromium.components.browser_ui.styles.SemanticColorUtils; import org.chromium.ui.animation.ViewCurvedMotionAnimatorFactory; -import org.chromium.ui.base.LocalizationUtils; import org.chromium.ui.interpolators.Interpolators; import java.lang.annotation.ElementType; @@ -58,8 +57,8 @@ private NewBackgroundTabFakeTabSwitcherButton mFakeTabSwitcherButton; private ImageView mLinkIcon; private @AnimationType int mAnimationType; - private boolean mIsRtl; - private int mYOffset; + private boolean mIsTopToolbar; + private int mStatusBarHeight; /** Default constructor for inflation. */ public NewBackgroundTabAnimationHostView(Context context, AttributeSet attrs) { @@ -73,7 +72,6 @@ mLinkIcon = findViewById(R.id.new_tab_background_animation_link_icon); setLinkIconTint(SemanticColorUtils.getDefaultIconColor(getContext())); mFakeTabSwitcherButton = findViewById(R.id.new_background_tab_fake_tab_switcher_button); - mIsRtl = LocalizationUtils.isLayoutRtl(); } /** @@ -81,13 +79,11 @@ * * @param originX x-coordinate for the start point. * @param originY y-coordinate for the start point. - * @param statusBarHeight The status bar height (px), if needed for y-offset. */ - /* package */ AnimatorSet getAnimatorSet(float originX, float originY, int statusBarHeight) { - // TODO(crbug.com/40282469): Make animation compatible with bottom toolbar. + /* package */ AnimatorSet getAnimatorSet(float originX, float originY) { assert mAnimationType != AnimationType.UNINITIALIZED; int[] target = new int[2]; - mFakeTabSwitcherButton.getButtonLocation(target, mYOffset + statusBarHeight); + mFakeTabSwitcherButton.getButtonLocation(target, mStatusBarHeight); target[0] -= Math.round(mLinkIcon.getWidth() / 2f); target[1] -= Math.round(mLinkIcon.getHeight() / 2f); @@ -123,38 +119,45 @@ } /** - * Sets the {@link #mFakeTabSwitcherButton} into the correct status. + * Prepares the animation. * * @param tabSwitcherButton The real Tab Switcher Button. - * @param tabCount The tab count to display. - * @param backgroundColor The current color of the toolbar. * @param isNtp True if the current tab is the regular Ntp. * @param isIncognito True if the current tab is an incognito tab. - * @param yOffset y-offset to account for the status indicator (ex: no internet connection). + * @param isTopToolbar True if current tab has a top toolbar. + * @param backgroundColor The current color of the toolbar. + * @param tabCount The tab count to display. + * @param toolbarHeight Current height of the toolbar in the screen (absolute y-coordinate in + * the screen). + * @param statusBarHeight The status bar height to calculate the y-offset within the screen. * @param ntpToolbarTransitionPercentage To know if the search box is in the toolbar position. */ - /* package */ void updateFakeTabSwitcherButton( + /* package */ void setUpAnimation( ToggleTabStackButton tabSwitcherButton, - int tabCount, - @ColorInt int backgroundColor, boolean isNtp, boolean isIncognito, - int yOffset, + boolean isTopToolbar, + @ColorInt int backgroundColor, + int tabCount, + int toolbarHeight, + int statusBarHeight, float ntpToolbarTransitionPercentage) { - mYOffset = yOffset; + mStatusBarHeight = statusBarHeight; + mIsTopToolbar = isTopToolbar; mFakeTabSwitcherButton.setTabCount(tabCount, isIncognito); Rect tabSwitcherRect = new Rect(); boolean tabSwitcherButtonIsVisible = tabSwitcherButton.getGlobalVisibleRect(tabSwitcherRect); int horizontalMargin = tabSwitcherRect.left; - int verticalMargin = yOffset; + int verticalMargin = toolbarHeight - statusBarHeight; + Context context = getContext(); if (tabSwitcherButtonIsVisible || !isNtp) { mAnimationType = AnimationType.DEFAULT; @BrandedColorScheme int brandedColorScheme = - ThemeUtils.getBrandedColorScheme(getContext(), backgroundColor, isIncognito); + ThemeUtils.getBrandedColorScheme(context, backgroundColor, isIncognito); mFakeTabSwitcherButton.setBrandedColorScheme(brandedColorScheme); mFakeTabSwitcherButton.setButtonColor(backgroundColor); mFakeTabSwitcherButton.setNotificationIconStatus( @@ -165,8 +168,7 @@ mAnimationType = AnimationType.NTP_FULL_SCROLL; verticalMargin += Math.round( - getContext() - .getResources() + context.getResources() .getDimension(R.dimen.toolbar_height_no_shadow)); } else { mAnimationType = AnimationType.NTP_PARTIAL_SCROLL; @@ -185,9 +187,11 @@ */ private ObjectAnimator getCurvedMotionAnimator( float originX, float originY, float finalX, float finalY) { + boolean isClockwise = mIsTopToolbar ? (originX >= finalX) : (originX <= finalX); + ObjectAnimator animator = ViewCurvedMotionAnimatorFactory.build( - mLinkIcon, originX, originY, finalX, finalY, /* isClockwise= */ mIsRtl); + mLinkIcon, originX, originY, finalX, finalY, isClockwise); animator.setDuration(CURVED_MOTION_DURATION_MS); animator.setInterpolator(Interpolators.NEW_BACKGROUND_TAB_ANIMATION_PATH_INTERPOLATOR); animator.addListener(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostViewUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostViewUnitTest.java index 950948e7..43bb3e91 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostViewUnitTest.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewBackgroundTabAnimationHostViewUnitTest.java
@@ -83,109 +83,122 @@ } @Test - public void testUpdateFakeTabSwitcherButton_Default() { + public void testSetUpAnimation_Default() { // Default for non-NTP tab (button visibility at the start of the animation is irrelevant). setButtonVisibility(false); when(mTabSwitcherButton.shouldShowNotificationIcon()).thenReturn(true); - mHostView.updateFakeTabSwitcherButton( + + mHostView.setUpAnimation( mTabSwitcherButton, - /* tabCount= */ 12, - /* backgroundColor= */ Color.WHITE, /* isNtp= */ false, /* isIncognito= */ false, - /* yOffset= */ 7, - /* ntpToolbarTransitionPercentage= */ 1f); - assertDefaultSettings( - NewBackgroundTabAnimationHostView.AnimationType.DEFAULT, + /* isTopToolbar= */ false, + /* backgroundColor= */ Color.WHITE, /* tabCount= */ 12, + /* toolbarHeight= */ 30, + /* statusBarHeight= */ 5, + /* ntpToolbarTransitionPercentage= */ 1f); + + assertDefaultSettings( /* buttonColor= */ Color.WHITE, - /* brandedColorScheme= */ BrandedColorScheme.APP_DEFAULT, - /* showNotificationIcon= */ true, - /* yOffset= */ 7); + BrandedColorScheme.APP_DEFAULT, + /* tabCount= */ 12, + /* topMargin= */ 25, + /* showNotificationIcon= */ true); when(mTabSwitcherButton.shouldShowNotificationIcon()).thenReturn(false); - mHostView.updateFakeTabSwitcherButton( + mHostView.setUpAnimation( mTabSwitcherButton, - /* tabCount= */ 12, - /* backgroundColor= */ Color.WHITE, /* isNtp= */ false, /* isIncognito= */ false, - /* yOffset= */ 7, + /* isTopToolbar= */ false, + /* backgroundColor= */ Color.WHITE, + /* tabCount= */ 12, + /* toolbarHeight= */ 7, + /* statusBarHeight= */ 10, /* ntpToolbarTransitionPercentage= */ 1f); assertFalse(mFakeTabSwitcherButton.getShowIconNotificationStatusForTesting()); // Default for NTP. setButtonVisibility(true); - mHostView.updateFakeTabSwitcherButton( + mHostView.setUpAnimation( mTabSwitcherButton, - /* tabCount= */ 12, - /* backgroundColor= */ Color.WHITE, /* isNtp= */ true, /* isIncognito= */ false, - /* yOffset= */ 7, + /* isTopToolbar= */ false, + /* backgroundColor= */ Color.WHITE, + /* tabCount= */ 56, + /* toolbarHeight= */ 94, + /* statusBarHeight= */ 10, /* ntpToolbarTransitionPercentage= */ 1f); + assertDefaultSettings( - NewBackgroundTabAnimationHostView.AnimationType.DEFAULT, - /* tabCount= */ 12, - /* buttonColor= */ Color.WHITE, - /* brandedColorScheme= */ BrandedColorScheme.APP_DEFAULT, - /* showNotificationIcon= */ false, - /* yOffset= */ 7); + Color.WHITE, + BrandedColorScheme.APP_DEFAULT, + /* tabCount= */ 56, + /* topMargin= */ 84, + /* showNotificationIcon= */ false); } @Test - public void testUpdateFakeTabSwitcherButton_NtpPartialScroll() { + public void testSetUpAnimation_NtpPartialScroll() { setButtonVisibility(false); - mHostView.updateFakeTabSwitcherButton( + mHostView.setUpAnimation( mTabSwitcherButton, - /* tabCount= */ 9, - /* backgroundColor= */ Color.CYAN, /* isNtp= */ true, /* isIncognito= */ false, - /* yOffset= */ 7, + /* isTopToolbar= */ false, + /* backgroundColor= */ Color.CYAN, + /* tabCount= */ 38, + /* toolbarHeight= */ 7, + /* statusBarHeight= */ 3, /* ntpToolbarTransitionPercentage= */ 0.5f); + assertNtpSettings( NewBackgroundTabAnimationHostView.AnimationType.NTP_PARTIAL_SCROLL, - /* tabCount= */ 9, - /* yOffset= */ 7); + /* tabCount= */ 38, + /* topMargin= */ 4); } @Test - public void testUpdateFakeTabSwitcherButton_NtpFullScroll() { + public void testSetUpAnimation_NtpFullScroll() { setButtonVisibility(false); - mHostView.updateFakeTabSwitcherButton( + mHostView.setUpAnimation( mTabSwitcherButton, - /* tabCount= */ 9, - /* backgroundColor= */ Color.CYAN, /* isNtp= */ true, /* isIncognito= */ false, - /* yOffset= */ 7, + /* isTopToolbar= */ false, + /* backgroundColor= */ Color.CYAN, + /* tabCount= */ 9, + /* toolbarHeight= */ 12, + /* statusBarHeight= */ 10, /* ntpToolbarTransitionPercentage= */ 1f); assertNtpSettings( NewBackgroundTabAnimationHostView.AnimationType.NTP_FULL_SCROLL, /* tabCount= */ 9, - /* yOffset= */ 7); + /* topMargin= */ 2); } @Test(expected = AssertionError.class) public void testGetAnimatorSet_Uninitialized() { - mHostView.getAnimatorSet(/* originX= */ 0, /* originY= */ 0, /* statusBarHeight= */ 0); + mHostView.getAnimatorSet(/* originX= */ 0, /* originY= */ 0); } @Test public void testGetAnimatorSet_Default() { setButtonVisibility(true); - mHostView.updateFakeTabSwitcherButton( + mHostView.setUpAnimation( mTabSwitcherButton, - /* tabCount= */ 9, - /* backgroundColor= */ Color.CYAN, /* isNtp= */ false, /* isIncognito= */ false, - /* yOffset= */ 0, + /* isTopToolbar= */ false, + /* backgroundColor= */ Color.CYAN, + /* tabCount= */ 9, + /* toolbarHeight= */ 0, + /* statusBarHeight= */ 0, /* ntpToolbarTransitionPercentage= */ 1f); - AnimatorSet animatorSet = - mHostView.getAnimatorSet( - /* originX= */ -1, /* originY= */ -1, /* statusBarHeight= */ 0); + + AnimatorSet animatorSet = mHostView.getAnimatorSet(/* originX= */ -1, /* originY= */ -1); ArrayList<Animator> animators = animatorSet.getChildAnimations(); assertEquals(3, animators.size()); AnimatorSet transitionAnimator = (AnimatorSet) animators.get(0); @@ -195,17 +208,18 @@ @Test public void testGetAnimatorSet_NtpPartialScroll() { setButtonVisibility(false); - mHostView.updateFakeTabSwitcherButton( + mHostView.setUpAnimation( mTabSwitcherButton, - /* tabCount= */ 9, - /* backgroundColor= */ Color.CYAN, /* isNtp= */ true, /* isIncognito= */ false, - /* yOffset= */ 0, + /* isTopToolbar= */ false, + /* backgroundColor= */ Color.CYAN, + /* tabCount= */ 9, + /* toolbarHeight= */ 0, + /* statusBarHeight= */ 0, /* ntpToolbarTransitionPercentage= */ 0.4f); - AnimatorSet animatorSet = - mHostView.getAnimatorSet( - /* originX= */ -1, /* originY= */ -1, /* statusBarHeight= */ 0); + + AnimatorSet animatorSet = mHostView.getAnimatorSet(/* originX= */ -1, /* originY= */ -1); ArrayList<Animator> animators = animatorSet.getChildAnimations(); assertEquals(4, animators.size()); AnimatorSet transitionAnimator = (AnimatorSet) animators.get(1); @@ -215,17 +229,17 @@ @Test public void testGetAnimatorSet_NtpFullScroll() { setButtonVisibility(false); - mHostView.updateFakeTabSwitcherButton( + mHostView.setUpAnimation( mTabSwitcherButton, - /* tabCount= */ 9, - /* backgroundColor= */ Color.CYAN, /* isNtp= */ true, /* isIncognito= */ false, - /* yOffset= */ 0, + /* isTopToolbar= */ false, + /* backgroundColor= */ Color.CYAN, + /* tabCount= */ 9, + /* toolbarHeight= */ 0, + /* statusBarHeight= */ 0, /* ntpToolbarTransitionPercentage= */ 1f); - AnimatorSet animatorSet = - mHostView.getAnimatorSet( - /* originX= */ -1, /* originY= */ -1, /* statusBarHeight= */ 0); + AnimatorSet animatorSet = mHostView.getAnimatorSet(/* originX= */ -1, /* originY= */ -1); ArrayList<Animator> animators = animatorSet.getChildAnimations(); assertEquals(4, animators.size()); AnimatorSet transitionAnimator = (AnimatorSet) animators.get(1); @@ -235,49 +249,57 @@ @Test public void testBrandedColorScheme() { setButtonVisibility(true); - mHostView.updateFakeTabSwitcherButton( + mHostView.setUpAnimation( mTabSwitcherButton, - /* tabCount= */ 0, - /* backgroundColor= */ Color.WHITE, /* isNtp= */ false, /* isIncognito= */ false, - /* yOffset= */ 0, + /* isTopToolbar= */ false, + /* backgroundColor= */ Color.WHITE, + /* tabCount= */ 0, + /* toolbarHeight= */ 0, + /* statusBarHeight= */ 0, /* ntpToolbarTransitionPercentage= */ 1f); assertEquals( BrandedColorScheme.APP_DEFAULT, mFakeTabSwitcherButton.getBrandedColorSchemeForTesting()); - mHostView.updateFakeTabSwitcherButton( + mHostView.setUpAnimation( mTabSwitcherButton, - /* tabCount= */ 0, - /* backgroundColor= */ Color.WHITE, /* isNtp= */ false, /* isIncognito= */ true, - /* yOffset= */ 0, + /* isTopToolbar= */ false, + /* backgroundColor= */ Color.WHITE, + /* tabCount= */ 0, + /* toolbarHeight= */ 0, + /* statusBarHeight= */ 0, /* ntpToolbarTransitionPercentage= */ 1f); assertEquals( BrandedColorScheme.INCOGNITO, mFakeTabSwitcherButton.getBrandedColorSchemeForTesting()); - mHostView.updateFakeTabSwitcherButton( + mHostView.setUpAnimation( mTabSwitcherButton, - /* tabCount= */ 0, - /* backgroundColor= */ Color.GREEN, /* isNtp= */ false, /* isIncognito= */ false, - /* yOffset= */ 0, + /* isTopToolbar= */ false, + /* backgroundColor= */ Color.GREEN, + /* tabCount= */ 0, + /* toolbarHeight= */ 0, + /* statusBarHeight= */ 0, /* ntpToolbarTransitionPercentage= */ 1f); assertEquals( BrandedColorScheme.LIGHT_BRANDED_THEME, mFakeTabSwitcherButton.getBrandedColorSchemeForTesting()); - mHostView.updateFakeTabSwitcherButton( + mHostView.setUpAnimation( mTabSwitcherButton, - /* tabCount= */ 0, - /* backgroundColor= */ Color.RED, /* isNtp= */ false, /* isIncognito= */ false, - /* yOffset= */ 0, + /* isTopToolbar= */ false, + /* backgroundColor= */ Color.RED, + /* tabCount= */ 0, + /* toolbarHeight= */ 0, + /* statusBarHeight= */ 0, /* ntpToolbarTransitionPercentage= */ 1f); assertEquals( BrandedColorScheme.DARK_BRANDED_THEME, @@ -306,13 +328,12 @@ } private void assertDefaultSettings( - @NewBackgroundTabAnimationHostView.AnimationType int animationType, - int tabCount, @ColorInt int buttonColor, @BrandedColorScheme int brandedColorScheme, - boolean showNotificationIcon, - int yOffset) { - assertCommonElements(animationType, tabCount); + int tabCount, + int topMargin, + boolean showNotificationIcon) { + assertCommonElements(NewBackgroundTabAnimationHostView.AnimationType.DEFAULT, tabCount); assertEquals(buttonColor, mFakeTabSwitcherButton.getButtonColorForTesting()); assertEquals(brandedColorScheme, mFakeTabSwitcherButton.getBrandedColorSchemeForTesting()); assertEquals( @@ -322,19 +343,19 @@ FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mFakeTabSwitcherButton.getLayoutParams(); - assertEquals(yOffset, params.topMargin); + assertEquals(topMargin, params.topMargin); } private void assertNtpSettings( @NewBackgroundTabAnimationHostView.AnimationType int animationType, int tabCount, - int yOffset) { + int topMargin) { FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mFakeTabSwitcherButton.getLayoutParams(); // For Ntp, the tabCount increases when calling {@link // mFakeTabSwitcherButton#setUpNtpAnimation}. assertCommonElements(animationType, tabCount + 1); - int height = yOffset; + int height = topMargin; if (animationType == NewBackgroundTabAnimationHostView.AnimationType.NTP_FULL_SCROLL) { height += Math.round(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java index 5e1e08e..07b4272 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java
@@ -446,20 +446,6 @@ } /** - * Returns the status bar height if {@link - * org.chromium.components.browser_ui.edge_to_edge.layout.EdgeToEdgeBaseLayout} is not present. - */ - private int getStatusBarHeightIfNeeded() { - // TODO(crbug.com/40282469): Remove this method once EdgeToEdgeBaseLayout is always present. - if (mAnimationHostView.getId() == mContentContainer.getId()) { - Rect compositorViewRect = new Rect(); - mCompositorViewHolder.getGlobalVisibleRect(compositorViewRect); - return compositorViewRect.top; - } - return 0; - } - - /** * Forces the new tab animation to finish. * * <p>This method is intended for internal use within {@link NewTabAnimationLayout}. It ensures @@ -665,14 +651,22 @@ ? NewTabAnimationUtils.getBackgroundColor(context, isIncognito) : mToolbarManager.getPrimaryColor(); - // TODO(crbug.com/40282469): Make sure we get the proper y-offset. - mBackgroundHostView.updateFakeTabSwitcherButton( + int[] toolbarPosition = new int[2]; + mAnimationHostView.findViewById(R.id.toolbar).getLocationInWindow(toolbarPosition); + Rect compositorViewRect = new Rect(); + mCompositorViewHolder.getGlobalVisibleRect(compositorViewRect); + boolean isTopToolbar = + isRegularNtp || ToolbarPositionController.shouldShowToolbarOnTop(animationTab); + + mBackgroundHostView.setUpAnimation( tabSwitcherButton, - prevTabCount, - toolbarColor, isRegularNtp, isIncognito, - mBrowserControlsManager.getTopControlsMinHeight(), + isTopToolbar, + toolbarColor, + prevTabCount, + toolbarPosition[1], + compositorViewRect.top, mToolbarManager.getNtpTransitionPercentage()); // TODO(crbug.com/40282469): Get correct x and y for the NTP. @@ -704,8 +698,7 @@ mScrimVisibilitySupplier, this::forceNewTabAnimationToFinish); mTabCreatedBackgroundAnimation = - mBackgroundHostView.getAnimatorSet( - originX, originY, getStatusBarHeightIfNeeded()); + mBackgroundHostView.getAnimatorSet(originX, originY); mTabCreatedBackgroundAnimation.addListener( new AnimatorListenerAdapter() { @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayoutUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayoutUnitTest.java index a44eff9..ad856d5d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayoutUnitTest.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayoutUnitTest.java
@@ -108,6 +108,7 @@ @Mock private Tab mNewTab; @Mock private LayoutTab mLayoutTab; @Mock private ToggleTabStackButton mTabSwitcherButton; + @Mock private View mToolbar; private ObservableSupplierImpl<Tab> mCurrentTabSupplier = new ObservableSupplierImpl<>(); private ObservableSupplierImpl<CompositorViewHolder> mCompositorViewHolderSupplier = @@ -193,6 +194,7 @@ mNewTabAnimationLayout.setTabContentManager(mTabContentManager); when(mAnimationHostView.findViewById(R.id.tab_switcher_button)) .thenReturn(mTabSwitcherButton); + when(mAnimationHostView.findViewById(R.id.toolbar)).thenReturn(mToolbar); mNewTabAnimationLayout.onFinishNativeInitialization(); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java index 077ada3..55b50f5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/ClientManager.java
@@ -494,6 +494,10 @@ return urlsMatch ? PredictionStatus.GOOD : PredictionStatus.BAD; } + public boolean isSessionValid(SessionHolder<?> session) { + return mSessionParams.get(session) != null; + } + /** Registers that a client has launched a URL inside a Custom Tab. */ public synchronized void registerLaunch(SessionHolder<?> session, String url) { @PredictionStatus int outcome = getPredictionOutcome(session, url);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java index b54e38c..21156aec 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsConnection.java
@@ -56,6 +56,7 @@ import org.chromium.base.task.ChainedTasks; import org.chromium.base.task.PostTask; import org.chromium.base.task.TaskTraits; +import org.chromium.build.annotations.MockedInTests; import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeApplicationImpl; import org.chromium.chrome.browser.IntentHandler; @@ -118,6 +119,7 @@ * ChromeApplicationImpl}. */ @JNINamespace("customtabs") +@MockedInTests public class CustomTabsConnection { private static final String TAG = "ChromeConnection"; private static final String LOG_SERVICE_REQUESTS = "custom-tabs-log-service-requests"; @@ -1085,9 +1087,9 @@ * @return The hidden tab, or null. */ public @Nullable HiddenTabHolder.HiddenTab takeHiddenTab( - @Nullable SessionHolder<?> session, String url, @Nullable String referrer) { + @Nullable SessionHolder<?> session, String url, Intent intent) { return mHiddenTabHolder.takeHiddenTab( - session, mClientManager.getIgnoreFragmentsForSession(session), url, referrer); + session, mClientManager.getIgnoreFragmentsForSession(session), url, intent); } /** @@ -2291,6 +2293,10 @@ return success; } + public boolean isSessionValid(SessionHolder<?> session) { + return mClientManager.isSessionValid(session); + } + public static void setInstanceForTesting(CustomTabsConnection connection) { var oldValue = sInstance; sInstance = connection;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java index 3a21394..7b8c72f4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/HiddenTabHolder.java
@@ -10,6 +10,7 @@ import android.os.Bundle; import android.text.TextUtils; +import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.browser.customtabs.CustomTabsSessionToken; @@ -19,6 +20,7 @@ import org.chromium.base.Callback; import org.chromium.base.IntentUtils; import org.chromium.base.TraceEvent; +import org.chromium.base.metrics.RecordHistogram; import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.WarmupManager; import org.chromium.chrome.browser.browserservices.intents.SessionHolder; @@ -40,20 +42,60 @@ import org.chromium.ui.base.WindowAndroid; import org.chromium.url.Origin; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Holds a hidden tab which may be used to preload pages before a CustomTabActivity is launched. * - * Lifecycle: 1:1 relationship between this and {@link CustomTabsConnection}. - * Thread safety: Only access on UI Thread. + * <p>Lifecycle: 1:1 relationship between this and {@link CustomTabsConnection}. <br> + * Thread safety: Only access on UI Thread. <br> * Native: This class needs native to be loaded (since it creates Tabs). */ public class HiddenTabHolder { + private static final String EARLY_NAV_FAILURE_NAME = "CustomTabs.Startup.EarlyNavFailureReason"; + + /** + * Records why External Navigation would have failed if we didn't work around whatever bug we're + * hitting. Used for debugging. + */ + @IntDef({ + EarlyNavFailureReason.SUCCESS, + EarlyNavFailureReason.NO_SESSION, + EarlyNavFailureReason.NO_SPECULATION, + EarlyNavFailureReason.WRONG_URL_VALID_SESSION, + EarlyNavFailureReason.WRONG_REFERRER_VALID_SESSION, + EarlyNavFailureReason.WRONG_URL_INVALID_SESSION, + EarlyNavFailureReason.WRONG_REFERRER_INVALID_SESSION, + EarlyNavFailureReason.NUM_ENTRIES + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EarlyNavFailureReason { + /* Not a failure. */ + int SUCCESS = 0; + /* Intent has no associated session. */ + int NO_SESSION = 1; + /* Speculation was gone by the time we received the intent. */ + int NO_SPECULATION = 2; + /* Intent URL doesn't match speculated URL, session is valid. */ + int WRONG_URL_VALID_SESSION = 3; + /* Intent referrer doesn't match speculated referrer, session is valid. */ + int WRONG_REFERRER_VALID_SESSION = 4; + /* Intent URL doesn't match speculated URL, session is invalid. */ + int WRONG_URL_INVALID_SESSION = 5; + /* Intent referrer doesn't match speculated referrer, session is invalid. */ + int WRONG_REFERRER_INVALID_SESSION = 6; + + int NUM_ENTRIES = 6; + } + /** Holds the parameters for the current hidden tab speculation. */ @VisibleForTesting static final class SpeculationParams { public final SessionHolder<?> session; public final HiddenTab hiddenTab; public final String referrer; + public final boolean isEarlyNav; private SpeculationParams( SessionHolder<?> session, @@ -62,7 +104,8 @@ String referrer, TabObserverRegistrar tabObserverRegistrar, CustomTabObserver customTabObserver, - CustomTabNavigationEventObserver customTabNavigationEventObserver) { + CustomTabNavigationEventObserver customTabNavigationEventObserver, + boolean isEarlyNav) { this.session = session; this.hiddenTab = new HiddenTab( @@ -72,6 +115,7 @@ customTabNavigationEventObserver, url); this.referrer = referrer; + this.isEarlyNav = isEarlyNav; } } @@ -133,6 +177,8 @@ String url, @Nullable Bundle extras, @Nullable WebContents webContents) { + // If a new speculation arrives with an early-nav in progress, don't clobber the early nav. + if (mSpeculation != null && mSpeculation.isEarlyNav) return; assert mSpeculation == null; Intent extrasIntent = new Intent(); if (extras != null) extrasIntent.putExtras(extras); @@ -197,7 +243,8 @@ referrer, registrar, customTabObserver, - customTabNavigationEventObserver); + customTabNavigationEventObserver, + /* isEarlyNav= */ false); tab.loadUrl(loadParams); } @@ -216,8 +263,20 @@ @Nullable SessionHolder<?> session, boolean ignoreFragments, String url, - @Nullable String referrer) { + Intent intent) { try (TraceEvent e = TraceEvent.scoped("CustomTabsConnection.takeHiddenTab")) { + if (intent.getBooleanExtra(IntentHandler.EXTRA_CCT_EARLY_NAV, false)) { + recordEarlyNavDebugMetric(session, ignoreFragments, url, intent); + + // Work around Early Nav failures by always continuing with the early nav if it + // hasn't been cleaned up. + if (mSpeculation != null && mSpeculation.isEarlyNav) { + HiddenTab hiddenTab = mSpeculation.hiddenTab; + mSpeculation = null; + return hiddenTab; + } + } + if (mSpeculation == null || session == null) return null; if (!session.equals(mSpeculation.session)) return null; @@ -232,6 +291,7 @@ ? UrlUtilities.urlsMatchIgnoringFragments(speculatedUrl, url) : TextUtils.equals(speculatedUrl, url); + String referrer = IntentHandler.getReferrerUrlIncludingExtraHeaders(intent); if (referrer == null) referrer = ""; if (urlsMatch && TextUtils.equals(speculationReferrer, referrer)) { @@ -243,10 +303,55 @@ } } + // Not a dynamic string, just an error code enum. + @SuppressWarnings("NoDynamicStringsInTraceEventCheck") + void recordEarlyNavDebugMetric( + @Nullable SessionHolder<?> session, + boolean ignoreFragments, + String url, + Intent intent) { + @EarlyNavFailureReason int failureReason = EarlyNavFailureReason.SUCCESS; + if (mSpeculation == null) { + failureReason = EarlyNavFailureReason.NO_SPECULATION; + } else if (session == null) { + failureReason = EarlyNavFailureReason.NO_SESSION; + } else { + boolean isSessionValid = CustomTabsConnection.getInstance().isSessionValid(session); + HiddenTab hiddenTab = mSpeculation.hiddenTab; + String speculatedUrl = hiddenTab.url; + boolean urlsMatch = + ignoreFragments + ? UrlUtilities.urlsMatchIgnoringFragments(speculatedUrl, url) + : TextUtils.equals(speculatedUrl, url); + String referrer = IntentHandler.getReferrerUrlIncludingExtraHeaders(intent); + if (referrer == null) referrer = ""; + if (!urlsMatch) { + if (isSessionValid) { + failureReason = EarlyNavFailureReason.WRONG_URL_VALID_SESSION; + } else { + failureReason = EarlyNavFailureReason.WRONG_URL_INVALID_SESSION; + } + } else { + if (referrer == null) referrer = ""; + if (!TextUtils.equals(mSpeculation.referrer, referrer)) { + if (isSessionValid) { + failureReason = EarlyNavFailureReason.WRONG_REFERRER_VALID_SESSION; + } else { + failureReason = EarlyNavFailureReason.WRONG_REFERRER_INVALID_SESSION; + } + } + } + } + TraceEvent.instant(EARLY_NAV_FAILURE_NAME + "_" + failureReason); + RecordHistogram.recordEnumeratedHistogram( + EARLY_NAV_FAILURE_NAME, failureReason, EarlyNavFailureReason.NUM_ENTRIES); + } + /** Cancels the speculation for a given session, or any session if null. */ void destroyHiddenTab(@Nullable SessionHolder<?> session) { if (mSpeculation == null) return; if (session != null && !session.equals(mSpeculation.session)) return; + if (mSpeculation.isEarlyNav) return; mSpeculation.hiddenTab.tab.destroy(); mSpeculation = null; @@ -322,7 +427,8 @@ referrer, registrar, customTabObserver, - customTabNavigationEventObserver); + customTabNavigationEventObserver, + /* isEarlyNav= */ true); // Notifies PreloadingImpl that a navigation to CCT is happening. This is used to calculate // the recall of CCT prefetch's attempt. Please see @@ -335,6 +441,7 @@ } } + intent.putExtra(IntentHandler.EXTRA_CCT_EARLY_NAV, true); tab.loadUrl(params); return true; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java index 3f1ead8f..ee67c9d2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
@@ -393,11 +393,10 @@ public static @Nullable HiddenTab getHiddenTab( BrowserServicesIntentDataProvider intentDataProvider) { String url = intentDataProvider.getUrlToLoad(); - String referrerUrl = - IntentHandler.getReferrerUrlIncludingExtraHeaders(intentDataProvider.getIntent()); SessionHolder<?> token = intentDataProvider.getSession(); HiddenTab hiddenTab = - CustomTabsConnection.getInstance().takeHiddenTab(token, url, referrerUrl); + CustomTabsConnection.getInstance() + .takeHiddenTab(token, url, intentDataProvider.getIntent()); if (hiddenTab == null) return null; RecordHistogram.recordEnumeratedHistogram( "CustomTabs.WebContentsStateOnLaunch",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java index 64464bd..8137b69 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java
@@ -32,6 +32,7 @@ import org.chromium.chrome.browser.browser_controls.BrowserControlsUtils; import org.chromium.chrome.browser.browser_controls.BrowserStateBrowserControlsVisibilityDelegate; import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher; import org.chromium.chrome.browser.tab.SadTab; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabBrowserControlsConstraintsHelper; @@ -167,30 +168,43 @@ /** * Creates an instance of the browser controls manager. + * * @param activity The activity that supports browser controls. * @param controlsPosition Where the browser controls are. - */ - public BrowserControlsManager(Activity activity, @ControlsPosition int controlsPosition) { - this(activity, controlsPosition, true); - } - - /** - * Creates an instance of the browser controls manager. - * @param activity The activity that supports browser controls. - * @param controlsPosition Where the browser controls are. - * @param exitFullscreenOnStop Whether fullscreen mode should exit on stop - should be - * true for Activities that are not always fullscreen. + * @param multiWindowDispatcher The multi-window mode observer for exiting fullscreen when the + * user drags the window out of edge-to-edge fullscreen */ public BrowserControlsManager( Activity activity, @ControlsPosition int controlsPosition, - boolean exitFullscreenOnStop) { + MultiWindowModeStateDispatcher multiWindowDispatcher) { + this(activity, controlsPosition, true, multiWindowDispatcher); + } + + /** + * Creates an instance of the browser controls manager. + * + * @param activity The activity that supports browser controls. + * @param controlsPosition Where the browser controls are. + * @param exitFullscreenOnStop Whether fullscreen mode should exit on stop - should be true for + * Activities that are not always fullscreen. + * @param multiWindowDispatcher The multi-window mode observer for exiting fullscreen when the + * user drags the window out of edge-to-edge fullscreen + */ + public BrowserControlsManager( + Activity activity, + @ControlsPosition int controlsPosition, + boolean exitFullscreenOnStop, + MultiWindowModeStateDispatcher multiWindowDispatcher) { mActivity = activity; mControlsPosition = controlsPosition; mControlsAtMinHeight.set(false); mHtmlApiHandler = FullscreenHtmlApiHandlerFactory.createInstance( - activity, mControlsAtMinHeight, exitFullscreenOnStop); + activity, + mControlsAtMinHeight, + exitFullscreenOnStop, + multiWindowDispatcher); mBrowserVisibilityDelegate = new BrowserStateBrowserControlsVisibilityDelegate( mHtmlApiHandler.getPersistentFullscreenModeSupplier());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerBase.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerBase.java index 68ffb314..545b9ee3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerBase.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerBase.java
@@ -5,8 +5,11 @@ package org.chromium.chrome.browser.fullscreen; import android.app.Activity; +import android.os.Build; import android.os.Handler; import android.os.Message; +import android.os.OutcomeReceiver; +import android.support.annotation.NonNull; import android.view.View; import android.view.View.OnLayoutChangeListener; import android.view.Window; @@ -30,13 +33,13 @@ import org.chromium.chrome.browser.ActivityTabProvider; import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabTabObserver; import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher; import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabAttributeKeys; import org.chromium.chrome.browser.tab.TabAttributes; import org.chromium.chrome.browser.tab.TabBrowserControlsConstraintsHelper; import org.chromium.chrome.browser.tab.TabHidingType; -import org.chromium.chrome.browser.tab.TabUtils; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver; import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeUtils; @@ -54,6 +57,7 @@ /** Handles updating the UI based on requests to the HTML Fullscreen API. */ public abstract class FullscreenHtmlApiHandlerBase implements ActivityStateListener, WindowFocusChangedListener, FullscreenManager { + private static final String TAG = "FullscreenHTMLBase"; private static final boolean DEBUG_LOGS = false; private static final String BROWSER_CONTROLS_FORCED_UPON_FULLSCREEN_EXIT_HISTOGRAM = "Android.FullscreenExit.BrowserControlsForced"; @@ -111,6 +115,10 @@ private ActivityTabTabObserver mActiveTabObserver; private TabModelSelectorTabObserver mTabFullscreenObserver; @Nullable private Tab mTab; + private boolean mDisplayEdgeToEdgeFullscreenToBeExited; + private boolean mIsInMultiWindowMode; + + private FullscreenMultiWindowModeObserver mMultiWindowModeObserver; private boolean mNotifyOnNextExit; @@ -224,6 +232,17 @@ } } + class FullscreenMultiWindowModeObserver + implements MultiWindowModeStateDispatcher.MultiWindowModeObserver { + @Override + public void onMultiWindowModeChanged(boolean isInMultiWindowMode) { + if (!mIsInMultiWindowMode && isInMultiWindowMode) { + onExitFullscreen(mTab); + } + mIsInMultiWindowMode = isInMultiWindowMode; + } + } + /** * Constructs the handler that will manage the UI transitions from the HTML fullscreen API. * @@ -231,11 +250,14 @@ * @param areControlsHidden Supplier of a flag indicating if browser controls are hidden. * @param exitFullscreenOnStop Whether fullscreen mode should exit on stop - should be true for * Activities that are not always fullscreen. + * @param multiWindowDispatcher multi window mode observer allows to exit fullscreen when user + * drag the window out of edge-to-edge fullscreen */ public FullscreenHtmlApiHandlerBase( Activity activity, ObservableSupplier<Boolean> areControlsHidden, - boolean exitFullscreenOnStop) { + boolean exitFullscreenOnStop, + MultiWindowModeStateDispatcher multiWindowDispatcher) { mActivity = activity; mAreControlsHidden = areControlsHidden; mAreControlsHidden.addObserver(this::maybeEnterFullscreenFromPendingState); @@ -244,6 +266,9 @@ mPersistentModeSupplier = new ObservableSupplierImpl<>(); mPersistentModeSupplier.set(false); mExitFullscreenOnStop = exitFullscreenOnStop; + + mMultiWindowModeObserver = new FullscreenMultiWindowModeObserver(); + multiWindowDispatcher.addObserver(mMultiWindowModeObserver); } /** @@ -333,7 +358,7 @@ enterPersistentFullscreenMode(options); destroySelectActionMode(tab); setEnterFullscreenRunnable(tab, null); - for (FullscreenManager.Observer observer : mObservers) { + for (Observer observer : mObservers) { observer.onEnterFullscreen(tab, options); } }; @@ -404,13 +429,36 @@ } /** - * Enters persistent fullscreen mode. In this mode, the browser controls will be - * permanently hidden until this mode is exited. + * Enters persistent fullscreen mode. In this mode, the browser controls will be permanently + * hidden until this mode is exited. * * @param options Options to choose mode of fullscreen. */ private void enterPersistentFullscreenMode(FullscreenOptions options) { if (!shouldSkipEnterFullscreenRequest(options)) { + if (ChromeFeatureList.isEnabled(ChromeFeatureList.DISPLAY_EDGE_TO_EDGE_FULLSCREEN)) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + OutcomeReceiver<Void, Throwable> resultCb = + new OutcomeReceiver<>() { + @Override + public void onResult(Void unused) { + // Mark that the Window mode was changed during the fullscreen + // transition. It needs to be reverted on exit. + mDisplayEdgeToEdgeFullscreenToBeExited = true; + } + + @Override + public void onError(@NonNull Throwable error) { + // There is nothing to be done in case of failed transition to + // fullscreen mode. It can happen when in split screen or + // already in fullscreen mode. + mDisplayEdgeToEdgeFullscreenToBeExited = false; + } + }; + enterFullscreenMode(resultCb); + } + } + mPersistentModeSupplier.set(true); mNotifyOnNextExit = true; if (mAreControlsHidden.get()) { @@ -443,6 +491,14 @@ @Override public void exitPersistentFullscreenMode() { + // Exit window edge to edge fullscreen mode only if element fullscreen mode was triggered + // when the window was in free form mode. This prevent exiting window fullscreen mode when + // user requested it independently. + if (mDisplayEdgeToEdgeFullscreenToBeExited) { + maybeExitFullscreenMode(null); + mDisplayEdgeToEdgeFullscreenToBeExited = false; + } + if (getPersistentFullscreenMode()) { getToast().onExitPersistentFullscreen(); mPersistentModeSupplier.set(false); @@ -615,9 +671,6 @@ resetEnterFullscreenLayoutChangeListener(contentView); hideSystemBars(contentView, mFullscreenOptions); } else { - Activity activity = TabUtils.getActivity(tab); - boolean isMultiWindow = MultiWindowUtils.getInstance().isInMultiWindowMode(activity); - // To avoid a double layout that is caused by the system when just hiding // the status bar set the status bar as translucent immediately. This causes // it not to take up space so the layout is stable. (See https://crbug.com/935015). @@ -625,7 +678,7 @@ // on some automotive devices), since the status bar will be forced to always stay // visible. if (!mFullscreenOptions.showStatusBar - && !isMultiWindow + && !mIsInMultiWindowMode && !BuildInfo.getInstance().isAutomotive) { setTranslucentStatusBar(); } @@ -748,6 +801,21 @@ window.setAttributes(attrs); } + private void enterFullscreenMode(OutcomeReceiver<Void, Throwable> callback) { + runRequestFullscreenMode(callback, Activity.FULLSCREEN_MODE_REQUEST_ENTER); + } + + private void maybeExitFullscreenMode(OutcomeReceiver<Void, Throwable> callback) { + runRequestFullscreenMode(callback, Activity.FULLSCREEN_MODE_REQUEST_EXIT); + } + + private void runRequestFullscreenMode( + OutcomeReceiver<Void, Throwable> callback, int fullscreenModeRequest) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + mActivity.requestFullscreenMode(fullscreenModeRequest, callback); + } + } + /** Destroys the FullscreenHtmlApiHandler. */ public void destroy() { mTab = null;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompat.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompat.java index 593d89f..1a3d4d6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompat.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompat.java
@@ -18,6 +18,7 @@ import org.chromium.base.BuildInfo; import org.chromium.base.Log; import org.chromium.base.supplier.ObservableSupplier; +import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher; import org.chromium.components.embedder_support.view.ContentView; public class FullscreenHtmlApiHandlerCompat extends FullscreenHtmlApiHandlerBase @@ -34,12 +35,15 @@ * @param areControlsHidden Supplier of a flag indicating if browser controls are hidden. * @param exitFullscreenOnStop Whether fullscreen mode should exit on stop - should be true for * Activities that are not always fullscreen. + * @param multiWindowDispatcher multi window mode observer allows to exit fullscreen when user + * drag the window out of edge-to-edge fullscreen */ public FullscreenHtmlApiHandlerCompat( Activity activity, ObservableSupplier<Boolean> areControlsHidden, - boolean exitFullscreenOnStop) { - super(activity, areControlsHidden, exitFullscreenOnStop); + boolean exitFullscreenOnStop, + MultiWindowModeStateDispatcher multiWindowDispatcher) { + super(activity, areControlsHidden, exitFullscreenOnStop, multiWindowDispatcher); } // View.OnApplyWindowInsetsListener
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerFactory.java index f640878e..17fe844 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerFactory.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerFactory.java
@@ -9,6 +9,7 @@ import org.chromium.base.BuildInfo; import org.chromium.base.supplier.ObservableSupplier; import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher; public class FullscreenHtmlApiHandlerFactory { @@ -16,13 +17,14 @@ static FullscreenHtmlApiHandlerBase createInstance( Activity activity, ObservableSupplier<Boolean> areControlsHidden, - boolean exitFullscreenOnStop) { + boolean exitFullscreenOnStop, + MultiWindowModeStateDispatcher multiWindowDispatcher) { if (isFullscreenApiMigrationEnabled()) { return new FullscreenHtmlApiHandlerCompat( - activity, areControlsHidden, exitFullscreenOnStop); + activity, areControlsHidden, exitFullscreenOnStop, multiWindowDispatcher); } return new FullscreenHtmlApiHandlerLegacy( - activity, areControlsHidden, exitFullscreenOnStop); + activity, areControlsHidden, exitFullscreenOnStop, multiWindowDispatcher); } private static boolean isFullscreenApiMigrationEnabled() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerLegacy.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerLegacy.java index 1bb1bcb..4e9d82f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerLegacy.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerLegacy.java
@@ -18,6 +18,7 @@ import org.chromium.base.BuildInfo; import org.chromium.base.Log; import org.chromium.base.supplier.ObservableSupplier; +import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher; import org.chromium.components.embedder_support.view.ContentView; public class FullscreenHtmlApiHandlerLegacy extends FullscreenHtmlApiHandlerBase @@ -34,12 +35,15 @@ * @param areControlsHidden Supplier of a flag indicating if browser controls are hidden. * @param exitFullscreenOnStop Whether fullscreen mode should exit on stop - should be true for * Activities that are not always fullscreen. + * @param multiWindowDispatcher multi window mode observer allows to exit fullscreen when user + * drag the window out of edge-to-edge fullscreen */ public FullscreenHtmlApiHandlerLegacy( Activity activity, ObservableSupplier<Boolean> areControlsHidden, - boolean exitFullscreenOnStop) { - super(activity, areControlsHidden, exitFullscreenOnStop); + boolean exitFullscreenOnStop, + MultiWindowModeStateDispatcher multiWindowDispatcher) { + super(activity, areControlsHidden, exitFullscreenOnStop, multiWindowDispatcher); } // View.OnSystemUiVisibilityChangeListener
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenToast.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenToast.java index d9fb942..c03f5b3c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenToast.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenToast.java
@@ -9,6 +9,7 @@ import org.chromium.base.BuildInfo; import org.chromium.chrome.R; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.ui.UiUtils; import org.chromium.ui.widget.Toast; import org.chromium.ui.widget.Toast.ToastPriority; @@ -91,6 +92,12 @@ UiUtils.isGestureNavigationMode(mActivity.getWindow()) ? R.string.immersive_fullscreen_gesture_navigation_mode_api_notification : R.string.immersive_fullscreen_api_notification; + if (BuildInfo.getInstance().isDesktop) { + if (ChromeFeatureList.isEnabled( + ChromeFeatureList.DISPLAY_EDGE_TO_EDGE_FULLSCREEN)) { + toastTextId = R.string.immersive_fullscreen_api_notification_desktop; + } + } if (BuildInfo.getInstance().isAutomotive) { toastTextId = R.string.immersive_fullscreen_automotive_toolbar_improvements; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java index 6b068c8..e70d447 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
@@ -247,8 +247,8 @@ TraceEvent.begin("maybePreconnect"); Intent intent = getIntent(); if (intent == null || !Intent.ACTION_VIEW.equals(intent.getAction())) return; - // TODO(https://crbug.com/372710946): Clean this up if CCTEarlyNav doesn't ship. - if (intent.getBooleanExtra(IntentHandler.EXTRA_SKIP_PRECONNECT, false)) return; + // Preconnect is too late if we've already started navigation early. + if (intent.getBooleanExtra(IntentHandler.EXTRA_CCT_EARLY_NAV, false)) return; String url = IntentHandler.getUrlFromIntent(intent); if (url == null) return; // Blocking pre-connect for all off-the-record profiles.
diff --git a/chrome/android/javatests/BUILD.gn b/chrome/android/javatests/BUILD.gn index 43b55416..bce9b7d0 100644 --- a/chrome/android/javatests/BUILD.gn +++ b/chrome/android/javatests/BUILD.gn
@@ -559,7 +559,6 @@ "src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragmentTest.java", "src/org/chromium/chrome/browser/autofill/settings/AutofillServerCardEditorTest.java", "src/org/chromium/chrome/browser/autofill/settings/AutofillTestRule.java", - "src/org/chromium/chrome/browser/autofill/settings/CreditCardScannerManagerTest.java", "src/org/chromium/chrome/browser/autofill/settings/FinancialAccountsManagementFragmentTest.java", "src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetRenderTest.java", "src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetViewBinderTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java index a92c69b3..85495c9 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java
@@ -13,6 +13,7 @@ import org.chromium.chrome.browser.fullscreen.BrowserControlsManager; import org.chromium.chrome.browser.fullscreen.FullscreenManager; import org.chromium.chrome.browser.layouts.SceneOverlay; +import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcherImpl; import org.chromium.ui.resources.ResourceManager; /** @@ -31,7 +32,10 @@ public MockLayoutHost(Context context) { mContext = context; mBrowserControlsManager = - new BrowserControlsManager(null, BrowserControlsStateProvider.ControlsPosition.TOP); + new BrowserControlsManager( + null, + BrowserControlsStateProvider.ControlsPosition.TOP, + new MultiWindowModeStateDispatcherImpl(null)); } public void setOrientation(boolean portrait) {
diff --git a/chrome/android/junit/BUILD.gn b/chrome/android/junit/BUILD.gn index a70b421..d3d1d23 100644 --- a/chrome/android/junit/BUILD.gn +++ b/chrome/android/junit/BUILD.gn
@@ -462,6 +462,7 @@ "src/org/chromium/chrome/browser/autofill/settings/AutofillLocalIbanEditorTest.java", "src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardEnrollmentDialogTest.java", "src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardUnenrollmentDialogTest.java", + "src/org/chromium/chrome/browser/autofill/settings/CreditCardScannerManagerTest.java", "src/org/chromium/chrome/browser/autofill/settings/SettingsNavigationHelperTest.java", "src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetBridgeTest.java", "src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetContentTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/CreditCardScannerManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/CreditCardScannerManagerTest.java similarity index 97% rename from chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/CreditCardScannerManagerTest.java rename to chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/CreditCardScannerManagerTest.java index 683c714..33cc7ad 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/CreditCardScannerManagerTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/CreditCardScannerManagerTest.java
@@ -25,8 +25,9 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import org.robolectric.annotation.Config; -import org.chromium.base.test.util.Batch; +import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.Features.DisableFeatures; import org.chromium.base.test.util.Features.EnableFeatures; import org.chromium.base.test.util.HistogramWatcher; @@ -36,14 +37,13 @@ import org.chromium.chrome.browser.autofill.settings.CreditCardScannerManager.FieldType; import org.chromium.chrome.browser.autofill.settings.CreditCardScannerManager.ScanResult; import org.chromium.chrome.browser.flags.ChromeFeatureList; -import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.ui.base.IntentRequestTracker; import java.util.Set; -/** Test relating to {@link CreditCardScannerManager} */ -@RunWith(ChromeJUnit4ClassRunner.class) -@Batch(Batch.PER_CLASS) // PER_CLASS required for the UserActionTester. +/** Unit tests for {@link CreditCardScannerManager}. */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) @EnableFeatures({ChromeFeatureList.AUTOFILL_ENABLE_PAYMENT_SETTINGS_CARD_PROMO_AND_SCAN_CARD}) public class CreditCardScannerManagerTest { @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -51,6 +51,7 @@ @Mock private CreditCardScanner mScanner; @Mock private CreditCardScannerManager.Delegate mDelegate; @Mock private IntentRequestTracker mTracker; + private UserActionTester mActionTester; @Before
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorViewHolderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorViewHolderUnitTest.java index 8c0edd4..9758106 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorViewHolderUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/CompositorViewHolderUnitTest.java
@@ -63,6 +63,7 @@ import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.fullscreen.BrowserControlsManager; import org.chromium.chrome.browser.layouts.EventFilter.EventType; +import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tab.MockTab; import org.chromium.chrome.browser.tab.Tab; @@ -175,6 +176,7 @@ @Mock private OnscreenContentProvider.Natives mOnscreenContentProviderJni; @Mock private ContentCaptureFeatures.Natives mContentCaptureFeaturesJni; @Mock private InputHintChecker.Natives mInputHintCheckerJni; + @Mock private MultiWindowModeStateDispatcher mMultiWindowModeStateDispatcher; @Captor private ArgumentCaptor<TabObserver> mTabObserverCaptor; @@ -229,7 +231,9 @@ BrowserControlsManager browserControlsManager = new BrowserControlsManager( - mActivity, BrowserControlsStateProvider.ControlsPosition.TOP); + mActivity, + BrowserControlsStateProvider.ControlsPosition.TOP, + mMultiWindowModeStateDispatcher); mBrowserControlsManager = spy(browserControlsManager); mBrowserControlsManager.initialize( mControlContainer,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManagerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManagerUnitTest.java index 37283fc..e9596b9 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManagerUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManagerUnitTest.java
@@ -56,6 +56,7 @@ import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider.ControlsPosition; import org.chromium.chrome.browser.browser_controls.BrowserStateBrowserControlsVisibilityDelegate; import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabBrowserControlsOffsetHelper; import org.chromium.chrome.browser.tab.TabCreationState; @@ -91,6 +92,7 @@ @Mock private ContentView mContentView; @Mock private TabModel mTabModel; @Mock private TabBrowserControlsOffsetHelper mTabBrowserControlsOffsetHelper; + @Mock private MultiWindowModeStateDispatcher mMultiWindowModeStateDispatcher; private @Captor ArgumentCaptor<Callback<Tab>> mCallbackTabCaptor; private @Captor ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor; @@ -129,7 +131,9 @@ BrowserControlsManager browserControlsManager = new BrowserControlsManager( - mActivity, BrowserControlsStateProvider.ControlsPosition.TOP); + mActivity, + BrowserControlsStateProvider.ControlsPosition.TOP, + mMultiWindowModeStateDispatcher); mBrowserControlsManager = spy(browserControlsManager); mBrowserControlsManager.initialize( mControlContainer, @@ -145,7 +149,9 @@ mBrowserControlsManager.destroy(); mBrowserControlsManager = new BrowserControlsManager( - mActivity, BrowserControlsStateProvider.ControlsPosition.TOP); + mActivity, + BrowserControlsStateProvider.ControlsPosition.TOP, + mMultiWindowModeStateDispatcher); mBrowserControlsManager.initialize( mControlContainer, mActivityTabProvider, @@ -429,7 +435,9 @@ public void testGetAndroidControlsVisibility() { BrowserControlsManager browserControlsManager = new BrowserControlsManager( - mActivity, BrowserControlsStateProvider.ControlsPosition.TOP); + mActivity, + BrowserControlsStateProvider.ControlsPosition.TOP, + mMultiWindowModeStateDispatcher); assertEquals(View.INVISIBLE, browserControlsManager.getAndroidControlsVisibility()); browserControlsManager.initialize( @@ -608,7 +616,9 @@ public void testStartWithBottom() { BrowserControlsManager browserControlsManager = new BrowserControlsManager( - mActivity, BrowserControlsStateProvider.ControlsPosition.BOTTOM); + mActivity, + BrowserControlsStateProvider.ControlsPosition.BOTTOM, + mMultiWindowModeStateDispatcher); browserControlsManager.initialize( mControlContainer, mActivityTabProvider,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompatUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompatUnitTest.java index 5b3f958..6d43b55 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompatUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerCompatUnitTest.java
@@ -5,6 +5,7 @@ package org.chromium.chrome.browser.fullscreen; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; @@ -13,6 +14,8 @@ import android.annotation.SuppressLint; import android.app.Activity; +import android.os.Build; +import android.os.OutcomeReceiver; import android.view.View.OnLayoutChangeListener; import androidx.core.graphics.Insets; @@ -29,23 +32,35 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.Robolectric; +import org.robolectric.Shadows; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.shadows.ShadowActivity; import org.chromium.base.ActivityState; import org.chromium.base.UserDataHost; import org.chromium.base.supplier.ObservableSupplierImpl; import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.Features; import org.chromium.cc.input.BrowserControlsState; import org.chromium.chrome.browser.ActivityTabProvider; +import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher; +import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcherImpl; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabBrowserControlsConstraintsHelper; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.components.embedder_support.view.ContentView; import org.chromium.content_public.browser.WebContents; +import java.util.HashMap; + /** * Unit tests for {@link FullscreenHtmlApiHandlerCompat}. TODO(crbug.com/40525786): Can be * parametrized with a parametrized Robolectric test runner. */ +@Features.EnableFeatures({ChromeFeatureList.DISPLAY_EDGE_TO_EDGE_FULLSCREEN}) @RunWith(BaseRobolectricTestRunner.class) public class FullscreenHtmlApiHandlerCompatUnitTest { private static final int DEVICE_WIDTH = 900; @@ -60,6 +75,7 @@ @Mock private ContentView mContentView; @Mock private ActivityTabProvider mActivityTabProvider; @Mock private TabModelSelector mTabModelSelector; + private MultiWindowModeStateDispatcher mMultiWindowModeStateDispatcher; private FullscreenHtmlApiHandlerCompat mFullscreenHtmlApiHandlerCompat; private ObservableSupplierImpl<Boolean> mAreControlsHidden; @@ -78,6 +94,43 @@ .setInsets(WindowInsetsCompat.Type.systemBars(), SYSTEM_BAR_INSETS) .build(); + @Implements(Activity.class) + public static class FullscreenShadowActivity extends ShadowActivity { + public HashMap<Integer, Integer> counters = new HashMap<>(); + + public FullscreenShadowActivity() {} + + @Implementation(minSdk = 34) + protected void requestFullscreenMode( + int request, OutcomeReceiver<Void, Throwable> approvalCallback) { + if (approvalCallback != null) { + approvalCallback.onResult(null); + } + if (counters.containsKey(request)) { + counters.put(request, counters.get(request) + 1); + } else { + counters.put(request, 1); + } + } + } + + private void assertEqualNumberOfEnterAndExitActivityFullscreenMode(int numberOfEnters) { + FullscreenShadowActivity shadow = (FullscreenShadowActivity) Shadows.shadowOf(mActivity); + if (numberOfEnters == 0) { + assertFalse(shadow.counters.containsKey(Activity.FULLSCREEN_MODE_REQUEST_ENTER)); + assertFalse(shadow.counters.containsKey(Activity.FULLSCREEN_MODE_REQUEST_EXIT)); + } else { + assertTrue(shadow.counters.containsKey(Activity.FULLSCREEN_MODE_REQUEST_ENTER)); + assertEquals( + numberOfEnters, + (int) shadow.counters.get(Activity.FULLSCREEN_MODE_REQUEST_ENTER)); + assertTrue(shadow.counters.containsKey(Activity.FULLSCREEN_MODE_REQUEST_EXIT)); + assertEquals( + numberOfEnters, + (int) shadow.counters.get(Activity.FULLSCREEN_MODE_REQUEST_EXIT)); + } + } + @Before public void setUp() { mActivity = Robolectric.buildActivity(Activity.class).setup().get(); @@ -85,8 +138,10 @@ doReturn(mHost).when(mTab).getUserDataHost(); mAreControlsHidden = new ObservableSupplierImpl<Boolean>(); + mMultiWindowModeStateDispatcher = new MultiWindowModeStateDispatcherImpl(mActivity); mFullscreenHtmlApiHandlerCompat = - new FullscreenHtmlApiHandlerCompat(mActivity, mAreControlsHidden, false) { + new FullscreenHtmlApiHandlerCompat( + mActivity, mAreControlsHidden, false, mMultiWindowModeStateDispatcher) { // This needs a PopupController, which isn't available in the test since we // can't mock statics in this version of mockito. Even if we could mock it, it // casts to WebContentsImpl and other things that we can't reference due to @@ -100,6 +155,9 @@ } @Test + @Config( + shadows = {FullscreenShadowActivity.class}, + sdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public void testFullscreenRequestCanceledAtPendingStateBeforeControlsDisappear() { // avoid calling GestureListenerManager/SelectionPopupController doReturn(null).when(mTab).getWebContents(); @@ -127,9 +185,15 @@ // The fullscreen request was canceled. Verify the controls are restored. verify(mTabBrowserControlsConstraintsHelper).update(BrowserControlsState.SHOWN, true); assertEquals(null, mFullscreenHtmlApiHandlerCompat.getPendingFullscreenOptionsForTesting()); + + // Verify that fullscreen mode was exited properly + assertEqualNumberOfEnterAndExitActivityFullscreenMode(1); } @Test + @Config( + shadows = {FullscreenShadowActivity.class}, + sdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public void testFullscreenRequestCanceledAtPendingStateAfterControlsDisappear() { // Avoid calling GestureListenerManager/SelectionPopupController doReturn(null).when(mTab).getWebContents(); @@ -150,9 +214,15 @@ // Verify the browser controls are restored. verify(mTabBrowserControlsConstraintsHelper).update(BrowserControlsState.SHOWN, true); assertEquals(null, mFullscreenHtmlApiHandlerCompat.getPendingFullscreenOptionsForTesting()); + + // Verify that fullscreen mode was exited properly + assertEqualNumberOfEnterAndExitActivityFullscreenMode(1); } @Test + @Config( + shadows = {FullscreenShadowActivity.class}, + sdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public void testFullscreenAddAndRemoveObserver() { // avoid calling GestureListenerManager/SelectionPopupController doReturn(null).when(mTab).getWebContents(); @@ -184,6 +254,9 @@ } @Test + @Config( + shadows = {FullscreenShadowActivity.class}, + sdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public void testFullscreenObserverCalledOncePerSession() { // avoid calling GestureListenerManager/SelectionPopupController doReturn(null).when(mTab).getWebContents(); @@ -214,9 +287,14 @@ verify(observer, times(3)).onEnterFullscreen(mTab, fullscreenOptions); mFullscreenHtmlApiHandlerCompat.onExitFullscreen(mTab); verify(observer, times(3)).onExitFullscreen(mTab); + + assertEqualNumberOfEnterAndExitActivityFullscreenMode(3); } @Test + @Config( + shadows = {FullscreenShadowActivity.class}, + sdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public void testFullscreenObserverCalledOncePerSessionWhenWebContentsNotNull() { doReturn(mWebContents).when(mTab).getWebContents(); doReturn(mContentView).when(mTab).getContentView(); @@ -252,9 +330,14 @@ verify(observer, times(3)).onEnterFullscreen(mTab, fullscreenOptions); mFullscreenHtmlApiHandlerCompat.onExitFullscreen(mTab); verify(observer, times(3)).onExitFullscreen(mTab); + + assertEqualNumberOfEnterAndExitActivityFullscreenMode(3); } @Test + @Config( + shadows = {FullscreenShadowActivity.class}, + sdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public void testNoObserverWhenCanceledBeforeBeingInteractable() { doReturn(mWebContents).when(mTab).getWebContents(); doReturn(false).when(mTab).isUserInteractable(); @@ -271,9 +354,14 @@ verify(observer, never()).onEnterFullscreen(mTab, fullscreenOptions); verify(observer, never()).onExitFullscreen(mTab); + + assertEqualNumberOfEnterAndExitActivityFullscreenMode(0); } @Test + @Config( + shadows = {FullscreenShadowActivity.class}, + sdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public void testFullscreenObserverInTabNonInteractableState() { doReturn(mWebContents).when(mTab).getWebContents(); doReturn(false).when(mTab).isUserInteractable(); // Tab not interactable at first. @@ -297,6 +385,8 @@ verify(observer, times(1)).onExitFullscreen(mTab); mFullscreenHtmlApiHandlerCompat.destroy(); + + assertEqualNumberOfEnterAndExitActivityFullscreenMode(1); } @Test @@ -401,9 +491,13 @@ } @Test + @Config( + shadows = {FullscreenShadowActivity.class}, + sdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public void testFullscreenObserverNotifiedWhenActivityStopped() { mFullscreenHtmlApiHandlerCompat = - new FullscreenHtmlApiHandlerCompat(mActivity, mAreControlsHidden, true) { + new FullscreenHtmlApiHandlerCompat( + mActivity, mAreControlsHidden, true, mMultiWindowModeStateDispatcher) { @Override public void destroySelectActionMode(Tab tab) {} }; @@ -430,9 +524,14 @@ mFullscreenHtmlApiHandlerCompat.onActivityStateChange(mActivity, ActivityState.STOPPED); mFullscreenHtmlApiHandlerCompat.onExitFullscreen(mTab); verify(observer, times(1)).onExitFullscreen(mTab); + + assertEqualNumberOfEnterAndExitActivityFullscreenMode(1); } @Test + @Config( + shadows = {FullscreenShadowActivity.class}, + sdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public void testFullscreenObserverCalledOnceWhenExitPersistentFullscreenModeCalled() { doReturn(mWebContents).when(mTab).getWebContents(); doReturn(mContentView).when(mTab).getContentView(); @@ -461,5 +560,7 @@ mFullscreenHtmlApiHandlerCompat.onExitFullscreen(mTab); mFullscreenHtmlApiHandlerCompat.onExitFullscreen(mTab); verify(observer, times(1)).onExitFullscreen(mTab); + + assertEqualNumberOfEnterAndExitActivityFullscreenMode(1); } }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerLegacyUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerLegacyUnitTest.java index 919d992..338ca00 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerLegacyUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandlerLegacyUnitTest.java
@@ -30,8 +30,11 @@ import org.chromium.base.UserDataHost; import org.chromium.base.supplier.ObservableSupplierImpl; import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.Features; import org.chromium.cc.input.BrowserControlsState; import org.chromium.chrome.browser.ActivityTabProvider; +import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabBrowserControlsConstraintsHelper; import org.chromium.chrome.browser.tabmodel.TabModelSelector; @@ -39,6 +42,7 @@ import org.chromium.content_public.browser.WebContents; /** Unit tests for {@link FullscreenHtmlApiHandlerLegacy}. */ +@Features.EnableFeatures({ChromeFeatureList.DISPLAY_EDGE_TO_EDGE_FULLSCREEN}) @RunWith(BaseRobolectricTestRunner.class) public class FullscreenHtmlApiHandlerLegacyUnitTest { private static final int DEVICE_WIDTH = 900; @@ -53,6 +57,7 @@ @Mock private ContentView mContentView; @Mock private ActivityTabProvider mActivityTabProvider; @Mock private TabModelSelector mTabModelSelector; + @Mock private MultiWindowModeStateDispatcher mMultiWindowModeStateDispatcher; private FullscreenHtmlApiHandlerLegacy mFullscreenHtmlApiHandlerLegacy; private ObservableSupplierImpl<Boolean> mAreControlsHidden; @@ -66,7 +71,8 @@ mAreControlsHidden = new ObservableSupplierImpl<Boolean>(); mFullscreenHtmlApiHandlerLegacy = - new FullscreenHtmlApiHandlerLegacy(mActivity, mAreControlsHidden, false) { + new FullscreenHtmlApiHandlerLegacy( + mActivity, mAreControlsHidden, false, mMultiWindowModeStateDispatcher) { // This needs a PopupController, which isn't available in the test since we // can't mock statics in this version of mockito. Even if we could mock it, it // casts to WebContentsImpl and other things that we can't reference due to @@ -374,7 +380,8 @@ @Test public void testFullscreenObserverNotifiedWhenActivityStopped() { mFullscreenHtmlApiHandlerLegacy = - new FullscreenHtmlApiHandlerLegacy(mActivity, mAreControlsHidden, true) { + new FullscreenHtmlApiHandlerLegacy( + mActivity, mAreControlsHidden, true, mMultiWindowModeStateDispatcher) { @Override public void destroySelectActionMode(Tab tab) {} };
diff --git a/chrome/app/chrome_exe_main_mac.cc b/chrome/app/chrome_exe_main_mac.cc index 68f0f50..c016635b 100644 --- a/chrome/app/chrome_exe_main_mac.cc +++ b/chrome/app/chrome_exe_main_mac.cc
@@ -134,7 +134,7 @@ // revised. Hopefully a more elegant solution will become apparent before that's // required. #if !defined(DCHECK_ALWAYS_ON) -__attribute__((used)) const char kGrossPaddingForCrbug1300598[68 * 1024] = {}; +__attribute__((used)) const char kGrossPaddingForCrbug1300598[80 * 1024] = {}; #else // DCHECK builds are larger and therefore require less padding. See // https://crbug.com/1394196 for the calculations, and
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index c08353b5c..2fd52c4 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -2810,6 +2810,9 @@ <message name="IDS_KIOSK_APP_ERROR_USER_CANCEL" desc="Error message used when a kiosk app launch is canceled by user."> Kiosk application launch canceled. </message> + <message name="IDS_KIOSK_APP_ERROR_UNABLE_TO_LAUNCH_CHROME_APP" desc="Error message used when a kiosk session fails to launch due to the app being launched is a deprecated Chrome App."> + Can't launch unsupported Chrome app. Notify your administrator about this issue. + </message> <message name="IDS_KIOSK_EXTERNAL_UPDATE_IN_PROGRESS" desc="Message shown on screen when the system is updating kiosk apps from usb stick."> Please wait....Kiosk app is in the process of being updated. Do not remove the USB stick. </message>
diff --git a/chrome/app/chromeos_strings_grdp/IDS_KIOSK_APP_ERROR_UNABLE_TO_LAUNCH_CHROME_APP.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_KIOSK_APP_ERROR_UNABLE_TO_LAUNCH_CHROME_APP.png.sha1 new file mode 100644 index 0000000..06c838f6 --- /dev/null +++ b/chrome/app/chromeos_strings_grdp/IDS_KIOSK_APP_ERROR_UNABLE_TO_LAUNCH_CHROME_APP.png.sha1
@@ -0,0 +1 @@ +ff0c241b0ed08f6ac9d1a62bcf73d9b0501912d9 \ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index e13b327..2fd8235d 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -14476,24 +14476,12 @@ <message name="IDS_SYNC_ERROR_USER_MENU_TITLE" desc="Title of the sync/signin error header of desktop user menu."> Sync isn't working </message> - <message name="IDS_SYNC_ERROR_PASSWORDS_USER_MENU_TITLE" desc="Title of the sync/signin error header of desktop user menu for the case where the error only affects sync-ed passwords.."> - Password sync isn't working - </message> <message name="IDS_SYNC_ERROR_PASSWORDS_USER_MENU_ERROR_DESCRIPTION" desc="Error description of the sync/signin error header of desktop user menu."> Verify it's you to use and save passwords in your account, <ph name="ACCOUNT_EMAIL">$1<ex>elisa.beckett@gmail.com</ex></ph> </message> - <message name="IDS_SYNC_ERROR_PASSWORDS_USER_MENU_TITLE_SIGNED_IN_ONLY" desc="Title of the sync/signin error header of desktop user menu. Shown for users who are signed-in but not syncing."> - To continue saving passwords in your Google Account, verify it’s you - </message> - <message name="IDS_SYNC_ERROR_RECOVERABILITY_DEGRADED_FOR_EVERYTHING_USER_MENU_TITLE" desc="Title of a sync error header of desktop user menu that prompts the user to add recovery options."> - Make sure you can always access your sync data - </message> <message name="IDS_SYNC_ERROR_RECOVERABILITY_DEGRADED_FOR_PASSWORDS_USER_MENU_ERROR_DESCRIPTION" desc="Error description of a sync error header of desktop user menu that prompts the user to add recovery options."> Verify it's you to make sure you can always use the passwords in your account, <ph name="ACCOUNT_EMAIL">$1<ex>elisa.beckett@gmail.com</ex></ph> </message> - <message name="IDS_SYNC_ERROR_RECOVERABILITY_DEGRADED_FOR_PASSWORDS_USER_MENU_TITLE" desc="Title of a sync error header of desktop user menu that prompts the user to add recovery options."> - Make sure you can always access your saved passwords - </message> <message name="IDS_SYNC_NEEDS_VERIFICATION_BUBBLE_VIEW_TITLE" desc="Title in the sync error bubble view/notification for the case where sync needs to verify the user."> Sync needs to verify it's you </message>
diff --git a/chrome/app/generated_resources_grd/IDS_SYNC_ERROR_PASSWORDS_USER_MENU_TITLE_SIGNED_IN_ONLY.png.sha1 b/chrome/app/generated_resources_grd/IDS_SYNC_ERROR_PASSWORDS_USER_MENU_TITLE_SIGNED_IN_ONLY.png.sha1 deleted file mode 100644 index a769868..0000000 --- a/chrome/app/generated_resources_grd/IDS_SYNC_ERROR_PASSWORDS_USER_MENU_TITLE_SIGNED_IN_ONLY.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -f46af691d5a7acdc76991c666370325ffbee76f8 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_SYNC_ERROR_RECOVERABILITY_DEGRADED_FOR_EVERYTHING_USER_MENU_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_SYNC_ERROR_RECOVERABILITY_DEGRADED_FOR_EVERYTHING_USER_MENU_TITLE.png.sha1 deleted file mode 100644 index 385d201..0000000 --- a/chrome/app/generated_resources_grd/IDS_SYNC_ERROR_RECOVERABILITY_DEGRADED_FOR_EVERYTHING_USER_MENU_TITLE.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -9dbb28b3abd28a8046da9dfabdb8f786ef65c213 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_SYNC_ERROR_RECOVERABILITY_DEGRADED_FOR_PASSWORDS_USER_MENU_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_SYNC_ERROR_RECOVERABILITY_DEGRADED_FOR_PASSWORDS_USER_MENU_TITLE.png.sha1 deleted file mode 100644 index 6dbd2f47..0000000 --- a/chrome/app/generated_resources_grd/IDS_SYNC_ERROR_RECOVERABILITY_DEGRADED_FOR_PASSWORDS_USER_MENU_TITLE.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -a52e087aa4b3cb0c688d973c4df00af1652c968f \ No newline at end of file
diff --git a/chrome/app/theme/default_100_percent/common/autofill_ai_ffr_banner.png b/chrome/app/theme/default_100_percent/common/autofill_ai_ffr_banner.png new file mode 100644 index 0000000..54723ee --- /dev/null +++ b/chrome/app/theme/default_100_percent/common/autofill_ai_ffr_banner.png Binary files differ
diff --git a/chrome/app/theme/default_100_percent/common/autofill_ai_ffr_banner_dark.png b/chrome/app/theme/default_100_percent/common/autofill_ai_ffr_banner_dark.png new file mode 100644 index 0000000..2f02005d --- /dev/null +++ b/chrome/app/theme/default_100_percent/common/autofill_ai_ffr_banner_dark.png Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/autofill_ai_ffr_banner.png b/chrome/app/theme/default_200_percent/common/autofill_ai_ffr_banner.png new file mode 100644 index 0000000..0260ce6 --- /dev/null +++ b/chrome/app/theme/default_200_percent/common/autofill_ai_ffr_banner.png Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/autofill_ai_ffr_banner_dark.png b/chrome/app/theme/default_200_percent/common/autofill_ai_ffr_banner_dark.png new file mode 100644 index 0000000..2cbcf64 --- /dev/null +++ b/chrome/app/theme/default_200_percent/common/autofill_ai_ffr_banner_dark.png Binary files differ
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd index d3bcc05..5102753 100644 --- a/chrome/app/theme/theme_resources.grd +++ b/chrome/app/theme/theme_resources.grd
@@ -266,6 +266,8 @@ </if> <structure type="chrome_scaled_image" name="IDR_RESTORE_BUTTON_MASK" file="common/restore_button_mask.png" /> <if expr="not is_android"> + <structure type="chrome_scaled_image" name="IDR_AUTOFILL_AI_FFR_BANNER" file="common/autofill_ai_ffr_banner.png" /> + <structure type="chrome_scaled_image" name="IDR_AUTOFILL_AI_FFR_BANNER_DARK" file="common/autofill_ai_ffr_banner_dark.png" /> <structure type="chrome_scaled_image" name="IDR_SAFETY_TIP_ILLUSTRATION_DARK" file="common/safety_tip_illustration_dark.png" /> <structure type="chrome_scaled_image" name="IDR_SAFETY_TIP_ILLUSTRATION_LIGHT" file="common/safety_tip_illustration_light.png" /> <structure type="chrome_scaled_image" name="IDR_SAVED_PASSWORDS_NEUTRAL_STATE" file="common/passwords_neutral_state.png" />
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn index 28f85e10..ab5b009b 100644 --- a/chrome/app/vector_icons/BUILD.gn +++ b/chrome/app/vector_icons/BUILD.gn
@@ -150,7 +150,6 @@ "performance.icon", "performance_speedometer.icon", "person.icon", - "person_filled_padded_large.icon", "person_filled_padded_small.icon", "picture_in_picture_alt.icon", "print_menu.icon",
diff --git a/chrome/app/vector_icons/person_filled_padded_large.icon b/chrome/app/vector_icons/person_filled_padded_large.icon deleted file mode 100644 index e1a7cc0..0000000 --- a/chrome/app/vector_icons/person_filled_padded_large.icon +++ /dev/null
@@ -1,20 +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. - -// This differs from person_filled_padded_small in that it has even larger -// padding. -CANVAS_DIMENSIONS, 60, -MOVE_TO, 30, 20, -R_CUBIC_TO, -2.76, 0, -5, 2.24, -5, 5, -R_CUBIC_TO, 0, 2.76, 2.24, 5, 5, 5, -R_CUBIC_TO, 2.76, 0, 5, -2.24, 5, -5, -R_CUBIC_TO, 0, -2.76, -2.24, -5, -5, -5, -CLOSE, -R_MOVE_TO, 0, 11.25, -R_CUBIC_TO, -3.34, 0, -10, 1.68, -10, 5, -V_LINE_TO, 40, -R_H_LINE_TO, 20, -R_V_LINE_TO, -3.75, -R_CUBIC_TO, 0, -3.32, -6.66, -5, -10, -5, -CLOSE
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 58b3b7c..f5927b9 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -11283,16 +11283,6 @@ FEATURE_VALUE_TYPE( autofill::features::kAutofillEnableCvcStorageAndFillingEnhancement)}, - {"improved-signin-ui-on-desktop", - flag_descriptions::kImprovedSigninUIOnDesktopName, - flag_descriptions::kImprovedSigninUIOnDesktopDescription, - kOsMac | kOsWin | kOsLinux, - FEATURE_VALUE_TYPE(switches::kImprovedSigninUIOnDesktop)}, - - {"outline-silhouette-icon", flag_descriptions::kOutlineSilhouetteIconName, - flag_descriptions::kOutlineSilhouetteIconDescription, - kOsMac | kOsWin | kOsLinux, FEATURE_VALUE_TYPE(kOutlineSilhouetteIcon)}, - {"improved-settings-ui-on-desktop", flag_descriptions::kImprovedSettingsUIOnDesktopName, flag_descriptions::kImprovedSettingsUIOnDesktopDescription, @@ -12161,6 +12151,10 @@ {"android-theme-module", flag_descriptions::kAndroidThemeModuleName, flag_descriptions::kAndroidThemeModuleDescription, kOsAndroid, FEATURE_VALUE_TYPE(chrome::android::kAndroidThemeModule)}, + {"display-edge-to-edge-fullscreen", + flag_descriptions::kDisplayEdgeToEdgeFullscreenName, + flag_descriptions::kDisplayEdgeToEdgeFullscreenDescription, kOsAndroid, + FEATURE_VALUE_TYPE(features::kDisplayEdgeToEdgeFullscreen)}, #endif // BUILDFLAG(IS_ANDROID) #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \
diff --git a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.cc b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.cc index 0f855c4..f8217ea 100644 --- a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.cc +++ b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.cc
@@ -29,7 +29,8 @@ base::FEATURE_ENABLED_BY_DEFAULT); namespace { -constexpr auto kUserInstalledAndKiosk = base::MakeFixedFlatSet< +// TODO(crbug.com/413912653): Split the allowlists per context. +constexpr auto kUserInstalledAllowlist = base::MakeFixedFlatSet< std::string_view>( {"aakfkoilmhehmmadlkedfbcelkbamdkj", "aepgaekjheajlcifmpjcnpbjcencoefn", "afoipjmffplafpbfjopglheidddioiai", "afpnehpifljbjjplppeplamalioanmio", @@ -104,17 +105,31 @@ "pifpopligmljinioeacaccciabhbbpjo", "plhmjahmpikllpphfaoopdhnkbpffccm", "pnclfbefcgmenbbbpljbhbdacgkgkjlh", "ppkfnjlimknmjoaemnpidmdlfchhehel"}); +// TODO(crbug.com/383754553): Add the finalised list only in M138 builds. +constexpr auto kKioskSessionAllowlist = + base::MakeFixedFlatSet<std::string_view>({""}); + // The std::unordered_set<std::string_view> type has complex constructors and // for static variables it would require an exit-time destructor. For these // cases go/totw/110 suggests using NoDestructor to prevent the destructor from // running and avoid multi-thread race conditions. We do not risk memory leaks // because the allowlist are always valid while Chrome is running. -static base::NoDestructor<std::unordered_set<std::string_view>> - kTestAllowlistedApps; +static base::NoDestructor<std::unordered_set<std::string>> testAllowlistedApps; -bool IsAllowlisted(std::string_view app_id) { - return kUserInstalledAndKiosk.contains(app_id) || - kTestAllowlistedApps->contains(app_id); +static bool fakeKioskSessionForTesting = false; + +enum class AllowlistContext { UserInstalled, KioskSession }; + +bool IsAllowlisted(std::string_view app_id, AllowlistContext context) { + switch (context) { + case AllowlistContext::UserInstalled: + return kUserInstalledAllowlist.contains(app_id) || + testAllowlistedApps->contains(app_id.data()); + case AllowlistContext::KioskSession: + return kKioskSessionAllowlist.contains(app_id) || + kUserInstalledAllowlist.contains(app_id) || + testAllowlistedApps->contains(app_id.data()); + } } void ShowNotification(const extensions::Extension& app, Profile* profile) { @@ -155,7 +170,7 @@ DeprecationStatus HandleUserInstalledApp(const extensions::Extension& app, Profile* profile) { // TODO(crbug.com/379261516): Block the execution in M139. - if (IsAllowlisted(app.id())) { + if (IsAllowlisted(app.id(), AllowlistContext::UserInstalled)) { return DeprecationStatus::kLaunchAllowed; } @@ -170,7 +185,7 @@ DeprecationStatus HandleKioskSessionApp(const extensions::Extension& app, Profile* profile) { // TODO(crbug.com/379262711): Block the execution in M151. - if (base::FeatureList::IsEnabled(kAllowChromeAppsInKioskSessions)) { + if (IsAllowlisted(app.id(), AllowlistContext::KioskSession)) { return DeprecationStatus::kLaunchAllowed; } @@ -178,6 +193,10 @@ return DeprecationStatus::kLaunchAllowed; } + if (base::FeatureList::IsEnabled(kAllowChromeAppsInKioskSessions)) { + return DeprecationStatus::kLaunchAllowed; + } + return DeprecationStatus::kLaunchBlocked; } } // namespace @@ -191,21 +210,27 @@ return DeprecationStatus::kLaunchAllowed; } + if (chromeos::IsKioskSession() || fakeKioskSessionForTesting) { + return HandleKioskSessionApp(*app, profile); + } + if (IsUserInstalled(app_id, profile)) { return HandleUserInstalledApp(*app, profile); - } else if (chromeos::IsKioskSession()) { - return HandleKioskSessionApp(*app, profile); } return DeprecationStatus::kLaunchAllowed; } void AddAppToAllowlistForTesting(std::string_view app_id) { - kTestAllowlistedApps->emplace(app_id); + testAllowlistedApps->emplace(app_id.data()); } void ResetAllowlistForTesting() { - kTestAllowlistedApps->clear(); + testAllowlistedApps->clear(); +} + +void SetKioskSessionForTesting() { + fakeKioskSessionForTesting = true; } } // namespace apps::chrome_app_deprecation
diff --git a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.h b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.h index fdca53d..9d26e046 100644 --- a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.h +++ b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation.h
@@ -23,6 +23,7 @@ void AddAppToAllowlistForTesting(std::string_view app_id); void ResetAllowlistForTesting(); +void SetKioskSessionForTesting(); BASE_DECLARE_FEATURE(kAllowUserInstalledChromeApps); BASE_DECLARE_FEATURE(kAllowChromeAppsInKioskSessions);
diff --git a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_browsertest.cc b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_browsertest.cc index 95edc866..be9bec3 100644 --- a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_browsertest.cc +++ b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_browsertest.cc
@@ -4,20 +4,34 @@ #include "chrome/browser/apps/app_service/publishers/chrome_app_deprecation.h" +#include "base/notreached.h" #include "chrome/browser/apps/app_service/app_launch_params.h" #include "chrome/browser/apps/app_service/app_service_proxy.h" #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" #include "chrome/browser/apps/platform_apps/app_browsertest_util.h" +#include "chrome/browser/ash/app_mode/kiosk_app_launch_error.h" +#include "chrome/browser/ash/app_mode/kiosk_controller.h" +#include "chrome/browser/ash/app_mode/test/fake_cws_chrome_apps.h" +#include "chrome/browser/ash/app_mode/test/kiosk_mixin.h" +#include "chrome/browser/ash/app_mode/test/kiosk_test_utils.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/ui/browser.h" +#include "chrome/common/pref_names.h" +#include "components/prefs/pref_service.h" #include "content/public/test/browser_test.h" +#include "extensions/test/test_extension_dir.h" #include "ui/message_center/message_center.h" +using ash::KioskMixin; +using ash::kiosk::test::LaunchAppManually; +using ash::kiosk::test::OfflineEnabledChromeAppV1; +using ash::kiosk::test::WaitKioskLaunched; + namespace apps { using extensions::Extension; -class DeprecationControllerBrowserTest +class ChromeAppDeprecationUserInstalledAppsBrowserTest : public extensions::PlatformAppBrowserTest { protected: const Extension* LoadPlatformApp() { @@ -39,8 +53,8 @@ } }; -IN_PROC_BROWSER_TEST_F(DeprecationControllerBrowserTest, - UserInstalledAppNotAllowlisted) { +IN_PROC_BROWSER_TEST_F(ChromeAppDeprecationUserInstalledAppsBrowserTest, + NotAllowlisted) { auto* center = message_center::MessageCenter::Get(); auto notifications_count = center->GetNotifications().size(); @@ -51,8 +65,8 @@ ASSERT_TRUE(center->GetNotifications().size() == notifications_count + 1); } -IN_PROC_BROWSER_TEST_F(DeprecationControllerBrowserTest, - AllowlistedUserInstalledApp) { +IN_PROC_BROWSER_TEST_F(ChromeAppDeprecationUserInstalledAppsBrowserTest, + Allowlisted) { auto* center = message_center::MessageCenter::Get(); auto notifications_count = center->GetNotifications().size(); @@ -64,4 +78,106 @@ RunPlatformApp(app); ASSERT_TRUE(center->GetNotifications().size() == notifications_count); } + +class ChromeAppDeprecationKioskSessionBrowserTest + : public MixinBasedInProcessBrowserTest { + public: + ChromeAppDeprecationKioskSessionBrowserTest() = default; + ChromeAppDeprecationKioskSessionBrowserTest( + const ChromeAppDeprecationKioskSessionBrowserTest&) = delete; + ChromeAppDeprecationKioskSessionBrowserTest& operator=( + const ChromeAppDeprecationKioskSessionBrowserTest&) = delete; + ~ChromeAppDeprecationKioskSessionBrowserTest() override = default; + + KioskMixin kiosk_{ + &mixin_host_, + /*cached_configuration=*/KioskMixin::Config{ + /*name=*/"ChromeApp", + KioskMixin::AutoLaunchAccount{OfflineEnabledChromeAppV1().account_id}, + {OfflineEnabledChromeAppV1()}}}; + + protected: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +class KioskDefaultFeatureFlag + : public ChromeAppDeprecationKioskSessionBrowserTest { + public: + KioskDefaultFeatureFlag() { + scoped_feature_list_.InitWithEmptyFeatureAndFieldTrialLists(); + } + KioskDefaultFeatureFlag(const KioskDefaultFeatureFlag&) = delete; + KioskDefaultFeatureFlag& operator=(const KioskDefaultFeatureFlag&) = delete; + ~KioskDefaultFeatureFlag() override = default; +}; + +IN_PROC_BROWSER_TEST_F(KioskDefaultFeatureFlag, FeatureFlag) { + ASSERT_TRUE(base::FeatureList::IsEnabled( + chrome_app_deprecation::kAllowChromeAppsInKioskSessions)); + ASSERT_TRUE(WaitKioskLaunched()); +} + +class KioskEnabledFeatureFlag + : public ChromeAppDeprecationKioskSessionBrowserTest { + public: + KioskEnabledFeatureFlag() { + scoped_feature_list_.InitAndEnableFeature( + chrome_app_deprecation::kAllowChromeAppsInKioskSessions); + } + KioskEnabledFeatureFlag(const KioskEnabledFeatureFlag&) = delete; + KioskEnabledFeatureFlag& operator=(const KioskEnabledFeatureFlag&) = delete; + ~KioskEnabledFeatureFlag() override = default; +}; + +IN_PROC_BROWSER_TEST_F(KioskEnabledFeatureFlag, FeatureFlag) { + ASSERT_TRUE(base::FeatureList::IsEnabled( + chrome_app_deprecation::kAllowChromeAppsInKioskSessions)); + ASSERT_TRUE(WaitKioskLaunched()); +} + +class KioskDisabledFeatureFlag + : public ChromeAppDeprecationKioskSessionBrowserTest { + public: + KioskDisabledFeatureFlag() { + scoped_feature_list_.InitAndDisableFeature( + chrome_app_deprecation::kAllowChromeAppsInKioskSessions); + } + KioskDisabledFeatureFlag(const KioskDisabledFeatureFlag&) = delete; + KioskDisabledFeatureFlag& operator=(const KioskDisabledFeatureFlag&) = delete; + ~KioskDisabledFeatureFlag() override = default; +}; + +IN_PROC_BROWSER_TEST_F(KioskDisabledFeatureFlag, FeatureFlag) { + ASSERT_FALSE(base::FeatureList::IsEnabled( + chrome_app_deprecation::kAllowChromeAppsInKioskSessions)); + + RunUntilBrowserProcessQuits(); + EXPECT_EQ(ash::KioskAppLaunchError::Error::kChromeAppDeprecated, + ash::KioskAppLaunchError::Get()); + EXPECT_FALSE(ash::KioskController::Get().IsSessionStarting()); +} + +class KioskDisabledFeatureFlagWithAllowlistedApp + : public ChromeAppDeprecationKioskSessionBrowserTest { + public: + KioskDisabledFeatureFlagWithAllowlistedApp() { + scoped_feature_list_.InitAndDisableFeature( + chrome_app_deprecation::kAllowChromeAppsInKioskSessions); + chrome_app_deprecation::AddAppToAllowlistForTesting( + OfflineEnabledChromeAppV1().app_id); + } + KioskDisabledFeatureFlagWithAllowlistedApp( + const KioskDisabledFeatureFlagWithAllowlistedApp&) = delete; + KioskDisabledFeatureFlagWithAllowlistedApp& operator=( + const KioskDisabledFeatureFlagWithAllowlistedApp&) = delete; + ~KioskDisabledFeatureFlagWithAllowlistedApp() override = default; +}; + +IN_PROC_BROWSER_TEST_F(KioskDisabledFeatureFlagWithAllowlistedApp, + AllowlistedApp) { + ASSERT_FALSE(base::FeatureList::IsEnabled( + chrome_app_deprecation::kAllowChromeAppsInKioskSessions)); + + ASSERT_TRUE(WaitKioskLaunched()); +} } // namespace apps
diff --git a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_unittest.cc b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_unittest.cc index 64b5c8e..1f3aaf5 100644 --- a/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_unittest.cc +++ b/chrome/browser/apps/app_service/publishers/chrome_app_deprecation_unittest.cc
@@ -8,6 +8,7 @@ #include "chrome/browser/extensions/chrome_test_extension_loader.h" #include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/extension_service_test_base.h" +#include "chrome/common/pref_names.h" #include "extensions/browser/extension_registrar.h" #include "extensions/browser/extension_registry.h" #include "extensions/common/extension_builder.h" @@ -24,7 +25,7 @@ namespace apps::chrome_app_deprecation { -class DeprecationControllerTest : public extensions::ExtensionServiceTestBase { +class ChromeAppDeprecationTest : public extensions::ExtensionServiceTestBase { protected: void SetUp() override { ExtensionServiceTestBase::SetUp(); @@ -66,18 +67,15 @@ scoped_refptr<const Extension> app_; }; -TEST_F(DeprecationControllerTest, HandleDeprecationDefaultFeatureFlag) { +TEST_F(ChromeAppDeprecationTest, DefaultFeatureFlag) { scoped_feature_list_.InitWithEmptyFeatureAndFieldTrialLists(); ASSERT_TRUE(base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps)); EXPECT_EQ(HandleDeprecation(app_->id(), profile()), DeprecationStatus::kLaunchAllowed); - - EXPECT_EQ(HandleDeprecation(app_->id(), profile()), - DeprecationStatus::kLaunchAllowed); } -TEST_F(DeprecationControllerTest, HandleDeprecationDisabledFeatureFlag) { +TEST_F(ChromeAppDeprecationTest, DisabledFeatureFlag) { scoped_feature_list_.InitAndDisableFeature(kAllowUserInstalledChromeApps); ASSERT_FALSE(base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps)); @@ -85,7 +83,7 @@ DeprecationStatus::kLaunchBlocked); } -TEST_F(DeprecationControllerTest, HandleDeprecationEnabledFeatureFlag) { +TEST_F(ChromeAppDeprecationTest, EnabledFeatureFlag) { scoped_feature_list_.InitAndEnableFeature(kAllowUserInstalledChromeApps); ASSERT_TRUE(base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps)); @@ -93,10 +91,64 @@ DeprecationStatus::kLaunchAllowed); } -class DeprecationControllerAllowlistTest : public DeprecationControllerTest { +class ChromeAppDeprecationKioskTest : public ChromeAppDeprecationTest { + void SetUp() override { + ChromeAppDeprecationTest::SetUp(); + + SetKioskSessionForTesting(); + } +}; + +TEST_F(ChromeAppDeprecationKioskTest, DefaultFeatureFlag) { + scoped_feature_list_.InitWithEmptyFeatureAndFieldTrialLists(); + ASSERT_TRUE(base::FeatureList::IsEnabled(kAllowChromeAppsInKioskSessions)); + + EXPECT_EQ(HandleDeprecation(app_->id(), profile()), + DeprecationStatus::kLaunchAllowed); +} + +TEST_F(ChromeAppDeprecationKioskTest, DisabledFeatureFlag) { + scoped_feature_list_.InitAndDisableFeature(kAllowChromeAppsInKioskSessions); + ASSERT_FALSE(base::FeatureList::IsEnabled(kAllowChromeAppsInKioskSessions)); + + EXPECT_EQ(HandleDeprecation(app_->id(), profile()), + DeprecationStatus::kLaunchBlocked); +} + +TEST_F(ChromeAppDeprecationKioskTest, EnabledFeatureFlag) { + scoped_feature_list_.InitAndEnableFeature(kAllowChromeAppsInKioskSessions); + ASSERT_TRUE(base::FeatureList::IsEnabled(kAllowChromeAppsInKioskSessions)); + + EXPECT_EQ(HandleDeprecation(app_->id(), profile()), + DeprecationStatus::kLaunchAllowed); +} + +TEST_F(ChromeAppDeprecationKioskTest, DisabledFeatureFlagDefaultPolicy) { + scoped_feature_list_.InitAndDisableFeature(kAllowChromeAppsInKioskSessions); + ASSERT_FALSE(base::FeatureList::IsEnabled(kAllowChromeAppsInKioskSessions)); + ASSERT_FALSE( + profile()->GetPrefs()->GetBoolean(prefs::kKioskChromeAppsForceAllowed)); + + EXPECT_EQ(HandleDeprecation(app_->id(), profile()), + DeprecationStatus::kLaunchBlocked); +} + +TEST_F(ChromeAppDeprecationKioskTest, DisabledFeatureFlagOverridenByPolicy) { + scoped_feature_list_.InitAndDisableFeature(kAllowChromeAppsInKioskSessions); + ASSERT_FALSE(base::FeatureList::IsEnabled(kAllowChromeAppsInKioskSessions)); + + profile()->GetPrefs()->SetBoolean(prefs::kKioskChromeAppsForceAllowed, true); + ASSERT_TRUE( + profile()->GetPrefs()->GetBoolean(prefs::kKioskChromeAppsForceAllowed)); + + EXPECT_EQ(HandleDeprecation(app_->id(), profile()), + DeprecationStatus::kLaunchAllowed); +} + +class ChromeAppDeprecationAllowlistTest : public ChromeAppDeprecationTest { protected: void SetUp() override { - DeprecationControllerTest::SetUp(); + ChromeAppDeprecationTest::SetUp(); AddAppToAllowlistForTesting(app_->id()); } @@ -104,12 +156,11 @@ void TearDown() override { ResetAllowlistForTesting(); - DeprecationControllerTest::TearDown(); + ChromeAppDeprecationTest::TearDown(); } }; -TEST_F(DeprecationControllerAllowlistTest, - HandleDeprecationDefaultFeatureFlag) { +TEST_F(ChromeAppDeprecationAllowlistTest, DefaultFeatureFlag) { scoped_feature_list_.InitWithEmptyFeatureAndFieldTrialLists(); ASSERT_TRUE(base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps)); @@ -117,8 +168,7 @@ DeprecationStatus::kLaunchAllowed); } -TEST_F(DeprecationControllerAllowlistTest, - HandleDeprecationDisabledFeatureFlag) { +TEST_F(ChromeAppDeprecationAllowlistTest, DisabledFeatureFlag) { scoped_feature_list_.InitAndDisableFeature(kAllowUserInstalledChromeApps); ASSERT_FALSE(base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps)); @@ -126,8 +176,7 @@ DeprecationStatus::kLaunchAllowed); } -TEST_F(DeprecationControllerAllowlistTest, - HandleDeprecationEnabledFeatureFlag) { +TEST_F(ChromeAppDeprecationAllowlistTest, EnabledFeatureFlag) { scoped_feature_list_.InitAndEnableFeature(kAllowUserInstalledChromeApps); ASSERT_TRUE(base::FeatureList::IsEnabled(kAllowUserInstalledChromeApps));
diff --git a/chrome/browser/ash/app_mode/auto_sleep/BUILD.gn b/chrome/browser/ash/app_mode/auto_sleep/BUILD.gn index e9b33aa..1397acb 100644 --- a/chrome/browser/ash/app_mode/auto_sleep/BUILD.gn +++ b/chrome/browser/ash/app_mode/auto_sleep/BUILD.gn
@@ -85,6 +85,7 @@ ":test_support", "//chrome/browser:browser_process", "//chrome/browser/ash/app_mode", + "//chrome/browser/ash/app_mode/test:test_support", "//chrome/browser/ash/login/app_mode/test:test_support", "//chrome/browser/devtools:test_support", "//chrome/browser/ui/ash/system_web_apps",
diff --git a/chrome/browser/ash/app_mode/auto_sleep/device_weekly_scheduled_suspend_controller_browsertest.cc b/chrome/browser/ash/app_mode/auto_sleep/device_weekly_scheduled_suspend_controller_browsertest.cc index c1fc189..2a3f7352 100644 --- a/chrome/browser/ash/app_mode/auto_sleep/device_weekly_scheduled_suspend_controller_browsertest.cc +++ b/chrome/browser/ash/app_mode/auto_sleep/device_weekly_scheduled_suspend_controller_browsertest.cc
@@ -5,12 +5,11 @@ #include "chrome/browser/ash/app_mode/auto_sleep/device_weekly_scheduled_suspend_controller.h" #include <cstddef> -#include <functional> #include <memory> #include <utility> -#include "base/functional/bind.h" #include "base/functional/callback_helpers.h" +#include "base/test/bind.h" #include "base/test/scoped_mock_time_message_loop_task_runner.h" #include "base/test/test_mock_time_task_runner.h" #include "base/time/time.h" @@ -19,9 +18,11 @@ #include "chrome/browser/ash/app_mode/auto_sleep/weekly_interval_timer.h" #include "chrome/browser/ash/app_mode/kiosk_controller.h" #include "chrome/browser/ash/app_mode/kiosk_system_session.h" -#include "chrome/browser/ash/login/app_mode/test/web_kiosk_base_test.h" +#include "chrome/browser/ash/app_mode/test/kiosk_mixin.h" +#include "chrome/browser/ash/app_mode/test/kiosk_test_utils.h" #include "chrome/browser/browser_process.h" #include "chrome/common/pref_names.h" +#include "chrome/test/base/mixin_based_in_process_browser_test.h" #include "chrome/test/base/testing_browser_process.h" #include "chromeos/ash/components/policy/weekly_time/weekly_time.h" #include "chromeos/dbus/power/fake_power_manager_client.h" @@ -31,6 +32,8 @@ namespace ash { +using kiosk::test::WaitKioskLaunched; + namespace { using DayOfWeek = DeviceWeeklyScheduledSuspendTestPolicyBuilder::DayOfWeek; @@ -42,6 +45,13 @@ return start_weekly_time.GetDurationTo(end); } +// Sets `schedule_list` as prefs for `kDeviceWeeklyScheduledSuspend` in local +// state, simulating a policy change. +void SetPrefInLocalState(base::Value::List schedule_list) { + g_browser_process->local_state()->SetList( + prefs::kDeviceWeeklyScheduledSuspend, std::move(schedule_list)); +} + // Scoped test helper that is responsible for the following things: // - Mocking time by setting up the dependencies on construction and cleaning // them up to prevent dangling pointers. @@ -95,58 +105,73 @@ base::ScopedMockTimeMessageLoopTaskRunner task_runner_; }; -} // namespace - -class DeviceWeeklyScheduledSuspendControllerTest : public WebKioskBaseTest { +// Helper mixin to encapsulate `FakePowerManagerClient` setup and functionality. +class FakePowerManagerMixin : public InProcessBrowserTestMixin { public: - // WebKioskBaseTest: - void SetUpOnMainThread() override { - WebKioskBaseTest::SetUpOnMainThread(); - chromeos::FakePowerManagerClient::Get()->set_user_activity_callback( - base::BindRepeating([](int& count) { ++count; }, - std::ref(user_activity_calls_))); + explicit FakePowerManagerMixin(InProcessBrowserTestMixinHost* host) + : InProcessBrowserTestMixin(host) {} + FakePowerManagerMixin(const FakePowerManagerMixin&) = delete; + FakePowerManagerMixin(FakePowerManagerMixin&&) = delete; + ~FakePowerManagerMixin() override = default; - InitializeRegularOnlineKiosk(); - ASSERT_TRUE(KioskController::Get().GetKioskSystemSession()); + void SetUpOnMainThread() override { + chromeos::FakePowerManagerClient::Get()->set_user_activity_callback( + base::BindLambdaForTesting([this] { ++user_activity_calls_; })); } void TearDownOnMainThread() override { chromeos::FakePowerManagerClient::Get()->set_user_activity_callback( base::NullCallback()); - WebKioskBaseTest::TearDownOnMainThread(); - } - - // Updates the policy preferences which in turn trigger the pref observers in - // the controller. - void UpdatePolicyPref(base::Value::List schedule_list) { - g_browser_process->local_state()->SetList( - prefs::kDeviceWeeklyScheduledSuspend, std::move(schedule_list)); } void SimulateResumeSuspend() { chromeos::FakePowerManagerClient::Get()->SendDarkSuspendImminent(); } - int user_activity_calls() { return user_activity_calls_; } + int user_activity_calls() const { return user_activity_calls_; } private: int user_activity_calls_ = 0; }; -IN_PROC_BROWSER_TEST_F(DeviceWeeklyScheduledSuspendControllerTest, +} // namespace + +class DeviceWeeklyScheduledSuspendControllerTest + : public MixinBasedInProcessBrowserTest, + public testing::WithParamInterface<KioskMixin::Config> { + public: + DeviceWeeklyScheduledSuspendControllerTest() = default; + DeviceWeeklyScheduledSuspendControllerTest( + const DeviceWeeklyScheduledSuspendControllerTest&) = delete; + DeviceWeeklyScheduledSuspendControllerTest( + DeviceWeeklyScheduledSuspendControllerTest&&) = delete; + ~DeviceWeeklyScheduledSuspendControllerTest() override = default; + + void SetUpOnMainThread() override { + MixinBasedInProcessBrowserTest::SetUpOnMainThread(); + ASSERT_TRUE(WaitKioskLaunched()); + } + + FakePowerManagerMixin power_manager_{&mixin_host_}; + KioskMixin kiosk_{&mixin_host_, + /*cached_configuration=*/GetParam()}; +}; + +IN_PROC_BROWSER_TEST_P(DeviceWeeklyScheduledSuspendControllerTest, SuspendControllerExistOnKioskStartUp) { ASSERT_TRUE(KioskController::Get() .GetKioskSystemSession() ->device_weekly_scheduled_suspend_controller_for_testing()); } -IN_PROC_BROWSER_TEST_F(DeviceWeeklyScheduledSuspendControllerTest, +IN_PROC_BROWSER_TEST_P(DeviceWeeklyScheduledSuspendControllerTest, SuspendAndWakeTest) { ScopedMockTimeScheduledSuspendTestHelper helper; auto* power_client = chromeos::FakePowerManagerClient::Get(); EXPECT_EQ(power_client->num_request_suspend_calls(), 0); - EXPECT_EQ(user_activity_calls(), 0); + EXPECT_EQ(power_manager_.user_activity_calls(), 0); + auto policy_builder = DeviceWeeklyScheduledSuspendTestPolicyBuilder().AddWeeklySuspendInterval( DayOfWeek::MONDAY, base::Hours(0), DayOfWeek::MONDAY, base::Hours(9)); @@ -157,28 +182,28 @@ GetDuration(clock->Now(), intervals[0]->start()) - base::Minutes(5); helper.task_runner()->FastForwardBy(duration); - UpdatePolicyPref(policy_builder.GetAsPrefValue()); + SetPrefInLocalState(policy_builder.GetAsPrefValue()); EXPECT_EQ(power_client->num_request_suspend_calls(), 0); - EXPECT_EQ(user_activity_calls(), 0); + EXPECT_EQ(power_manager_.user_activity_calls(), 0); helper.FastForwardTimeTo(intervals[0]->start()); EXPECT_EQ(power_client->num_request_suspend_calls(), 1); helper.FastForwardTimeTo(intervals[0]->end()); - SimulateResumeSuspend(); + power_manager_.SimulateResumeSuspend(); - EXPECT_EQ(user_activity_calls(), 1); + EXPECT_EQ(power_manager_.user_activity_calls(), 1); } -IN_PROC_BROWSER_TEST_F(DeviceWeeklyScheduledSuspendControllerTest, +IN_PROC_BROWSER_TEST_P(DeviceWeeklyScheduledSuspendControllerTest, MultipleSuspendAndWakeTest) { ScopedMockTimeScheduledSuspendTestHelper helper; auto* power_client = chromeos::FakePowerManagerClient::Get(); EXPECT_EQ(power_client->num_request_suspend_calls(), 0); - EXPECT_EQ(user_activity_calls(), 0); + EXPECT_EQ(power_manager_.user_activity_calls(), 0); auto builder = DeviceWeeklyScheduledSuspendTestPolicyBuilder() .AddWeeklySuspendInterval(DayOfWeek::MONDAY, base::Hours(0), @@ -195,35 +220,36 @@ helper.task_runner()->FastForwardBy(duration); EXPECT_EQ(power_client->num_request_suspend_calls(), 0); - UpdatePolicyPref(builder.GetAsPrefValue()); + SetPrefInLocalState(builder.GetAsPrefValue()); helper.FastForwardTimeTo(intervals[0]->start()); EXPECT_EQ(power_client->num_request_suspend_calls(), 1); helper.FastForwardTimeTo(intervals[0]->end()); - SimulateResumeSuspend(); - EXPECT_EQ(user_activity_calls(), 1); + power_manager_.SimulateResumeSuspend(); + EXPECT_EQ(power_manager_.user_activity_calls(), 1); helper.FastForwardTimeTo(intervals[1]->start()); EXPECT_EQ(power_client->num_request_suspend_calls(), 2); helper.FastForwardTimeTo(intervals[1]->end()); - SimulateResumeSuspend(); - EXPECT_EQ(user_activity_calls(), 2); + power_manager_.SimulateResumeSuspend(); + EXPECT_EQ(power_manager_.user_activity_calls(), 2); helper.FastForwardTimeTo(intervals[2]->start()); EXPECT_EQ(power_client->num_request_suspend_calls(), 3); helper.FastForwardTimeTo(intervals[2]->end()); - SimulateResumeSuspend(); - EXPECT_EQ(user_activity_calls(), 3); + power_manager_.SimulateResumeSuspend(); + EXPECT_EQ(power_manager_.user_activity_calls(), 3); EXPECT_EQ(power_client->num_request_suspend_calls(), static_cast<int>(intervals.size())); - EXPECT_EQ(user_activity_calls(), static_cast<int>(intervals.size())); + EXPECT_EQ(power_manager_.user_activity_calls(), + static_cast<int>(intervals.size())); } -IN_PROC_BROWSER_TEST_F(DeviceWeeklyScheduledSuspendControllerTest, +IN_PROC_BROWSER_TEST_P(DeviceWeeklyScheduledSuspendControllerTest, ManualWakeDuringIntervalClearsState) { ScopedMockTimeScheduledSuspendTestHelper helper; @@ -239,7 +265,7 @@ GetDuration(clock->Now(), intervals[0]->start()) - base::Minutes(5); helper.task_runner()->FastForwardBy(duration); - UpdatePolicyPref(policy_builder.GetAsPrefValue()); + SetPrefInLocalState(policy_builder.GetAsPrefValue()); EXPECT_EQ(power_client->num_request_suspend_calls(), 0); @@ -258,18 +284,18 @@ // Confirm that subsequent resume events will not cause // unnecessary user activity calls to wake the device when we are at the end // of the interval. - SimulateResumeSuspend(); + power_manager_.SimulateResumeSuspend(); - EXPECT_EQ(user_activity_calls(), 0); + EXPECT_EQ(power_manager_.user_activity_calls(), 0); } -IN_PROC_BROWSER_TEST_F(DeviceWeeklyScheduledSuspendControllerTest, +IN_PROC_BROWSER_TEST_P(DeviceWeeklyScheduledSuspendControllerTest, SuspendAndWakeRepeatsEveryWeekTest) { ScopedMockTimeScheduledSuspendTestHelper helper; auto* power_client = chromeos::FakePowerManagerClient::Get(); EXPECT_EQ(power_client->num_request_suspend_calls(), 0); - EXPECT_EQ(user_activity_calls(), 0); + EXPECT_EQ(power_manager_.user_activity_calls(), 0); auto policy_builder = DeviceWeeklyScheduledSuspendTestPolicyBuilder().AddWeeklySuspendInterval( DayOfWeek::SATURDAY, base::Hours(0), DayOfWeek::SATURDAY, @@ -281,10 +307,10 @@ GetDuration(clock->Now(), intervals[0]->start()) - base::Minutes(5); helper.task_runner()->FastForwardBy(duration); - UpdatePolicyPref(policy_builder.GetAsPrefValue()); + SetPrefInLocalState(policy_builder.GetAsPrefValue()); EXPECT_EQ(power_client->num_request_suspend_calls(), 0); - EXPECT_EQ(user_activity_calls(), 0); + EXPECT_EQ(power_manager_.user_activity_calls(), 0); helper.FastForwardTimeTo(intervals[0]->start()); EXPECT_EQ(power_client->num_request_suspend_calls(), 1); @@ -297,4 +323,10 @@ EXPECT_EQ(power_client->num_request_suspend_calls(), 2); } +INSTANTIATE_TEST_SUITE_P( + All, + DeviceWeeklyScheduledSuspendControllerTest, + testing::ValuesIn(KioskMixin::ConfigsToAutoLaunchEachAppType()), + KioskMixin::ConfigName); + } // namespace ash
diff --git a/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc b/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc index 96d9bd12..8b3e94d 100644 --- a/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc +++ b/chrome/browser/ash/app_mode/kiosk_app_launch_error.cc
@@ -71,6 +71,9 @@ case Error::kExtensionsPolicyInvalid: return l10n_util::GetStringUTF8( IDS_KIOSK_APP_ERROR_EXTENSIONS_POLICY_INVALID); + case Error::kChromeAppDeprecated: + return l10n_util::GetStringUTF8( + IDS_KIOSK_APP_ERROR_UNABLE_TO_LAUNCH_CHROME_APP); } NOTREACHED() << "Unknown kiosk app launch error, error="
diff --git a/chrome/browser/ash/app_mode/kiosk_app_launch_error.h b/chrome/browser/ash/app_mode/kiosk_app_launch_error.h index 040422ab..83f7886d 100644 --- a/chrome/browser/ash/app_mode/kiosk_app_launch_error.h +++ b/chrome/browser/ash/app_mode/kiosk_app_launch_error.h
@@ -39,7 +39,8 @@ kUserNotAllowlisted = 16, // LoginPerformer disallowed this user. // kLacrosDataMigrationStarted = 17, // Deprecated // kLacrosBackwardDataMigrationStarted = 18, // Deprecated - kMaxValue = kUserNotAllowlisted, // Max value of errors. + kChromeAppDeprecated = 19, + kMaxValue = kChromeAppDeprecated, // Max value of errors. }; // Returns a message for given `error`.
diff --git a/chrome/browser/ash/app_mode/startup_app_launcher.cc b/chrome/browser/ash/app_mode/startup_app_launcher.cc index edd1cd49..3ceb021 100644 --- a/chrome/browser/ash/app_mode/startup_app_launcher.cc +++ b/chrome/browser/ash/app_mode/startup_app_launcher.cc
@@ -239,6 +239,9 @@ OnLaunchFailure(KioskAppLaunchError::Error::kUnableToLaunch); } return; + case ChromeKioskAppLauncher::LaunchResult::kChromeAppDeprecated: + OnLaunchFailure(KioskAppLaunchError::Error::kChromeAppDeprecated); + return; case ChromeKioskAppLauncher::LaunchResult::kUnknown: SYSLOG(ERROR) << "Received unknown LaunchResult"; OnLaunchFailure(KioskAppLaunchError::Error::kUnableToLaunch);
diff --git a/chrome/browser/ash/file_manager/copy_or_move_io_task_policy_unittest.cc b/chrome/browser/ash/file_manager/copy_or_move_io_task_policy_unittest.cc index 7a665fd..f034468 100644 --- a/chrome/browser/ash/file_manager/copy_or_move_io_task_policy_unittest.cc +++ b/chrome/browser/ash/file_manager/copy_or_move_io_task_policy_unittest.cc
@@ -209,7 +209,7 @@ file_system_context_ = storage::CreateFileSystemContextForTesting( nullptr, source_destination_testing_helper_->GetTempDirPath()); - enterprise_connectors::FileTransferAnalysisDelegate::SetFactorForTesting( + enterprise_connectors::FileTransferAnalysisDelegate::SetFactoryForTesting( base::BindRepeating( [](base::RepeatingCallback<void( enterprise_connectors::MockFileTransferAnalysisDelegate*,
diff --git a/chrome/browser/ash/file_manager/file_manager_policy_browsertest.cc b/chrome/browser/ash/file_manager/file_manager_policy_browsertest.cc index b719d0c..a1596b41 100644 --- a/chrome/browser/ash/file_manager/file_manager_policy_browsertest.cc +++ b/chrome/browser/ash/file_manager/file_manager_policy_browsertest.cc
@@ -8,6 +8,7 @@ #include "base/test/bind.h" #include "base/test/gmock_callback_support.h" #include "base/test/mock_callback.h" +#include "base/test/scoped_feature_list.h" #include "base/test/test_mock_time_task_runner.h" #include "build/config/coverage/buildflags.h" #include "chrome/browser/ash/file_manager/file_manager_browsertest_base.h" @@ -42,6 +43,7 @@ #include "components/file_access/test/mock_scoped_file_access_delegate.h" #include "components/policy/core/common/cloud/mock_cloud_policy_client.h" #include "components/prefs/pref_service.h" +#include "components/safe_browsing/core/common/features.h" #include "components/signin/public/identity_manager/identity_test_environment.h" #include "content/public/test/browser_test.h" #include "storage/browser/file_system/external_mount_points.h" @@ -413,7 +415,7 @@ return &set; } -// Base class for Enterprise connectrs setup needed for browsertests. +// Base class for Enterprise connectors setup needed for browsertests. class FileTransferConnectorFilesAppBrowserTestBase { public: FileTransferConnectorFilesAppBrowserTestBase( @@ -460,7 +462,7 @@ } void ScanningHasCompletedCallback() { - DCHECK(run_loop_) + DCHECK(scanning_run_loop_) << "run loop not configured, missing call to `setupScanningRunLoop`"; ++finished_file_transfer_analysis_delegates_; DCHECK_LE(finished_file_transfer_analysis_delegates_, @@ -470,7 +472,7 @@ expected_number_of_file_transfer_analysis_delegates_) { // If all FileTransferAnalysisDelegates finished, scanning has been // completed. - run_loop_->QuitClosure().Run(); + scanning_run_loop_->QuitClosure().Run(); } } @@ -547,7 +549,7 @@ base::Unretained(this), *source, *destination))); // Setup FileTransferAnalysisDelegate mock. - enterprise_connectors::FileTransferAnalysisDelegate::SetFactorForTesting( + enterprise_connectors::FileTransferAnalysisDelegate::SetFactoryForTesting( base::BindRepeating( [](base::RepeatingCallback<void( enterprise_connectors::MockFileTransferAnalysisDelegate*)> @@ -577,6 +579,9 @@ if (name == "issueFileTransferResponses") { // Issue all saved responses and issue all future responses directly. IssueResponses(); + if (reporting_run_loop_) { + reporting_run_loop_->Run(); + } return true; } if (name == "isReportOnlyFileTransferConnector") { @@ -605,14 +610,14 @@ auto maybe_int = value.FindInt("number_of_expected_delegates"); DCHECK(maybe_int.has_value()); expected_number_of_file_transfer_analysis_delegates_ = maybe_int.value(); - DCHECK(!run_loop_); - run_loop_ = std::make_unique<base::RunLoop>(); + DCHECK(!scanning_run_loop_); + scanning_run_loop_ = std::make_unique<base::RunLoop>(); return true; } if (name == "waitForFileTransferScanningToComplete") { - DCHECK(run_loop_); + DCHECK(scanning_run_loop_); // Wait until the scanning is complete. - run_loop_->Run(); + scanning_run_loop_->Run(); return true; } if (name == "expectFileTransferReports") { @@ -678,7 +683,7 @@ } } - // For report-only mode, the transfer is always allowed. It's blocked, + // For report-only mode, the transfer is always allowed. It's blocked // otherwise. expected_results.push_back(enterprise_connectors::EventResultToString( options.file_transfer_connector_report_only @@ -691,9 +696,11 @@ expected_scan_ids.push_back(GetScanIDForFileName(file_name)); } + reporting_run_loop_ = std::make_unique<base::RunLoop>(); validator_ = std::make_unique<enterprise_connectors::test::EventReportValidator>( cloud_policy_client()); + validator_->SetDoneClosure(reporting_run_loop_->QuitClosure()); validator_->ExpectSensitiveDataEvents( /*url*/ "", /*tab_url*/ "", @@ -820,7 +827,14 @@ std::vector<std::string> expected_blocked_files_; std::vector<std::string> expected_warned_files_; - std::unique_ptr<base::RunLoop> run_loop_; + // Used to wait for scanning to finish. This can be run from TS + // by using the "waitForFileTransferScanningToComplete" command name. + std::unique_ptr<base::RunLoop> scanning_run_loop_; + + // Used to wait for event reporting to be done. This is run by + // the "issueFileTransferResponses" command as event reporting + // happens after DLP responses are received. + std::unique_ptr<base::RunLoop> reporting_run_loop_; }; } // namespace @@ -1071,7 +1085,10 @@ const DlpAndEnterpriseConnectorsFilesAppBrowserTest&) = delete; protected: - DlpAndEnterpriseConnectorsFilesAppBrowserTest() = default; + DlpAndEnterpriseConnectorsFilesAppBrowserTest() { + scoped_feature_list_.InitAndEnableFeature( + safe_browsing::kLocalIpAddressInEvents); + } ~DlpAndEnterpriseConnectorsFilesAppBrowserTest() override = default; void SetUpOnMainThread() override { @@ -1118,6 +1135,9 @@ FileManagerBrowserTestBase::Options GetOptions() const override { return GetParam().options; } + + private: + base::test::ScopedFeatureList scoped_feature_list_; }; IN_PROC_BROWSER_TEST_P(DlpAndEnterpriseConnectorsFilesAppBrowserTest, Test) {
diff --git a/chrome/browser/ash/keyed_service/browser_context_keyed_service_factories.cc b/chrome/browser/ash/keyed_service/browser_context_keyed_service_factories.cc index 233ee13..d8f22b1 100644 --- a/chrome/browser/ash/keyed_service/browser_context_keyed_service_factories.cc +++ b/chrome/browser/ash/keyed_service/browser_context_keyed_service_factories.cc
@@ -74,10 +74,10 @@ #include "chrome/browser/ash/login/saml/password_sync_token_verifier_factory.h" #include "chrome/browser/ash/login/security_token_session_controller_factory.h" #include "chrome/browser/ash/login/signin/auth_error_observer_factory.h" +#include "chrome/browser/ash/login/signin/legacy_token_handle_fetcher.h" #include "chrome/browser/ash/login/signin/oauth2_login_manager_factory.h" #include "chrome/browser/ash/login/signin/offline_signin_limiter_factory.h" #include "chrome/browser/ash/login/signin/signin_error_notifier_factory.h" -#include "chrome/browser/ash/login/signin/token_handle_fetcher.h" #include "chrome/browser/ash/login/signin_partition_manager.h" #include "chrome/browser/ash/login/smart_lock/smart_lock_service_factory.h" #include "chrome/browser/ash/multidevice_setup/auth_token_validator_factory.h" @@ -254,7 +254,7 @@ SystemLiveCaptionServiceFactory::GetInstance(); SystemWebAppManagerFactory::GetInstance(); tether::TetherServiceFactory::GetInstance(); - TokenHandleFetcher::EnsureFactoryBuilt(); + LegacyTokenHandleFetcher::EnsureFactoryBuilt(); TtsEngineExtensionObserverChromeOSFactory::GetInstance(); }
diff --git a/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc b/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc index a0784dd..4c9a0a79 100644 --- a/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc +++ b/chrome/browser/ash/login/app_mode/kiosk_launch_controller.cc
@@ -260,6 +260,7 @@ CASE(kExtensionsLoadTimeout); CASE(kExtensionsPolicyInvalid); CASE(kUserNotAllowlisted); + CASE(kChromeAppDeprecated); } NOTREACHED(); #undef CASE @@ -589,6 +590,7 @@ case Error::kExtensionsLoadTimeout: case Error::kExtensionsPolicyInvalid: case Error::kUserNotAllowlisted: + case Error::kChromeAppDeprecated: if (KioskLaunchController::TestOverrides::block_exit_on_failure) { // Don't exit on launch failure if a test checks for Kiosk splash screen // after launch fails, which happens to MSan browser_tests since this
diff --git a/chrome/browser/ash/login/password_change_browsertest.cc b/chrome/browser/ash/login/password_change_browsertest.cc index a5803c5d..7168e6d9 100644 --- a/chrome/browser/ash/login/password_change_browsertest.cc +++ b/chrome/browser/ash/login/password_change_browsertest.cc
@@ -14,6 +14,7 @@ #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/functional/callback.h" +#include "base/json/values_util.h" #include "base/run_loop.h" #include "base/test/metrics/histogram_tester.h" #include "base/threading/thread_restrictions.h" @@ -63,6 +64,7 @@ constexpr GaiaId::Literal kGaiaID("111111"); constexpr char kTokenHandle[] = "test_token_handle"; constexpr char kTestingFileName[] = "testing-file.txt"; +constexpr char kTokenHandleLastCheckedPref[] = "TokenHandleLastChecked"; using AuthOp = FakeUserDataAuthClient::Operation; @@ -339,19 +341,20 @@ ignore_sync_errors_for_test_ = SigninErrorNotifier::IgnoreSyncErrorsForTesting(); UserDataAuthClient::InitializeFake(); - token_handle_store_ = TokenHandleStoreFactory::Get()->GetTokenHandleStore(); } protected: // PasswordChangeTest: - void SetUpInProcessBrowserTestFixture() override { - PasswordChangeTest::SetUpInProcessBrowserTestFixture(); + void SetUpOnMainThread() override { + PasswordChangeTest::SetUpOnMainThread(); + token_handle_store_ = TokenHandleStoreFactory::Get()->GetTokenHandleStore(); token_handle_store_->SetInvalidTokenForTesting(kTokenHandle); } - void TearDownInProcessBrowserTestFixture() override { + void TearDownOnMainThread() override { token_handle_store_->SetInvalidTokenForTesting(nullptr); - PasswordChangeTest::TearDownInProcessBrowserTestFixture(); + token_handle_store_ = nullptr; + LoginManagerTest::TearDownOnMainThread(); } AccountId user_with_invalid_token_; @@ -429,8 +432,9 @@ // Store invalid token to trigger notification in the session. token_handle_store_->StoreTokenHandle(user_with_invalid_token_, kTokenHandle); // Make token not "checked recently". - token_handle_store_->SetLastCheckedPrefForTesting(user_with_invalid_token_, - base::Time()); + user_manager::KnownUser known_user(g_browser_process->local_state()); + known_user.SetPath(user_with_invalid_token_, kTokenHandleLastCheckedPref, + base::TimeToValue(base::Time())); ProfileWaiter waiter; login_mixin_.LoginWithDefaultContext(login_mixin_.users().back()); @@ -501,9 +505,18 @@ login_mixin_.SetShouldObtainHandle(true); login_mixin_.AppendRegularUsers(1); UserDataAuthClient::InitializeFake(); + } + + void SetUpOnMainThread() override { + MixinBasedInProcessBrowserTest::SetUpOnMainThread(); token_handle_store_ = TokenHandleStoreFactory::Get()->GetTokenHandleStore(); } + void TearDownOnMainThread() override { + token_handle_store_ = nullptr; + MixinBasedInProcessBrowserTest::TearDownOnMainThread(); + } + protected: LoginManagerMixin login_mixin_{&mixin_host_}; raw_ptr<TokenHandleStore> token_handle_store_; @@ -565,12 +578,13 @@ account_id_ = login_mixin_.users()[0].account_id; UserDataAuthClient::InitializeFake(); - token_handle_store_ = TokenHandleStoreFactory::Get()->GetTokenHandleStore(); } // LocalStateMixin::Delegate: void SetUpLocalState() override { + token_handle_store_ = TokenHandleStoreFactory::Get()->GetTokenHandleStore(); token_handle_store_->StoreTokenHandle(account_id_, kTokenHandle); + token_handle_store_->SetInvalidTokenForTesting(kTokenHandle); if (content::IsPreTest()) { // Keep `TokenHandleRotated` flag to disable logic of neglecting not @@ -579,15 +593,10 @@ } } - // LoginManagerTest: - void SetUpInProcessBrowserTestFixture() override { - LoginManagerTest::SetUpInProcessBrowserTestFixture(); - token_handle_store_->SetInvalidTokenForTesting(kTokenHandle); - } - - void TearDownInProcessBrowserTestFixture() override { + void TearDownOnMainThread() override { token_handle_store_->SetInvalidTokenForTesting(nullptr); - LoginManagerTest::TearDownInProcessBrowserTestFixture(); + token_handle_store_ = nullptr; + LoginManagerTest::TearDownOnMainThread(); } protected:
diff --git a/chrome/browser/ash/login/screens/user_selection_screen.cc b/chrome/browser/ash/login/screens/user_selection_screen.cc index 158b2d5f..e479690 100644 --- a/chrome/browser/ash/login/screens/user_selection_screen.cc +++ b/chrome/browser/ash/login/screens/user_selection_screen.cc
@@ -10,11 +10,13 @@ #include <optional> #include <utility> +#include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" #include "ash/constants/ash_switches.h" #include "ash/public/cpp/login/login_utils.h" #include "ash/public/cpp/login_screen.h" #include "ash/public/cpp/login_screen_model.h" +#include "ash/public/cpp/token_handle_store.h" #include "base/command_line.h" #include "base/functional/bind.h" #include "base/functional/callback.h" @@ -35,6 +37,7 @@ #include "chrome/browser/ash/login/quick_unlock/quick_unlock_storage.h" #include "chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h" #include "chrome/browser/ash/login/reauth_stats.h" +#include "chrome/browser/ash/login/signin/token_handle_store_factory.h" #include "chrome/browser/ash/login/smart_lock/smart_lock_service.h" #include "chrome/browser/ash/login/users/default_user_image/default_user_images.h" #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h" @@ -585,12 +588,12 @@ return; } - if (!token_handle_util_.get()) { - token_handle_util_ = std::make_unique<TokenHandleUtil>(); + if (!token_handle_store_.get()) { + token_handle_store_ = TokenHandleStoreFactory::Get()->GetTokenHandleStore(); } - if (token_handle_util_->HasToken(account_id)) { - token_handle_util_->IsReauthRequired( + if (token_handle_store_->HasToken(account_id)) { + token_handle_store_->IsReauthRequired( account_id, ProfileHelper::Get()->GetSigninProfile()->GetURLLoaderFactory(), base::BindOnce(&UserSelectionScreen::OnUserStatusChecked,
diff --git a/chrome/browser/ash/login/screens/user_selection_screen.h b/chrome/browser/ash/login/screens/user_selection_screen.h index 9257749e..027fd36 100644 --- a/chrome/browser/ash/login/screens/user_selection_screen.h +++ b/chrome/browser/ash/login/screens/user_selection_screen.h
@@ -9,12 +9,12 @@ #include <string> #include <vector> +#include "ash/public/cpp/token_handle_store.h" #include "base/memory/raw_ptr.h" #include "base/scoped_observation.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "chrome/browser/ash/login/saml/password_sync_token_checkers_collection.h" -#include "chrome/browser/ash/login/signin/token_handle_util.h" #include "chrome/browser/ash/login/user_online_signin_notifier.h" #include "chrome/browser/ash/system/system_clock.h" #include "chromeos/ash/components/dbus/cryptohome/rpc.pb.h" @@ -130,8 +130,8 @@ // contained in the map, it is using the default authentication type. std::map<AccountId, proximity_auth::mojom::AuthType> user_auth_type_map_; - // Token handler util for checking user OAuth token status. - std::unique_ptr<TokenHandleUtil> token_handle_util_; + // Used for checking user OAuth token status. + raw_ptr<TokenHandleStore> token_handle_store_; // Helper to check whether a user needs dircrypto migration. std::unique_ptr<DircryptoMigrationChecker> dircrypto_migration_checker_;
diff --git a/chrome/browser/ash/login/session/user_session_manager.cc b/chrome/browser/ash/login/session/user_session_manager.cc index c4077dc..b3fca30 100644 --- a/chrome/browser/ash/login/session/user_session_manager.cc +++ b/chrome/browser/ash/login/session/user_session_manager.cc
@@ -17,6 +17,7 @@ #include "ash/constants/ash_pref_names.h" #include "ash/constants/ash_switches.h" #include "ash/metrics/login_unlock_throughput_recorder.h" +#include "ash/public/cpp/token_handle_store.h" #include "ash/shell.h" #include "ash/wm/window_util.h" #include "base/base_paths.h" @@ -83,10 +84,11 @@ #include "chrome/browser/ash/login/session/user_session_initializer.h" #include "chrome/browser/ash/login/signin/auth_error_observer.h" #include "chrome/browser/ash/login/signin/auth_error_observer_factory.h" +#include "chrome/browser/ash/login/signin/legacy_token_handle_fetcher.h" #include "chrome/browser/ash/login/signin/oauth2_login_manager_factory.h" #include "chrome/browser/ash/login/signin/offline_signin_limiter.h" #include "chrome/browser/ash/login/signin/offline_signin_limiter_factory.h" -#include "chrome/browser/ash/login/signin/token_handle_fetcher.h" +#include "chrome/browser/ash/login/signin/token_handle_store_factory.h" #include "chrome/browser/ash/login/startup_utils.h" #include "chrome/browser/ash/login/wizard_controller.h" #include "chrome/browser/ash/net/alwayson_vpn_pre_connect_url_allowlist_service.h" @@ -2134,10 +2136,10 @@ // If the user has gone through an online Gaia flow, then their LST is // guaranteed to have changed/created. We need to update the token handle, // regardless of the state of the previous token handle, if any. - if (!token_handle_util_->HasToken(user_context_.GetAccountId())) { + if (!token_handle_store_->HasToken(user_context_.GetAccountId())) { // New user. - token_handle_fetcher_ = std::make_unique<TokenHandleFetcher>( - profile, token_handle_util_.get(), user_context_.GetAccountId()); + token_handle_fetcher_ = std::make_unique<LegacyTokenHandleFetcher>( + profile, token_handle_store_.get(), user_context_.GetAccountId()); token_handle_fetcher_->FillForNewUser( user_context_.GetAccessToken(), Sha1Digest(user_context_.GetRefreshToken()), @@ -2563,7 +2565,6 @@ void UserSessionManager::Shutdown() { token_handle_fetcher_.reset(); - token_handle_util_.reset(); token_observers_.clear(); always_on_vpn_manager_.reset(); child_policy_observer_.reset(); @@ -2572,6 +2573,7 @@ password_service_voted_.reset(); password_was_saved_ = false; xdr_manager_.reset(); + token_handle_store_ = nullptr; } void UserSessionManager::SetSwitchesForUser( @@ -2629,15 +2631,17 @@ } void UserSessionManager::CreateTokenUtilIfMissing() { - if (!token_handle_util_.get()) - token_handle_util_ = std::make_unique<TokenHandleUtil>(); + if (!token_handle_store_) { + token_handle_store_ = TokenHandleStoreFactory::Get()->GetTokenHandleStore(); + } } void UserSessionManager::UpdateTokenHandleIfRequired( Profile* const profile, const AccountId& account_id) { - if (!token_handle_util_->ShouldObtainHandle(account_id)) + if (!token_handle_store_->ShouldObtainHandle(account_id)) { return; + } if (token_handle_fetcher_.get()) return; @@ -2646,8 +2650,8 @@ void UserSessionManager::UpdateTokenHandle(Profile* const profile, const AccountId& account_id) { - token_handle_fetcher_ = std::make_unique<TokenHandleFetcher>( - profile, token_handle_util_.get(), account_id); + token_handle_fetcher_ = std::make_unique<LegacyTokenHandleFetcher>( + profile, token_handle_store_.get(), account_id); token_handle_fetcher_->BackfillToken( base::BindOnce(&UserSessionManager::OnTokenHandleObtained, GetUserSessionManagerAsWeakPtr()));
diff --git a/chrome/browser/ash/login/session/user_session_manager.h b/chrome/browser/ash/login/session/user_session_manager.h index 415f847..43e3c33 100644 --- a/chrome/browser/ash/login/session/user_session_manager.h +++ b/chrome/browser/ash/login/session/user_session_manager.h
@@ -13,6 +13,7 @@ #include <string> #include <vector> +#include "ash/public/cpp/token_handle_store.h" #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/functional/callback.h" @@ -27,7 +28,6 @@ #include "chrome/browser/ash/child_accounts/child_policy_observer.h" #include "chrome/browser/ash/hats/hats_notification_controller.h" #include "chrome/browser/ash/login/signin/oauth2_login_manager.h" -#include "chrome/browser/ash/login/signin/token_handle_util.h" #include "chrome/browser/ash/net/xdr_manager.h" #include "chrome/browser/ash/release_notes/release_notes_notification.h" #include "chrome/browser/ash/system_web_apps/apps/help_app/help_app_notification_controller.h" @@ -58,7 +58,7 @@ class AuthStatusConsumer; class OnboardingUserActivityCounter; class AuthenticatorBuilder; -class TokenHandleFetcher; +class LegacyTokenHandleFetcher; class EolNotification; class InputEventsBlocker; class U2FNotification; @@ -582,8 +582,8 @@ // Whether should fetch token handles, tests may override this value. bool should_obtain_handles_; - std::unique_ptr<TokenHandleUtil> token_handle_util_; - std::unique_ptr<TokenHandleFetcher> token_handle_fetcher_; + raw_ptr<TokenHandleStore> token_handle_store_; + std::unique_ptr<LegacyTokenHandleFetcher> token_handle_fetcher_; std::map<Profile*, std::unique_ptr<DeviceAccountGaiaTokenObserver>> token_observers_;
diff --git a/chrome/browser/ash/login/signin/BUILD.gn b/chrome/browser/ash/login/signin/BUILD.gn index 9d432af..e76c5ac 100644 --- a/chrome/browser/ash/login/signin/BUILD.gn +++ b/chrome/browser/ash/login/signin/BUILD.gn
@@ -12,6 +12,8 @@ "auth_error_observer_factory.h", "authentication_flow_auto_reload_manager.cc", "authentication_flow_auto_reload_manager.h", + "legacy_token_handle_fetcher.cc", + "legacy_token_handle_fetcher.h", "merge_session_navigation_throttle.cc", "merge_session_navigation_throttle.h", "merge_session_throttling_utils.cc", @@ -34,8 +36,6 @@ "signin_error_notifier_factory.h", "token_handle_checker.cc", "token_handle_checker.h", - "token_handle_fetcher.cc", - "token_handle_fetcher.h", "token_handle_store_factory.cc", "token_handle_store_factory.h", "token_handle_store_impl.cc",
diff --git a/chrome/browser/ash/login/signin/token_handle_fetcher.cc b/chrome/browser/ash/login/signin/legacy_token_handle_fetcher.cc similarity index 73% rename from chrome/browser/ash/login/signin/token_handle_fetcher.cc rename to chrome/browser/ash/login/signin/legacy_token_handle_fetcher.cc index 604a8e9..3b9738d 100644 --- a/chrome/browser/ash/login/signin/token_handle_fetcher.cc +++ b/chrome/browser/ash/login/signin/legacy_token_handle_fetcher.cc
@@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/ash/login/signin/token_handle_fetcher.h" +#include "chrome/browser/ash/login/signin/legacy_token_handle_fetcher.h" #include <memory> +#include "ash/public/cpp/token_handle_store.h" #include "base/check.h" #include "base/functional/bind.h" #include "base/metrics/histogram_functions.h" #include "base/values.h" -#include "chrome/browser/ash/login/signin/token_handle_util.h" #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process_platform_part_ash.h" @@ -42,28 +42,29 @@ // hash of the OAuth refresh token from which the token handle was derived. constexpr char kTokenHandleMap[] = "ash.token_handle_map"; -class TokenHandleFetcherShutdownNotifierFactory +class LegacyTokenHandleFetcherShutdownNotifierFactory : public BrowserContextKeyedServiceShutdownNotifierFactory { public: - static TokenHandleFetcherShutdownNotifierFactory* GetInstance() { - return base::Singleton<TokenHandleFetcherShutdownNotifierFactory>::get(); + static LegacyTokenHandleFetcherShutdownNotifierFactory* GetInstance() { + return base::Singleton< + LegacyTokenHandleFetcherShutdownNotifierFactory>::get(); } - TokenHandleFetcherShutdownNotifierFactory( - const TokenHandleFetcherShutdownNotifierFactory&) = delete; - TokenHandleFetcherShutdownNotifierFactory& operator=( - const TokenHandleFetcherShutdownNotifierFactory&) = delete; + LegacyTokenHandleFetcherShutdownNotifierFactory( + const LegacyTokenHandleFetcherShutdownNotifierFactory&) = delete; + LegacyTokenHandleFetcherShutdownNotifierFactory& operator=( + const LegacyTokenHandleFetcherShutdownNotifierFactory&) = delete; private: friend struct base::DefaultSingletonTraits< - TokenHandleFetcherShutdownNotifierFactory>; + LegacyTokenHandleFetcherShutdownNotifierFactory>; - TokenHandleFetcherShutdownNotifierFactory() + LegacyTokenHandleFetcherShutdownNotifierFactory() : BrowserContextKeyedServiceShutdownNotifierFactory( - "TokenHandleFetcher") { + "LegacyTokenHandleFetcher") { DependsOn(IdentityManagerFactory::GetInstance()); } - ~TokenHandleFetcherShutdownNotifierFactory() override = default; + ~LegacyTokenHandleFetcherShutdownNotifierFactory() override = default; }; account_manager::AccountManager* GetAccountManager(Profile* profile) { @@ -74,9 +75,10 @@ } // namespace -TokenHandleFetcher::TokenHandleFetcher(Profile* profile, - TokenHandleStore* token_handle_store, - const AccountId& account_id) +LegacyTokenHandleFetcher::LegacyTokenHandleFetcher( + Profile* profile, + TokenHandleStore* token_handle_store, + const AccountId& account_id) : profile_(profile), token_handle_store_(token_handle_store), account_id_(account_id) { @@ -84,9 +86,9 @@ CHECK(token_handle_store_.get()); } -TokenHandleFetcher::~TokenHandleFetcher() = default; +LegacyTokenHandleFetcher::~LegacyTokenHandleFetcher() = default; -void TokenHandleFetcher::BackfillToken(TokenFetchingCallback callback) { +void LegacyTokenHandleFetcher::BackfillToken(TokenFetchingCallback callback) { callback_ = std::move(callback); if (account_id_.GetAccountType() != AccountType::GOOGLE) { @@ -99,11 +101,11 @@ identity_manager_->GetPrimaryAccountId( signin::ConsentLevel::kSignin))) { profile_shutdown_subscription_ = - TokenHandleFetcherShutdownNotifierFactory::GetInstance() + LegacyTokenHandleFetcherShutdownNotifierFactory::GetInstance() ->Get(profile_) - ->Subscribe( - base::BindRepeating(&TokenHandleFetcher::OnProfileDestroyed, - base::Unretained(this))); + ->Subscribe(base::BindRepeating( + &LegacyTokenHandleFetcher::OnProfileDestroyed, + base::Unretained(this))); } // Now we can request the token, knowing that it will be immediately requested @@ -119,13 +121,13 @@ access_token_fetcher_ = std::make_unique<signin::PrimaryAccountAccessTokenFetcher>( kAccessTokenFetchId, identity_manager_, scopes, - base::BindOnce(&TokenHandleFetcher::OnAccessTokenFetchComplete, + base::BindOnce(&LegacyTokenHandleFetcher::OnAccessTokenFetchComplete, base::Unretained(this)), signin::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable, signin::ConsentLevel::kSignin); } -void TokenHandleFetcher::OnAccessTokenFetchComplete( +void LegacyTokenHandleFetcher::OnAccessTokenFetchComplete( GoogleServiceAuthError error, signin::AccessTokenInfo token_info) { access_token_fetcher_.reset(); @@ -139,24 +141,25 @@ GetAccountManager(profile_)->GetTokenHash( account_manager::AccountKey::FromGaiaId(account_id_.GetGaiaId()), - base::BindOnce(&TokenHandleFetcher::FillForAccessToken, + base::BindOnce(&LegacyTokenHandleFetcher::FillForAccessToken, weak_factory_.GetWeakPtr(), /*access_token=*/token_info.token)); } // static -void TokenHandleFetcher::RegisterPrefs(PrefRegistrySimple* registry) { +void LegacyTokenHandleFetcher::RegisterPrefs(PrefRegistrySimple* registry) { registry->RegisterDictionaryPref(/*path=*/kTokenHandleMap); } -void TokenHandleFetcher::FillForNewUser(const std::string& access_token, - const std::string& refresh_token_hash, - TokenFetchingCallback callback) { +void LegacyTokenHandleFetcher::FillForNewUser( + const std::string& access_token, + const std::string& refresh_token_hash, + TokenFetchingCallback callback) { callback_ = std::move(callback); FillForAccessToken(access_token, refresh_token_hash); } -void TokenHandleFetcher::FillForAccessToken( +void LegacyTokenHandleFetcher::FillForAccessToken( const std::string& access_token, const std::string& refresh_token_hash) { refresh_token_hash_ = refresh_token_hash; @@ -168,15 +171,15 @@ gaia_client_->GetTokenInfo(access_token, kMaxRetries, this); } -void TokenHandleFetcher::OnOAuthError() { +void LegacyTokenHandleFetcher::OnOAuthError() { std::move(callback_).Run(account_id_, false); } -void TokenHandleFetcher::OnNetworkError(int response_code) { +void LegacyTokenHandleFetcher::OnNetworkError(int response_code) { std::move(callback_).Run(account_id_, false); } -void TokenHandleFetcher::OnGetTokenInfoResponse( +void LegacyTokenHandleFetcher::OnGetTokenInfoResponse( const base::Value::Dict& token_info) { bool success = false; if (!token_info.Find("error")) { @@ -193,7 +196,7 @@ std::move(callback_).Run(account_id_, success); } -void TokenHandleFetcher::StoreTokenHandleMapping( +void LegacyTokenHandleFetcher::StoreTokenHandleMapping( const std::string& token_handle) { PrefService* prefs = profile_->GetPrefs(); ScopedDictPrefUpdate update(prefs, kTokenHandleMap); @@ -201,15 +204,16 @@ update->Set(token_handle, refresh_token_hash_); } -void TokenHandleFetcher::DiagnoseTokenHandleMapping(const AccountId& account_id, - const std::string& token) { +void LegacyTokenHandleFetcher::DiagnoseTokenHandleMapping( + const AccountId& account_id, + const std::string& token) { GetAccountManager(profile_)->GetTokenHash( account_manager::AccountKey::FromGaiaId(account_id.GetGaiaId()), - base::BindOnce(&TokenHandleFetcher::OnGetTokenHash, + base::BindOnce(&LegacyTokenHandleFetcher::OnGetTokenHash, weak_factory_.GetWeakPtr(), token)); } -void TokenHandleFetcher::OnGetTokenHash( +void LegacyTokenHandleFetcher::OnGetTokenHash( const std::string& token, const std::string& account_manager_stored_hash) { PrefService* prefs = profile_->GetPrefs(); @@ -228,13 +232,13 @@ hashes_match); } -void TokenHandleFetcher::OnProfileDestroyed() { +void LegacyTokenHandleFetcher::OnProfileDestroyed() { std::move(callback_).Run(account_id_, false); } // static -void TokenHandleFetcher::EnsureFactoryBuilt() { - TokenHandleFetcherShutdownNotifierFactory::GetInstance(); +void LegacyTokenHandleFetcher::EnsureFactoryBuilt() { + LegacyTokenHandleFetcherShutdownNotifierFactory::GetInstance(); } } // namespace ash
diff --git a/chrome/browser/ash/login/signin/token_handle_fetcher.h b/chrome/browser/ash/login/signin/legacy_token_handle_fetcher.h similarity index 80% rename from chrome/browser/ash/login/signin/token_handle_fetcher.h rename to chrome/browser/ash/login/signin/legacy_token_handle_fetcher.h index 3a6d367..08d8f0e 100644 --- a/chrome/browser/ash/login/signin/token_handle_fetcher.h +++ b/chrome/browser/ash/login/signin/legacy_token_handle_fetcher.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_ASH_LOGIN_SIGNIN_TOKEN_HANDLE_FETCHER_H_ -#define CHROME_BROWSER_ASH_LOGIN_SIGNIN_TOKEN_HANDLE_FETCHER_H_ +#ifndef CHROME_BROWSER_ASH_LOGIN_SIGNIN_LEGACY_TOKEN_HANDLE_FETCHER_H_ +#define CHROME_BROWSER_ASH_LOGIN_SIGNIN_LEGACY_TOKEN_HANDLE_FETCHER_H_ #include <string> @@ -29,16 +29,16 @@ // It can be used in two ways. When a user has just used Gaia signin there is // an OAuth2 token available. If there is profile already loaded, then // minting additional access token might be required. -class TokenHandleFetcher : public gaia::GaiaOAuthClient::Delegate { +class LegacyTokenHandleFetcher : public gaia::GaiaOAuthClient::Delegate { public: - TokenHandleFetcher(Profile* profile, - TokenHandleStore* token_handle_store, - const AccountId& account_id); + LegacyTokenHandleFetcher(Profile* profile, + TokenHandleStore* token_handle_store, + const AccountId& account_id); - TokenHandleFetcher(const TokenHandleFetcher&) = delete; - TokenHandleFetcher& operator=(const TokenHandleFetcher&) = delete; + LegacyTokenHandleFetcher(const LegacyTokenHandleFetcher&) = delete; + LegacyTokenHandleFetcher& operator=(const LegacyTokenHandleFetcher&) = delete; - ~TokenHandleFetcher() override; + ~LegacyTokenHandleFetcher() override; using TokenFetchingCallback = base::OnceCallback<void(const AccountId&, bool success)>; @@ -90,9 +90,9 @@ access_token_fetcher_; base::CallbackListSubscription profile_shutdown_subscription_; - base::WeakPtrFactory<TokenHandleFetcher> weak_factory_{this}; + base::WeakPtrFactory<LegacyTokenHandleFetcher> weak_factory_{this}; }; } // namespace ash -#endif // CHROME_BROWSER_ASH_LOGIN_SIGNIN_TOKEN_HANDLE_FETCHER_H_ +#endif // CHROME_BROWSER_ASH_LOGIN_SIGNIN_LEGACY_TOKEN_HANDLE_FETCHER_H_
diff --git a/chrome/browser/ash/login/signin/signin_error_notifier.cc b/chrome/browser/ash/login/signin/signin_error_notifier.cc index 5a8653b..43faefb 100644 --- a/chrome/browser/ash/login/signin/signin_error_notifier.cc +++ b/chrome/browser/ash/login/signin/signin_error_notifier.cc
@@ -16,7 +16,7 @@ #include "build/build_config.h" #include "chrome/browser/ash/account_manager/account_manager_util.h" #include "chrome/browser/ash/login/reauth_stats.h" -#include "chrome/browser/ash/login/signin/token_handle_fetcher.h" +#include "chrome/browser/ash/login/signin/legacy_token_handle_fetcher.h" #include "chrome/browser/ash/login/signin/token_handle_store_factory.h" #include "chrome/browser/ash/login/signin/token_handle_util.h" #include "chrome/browser/browser_process.h" @@ -163,13 +163,13 @@ } } -std::unique_ptr<TokenHandleFetcher> CreateTokenHandleFetcher( +std::unique_ptr<LegacyTokenHandleFetcher> CreateTokenHandleFetcher( Profile* profile, TokenHandleStore* token_handle_store) { const AccountId account_id = multi_user_util::GetAccountIdFromProfile(profile); - return std::make_unique<TokenHandleFetcher>(profile, token_handle_store, - account_id); + return std::make_unique<LegacyTokenHandleFetcher>(profile, token_handle_store, + account_id); } } // namespace @@ -242,8 +242,14 @@ } void SigninErrorNotifier::Shutdown() { - error_controller_->RemoveObserver(this); + if (error_controller_) { + error_controller_->RemoveObserver(this); + } error_controller_ = nullptr; + if (token_handle_fetcher_) { + token_handle_fetcher_.reset(); + } + token_handle_store_ = nullptr; } void SigninErrorNotifier::OnErrorChanged() {
diff --git a/chrome/browser/ash/login/signin/signin_error_notifier.h b/chrome/browser/ash/login/signin/signin_error_notifier.h index a4b2bf6..65c8c7b3 100644 --- a/chrome/browser/ash/login/signin/signin_error_notifier.h +++ b/chrome/browser/ash/login/signin/signin_error_notifier.h
@@ -12,7 +12,7 @@ #include "base/gtest_prod_util.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" -#include "chrome/browser/ash/login/signin/token_handle_fetcher.h" +#include "chrome/browser/ash/login/signin/legacy_token_handle_fetcher.h" #include "chrome/browser/ash/login/signin/token_handle_util.h" #include "components/account_id/account_id.h" #include "components/account_manager_core/account.h" @@ -104,7 +104,7 @@ // `TokenHandleStore` instance which is created early, prior to profile // loading, and never destroyed. raw_ptr<TokenHandleStore> token_handle_store_; - const std::unique_ptr<TokenHandleFetcher> token_handle_fetcher_; + std::unique_ptr<LegacyTokenHandleFetcher> token_handle_fetcher_; // Used to keep track of the message center notifications. std::string device_account_notification_id_;
diff --git a/chrome/browser/ash/login/signin/signin_error_notifier_unittest.cc b/chrome/browser/ash/login/signin/signin_error_notifier_unittest.cc index 13acd9a80..3b601101 100644 --- a/chrome/browser/ash/login/signin/signin_error_notifier_unittest.cc +++ b/chrome/browser/ash/login/signin/signin_error_notifier_unittest.cc
@@ -74,7 +74,10 @@ // will be destroyed as part of the TearDown() process. identity_test_env_profile_adaptor_.reset(); + token_handle_store_ = nullptr; ash::UserDataAuthClient::Shutdown(); + SigninErrorNotifierFactory::GetForProfile(GetProfile())->Shutdown(); + TokenHandleStoreFactory::Get()->DestroyTokenHandleStore(); BrowserWithTestWindowTest::TearDown(); }
diff --git a/chrome/browser/ash/login/signin/token_handle_store_factory.cc b/chrome/browser/ash/login/signin/token_handle_store_factory.cc index 61ef91d..5ad4814 100644 --- a/chrome/browser/ash/login/signin/token_handle_store_factory.cc +++ b/chrome/browser/ash/login/signin/token_handle_store_factory.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "ash/constants/ash_features.h" #include "base/containers/flat_map.h" #include "base/functional/callback_helpers.h" #include "base/memory/weak_ptr.h" @@ -112,11 +113,18 @@ TokenHandleStore* TokenHandleStoreFactory::GetTokenHandleStore() { if (token_handle_store_ == nullptr) { - // TODO(b/383733245): switch based on feature flag state. - token_handle_store_ = std::make_unique<TokenHandleUtil>(); + if (features::IsUseTokenHandleStoreEnabled()) { + token_handle_store_ = CreateTokenHandleStoreImpl(); + } else { + token_handle_store_ = std::make_unique<TokenHandleUtil>(); + } } return token_handle_store_.get(); } +void TokenHandleStoreFactory::DestroyTokenHandleStore() { + token_handle_store_.reset(); +} + } // namespace ash
diff --git a/chrome/browser/ash/login/signin/token_handle_store_factory.h b/chrome/browser/ash/login/signin/token_handle_store_factory.h index 6d9341e..92a15c70 100644 --- a/chrome/browser/ash/login/signin/token_handle_store_factory.h +++ b/chrome/browser/ash/login/signin/token_handle_store_factory.h
@@ -31,6 +31,8 @@ TokenHandleStore* GetTokenHandleStore(); + void DestroyTokenHandleStore(); + private: // Functor that determines if a given `account_id` has a gaia password. // The class maintains the invariant that at any given time, there is at most
diff --git a/chrome/browser/ash/login/signin/token_handle_store_impl.cc b/chrome/browser/ash/login/signin/token_handle_store_impl.cc index def15e9..7ef8c3f 100644 --- a/chrome/browser/ash/login/signin/token_handle_store_impl.cc +++ b/chrome/browser/ash/login/signin/token_handle_store_impl.cc
@@ -100,6 +100,11 @@ return; } + if (invalid_token_for_testing_ == *token) { + std::move(callback).Run(account_id, *token, /*reauth_required=*/true); + return; + } + pending_callbacks_[account_id].push_back(std::move(callback)); // Overwriting the `TokenHandleChecker` for `account_id` while the check is @@ -195,7 +200,13 @@ return status && *status == kTokenHandleStatusInvalid; } -void TokenHandleStoreImpl::SetInvalidTokenForTesting(const char* token) {} +void TokenHandleStoreImpl::SetInvalidTokenForTesting(const char* token) { + if (!token) { + invalid_token_for_testing_->clear(); + return; + } + invalid_token_for_testing_ = token; +} void TokenHandleStoreImpl::SetLastCheckedPrefForTesting( const AccountId& account_id,
diff --git a/chrome/browser/ash/login/signin/token_handle_store_impl.h b/chrome/browser/ash/login/signin/token_handle_store_impl.h index 93300725..109fe26 100644 --- a/chrome/browser/ash/login/signin/token_handle_store_impl.h +++ b/chrome/browser/ash/login/signin/token_handle_store_impl.h
@@ -92,6 +92,8 @@ DoesUserHaveGaiaPasswordCallback does_user_have_gaia_password_; + std::optional<std::string> invalid_token_for_testing_; + base::WeakPtrFactory<TokenHandleStoreImpl> weak_factory_{this}; };
diff --git a/chrome/browser/ash/login/webview_login_browsertest.cc b/chrome/browser/ash/login/webview_login_browsertest.cc index 3298dc7..54ddddf 100644 --- a/chrome/browser/ash/login/webview_login_browsertest.cc +++ b/chrome/browser/ash/login/webview_login_browsertest.cc
@@ -1276,7 +1276,18 @@ user_with_invalid_token_ = login_manager_mixin_.users().back().account_id; cryptohome_mixin_.MarkUserAsExisting(user_with_invalid_token_); UserDataAuthClient::InitializeFake(); + } + + void SetUpOnMainThread() override { + ReauthWebviewLoginTest::SetUpOnMainThread(); token_handle_store_ = TokenHandleStoreFactory::Get()->GetTokenHandleStore(); + token_handle_store_->SetInvalidTokenForTesting(kTestTokenHandle); + } + + void TearDownOnMainThread() override { + token_handle_store_->SetInvalidTokenForTesting(nullptr); + token_handle_store_ = nullptr; + ReauthWebviewLoginTest::TearDownOnMainThread(); } void ShowReauthDialog() { @@ -1300,16 +1311,6 @@ } protected: - void SetUpInProcessBrowserTestFixture() override { - ReauthWebviewLoginTest::SetUpInProcessBrowserTestFixture(); - token_handle_store_->SetInvalidTokenForTesting(kTestTokenHandle); - } - - void TearDownInProcessBrowserTestFixture() override { - token_handle_store_->SetInvalidTokenForTesting(nullptr); - ReauthWebviewLoginTest::TearDownInProcessBrowserTestFixture(); - } - AccountId user_with_invalid_token_; CryptohomeMixin cryptohome_mixin_{&mixin_host_}; FakeRecoveryServiceMixin fake_recovery_service_{&mixin_host_,
diff --git a/chrome/browser/ash/main_parts/BUILD.gn b/chrome/browser/ash/main_parts/BUILD.gn index 5bf5188..8666331e 100644 --- a/chrome/browser/ash/main_parts/BUILD.gn +++ b/chrome/browser/ash/main_parts/BUILD.gn
@@ -60,6 +60,7 @@ "//chrome/browser/ash/login/lock", "//chrome/browser/ash/login/osauth", "//chrome/browser/ash/login/session", + "//chrome/browser/ash/login/signin", "//chrome/browser/ash/login/users/avatar", "//chrome/browser/ash/magic_boost", "//chrome/browser/ash/mahi/web_contents",
diff --git a/chrome/browser/ash/main_parts/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/main_parts/chrome_browser_main_parts_ash.cc index 616e060..4a4e49a 100644 --- a/chrome/browser/ash/main_parts/chrome_browser_main_parts_ash.cc +++ b/chrome/browser/ash/main_parts/chrome_browser_main_parts_ash.cc
@@ -114,6 +114,7 @@ #include "chrome/browser/ash/login/osauth/chrome_auth_parts.h" #include "chrome/browser/ash/login/session/chrome_session_manager.h" #include "chrome/browser/ash/login/session/user_session_manager.h" +#include "chrome/browser/ash/login/signin/token_handle_store_factory.h" #include "chrome/browser/ash/login/startup_utils.h" #include "chrome/browser/ash/login/users/avatar/user_image_manager_registry.h" #include "chrome/browser/ash/login/wizard_controller.h" @@ -1736,6 +1737,12 @@ // NOTE: Closes ash and destroys `Shell`. ChromeBrowserMainPartsLinux::PostMainMessageLoopRun(); + // TokenHandleStore needs to outlive the Profile, which + // is destroyed inside ChromeBrowserMainPartsLinux::PostMainMessageLoopRun(). + if (ash::features::IsUseTokenHandleStoreEnabled()) { + TokenHandleStoreFactory::Get()->DestroyTokenHandleStore(); + } + magic_boost_controller_ash_.reset(); // BrowserManager and CrosapiManager need to outlive the Profile, which
diff --git a/chrome/browser/autofill/android/BUILD.gn b/chrome/browser/autofill/android/BUILD.gn index f438bb43..d7590c8 100644 --- a/chrome/browser/autofill/android/BUILD.gn +++ b/chrome/browser/autofill/android/BUILD.gn
@@ -164,13 +164,13 @@ "java/res/drawable/store_locally_tooltip_background.xml", "java/res/layout/autofill_editor_base.xml", "java/res/layout/autofill_editor_base_buttons.xml", + "java/res/layout/autofill_editor_dialog.xml", + "java/res/layout/autofill_editor_dialog_dropdown.xml", + "java/res/layout/autofill_editor_dialog_textview.xml", "java/res/layout/autofill_local_card_editor.xml", "java/res/layout/autofill_local_iban_editor.xml", "java/res/layout/autofill_vcn_enroll_bottom_sheet_content.xml", "java/res/layout/editable_option_editor_footer.xml", - "java/res/layout/payment_request_editor.xml", - "java/res/layout/payment_request_editor_dropdown.xml", - "java/res/layout/payments_request_editor_textview.xml", "java/res/layout/radio_button_group_third_party_preference.xml", "java/res/menu/prefeditor_editor_menu.xml", "java/res/values/dimens.xml",
diff --git a/chrome/browser/autofill/android/java/res/layout/payment_request_editor.xml b/chrome/browser/autofill/android/java/res/layout/autofill_editor_dialog.xml similarity index 97% rename from chrome/browser/autofill/android/java/res/layout/payment_request_editor.xml rename to chrome/browser/autofill/android/java/res/layout/autofill_editor_dialog.xml index 7368e87..9603a27d 100644 --- a/chrome/browser/autofill/android/java/res/layout/payment_request_editor.xml +++ b/chrome/browser/autofill/android/java/res/layout/autofill_editor_dialog.xml
@@ -5,7 +5,7 @@ found in the LICENSE file. --> -<!-- PaymentRequestUI editor dialog. --> +<!-- Autofill editor dialog. --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/editor_container"
diff --git a/chrome/browser/autofill/android/java/res/layout/payment_request_editor_dropdown.xml b/chrome/browser/autofill/android/java/res/layout/autofill_editor_dialog_dropdown.xml similarity index 100% rename from chrome/browser/autofill/android/java/res/layout/payment_request_editor_dropdown.xml rename to chrome/browser/autofill/android/java/res/layout/autofill_editor_dialog_dropdown.xml
diff --git a/chrome/browser/autofill/android/java/res/layout/payments_request_editor_textview.xml b/chrome/browser/autofill/android/java/res/layout/autofill_editor_dialog_textview.xml similarity index 90% rename from chrome/browser/autofill/android/java/res/layout/payments_request_editor_textview.xml rename to chrome/browser/autofill/android/java/res/layout/autofill_editor_dialog_textview.xml index 2ae7f1d..ca47cd2 100644 --- a/chrome/browser/autofill/android/java/res/layout/payments_request_editor_textview.xml +++ b/chrome/browser/autofill/android/java/res/layout/autofill_editor_dialog_textview.xml
@@ -18,11 +18,9 @@ android:id="@+id/text_input_layout" android:layout_width="match_parent" android:layout_height="wrap_content" - app:errorTextAppearance="@style/TextAppearance.ErrorCaption"> - - <!-- TODO(crbug.com/40600572): Fix and remove lint ignore --> + app:errorTextAppearance="@style/TextAppearance.ErrorCaption" + android:labelFor="@+id/text_view"> <AutoCompleteTextView - tools:ignore="LabelFor" android:id="@+id/text_view" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -45,7 +43,7 @@ android:layout_gravity="center_vertical" android:layout_marginStart="8dp" android:adjustViewBounds="true" - tools:ignore="ContentDescription" + android:importantForAccessibility="no" android:clickable="false" android:visibility="gone"/> @@ -54,7 +52,7 @@ android:layout_width="48dp" android:layout_height="48dp" android:padding="12dp" - tools:ignore="ContentDescription" + android:importantForAccessibility="no" android:visibility="gone"/> </LinearLayout> </FrameLayout>
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/DropdownFieldView.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/DropdownFieldView.java index 2f714ff..0386a1da 100644 --- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/DropdownFieldView.java +++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/DropdownFieldView.java
@@ -67,7 +67,7 @@ mLayout = LayoutInflater.from(context) - .inflate(R.layout.payment_request_editor_dropdown, root, false); + .inflate(R.layout.autofill_editor_dialog_dropdown, root, false); mLabel = (TextView) mLayout.findViewById(R.id.spinner_label); setShowRequiredIndicator(/* showRequiredIndicator= */ false);
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/EditorDialogView.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/EditorDialogView.java index 0bf0336..8515d23 100644 --- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/EditorDialogView.java +++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/EditorDialogView.java
@@ -143,7 +143,7 @@ setOnDismissListener(this); mContainerView = - LayoutInflater.from(mActivity).inflate(R.layout.payment_request_editor, null); + LayoutInflater.from(mActivity).inflate(R.layout.autofill_editor_dialog, null); setContentView(mContainerView); prepareToolbar();
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/TextFieldView.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/TextFieldView.java index dd76ad02..a12048d 100644 --- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/TextFieldView.java +++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/TextFieldView.java
@@ -80,7 +80,7 @@ super(context); mEditorFieldModel = fieldModel; - LayoutInflater.from(context).inflate(R.layout.payments_request_editor_textview, this, true); + LayoutInflater.from(context).inflate(R.layout.autofill_editor_dialog_textview, this, true); mInputLayout = (TextInputLayout) findViewById(R.id.text_input_layout); mInput = (AutoCompleteTextView) mInputLayout.findViewById(R.id.text_view);
diff --git a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc index 6f6bd9a7..feb13ac 100644 --- a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc +++ b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_launcher.cc
@@ -11,6 +11,7 @@ #include "base/syslog_logging.h" #include "chrome/browser/apps/app_service/app_launch_params.h" #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" +#include "chrome/browser/apps/app_service/publishers/chrome_app_deprecation.h" #include "chrome/browser/ash/app_mode/kiosk_chrome_app_manager.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_service_launcher.h" #include "chrome/browser/extensions/extension_service.h" @@ -50,6 +51,7 @@ on_ready_callback_ = std::move(callback); const extensions::Extension* primary_app = GetPrimaryAppExtension(); + // Verify that required apps are installed. While the apps should be // present at this point, crash recovery flow skips app installation steps - // this means that the kiosk app might not yet be downloaded. If that is @@ -59,6 +61,13 @@ return; } + if (apps::chrome_app_deprecation::HandleDeprecation(primary_app->id(), + profile_) == + apps::chrome_app_deprecation::DeprecationStatus::kLaunchBlocked) { + ReportLaunchFailure(LaunchResult::kChromeAppDeprecated); + return; + } + if (!extensions::KioskModeInfo::IsKioskEnabled(primary_app)) { SYSLOG(WARNING) << "Kiosk app not kiosk enabled"; ReportLaunchFailure(LaunchResult::kUnableToLaunch);
diff --git a/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetContent.java b/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetContent.java index 6db39f6..dbc7ec1 100644 --- a/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetContent.java +++ b/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetContent.java
@@ -9,14 +9,15 @@ import android.view.View; import android.widget.ScrollView; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.annotation.StringRes; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent; /** An implementation of {@link BottomSheetContent} for the price insights bottom sheet content. */ +@NullMarked public class PriceInsightsBottomSheetContent implements BottomSheetContent { private final View mContentView; private final ScrollView mScrollView; @@ -32,9 +33,8 @@ return mContentView; } - @Nullable @Override - public View getToolbarView() { + public @Nullable View getToolbarView() { return null; } @@ -75,7 +75,7 @@ public void destroy() {} @Override - public @NonNull String getSheetContentDescription(Context context) { + public String getSheetContentDescription(Context context) { return context.getString(R.string.price_insights_bottom_sheet_content_description); }
diff --git a/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetCoordinator.java b/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetCoordinator.java index 0407d644..81eaabdc 100644 --- a/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetCoordinator.java +++ b/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetCoordinator.java
@@ -10,12 +10,11 @@ import android.view.View; import android.widget.ScrollView; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import org.chromium.base.Callback; import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.supplier.ObservableSupplier; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent; @@ -33,6 +32,7 @@ * <p>This component shows bottom sheet content including the price history, price tracking, and * jackpot url links of the product. */ +@NullMarked public class PriceInsightsBottomSheetCoordinator { /** Delegate interface for price insights feature. */ @@ -66,11 +66,11 @@ private final Context mContext; private final BottomSheetController mBottomSheetController; - private PriceInsightsBottomSheetContent mBottomSheetContent; + private @Nullable PriceInsightsBottomSheetContent mBottomSheetContent; private PriceInsightsBottomSheetMediator mBottomSheetMediator; private BottomSheetObserver mBottomSheetObserver; private View mPriceInsightsView; - private Long mSheetOpenTimeMs; + private @Nullable Long mSheetOpenTimeMs; /** * @param context The {@link Context} associated with this coordinator. @@ -78,12 +78,12 @@ * @param shoppingService Network service for fetching price insights and price tracking info. */ public PriceInsightsBottomSheetCoordinator( - @NonNull Context context, - @NonNull BottomSheetController bottomSheetController, - @NonNull Tab tab, - @NonNull TabModelSelector tabModelSelector, - @NonNull ShoppingService shoppingService, - @NonNull PriceInsightsDelegate priceInsightsDelegate) { + Context context, + BottomSheetController bottomSheetController, + Tab tab, + TabModelSelector tabModelSelector, + ShoppingService shoppingService, + PriceInsightsDelegate priceInsightsDelegate) { mContext = context; mBottomSheetController = bottomSheetController; PropertyModel propertyModel =
diff --git a/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetMediator.java b/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetMediator.java index e073d3a..811ade3 100644 --- a/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetMediator.java +++ b/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetMediator.java
@@ -20,12 +20,13 @@ import android.content.Context; import android.view.View.OnClickListener; -import androidx.annotation.NonNull; import androidx.annotation.StringRes; import org.chromium.base.Callback; import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.supplier.ObservableSupplier; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.commerce.ShoppingServiceFactory; import org.chromium.chrome.browser.price_insights.PriceInsightsBottomSheetCoordinator.PriceInsightsDelegate; import org.chromium.chrome.browser.tab.Tab; @@ -43,6 +44,7 @@ import org.chromium.url.GURL; /** Mediator for price insights bottom sheet responsible for model update. */ +@NullMarked public class PriceInsightsBottomSheetMediator { private final Context mContext; private final Tab mTab; @@ -56,12 +58,12 @@ private @PriceBucket int mPriceBucket; public PriceInsightsBottomSheetMediator( - @NonNull Context context, - @NonNull Tab tab, - @NonNull TabModelSelector tabModelSelector, - @NonNull ShoppingService shoppingService, - @NonNull PriceInsightsDelegate priceInsightsDelegate, - @NonNull PropertyModel propertyModel) { + Context context, + Tab tab, + TabModelSelector tabModelSelector, + ShoppingService shoppingService, + PriceInsightsDelegate priceInsightsDelegate, + PropertyModel propertyModel) { mContext = context; mTab = tab; mTabModelSelector = tabModelSelector; @@ -188,7 +190,7 @@ Toast.makeText(mContext, textResId, Toast.LENGTH_SHORT).show(); } - private void updatePriceInsightsInfo(PriceInsightsInfo info) { + private void updatePriceInsightsInfo(@Nullable PriceInsightsInfo info) { if (info == null || info.currencyCode.isEmpty() || info.catalogHistoryPrices == null
diff --git a/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetProperties.java b/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetProperties.java index 2b58df2d..bb36c68 100644 --- a/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetProperties.java +++ b/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetProperties.java
@@ -7,12 +7,14 @@ import android.view.View; import android.view.View.OnClickListener; +import org.chromium.build.annotations.NullMarked; import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey; import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey; import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey; /** Properties for price insights bottom sheet. */ +@NullMarked public class PriceInsightsBottomSheetProperties { public static final WritableObjectPropertyKey<String> PRICE_TRACKING_TITLE = new WritableObjectPropertyKey<>();
diff --git a/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetViewBinder.java b/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetViewBinder.java index 1312ad9..8891afc 100644 --- a/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetViewBinder.java +++ b/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsBottomSheetViewBinder.java
@@ -28,11 +28,13 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat; import androidx.core.widget.TextViewCompat; +import org.chromium.build.annotations.NullMarked; import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.widget.ButtonCompat; /** ViewBinder for the price insights bottom sheet */ +@NullMarked public class PriceInsightsBottomSheetViewBinder { private static final int PRICE_HISTORY_CHART_ID = View.generateViewId();
diff --git a/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsButtonController.java b/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsButtonController.java index 2bd3205b..d7bebe8 100644 --- a/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsButtonController.java +++ b/chrome/browser/commerce/price_insights/android/java/src/org/chromium/chrome/browser/price_insights/PriceInsightsButtonController.java
@@ -9,11 +9,12 @@ import android.graphics.drawable.Drawable; import android.view.View; -import androidx.annotation.NonNull; import androidx.annotation.StringRes; import org.chromium.base.ResettersForTesting; import org.chromium.base.supplier.Supplier; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; import org.chromium.chrome.browser.commerce.CommerceBottomSheetContentController; import org.chromium.chrome.browser.price_insights.PriceInsightsBottomSheetCoordinator.PriceInsightsDelegate; @@ -37,6 +38,7 @@ * Responsible for providing UI resources for showing price insights action on optional toolbar * button. */ +@NullMarked public class PriceInsightsButtonController extends BaseButtonDataProvider { private final Context mContext; @@ -46,10 +48,10 @@ private final Supplier<TabModelSelector> mTabModelSelectorSupplier; private final Supplier<Tab> mTabSupplier; private final PriceInsightsDelegate mPriceInsightsDelegate; - private PriceInsightsBottomSheetCoordinator mBottomSheetCoordinator; - private PriceInsightsBottomSheetCoordinator mBottomSheetCoordinatorForTesting; + private @Nullable PriceInsightsBottomSheetCoordinator mBottomSheetCoordinator; + private @Nullable PriceInsightsBottomSheetCoordinator mBottomSheetCoordinatorForTesting; - @NonNull Supplier<CommerceBottomSheetContentController> mCommerceBottomSheetContentController; + Supplier<CommerceBottomSheetContentController> mCommerceBottomSheetContentController; public PriceInsightsButtonController( Context context, @@ -61,9 +63,7 @@ SnackbarManager snackbarManager, PriceInsightsDelegate priceInsightsDelegate, Drawable buttonDrawable, - @NonNull - Supplier<CommerceBottomSheetContentController> - commerceBottomSheetContentController) { + Supplier<CommerceBottomSheetContentController> commerceBottomSheetContentController) { super( tabSupplier, modalDialogManager,
diff --git a/chrome/browser/companion/text_finder/text_finder.cc b/chrome/browser/companion/text_finder/text_finder.cc index fc93923..08f67d83 100644 --- a/chrome/browser/companion/text_finder/text_finder.cc +++ b/chrome/browser/companion/text_finder/text_finder.cc
@@ -37,7 +37,8 @@ // Create an annotation agent for text finder, and bind to it. agent_container->CreateAgent( receiver_.BindNewPipeAndPassRemote(), agent_.BindNewPipeAndPassReceiver(), - blink::mojom::AnnotationType::kTextFinder, text_directive_, + blink::mojom::AnnotationType::kTextFinder, + blink::mojom::Selector::NewSerializedSelector(text_directive_), /*search_range_start_node_id=*/std::nullopt); }
diff --git a/chrome/browser/companion/text_finder/text_finder_manager_base_test.h b/chrome/browser/companion/text_finder/text_finder_manager_base_test.h index ff60600..e3a52b8 100644 --- a/chrome/browser/companion/text_finder/text_finder_manager_base_test.h +++ b/chrome/browser/companion/text_finder/text_finder_manager_base_test.h
@@ -31,7 +31,7 @@ void(mojo::PendingRemote<blink::mojom::AnnotationAgentHost>, mojo::PendingReceiver<blink::mojom::AnnotationAgent>, blink::mojom::AnnotationType, - const std::string& /*serialized_selector*/, + blink::mojom::SelectorPtr, std::optional<int> /*search_range_start_node_id*/)); MOCK_METHOD2(CreateAgentFromSelection,
diff --git a/chrome/browser/companion/text_finder/text_highlighter.cc b/chrome/browser/companion/text_finder/text_highlighter.cc index a64f677..553b6331 100644 --- a/chrome/browser/companion/text_finder/text_highlighter.cc +++ b/chrome/browser/companion/text_finder/text_highlighter.cc
@@ -29,7 +29,8 @@ // Create an annotation agent and bind to it. agent_container->CreateAgent( receiver_.BindNewPipeAndPassRemote(), agent_.BindNewPipeAndPassReceiver(), - blink::mojom::AnnotationType::kSharedHighlight, text_directive_, + blink::mojom::AnnotationType::kSharedHighlight, + blink::mojom::Selector::NewSerializedSelector(text_directive_), /*search_range_start_node_id=*/std::nullopt); }
diff --git a/chrome/browser/educational_tip/BUILD.gn b/chrome/browser/educational_tip/BUILD.gn index 4e027786..e29bf0e8 100644 --- a/chrome/browser/educational_tip/BUILD.gn +++ b/chrome/browser/educational_tip/BUILD.gn
@@ -35,6 +35,7 @@ "//chrome/browser/profiles/android:java", "//chrome/browser/segmentation_platform:factory_java", "//chrome/browser/signin/services/android:java", + "//chrome/browser/sync/android:java", "//chrome/browser/tab_group_sync:factory_java", "//chrome/browser/tab_group_sync:features_java", "//chrome/browser/tab_ui/android:java", @@ -47,6 +48,7 @@ "//components/saved_tab_groups/public:java", "//components/segmentation_platform/public:public_java", "//components/signin/public/android:java", + "//components/sync/android:sync_java", "//third_party/androidx:androidx_annotation_annotation_java", "//third_party/androidx:androidx_annotation_annotation_java", "//ui/android:ui_java", @@ -109,6 +111,7 @@ "//chrome/browser/magic_stack/android:java", "//chrome/browser/profiles/android:java", "//chrome/browser/signin/services/android:java", + "//chrome/browser/sync/android:java", "//chrome/browser/tab_group_sync:factory_java", "//chrome/browser/tab_group_sync:features_java", "//chrome/browser/tab_ui/android:java", @@ -118,6 +121,7 @@ "//components/feature_engagement/public:public_java", "//components/saved_tab_groups/public:java", "//components/segmentation_platform/public:public_java", + "//components/sync/android:sync_java", "//third_party/androidx:androidx_annotation_annotation_java", "//third_party/androidx:androidx_test_core_java", "//third_party/androidx:androidx_test_runner_java",
diff --git a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/HistorySyncPromoCoordinator.java b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/HistorySyncPromoCoordinator.java index a24460bcb..c64da34 100644 --- a/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/HistorySyncPromoCoordinator.java +++ b/chrome/browser/educational_tip/java/src/org/chromium/chrome/browser/educational_tip/cards/HistorySyncPromoCoordinator.java
@@ -12,12 +12,18 @@ import org.chromium.chrome.browser.educational_tip.EducationTipModuleActionDelegate; import org.chromium.chrome.browser.educational_tip.EducationalTipCardProvider; import org.chromium.chrome.browser.educational_tip.R; +import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.signin.services.IdentityServicesProvider; +import org.chromium.chrome.browser.sync.SyncServiceFactory; import org.chromium.components.signin.SigninFeatureMap; import org.chromium.components.signin.SigninFeatures; import org.chromium.components.signin.identitymanager.ConsentLevel; import org.chromium.components.signin.identitymanager.IdentityManager; import org.chromium.components.signin.identitymanager.PrimaryAccountChangeEvent; +import org.chromium.components.sync.SyncService; +import org.chromium.components.sync.UserSelectableType; + +import java.util.Set; /** * A coordinator that is responsible for displaying the history sync education tip that is show on @@ -25,7 +31,9 @@ */ @NullMarked public class HistorySyncPromoCoordinator - implements EducationalTipCardProvider, IdentityManager.Observer { + implements EducationalTipCardProvider, + IdentityManager.Observer, + SyncService.SyncStateChangedListener { private static final String HISTORY_OPT_IN_EDUCATIONAL_TIP_PARAM = "history_opt_in_educational_tip_param"; @@ -34,6 +42,7 @@ private final Runnable mOnClickedRunnable; private final Runnable mRemoveModuleRunnable; private final @Nullable IdentityManager mIdentityManager; + private final @Nullable SyncService mSyncService; public HistorySyncPromoCoordinator( Runnable onModuleClickedCallback, @@ -58,13 +67,16 @@ onModuleClickedCallback.run(); }); - assert mActionDelegate.getProfileSupplier().hasValue(); - mIdentityManager = - IdentityServicesProvider.get() - .getIdentityManager(mActionDelegate.getProfileSupplier().get()); + Profile profile = mActionDelegate.getProfileSupplier().get(); + assert profile != null; + mIdentityManager = IdentityServicesProvider.get().getIdentityManager(profile); assert mIdentityManager != null; mIdentityManager.addObserver(this); + + mSyncService = SyncServiceFactory.getForProfile(profile); + assert mSyncService != null; + mSyncService.addSyncStateChangedListener(this); } @Override @@ -126,9 +138,23 @@ } } + /** Implements {@link SyncService.SyncStateChangedListener} */ + @Override + public void syncStateChanged() { + assert mSyncService != null; + if (mSyncService + .getSelectedTypes() + .containsAll(Set.of(UserSelectableType.HISTORY, UserSelectableType.TABS))) { + mRemoveModuleRunnable.run(); + } + } + @Override public void destroy() { assert mIdentityManager != null; + assert mSyncService != null; + mIdentityManager.removeObserver(this); + mSyncService.removeSyncStateChangedListener(this); } }
diff --git a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleMediatorUnitTest.java b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleMediatorUnitTest.java index 9d7d69b..eda5b124 100644 --- a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleMediatorUnitTest.java +++ b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/EducationalTipModuleMediatorUnitTest.java
@@ -34,11 +34,13 @@ import org.chromium.chrome.browser.magic_stack.ModuleDelegate.ModuleType; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.signin.services.IdentityServicesProvider; +import org.chromium.chrome.browser.sync.SyncServiceFactory; import org.chromium.chrome.browser.ui.default_browser_promo.DefaultBrowserPromoUtils; import org.chromium.chrome.browser.ui.default_browser_promo.DefaultBrowserPromoUtils.DefaultBrowserPromoTriggerStateListener; import org.chromium.components.feature_engagement.FeatureConstants; import org.chromium.components.feature_engagement.Tracker; import org.chromium.components.signin.identitymanager.IdentityManager; +import org.chromium.components.sync.SyncService; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.shadows.ShadowAppCompatResources; @@ -57,6 +59,7 @@ @Mock private Tracker mTracker; @Mock private DefaultBrowserPromoUtils mMockDefaultBrowserPromoUtils; @Mock private IdentityServicesProvider mIdentityServicesProvider; + @Mock private SyncService mSyncService; @Mock private IdentityManager mIdentityManager; @Captor @@ -64,7 +67,6 @@ mDefaultBrowserPromoTriggerStateListener; ObservableSupplierImpl<Profile> mProfileSupplier; - private Context mContext; private @ModuleType int mDefaultModuleTypeForTesting; private EducationalTipModuleMediator mEducationalTipModuleMediator; @@ -83,6 +85,7 @@ when(mActionDelegate.getProfileSupplier()).thenReturn(mProfileSupplier); IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider); when(mIdentityServicesProvider.getIdentityManager(mProfile)).thenReturn(mIdentityManager); + SyncServiceFactory.setInstanceForTesting(mSyncService); mEducationalTipModuleMediator = new EducationalTipModuleMediator(
diff --git a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/HistorySyncPromoCoordinatorUnitTest.java b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/HistorySyncPromoCoordinatorUnitTest.java index b23851d..56a53cf 100644 --- a/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/HistorySyncPromoCoordinatorUnitTest.java +++ b/chrome/browser/educational_tip/junit/src/org/chromium/chrome/browser/educational_tip/HistorySyncPromoCoordinatorUnitTest.java
@@ -3,6 +3,7 @@ // found in the LICENSE file. package org.chromium.chrome.browser.educational_tip; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -22,9 +23,14 @@ import org.chromium.chrome.browser.educational_tip.cards.HistorySyncPromoCoordinator; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.signin.services.IdentityServicesProvider; +import org.chromium.chrome.browser.sync.SyncServiceFactory; import org.chromium.components.signin.identitymanager.ConsentLevel; import org.chromium.components.signin.identitymanager.IdentityManager; import org.chromium.components.signin.identitymanager.PrimaryAccountChangeEvent; +import org.chromium.components.sync.SyncService; +import org.chromium.components.sync.UserSelectableType; + +import java.util.Set; /** Test relating to {@link HistorySyncPromoCoordinator} */ @RunWith(BaseRobolectricTestRunner.class) @@ -36,7 +42,9 @@ @Mock private IdentityManager mIdentityManager; @Mock private Profile mProfile; @Mock private IdentityServicesProvider mIdentityServicesProvider; + @Mock private SyncService mSyncService; ObservableSupplierImpl<Profile> mProfileSupplier; + private HistorySyncPromoCoordinator mHistorySyncPromoCoordinator; @Before @@ -47,6 +55,8 @@ when(mActionDelegate.getProfileSupplier()).thenReturn(mProfileSupplier); IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider); when(mIdentityServicesProvider.getIdentityManager(mProfile)).thenReturn(mIdentityManager); + SyncServiceFactory.setInstanceForTesting(mSyncService); + mHistorySyncPromoCoordinator = new HistorySyncPromoCoordinator( mOnModuleClickedCallback, @@ -64,4 +74,25 @@ verify(mRemoveModuleCallback).run(); } + + @Test + @SmallTest + public void testCardRemovedOnWhenHistorySyncUpdated() { + when(mSyncService.getSelectedTypes()) + .thenReturn(Set.of(UserSelectableType.HISTORY, UserSelectableType.TABS)); + + mHistorySyncPromoCoordinator.syncStateChanged(); + + verify(mRemoveModuleCallback).run(); + } + + @Test + @SmallTest + public void testCardNotRemovedOnWhenOtherSyncTypeUpdated() { + when(mSyncService.getSelectedTypes()).thenReturn(Set.of(UserSelectableType.BOOKMARKS)); + + mHistorySyncPromoCoordinator.syncStateChanged(); + + verify(mRemoveModuleCallback, never()).run(); + } }
diff --git a/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.cc b/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.cc index c8c7ce5..7508800c 100644 --- a/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.cc +++ b/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.cc
@@ -250,7 +250,7 @@ } // static -void FileTransferAnalysisDelegate::SetFactorForTesting( +void FileTransferAnalysisDelegate::SetFactoryForTesting( FileTransferAnalysisDelegateFactory factory) { GetFactoryStorage() = factory; }
diff --git a/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.h b/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.h index 955c624..0face2d7 100644 --- a/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.h +++ b/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.h
@@ -110,7 +110,7 @@ virtual ~FileTransferAnalysisDelegate(); // Create the FileTransferAnalysisDelegate. This function uses the factory if - // it is set via `SetFactorForTesting()`. + // it is set via `SetFactoryForTesting()`. // // For `block_until_verdict == 0`, the `destination_url` has to point to the // copied file/directory and not its parent. If it points to the parent, all @@ -125,7 +125,7 @@ // Set a factory for the FileTransferAnalysisDelegate. // Can be used in testing to create `MockFileTransferAnalysisDelegate`s. - static void SetFactorForTesting(FileTransferAnalysisDelegateFactory factory); + static void SetFactoryForTesting(FileTransferAnalysisDelegateFactory factory); // Returns a vector with the AnalysisSettings for file transfers from the // respective source url to the destination_url.
diff --git a/chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.cc b/chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.cc index 9efc0e1..9c3b8e9 100644 --- a/chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.cc +++ b/chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.h" +#include "base/barrier_closure.h" #include "base/containers/contains.h" #include "base/containers/flat_map.h" #include "base/json/json_reader.h" @@ -316,12 +317,24 @@ content_transfer_method_ = expected_content_transfer_method; user_justification_ = expected_user_justification; + base::RepeatingClosure barrier_closure = base::BarrierClosure( + expected_filenames.size(), base::BindOnce( + [](base::RepeatingClosure closure) { + if (!closure.is_null()) { + closure.Run(); + } + }, + std::move(done_closure_))); + EXPECT_CALL(*client_, UploadSecurityEventReport) .Times(expected_filenames.size()) .WillRepeatedly( - [this](bool include_device_info, base::Value::Dict report, + [this, barrier_closure](bool include_device_info, base::Value::Dict report, base::OnceCallback<void(policy::CloudPolicyClient::Result)> - callback) { ValidateReport(&report); }); + callback) { + ValidateReport(&report); + barrier_closure.Run(); + }); } void EventReportValidator:: @@ -546,7 +559,7 @@ const std::string* url = actual_identity_dict.FindString(kKeyPasswordBreachIdentitiesUrl); const std::string* actual_username = actual_identity_dict.FindString( - kKeyPasswordBreachIdentitiesUsername); + kKeyPasswordBreachIdentitiesUsername); EXPECT_NE(nullptr, actual_username); const std::u16string username = base::UTF8ToUTF16(*actual_username); EXPECT_NE(nullptr, url);
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc index 23df0b2..22f954d 100644 --- a/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc +++ b/chrome/browser/extensions/api/developer_private/developer_private_api_unittest.cc
@@ -493,8 +493,9 @@ R"({ "name": "foo", "version": "1.0", - "manifest_version": 2, - "permissions": ["*://*/*"] + "manifest_version": 3, + "permissions": ["userScripts"], + "host_permissions": ["*://*/*"] })"; test_extension_dirs_.emplace_back();
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator_shared.cc b/chrome/browser/extensions/api/developer_private/extension_info_generator_shared.cc index b2a4d5ce..e6c15e15 100644 --- a/chrome/browser/extensions/api/developer_private/extension_info_generator_shared.cc +++ b/chrome/browser/extensions/api/developer_private/extension_info_generator_shared.cc
@@ -660,7 +660,7 @@ ExtensionSystem::Get(browser_context_)->user_script_manager(); if (user_script_manager) { // Not created in some unit tests. info.user_scripts_access.is_active = - user_script_manager->AreUserScriptsAllowed(extension, browser_context_); + user_script_manager->AreUserScriptsAllowed(extension); } // Install warnings, but only if unpacked, the error console isn't enabled
diff --git a/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc b/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc index 0b0c5d8..0d04d61 100644 --- a/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc +++ b/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
@@ -1612,8 +1612,8 @@ } // Test if unscoped suggestions send in zero suggest. -// TODO(crbug.com/409601761): Test is flaky on Linux. -#if BUILDFLAG(IS_LINUX) +// TODO(crbug.com/409601761): Test is flaky on Linux and ChromeOS. +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) #define MAYBE_UnscopedExtensionZeroSuggest DISABLED_UnscopedExtensionZeroSuggest #else #define MAYBE_UnscopedExtensionZeroSuggest UnscopedExtensionZeroSuggest
diff --git a/chrome/browser/extensions/api/user_scripts/user_scripts_apitest.cc b/chrome/browser/extensions/api/user_scripts/user_scripts_apitest.cc index 3e94918..befc890d 100644 --- a/chrome/browser/extensions/api/user_scripts/user_scripts_apitest.cc +++ b/chrome/browser/extensions/api/user_scripts/user_scripts_apitest.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/extensions/api/user_scripts/user_scripts_apitest.h" #include "base/feature_list.h" +#include "base/one_shot_event.h" #include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "base/test/scoped_run_loop_timeout.h" @@ -18,7 +19,11 @@ #include "content/public/test/browser_test.h" #include "content/public/test/test_navigation_observer.h" #include "extensions/browser/background_script_executor.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_util.h" +#include "extensions/browser/renderer_startup_helper.h" +#include "extensions/browser/user_script_manager.h" #include "extensions/common/extension_features.h" #include "extensions/common/features/feature_developer_mode_only.h" #include "extensions/common/user_scripts_allowed_state.h" @@ -563,7 +568,8 @@ // userScripts should remain disallowed after browser restart. if (GetParam()) { EXPECT_FALSE(GetCurrentUserScriptAllowedState( - util::GetBrowserContextId(profile()), extension_id)); + util::GetBrowserContextId(profile()), extension_id) + .value_or(false)); } else { EXPECT_FALSE(GetCurrentDeveloperMode(util::GetBrowserContextId(profile()))); } @@ -594,4 +600,183 @@ // another profile. Also write tests to confirm incognito split/span mode // behavior. +class MigrateUserScriptsAPITest : public ExtensionApiTest { + public: + MigrateUserScriptsAPITest() { + // Tests the migration capability by disabling the feature in only the + // PRE_<test_name> setup methods so the extension can be installed without + // the migration. Then the primary test method restarts the browser with the + // migration enabled. + scoped_feature_list_.InitWithFeatureState( + extensions_features::kUserScriptUserExtensionToggle, + /*enabled=*/!GetTestPreCount()); + } + + void SetUpOnMainThread() override { + ExtensionApiTest::SetUpOnMainThread(); + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(StartEmbeddedTestServer()); + } + + bool ExtensionPrefEnabled(const ExtensionId& extension_id, + const PrefMap& pref) { + bool user_scripts_allowed = false; + ExtensionPrefs::Get(profile())->ReadPrefAsBoolean(extension_id, pref, + &user_scripts_allowed); + return user_scripts_allowed; + } + + bool PrefEnabled(const PrefMap& pref) { + return ExtensionPrefs::Get(profile())->GetPrefAsBoolean(pref); + } + + void WaitForAllExtensionsToLoad() { + // Wait for all extensions to load. + SCOPED_TRACE("waiting for all extensions to load"); + ExtensionSystem* extension_system = ExtensionSystem::Get(profile()); + ASSERT_TRUE(extension_system); + base::RunLoop run_loop; + extension_system->ready().Post(FROM_HERE, run_loop.QuitClosure()); + run_loop.Run(); + } + + const ExtensionId GetTestExtensionId(const char* extension_name) { + ExtensionId extension_id; + const auto extensions = + ExtensionRegistry::Get(profile())->GenerateInstalledExtensionsSet(); + for (const auto& extension : extensions) { + if (extension->name() == extension_name) { + extension_id = extension->id(); + break; + } + } + return extension_id; + } + + void SetDevMode(bool enabled) { + util::SetDeveloperModeForProfile(profile(), + /*in_developer_mode=*/enabled); + + // Wait for the above IPC(s) to send. + RendererStartupHelper* renderer_startup_helper = + RendererStartupHelperFactory::GetForBrowserContext(profile()); + renderer_startup_helper->FlushAllForTesting(); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +// Installs an extension without the user script permission prior to the +// migration. +IN_PROC_BROWSER_TEST_F(MigrateUserScriptsAPITest, + PRE_ExtensionWithoutPermission_Allowed_AfterMigration) { + const Extension* extension = LoadExtension(test_data_dir_.AppendASCII( + "user_scripts/migration_tests/extension_without_userscript_permission")); + ASSERT_TRUE(extension); + + // Confirm no migration has occurred. + ASSERT_FALSE(PrefEnabled(UserScriptManager::kUserScriptsToggleMigratedPref)); +} + +// Tests that extensions that do not have permission to use the user scripts API +// have the allowed preference set to false after migration. +IN_PROC_BROWSER_TEST_F(MigrateUserScriptsAPITest, + ExtensionWithoutPermission_Allowed_AfterMigration) { + WaitForAllExtensionsToLoad(); + + // Find the extension's ID so we can make some assertions. + ExtensionId extension_id = + GetTestExtensionId("extension_without_userscript_permission"); + ASSERT_FALSE(extension_id.empty()); + + // Confirm migration occurred and extension disallowed. + EXPECT_TRUE(PrefEnabled(UserScriptManager::kUserScriptsToggleMigratedPref)); + EXPECT_FALSE(ExtensionPrefEnabled( + extension_id, UserScriptManager::kUserScriptsAllowedPref)); +} + +// Installs two extensions (one enabled and one disabled) and disables dev mode +// prior to the migration. +IN_PROC_BROWSER_TEST_F(MigrateUserScriptsAPITest, + PRE_DevModeOff_Disallowed_AfterMigration) { + const Extension* enabled_extension = LoadExtension(test_data_dir_.AppendASCII( + "user_scripts/migration_tests/extension_with_userscript_permission")); + ASSERT_TRUE(enabled_extension); + const Extension* disabled_extension = + LoadExtension(test_data_dir_.AppendASCII("user_scripts/allowed_tests")); + ASSERT_TRUE(disabled_extension); + // Confirm no migration has occurred. + ASSERT_FALSE(PrefEnabled(UserScriptManager::kUserScriptsToggleMigratedPref)); + + // Disable dev mode so it is off during the migration. + SetDevMode(/*enabled=*/false); + + DisableExtension(disabled_extension->id()); +} + +// Tests that user script API extensions where dev mode is off have the allowed +// preference set to false after restart (regardless if the extension is enabled +// or disabled). +IN_PROC_BROWSER_TEST_F(MigrateUserScriptsAPITest, + DevModeOff_Disallowed_AfterMigration) { + WaitForAllExtensionsToLoad(); + + // Find the extension's ID so we can make some assertions. + ExtensionId enabled_extension_id = + GetTestExtensionId("extension_with_userscript_permission"); + ASSERT_FALSE(enabled_extension_id.empty()); + ExtensionId disabled_extension_id = GetTestExtensionId("Test"); + ASSERT_FALSE(disabled_extension_id.empty()); + + // Confirm extension is migrated and disallowed. + EXPECT_TRUE(PrefEnabled(UserScriptManager::kUserScriptsToggleMigratedPref)); + EXPECT_FALSE(ExtensionPrefEnabled( + enabled_extension_id, UserScriptManager::kUserScriptsAllowedPref)); + EXPECT_FALSE(ExtensionPrefEnabled( + disabled_extension_id, UserScriptManager::kUserScriptsAllowedPref)); +} + +// Installs two extensions (one enabled and one disabled) and enables dev mode +// prior to the migration. +IN_PROC_BROWSER_TEST_F(MigrateUserScriptsAPITest, + PRE_DevModeOn_Allowed_AfterMigration) { + const Extension* enabled_extension = LoadExtension(test_data_dir_.AppendASCII( + "user_scripts/migration_tests/extension_with_userscript_permission")); + ASSERT_TRUE(enabled_extension); + const Extension* disabled_extension = + LoadExtension(test_data_dir_.AppendASCII("user_scripts/allowed_tests")); + ASSERT_TRUE(disabled_extension); + + // Confirm no migration has occurred. + ASSERT_FALSE(PrefEnabled(UserScriptManager::kUserScriptsToggleMigratedPref)); + + // Enable dev mode so it is on during the migration. + SetDevMode(/*enabled=*/true); + + DisableExtension(disabled_extension->id()); +} + +// Tests that user script API extensions where dev mode is on have the allowed +// preference set to true after restart (regardless if the extension is enabled +// or disabled). +IN_PROC_BROWSER_TEST_F(MigrateUserScriptsAPITest, + DevModeOn_Allowed_AfterMigration) { + WaitForAllExtensionsToLoad(); + + // Find the extension's ID so we can make some assertions. + ExtensionId enabled_extension_id = + GetTestExtensionId("extension_with_userscript_permission"); + ASSERT_FALSE(enabled_extension_id.empty()); + ExtensionId disabled_extension_id = GetTestExtensionId("Test"); + ASSERT_FALSE(disabled_extension_id.empty()); + + // Confirm extension is migrated and disallowed. + EXPECT_TRUE(PrefEnabled(UserScriptManager::kUserScriptsToggleMigratedPref)); + EXPECT_TRUE(ExtensionPrefEnabled(enabled_extension_id, + UserScriptManager::kUserScriptsAllowedPref)); + EXPECT_TRUE(ExtensionPrefEnabled(disabled_extension_id, + UserScriptManager::kUserScriptsAllowedPref)); +} + } // namespace extensions
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index eeb00a7..75358ea0 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -2245,6 +2245,11 @@ "expiry_milestone": 140 }, { + "name": "display-edge-to-edge-fullscreen", + "owners": [ "wzwonarz@google.com", "cros-web-apps-krk@google.com" ], + "expiry_milestone": 140 + }, + { "name": "document-picture-in-picture-animate-resize", "owners": [ "steimel@chromium.org", "media-dev@chromium.org" ], "expiry_milestone": 150 @@ -5489,6 +5494,11 @@ "expiry_milestone": 132 }, { + "name": "import-passwords-from-safari", + "owners": [ "sinhasourav@google.com", "sugoi@chromium.org", "tmartino@chromium.org", "bling-transactions@google.com" ], + "expiry_milestone": 140 + }, + { "name": "improved-keyboard-shortcuts", "owners": [ "jimmyxgong@chromium.org", @@ -5513,14 +5523,6 @@ "expiry_milestone": 136 }, { - "name": "improved-signin-ui-on-desktop", - "owners": [ - "uno-desktop@google.com", - "//components/signin/OWNERS" - ], - "expiry_milestone": 136 - }, - { "name": "in-product-help-demo-mode-choice", "owners": [ "dtrainor@chromium.org", "nyquist@chromium.org" ], // This flag is used by teams as they develop in-product help integrations, @@ -7232,14 +7234,6 @@ "expiry_milestone": 140 }, { - "name": "outline-silhouette-icon", - "owners": [ - "uno-desktop@google.com", - "//components/signin/OWNERS" - ], - "expiry_milestone": 136 - }, - { "name": "overlay-scrollbars", "owners": [ "zhzhliu@chromium.org",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 738699bc..93635d9 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -469,6 +469,12 @@ const char kDisableInstanceLimitDescription[] = "Disable limit on number of app instances allowed (current limit is 5)."; +const char kDisplayEdgeToEdgeFullscreenName[] = + "Enable Display Edge to Edge Fullscreen"; +const char kDisplayEdgeToEdgeFullscreenDescription[] = + "Enable Display Edge to Edge Fullscreen when Chrome on Android is running " + "in a windowing mode."; + const char kClearInstanceInfoWhenClosedIntentionallyName[] = "Clear Instance Info When Closed Intentionally"; const char kClearInstanceInfoWhenClosedIntentionallyDescription[] = @@ -1266,11 +1272,6 @@ "When adding automatic captions to images, use a different route to " "acquire descriptions."; -const char kImprovedSigninUIOnDesktopName[] = "Improved signin UI"; -const char kImprovedSigninUIOnDesktopDescription[] = - "Enables redesign of profile menu and signin settings ; new signin promos " - "and better signin errors."; - const char kImprovedSettingsUIOnDesktopName[] = "Improved settings UI"; const char kImprovedSettingsUIOnDesktopDescription[] = "Enables redesign of signin settings and better signin errors in " @@ -2185,10 +2186,6 @@ "A reactive programming primitive for ergonomically handling streams of " "async data. See https://github.com/WICG/observable."; -const char kOutlineSilhouetteIconName[] = "Outline Silhouette Icon"; -const char kOutlineSilhouetteIconDescription[] = - "Enables the improved silhouette icon for signed out profiles"; - const char kCastMessageLoggingName[] = "Enables logging of all Cast messages."; const char kCastMessageLoggingDescription[] = "Enables logging of all messages exchanged between websites, Chrome, "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index d0f7b558..523c32d 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -297,6 +297,9 @@ extern const char kDisableInstanceLimitName[]; extern const char kDisableInstanceLimitDescription[]; +extern const char kDisplayEdgeToEdgeFullscreenName[]; +extern const char kDisplayEdgeToEdgeFullscreenDescription[]; + extern const char kClearInstanceInfoWhenClosedIntentionallyName[]; extern const char kClearInstanceInfoWhenClosedIntentionallyDescription[]; #endif @@ -367,9 +370,6 @@ extern const char kImageDescriptionsAlternateRoutingName[]; extern const char kImageDescriptionsAlternateRoutingDescription[]; -extern const char kImprovedSigninUIOnDesktopName[]; -extern const char kImprovedSigninUIOnDesktopDescription[]; - extern const char kImprovedSettingsUIOnDesktopName[]; extern const char kImprovedSettingsUIOnDesktopDescription[]; @@ -1255,9 +1255,6 @@ extern const char kObservableAPIName[]; extern const char kObservableAPIDescription[]; -extern const char kOutlineSilhouetteIconName[]; -extern const char kOutlineSilhouetteIconDescription[]; - extern const char kCastMessageLoggingName[]; extern const char kCastMessageLoggingDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc index efef92d..98c63a9 100644 --- a/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -136,6 +136,7 @@ &features::kBackForwardCache, &features::kBoardingPassDetector, &features::kContextMenuEmptySpace, + &features::kDisplayEdgeToEdgeFullscreen, &features::kHttpsFirstBalancedMode, &features::kNetworkServiceInProcess, &features::kElasticOverscroll, @@ -294,6 +295,7 @@ &kLegacyTabStateDeprecation, &kLockBackPressHandlerAtStart, &kIncognitoScreenshot, + &kKeyboardEscBackNavigation, &kLensOnQuickActionSearchWidget, &kMagicStackAndroid, &kMayLaunchUrlUsesSeparateStoragePartition, @@ -941,6 +943,10 @@ "IncognitoScreenshot", base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kKeyboardEscBackNavigation, + "KeyboardEscBackNavigation", + base::FEATURE_DISABLED_BY_DEFAULT); + BASE_FEATURE(kMagicStackAndroid, "MagicStackAndroid", base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h index 435be80..116890b 100644 --- a/chrome/browser/flags/android/chrome_feature_list.h +++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -135,6 +135,7 @@ BASE_DECLARE_FEATURE(kLockBackPressHandlerAtStart); BASE_DECLARE_FEATURE(kIncognitoScreenshot); BASE_DECLARE_FEATURE(kImprovedA2HS); +BASE_DECLARE_FEATURE(kKeyboardEscBackNavigation); BASE_DECLARE_FEATURE(kLanguagesPreference); BASE_DECLARE_FEATURE(kLegacyTabStateDeprecation); BASE_DECLARE_FEATURE(kLensOnQuickActionSearchWidget);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java index e8cafbd..5bbcba69 100644 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -339,6 +339,7 @@ public static final String DISCO_FEED_ENDPOINT = "DiscoFeedEndpoint"; public static final String DISPLAY_WILDCARD_CONTENT_SETTINGS = "DisplayWildcardInContentSettings"; + public static final String DISPLAY_EDGE_TO_EDGE_FULLSCREEN = "DisplayEdgeToEdgeFullscreen"; public static final String DRAW_CUTOUT_EDGE_TO_EDGE = "DrawCutoutEdgeToEdge"; public static final String DRAW_KEY_NATIVE_EDGE_TO_EDGE = "DrawKeyNativeEdgeToEdge"; public static final String DYNAMIC_SAFE_AREA_INSETS = "DynamicSafeAreaInsets"; @@ -395,6 +396,7 @@ public static final String HTTPS_FIRST_BALANCED_MODE = "HttpsFirstBalancedMode"; public static final String INCOGNITO_SCREENSHOT = "IncognitoScreenshot"; public static final String IP_PROTECTION_UX = "IpProtectionUx"; + public static final String KEYBOARD_ESC_BACK_NAVIGAION = "KeyboardEscBackNavigation"; public static final String LEGACY_TAB_STATE_DEPRECATION = "LegacyTabStateDeprecation"; public static final String LENS_ON_QUICK_ACTION_SEARCH_WIDGET = "LensOnQuickActionSearchWidget"; public static final String LINKED_SERVICES_SETTING = "LinkedServicesSetting"; @@ -751,6 +753,8 @@ public static final CachedFlag sHideTabletToolbarDownloadButton = newCachedFlag(HIDE_TABLET_TOOLBAR_DOWNLOAD_BUTTON, true); public static final CachedFlag sHistoryPaneAndroid = newCachedFlag(HISTORY_PANE_ANDROID, false); + public static final CachedFlag sKeyboardEscBackNavigation = + newCachedFlag(KEYBOARD_ESC_BACK_NAVIGAION, false); public static final CachedFlag sLegacyTabStateDeprecation = newCachedFlag( LEGACY_TAB_STATE_DEPRECATION, @@ -908,6 +912,7 @@ sGridTabSwitcherUpdate, sHideTabletToolbarDownloadButton, sHistoryPaneAndroid, + sKeyboardEscBackNavigation, sLegacyTabStateDeprecation, sLockBackPressHandlerAtStart, sMagicStackAndroid,
diff --git a/chrome/browser/glic/host/OWNERS b/chrome/browser/glic/host/OWNERS index 02c6299..9db4508 100644 --- a/chrome/browser/glic/host/OWNERS +++ b/chrome/browser/glic/host/OWNERS
@@ -4,5 +4,9 @@ harringtond@chromium.org wry@chromium.org +per-file glic_actor_controller.cc=file://chrome/browser/actor/OWNERS +per-file glic_actor_controller.h=file://chrome/browser/actor/OWNERS +per-file glic_actor_controller_interactive_uitest.cc=file://chrome/browser/actor/OWNERS + per-file *.mojom=set noparent per-file *.mojom=file://ipc/SECURITY_OWNERS \ No newline at end of file
diff --git a/chrome/browser/glic/host/glic.mojom b/chrome/browser/glic/host/glic.mojom index 317273b3..2f682016 100644 --- a/chrome/browser/glic/host/glic.mojom +++ b/chrome/browser/glic/host/glic.mojom
@@ -240,6 +240,7 @@ union ScrollToSelector { ScrollToTextSelector exact_text_selector; ScrollToTextFragmentSelector text_fragment_selector; + ScrollToNodeSelector node_selector; }; // Used to specify exact text to scroll to and highlight. @@ -266,6 +267,13 @@ int32? search_range_start_node_id; }; +// Used to specify a node to scroll to and highlight. All text within the node +// will be highlighted. +struct ScrollToNodeSelector { + // DOMNodeId of the node whose text will be highlighted (and scrolled to). + int32 node_id; +}; + // Note: This must be kept in sync with the corresponding enum (same name) in // glic_api.ts // Next version: 6
diff --git a/chrome/browser/glic/host/glic_annotation_manager.cc b/chrome/browser/glic/host/glic_annotation_manager.cc index 8e1abeb..b55f7ce5 100644 --- a/chrome/browser/glic/host/glic_annotation_manager.cc +++ b/chrome/browser/glic/host/glic_annotation_manager.cc
@@ -39,6 +39,7 @@ mojom::ScrollToSelector* selector = params->selector.get(); std::optional<shared_highlighting::TextFragment> text_fragment; std::optional<int> search_range_start_node_id = std::nullopt; + std::optional<int> node_id = std::nullopt; if (selector->is_exact_text_selector()) { auto* exact_text_selector = selector->get_exact_text_selector().get(); @@ -83,6 +84,13 @@ text_fragment = shared_highlighting::TextFragment(text_start, text_end, /*prefix=*/std::string(), /*suffix=*/std::string()); + } else if (selector->is_node_selector()) { + if (!params->document_id) { + mojo::ReportBadMessage( + "When node_id is set, document_id should be set as well."); + return; + } + node_id = selector->get_node_selector()->node_id; } else { mojo::ReportBadMessage( "The client should have verified that one of the selector types was " @@ -90,9 +98,9 @@ return; } - // The only support selector types currently are text and text fragment, so - // this must have a non-empty value. - CHECK(text_fragment.has_value()); + // "exact_text" and "text_fragment" selectors will set `text_fragment`, "node" + // selector will set `node_id`. + CHECK(text_fragment.has_value() || node_id.has_value()); auto focused_tab_data = service_->GetFocusedTabData(); content::Page* focused_primary_page = nullptr; @@ -137,15 +145,22 @@ } } + blink::mojom::SelectorPtr blink_mojom_selector; + if (text_fragment) { + blink_mojom_selector = blink::mojom::Selector::NewSerializedSelector( + text_fragment->ToEscapedString( + shared_highlighting::TextFragment::EscapedStringFormat:: + kWithoutTextDirective)); + } else { + blink_mojom_selector = blink::mojom::Selector::NewNodeId(node_id.value()); + } + mojo::PendingReceiver<blink::mojom::AnnotationAgentHost> agent_host_receiver; mojo::Remote<blink::mojom::AnnotationAgent> agent_remote; annotation_agent_container_->remote->CreateAgent( agent_host_receiver.InitWithNewPipeAndPassRemote(), agent_remote.BindNewPipeAndPassReceiver(), - blink::mojom::AnnotationType::kGlic, - text_fragment->ToEscapedString( - shared_highlighting::TextFragment::EscapedStringFormat:: - kWithoutTextDirective), + blink::mojom::AnnotationType::kGlic, std::move(blink_mojom_selector), search_range_start_node_id); annotation_task_ = std::make_unique<AnnotationTask>( this, std::move(agent_remote), std::move(agent_host_receiver),
diff --git a/chrome/browser/glic/host/glic_annotation_manager_interactive_uitest.cc b/chrome/browser/glic/host/glic_annotation_manager_interactive_uitest.cc index 878a7a3..93b23fe 100644 --- a/chrome/browser/glic/host/glic_annotation_manager_interactive_uitest.cc +++ b/chrome/browser/glic/host/glic_annotation_manager_interactive_uitest.cc
@@ -10,6 +10,7 @@ #include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "base/time/time.h" +#include "chrome/browser/actor/actor_test_util.h" #include "chrome/browser/glic/host/context/glic_page_context_fetcher.h" #include "chrome/browser/glic/host/glic.mojom-shared.h" #include "chrome/browser/glic/test_support/interactive_glic_test.h" @@ -63,7 +64,7 @@ pending_host_remote, mojo::PendingReceiver<blink::mojom::AnnotationAgent> agent_receiver, blink::mojom::AnnotationType type, - const std::string& serialized_selector, + const blink::mojom::SelectorPtr selector, std::optional<int> search_range_start_node_id) override { if (agent_receiver_.is_bound()) { agent_disconnected_ = false; @@ -361,6 +362,16 @@ std::move(text_start), std::move(text_end), std::move(node_id_cb)); } + Selector NodeIdSelector(NodeIdCallback node_id_cb) { + return base::BindOnce( + [](NodeIdCallback node_id_cb) { + return base::Value::Dict().Set( + "node", + base::Value::Dict().Set("nodeId", std::move(node_id_cb).Run())); + }, + std::move(node_id_cb)); + } + FakeAnnotationAgentContainer* fake_service() { return fake_service_.get(); } // Returns the main frame's document identifier in `annotated_page_content_`. @@ -729,6 +740,37 @@ mojom::ScrollToErrorReason::kSearchRangeInvalid)); } +IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, NodeIdSelector) { + NodeIdCallback text_node = base::BindLambdaForTesting([&]() { + return actor::FindContentNodeId(*browser() + ->tab_strip_model() + ->GetActiveWebContents() + ->GetPrimaryMainFrame(), + "p#text") + .value(); + }); + RunTestSequence(InstrumentTab(kActiveTabId), + NavigateWebContents( + kActiveTabId, embedded_test_server()->GetURL( + "/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), + GetPageContextFromFocusedTab(), // + ScrollToWithDocumentId(NodeIdSelector(std::move(text_node)))); +} + +IN_PROC_BROWSER_TEST_F(GlicAnnotationManagerUiTest, + NodeIdSelectorWithInvalidNode) { + RunTestSequence(InstrumentTab(kActiveTabId), + NavigateWebContents( + kActiveTabId, embedded_test_server()->GetURL( + "/scrollable_page_with_content.html")), + OpenGlicWindow(GlicWindowMode::kDetached), + GetPageContextFromFocusedTab(), // + ScrollToWithDocumentIdExpectingError( + NodeIdSelector(base::BindOnce([]() { return -1; })), + mojom::ScrollToErrorReason::kNoMatchFound)); +} + class GlicAnnotationManagerWithScrollToDisabledUiTest : public InteractiveGlicTest { public:
diff --git a/chrome/browser/metrics/family_link_user_metrics_provider_unittest.cc b/chrome/browser/metrics/family_link_user_metrics_provider_unittest.cc index fea948f..cc82113 100644 --- a/chrome/browser/metrics/family_link_user_metrics_provider_unittest.cc +++ b/chrome/browser/metrics/family_link_user_metrics_provider_unittest.cc
@@ -7,7 +7,6 @@ #include <string> #include "base/test/metrics/histogram_tester.h" -#include "base/test/scoped_feature_list.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h" @@ -26,7 +25,6 @@ #include "components/supervised_user/core/browser/supervised_user_service.h" #include "components/supervised_user/core/browser/supervised_user_url_filter.h" #include "components/supervised_user/core/browser/supervised_user_utils.h" -#include "components/supervised_user/core/common/features.h" #include "components/supervised_user/core/common/pref_names.h" #include "components/supervised_user/core/common/supervised_user_constants.h" #include "content/public/test/browser_task_environment.h" @@ -317,25 +315,13 @@ class FamilyLinkUserMetricsProviderTestWithExtensionsPermissionsEnabled : public FamilyLinkUserMetricsProviderTest { protected: - FamilyLinkUserMetricsProviderTestWithExtensionsPermissionsEnabled() { - feature_list_.InitWithFeatures( - { -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) - supervised_user:: - kEnableExtensionsPermissionsForSupervisedUsersOnDesktop, -#endif - supervised_user:: - kEnableSupervisedUserSkipParentApprovalToInstallExtensions}, - {}); - } + FamilyLinkUserMetricsProviderTestWithExtensionsPermissionsEnabled() = default; void SetExtensionToggleStateForSupervisedUser(Profile* profile, bool toggle_state) { supervised_user_test_util::SetSkipParentApprovalToInstallExtensionsPref( profile, toggle_state); } - - base::test::ScopedFeatureList feature_list_; }; TEST_F(FamilyLinkUserMetricsProviderTestWithExtensionsPermissionsEnabled,
diff --git a/chrome/browser/password_edit_dialog/android/password_edit_dialog_bridge_delegate.h b/chrome/browser/password_edit_dialog/android/password_edit_dialog_bridge_delegate.h index 21ed81b1..4884e856 100644 --- a/chrome/browser/password_edit_dialog/android/password_edit_dialog_bridge_delegate.h +++ b/chrome/browser/password_edit_dialog/android/password_edit_dialog_bridge_delegate.h
@@ -22,7 +22,7 @@ const std::u16string& password) = 0; // Returns true if the specified credential will be saved/updated in the - // profile store. + // account storage. virtual bool IsUsingAccountStorage(const std::u16string& username) = 0; };
diff --git a/chrome/browser/password_manager/android/BUILD.gn b/chrome/browser/password_manager/android/BUILD.gn index 17dfe93..e74fba9 100644 --- a/chrome/browser/password_manager/android/BUILD.gn +++ b/chrome/browser/password_manager/android/BUILD.gn
@@ -649,6 +649,7 @@ "//chrome/browser", "//chrome/browser/autofill", "//chrome/browser/keyboard_accessory/test_utils/android", + "//chrome/browser/password_edit_dialog/android:android", "//chrome/browser/password_manager/android/access_loss:test_support", "//chrome/browser/password_manager/android/add_username_dialog:android", "//chrome/browser/prefs",
diff --git a/chrome/browser/password_manager/android/save_update_password_message_delegate.cc b/chrome/browser/password_manager/android/save_update_password_message_delegate.cc index 5ad35475..b9171a8 100644 --- a/chrome/browser/password_manager/android/save_update_password_message_delegate.cc +++ b/chrome/browser/password_manager/android/save_update_password_message_delegate.cc
@@ -10,6 +10,7 @@ #include "base/android/build_info.h" #include "base/check.h" +#include "base/feature_list.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/strings/utf_string_conversions.h" @@ -25,6 +26,7 @@ #include "chrome/grit/branded_strings.h" #include "chrome/grit/generated_resources.h" #include "components/messages/android/message_dispatcher_bridge.h" +#include "components/password_manager/core/browser/features/password_features.h" #include "components/password_manager/core/browser/password_form.h" #include "components/password_manager/core/browser/password_form_metrics_recorder.h" #include "components/password_manager/core/browser/password_manager_metrics_util.h" @@ -452,16 +454,23 @@ return false; } - // Pre-UPM the profile storage was used in fact as the account store (when - // sync is on). So this is the cut-off for the users who are not using UPM - // (this evaluates to using account store when the user is syncing and using - // profile store when they are not syncing). Profile* profile = Profile::FromBrowserContext(web_contents_->GetBrowserContext()); - if (!UsesSplitStoresAndUPMForLocal(profile->GetPrefs())) { + + // Pre-UPM there was a single storage, which would either store account + // storage credentials (when sync was on for passwords) or local passwords. + // After login db deprecation, pre-UPM clients can no longer save passwords, + // so this code is only reached for clients that have access to split stores. + if (!base::FeatureList::IsEnabled( + password_manager::features::kLoginDbDeprecationAndroid) && + !UsesSplitStoresAndUPMForLocal(profile->GetPrefs())) { return account_email_.has_value(); } + // After UPM, an updated credential can be saved either to the local or + // account storage, so the credential itself needs to be checked to determine + // whether account storage messaging needs to be displayed. + // Copy the pending password form here and assign the new username. password_manager::PasswordForm updated_credentials = passwords_state_.form_manager()->GetPendingCredentials();
diff --git a/chrome/browser/password_manager/android/save_update_password_message_delegate_unittest.cc b/chrome/browser/password_manager/android/save_update_password_message_delegate_unittest.cc index 5876386..41f2229a 100644 --- a/chrome/browser/password_manager/android/save_update_password_message_delegate_unittest.cc +++ b/chrome/browser/password_manager/android/save_update_password_message_delegate_unittest.cc
@@ -15,8 +15,10 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" #include "chrome/browser/android/android_theme_resources.h" #include "chrome/browser/android/resource_mapper.h" +#include "chrome/browser/password_edit_dialog/android/password_edit_dialog_bridge_delegate.h" #include "chrome/browser/password_manager/android/access_loss/mock_password_access_loss_warning_bridge.h" #include "chrome/browser/password_manager/chrome_password_manager_client.h" #include "chrome/browser/ui/autofill/chrome_autofill_client.h" @@ -25,6 +27,7 @@ #include "chrome/test/base/chrome_render_view_host_test_harness.h" #include "components/browser_ui/device_lock/android/device_lock_bridge.h" #include "components/messages/android/mock_message_dispatcher_bridge.h" +#include "components/password_manager/core/browser/features/password_features.h" #include "components/password_manager/core/browser/mock_password_form_manager_for_ui.h" #include "components/password_manager/core/browser/password_form.h" #include "components/password_manager/core/browser/password_form_metrics_recorder.h" @@ -1504,3 +1507,52 @@ password_manager::metrics_util::SaveFlowStep::kSavePromptShown, 1); DismissMessage(messages::DismissReason::UNKNOWN); } + +// Tests `IsUsingAccountStorage` returns false if the credential being +// updated comes from the local storage, despite the user being signed in, +// if the credential comes from the profile store. +TEST_F(SaveUpdatePasswordMessageDelegateTest, + LocalCredentialNotUsingAccountStorage_DbDeprecationOn) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + password_manager::features::kLoginDbDeprecationAndroid); + profile()->GetPrefs()->SetInteger( + password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores, + static_cast<int>( + password_manager::prefs::UseUpmLocalAndSeparateStoresState::kOff)); + SetPendingCredentials(kUsername, kPassword, /*is_account_store=*/false); + auto form_manager = + CreateFormManager(GURL(kDefaultUrl), empty_best_matches()); + EnqueueMessage(std::move(form_manager), /*user_signed_in=*/true, + /*update_password=*/true); + PasswordEditDialogBridgeDelegate* edit_dialog_delegate = + get_password_edit_dialog_bridge_delegate(); + + EXPECT_FALSE(edit_dialog_delegate->IsUsingAccountStorage(kUsername)); + + DismissMessage(messages::DismissReason::UNKNOWN); +} + +// Tests `IsUsingAccountStorage` returns true if the crential comes from +// the account store. +TEST_F(SaveUpdatePasswordMessageDelegateTest, + CredentialUsingAccountStorage_DbDeprecationOn) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + password_manager::features::kLoginDbDeprecationAndroid); + profile()->GetPrefs()->SetInteger( + password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores, + static_cast<int>( + password_manager::prefs::UseUpmLocalAndSeparateStoresState::kOff)); + SetPendingCredentials(kUsername, kPassword, /*is_account_store=*/true); + auto form_manager = + CreateFormManager(GURL(kDefaultUrl), empty_best_matches()); + EnqueueMessage(std::move(form_manager), /*user_signed_in=*/true, + /*update_password=*/true); + PasswordEditDialogBridgeDelegate* edit_dialog_delegate = + get_password_edit_dialog_bridge_delegate(); + + EXPECT_TRUE(edit_dialog_delegate->IsUsingAccountStorage(kUsername)); + + DismissMessage(messages::DismissReason::UNKNOWN); +}
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc index 798d0b7..0ea1369 100644 --- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc +++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -1946,6 +1946,9 @@ { key::kProfileSeparationDomainExceptionList, prefs::kProfileSeparationDomainExceptionList, base::Value::Type::LIST }, + { key::kProfileSeparationSettings, + prefs::kProfileSeparationSettings, + base::Value::Type::INTEGER }, { key::kLiveTranslateEnabled, prefs::kLiveTranslateEnabled, base::Value::Type::BOOLEAN }, @@ -2812,11 +2815,14 @@ key::kBrowserContextAwareAccessSignalsAllowlist, enterprise_connectors::kBrowserContextAwareAccessSignalsAllowlistPref, chrome_schema)); - handlers->AddHandler(std::make_unique<SimpleDeprecatingPolicyHandler>( - std::make_unique<ManagedAccountRestrictionsPolicyHandler>(chrome_schema), - std::make_unique<SimplePolicyHandler>(key::kProfileSeparationSettings, - prefs::kProfileSeparationSettings, - base::Value::Type::INTEGER))); + handlers->AddHandler( + std::make_unique<SingleDeprecatedPolicyToMultipleNewPolicyHandler>( + std::make_unique<ManagedAccountRestrictionsPolicyHandler>( + chrome_schema), + std::vector<std::string>{ + key::kProfileSeparationSettings, + key::kProfileSeparationDataMigrationSettings, + key::kProfileSeparationDomainExceptionList})); handlers->AddHandler(std::make_unique<SimpleDeprecatingPolicyHandler>( std::make_unique<SimplePolicyHandler>(
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 412675b2..aa32620 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -378,8 +378,8 @@ #include "chrome/browser/ash/login/security_token_session_controller.h" #include "chrome/browser/ash/login/session/chrome_session_manager.h" #include "chrome/browser/ash/login/session/user_session_manager.h" +#include "chrome/browser/ash/login/signin/legacy_token_handle_fetcher.h" #include "chrome/browser/ash/login/signin/signin_error_notifier.h" -#include "chrome/browser/ash/login/signin/token_handle_fetcher.h" #include "chrome/browser/ash/login/startup_utils.h" #include "chrome/browser/ash/login/users/avatar/user_image_manager_impl.h" #include "chrome/browser/ash/login/users/avatar/user_image_prefs.h" @@ -2308,7 +2308,7 @@ #endif #if BUILDFLAG(IS_CHROMEOS) ash::RegisterUserProfilePrefs(registry, locale); - ash::TokenHandleFetcher::RegisterPrefs(registry); + ash::LegacyTokenHandleFetcher::RegisterPrefs(registry); #endif }
diff --git a/chrome/browser/privacy_sandbox/notice/notice_storage.cc b/chrome/browser/privacy_sandbox/notice/notice_storage.cc index 81b34e0..ad8efed 100644 --- a/chrome/browser/privacy_sandbox/notice/notice_storage.cc +++ b/chrome/browser/privacy_sandbox/notice/notice_storage.cc
@@ -172,6 +172,13 @@ } } +void MaybeEraseV1Fields(PrefService* pref_service, std::string_view notice) { + ScopedDictPrefUpdate update(pref_service, kNoticeDataPath); + update->RemoveByDottedPath(CreatePrefPath(notice, kNoticeActionTakenKey)); + update->RemoveByDottedPath(CreatePrefPath(notice, kNoticeActionTakenTimeKey)); + update->RemoveByDottedPath(CreatePrefPath(notice, kNoticeLastShownKey)); +} + } // namespace std::optional<base::Time> GetNoticeFirstShownFromEvents( @@ -229,6 +236,28 @@ NoticeStorageData& NoticeStorageData::operator=(NoticeStorageData&& data) = default; +bool NoticeStorageData::operator==(const NoticeStorageData& other) const { + if (schema_version != other.schema_version || + chrome_version != other.chrome_version || + notice_events.size() != other.notice_events.size()) { + return false; + } + for (size_t i = 0; i < notice_events.size(); ++i) { + const auto& lhs_event_ptr = notice_events[i]; + const auto& rhs_event_ptr = other.notice_events[i]; + if (!lhs_event_ptr && !rhs_event_ptr) { + continue; + } + if (!lhs_event_ptr || !rhs_event_ptr) { + return false; + } + if (!(*lhs_event_ptr == *rhs_event_ptr)) { + return false; + } + } + return true; +} + void NoticeStorageData::RegisterJSONConverter( base::JSONValueConverter<NoticeStorageData>* converter) { converter->RegisterIntField(kSchemaVersionKey, @@ -343,12 +372,17 @@ for (const auto [notice, notice_value] : *data) { auto data_v1 = ConvertTo<V1MigrationData>(¬ice_value); - if (!data_v1 || data_v1->schema_version != 1) { + if (!data_v1) { continue; } - PopulateV2NoticeData(pref_service, notice, ToV2Schema(*data_v1)); - // TODO(boujane) Erase V1 Only fields. + if (data_v1->schema_version == 1) { + PopulateV2NoticeData(pref_service, notice, ToV2Schema(*data_v1)); + } + + // We always erase V1 fields. Even if the current version isn't V1. This is + // because the previously migration to V2 didn't erase the V1 fields. + MaybeEraseV1Fields(pref_service, notice); } }
diff --git a/chrome/browser/privacy_sandbox/notice/notice_storage.h b/chrome/browser/privacy_sandbox/notice/notice_storage.h index 42a93ff4..2847b2b 100644 --- a/chrome/browser/privacy_sandbox/notice/notice_storage.h +++ b/chrome/browser/privacy_sandbox/notice/notice_storage.h
@@ -107,7 +107,7 @@ NoticeStorageData(const NoticeStorageData& data) = delete; NoticeStorageData(NoticeStorageData&& data); NoticeStorageData& operator=(NoticeStorageData&& data); - bool operator==(const NoticeStorageData& other) const = default; + bool operator==(const NoticeStorageData& other) const; static void RegisterJSONConverter( base::JSONValueConverter<NoticeStorageData>* converter);
diff --git a/chrome/browser/privacy_sandbox/notice/notice_storage_unittest.cc b/chrome/browser/privacy_sandbox/notice/notice_storage_unittest.cc index 8d49f09..e8cffc5 100644 --- a/chrome/browser/privacy_sandbox/notice/notice_storage_unittest.cc +++ b/chrome/browser/privacy_sandbox/notice/notice_storage_unittest.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/privacy_sandbox/notice/notice_storage.h" +#include "base/json/json_reader.h" #include "base/json/values_util.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" @@ -35,10 +36,10 @@ // Feature providing the storage name for the default notice in the catalog. BASE_FEATURE(kTestFeature1, - "TopicsConsentDesktopModal", + "Notice1StorageName", base::FEATURE_DISABLED_BY_DEFAULT); BASE_FEATURE(kTestFeature2, - "TopicsConsentModalClankCCT", + "Notice2StorageName", base::FEATURE_DISABLED_BY_DEFAULT); // Notice ID for the default notice in the catalog. @@ -51,8 +52,26 @@ constexpr NoticeId kNoticeIdNotInCatalog = { PrivacySandboxNotice::kMeasurementNotice, SurfaceType::kClankCustomTab}; -base::Time UnixMs(int64_t ms) { - return base::Time::FromMillisecondsSinceUnixEpoch(ms); +base::Time TimeFromMs(int64_t ms) { + return base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(ms)); +} + +void ParseDict(base::Value::Dict* dict, std::string&& json_string) { + auto parsed_json_data = base::JSONReader::ReadDict(json_string); + ASSERT_TRUE(parsed_json_data.has_value()); + *dict = std::move(*parsed_json_data); +} + +std::vector<std::unique_ptr<EventTimePair>> BuildEvents( + std::initializer_list<std::pair<PrivacySandboxNoticeEvent, int64_t>> + raw_events) { + std::vector<std::unique_ptr<EventTimePair>> events; + events.reserve(raw_events.size()); + for (const auto& raw_event : raw_events) { + events.emplace_back(std::make_unique<EventTimePair>( + EventTimePair{raw_event.first, TimeFromMs(raw_event.second)})); + } + return events; } // TODO(crbug.com/333406690): Make a test notice name list injectable so tests @@ -98,6 +117,14 @@ return map; } + void SetNoticeStateFromJSON(const std::string& notice_name, + std::string&& json_data_string) { + base::Value::Dict notice_data_dict; + ParseDict(¬ice_data_dict, std::move(json_data_string)); + ScopedDictPrefUpdate update(prefs(), "privacy_sandbox.notices"); + update->Set(notice_name, std::move(notice_data_dict)); + } + base::HistogramTester histogram_tester_; base::test::TaskEnvironment task_env_; TestingPrefServiceSimple prefs_; @@ -110,8 +137,7 @@ }; TEST_F(PrivacySandboxNoticeStorageTest, NoticePathNotFound) { - const auto actual = - notice_storage()->ReadNoticeData("TopicsConsentDesktopModal"); + const auto actual = notice_storage()->ReadNoticeData("Notice1StorageName"); EXPECT_FALSE(actual.has_value()); } @@ -120,10 +146,10 @@ const std::string histograms = histogram_tester_.GetAllHistogramsRecorded(); EXPECT_THAT(histograms, testing::Not(testing::AnyOf( "PrivacySandbox.Notice.NoticeStartupState." - "TopicsConsentDesktopModal"))); + "Notice1StorageName"))); EXPECT_THAT(histograms, testing::Not(testing::AnyOf( "PrivacySandbox.Notice.NoticeStartupState2." - "TopicsConsentDesktopModal"))); + "Notice1StorageName"))); } TEST_F(PrivacySandboxNoticeStorageTest, NoNoticeNameExpectCrash) { @@ -136,30 +162,29 @@ notice_storage()->RecordStartupHistograms(); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeStartupState.TopicsConsentDesktopModal", + "PrivacySandbox.Notice.NoticeStartupState.Notice1StorageName", NoticeStartupState::kPromptWaiting, 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeStartupState2.TopicsConsentDesktopModal", + "PrivacySandbox.Notice.NoticeStartupState2.Notice1StorageName", NoticeStartupState::kPromptWaiting, 1); } TEST_F(PrivacySandboxNoticeStorageTest, StartupStateEmitsUnknownState) { // Migrate actions without shown. ScopedDictPrefUpdate update(prefs(), "privacy_sandbox.notices"); - update.Get().SetByDottedPath("TopicsConsentDesktopModal.schema_version", 1); - update.Get().SetByDottedPath("TopicsConsentDesktopModal.notice_action_taken", + update.Get().SetByDottedPath("Notice1StorageName.schema_version", 1); + update.Get().SetByDottedPath("Notice1StorageName.notice_action_taken", static_cast<int>(NoticeActionTaken::kAck)); - update.Get().SetByDottedPath( - "TopicsConsentDesktopModal.notice_action_taken_time", - base::TimeToValue(UnixMs(200))); + update.Get().SetByDottedPath("Notice1StorageName.notice_action_taken_time", + base::TimeToValue(TimeFromMs(200))); PrivacySandboxNoticeStorage::UpdateNoticeSchemaV2(prefs()); notice_storage()->RecordStartupHistograms(); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeStartupState.TopicsConsentDesktopModal", + "PrivacySandbox.Notice.NoticeStartupState.Notice1StorageName", NoticeStartupState::kUnknownState, 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeStartupState2.TopicsConsentDesktopModal", + "PrivacySandbox.Notice.NoticeStartupState2.Notice1StorageName", NoticeStartupState::kUnknownState, 1); } @@ -186,10 +211,10 @@ notice_storage()->RecordStartupHistograms(); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeStartupState.TopicsConsentDesktopModal", + "PrivacySandbox.Notice.NoticeStartupState.Notice1StorageName", std::get<1>(GetParam()), 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeStartupState2.TopicsConsentDesktopModal", + "PrivacySandbox.Notice.NoticeStartupState2.Notice1StorageName", std::get<1>(GetParam()), 1); } @@ -204,8 +229,7 @@ base::Time t1 = base::Time::Now(); notice_storage()->RecordEvent(kNotice1InCatalog, kAck); - const auto actual = - notice_storage()->ReadNoticeData("TopicsConsentDesktopModal"); + const auto actual = notice_storage()->ReadNoticeData("Notice1StorageName"); ASSERT_TRUE(actual.has_value()); EXPECT_THAT(actual->notice_events, @@ -213,22 +237,22 @@ Pointee(Eq(EventTimePair{kAck, t1})))); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeEvent.TopicsConsentDesktopModal", kAck, 1); + "PrivacySandbox.Notice.NoticeEvent.Notice1StorageName", kAck, 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeAction.TopicsConsentDesktopModal", + "PrivacySandbox.Notice.NoticeAction.Notice1StorageName", NoticeActionTaken::kAck, 1); histogram_tester_.ExpectTimeBucketCount( "PrivacySandbox.Notice.FirstShownToInteractedDuration." - "TopicsConsentDesktopModal_Ack", + "Notice1StorageName_Ack", base::Milliseconds(100), 1); histogram_tester_.ExpectTimeBucketCount( "PrivacySandbox.Notice.LastShownToInteractedDuration." - "TopicsConsentDesktopModal_Ack", + "Notice1StorageName_Ack", base::Milliseconds(100), 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeShown.TopicsConsentDesktopModal", true, 1); + "PrivacySandbox.Notice.NoticeShown.Notice1StorageName", true, 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeEvent.TopicsConsentDesktopModal", kShown, 1); + "PrivacySandbox.Notice.NoticeEvent.Notice1StorageName", kShown, 1); } TEST_F(PrivacySandboxNoticeStorageTest, @@ -238,7 +262,7 @@ base::Time t1 = base::Time::Now(); notice_storage()->RecordEvent(kNotice1InCatalog, kSettings); - auto actual = notice_storage()->ReadNoticeData("TopicsConsentDesktopModal"); + auto actual = notice_storage()->ReadNoticeData("Notice1StorageName"); ASSERT_TRUE(actual.has_value()); EXPECT_THAT( @@ -248,17 +272,16 @@ Pointee(Eq(EventTimePair{kSettings, t1})))); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeAction.TopicsConsentDesktopModal", + "PrivacySandbox.Notice.NoticeAction.Notice1StorageName", NoticeActionTaken::kSettings, 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeEvent.TopicsConsentDesktopModal", kSettings, - 1); + "PrivacySandbox.Notice.NoticeEvent.Notice1StorageName", kSettings, 1); // Tries to override action, should not override and emits histograms. task_env_.AdvanceClock(base::Milliseconds(50)); notice_storage()->RecordEvent(kNotice1InCatalog, kAck); actual = notice_storage()->ReadNoticeData( - "TopicsConsentDesktopModal"); // Re-read data after potential change + "Notice1StorageName"); // Re-read data after potential change ASSERT_TRUE(actual.has_value()); EXPECT_THAT( @@ -268,16 +291,16 @@ Pointee(Eq(EventTimePair{kSettings, t1})))); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeEvent.TopicsConsentDesktopModal", kAck, 0); + "PrivacySandbox.Notice.NoticeEvent.Notice1StorageName", kAck, 0); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeAction.TopicsConsentDesktopModal", + "PrivacySandbox.Notice.NoticeAction.Notice1StorageName", NoticeActionTaken::kAck, 0); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeAction.TopicsConsentDesktopModal", + "PrivacySandbox.Notice.NoticeAction.Notice1StorageName", NoticeActionTaken::kSettings, 1); histogram_tester_.ExpectBucketCount( "PrivacySandbox.Notice.NoticeActionTakenBehavior." - "TopicsConsentDesktopModal", + "Notice1StorageName", NoticeActionBehavior::kDuplicateActionTaken, 1); } @@ -288,43 +311,42 @@ task_env_.AdvanceClock(base::Milliseconds(100)); notice_storage()->RecordEvent(kNotice1InCatalog, kSettings); - auto actual = notice_storage()->ReadNoticeData("TopicsConsentDesktopModal"); + auto actual = notice_storage()->ReadNoticeData("Notice1StorageName"); ASSERT_TRUE(actual.has_value()); EXPECT_EQ(t0, GetNoticeFirstShownFromEvents(*actual)); EXPECT_EQ(t0, GetNoticeLastShownFromEvents(*actual)); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeShownForFirstTime.TopicsConsentDesktopModal", - true, 1); - histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeEvent.TopicsConsentDesktopModal", kSettings, + "PrivacySandbox.Notice.NoticeShownForFirstTime.Notice1StorageName", true, 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeAction.TopicsConsentDesktopModal", + "PrivacySandbox.Notice.NoticeEvent.Notice1StorageName", kSettings, 1); + histogram_tester_.ExpectBucketCount( + "PrivacySandbox.Notice.NoticeAction.Notice1StorageName", NoticeActionTaken::kSettings, 1); histogram_tester_.ExpectTimeBucketCount( "PrivacySandbox.Notice.FirstShownToInteractedDuration." - "TopicsConsentDesktopModal_Settings", + "Notice1StorageName_Settings", base::Milliseconds(100), 1); histogram_tester_.ExpectTimeBucketCount( "PrivacySandbox.Notice.LastShownToInteractedDuration." - "TopicsConsentDesktopModal_Settings", + "Notice1StorageName_Settings", base::Milliseconds(100), 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeShown.TopicsConsentDesktopModal", true, 1); + "PrivacySandbox.Notice.NoticeShown.Notice1StorageName", true, 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeEvent.TopicsConsentDesktopModal", kShown, 1); + "PrivacySandbox.Notice.NoticeEvent.Notice1StorageName", kShown, 1); // Set notice shown value again. task_env_.AdvanceClock(base::Milliseconds(50)); base::Time t1 = base::Time::Now(); notice_storage()->RecordEvent(kNotice1InCatalog, kShown); - actual = notice_storage()->ReadNoticeData("TopicsConsentDesktopModal"); + actual = notice_storage()->ReadNoticeData("Notice1StorageName"); ASSERT_TRUE(actual.has_value()); EXPECT_EQ(t1, GetNoticeLastShownFromEvents(*actual)); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeShownForFirstTime.TopicsConsentDesktopModal", - false, 1); + "PrivacySandbox.Notice.NoticeShownForFirstTime.Notice1StorageName", false, + 1); EXPECT_EQ(t0, GetNoticeFirstShownFromEvents(*actual)); } @@ -334,7 +356,7 @@ task_env_.AdvanceClock(base::Milliseconds(100)); notice_storage()->RecordEvent(kNotice1InCatalog, kSettings); const auto actual_notice1 = - notice_storage()->ReadNoticeData("TopicsConsentDesktopModal"); + notice_storage()->ReadNoticeData("Notice1StorageName"); ASSERT_TRUE(actual_notice1.has_value()); // Notice data 2. @@ -342,46 +364,44 @@ task_env_.AdvanceClock(base::Milliseconds(20)); notice_storage()->RecordEvent(kNotice2InCatalog, kAck); const auto actual_notice2 = - notice_storage()->ReadNoticeData("TopicsConsentModalClankCCT"); + notice_storage()->ReadNoticeData("Notice2StorageName"); ASSERT_TRUE(actual_notice2.has_value()); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeAction.TopicsConsentDesktopModal", + "PrivacySandbox.Notice.NoticeAction.Notice1StorageName", NoticeActionTaken::kSettings, 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeEvent.TopicsConsentDesktopModal", kSettings, - 1); + "PrivacySandbox.Notice.NoticeEvent.Notice1StorageName", kSettings, 1); histogram_tester_.ExpectTimeBucketCount( "PrivacySandbox.Notice.FirstShownToInteractedDuration." - "TopicsConsentDesktopModal_Settings", + "Notice1StorageName_Settings", base::Milliseconds(100), 1); histogram_tester_.ExpectTimeBucketCount( "PrivacySandbox.Notice.LastShownToInteractedDuration." - "TopicsConsentDesktopModal_Settings", + "Notice1StorageName_Settings", base::Milliseconds(100), 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeEvent.TopicsConsentDesktopModal", kShown, 1); + "PrivacySandbox.Notice.NoticeEvent.Notice1StorageName", kShown, 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeShown.TopicsConsentDesktopModal", true, 1); + "PrivacySandbox.Notice.NoticeShown.Notice1StorageName", true, 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeAction.TopicsConsentModalClankCCT", + "PrivacySandbox.Notice.NoticeAction.Notice2StorageName", NoticeActionTaken::kAck, 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeEvent.TopicsConsentModalClankCCT", kAck, 1); + "PrivacySandbox.Notice.NoticeEvent.Notice2StorageName", kAck, 1); histogram_tester_.ExpectTimeBucketCount( "PrivacySandbox.Notice.FirstShownToInteractedDuration." - "TopicsConsentModalClankCCT_" + "Notice2StorageName_" "Ack", base::Milliseconds(20), 1); histogram_tester_.ExpectTimeBucketCount( "PrivacySandbox.Notice.LastShownToInteractedDuration." - "TopicsConsentModalClankCCT_Ack", + "Notice2StorageName_Ack", base::Milliseconds(20), 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeShown.TopicsConsentModalClankCCT", true, 1); + "PrivacySandbox.Notice.NoticeShown.Notice2StorageName", true, 1); histogram_tester_.ExpectBucketCount( - "PrivacySandbox.Notice.NoticeEvent.TopicsConsentModalClankCCT", kShown, - 1); + "PrivacySandbox.Notice.NoticeEvent.Notice2StorageName", kShown, 1); } using NoticeEvents = base::span<const std::unique_ptr<EventTimePair>>; @@ -396,19 +416,17 @@ scoped_feature_list.InitAndDisableFeature( kPrivacySandboxMigratePrefsToSchemaV2); ScopedDictPrefUpdate update(prefs(), "privacy_sandbox.notices"); - update.Get().SetByDottedPath("TopicsConsentDesktopModal.schema_version", 1); - update.Get().SetByDottedPath("TopicsConsentDesktopModal.notice_last_shown", - base::TimeToValue(UnixMs(100))); - update.Get().SetByDottedPath("TopicsConsentDesktopModal.notice_action_taken", + update.Get().SetByDottedPath("Notice1StorageName.schema_version", 1); + update.Get().SetByDottedPath("Notice1StorageName.notice_last_shown", + base::TimeToValue(TimeFromMs(100))); + update.Get().SetByDottedPath("Notice1StorageName.notice_action_taken", static_cast<int>(NoticeActionTaken::kAck)); - update.Get().SetByDottedPath( - "TopicsConsentDesktopModal.notice_action_taken_time", - base::TimeToValue(UnixMs(200))); + update.Get().SetByDottedPath("Notice1StorageName.notice_action_taken_time", + base::TimeToValue(TimeFromMs(200))); PrivacySandboxNoticeStorage::UpdateNoticeSchemaV2(prefs()); - auto notice_data = - notice_storage()->ReadNoticeData("TopicsConsentDesktopModal"); + auto notice_data = notice_storage()->ReadNoticeData("Notice1StorageName"); ASSERT_TRUE(notice_data.has_value()); EXPECT_EQ(notice_data->schema_version, 1); EXPECT_THAT(notice_data->notice_events, ElementsAre()); @@ -417,54 +435,50 @@ TEST_F(PrivacySandboxNoticeStorageV2Test, AllEventsPopulatedMigrateSuccessfully) { ScopedDictPrefUpdate update(prefs(), "privacy_sandbox.notices"); - update.Get().SetByDottedPath("TopicsConsentDesktopModal.schema_version", 1); - update.Get().SetByDottedPath("TopicsConsentDesktopModal.notice_last_shown", - base::TimeToValue(UnixMs(100))); - update.Get().SetByDottedPath("TopicsConsentDesktopModal.notice_action_taken", + update.Get().SetByDottedPath("Notice1StorageName.schema_version", 1); + update.Get().SetByDottedPath("Notice1StorageName.notice_last_shown", + base::TimeToValue(TimeFromMs(100))); + update.Get().SetByDottedPath("Notice1StorageName.notice_action_taken", static_cast<int>(NoticeActionTaken::kAck)); - update.Get().SetByDottedPath( - "TopicsConsentDesktopModal.notice_action_taken_time", - base::TimeToValue(UnixMs(200))); + update.Get().SetByDottedPath("Notice1StorageName.notice_action_taken_time", + base::TimeToValue(TimeFromMs(200))); PrivacySandboxNoticeStorage::UpdateNoticeSchemaV2(prefs()); - auto notice_data = - notice_storage()->ReadNoticeData("TopicsConsentDesktopModal"); + auto notice_data = notice_storage()->ReadNoticeData("Notice1StorageName"); ASSERT_TRUE(notice_data.has_value()); EXPECT_EQ(notice_data->schema_version, 2); const NoticeEvents& events = notice_data->notice_events; EXPECT_THAT(events, - ElementsAre(Pointee(Eq(EventTimePair{kShown, UnixMs(100)})), - Pointee(Eq(EventTimePair{kAck, UnixMs(200)})))); + ElementsAre(Pointee(Eq(EventTimePair{kShown, TimeFromMs(100)})), + Pointee(Eq(EventTimePair{kAck, TimeFromMs(200)})))); } TEST_F(PrivacySandboxNoticeStorageV2Test, NoticeShownPopulatedMigrateSuccessfully) { ScopedDictPrefUpdate update(prefs(), "privacy_sandbox.notices"); - update.Get().SetByDottedPath("TopicsConsentDesktopModal.schema_version", 1); - update.Get().SetByDottedPath("TopicsConsentDesktopModal.notice_last_shown", - base::TimeToValue(UnixMs(500))); + update.Get().SetByDottedPath("Notice1StorageName.schema_version", 1); + update.Get().SetByDottedPath("Notice1StorageName.notice_last_shown", + base::TimeToValue(TimeFromMs(500))); PrivacySandboxNoticeStorage::UpdateNoticeSchemaV2(prefs()); - auto notice_data = - notice_storage()->ReadNoticeData("TopicsConsentDesktopModal"); + auto notice_data = notice_storage()->ReadNoticeData("Notice1StorageName"); ASSERT_TRUE(notice_data.has_value()); EXPECT_EQ(notice_data->schema_version, 2); EXPECT_THAT(notice_data->notice_events, - ElementsAre(Pointee(Eq(EventTimePair{kShown, UnixMs(500)})))); + ElementsAre(Pointee(Eq(EventTimePair{kShown, TimeFromMs(500)})))); } TEST_F(PrivacySandboxNoticeStorageV2Test, SchemaAlreadyUpToDateDoesNotMigrate) { ScopedDictPrefUpdate update(prefs(), "privacy_sandbox.notices"); - update.Get().SetByDottedPath("TopicsConsentDesktopModal.schema_version", 2); + update.Get().SetByDottedPath("Notice1StorageName.schema_version", 2); PrivacySandboxNoticeStorage::UpdateNoticeSchemaV2(prefs()); - const NoticeEvents& events = notice_storage() - ->ReadNoticeData("TopicsConsentDesktopModal") - ->notice_events; + const NoticeEvents& events = + notice_storage()->ReadNoticeData("Notice1StorageName")->notice_events; EXPECT_THAT(events, ElementsAre()); } @@ -477,25 +491,22 @@ TEST_P(PrivacySandboxNoticeStorageV2ActionsTest, NoticeActionWithoutShownPopulatedMigrateSuccessfully) { ScopedDictPrefUpdate update(prefs(), "privacy_sandbox.notices"); - update.Get().SetByDottedPath("TopicsConsentDesktopModal.schema_version", 1); - update.Get().SetByDottedPath("TopicsConsentDesktopModal.notice_action_taken", + update.Get().SetByDottedPath("Notice1StorageName.schema_version", 1); + update.Get().SetByDottedPath("Notice1StorageName.notice_action_taken", static_cast<int>(std::get<0>(GetParam()))); - update.Get().SetByDottedPath( - "TopicsConsentDesktopModal.notice_action_taken_time", - base::TimeToValue(UnixMs(200))); + update.Get().SetByDottedPath("Notice1StorageName.notice_action_taken_time", + base::TimeToValue(TimeFromMs(200))); PrivacySandboxNoticeStorage::UpdateNoticeSchemaV2(prefs()); - auto notice_data = - notice_storage()->ReadNoticeData("TopicsConsentDesktopModal"); + auto notice_data = notice_storage()->ReadNoticeData("Notice1StorageName"); ASSERT_TRUE(notice_data.has_value()); EXPECT_EQ(notice_data->schema_version, 2); const NoticeEvents& events = notice_data->notice_events; auto notice_event = std::get<1>(GetParam()); if (notice_event) { - EXPECT_THAT( - events, - ElementsAre(Pointee(Eq(EventTimePair{*notice_event, UnixMs(200)})))); + EXPECT_THAT(events, ElementsAre(Pointee(Eq( + EventTimePair{*notice_event, TimeFromMs(200)})))); } else { EXPECT_THAT(events, ElementsAre()); } @@ -504,14 +515,13 @@ TEST_P(PrivacySandboxNoticeStorageV2ActionsTest, NoticeActionPopulatedWithoutTimestampMigrateSuccessfully) { ScopedDictPrefUpdate update(prefs(), "privacy_sandbox.notices"); - update.Get().SetByDottedPath("TopicsConsentDesktopModal.schema_version", 1); - update.Get().SetByDottedPath("TopicsConsentDesktopModal.notice_action_taken", + update.Get().SetByDottedPath("Notice1StorageName.schema_version", 1); + update.Get().SetByDottedPath("Notice1StorageName.notice_action_taken", static_cast<int>(std::get<0>(GetParam()))); PrivacySandboxNoticeStorage::UpdateNoticeSchemaV2(prefs()); - auto notice_data = - notice_storage()->ReadNoticeData("TopicsConsentDesktopModal"); + auto notice_data = notice_storage()->ReadNoticeData("Notice1StorageName"); ASSERT_TRUE(notice_data.has_value()); EXPECT_EQ(notice_data->schema_version, 2); @@ -543,16 +553,114 @@ {NoticeActionTaken::kUnknownActionPreMigration, std::nullopt}, {NoticeActionTaken::kTimedOut, std::nullopt}})); -std::vector<std::unique_ptr<EventTimePair>> BuildEvents( - std::initializer_list<std::pair<PrivacySandboxNoticeEvent, int64_t>> - raw_events) { - std::vector<std::unique_ptr<EventTimePair>> events; - events.reserve(raw_events.size()); - for (const auto& raw_event : raw_events) { - events.emplace_back(std::make_unique<EventTimePair>( - EventTimePair{raw_event.first, UnixMs(raw_event.second)})); - } - return events; +TEST_F(PrivacySandboxNoticeStorageV2Test, + V1FieldsPresentSchemaV2_ErasesV1Fields) { + SetNoticeStateFromJSON("Notice1StorageName", R"({ + "schema_version": 2, + "notice_action_taken": 1, + "notice_action_taken_time": "333333", + "notice_last_shown": "222222", + "events": [{"event": 5, "timestamp": "333333"}] + })"); + + PrivacySandboxNoticeStorage::UpdateNoticeSchemaV2(prefs()); + + base::Value::Dict expected_stored_prefs; + ParseDict(&expected_stored_prefs, R"({ + "schema_version": 2, + "events": [{"event": 5, "timestamp": "333333"}] + })"); + + const base::Value::Dict* actual_stored_prefs = + prefs() + ->GetDict("privacy_sandbox.notices") + .FindDict("Notice1StorageName"); + ASSERT_NE(nullptr, actual_stored_prefs); + EXPECT_EQ(*actual_stored_prefs, expected_stored_prefs); +} + +TEST_F(PrivacySandboxNoticeStorageV2Test, + V1FieldsPresentAndDefaultWithSchemaV1_V1FieldsErased_MigratesToEmptyV2) { + SetNoticeStateFromJSON("Notice1StorageName", R"({ + "schema_version": 1, + "chrome_version": "1.2.3", + "notice_action_taken": 0, // NoticeActionTaken::kNotSet + "notice_action_taken_time": "0", + "notice_last_shown": "0" + })"); + + PrivacySandboxNoticeStorage::UpdateNoticeSchemaV2(prefs()); + + base::Value::Dict expected_stored_prefs; + ParseDict(&expected_stored_prefs, + R"({"schema_version": 2, "chrome_version": "1.2.3"})"); + + const base::Value::Dict* actual_stored_prefs = + prefs() + ->GetDict("privacy_sandbox.notices") + .FindDict("Notice1StorageName"); + ASSERT_NE(nullptr, actual_stored_prefs); + EXPECT_EQ(*actual_stored_prefs, expected_stored_prefs); +} + +TEST_F( + PrivacySandboxNoticeStorageV2Test, + V1FieldsAllDefaultAndAbsentWithSchemaV1_NoFieldsErased_MigratesToEmptyV2) { + SetNoticeStateFromJSON("Notice1StorageName", R"({"schema_version": 1})"); + + PrivacySandboxNoticeStorage::UpdateNoticeSchemaV2(prefs()); + + base::Value::Dict expected_stored_prefs; + ParseDict(&expected_stored_prefs, R"({"schema_version": 2})"); + + const base::Value::Dict* actual_stored_prefs = + prefs() + ->GetDict("privacy_sandbox.notices") + .FindDict("Notice1StorageName"); + ASSERT_NE(nullptr, actual_stored_prefs); + EXPECT_EQ(*actual_stored_prefs, expected_stored_prefs); +} + +TEST_F(PrivacySandboxNoticeStorageV2Test, NoNoticeData_UpdateDoesNothing) { + ASSERT_TRUE(prefs()->GetDict("privacy_sandbox.notices").empty()); + + PrivacySandboxNoticeStorage::UpdateNoticeSchemaV2(prefs()); + + EXPECT_TRUE(prefs()->GetDict("privacy_sandbox.notices").empty()); + const base::Value::Dict* actual_stored_prefs = + prefs() + ->GetDict("privacy_sandbox.notices") + .FindDict("Notice1StorageName"); + EXPECT_EQ(nullptr, actual_stored_prefs); +} + +TEST_F(PrivacySandboxNoticeStorageV2Test, + NonDefaultV1FieldsPresentSchemaV1_ErasesV1Fields_Migrates) { + SetNoticeStateFromJSON("Notice1StorageName", R"({ + "schema_version": 1, + "notice_action_taken": 4, // NoticeActionTaken::kOptIn + "notice_last_shown": "100100", + "notice_action_taken_time": "222222" + })"); + + PrivacySandboxNoticeStorage::UpdateNoticeSchemaV2(prefs()); + + base::Value::Dict expected_stored_prefs; + // V1 fields are erased. Events are migrated. + ParseDict(&expected_stored_prefs, R"({ + "schema_version": 2, + "events": [ + { "event": 5, "timestamp": "100100" }, // kShown event + { "event": 2, "timestamp": "222222" } // kOptIn event + ] + })"); + + const base::Value::Dict* actual_stored_prefs = + prefs() + ->GetDict("privacy_sandbox.notices") + .FindDict("Notice1StorageName"); + ASSERT_NE(nullptr, actual_stored_prefs); + EXPECT_EQ(*actual_stored_prefs, expected_stored_prefs); } class PrivacySandboxNoticeDataTest : public testing::Test {}; @@ -573,7 +681,7 @@ {kShown, 200}, }); - EXPECT_EQ(GetNoticeFirstShownFromEvents(data), UnixMs(100)); + EXPECT_EQ(GetNoticeFirstShownFromEvents(data), TimeFromMs(100)); } TEST_F(PrivacySandboxNoticeDataTest, @@ -585,7 +693,7 @@ {kShown, 200}, }); - EXPECT_EQ(GetNoticeLastShownFromEvents(data), UnixMs(200)); + EXPECT_EQ(GetNoticeLastShownFromEvents(data), TimeFromMs(200)); } TEST_F(PrivacySandboxNoticeDataTest, @@ -610,7 +718,7 @@ }); EXPECT_EQ(GetNoticeActionTakenForFirstShownFromEvents(data), - (EventTimePair{kAck, UnixMs(120)})); + (EventTimePair{kAck, TimeFromMs(120)})); } TEST_F( @@ -626,7 +734,7 @@ }); EXPECT_EQ(GetNoticeActionTakenForFirstShownFromEvents(data), - (EventTimePair{kSettings, UnixMs(150)})); + (EventTimePair{kSettings, TimeFromMs(150)})); } TEST_F( @@ -644,7 +752,7 @@ }); EXPECT_EQ(GetNoticeActionTakenForFirstShownFromEvents(data), - (EventTimePair{kSettings, UnixMs(150)})); + (EventTimePair{kSettings, TimeFromMs(150)})); } } // namespace
diff --git a/chrome/browser/profiles/profile_attributes_entry.cc b/chrome/browser/profiles/profile_attributes_entry.cc index 5e38eaa..84ffc76 100644 --- a/chrome/browser/profiles/profile_attributes_entry.cc +++ b/chrome/browser/profiles/profile_attributes_entry.cc
@@ -62,8 +62,6 @@ const char kProfileManagementOidcState[] = "profile_management_oidc_state"; const char kUserAcceptedAccountManagement[] = "user_accepted_account_management"; -const char kIsUsingNewPlaceholderAvatarIcon[] = - "is_using_new_placeholder_avatar_icon"; // All accounts info. This is a dictionary containing sub-dictionaries of // account information, keyed by the gaia ID. The sub-dictionaries are empty for @@ -814,16 +812,6 @@ profile_attributes_storage_->NotifyProfileThemeColorsChanged(GetPath()); } - // If the kOutlineSilhouetteIcon feature state has changed, notify that the - // avatar icon has changed once so that cached avatar images will be updated - // (e.g. the application badge icon on Windows). - if (base::FeatureList::IsEnabled(kOutlineSilhouetteIcon) != - GetBool(kIsUsingNewPlaceholderAvatarIcon)) { - SetBool(kIsUsingNewPlaceholderAvatarIcon, - base::FeatureList::IsEnabled(kOutlineSilhouetteIcon)); - changed = true; - } - // Only notify if the profile uses the placeholder avatar. if (changed && GetAvatarIconIndex() == profiles::GetPlaceholderAvatarIndex()) { @@ -925,13 +913,6 @@ const PlaceholderAvatarIconParams& icon_params) const { ProfileThemeColors colors = GetProfileThemeColors(); - // Filled Person Icon - if (!base::FeatureList::IsEnabled(kOutlineSilhouetteIcon)) { - return profiles::GetPlaceholderAvatarIconWithColors( - colors.default_avatar_fill_color, colors.default_avatar_stroke_color, - size, icon_params); - } - // Outline Silhouette Person Icon if (icon_params.visibility_against_background.has_value()) { // If the icon should be visible against the background, it cannot have a
diff --git a/chrome/browser/profiles/profile_avatar_icon_util.cc b/chrome/browser/profiles/profile_avatar_icon_util.cc index ae4a65f..23455d7d 100644 --- a/chrome/browser/profiles/profile_avatar_icon_util.cc +++ b/chrome/browser/profiles/profile_avatar_icon_util.cc
@@ -349,20 +349,6 @@ return gfx::CanvasImageSource::CreatePadded(sized_icon, gfx::Insets(padding)); } -// Returns a filled person avatar icon. -gfx::Image GetLegacyPlaceholderAvatarIconWithColors(SkColor fill_color, - SkColor stroke_color, - int size) { - CHECK(!base::FeatureList::IsEnabled(kOutlineSilhouetteIcon)); - - const gfx::VectorIcon& person_icon = - size >= 40 ? kPersonFilledPaddedLargeIcon : kPersonFilledPaddedSmallIcon; - const gfx::ImageSkia icon_without_background = gfx::CreateVectorIcon( - gfx::IconDescription(person_icon, size, stroke_color)); - return gfx::Image( - profiles::AddBackgroundToImage(icon_without_background, fill_color)); -} - class CircleImageSource : public gfx::CanvasImageSource { public: CircleImageSource(int size, SkColor color) @@ -768,8 +754,6 @@ SkColor profile_color_seed, int size, AvatarVisibilityAgainstBackground visibility) { - CHECK(base::FeatureList::IsEnabled(kOutlineSilhouetteIcon)); - const gfx::VectorIcon& person_icon = vector_icons::kAccountCircleChromeRefreshIcon; @@ -794,11 +778,6 @@ SkColor stroke_color, int size, const PlaceholderAvatarIconParams& icon_params) { - if (!base::FeatureList::IsEnabled(kOutlineSilhouetteIcon)) { - return GetLegacyPlaceholderAvatarIconWithColors(fill_color, stroke_color, - size); - } - // If the icon should be an outline icon visible against the background, use // `GetPlaceholderAvatarIconVisibleAgainstBackground()` instead. CHECK(!icon_params.visibility_against_background.has_value());
diff --git a/chrome/browser/profiles/profile_avatar_icon_util.h b/chrome/browser/profiles/profile_avatar_icon_util.h index f45d405..ba18f94 100644 --- a/chrome/browser/profiles/profile_avatar_icon_util.h +++ b/chrome/browser/profiles/profile_avatar_icon_util.h
@@ -143,15 +143,13 @@ std::string GetPlaceholderAvatarIconUrl(); // Returns the outline silhouette colored generic avatar, either visible against -// a dark or a light theme background. This function is currently under -// experiment and only used when `kOutlineSilhouetteIcon` is enabled. +// a dark or a light theme background. gfx::Image GetPlaceholderAvatarIconVisibleAgainstBackground( SkColor profile_color_seed, int size, AvatarVisibilityAgainstBackground visibility); -// Returns a filled person icon if `kOutlineSilhouetteIcon` is disabled, and the -// outline silhouette colored generic avatar if it is enabled. Depending on the +// Returns the outline silhouette colored generic avatar. Depending on the // `icon_params`, the outline silhouette avatar will have a background/padding // or not. //
diff --git a/chrome/browser/resources/ash/settings/os_printing_page/cups_edit_printer_dialog.html b/chrome/browser/resources/ash/settings/os_printing_page/cups_edit_printer_dialog.html index 485ed4d..c41d5d1d 100644 --- a/chrome/browser/resources/ash/settings/os_printing_page/cups_edit_printer_dialog.html +++ b/chrome/browser/resources/ash/settings/os_printing_page/cups_edit_printer_dialog.html
@@ -137,15 +137,15 @@ </localized-link> </div> </div> - <div class="settings-box two-line"> + <div class="settings-box two-line" + hidden="[[pendingPrinter_.isManaged]]"> <cr-input class="browse-file-input" readonly input-tabindex="-1" value="[[userPPD_]]" aria-labelledby="ppdLabel" error-message="$i18n{selectDriverErrorMessage}" - invalid="[[invalidPPD_]]" hidden="[[shouldHidePpdTextbox( - pendingPrinter_.isManaged, userPPD_.length)]]"> + invalid="[[invalidPPD_]]"> </cr-input> <cr-button class="browse-button" on-click="onBrowseFile_" - disabled="[[!isOnline_]]" hidden="[[pendingPrinter_.isManaged]]" + disabled="[[!isOnline_]]" aria-label="$i18n{selectDriverButtonAriaLabel}"> $i18n{selectDriverButtonText} </cr-button>
diff --git a/chrome/browser/resources/ash/settings/os_printing_page/cups_edit_printer_dialog.ts b/chrome/browser/resources/ash/settings/os_printing_page/cups_edit_printer_dialog.ts index 8945419..85a35d7 100644 --- a/chrome/browser/resources/ash/settings/os_printing_page/cups_edit_printer_dialog.ts +++ b/chrome/browser/resources/ash/settings/os_printing_page/cups_edit_printer_dialog.ts
@@ -570,15 +570,6 @@ return !this.isOnline_ || (this.pendingPrinter_ && this.pendingPrinter_.isManaged); } - - /** - * @return Returns true if the printer is managed and the custom PPD is not - * set. - */ - private shouldHidePpdTextbox(): boolean { - return this.pendingPrinter_ && this.pendingPrinter_.isManaged && - this.userPPD_.length === 0; - } } declare global {
diff --git a/chrome/browser/resources/glic/glic_api/glic_api.ts b/chrome/browser/resources/glic/glic_api/glic_api.ts index ae0e2e15..1ffe31c 100644 --- a/chrome/browser/resources/glic/glic_api/glic_api.ts +++ b/chrome/browser/resources/glic/glic_api/glic_api.ts
@@ -941,9 +941,12 @@ exactText?: ScrollToTextSelector; /** - * Text fragment selector, see ScrollToTextFragmentSelector for more details + * Text fragment selector, see ScrollToTextFragmentSelector for more details. */ textFragment?: ScrollToTextFragmentSelector; + + /** Node selector, see ScrollToNodeSelector for more details. */ + node?: ScrollToNodeSelector; } /** @@ -984,6 +987,20 @@ searchRangeStartNodeId?: number; } +/** + * scrollTo() selector to select all text inside a specific node (corresponding + * to the provided nodeId). documentId must also be specified in ScrollToParams + * when this selector is used. + */ +export declare interface ScrollToNodeSelector { + /** + * Value should be obtained from common_ancestor_dom_node_id in + * ContentAttributes (see + * components/optimization_guide/proto/features/common_quality_data.proto) + */ + nodeId: number; +} + /** Error type used for scrollTo(). */ export type ScrollToError = ErrorWithReason<'scrollTo'>;
diff --git a/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts b/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts index baf8490..5f5cb383 100644 --- a/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts +++ b/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts
@@ -479,6 +479,18 @@ }, }; } + if (selector.node !== undefined) { + if (params.documentId === undefined) { + throw new ErrorWithReasonImpl( + 'scrollTo', ScrollToErrorReason.NOT_SUPPORTED, + 'nodeId without documentId'); + } + return { + nodeSelector: { + nodeId: selector.node.nodeId, + }, + }; + } throw new ErrorWithReasonImpl( 'scrollTo', ScrollToErrorReason.NOT_SUPPORTED); }
diff --git a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/UnsubscribedNotificationsNotificationManager.java b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/UnsubscribedNotificationsNotificationManager.java index 4e99059f..1546061 100644 --- a/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/UnsubscribedNotificationsNotificationManager.java +++ b/chrome/browser/safety_hub/android/java/src/org/chromium/chrome/browser/safety_hub/UnsubscribedNotificationsNotificationManager.java
@@ -4,6 +4,7 @@ package org.chromium.chrome.browser.safety_hub; +import android.app.Notification; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; @@ -14,13 +15,16 @@ import org.jni_zero.JniType; import org.chromium.base.ContextUtils; +import org.chromium.base.TimeUtils; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.notifications.NotificationConstants; import org.chromium.chrome.browser.notifications.NotificationUmaTracker; import org.chromium.chrome.browser.notifications.NotificationWrapperBuilderFactory; import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions.ChannelId; import org.chromium.chrome.browser.settings.SettingsNavigationFactory; +import org.chromium.components.browser_ui.notifications.BaseNotificationManagerProxy.StatusBarNotificationProxy; import org.chromium.components.browser_ui.notifications.BaseNotificationManagerProxyFactory; import org.chromium.components.browser_ui.notifications.NotificationMetadata; import org.chromium.components.browser_ui.notifications.NotificationWrapper; @@ -28,6 +32,8 @@ import org.chromium.components.browser_ui.notifications.PendingIntentProvider; import org.chromium.components.browser_ui.settings.SettingsNavigation; +import java.util.List; + /** Shows the Safety Hub notification about revoked notification permissions. */ @NullMarked public class UnsubscribedNotificationsNotificationManager { @@ -60,6 +66,23 @@ .NOTIFICATION_ID_SAFETY_HUB_UNSUBSCRIBED_NOTIFICATIONS)); } + private static boolean isDisruptiveNotificationRevocationEnabled() { + return ChromeFeatureList.isEnabled( + ChromeFeatureList.SAFETY_HUB_DISRUPTIVE_NOTIFICATION_REVOCATION) + && !ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean( + ChromeFeatureList.SAFETY_HUB_DISRUPTIVE_NOTIFICATION_REVOCATION, + "shadow_run", + false); + } + + private static long getNotificationTimeout() { + return ChromeFeatureList.getFieldTrialParamByFeatureAsInt( + ChromeFeatureList.SAFETY_HUB_DISRUPTIVE_NOTIFICATION_REVOCATION, + "notification_timeout_seconds", + 7 * 24 * 3600) + * 1000L; + } + /** * Displays, updates or dismisses the notification about revoked subscriptions from Safety Hub. * @@ -67,13 +90,21 @@ * dismissed. */ @CalledByNative - static void updateNotification(@JniType("int32_t") int numRevokedPermissions) { + static void displayNotification(@JniType("int32_t") int numRevokedPermissions) { assert numRevokedPermissions >= 0 : "This function expects a non-negative parameter numRevokedPermissions"; if (numRevokedPermissions <= 0) { dismissNotification(); return; } + displayOrUpdateNotification(numRevokedPermissions, TimeUtils.currentTimeMillis()); + } + + private static void displayOrUpdateNotification(int numRevokedPermissions, long when) { + if (!isDisruptiveNotificationRevocationEnabled()) { + dismissNotification(); + return; + } Context context = ContextUtils.getApplicationContext(); Resources res = context.getResources(); @@ -95,7 +126,8 @@ SettingsNavigation settingsNavigation = SettingsNavigationFactory.createSettingsNavigation(); Intent settingsIntent = - settingsNavigation.createSettingsIntent(context, SafetyHubFragment.class); + settingsNavigation.createSettingsIntent( + context, SafetyHubPermissionsFragment.class); PendingIntentProvider settingsIntentProvider = PendingIntentProvider.getActivity( @@ -107,16 +139,23 @@ context.getString( R.string.safety_hub_unsubscribed_notifications_notification_review); + long notificationTimeout = getNotificationTimeout(); + long now = TimeUtils.currentTimeMillis(); + if (now - when < 0 || now - when > notificationTimeout) { + return; + } + NotificationWrapperBuilder notificationWrapperBuilder = createNotificationBuilder() .setSilent(true) .setSmallIcon(R.drawable.ic_chrome) - .setAutoCancel(false) + .setAutoCancel(true) .setLocalOnly(true) .setContentTitle(title) .setContentText(contents) .setContentIntent(settingsIntentProvider) - .setTimeoutAfter(7 * 24 * 3600 * 1000L) // 7 days + .setWhen(when) + .setTimeoutAfter(notificationTimeout) .addAction( /* icon= */ 0, review, @@ -142,6 +181,51 @@ notification.getNotification()); } + /** + * Searches a notifications list for the Safety Hub notification. Returns the notification, or + * null if the notification is not found. + */ + private static @Nullable Notification findNotification( + List<? extends StatusBarNotificationProxy> notifications) { + for (StatusBarNotificationProxy proxy : notifications) { + if (proxy.getId() + == NotificationConstants + .NOTIFICATION_ID_SAFETY_HUB_UNSUBSCRIBED_NOTIFICATIONS + && proxy.getTag().equals(TAG)) { + return proxy.getNotification(); + } + } + return null; + } + + /** + * Updates the notification, only if it is currently displayed, with the updated number of + * revoked permissions. Everything else stays the same. + * + * @param numRevokedPermissions is the number of permissions revoked. If 0, the notification is + * dismissed. + */ + @CalledByNative + static void updateNotification(@JniType("int32_t") int numRevokedPermissions) { + assert numRevokedPermissions >= 0 + : "This function expects a non-negative parameter numRevokedPermissions"; + if (numRevokedPermissions <= 0) { + dismissNotification(); + return; + } + + BaseNotificationManagerProxyFactory.create() + .getActiveNotifications( + (activeNotifications) -> { + Notification activeNotification = findNotification(activeNotifications); + if (activeNotification == null) { + return; + } + displayOrUpdateNotification( + numRevokedPermissions, activeNotification.when); + }); + } + private static void dismissNotification() { BaseNotificationManagerProxyFactory.create() .cancel(
diff --git a/chrome/browser/safety_hub/android/javatests/src/org/chromium/chrome/browser/safety_hub/UnsubscribedNotificationsNotificationTest.java b/chrome/browser/safety_hub/android/javatests/src/org/chromium/chrome/browser/safety_hub/UnsubscribedNotificationsNotificationTest.java index 01227f3..6f7c7a1e8 100644 --- a/chrome/browser/safety_hub/android/javatests/src/org/chromium/chrome/browser/safety_hub/UnsubscribedNotificationsNotificationTest.java +++ b/chrome/browser/safety_hub/android/javatests/src/org/chromium/chrome/browser/safety_hub/UnsubscribedNotificationsNotificationTest.java
@@ -40,7 +40,8 @@ @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) @Features.EnableFeatures({ ChromeFeatureList.SAFETY_HUB, - NotificationFeatureMap.CACHE_NOTIIFICATIONS_ENABLED + NotificationFeatureMap.CACHE_NOTIIFICATIONS_ENABLED, + ChromeFeatureList.SAFETY_HUB_DISRUPTIVE_NOTIFICATION_REVOCATION + ":shadow_run/false" }) @Batch(Batch.PER_CLASS) @Restriction(DeviceRestriction.RESTRICTION_TYPE_NON_AUTO) @@ -51,7 +52,7 @@ @SmallTest @Feature({"SafetyHubNotification"}) public void testReviewNotification() throws Exception { - UnsubscribedNotificationsNotificationManager.updateNotification(1); + UnsubscribedNotificationsNotificationManager.displayNotification(1); List<MockNotificationManagerProxy.NotificationEntry> notifications = mNotificationTestRule.getNotificationEntries(); assertEquals(1, notifications.size()); @@ -71,15 +72,14 @@ fail(e.getMessage()); } }); - assertTrue(settingsActivity.getMainFragment() instanceof SafetyHubFragment); - assertEquals(1, mNotificationTestRule.getNotificationEntries().size()); + assertTrue(settingsActivity.getMainFragment() instanceof SafetyHubPermissionsFragment); } @Test @SmallTest @Feature({"SafetyHubNotification"}) public void testReviewNotificationClickingOnContent() throws Exception { - UnsubscribedNotificationsNotificationManager.updateNotification(1); + UnsubscribedNotificationsNotificationManager.displayNotification(1); List<MockNotificationManagerProxy.NotificationEntry> notifications = mNotificationTestRule.getNotificationEntries(); assertEquals(1, notifications.size()); @@ -98,7 +98,6 @@ fail(e.getMessage()); } }); - assertTrue(settingsActivity.getMainFragment() instanceof SafetyHubFragment); - assertEquals(1, mNotificationTestRule.getNotificationEntries().size()); + assertTrue(settingsActivity.getMainFragment() instanceof SafetyHubPermissionsFragment); } }
diff --git a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/UnsubscribedNotificationsNotificationManagerTest.java b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/UnsubscribedNotificationsNotificationManagerTest.java index 859f76da..219f597c 100644 --- a/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/UnsubscribedNotificationsNotificationManagerTest.java +++ b/chrome/browser/safety_hub/android/junit/src/org/chromium/chrome/browser/safety_hub/UnsubscribedNotificationsNotificationManagerTest.java
@@ -4,8 +4,10 @@ package org.chromium.chrome.browser.safety_hub; +import static org.hamcrest.Matchers.greaterThan; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; import android.app.Notification; @@ -16,6 +18,7 @@ import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.Features.EnableFeatures; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.components.browser_ui.notifications.BaseNotificationManagerProxyFactory; import org.chromium.components.browser_ui.notifications.MockNotificationManagerProxy; import org.chromium.components.browser_ui.notifications.NotificationFeatureMap; @@ -25,7 +28,10 @@ /** JUnit tests for {@link UnsubscribedNotificationsNotificationManager} */ @RunWith(BaseRobolectricTestRunner.class) @Config(manifest = Config.NONE) -@EnableFeatures({NotificationFeatureMap.CACHE_NOTIIFICATIONS_ENABLED}) +@EnableFeatures({ + NotificationFeatureMap.CACHE_NOTIIFICATIONS_ENABLED, + ChromeFeatureList.SAFETY_HUB_DISRUPTIVE_NOTIFICATION_REVOCATION + ":shadow_run/false" +}) public class UnsubscribedNotificationsNotificationManagerTest { private MockNotificationManagerProxy mMockNotificationManager; @@ -36,10 +42,83 @@ } @Test - public void testShowNotificationOneSite() { + public void testDisplayNotificationOneSite() { + UnsubscribedNotificationsNotificationManager.displayNotification(1); + List<MockNotificationManagerProxy.NotificationEntry> notifications = + mMockNotificationManager.getNotifications(); + assertEquals(1, notifications.size()); + Notification notification = notifications.get(0).notification; + assertEquals( + "Chrome unsubscribed you from notifications", + notification.extras.getString(Notification.EXTRA_TITLE)); + assertEquals( + "Removed notification permissions from one site you haven’t visited recently", + notification.extras.getString(Notification.EXTRA_TEXT)); + assertEquals("Review", notification.actions[0].title); + assertNotNull(notification.actions[0].actionIntent); + assertEquals("Got it", notification.actions[1].title); + assertNotNull(notification.actions[1].actionIntent); + } + + @Test + public void testDisplayNotificationMultipleSites() { + UnsubscribedNotificationsNotificationManager.displayNotification(2); + List<MockNotificationManagerProxy.NotificationEntry> notifications = + mMockNotificationManager.getNotifications(); + assertEquals(1, notifications.size()); + Notification notification = notifications.get(0).notification; + assertEquals( + "Chrome unsubscribed you from notifications", + notification.extras.getString(Notification.EXTRA_TITLE)); + assertEquals( + "Removed notification permissions from 2 sites you haven’t visited recently", + notification.extras.getString(Notification.EXTRA_TEXT)); + } + + @Test + public void testDisplayNotificationUpdates() { + assertEquals(0, mMockNotificationManager.getNotifications().size()); + + UnsubscribedNotificationsNotificationManager.displayNotification(0); + assertEquals(0, mMockNotificationManager.getNotifications().size()); + + UnsubscribedNotificationsNotificationManager.displayNotification(1); + List<MockNotificationManagerProxy.NotificationEntry> notifications = + mMockNotificationManager.getNotifications(); + assertEquals(1, notifications.size()); + assertEquals( + "Removed notification permissions from one site you haven’t visited recently", + notifications.get(0).notification.extras.getString(Notification.EXTRA_TEXT)); + + UnsubscribedNotificationsNotificationManager.displayNotification(3); + List<MockNotificationManagerProxy.NotificationEntry> notificationsAfter = + mMockNotificationManager.getNotifications(); + assertEquals(1, notificationsAfter.size()); + assertEquals( + "Removed notification permissions from 3 sites you haven’t visited recently", + notificationsAfter.get(0).notification.extras.getString(Notification.EXTRA_TEXT)); + + assertThat( + notificationsAfter.get(0).notification.when, + greaterThan(notifications.get(0).notification.when)); + + UnsubscribedNotificationsNotificationManager.displayNotification(0); + assertEquals(0, mMockNotificationManager.getNotifications().size()); + } + + @Test + public void testUpdateNotificationWithNoPreexistentNotification() { UnsubscribedNotificationsNotificationManager.updateNotification(1); List<MockNotificationManagerProxy.NotificationEntry> notifications = mMockNotificationManager.getNotifications(); + assertEquals(0, notifications.size()); + } + + @Test + public void testUpdateNotificationNewNumber() { + UnsubscribedNotificationsNotificationManager.displayNotification(1); + List<MockNotificationManagerProxy.NotificationEntry> notifications = + mMockNotificationManager.getNotifications(); assertEquals(1, notifications.size()); assertEquals( "Chrome unsubscribed you from notifications", @@ -48,54 +127,26 @@ "Removed notification permissions from one site you haven’t visited recently", notifications.get(0).notification.extras.getString(Notification.EXTRA_TEXT)); - Notification notification = notifications.get(0).getNotification(); - assertEquals("Review", notification.actions[0].title); - assertNotNull(notification.actions[0].actionIntent); - assertEquals("Got it", notification.actions[1].title); - assertNotNull(notification.actions[1].actionIntent); - } - - @Test - public void testShowNotificationMultipleSites() { UnsubscribedNotificationsNotificationManager.updateNotification(2); - List<MockNotificationManagerProxy.NotificationEntry> notifications = + List<MockNotificationManagerProxy.NotificationEntry> notificationsAfter = mMockNotificationManager.getNotifications(); - assertEquals(1, notifications.size()); + assertEquals(1, notificationsAfter.size()); assertEquals( "Chrome unsubscribed you from notifications", - notifications.get(0).notification.extras.getString(Notification.EXTRA_TITLE)); + notificationsAfter.get(0).notification.extras.getString(Notification.EXTRA_TITLE)); assertEquals( "Removed notification permissions from 2 sites you haven’t visited recently", - notifications.get(0).notification.extras.getString(Notification.EXTRA_TEXT)); + notificationsAfter.get(0).notification.extras.getString(Notification.EXTRA_TEXT)); + + assertEquals( + notificationsAfter.get(0).notification.when, + notifications.get(0).notification.when); } @Test - public void testUpdateNotification() { - assertEquals(0, mMockNotificationManager.getNotifications().size()); - - UnsubscribedNotificationsNotificationManager.updateNotification(0); - assertEquals(0, mMockNotificationManager.getNotifications().size()); - - UnsubscribedNotificationsNotificationManager.updateNotification(1); - { - List<MockNotificationManagerProxy.NotificationEntry> notifications = - mMockNotificationManager.getNotifications(); - assertEquals(1, notifications.size()); - assertEquals( - "Removed notification permissions from one site you haven’t visited recently", - notifications.get(0).notification.extras.getString(Notification.EXTRA_TEXT)); - } - - UnsubscribedNotificationsNotificationManager.updateNotification(3); - { - List<MockNotificationManagerProxy.NotificationEntry> notifications = - mMockNotificationManager.getNotifications(); - assertEquals(1, notifications.size()); - assertEquals( - "Removed notification permissions from 3 sites you haven’t visited recently", - notifications.get(0).notification.extras.getString(Notification.EXTRA_TEXT)); - } - + public void testUpdateNotificationDismisses() { + UnsubscribedNotificationsNotificationManager.displayNotification(1); + assertEquals(1, mMockNotificationManager.getNotifications().size()); UnsubscribedNotificationsNotificationManager.updateNotification(0); assertEquals(0, mMockNotificationManager.getNotifications().size()); }
diff --git a/chrome/browser/signin/signin_promo_unittest.cc b/chrome/browser/signin/signin_promo_unittest.cc index c08131d..12d49320 100644 --- a/chrome/browser/signin/signin_promo_unittest.cc +++ b/chrome/browser/signin/signin_promo_unittest.cc
@@ -187,16 +187,6 @@ scoped_refptr<const extensions::Extension> extension_; }; -TEST_F(ShowPromoTest, DoNotShowAddressSignInPromoWithoutImprovedBrowserSignin) { - base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatures( - /*enabled_features=*/{}, - /*disabled_features=*/{switches::kImprovedSigninUIOnDesktop}); - - EXPECT_FALSE(ShouldShowAddressSignInPromo(*profile(), - autofill::test::StandardProfile())); -} - TEST_F(ShowPromoTest, DoNotShowBookmarkSignInPromoWithoutExplicitSignIn) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeatures( @@ -311,8 +301,7 @@ ShowPromoTest::SetUp(); feature_list_.InitWithFeatures( /*enabled_features=*/ - {switches::kImprovedSigninUIOnDesktop, - switches::kSyncEnableBookmarksInTransportMode, + {switches::kSyncEnableBookmarksInTransportMode, switches::kEnableExtensionsExplicitBrowserSignin}, /*disabled_features=*/{}); ON_CALL(*sync_service(), GetDataTypesForTransportOnlyMode())
diff --git a/chrome/browser/supervised_user/extensions_interactive_uitest.cc b/chrome/browser/supervised_user/extensions_interactive_uitest.cc index 38c6bac..b056e72 100644 --- a/chrome/browser/supervised_user/extensions_interactive_uitest.cc +++ b/chrome/browser/supervised_user/extensions_interactive_uitest.cc
@@ -8,7 +8,6 @@ #include "base/notreached.h" #include "base/strings/to_string.h" -#include "base/test/scoped_feature_list.h" #include "chrome/browser/extensions/chrome_test_extension_loader.h" #include "chrome/browser/extensions/extension_keybinding_registry.h" #include "chrome/browser/extensions/install_verifier.h" @@ -21,7 +20,6 @@ #include "chrome/test/supervised_user/browser_user.h" #include "chrome/test/supervised_user/family_live_test.h" #include "components/prefs/pref_service.h" -#include "components/supervised_user/core/common/features.h" #include "components/supervised_user/core/common/pref_names.h" #include "components/supervised_user/test_support/family_link_settings_state_management.h" #include "content/public/test/browser_test.h" @@ -41,12 +39,6 @@ "chrome://settings/content/siteDetails?site=chrome-extension://"; static constexpr std::string_view kExtensionName = "An Extension"; -// Family Link switch that governs the handling of extensions for SU. -enum class ExtensionHandlingMode : int { - kExtensionsGovernedByPermissionsSwitch = 0, - kExtensionsGovernedByExtensionsSwitch = 1, -}; - // TODO(b/321242366): Consider moving to helper class. // Checks if a page title matches the given regexp in ecma script dialect. InteractiveBrowserTestApi::StateChange PageWithMatchingTitle( @@ -64,42 +56,16 @@ return state_change; } -// Test the behavior of handling extensions for supervised users when parental -// controls apply on extensions (by default on Chrome OS, depending on the -// kEnableExtensionsPermissionsForSupervisedUsersOnDesktop feature on -// Win/Mac/Linux). +// Test the behavior of handling extensions for supervised users. class SupervisedUserExtensionsParentalControlsUiTest : public InteractiveFamilyLiveTest, - public testing::WithParamInterface<std::tuple< - FamilyLiveTest::RpcMode, - /*permissions_switch_state=*/FamilyLinkToggleState, - /*extensions_switch_state=*/FamilyLinkToggleState, - // Depending on the ExtensionHandlingMode only one switch - // should affect the behaviour of supervised user's extensions. - // Toggling the other switch should have no effect to the result. - /*extensions_handling_mode=*/ExtensionHandlingMode>> { + public testing::WithParamInterface< + std::tuple<FamilyLiveTest::RpcMode, + /*permissions_switch_state=*/FamilyLinkToggleState, + /*extensions_switch_state=*/FamilyLinkToggleState>> { public: SupervisedUserExtensionsParentalControlsUiTest() - : InteractiveFamilyLiveTest(GetRpcMode()) { - std::vector<base::test::FeatureRef> enabled_features; - std::vector<base::test::FeatureRef> disabled_features; - - if (GetExtensionHandlingMode() == - ExtensionHandlingMode::kExtensionsGovernedByExtensionsSwitch) { - enabled_features.push_back( - kEnableSupervisedUserSkipParentApprovalToInstallExtensions); - } else { - disabled_features.push_back( - kEnableSupervisedUserSkipParentApprovalToInstallExtensions); - } - -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) - // Enable extensions parental controls. - enabled_features.push_back( - kEnableExtensionsPermissionsForSupervisedUsersOnDesktop); -#endif - feature_list_.InitWithFeatures(enabled_features, disabled_features); - } + : InteractiveFamilyLiveTest(GetRpcMode()) {} protected: // Child tries to enable a disabled extension (which is pending parent @@ -170,19 +136,6 @@ } ui::ElementIdentifier GetTargetUIElement() { - if (GetExtensionHandlingMode() == - ExtensionHandlingMode::kExtensionsGovernedByPermissionsSwitch) { - // Depending on the "Permissions" switch's value either the "Parent - // Approval Dialog" (switch ON) or the "Extensions Blocked by Parent" - // error message will appear at the end. - return GetPermissionsSwitchTargetState() == - FamilyLinkToggleState::kEnabled - ? ParentPermissionDialog::kDialogViewIdForTesting - : extensions::kParentBlockedDialogMessage; - } - // If governed by extensions: - CHECK(GetExtensionHandlingMode() == - ExtensionHandlingMode::kExtensionsGovernedByExtensionsSwitch); CHECK(GetExtensionsSwitchTargetState() == FamilyLinkToggleState::kDisabled); // Parent approval dialog should appear. return ParentPermissionDialog::kDialogViewIdForTesting; @@ -195,14 +148,6 @@ // switch in Family Link. auto CheckExtensionLocationPermissions(ui::ElementIdentifier kChildElementId, Profile* profile) { - if (GetExtensionHandlingMode() == - ExtensionHandlingMode::kExtensionsGovernedByPermissionsSwitch && - GetPermissionsSwitchTargetState() == FamilyLinkToggleState::kDisabled) { - // No extension has been installed on this mode, there are no permissions - // to check. - return Steps(); - } - extensions::ExtensionId installed_extension_id; const auto& installed_extensions = extensions::ExtensionRegistry::Get(profile) @@ -283,13 +228,6 @@ static FamilyLinkToggleState GetExtensionsSwitchTargetState() { return std::get<2>(GetParam()); } - - static ExtensionHandlingMode GetExtensionHandlingMode() { - return std::get<3>(GetParam()); - } - - private: - base::test::ScopedFeatureList feature_list_; }; IN_PROC_BROWSER_TEST_P(SupervisedUserExtensionsParentalControlsUiTest, @@ -304,11 +242,8 @@ const int child_tab_index = 0; // The extensions should be disabled (pending parent approval) in all cases, - // expect when the new "Extensions" FL switch is enabled and is used - // in Chrome to manage extensions. + // expect when the new "Extensions" FL switch is ON. const bool should_be_enabled = - GetExtensionHandlingMode() == - ExtensionHandlingMode::kExtensionsGovernedByExtensionsSwitch && GetExtensionsSwitchTargetState() == FamilyLinkToggleState::kEnabled; TurnOnSync(); @@ -361,11 +296,7 @@ FamilyLinkToggleState::kDisabled), /*extensions_switch_target_value==*/ testing::Values(FamilyLinkToggleState::kEnabled, - FamilyLinkToggleState::kDisabled), - /*extensions_handling_mode=*/ - testing::Values( - ExtensionHandlingMode::kExtensionsGovernedByPermissionsSwitch, - ExtensionHandlingMode::kExtensionsGovernedByExtensionsSwitch)), + FamilyLinkToggleState::kDisabled)), [](const auto& info) { return ToString(std::get<0>(info.param)) + std::string( @@ -375,12 +306,7 @@ std::string( (std::get<2>(info.param) == FamilyLinkToggleState::kEnabled ? "WithExtensionsOn" - : "WithExtensionsOff")) + - std::string((std::get<3>(info.param) == - ExtensionHandlingMode:: - kExtensionsGovernedByPermissionsSwitch - ? "ManagedByPermissionsSwitch" - : "ManagedByExtensionsSwitch")); + : "WithExtensionsOff")); }); } // namespace
diff --git a/chrome/browser/supervised_user/linux_mac_windows/supervised_user_extensions_metrics_delegate_impl_unittest.cc b/chrome/browser/supervised_user/linux_mac_windows/supervised_user_extensions_metrics_delegate_impl_unittest.cc index 6304235d..b67a88b 100644 --- a/chrome/browser/supervised_user/linux_mac_windows/supervised_user_extensions_metrics_delegate_impl_unittest.cc +++ b/chrome/browser/supervised_user/linux_mac_windows/supervised_user_extensions_metrics_delegate_impl_unittest.cc
@@ -6,7 +6,6 @@ #include <memory> #include "base/test/metrics/histogram_tester.h" -#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/time/time.h" #include "chrome/browser/extensions/extension_service.h" @@ -17,7 +16,6 @@ #include "components/supervised_user/core/browser/supervised_user_preferences.h" #include "components/supervised_user/core/browser/supervised_user_service.h" #include "components/supervised_user/core/browser/supervised_user_url_filter.h" -#include "components/supervised_user/core/common/features.h" #include "components/supervised_user/core/common/pref_names.h" #include "extensions/browser/disable_reason.h" #include "extensions/browser/extension_registrar.h" @@ -42,16 +40,6 @@ std::make_unique<content::BrowserTaskEnvironment>( base::test::TaskEnvironment::MainThreadType::IO, content::BrowserTaskEnvironment::TimeSource::MOCK_TIME)) { - std::vector<base::test::FeatureRef> enabled_features; - std::vector<base::test::FeatureRef> disabled_features; - enabled_features.push_back( - supervised_user:: - kEnableSupervisedUserSkipParentApprovalToInstallExtensions); - enabled_features.push_back( - supervised_user:: - kEnableExtensionsPermissionsForSupervisedUsersOnDesktop); - feature_list_.InitWithFeatures(enabled_features, disabled_features); - ExtensionServiceInitParams params; params.profile_is_supervised = true; InitializeExtensionService(std::move(params)); @@ -92,7 +80,6 @@ private: std::unique_ptr<supervised_user::SupervisedUserMetricsService> supervised_user_metrics_service_; - base::test::ScopedFeatureList feature_list_; }; // Tests that the extensions histograms are recorded on each day.
diff --git a/chrome/browser/supervised_user/supervised_user_browser_utils.cc b/chrome/browser/supervised_user/supervised_user_browser_utils.cc index c56f59d..cbca0b93 100644 --- a/chrome/browser/supervised_user/supervised_user_browser_utils.cc +++ b/chrome/browser/supervised_user/supervised_user_browser_utils.cc
@@ -91,7 +91,6 @@ bool SupervisedUserCanSkipExtensionParentApprovals(const Profile* profile) { #if BUILDFLAG(ENABLE_EXTENSIONS_CORE) return profile->IsChild() && - IsSupervisedUserSkipParentApprovalToInstallExtensionsEnabled() && profile->GetPrefs()->GetBoolean( prefs::kSkipParentApprovalToInstallExtensions); #else @@ -101,14 +100,8 @@ bool AreExtensionsPermissionsEnabled(Profile* profile) { #if BUILDFLAG(ENABLE_EXTENSIONS_CORE) -#if BUILDFLAG(IS_CHROMEOS) return profile->IsChild(); #else - return profile->IsChild() && - base::FeatureList::IsEnabled( - kEnableExtensionsPermissionsForSupervisedUsersOnDesktop); -#endif // BUILDFLAG(IS_CHROMEOS) -#else return false; #endif // BUILDFLAG(ENABLE_EXTENSIONS_CORE) }
diff --git a/chrome/browser/supervised_user/supervised_user_browser_utils_unittest.cc b/chrome/browser/supervised_user/supervised_user_browser_utils_unittest.cc index ed3a9a9..c3bcef7 100644 --- a/chrome/browser/supervised_user/supervised_user_browser_utils_unittest.cc +++ b/chrome/browser/supervised_user/supervised_user_browser_utils_unittest.cc
@@ -14,7 +14,6 @@ #include "components/signin/public/base/consent_level.h" #include "components/signin/public/identity_manager/account_info.h" #include "components/signin/public/identity_manager/identity_test_environment.h" -#include "components/supervised_user/core/common/features.h" #include "content/public/test/browser_task_environment.h" #include "content/public/test/test_utils.h" #include "extensions/buildflags/buildflags.h" @@ -69,70 +68,26 @@ enum class ExtensionsPermissionStatus { kEnabled, kDisabled }; -// Tests for the method AreExtensionsPermissionsEnabled which -// depends on enabling platform-specific feature flags. +// Tests for the method AreExtensionsPermissionsEnabled. class SupervisedUserBrowserUtilsTestWithExtensionsPermissionsFeature : public SupervisedUserBrowserUtilsTest, public testing::WithParamInterface<ExtensionsPermissionStatus> { - public: - SupervisedUserBrowserUtilsTestWithExtensionsPermissionsFeature() { -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || \ - BUILDFLAG(IS_DESKTOP_ANDROID) - if (AreExtensionsPermitted()) { - feature_list_.InitAndEnableFeature( - supervised_user:: - kEnableExtensionsPermissionsForSupervisedUsersOnDesktop); - } else { - feature_list_.InitAndDisableFeature( - supervised_user:: - kEnableExtensionsPermissionsForSupervisedUsersOnDesktop); - } -#endif - } - - bool AreExtensionsPermitted() const { - return GetParam() == ExtensionsPermissionStatus::kEnabled; - } - - private: - base::test::ScopedFeatureList feature_list_; }; -TEST_P(SupervisedUserBrowserUtilsTestWithExtensionsPermissionsFeature, +TEST_F(SupervisedUserBrowserUtilsTestWithExtensionsPermissionsFeature, AreExtensionsPermissionsEnabledWithSupervisedUser) { profile()->AsTestingProfile()->SetIsSupervisedProfile(true); #if BUILDFLAG(ENABLE_EXTENSIONS_CORE) - EXPECT_EQ(supervised_user::AreExtensionsPermissionsEnabled(profile()), - AreExtensionsPermitted()); + EXPECT_TRUE(supervised_user::AreExtensionsPermissionsEnabled(profile())); #else EXPECT_FALSE(supervised_user::AreExtensionsPermissionsEnabled(profile())); #endif // BUILDFLAG(ENABLE_EXTENSIONS) } -TEST_P(SupervisedUserBrowserUtilsTestWithExtensionsPermissionsFeature, +TEST_F(SupervisedUserBrowserUtilsTestWithExtensionsPermissionsFeature, AreExtensionsPermissionsEnabledWithNonSupervisedUser) { profile()->AsTestingProfile()->SetIsSupervisedProfile(false); EXPECT_FALSE(supervised_user::AreExtensionsPermissionsEnabled(profile())); } - -INSTANTIATE_TEST_SUITE_P( - All, - SupervisedUserBrowserUtilsTestWithExtensionsPermissionsFeature, -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) - testing::Values(ExtensionsPermissionStatus::kEnabled, - ExtensionsPermissionStatus::kDisabled), -#else - // ChromeOS has supervised user extension permissions on by default. - testing::Values(ExtensionsPermissionStatus::kEnabled), -#endif - [](const testing::TestParamInfo<ExtensionsPermissionStatus> info) { - // Generate the test suffix from boolean param. - switch (info.param) { - case ExtensionsPermissionStatus::kEnabled: - return "with_enabled_extension_permissions"; - case ExtensionsPermissionStatus::kDisabled: - return "with_disabled_extension_permissions"; - } - });
diff --git a/chrome/browser/supervised_user/supervised_user_extensions_delegate_impl.cc b/chrome/browser/supervised_user/supervised_user_extensions_delegate_impl.cc index 85dcd69..2f83d486 100644 --- a/chrome/browser/supervised_user/supervised_user_extensions_delegate_impl.cc +++ b/chrome/browser/supervised_user/supervised_user_extensions_delegate_impl.cc
@@ -200,27 +200,10 @@ #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) CHECK(contents.value()); content::WebContents* web_contents = contents.value().get(); - if (supervised_user:: - IsSupervisedUserSkipParentApprovalToInstallExtensionsEnabled()) { - // On the new mode always invoke the parent permission dialog. - ShowParentPermissionDialogForExtension(extension, web_contents, icon, - extension_approval_entry_point); - return; - } - // On the old mode (to be deprecated) invoke the "Blocked extensions" screen - // if the parent had picked this settings. - // Let parent approval dialog handle the case of not being able to install - // extensions. - auto* profile = Profile::FromBrowserContext(context_); - if (!profile->GetPrefs()->GetBoolean( - prefs::kSupervisedUserExtensionsMayRequestPermissions)) { - ShowInstallBlockedByParentDialogForExtension( - extension, web_contents, - ExtensionInstalledBlockedByParentDialogAction::kEnable); - return; - } + // Always invoke the parent permission dialog. ShowParentPermissionDialogForExtension(extension, web_contents, icon, extension_approval_entry_point); + return; #elif BUILDFLAG(IS_CHROMEOS) // ParentAccessDialog handles the blocked use case for ChromeOS. extension_approvals_manager_ =
diff --git a/chrome/browser/supervised_user/supervised_user_extensions_manager.cc b/chrome/browser/supervised_user/supervised_user_extensions_manager.cc index f5a9bb1..3b07a9b8 100644 --- a/chrome/browser/supervised_user/supervised_user_extensions_manager.cc +++ b/chrome/browser/supervised_user/supervised_user_extensions_manager.cc
@@ -161,14 +161,7 @@ SupervisedUserServiceFactory::GetForBrowserContext(context_); bool has_custodian = supervised_user_service->GetCustodian() || supervised_user_service->GetSecondCustodian(); - - if (supervised_user:: - IsSupervisedUserSkipParentApprovalToInstallExtensionsEnabled()) { return has_custodian; - } - return has_custodian && - user_prefs_->GetBoolean( - prefs::kSupervisedUserExtensionsMayRequestPermissions); } void SupervisedUserExtensionsManager::RecordExtensionEnablementUmaMetrics( @@ -309,9 +302,7 @@ if (base::Contains(approved_extensions_set_, extension.id())) { return SupervisedUserExtensionsManager::ExtensionState::ALLOWED; } - if (IsLocallyParentApprovedExtension(extension.id()) && - supervised_user:: - IsSupervisedUserSkipParentApprovalToInstallExtensionsEnabled()) { + if (IsLocallyParentApprovedExtension(extension.id())) { return SupervisedUserExtensionsManager::ExtensionState::ALLOWED; } return SupervisedUserExtensionsManager::ExtensionState::REQUIRE_APPROVAL; @@ -453,26 +444,7 @@ bool SupervisedUserExtensionsManager::ShouldBlockExtension( const std::string& extension_id) const { - if (supervised_user:: - IsSupervisedUserSkipParentApprovalToInstallExtensionsEnabled()) { - // On this extension handling mode, the user is never blocked from - // installing extensions. - return false; - } - if (user_prefs_->GetBoolean( - prefs::kSupervisedUserExtensionsMayRequestPermissions)) { - return false; - } - if (!extension_registry_->GetInstalledExtension(extension_id)) { - // Block child users from installing new extensions. Already installed - // extensions should not be affected. - return true; - } - if (extension_prefs_->DidExtensionEscalatePermissions(extension_id)) { - // Block child users from approving existing extensions asking for - // additional permissions. - return true; - } + // Users can always install extensions. return false; } @@ -490,11 +462,6 @@ return; } - if (!supervised_user:: - IsSupervisedUserSkipParentApprovalToInstallExtensionsEnabled()) { - return; - } - if (supervised_user::IsSubjectToParentalControls(*user_prefs_)) { // In the case of of a supervised user locally approve their extensions. DoExtensionsMigrationToParentApproved(); @@ -513,9 +480,6 @@ } void SupervisedUserExtensionsManager::DoExtensionsMigrationToParentApproved() { - CHECK(supervised_user:: - IsSupervisedUserSkipParentApprovalToInstallExtensionsEnabled()); - base::Value::Dict unapproved_extensions_dict = GetExtensionsMissingApproval(*user_prefs_); user_prefs_->SetDict(prefs::kSupervisedUserLocallyParentApprovedExtensions, @@ -539,10 +503,6 @@ bool SupervisedUserExtensionsManager::IsLocallyParentApprovedExtension( const std::string& extension_id) const { - if (!supervised_user:: - IsSupervisedUserSkipParentApprovalToInstallExtensionsEnabled()) { - return false; - } const base::Value::Dict& current_locally_approved_dict = user_prefs_->GetDict( prefs::kSupervisedUserLocallyParentApprovedExtensions); return base::Contains(current_locally_approved_dict, extension_id);
diff --git a/chrome/browser/sync/sync_ui_util.cc b/chrome/browser/sync/sync_ui_util.cc index c881f96..5df3197 100644 --- a/chrome/browser/sync/sync_ui_util.cc +++ b/chrome/browser/sync/sync_ui_util.cc
@@ -374,16 +374,13 @@ // Note the condition checked is not IsInitialSyncFeatureSetupComplete(), // because the setup incomplete case is treated separately below. See the // comment in ShouldRequestSyncConfirmation() about dashboard resets. + if (service->RequiresClientUpgrade()) { + return AvatarSyncErrorType::kUpgradeClientError; + } - if (switches::IsImprovedSigninUIOnDesktopEnabled()) { - if (service->RequiresClientUpgrade()) { - return AvatarSyncErrorType::kUpgradeClientError; - } - - if (service->GetUserSettings() - ->IsPassphraseRequiredForPreferredDataTypes()) { - return AvatarSyncErrorType::kPassphraseError; - } + if (service->GetUserSettings() + ->IsPassphraseRequiredForPreferredDataTypes()) { + return AvatarSyncErrorType::kPassphraseError; } return GetTrustedVaultError(service); @@ -434,54 +431,31 @@ case AvatarSyncErrorType::kSyncPaused: return l10n_util::GetStringUTF16(IDS_PROFILES_DICE_SYNC_PAUSED_TITLE); case AvatarSyncErrorType::kTrustedVaultKeyMissingForPasswordsError: - if (switches::IsImprovedSigninUIOnDesktopEnabled()) { - return l10n_util::GetStringFUTF16( - IDS_SYNC_ERROR_PASSWORDS_USER_MENU_ERROR_DESCRIPTION, - base::UTF8ToUTF16(user_email)); - } - return l10n_util::GetStringUTF16( - is_sync_feature_enabled - ? IDS_SYNC_ERROR_PASSWORDS_USER_MENU_TITLE - : IDS_SYNC_ERROR_PASSWORDS_USER_MENU_TITLE_SIGNED_IN_ONLY); + return l10n_util::GetStringFUTF16( + IDS_SYNC_ERROR_PASSWORDS_USER_MENU_ERROR_DESCRIPTION, + base::UTF8ToUTF16(user_email)); case AvatarSyncErrorType:: kTrustedVaultRecoverabilityDegradedForPasswordsError: - if (switches::IsImprovedSigninUIOnDesktopEnabled()) { - return l10n_util::GetStringFUTF16( - IDS_SYNC_ERROR_RECOVERABILITY_DEGRADED_FOR_PASSWORDS_USER_MENU_ERROR_DESCRIPTION, - base::UTF8ToUTF16(user_email)); - } - return l10n_util::GetStringUTF16( - IDS_SYNC_ERROR_RECOVERABILITY_DEGRADED_FOR_PASSWORDS_USER_MENU_TITLE); + return l10n_util::GetStringFUTF16( + IDS_SYNC_ERROR_RECOVERABILITY_DEGRADED_FOR_PASSWORDS_USER_MENU_ERROR_DESCRIPTION, + base::UTF8ToUTF16(user_email)); case AvatarSyncErrorType:: kTrustedVaultRecoverabilityDegradedForEverythingError: - if (switches::IsImprovedSigninUIOnDesktopEnabled()) { - return l10n_util::GetStringFUTF16( - IDS_SYNC_ERROR_TRUSTED_VAULT_USER_MENU_ERROR_DESCRIPTION, - base::UTF8ToUTF16(user_email)); - } - return l10n_util::GetStringUTF16( - IDS_SYNC_ERROR_RECOVERABILITY_DEGRADED_FOR_EVERYTHING_USER_MENU_TITLE); + return l10n_util::GetStringFUTF16( + IDS_SYNC_ERROR_TRUSTED_VAULT_USER_MENU_ERROR_DESCRIPTION, + base::UTF8ToUTF16(user_email)); case AvatarSyncErrorType::kPassphraseError: - if (switches::IsImprovedSigninUIOnDesktopEnabled()) { - return l10n_util::GetStringFUTF16( - IDS_SYNC_ERROR_PASSPHRASE_USER_MENU_ERROR_DESCRIPTION, - base::UTF8ToUTF16(user_email)); - } - return l10n_util::GetStringUTF16(IDS_SYNC_ERROR_USER_MENU_TITLE); + return l10n_util::GetStringFUTF16( + IDS_SYNC_ERROR_PASSPHRASE_USER_MENU_ERROR_DESCRIPTION, + base::UTF8ToUTF16(user_email)); case AvatarSyncErrorType::kUpgradeClientError: - if (switches::IsImprovedSigninUIOnDesktopEnabled()) { - return l10n_util::GetStringFUTF16( - IDS_SYNC_ERROR_UPGRADE_CLIENT_USER_MENU_ERROR_DESCRIPTION, - base::UTF8ToUTF16(user_email)); - } - return l10n_util::GetStringUTF16(IDS_SYNC_ERROR_USER_MENU_TITLE); + return l10n_util::GetStringFUTF16( + IDS_SYNC_ERROR_UPGRADE_CLIENT_USER_MENU_ERROR_DESCRIPTION, + base::UTF8ToUTF16(user_email)); case AvatarSyncErrorType::kTrustedVaultKeyMissingForEverythingError: - if (switches::IsImprovedSigninUIOnDesktopEnabled()) { - return l10n_util::GetStringFUTF16( - IDS_SYNC_ERROR_TRUSTED_VAULT_USER_MENU_ERROR_DESCRIPTION, - base::UTF8ToUTF16(user_email)); - } - return l10n_util::GetStringUTF16(IDS_SYNC_ERROR_USER_MENU_TITLE); + return l10n_util::GetStringFUTF16( + IDS_SYNC_ERROR_TRUSTED_VAULT_USER_MENU_ERROR_DESCRIPTION, + base::UTF8ToUTF16(user_email)); case AvatarSyncErrorType::kSettingsUnconfirmedError: case AvatarSyncErrorType::kManagedUserUnrecoverableError: case AvatarSyncErrorType::kUnrecoverableError:
diff --git a/chrome/browser/sync/test/integration/select_type_and_migrate_local_data_items_when_active_sync_test.cc b/chrome/browser/sync/test/integration/select_type_and_migrate_local_data_items_when_active_sync_test.cc index be34f6a..db9c57a 100644 --- a/chrome/browser/sync/test/integration/select_type_and_migrate_local_data_items_when_active_sync_test.cc +++ b/chrome/browser/sync/test/integration/select_type_and_migrate_local_data_items_when_active_sync_test.cc
@@ -62,8 +62,7 @@ password_(CreateTestPasswordForm(0)) { feature_list_.InitWithFeatures( /*enabled_features=*/ - {switches::kImprovedSigninUIOnDesktop, - switches::kSyncEnableBookmarksInTransportMode, + {switches::kSyncEnableBookmarksInTransportMode, autofill::features::kAutofillSupportLastNamePrefix}, /*disabled_features=*/{ syncer::kSyncEnableContactInfoDataTypeForCustomPassphraseUsers}); @@ -476,16 +475,12 @@ SelectTypeAndMigrateLocalDataItemsWhenActiveWithContactInfoForCustomPassphraseUsersTest : public SelectTypeAndMigrateLocalDataItemsWhenActiveTest { public: - SelectTypeAndMigrateLocalDataItemsWhenActiveWithContactInfoForCustomPassphraseUsersTest() { - feature_list_.InitWithFeatures( - /*enabled_features=*/ - {switches::kImprovedSigninUIOnDesktop, - syncer::kSyncEnableContactInfoDataTypeForCustomPassphraseUsers}, - /*disabled_features=*/{}); - } + SelectTypeAndMigrateLocalDataItemsWhenActiveWithContactInfoForCustomPassphraseUsersTest() = + default; private: - base::test::ScopedFeatureList feature_list_; + base::test::ScopedFeatureList feature_list_{ + syncer::kSyncEnableContactInfoDataTypeForCustomPassphraseUsers}; }; IN_PROC_BROWSER_TEST_F(
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc index ffe83e5..d02f87b 100644 --- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc +++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -2970,6 +2970,11 @@ // Sanity-check the existence of a few comparison metrics. histogram_tester.ExpectUniqueSample( "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId." + "ConsideringAllBookmarks.ByUrl", + /*sample=*/4 /*kLocalDataIsStrictSubsetOfAccountData*/, + /*expected_bucket_count=*/1); + histogram_tester.ExpectUniqueSample( + "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId." "ConsideringAllBookmarks.ByUrlAndUuid", /*sample=*/4 /*kLocalDataIsStrictSubsetOfAccountData*/, /*expected_bucket_count=*/1);
diff --git a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc index 70da737a..ff5e7c9 100644 --- a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc +++ b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
@@ -63,6 +63,7 @@ #include "components/trusted_vault/test/fake_security_domains_server.h" #include "components/trusted_vault/trusted_vault_client.h" #include "components/trusted_vault/trusted_vault_connection.h" +#include "components/trusted_vault/trusted_vault_histograms.h" #include "components/trusted_vault/trusted_vault_server_constants.h" #include "components/trusted_vault/trusted_vault_service.h" #include "components/variations/synthetic_trial_registry.h" @@ -2117,8 +2118,12 @@ EXPECT_FALSE(GetSecurityDomainsServer()->ReceivedInvalidRequest()); histogram_tester.ExpectUniqueSample( - "TrustedVault.DownloadKeysStatus.ChromeSync", - /*sample=*/trusted_vault::TrustedVaultDownloadKeysStatus::kSuccess, + "TrustedVault.RecoverKeysOutcome.ChromeSync", + /*sample=*/trusted_vault::TrustedVaultRecoverKeysOutcomeForUMA::kSuccess, + /*expected_bucket_count=*/1); + histogram_tester.ExpectUniqueSample( + "TrustedVault.DownloadKeysStatus.PhysicalDevice.ChromeSync", + /*sample=*/trusted_vault::TrustedVaultDownloadKeysStatusForUMA::kSuccess, /*expected_bucket_count=*/1); histogram_tester.ExpectUniqueSample( "TrustedVault.SecurityDomainServiceURLFetchResponse.DownloadKeys",
diff --git a/chrome/browser/sync/test/integration/single_client_preferences_sync_test.cc b/chrome/browser/sync/test/integration/single_client_preferences_sync_test.cc index 2d07587..52304f12 100644 --- a/chrome/browser/sync/test/integration/single_client_preferences_sync_test.cc +++ b/chrome/browser/sync/test/integration/single_client_preferences_sync_test.cc
@@ -1865,8 +1865,11 @@ : public SingleClientPreferencesWithAccountStorageSyncTest { public: SingleClientDecouplePriorityPreferencesSyncTestWithFlagDisabled() { - feature_list_.InitAndDisableFeature( - syncer::kSyncSupportAlwaysSyncingPriorityPreferences); + feature_list_.InitWithFeatures( + /*enabled_features=*/{syncer::kSeparateLocalAndAccountSearchEngines}, + // This is needed to enable prefs in transport mode. + /*disabled_features=*/{ + syncer::kSyncSupportAlwaysSyncingPriorityPreferences}); } private: @@ -1880,8 +1883,17 @@ ASSERT_TRUE(GetSyncService(0)->GetActiveDataTypes().empty()); ASSERT_TRUE(GetClient(0)->SignInPrimaryAccount()); + ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive()); + ASSERT_TRUE(GetSyncService(0)->GetUserSettings()->GetSelectedTypes().Has( + syncer::UserSelectableType::kPreferences)); + ASSERT_TRUE( + GetSyncService(0)->GetActiveDataTypes().Has(syncer::PREFERENCES)); + ASSERT_TRUE( + GetSyncService(0)->GetActiveDataTypes().Has(syncer::PRIORITY_PREFERENCES)); + // Disable all user selectable types. - ASSERT_TRUE(GetClient(0)->DisableSyncForAllDatatypes()); + GetSyncService(0)->GetUserSettings()->SetSelectedTypes( + /*sync_everything=*/false, syncer::UserSelectableTypeSet()); ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive()); // User toggle is off. @@ -1896,8 +1908,19 @@ class SingleClientDecouplePriorityPreferencesSyncTest : public SingleClientPreferencesWithAccountStorageSyncTest { - base::test::ScopedFeatureList feature_list_{ - syncer::kSyncSupportAlwaysSyncingPriorityPreferences}; + public: + SingleClientDecouplePriorityPreferencesSyncTest() { + feature_list_.InitWithFeatures( + /*enabled_features=*/{syncer:: + kSyncSupportAlwaysSyncingPriorityPreferences, + // This is needed to enable prefs in transport + // mode. + syncer::kSeparateLocalAndAccountSearchEngines}, + /*disabled_features=*/{}); + } + + private: + base::test::ScopedFeatureList feature_list_; }; IN_PROC_BROWSER_TEST_F(SingleClientDecouplePriorityPreferencesSyncTest, @@ -1906,8 +1929,17 @@ ASSERT_TRUE(GetSyncService(0)->GetActiveDataTypes().empty()); ASSERT_TRUE(GetClient(0)->SignInPrimaryAccount()); + ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive()); + ASSERT_TRUE(GetSyncService(0)->GetUserSettings()->GetSelectedTypes().Has( + syncer::UserSelectableType::kPreferences)); + ASSERT_TRUE( + GetSyncService(0)->GetActiveDataTypes().Has(syncer::PREFERENCES)); + ASSERT_TRUE( + GetSyncService(0)->GetActiveDataTypes().Has(syncer::PRIORITY_PREFERENCES)); + // Disable all user selectable types. - ASSERT_TRUE(GetClient(0)->DisableSyncForAllDatatypes()); + GetSyncService(0)->GetUserSettings()->SetSelectedTypes( + /*sync_everything=*/false, syncer::UserSelectableTypeSet()); ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive()); // User toggle is off. @@ -1921,4 +1953,115 @@ syncer::PRIORITY_PREFERENCES)); } +IN_PROC_BROWSER_TEST_F(SingleClientDecouplePriorityPreferencesSyncTest, + ShouldSyncAllowlistedPriorityPrefWithoutOptIn) { + ASSERT_TRUE(SetupClients()); + // Regular priority pref. + GetRegistry(GetProfile(0)) + ->RegisterStringPref( + sync_preferences::kSyncablePriorityPrefForTesting, "", + user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF); + // Always syncing priority pref. + GetRegistry(GetProfile(0)) + ->RegisterStringPref( + sync_preferences::kSyncableAlwaysSyncingPriorityPrefForTesting, "", + user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF); + + ASSERT_FALSE(GetSyncService(0)->GetActiveDataTypes().Has( + syncer::PRIORITY_PREFERENCES)); + + ASSERT_TRUE(GetClient(0)->SignInPrimaryAccount()); + // Disable all user selectable types. + GetSyncService(0)->GetUserSettings()->SetSelectedTypes( + /*sync_everything=*/false, syncer::UserSelectableTypeSet()); + ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive()); + + ASSERT_FALSE(GetSyncService(0)->GetUserSettings()->GetSelectedTypes().Has( + syncer::UserSelectableType::kPreferences)); + // Priority preferences is still active. + EXPECT_TRUE(GetSyncService(0)->GetActiveDataTypes().Has( + syncer::PRIORITY_PREFERENCES)); + + InjectPreferenceToFakeServer( + syncer::PRIORITY_PREFERENCES, + sync_preferences::kSyncablePriorityPrefForTesting, base::Value("value1")); + InjectPreferenceToFakeServer( + syncer::PRIORITY_PREFERENCES, + sync_preferences::kSyncableAlwaysSyncingPriorityPrefForTesting, + base::Value("value1")); + + // Only the allowlisted priority pref is synced. + EXPECT_TRUE( + PrefValueChecker( + GetPrefs(0), + sync_preferences::kSyncableAlwaysSyncingPriorityPrefForTesting, + base::Value("value1")) + .Wait()); + // The regular priority pref is not synced. + EXPECT_EQ( + GetPrefs(0)->GetString(sync_preferences::kSyncablePriorityPrefForTesting), + ""); + + GetPrefs(0)->SetString(sync_preferences::kSyncablePriorityPrefForTesting, + "value2"); + GetPrefs(0)->SetString( + sync_preferences::kSyncableAlwaysSyncingPriorityPrefForTesting, "value2"); + EXPECT_TRUE( + FakeServerPrefMatchesValueChecker( + syncer::PRIORITY_PREFERENCES, + sync_preferences::kSyncableAlwaysSyncingPriorityPrefForTesting, + ConvertPrefValueToValueInSpecifics(base::Value("value2"))) + .Wait()); + // The regular priority pref is not synced, remote is set to the old value. + EXPECT_THAT( + preferences_helper::GetPreferenceInFakeServer( + syncer::PRIORITY_PREFERENCES, + sync_preferences::kSyncablePriorityPrefForTesting, GetFakeServer()) + ->value(), + ConvertPrefValueToValueInSpecifics(base::Value("value1"))); +} + +IN_PROC_BROWSER_TEST_F(SingleClientDecouplePriorityPreferencesSyncTest, + ShouldSyncRegularPriorityPrefWithOptIn) { + ASSERT_TRUE(SetupClients()); + // Regular syncable pref. + GetRegistry(GetProfile(0)) + ->RegisterStringPref( + sync_preferences::kSyncablePriorityPrefForTesting, "", + user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF); + + ASSERT_TRUE(GetClient(0)->SignInPrimaryAccount()); + // Disable all user selectable types except preferences. + GetSyncService(0)->GetUserSettings()->SetSelectedTypes( + /*sync_everything=*/false, + syncer::UserSelectableTypeSet( + {syncer::UserSelectableType::kPreferences})); + ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive()); + + ASSERT_TRUE(GetSyncService(0)->GetUserSettings()->GetSelectedTypes().Has( + syncer::UserSelectableType::kPreferences)); + ASSERT_TRUE(GetSyncService(0)->GetActiveDataTypes().Has( + syncer::PRIORITY_PREFERENCES)); + + InjectPreferenceToFakeServer( + syncer::PRIORITY_PREFERENCES, + sync_preferences::kSyncablePriorityPrefForTesting, base::Value("value1")); + + // The regular syncable pref is synced. + EXPECT_TRUE( + PrefValueChecker(GetPrefs(0), + sync_preferences::kSyncablePriorityPrefForTesting, + base::Value("value1")) + .Wait()); + + GetPrefs(0)->SetString(sync_preferences::kSyncablePriorityPrefForTesting, + "value2"); + // The regular syncable pref is synced. + EXPECT_TRUE(FakeServerPrefMatchesValueChecker( + syncer::PRIORITY_PREFERENCES, + sync_preferences::kSyncablePriorityPrefForTesting, + ConvertPrefValueToValueInSpecifics(base::Value("value2"))) + .Wait()); +} + } // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc index 403ddd6..fd8d117 100644 --- a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc +++ b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
@@ -3,7 +3,6 @@ // found in the LICENSE file. #include "base/command_line.h" -#include "base/feature_list.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/test/metrics/histogram_tester.h" @@ -32,7 +31,6 @@ #include "components/autofill/core/browser/webdata/payments/payments_sync_bridge_util.h" #include "components/autofill/core/common/autofill_switches.h" #include "components/prefs/pref_service.h" -#include "components/signin/public/base/signin_switches.h" #include "components/signin/public/identity_manager/identity_test_utils.h" #include "components/sync/base/data_type.h" #include "components/sync/base/features.h" @@ -194,36 +192,10 @@ base::HistogramTester histogram_tester_; }; -// Same as SingleClientWalletSyncTest but allows exercising tests with and -// without `switches::IsImprovedSigninUIOnDesktopEnabled()`. -class SingleClientWalletWithImprovedSigninUISyncTest - : public SingleClientWalletSyncTest, - public testing::WithParamInterface<bool> { - public: - SingleClientWalletWithImprovedSigninUISyncTest() { - if (GetParam()) { - feature_list_.InitWithFeatures( - /*enabled_features=*/{switches::kImprovedSigninUIOnDesktop}, - /*disabled_features=*/{}); - } else { - feature_list_.InitWithFeatures( - /*enabled_features=*/{}, - /*disabled_features=*/{switches::kImprovedSigninUIOnDesktop}); - } - - EXPECT_EQ(GetParam(), switches::IsImprovedSigninUIOnDesktopEnabled()); - } - - ~SingleClientWalletWithImprovedSigninUISyncTest() override = default; - - private: - base::test::ScopedFeatureList feature_list_; -}; - // ChromeOS does not support late signin after profile creation, so the test // below does not apply, at least in the current form. #if !BUILDFLAG(IS_CHROMEOS) -IN_PROC_BROWSER_TEST_P(SingleClientWalletWithImprovedSigninUISyncTest, +IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest, DownloadAccountStorage_Card) { ASSERT_TRUE(SetupClients()); PaymentsDataManager* paydm = GetPaymentsDataManager(0); @@ -256,26 +228,14 @@ // feature flags. The corresponding metric is recorded twice as an artifact // of the test setup: SyncTest creates a new profile for single-client tests, // disregarding the existing profile that browser tests already have. - if (switches::IsImprovedSigninUIOnDesktopEnabled()) { - EXPECT_FALSE(GetAccountWebDataService(0)->UsesInMemoryDatabaseForMetrics()); - histogram_tester_.ExpectUniqueSample("WebDatabase.AutofillAccountStorage", - /*sample=*/2, // kOnDisk_SignedOut. - /*expected_bucket_count=*/2); - histogram_tester_.ExpectUniqueSample( - "Sync.PaymentsAccountStorageUponSyncConfiguration", - /*sample=*/1, // kSignedInExplicitlyWithOnDiskStorage. - /*expected_bucket_count=*/1); - } else { - EXPECT_TRUE(GetAccountWebDataService(0)->UsesInMemoryDatabaseForMetrics()); - histogram_tester_.ExpectUniqueSample( - "WebDatabase.AutofillAccountStorage", - /*sample=*/0, // kInMemory_FlagDisabled. - /*expected_bucket_count=*/2); - histogram_tester_.ExpectUniqueSample( - "Sync.PaymentsAccountStorageUponSyncConfiguration", - /*sample=*/2, // kSignedInExplicitlyWithInMemoryStorage. - /*expected_bucket_count=*/1); - } + EXPECT_FALSE(GetAccountWebDataService(0)->UsesInMemoryDatabaseForMetrics()); + histogram_tester_.ExpectUniqueSample("WebDatabase.AutofillAccountStorage", + /*sample=*/2, // kOnDisk_SignedOut. + /*expected_bucket_count=*/2); + histogram_tester_.ExpectUniqueSample( + "Sync.PaymentsAccountStorageUponSyncConfiguration", + /*sample=*/1, // kSignedInExplicitlyWithOnDiskStorage. + /*expected_bucket_count=*/1); ASSERT_NE(nullptr, paydm); std::vector<const CreditCard*> cards = paydm->GetCreditCards(); @@ -300,7 +260,7 @@ // PRE_ test used to ensure the user is signed in at the time the browser starts // up, which is more realistic for the implicit signed-in state. -IN_PROC_BROWSER_TEST_P(SingleClientWalletWithImprovedSigninUISyncTest, +IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest, PRE_DownloadAccountStorageWithImplicitSignIn_Card) { ASSERT_TRUE(SetupClients()); @@ -314,7 +274,7 @@ syncer::AUTOFILL_WALLET_DATA)); } -IN_PROC_BROWSER_TEST_P(SingleClientWalletWithImprovedSigninUISyncTest, +IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest, DownloadAccountStorageWithImplicitSignIn_Card) { ASSERT_TRUE(SetupClients()); GetFakeServer()->SetWalletData( @@ -346,17 +306,10 @@ // single-client tests, disregarding the existing profile that browser tests // already have. EXPECT_TRUE(GetAccountWebDataService(0)->UsesInMemoryDatabaseForMetrics()); - if (switches::IsImprovedSigninUIOnDesktopEnabled()) { - histogram_tester_.ExpectBucketCount( - "WebDatabase.AutofillAccountStorage", - /*sample=*/1, // kInMemory_SignedInImplicitly. - /*expected_bucket_count=*/1); - } else { - histogram_tester_.ExpectUniqueSample( - "WebDatabase.AutofillAccountStorage", - /*sample=*/0, // kInMemory_FlagDisabled. - /*expected_bucket_count=*/2); - } + histogram_tester_.ExpectBucketCount( + "WebDatabase.AutofillAccountStorage", + /*sample=*/1, // kInMemory_SignedInImplicitly. + /*expected_bucket_count=*/1); histogram_tester_.ExpectUniqueSample( "Sync.PaymentsAccountStorageUponSyncConfiguration", @@ -385,10 +338,6 @@ EXPECT_EQ(0U, GetServerCards(account_data).size()); } -INSTANTIATE_TEST_SUITE_P(Enabled, - SingleClientWalletWithImprovedSigninUISyncTest, - testing::Bool()); - // Wallet data should get cleared from the database when the user signs out and // different data should get downstreamed when the user signs in with a // different account.
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabList.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabList.java index 21f09d3..e450679 100644 --- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabList.java +++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabList.java
@@ -13,7 +13,7 @@ * A read only list of {@link Tab}s. This list understands the concept of an incognito list as well * as a currently selected tab (see {@link #index}). */ -@MockedInTests // Needed due to R8's computeDelayedInterfaceMethodSyntheticBridges. b/147584922 +@MockedInTests @NullMarked public interface TabList { // Keep this in sync with chrome/browser/ui/android/tab_model/tab_model.cc
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java index 8449f13..b58dc7e7 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java
@@ -16,6 +16,7 @@ import androidx.appcompat.widget.AppCompatImageView; import org.chromium.build.annotations.CheckDiscard; +import org.chromium.build.annotations.MockedInTests; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.util.KeyNavigationUtil; @@ -31,6 +32,7 @@ * * @param <T> The type of View being wrapped by this container. */ +@MockedInTests @NullMarked public class BaseSuggestionView<T extends View> extends SuggestionLayout { public final ImageView decorationIcon;
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DynamicSpacingRecyclerViewItemDecoration.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DynamicSpacingRecyclerViewItemDecoration.java index c9107e69..0c4ac14 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DynamicSpacingRecyclerViewItemDecoration.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/DynamicSpacingRecyclerViewItemDecoration.java
@@ -7,6 +7,7 @@ import androidx.annotation.Px; import androidx.annotation.VisibleForTesting; +import org.chromium.build.annotations.MockedInTests; import org.chromium.build.annotations.NullMarked; /** @@ -18,6 +19,7 @@ * * <p>Note: currently dynamic spacing is activated in portrait mode only. */ +@MockedInTests @NullMarked public class DynamicSpacingRecyclerViewItemDecoration extends SpacingRecyclerViewItemDecoration { private final @Px int mMinElementSpace;
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionView.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionView.java index 61d4658..1fd4a657 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionView.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/carousel/BaseCarouselSuggestionView.java
@@ -13,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView; import org.chromium.build.annotations.CheckDiscard; +import org.chromium.build.annotations.MockedInTests; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.omnibox.suggestions.RecyclerViewSelectionController; @@ -21,6 +22,7 @@ import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter; /** View for Carousel Suggestions. */ +@MockedInTests @NullMarked public class BaseCarouselSuggestionView extends RecyclerView { private RecyclerViewSelectionController mSelectionController;
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java index 3c1fc6f..a2e61a0 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java
@@ -25,6 +25,7 @@ import org.chromium.base.ThreadUtils; import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.supplier.ObservableSupplier; +import org.chromium.build.annotations.MockedInTests; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.omnibox.LocationBarDataProvider; @@ -46,6 +47,7 @@ import java.util.List; /** Class containing functionality related to voice search. */ +@MockedInTests @NullMarked public class VoiceRecognitionHandler { private static final String TAG = "VoiceRecognition";
diff --git a/chrome/browser/ui/color/chrome_color_id.h b/chrome/browser/ui/color/chrome_color_id.h index dba63e5..052ed82 100644 --- a/chrome/browser/ui/color/chrome_color_id.h +++ b/chrome/browser/ui/color/chrome_color_id.h
@@ -45,7 +45,6 @@ E_CPONLY(kColorAvatarButtonHighlightIncognitoForeground) \ E_CPONLY(kColorAvatarButtonIncognitoHover) \ E_CPONLY(kColorAvatarButtonNormalRipple) \ - E_CPONLY(kColorAvatarStrokeLight) \ E_CPONLY(kColorAvatarStroke) \ E_CPONLY(kColorAvatarFillForContrast) \ /* Bookmark bar colors. */ \
diff --git a/chrome/browser/ui/color/chrome_color_mixer.cc b/chrome/browser/ui/color/chrome_color_mixer.cc index 06a4373..79a64a37 100644 --- a/chrome/browser/ui/color/chrome_color_mixer.cc +++ b/chrome/browser/ui/color/chrome_color_mixer.cc
@@ -125,7 +125,6 @@ kColorAvatarButtonHighlightNormal}; mixer[kColorAvatarButtonHighlightExplicitText] = { kColorAvatarButtonHighlightNormal}; - mixer[kColorAvatarStrokeLight] = {SK_ColorWHITE}; mixer[kColorAvatarStroke] = {kColorToolbarButtonIcon}; mixer[kColorAvatarFillForContrast] = {kColorToolbar}; mixer[kColorBookmarkBarBackground] = {kColorToolbar};
diff --git a/chrome/browser/ui/feature_first_run/autofill_ai_first_run_dialog.cc b/chrome/browser/ui/feature_first_run/autofill_ai_first_run_dialog.cc index 80428c22..d4858198 100644 --- a/chrome/browser/ui/feature_first_run/autofill_ai_first_run_dialog.cc +++ b/chrome/browser/ui/feature_first_run/autofill_ai_first_run_dialog.cc
@@ -79,8 +79,8 @@ autofill_ai::AutofillAiOptInFunnelEvents::kFFRDialogShown); ShowFeatureFirstRunDialog( l10n_util::GetStringUTF16(IDS_AUTOFILL_AI_OPT_IN_IPH_TITLE), - ui::ImageModel::FromResourceId(IDR_SAVE_PASSPORT), - ui::ImageModel::FromResourceId(IDR_SAVE_PASSPORT_DARK), + ui::ImageModel::FromResourceId(IDR_AUTOFILL_AI_FFR_BANNER), + ui::ImageModel::FromResourceId(IDR_AUTOFILL_AI_FFR_BANNER_DARK), CreateDialogContentView(web_contents), base::BindOnce(&OnDialogAccepted, web_contents), base::BindOnce(&OnDialogCancelled), web_contents);
diff --git a/chrome/browser/ui/profiles/profile_colors_util.cc b/chrome/browser/ui/profiles/profile_colors_util.cc index b437db7..18ae42b 100644 --- a/chrome/browser/ui/profiles/profile_colors_util.cc +++ b/chrome/browser/ui/profiles/profile_colors_util.cc
@@ -190,25 +190,21 @@ DefaultAvatarColors result; if (color_utils::IsDark(profile_highlight_color)) { - if (base::FeatureList::IsEnabled(kOutlineSilhouetteIcon)) { - result.stroke_color = color_provider.GetColor(kColorAvatarStroke); - result.fill_color = - color_utils::GetContrastRatio(profile_highlight_color, - result.stroke_color) >= - color_utils::kMinimumVisibleContrastRatio - ? profile_highlight_color - : color_provider.GetColor(kColorAvatarFillForContrast); - return result; - } - result.stroke_color = color_provider.GetColor(kColorAvatarStrokeLight); - } else { - color_utils::HSL color_hsl; - color_utils::SkColorToHSL(profile_highlight_color, &color_hsl); - color_hsl.l = std::max(0., color_hsl.l - 0.5); - result.stroke_color = color_utils::HSLToSkColor( - color_hsl, SkColorGetA(profile_highlight_color)); + result.stroke_color = color_provider.GetColor(kColorAvatarStroke); + result.fill_color = + color_utils::GetContrastRatio(profile_highlight_color, + result.stroke_color) >= + color_utils::kMinimumVisibleContrastRatio + ? profile_highlight_color + : color_provider.GetColor(kColorAvatarFillForContrast); + return result; } + color_utils::HSL color_hsl; + color_utils::SkColorToHSL(profile_highlight_color, &color_hsl); + color_hsl.l = std::max(0., color_hsl.l - 0.5); + result.stroke_color = color_utils::HSLToSkColor( + color_hsl, SkColorGetA(profile_highlight_color)); result.fill_color = profile_highlight_color; return result; }
diff --git a/chrome/browser/ui/safety_hub/BUILD.gn b/chrome/browser/ui/safety_hub/BUILD.gn index 05bb70de..9c0be98 100644 --- a/chrome/browser/ui/safety_hub/BUILD.gn +++ b/chrome/browser/ui/safety_hub/BUILD.gn
@@ -38,7 +38,10 @@ ] if (is_android) { - sources += [ "password_status_check_result_android.h" ] + sources += [ + "notification_wrapper_android.h", + "password_status_check_result_android.h", + ] public_deps += [ "//components/prefs" ] } else { @@ -101,7 +104,11 @@ ] if (is_android) { - sources += [ "password_status_check_result_android.cc" ] + sources += [ + "notification_wrapper_android.cc", + "password_status_check_result_android.cc", + ] + deps += [ "//chrome/browser/safety_hub/android:jni_headers" ] } else { sources += [ "card_data_helper.cc",
diff --git a/chrome/browser/ui/safety_hub/OWNERS b/chrome/browser/ui/safety_hub/OWNERS index 18a4c54..3b124853 100644 --- a/chrome/browser/ui/safety_hub/OWNERS +++ b/chrome/browser/ui/safety_hub/OWNERS
@@ -4,3 +4,5 @@ per-file disruptive_notification_permissions_manager*=olesiamarukhno@google.com per-file disruptive_notification_permissions_manager*=antoniosartori@chromium.org +per-file notification_wrapper_android*=olesiamarukhno@google.com +per-file notification_wrapper_android*=antoniosartori@chromium.org
diff --git a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.cc b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.cc index 08291cb..3d241596 100644 --- a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.cc +++ b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.h" +#include "base/auto_reset.h" #include "base/containers/map_util.h" #include "base/json/values_util.h" #include "base/metrics/histogram_functions.h" @@ -12,7 +13,9 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/safety_hub/safety_hub_constants.h" #include "chrome/browser/ui/safety_hub/safety_hub_util.h" +#include "components/content_settings/core/browser/content_settings_type_set.h" #include "components/content_settings/core/browser/host_content_settings_map.h" +#include "components/content_settings/core/common/content_settings_pattern.h" #include "components/content_settings/core/common/content_settings_utils.h" #include "components/permissions/notifications_engagement_service.h" #include "components/safe_browsing/core/common/features.h" @@ -21,6 +24,10 @@ #include "services/metrics/public/cpp/ukm_builders.h" #include "url/gurl.h" +#if BUILDFLAG(IS_ANDROID) +#include "chrome/browser/ui/safety_hub/notification_wrapper_android.h" +#endif + namespace { constexpr char kRevocationResultHistogram[] = @@ -77,20 +84,30 @@ } // namespace +DisruptiveNotificationPermissionsManager::SafetyHubNotificationWrapper:: + ~SafetyHubNotificationWrapper() = default; + DisruptiveNotificationPermissionsManager:: DisruptiveNotificationPermissionsManager( scoped_refptr<HostContentSettingsMap> hcsm, site_engagement::SiteEngagementService* site_engagement_service) : hcsm_(std::move(hcsm)), - site_engagement_service_(site_engagement_service) {} + site_engagement_service_(site_engagement_service) +#if BUILDFLAG(IS_ANDROID) + , + notification_wrapper_(std::make_unique<NotificationWrapperAndroid>()) +#endif +{ + content_settings_observation_.Observe(hcsm_.get()); +} DisruptiveNotificationPermissionsManager:: ~DisruptiveNotificationPermissionsManager() = default; void DisruptiveNotificationPermissionsManager::RevokeDisruptiveNotifications() { - is_revocation_running_ = true; + base::AutoReset<bool> is_revocation_running(&is_revocation_running_, true); - int revoked_sites_count = 0; + int proposed_revoked_sites_count = 0; ContentSetting default_notification_setting = hcsm_->GetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS); @@ -99,6 +116,7 @@ notification_count_map = permissions::NotificationsEngagementService:: GetNotificationCountMapPerPatternPair(hcsm_.get()); + bool revoked_anything = false; for (const auto& item : hcsm_->GetSettingsForOneType(ContentSettingsType::NOTIFICATIONS)) { // Skip default content setting. @@ -151,7 +169,8 @@ ContentSettingsType::REVOKED_DISRUPTIVE_NOTIFICATION_PERMISSIONS, &info); if (!stored_value.is_none()) { - HandleExistingValue(url, std::move(stored_value), info); + revoked_anything |= + HandleExistingValueAndMaybeRevoke(url, std::move(stored_value), info); continue; } auto it = notification_count_map.find( @@ -178,61 +197,65 @@ notification_count); base::UmaHistogramEnumeration(kRevocationResultHistogram, RevocationResult::kProposedRevoke); - revoked_sites_count++; + proposed_revoked_sites_count++; } base::UmaHistogramCounts100( "Settings.SafetyHub.DisruptiveNotificationRevocations." "RevokedWebsitesCount", - revoked_sites_count); + proposed_revoked_sites_count); - is_revocation_running_ = false; + if (revoked_anything) { + DisplayNotification(); + } } -void DisruptiveNotificationPermissionsManager::HandleExistingValue( - const GURL& url, - base::Value stored_value, - const content_settings::SettingInfo& info) { +bool DisruptiveNotificationPermissionsManager:: + HandleExistingValueAndMaybeRevoke( + const GURL& url, + base::Value stored_value, + const content_settings::SettingInfo& info) { CHECK(stored_value.is_dict()); base::Value::Dict dict = std::move(stored_value).TakeDict(); auto recorded_score = dict.FindDouble(safety_hub::kSiteEngagementStr); if (!recorded_score.has_value()) { - return; + return false; } const std::string* revoked_status = dict.FindString(safety_hub::kRevokedStatusDictKeyStr); if (!revoked_status) { - return; + return false; } if (*revoked_status == safety_hub::kFalsePositiveStr) { base::UmaHistogramEnumeration(kRevocationResultHistogram, RevocationResult::kAlreadyFalsePositive); - return; + return false; } if (*revoked_status == safety_hub::kIgnoreStr) { base::UmaHistogramEnumeration(kRevocationResultHistogram, RevocationResult::kIgnore); - return; + return false; } if (*revoked_status != safety_hub::kProposedStr) { - return; + return false; } const double new_score = site_engagement_service_->GetScore(url); if (recorded_score.value() < new_score) { RecordFalsePositive(url, std::move(dict), info, new_score); - return; + return false; } if (!safe_browsing::kSafetyHubDisruptiveNotificationRevocationShadowRun .Get()) { RevokeNotifications(url, std::move(dict)); - return; + return true; } base::UmaHistogramEnumeration(kRevocationResultHistogram, RevocationResult::kAlreadyInProposedRevokeList); + return false; } void DisruptiveNotificationPermissionsManager::RecordFalsePositive( @@ -265,6 +288,34 @@ RevocationResult::kRevoke); } +void DisruptiveNotificationPermissionsManager::DisplayNotification() { + if (notification_wrapper_) { + notification_wrapper_->DisplayNotification( + GetRevokedNotifications().size()); + } +} + +void DisruptiveNotificationPermissionsManager::OnContentSettingChanged( + const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + ContentSettingsTypeSet content_type_set) { + if (content_type_set.ContainsAllTypes() || + content_type_set.GetType() == + ContentSettingsType::REVOKED_DISRUPTIVE_NOTIFICATION_PERMISSIONS) { + UpdateNotificationCount(); + } +} + +void DisruptiveNotificationPermissionsManager::UpdateNotificationCount() { + // If revocation is currently running there is no point in updating, since + // the notification will be re-displayed when the revocation completes. + if (!safe_browsing::kSafetyHubDisruptiveNotificationRevocationShadowRun + .Get() && + notification_wrapper_ && !is_revocation_running_) { + notification_wrapper_->UpdateNotification(GetRevokedNotifications().size()); + } +} + ContentSettingsForOneType DisruptiveNotificationPermissionsManager::GetRevokedNotifications() { ContentSettingsForOneType result; @@ -296,8 +347,8 @@ return result; } -bool DisruptiveNotificationPermissionsManager::IsRevocationRunning() { - return is_revocation_running_; +bool DisruptiveNotificationPermissionsManager::IsRunning() { + return is_revocation_running_ || is_regrant_or_undo_running_; } void DisruptiveNotificationPermissionsManager::RegrantPermissionForUrl( @@ -308,7 +359,8 @@ if (!safety_hub_util::IsUrlRevokedDisruptiveNotification(hcsm_.get(), url)) { return; } - is_revocation_running_ = true; + + base::AutoReset<bool> is_regrant_running(&is_regrant_or_undo_running_, true); UpdateNotificationPermission(hcsm_.get(), url, ContentSetting::CONTENT_SETTING_ALLOW); @@ -321,8 +373,6 @@ // so the value won't expire. UpdateContentSettingValue(hcsm_.get(), url, std::move(dict), /*constraints*/ {}); - - is_revocation_running_ = false; } void DisruptiveNotificationPermissionsManager::UndoRegrantPermissionForUrl( @@ -343,14 +393,13 @@ return; } - is_revocation_running_ = true; + base::AutoReset<bool> is_regrant_running(&is_regrant_or_undo_running_, true); + UpdateNotificationPermission(hcsm_.get(), url, ContentSetting::CONTENT_SETTING_DEFAULT); base::Value::Dict dict = std::move(stored_value).TakeDict(); dict.Set(safety_hub::kRevokedStatusDictKeyStr, safety_hub::kRevokeStr); UpdateContentSettingValue(hcsm_.get(), url, std::move(dict), constraints); - - is_revocation_running_ = false; } void DisruptiveNotificationPermissionsManager::ClearRevokedPermissionsList() { @@ -485,3 +534,8 @@ base::Clock* clock) { clock_ = clock; } + +void DisruptiveNotificationPermissionsManager::SetNotificationWrapperForTesting( + std::unique_ptr<SafetyHubNotificationWrapper> wrapper) { + notification_wrapper_ = std::move(wrapper); +}
diff --git a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.h b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.h index 84031f99..2a5a1a4 100644 --- a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.h +++ b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.h
@@ -5,9 +5,15 @@ #ifndef CHROME_BROWSER_UI_SAFETY_HUB_DISRUPTIVE_NOTIFICATION_PERMISSIONS_MANAGER_H_ #define CHROME_BROWSER_UI_SAFETY_HUB_DISRUPTIVE_NOTIFICATION_PERMISSIONS_MANAGER_H_ +#include <memory> + +#include "base/scoped_observation.h" #include "base/time/clock.h" #include "base/time/default_clock.h" +#include "components/content_settings/core/browser/content_settings_observer.h" +#include "components/content_settings/core/browser/content_settings_type_set.h" #include "components/content_settings/core/browser/host_content_settings_map.h" +#include "components/content_settings/core/common/content_settings_pattern.h" #include "services/metrics/public/cpp/ukm_source_id.h" class GURL; @@ -19,7 +25,8 @@ // This class keeps track of disruptive notification permissions by checking the // average daily notification counts and site engagement score. -class DisruptiveNotificationPermissionsManager { +class DisruptiveNotificationPermissionsManager + : public content_settings::Observer { public: // These values are persisted to logs. Entries should not be renumbered and // numeric values should never be reused. @@ -62,6 +69,13 @@ }; // LINT.ThenChange(//tools/metrics/histograms/metadata/settings/enums.xml:DisruptiveNotificationRevocationResult) + class SafetyHubNotificationWrapper { + public: + virtual ~SafetyHubNotificationWrapper(); + virtual void DisplayNotification(int num_revoked_permissions) = 0; + virtual void UpdateNotification(int num_revoked_permissions) = 0; + }; + explicit DisruptiveNotificationPermissionsManager( scoped_refptr<HostContentSettingsMap> hcsm, site_engagement::SiteEngagementService* site_engagement_service); @@ -71,7 +85,7 @@ DisruptiveNotificationPermissionsManager& operator=( const DisruptiveNotificationPermissionsManager&) = delete; - ~DisruptiveNotificationPermissionsManager(); + ~DisruptiveNotificationPermissionsManager() override; // Revokes notification permissions for disruptive sites and records // the revoked websites in the content setting. @@ -82,7 +96,7 @@ ContentSettingsForOneType GetRevokedNotifications(); // Returns true if settings are being changed due to auto revocation; - bool IsRevocationRunning(); + bool IsRunning(); // If the url has a revoked disruptive notification permission, this method // allows the notification permissions again and adds a constraint so that @@ -114,16 +128,26 @@ const GURL& url, ukm::SourceId source_id); + // content_settings::Observer implementation. + void OnContentSettingChanged( + const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + ContentSettingsTypeSet content_type_set) override; + // Test support: void SetClockForTesting(base::Clock* clock); + void SetNotificationWrapperForTesting( + std::unique_ptr<SafetyHubNotificationWrapper> wrapper); private: // Process existing content setting value: record false positive, revoke // notifications or report the site as already in the proposed revocation - // list. - void HandleExistingValue(const GURL& url, - base::Value stored_value, - const content_settings::SettingInfo& info); + // list. Returns `true` if notifications were actually revoked, `false` + // otherwise. + bool HandleExistingValueAndMaybeRevoke( + const GURL& url, + base::Value stored_value, + const content_settings::SettingInfo& info); // Updates the content setting to false positive and reports metrics. void RecordFalsePositive(const GURL& url, @@ -148,15 +172,31 @@ const content_settings::ContentSettingConstraints& constraints, int daily_notification_count); + // Displays the safety hub notification informing the users about revoked + // notification permissions. + void DisplayNotification(); + + // Updates the count of revoked notification permissions in the safety hub + // notification informing the users about revoked notification permissions. + void UpdateNotificationCount(); + scoped_refptr<HostContentSettingsMap> hcsm_; raw_ptr<site_engagement::SiteEngagementService> site_engagement_service_; + // Observer to watch for content settings changed. + base::ScopedObservation<HostContentSettingsMap, content_settings::Observer> + content_settings_observation_{this}; + raw_ptr<base::Clock> clock_ = base::DefaultClock::GetInstance(); // Returns true if the revocation of disruptive notification // permissions is happening. bool is_revocation_running_ = false; + + bool is_regrant_or_undo_running_ = false; + + std::unique_ptr<SafetyHubNotificationWrapper> notification_wrapper_; }; #endif // CHROME_BROWSER_UI_SAFETY_HUB_DISRUPTIVE_NOTIFICATION_PERMISSIONS_MANAGER_H_
diff --git a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager_unittest.cc b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager_unittest.cc index e48cbef9d..4ee7711 100644 --- a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager_unittest.cc +++ b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager_unittest.cc
@@ -20,12 +20,15 @@ #include "components/ukm/test_ukm_recorder.h" #include "content/public/browser/browser_context.h" #include "services/metrics/public/cpp/ukm_recorder.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +namespace { + using RevocationResult = DisruptiveNotificationPermissionsManager::RevocationResult; - -namespace { +using testing::ElementsAre; +using testing::IsEmpty; constexpr char kRevocationResultHistogram[] = "Settings.SafetyHub.DisruptiveNotificationRevocations.RevocationResult"; @@ -38,6 +41,27 @@ "Settings.SafetyHub.DisruptiveNotificationRevocations.FalsePositive." "SiteEngagement"; +class SafetyHubNotificationWrapperForTesting + : public DisruptiveNotificationPermissionsManager:: + SafetyHubNotificationWrapper { + public: + SafetyHubNotificationWrapperForTesting(std::vector<int>& display_called_with, + std::vector<int>& update_called_with) + : display_called_with_(display_called_with), + update_called_with_(update_called_with) {} + + void DisplayNotification(int num_revoked_permissions) override { + display_called_with_->push_back(num_revoked_permissions); + } + void UpdateNotification(int num_revoked_permissions) override { + update_called_with_->push_back(num_revoked_permissions); + } + + private: + base::raw_ref<std::vector<int>> display_called_with_; + base::raw_ref<std::vector<int>> update_called_with_; +}; + } // namespace class DisruptiveNotificationPermissionsManagerTest : public ::testing::Test { @@ -45,6 +69,10 @@ void SetUp() override { manager_ = std::make_unique<DisruptiveNotificationPermissionsManager>( hcsm(), site_engagement_service()); + manager_->SetNotificationWrapperForTesting( + std::make_unique<SafetyHubNotificationWrapperForTesting>( + display_notification_function_called_with_, + update_notification_function_called_with_)); } HostContentSettingsMap* hcsm() { @@ -75,6 +103,14 @@ .size(); } + const std::vector<int>& GetDisplayNotificationFunctionCalledWith() { + return display_notification_function_called_with_; + } + + const std::vector<int>& GetUpdateNotificationFunctionCalledWith() { + return update_notification_function_called_with_; + } + DisruptiveNotificationPermissionsManager* manager() { return manager_.get(); } TestingProfile* profile() { return &profile_; } @@ -87,6 +123,8 @@ base::test::TaskEnvironment::TimeSource::MOCK_TIME}; TestingProfile profile_; + std::vector<int> display_notification_function_called_with_; + std::vector<int> update_notification_function_called_with_; std::unique_ptr<DisruptiveNotificationPermissionsManager> manager_; }; @@ -133,6 +171,7 @@ RevocationResult::kProposedRevoke, 1); t.ExpectBucketCount(kRevokedWebsitesCountHistogram, 1, 1); t.ExpectBucketCount(kNotificationCountHistogram, 3, 1); + EXPECT_THAT(GetDisplayNotificationFunctionCalledWith(), IsEmpty()); // On the next run, site goes from proposed to actual revocation. manager()->RevokeDisruptiveNotifications(); @@ -143,6 +182,7 @@ t.ExpectBucketCount(kRevocationResultHistogram, RevocationResult::kProposedRevoke, 1); t.ExpectBucketCount(kRevocationResultHistogram, RevocationResult::kRevoke, 1); + EXPECT_THAT(GetDisplayNotificationFunctionCalledWith(), ElementsAre(1)); // After that, no new metrics are reported since there is no notification // content setting exception. @@ -151,6 +191,7 @@ t.ExpectBucketCount(kRevocationResultHistogram, RevocationResult::kProposedRevoke, 1); t.ExpectBucketCount(kRevocationResultHistogram, RevocationResult::kRevoke, 1); + EXPECT_THAT(GetDisplayNotificationFunctionCalledWith(), ElementsAre(1)); } TEST_F(DisruptiveNotificationPermissionsManagerRevocationTest, @@ -242,7 +283,7 @@ } TEST_F(DisruptiveNotificationPermissionsManagerRevocationTest, - NotEligableNotificationContentSettings) { + NotEligibleNotificationContentSettings) { base::HistogramTester t; // Already blocked notification. GURL url("https://www.example.com"); @@ -509,6 +550,9 @@ CONTENT_SETTING_ALLOW, hcsm()->GetContentSetting(url, url, ContentSettingsType::NOTIFICATIONS)); + // The notification count has been updated. + EXPECT_THAT(GetUpdateNotificationFunctionCalledWith(), ElementsAre(1, 0)); + // The content setting was updated to "ignore" to prevent autorevoking in the // future. EXPECT_EQ(GetRevokedPermissionsCount(), 1); @@ -732,6 +776,23 @@ EXPECT_FALSE(ignored_stored_value.is_none()); } +TEST_F(DisruptiveNotificationPermissionsManagerRevocationTest, + UpdateNotificationContentSettingsChanged) { + GURL url("https://chrome.test/"); + base::Value::Dict dict; + dict.Set(safety_hub::kRevokedStatusDictKeyStr, safety_hub::kRevokeStr); + dict.Set(safety_hub::kSiteEngagementStr, 0.0); + dict.Set(safety_hub::kDailyNotificationCountStr, 4); + dict.Set(safety_hub::kTimestampStr, + base::TimeToValue(base::Time::Now() - base::Days(3))); + hcsm()->SetWebsiteSettingCustomScope( + ContentSettingsPattern::FromURLNoWildcard(url), + ContentSettingsPattern::Wildcard(), + ContentSettingsType::REVOKED_DISRUPTIVE_NOTIFICATION_PERMISSIONS, + base::Value(std::move(dict))); + EXPECT_THAT(GetUpdateNotificationFunctionCalledWith(), ElementsAre(1)); +} + class DisruptiveNotificationPermissionsManagerShadowRunTest : public DisruptiveNotificationPermissionsManagerTest { public: @@ -776,7 +837,7 @@ t.ExpectBucketCount(kNotificationCountHistogram, 3, 1); // Repeated runs during the shadow run don't revoke the notification but - // report the the site is already in proposed revocation list instead. + // report that the site is already in proposed revocation list instead. manager()->RevokeDisruptiveNotifications(); EXPECT_EQ( CONTENT_SETTING_ALLOW, @@ -786,6 +847,9 @@ RevocationResult::kProposedRevoke, 1); t.ExpectBucketCount(kRevocationResultHistogram, RevocationResult::kAlreadyInProposedRevokeList, 1); + + // The shadow run should never display notifications. + EXPECT_THAT(GetDisplayNotificationFunctionCalledWith(), IsEmpty()); } TEST_F(DisruptiveNotificationPermissionsManagerShadowRunTest, @@ -849,4 +913,7 @@ manager()->RevokeDisruptiveNotifications(); t.ExpectBucketCount(kRevocationResultHistogram, RevocationResult::kAlreadyFalsePositive, 1); + + // The shadow run should never display notifications. + EXPECT_THAT(GetDisplayNotificationFunctionCalledWith(), IsEmpty()); }
diff --git a/chrome/browser/ui/safety_hub/notification_wrapper_android.cc b/chrome/browser/ui/safety_hub/notification_wrapper_android.cc new file mode 100644 index 0000000..17f41a6 --- /dev/null +++ b/chrome/browser/ui/safety_hub/notification_wrapper_android.cc
@@ -0,0 +1,24 @@ +// Copyright 2025 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/safety_hub/notification_wrapper_android.h" + +#include "base/android/jni_android.h" +#include "chrome/browser/safety_hub/android/jni_headers/UnsubscribedNotificationsNotificationManager_jni.h" + +NotificationWrapperAndroid::~NotificationWrapperAndroid() = default; + +void NotificationWrapperAndroid::DisplayNotification( + int num_revoked_permissions) { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_UnsubscribedNotificationsNotificationManager_displayNotification( + env, num_revoked_permissions); +} + +void NotificationWrapperAndroid::UpdateNotification( + int num_revoked_permissions) { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_UnsubscribedNotificationsNotificationManager_updateNotification( + env, num_revoked_permissions); +}
diff --git a/chrome/browser/ui/safety_hub/notification_wrapper_android.h b/chrome/browser/ui/safety_hub/notification_wrapper_android.h new file mode 100644 index 0000000..68e743e --- /dev/null +++ b/chrome/browser/ui/safety_hub/notification_wrapper_android.h
@@ -0,0 +1,19 @@ +// Copyright 2025 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_SAFETY_HUB_NOTIFICATION_WRAPPER_ANDROID_H_ +#define CHROME_BROWSER_UI_SAFETY_HUB_NOTIFICATION_WRAPPER_ANDROID_H_ + +#include "chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.h" + +class NotificationWrapperAndroid + : public DisruptiveNotificationPermissionsManager:: + SafetyHubNotificationWrapper { + public: + ~NotificationWrapperAndroid() override; + void DisplayNotification(int num_revoked_permissions) override; + void UpdateNotification(int num_revoked_permissions) override; +}; + +#endif // CHROME_BROWSER_UI_SAFETY_HUB_NOTIFICATION_WRAPPER_ANDROID_H_
diff --git a/chrome/browser/ui/safety_hub/revoked_permissions_service.cc b/chrome/browser/ui/safety_hub/revoked_permissions_service.cc index 511388f..a04a2f5f 100644 --- a/chrome/browser/ui/safety_hub/revoked_permissions_service.cc +++ b/chrome/browser/ui/safety_hub/revoked_permissions_service.cc
@@ -468,7 +468,7 @@ : false; const bool is_disruptive_revocation_running = disruptive_notification_manager_ - ? disruptive_notification_manager_->IsRevocationRunning() + ? disruptive_notification_manager_->IsRunning() : false; should_clean_revoked_permission_data = !is_unused_site_revocation_running &&
diff --git a/chrome/browser/ui/signin/chrome_signout_confirmation_prompt_browsertest.cc b/chrome/browser/ui/signin/chrome_signout_confirmation_prompt_browsertest.cc index 42afa54..009442f 100644 --- a/chrome/browser/ui/signin/chrome_signout_confirmation_prompt_browsertest.cc +++ b/chrome/browser/ui/signin/chrome_signout_confirmation_prompt_browsertest.cc
@@ -35,11 +35,7 @@ public testing::WithParamInterface< ChromeSignoutConfirmationPromptVariant> { public: - ChromeSignoutConfirmationPromptPixelTest() { - feature_list_.InitWithFeatures( - /*enabled_features=*/{switches::kImprovedSigninUIOnDesktop}, - /*disabled_features=*/{}); - } + ChromeSignoutConfirmationPromptPixelTest() = default; void ShowUi(const std::string& name) override { auto url = GURL(chrome::kChromeUISignoutConfirmationURL); @@ -82,9 +78,6 @@ ChromeSignoutConfirmationPromptVariant GetVariant() const { return GetParam(); } - - private: - base::test::ScopedFeatureList feature_list_; }; IN_PROC_BROWSER_TEST_P(ChromeSignoutConfirmationPromptPixelTest, @@ -110,11 +103,6 @@ : public SigninBrowserTestBaseT<ChromeSignoutConfirmationPromptPixelTest> { public: ChromeSignoutConfirmationPromptWithExtensionsPixelTest() { - feature_list_.InitWithFeatures( - /*enabled_features=*/{switches::kImprovedSigninUIOnDesktop, - switches::kEnableExtensionsExplicitBrowserSignin}, - /*disabled_features=*/{}); - base::FilePath test_data_dir; if (!base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir)) { ADD_FAILURE(); @@ -127,7 +115,8 @@ base::FilePath extension_data_dir() { return extension_data_dir_; } private: - base::test::ScopedFeatureList feature_list_; + base::test::ScopedFeatureList feature_list_{ + switches::kEnableExtensionsExplicitBrowserSignin}; // chrome/test/data/extensions/ base::FilePath extension_data_dir_;
diff --git a/chrome/browser/ui/signin/promos/bubble_signin_promo_interactive_uitest.cc b/chrome/browser/ui/signin/promos/bubble_signin_promo_interactive_uitest.cc index 2685d22..b3f7c5e5 100644 --- a/chrome/browser/ui/signin/promos/bubble_signin_promo_interactive_uitest.cc +++ b/chrome/browser/ui/signin/promos/bubble_signin_promo_interactive_uitest.cc
@@ -80,11 +80,6 @@ base::BindRepeating(&BubbleSignInPromoInteractiveUITest:: OnWillCreateBrowserContextServices, base::Unretained(this))); - scoped_feature_list_.InitWithFeatures( - /*enabled_features=*/ - {switches::kImprovedSigninUIOnDesktop, - switches::kSyncEnableBookmarksInTransportMode}, - /*disabled_features=*/{}); } void OnWillCreateBrowserContextServices(content::BrowserContext* context) { @@ -161,7 +156,8 @@ } protected: - base::test::ScopedFeatureList scoped_feature_list_; + base::test::ScopedFeatureList scoped_feature_list_{ + switches::kSyncEnableBookmarksInTransportMode}; ChromeSigninClientWithURLLoaderHelper url_loader_factory_helper_; base::CallbackListSubscription create_services_subscription_;
diff --git a/chrome/browser/ui/signin/promos/signin_promo_tab_helper_browsertest.cc b/chrome/browser/ui/signin/promos/signin_promo_tab_helper_browsertest.cc index 67b3be9b..7eb79c78 100644 --- a/chrome/browser/ui/signin/promos/signin_promo_tab_helper_browsertest.cc +++ b/chrome/browser/ui/signin/promos/signin_promo_tab_helper_browsertest.cc
@@ -9,7 +9,6 @@ #include "chrome/browser/ui/browser.h" #include "chrome/test/base/in_process_browser_test.h" #include "components/autofill/core/browser/test_utils/autofill_test_utils.h" -#include "components/signin/public/base/signin_switches.h" #include "components/signin/public/identity_manager/accounts_mutator.h" #include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/identity_test_utils.h" @@ -22,8 +21,6 @@ } protected: - base::test::ScopedFeatureList scoped_feature_list_{ - switches::kImprovedSigninUIOnDesktop}; autofill::test::AutofillBrowserTestEnvironment autofill_test_environment_; };
diff --git a/chrome/browser/ui/signin/signin_view_controller.cc b/chrome/browser/ui/signin/signin_view_controller.cc index 24038ea..71bc4c9 100644 --- a/chrome/browser/ui/signin/signin_view_controller.cc +++ b/chrome/browser/ui/signin/signin_view_controller.cc
@@ -32,7 +32,6 @@ #include "components/signin/public/base/signin_metrics.h" #include "components/signin/public/base/signin_pref_names.h" #include "components/signin/public/base/signin_prefs.h" -#include "components/signin/public/base/signin_switches.h" #include "components/signin/public/identity_manager/account_info.h" #include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h" #include "components/signin/public/identity_manager/identity_manager.h" @@ -795,17 +794,6 @@ void SigninViewController::ShowSignoutConfirmationPrompt( ChromeSignoutConfirmationPromptVariant prompt_variant, SignoutConfirmationCallback callback) { - if (!switches::IsImprovedSigninUIOnDesktopEnabled() && - prompt_variant == - ChromeSignoutConfirmationPromptVariant::kNoUnsyncedData && - !ShowAccountExtensionsOnSignout(browser_->profile())) { - // This variant is not enabled and there are no account extensions. Skip the - // UI and sign out immediately. - std::move(callback).Run(ChromeSignoutConfirmationChoice::kSignout, - /*uninstall_account_extensions_on_signout=*/false); - return; - } - CloseModalSignin(); dialog_ = std::make_unique<SigninModalDialogImpl>( SigninViewControllerDelegate::CreateSignoutConfirmationDelegate(
diff --git a/chrome/browser/ui/signin/signin_view_controller_browsertest.cc b/chrome/browser/ui/signin/signin_view_controller_browsertest.cc index 57c85e7..8b274e4 100644 --- a/chrome/browser/ui/signin/signin_view_controller_browsertest.cc +++ b/chrome/browser/ui/signin/signin_view_controller_browsertest.cc
@@ -236,13 +236,7 @@ class SigninViewControllerBrowserTest : public SigninViewControllerBrowserTestBase { public: - SigninViewControllerBrowserTest() { - feature_list_.InitWithFeatures( - /*enabled_features=*/ - {switches::kImprovedSigninUIOnDesktop, - features::kManagedProfileRequiredInterstitial}, - /*disabled_features=*/{}); - } + SigninViewControllerBrowserTest() = default; views::DialogDelegate* TriggerChromeSigninDialogForExtensionsPrompt( base::OnceClosure on_complete) { @@ -260,7 +254,8 @@ } private: - base::test::ScopedFeatureList feature_list_; + base::test::ScopedFeatureList feature_list_{ + features::kManagedProfileRequiredInterstitial}; }; IN_PROC_BROWSER_TEST_F( @@ -727,12 +722,6 @@ public testing::WithParamInterface<bool> { public: SigninViewControllerInteractiveBrowserTest() { - feature_list_.InitWithFeatures( - /*enabled_features=*/ - {switches::kImprovedSigninUIOnDesktop, - switches::kEnableExtensionsExplicitBrowserSignin}, - /*disabled_features=*/{}); - base::FilePath test_data_dir; if (!base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir)) { ADD_FAILURE(); @@ -831,7 +820,8 @@ } private: - base::test::ScopedFeatureList feature_list_; + base::test::ScopedFeatureList feature_list_{ + switches::kEnableExtensionsExplicitBrowserSignin}; // chrome/test/data/extensions/ base::FilePath extension_data_dir_;
diff --git a/chrome/browser/ui/sync/sync_passphrase_dialog_browsertest.cc b/chrome/browser/ui/sync/sync_passphrase_dialog_browsertest.cc index b8ec4d6..594c0f4 100644 --- a/chrome/browser/ui/sync/sync_passphrase_dialog_browsertest.cc +++ b/chrome/browser/ui/sync/sync_passphrase_dialog_browsertest.cc
@@ -11,10 +11,8 @@ #include "base/functional/callback_helpers.h" #include "base/strings/string_util.h" #include "base/test/mock_callback.h" -#include "base/test/scoped_feature_list.h" #include "chrome/common/url_constants.h" #include "chrome/test/interaction/interactive_browser_test.h" -#include "components/signin/public/base/signin_switches.h" #include "content/public/test/browser_test.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/interaction/element_identifier.h" @@ -33,11 +31,7 @@ class SyncPassphraseDialogBrowserTest : public InteractiveBrowserTest { public: - SyncPassphraseDialogBrowserTest() { - feature_list_.InitWithFeatures( - /*enabled_features=*/{switches::kImprovedSigninUIOnDesktop}, - /*disabled_features=*/{}); - } + SyncPassphraseDialogBrowserTest() = default; // Sets the password and waits for the state to be propagated. // `kPolledTextfieldContent` must be initialized with `PollState()` before @@ -50,7 +44,6 @@ protected: const std::string kTextFieldName = "Texfield"; const std::string kFooterLabelName = "FooterLabel"; - base::test::ScopedFeatureList feature_list_; }; IN_PROC_BROWSER_TEST_F(SyncPassphraseDialogBrowserTest, PixelTest) {
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc index 94d1542..0ac99f7c9 100644 --- a/chrome/browser/ui/toolbar/app_menu_model.cc +++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -112,7 +112,6 @@ #include "components/saved_tab_groups/public/features.h" #include "components/signin/public/base/signin_metrics.h" #include "components/signin/public/base/signin_pref_names.h" -#include "components/signin/public/base/signin_switches.h" #include "components/signin/public/identity_manager/identity_manager.h" #include "components/strings/grit/components_strings.h" #include "components/user_education/common/feature_promo/feature_promo_controller.h" @@ -509,15 +508,9 @@ } } - if (switches::IsImprovedSigninUIOnDesktopEnabled()) { - BuildManageGoogleAccountRow(profile); - BuildCustomizeProfileRow(profile); - BuildCloseProfileRow(profile); - } else { - BuildCustomizeProfileRow(profile); - BuildCloseProfileRow(profile); - BuildManageGoogleAccountRow(profile); - } + BuildManageGoogleAccountRow(profile); + BuildCustomizeProfileRow(profile); + BuildCloseProfileRow(profile); bool needs_separator = false; const bool is_guest_mode_enabled = profiles::IsGuestModeEnabled(*profile); @@ -546,11 +539,6 @@ other_profiles_.insert({menu_id, profile_entry->GetPath()}); } - if (is_guest_mode_enabled && - !switches::IsImprovedSigninUIOnDesktopEnabled()) { - needs_separator = true; - BuildGuestProfileRow(profile); - } if (needs_separator) { AddSeparator(ui::NORMAL_SEPARATOR); } @@ -560,8 +548,7 @@ IDS_ADD_NEW_PROFILE, kAccountAddChromeRefreshIcon); } - if (switches::IsImprovedSigninUIOnDesktopEnabled() && - is_guest_mode_enabled) { + if (is_guest_mode_enabled) { BuildGuestProfileRow(profile); } @@ -657,11 +644,8 @@ } void ProfileSubMenuModel::BuildGuestProfileRow(Profile* profile) { - AddItemWithStringIdAndVectorIcon( - this, IDC_OPEN_GUEST_PROFILE, IDS_OPEN_GUEST_PROFILE, - switches::IsImprovedSigninUIOnDesktopEnabled() - ? kAccountBoxIcon - : vector_icons::kAccountCircleChromeRefreshIcon); + AddItemWithStringIdAndVectorIcon(this, IDC_OPEN_GUEST_PROFILE, + IDS_OPEN_GUEST_PROFILE, kAccountBoxIcon); SetElementIdentifierAt(GetIndexOfCommandId(IDC_OPEN_GUEST_PROFILE).value(), AppMenuModel::kProfileOpenGuestItem); }
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc index 6a2f91ba..3f4a012 100644 --- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc +++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
@@ -228,7 +228,7 @@ gfx::Size preferred_size = GetSizeForLabelWidth(preferred_label_width); if (size_bounds.width().is_bounded() && preferred_size.width() > size_bounds.width()) { - preferred_size = GetSizeForLabelWidth(0); + preferred_size = GetMinimumSize(); // If the label should be shown, and supports elision, then extend the // width to whatever space is available. if (ShouldShowLabel() && label()->GetElideBehavior() != gfx::NO_ELIDE) { @@ -507,7 +507,7 @@ } gfx::Size IconLabelBubbleView::GetMinimumSize() const { - return views::View::GetMinimumSize(); + return GetSizeForLabelWidth(0); } bool IconLabelBubbleView::OnMousePressed(const ui::MouseEvent& event) {
diff --git a/chrome/browser/ui/views/page_action/page_action_metric_recorder_unittest.cc b/chrome/browser/ui/views/page_action/page_action_metric_recorder_unittest.cc index 1289238..e9493c8 100644 --- a/chrome/browser/ui/views/page_action/page_action_metric_recorder_unittest.cc +++ b/chrome/browser/ui/views/page_action/page_action_metric_recorder_unittest.cc
@@ -35,13 +35,15 @@ } void SetUp() override { - // By default, let the page action be "not visible." Tests can override. + // By default, let the page action be "not visible". Tests can override. ON_CALL(mock_model_, GetVisible()).WillByDefault(Return(false)); + + // By default, let the page action be "ephemeral". Tests can override. + ON_CALL(mock_model_, IsEphemeral()).WillByDefault(Return(true)); } - void CreateRecorder(bool is_ephemeral) { + void CreateRecorder() { properties_.type = PageActionIconType::kLensOverlay; - properties_.is_ephemeral = is_ephemeral; properties_.histogram_name = "LensOverlay"; recorder_ = std::make_unique<PageActionMetricsRecorder>( tab_, properties_, mock_model_, @@ -70,11 +72,12 @@ TEST_F(PageActionMetricsRecorderTest, NoRecordIfNotEphemeral) { base::HistogramTester histogram_tester; - CreateRecorder(/*is_ephemeral=*/false); + CreateRecorder(); - // Make the action "visible." Because it's not ephemeral, no metric is + // Make the page action "visible" and "non ephemeral". No metric will be // recorded. ON_CALL(mock_model_, GetVisible()).WillByDefault(Return(true)); + ON_CALL(mock_model_, IsEphemeral()).WillByDefault(Return(false)); FireModelChanged(); // Because is_ephemeral=false, "PageActionController.ActionTypeShown2" is not @@ -84,7 +87,7 @@ TEST_F(PageActionMetricsRecorderTest, RecordOnlyOncePerUrlIfEphemeral) { base::HistogramTester histogram_tester; - CreateRecorder(/*is_ephemeral=*/true); + CreateRecorder(); // Make the model "visible" and simulate the user visiting a particular URL. GURL url1("https://www.example.com/"); @@ -115,10 +118,9 @@ TEST_F(PageActionMetricsRecorderTest, NoRecordIfPageActionIsNotVisible) { base::HistogramTester histogram_tester; - CreateRecorder(/*is_ephemeral=*/true); + CreateRecorder(); - // The action is ephemeral, but GetVisible() returns false. - ON_CALL(mock_model_, GetVisible()).WillByDefault(Return(false)); + ON_CALL(mock_model_, IsEphemeral()).WillByDefault(Return(false)); // Even if we call the model-changed event, because !GetVisible(), // we do nothing. @@ -128,7 +130,7 @@ TEST_F(PageActionMetricsRecorderTest, RecordShownMetricsGeneralAndSpecific) { base::HistogramTester histogram_tester; - CreateRecorder(/*is_ephemeral=*/true); + CreateRecorder(); const std::string general_histogram = "PageActionController.Icon.CTR2"; const std::string specific_histogram = base::StrCat( @@ -170,7 +172,9 @@ TEST_F(PageActionMetricsRecorderTest, NoShownMetricsWhenNotEphemeral) { base::HistogramTester histogram_tester; - CreateRecorder(/*is_ephemeral=*/false); + CreateRecorder(); + + ON_CALL(mock_model_, IsEphemeral()).WillByDefault(Return(false)); const std::string general_histogram = "PageActionController.Icon.CTR2"; const std::string specific_histogram = base::StrCat( @@ -178,7 +182,6 @@ GURL url("https://www.example.com/"); content::WebContentsTester::For(tab_.GetContents())->NavigateAndCommit(url); - ON_CALL(mock_model_, GetVisible()).WillByDefault(Return(true)); FireModelChanged(); @@ -189,7 +192,7 @@ TEST_F(PageActionMetricsRecorderTest, RecordClickMetric) { base::HistogramTester histogram_tester; - CreateRecorder(/*is_ephemeral=*/true); + CreateRecorder(); const std::string general_histogram = "PageActionController.Icon.CTR2"; const std::string specific_histogram = base::StrCat( @@ -232,7 +235,7 @@ TEST_F(PageActionMetricsRecorderTest, NumberActionsShownWhenClicked_OneVisibleAction) { base::HistogramTester histogram_tester; - CreateRecorder(/*is_ephemeral=*/true); + CreateRecorder(); constexpr char kHistogram[] = "PageActionController.Icon.NumberActionsShownWhenClicked"; @@ -249,7 +252,7 @@ TEST_F(PageActionMetricsRecorderTest, NumberActionsShownWhenClicked_VariousVisibleCounts) { base::HistogramTester histogram_tester; - CreateRecorder(/*is_ephemeral=*/true); + CreateRecorder(); constexpr char kHistogram[] = "PageActionController.Icon.NumberActionsShownWhenClicked";
diff --git a/chrome/browser/ui/views/page_action/page_action_metrics_recorder.cc b/chrome/browser/ui/views/page_action/page_action_metrics_recorder.cc index b44f029..71bb393a 100644 --- a/chrome/browser/ui/views/page_action/page_action_metrics_recorder.cc +++ b/chrome/browser/ui/views/page_action/page_action_metrics_recorder.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/ui/views/page_action/page_action_metrics_recorder.h" #include "base/metrics/histogram_functions.h" +#include "base/strings/strcat.h" #include "chrome/browser/ui/views/page_action/page_action_enums.h" #include "chrome/browser/ui/views/page_action/page_action_model.h" #include "chrome/browser/ui/views/page_action/page_action_properties_provider.h" @@ -20,8 +21,7 @@ PageActionModelInterface& model, VisibleEphemeralPageActionsCountCallback visible_ephemeral_page_actions_count_callback) - : is_ephemeral_(properties.is_ephemeral), - page_action_type_(properties.type), + : page_action_type_(properties.type), histogram_name_(properties.histogram_name), visible_ephemeral_page_actions_count_callback_( std::move(visible_ephemeral_page_actions_count_callback)), @@ -33,9 +33,14 @@ void PageActionMetricsRecorder::OnPageActionModelChanged( const PageActionModelInterface& model) { - if (model.GetVisible()) { - OnPageActionVisible(); + // Page action can be permanent or ephemeral. For the + // "PageActionController.ActionTypeShown2" metric, it should be recorded only + // for when the page action is ephemeral. + if (!model.GetVisible() || !model.IsEphemeral()) { + return; } + + OnPageActionVisible(); } void PageActionMetricsRecorder::OnPageActionModelWillBeDeleted( @@ -44,13 +49,6 @@ } void PageActionMetricsRecorder::OnPageActionVisible() { - // Page action can be permanent or ephemeral. For the - // "PageActionController.ActionTypeShown2" metric, it should be recorded only - // for when the page action is ephemeral. - if (!is_ephemeral_) { - return; - } - CHECK(tab_interface_->GetContents()); // Only record the "Shown" metric the first time the icon appears on a "page".
diff --git a/chrome/browser/ui/views/page_action/page_action_metrics_recorder.h b/chrome/browser/ui/views/page_action/page_action_metrics_recorder.h index b67dacd..66ff740 100644 --- a/chrome/browser/ui/views/page_action/page_action_metrics_recorder.h +++ b/chrome/browser/ui/views/page_action/page_action_metrics_recorder.h
@@ -89,7 +89,6 @@ std::set<GURL> page_action_recorded_urls_; // Properties associated with the specific page action being observed. - bool is_ephemeral_; PageActionIconType page_action_type_; std::string histogram_name_;
diff --git a/chrome/browser/ui/views/page_action/page_action_view_unittest.cc b/chrome/browser/ui/views/page_action/page_action_view_unittest.cc index 045daf3..9335032 100644 --- a/chrome/browser/ui/views/page_action/page_action_view_unittest.cc +++ b/chrome/browser/ui/views/page_action/page_action_view_unittest.cc
@@ -118,7 +118,6 @@ void TearDown() override { ChromeViewsTestBase::TearDown(); - page_action_view_.reset(); action_item_ = nullptr; actions::ActionManager::Get().ResetActions(); pinned_actions_model_.reset(); @@ -140,7 +139,6 @@ TestingProfile profile_; private: - std::unique_ptr<PageActionView> page_action_view_; std::unique_ptr<PageActionView> test_page_action_view_; raw_ptr<actions::ActionItem> action_item_;
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc index c264279282..4123fb3 100644 --- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc +++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
@@ -2639,14 +2639,7 @@ EXPECT_EQ(accessibility.GetCachedDescription(), std::u16string()); } -class AvatarToolbarButtonWithImprovedSigninUIBrowserTest - : public AvatarToolbarButtonBrowserTest { - private: - base::test::ScopedFeatureList scoped_feature_list_{ - switches::kImprovedSigninUIOnDesktop}; -}; - -IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonWithImprovedSigninUIBrowserTest, +IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest, PassphraseErrorSignedIn) { AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser()); SigninWithImageAndClearGreeting(avatar, u"test@gmail.com"); @@ -2662,7 +2655,7 @@ #else #define MAYBE_PassphraseErrorSyncing PassphraseErrorSyncing #endif -IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonWithImprovedSigninUIBrowserTest, +IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest, MAYBE_PassphraseErrorSyncing) { AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser()); EnableSyncWithImageAndClearGreeting(avatar, u"test@gmail.com"); @@ -2678,7 +2671,7 @@ #else #define MAYBE_UpgradeClientError UpgradeClientError #endif -IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonWithImprovedSigninUIBrowserTest, +IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest, MAYBE_UpgradeClientError) { AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser()); EnableSyncWithImageAndClearGreeting(avatar, u"test@gmail.com");
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc index c9207b6c..649e35e5 100644 --- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc +++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
@@ -1213,16 +1213,14 @@ std::make_unique<ShowIdentityNameStateProvider>( /*state_observer=*/*this, *profile, avatar_toolbar_button_.get()); - if (switches::IsImprovedSigninUIOnDesktopEnabled()) { - states_[ButtonState::kUpgradeClientError] = - std::make_unique<SyncErrorStateProvider>( - /*state_observer=*/*this, *profile, - AvatarSyncErrorType::kUpgradeClientError); - states_[ButtonState::kPassphraseError] = - std::make_unique<SyncErrorStateProvider>( - /*state_observer=*/*this, *profile, - AvatarSyncErrorType::kPassphraseError); - } + states_[ButtonState::kUpgradeClientError] = + std::make_unique<SyncErrorStateProvider>( + /*state_observer=*/*this, *profile, + AvatarSyncErrorType::kUpgradeClientError); + states_[ButtonState::kPassphraseError] = + std::make_unique<SyncErrorStateProvider>( + /*state_observer=*/*this, *profile, + AvatarSyncErrorType::kPassphraseError); if (AccountConsistencyModeManager::IsDiceEnabledForProfile(profile)) { states_[ButtonState::kSyncPaused] = @@ -1620,8 +1618,7 @@ break; case ButtonState::kSyncError: if (!IdentityManagerFactory::GetForProfile(profile_)->HasPrimaryAccount( - signin::ConsentLevel::kSync) && - switches::IsImprovedSigninUIOnDesktopEnabled()) { + signin::ConsentLevel::kSync)) { color = color_provider->GetColor(kColorAvatarButtonHighlightSigninPaused); text = l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_SIGNIN_PAUSED); @@ -1743,8 +1740,7 @@ kColorAvatarButtonHighlightIncognitoForeground); case ButtonState::kSyncError: if (IdentityManagerFactory::GetForProfile(profile_)->HasPrimaryAccount( - signin::ConsentLevel::kSync) || - !switches::IsImprovedSigninUIOnDesktopEnabled()) { + signin::ConsentLevel::kSync)) { return color_provider->GetColor( kColorAvatarButtonHighlightSyncErrorForeground); } @@ -1827,8 +1823,7 @@ break; case ButtonState::kSyncError: if (IdentityManagerFactory::GetForProfile(profile_)->HasPrimaryAccount( - signin::ConsentLevel::kSync) || - !switches::IsImprovedSigninUIOnDesktopEnabled()) { + signin::ConsentLevel::kSync)) { break; } [[fallthrough]]; @@ -1870,8 +1865,7 @@ profiles::SHAPE_CIRCLE)); case ButtonState::kSyncError: if (IdentityManagerFactory::GetForProfile(profile_)->HasPrimaryAccount( - signin::ConsentLevel::kSync) || - !switches::IsImprovedSigninUIOnDesktopEnabled()) { + signin::ConsentLevel::kSync)) { return ui::ImageModel::FromImage(profiles::GetSizedAvatarIcon( GetProfileAvatarImage(icon_size), icon_size, icon_size, profiles::SHAPE_CIRCLE));
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.cc b/chrome/browser/ui/views/profiles/profile_menu_view.cc index 9e90efb..410e55f 100644 --- a/chrome/browser/ui/views/profiles/profile_menu_view.cc +++ b/chrome/browser/ui/views/profiles/profile_menu_view.cc
@@ -554,8 +554,6 @@ ProfileMenuViewBase::IdentitySectionParams ProfileMenuView::GetIdentitySectionParams(const ProfileAttributesEntry& entry) { - CHECK(switches::IsImprovedSigninUIOnDesktopEnabled()); - Profile* profile = browser()->profile(); const std::optional<AvatarSyncErrorType> error = GetAvatarSyncErrorType(profile);
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_ui_browsertest.cc b/chrome/browser/ui/views/profiles/profile_menu_view_ui_browsertest.cc index bc629b5..0949b36 100644 --- a/chrome/browser/ui/views/profiles/profile_menu_view_ui_browsertest.cc +++ b/chrome/browser/ui/views/profiles/profile_menu_view_ui_browsertest.cc
@@ -106,26 +106,22 @@ .pixel_test_param = {.test_suffix = "SignedOut_MultipleProfiles_Improved"}, .use_multiple_profiles = true, - .extra_features_state_ = {{kOutlineSilhouetteIcon, true}}, }, { .pixel_test_param = {.test_suffix = "SignedOut_MultipleProfiles_DarkTheme_Improved", .use_dark_theme = true}, .use_multiple_profiles = true, - .extra_features_state_ = {{kOutlineSilhouetteIcon, true}}, }, { .pixel_test_param = {.test_suffix = "WebSignedIn_Improved"}, .signin_status = SigninStatusPixelTestParam::kWebSignedIn, - .extra_features_state_ = {{kOutlineSilhouetteIcon, true}}, }, { .pixel_test_param = {.test_suffix = "WebSignedIn_PlaceholderIcon_Improved"}, .signin_status = SigninStatusPixelTestParam::kWebSignedIn, .account_image_available = false, - .extra_features_state_ = {{kOutlineSilhouetteIcon, true}}, }, { .pixel_test_param = @@ -133,14 +129,12 @@ .use_dark_theme = true}, .signin_status = SigninStatusPixelTestParam::kWebSignedIn, .account_image_available = false, - .extra_features_state_ = {{kOutlineSilhouetteIcon, true}}, }, { .pixel_test_param = {.test_suffix = "SignedIn_MultipleProfiles_Improved"}, .signin_status = SigninStatusPixelTestParam::kSignedInNoSync, .use_multiple_profiles = true, - .extra_features_state_ = {{kOutlineSilhouetteIcon, true}}, }, { .pixel_test_param = {.test_suffix = @@ -148,15 +142,16 @@ .use_dark_theme = true}, .signin_status = SigninStatusPixelTestParam::kSignedInNoSync, .use_multiple_profiles = true, - .extra_features_state_ = {{kOutlineSilhouetteIcon, true}}, }, { .pixel_test_param = {.test_suffix = "SignedIn_Sync_Improved"}, .signin_status = SigninStatusPixelTestParam::kSignedInWithSync, }, - {.pixel_test_param = {.test_suffix = "SignedIn_SyncPaused_Improved", - .use_dark_theme = true}, - .signin_status = SigninStatusPixelTestParam::kSignedInSyncPaused}, + { + .pixel_test_param = {.test_suffix = "SignedIn_SyncPaused_Improved", + .use_dark_theme = true}, + .signin_status = SigninStatusPixelTestParam::kSignedInSyncPaused, + }, { .pixel_test_param = {.test_suffix = "SignInPending_Improved"}, .signin_status = SigninStatusPixelTestParam::kSignInPendingNoSync, @@ -176,7 +171,6 @@ .use_dark_theme = true}, .signin_status = SigninStatusPixelTestParam::kSignedOut, .management_status = ManagementStatus::kBrowserManaged, - .extra_features_state_ = {{kOutlineSilhouetteIcon, true}}, }, { .pixel_test_param = @@ -242,8 +236,6 @@ ProfileMenuViewPixelTest() : ProfilesPixelTestBaseT<DialogBrowserTest>(GetParam().pixel_test_param) { base::flat_map<base::test::FeatureRef, bool> features_state = { - // False by default but may be overridden by `extra_features_state_`. - {kOutlineSilhouetteIcon, false}, {features::kEnterpriseProfileBadgingForMenu, true}, {features::kEnterpriseProfileBadgingPolicies, true}, // False by default but may be overridden by `extra_features_state_`.
diff --git a/chrome/browser/ui/views/profiles/profile_picker_ui_browsertest.cc b/chrome/browser/ui/views/profiles/profile_picker_ui_browsertest.cc index 5f57fa7..baba897 100644 --- a/chrome/browser/ui/views/profiles/profile_picker_ui_browsertest.cc +++ b/chrome/browser/ui/views/profiles/profile_picker_ui_browsertest.cc
@@ -43,8 +43,6 @@ // Requires `use_multiple_profiles` to be enabled. bool has_supervised_user = false; bool show_kite_for_supervised_users = false; - // param to be removed when `kOutlineSilhouetteIcon` is enabled by default. - bool outline_silhouette_icon = false; bool disallow_profile_creation = false; bool use_glic_version = false; bool no_glic_eligible_profiles = false; @@ -63,8 +61,10 @@ // Permutations of supported parameters. const ProfilePickerTestParam kTestParams[] = { {.pixel_test_param = {.test_suffix = "Regular"}}, - {.pixel_test_param = {.test_suffix = "MultipleProfiles"}, - .use_multiple_profiles = true}, + { + .pixel_test_param = {.test_suffix = "MultipleProfiles"}, + .use_multiple_profiles = true, + }, {.pixel_test_param = {.test_suffix = "PortraitModeWindow", .window_size = PixelTestParam::kPortraitModeWindowSize}}, @@ -78,21 +78,13 @@ {.pixel_test_param = {.test_suffix = "MultipleProfilesNoProfileCreation"}, .use_multiple_profiles = true, .disallow_profile_creation = true}, - {.pixel_test_param = {.test_suffix = "MultipleProfiles_OutlineSilhouette"}, - .use_multiple_profiles = true, - .outline_silhouette_icon = true}, - {.pixel_test_param = {.test_suffix = "DarkRtlSmallMultipleProfiles", - .use_dark_theme = true, - .use_right_to_left_language = true, - .window_size = PixelTestParam::kSmallWindowSize}, - .use_multiple_profiles = true}, - {.pixel_test_param = {.test_suffix = - "DarkRtlSmallMultipleProfiles_OutlineSilhouette", - .use_dark_theme = true, - .use_right_to_left_language = true, - .window_size = PixelTestParam::kSmallWindowSize}, - .use_multiple_profiles = true, - .outline_silhouette_icon = true}, + { + .pixel_test_param = {.test_suffix = "DarkRtlSmallMultipleProfiles", + .use_dark_theme = true, + .use_right_to_left_language = true, + .window_size = PixelTestParam::kSmallWindowSize}, + .use_multiple_profiles = true, + }, #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) {.pixel_test_param = {.test_suffix = "MultipleProfiles_Kite"}, .use_multiple_profiles = true, @@ -240,7 +232,6 @@ scoped_feature_list_.InitWithFeatureStates( {{supervised_user::kShowKiteForSupervisedUsers, GetParam().show_kite_for_supervised_users}, - {kOutlineSilhouetteIcon, GetParam().outline_silhouette_icon}, {features::kEnterpriseProfileBadgingForAvatar, GetParam().is_enterprise_badging_enabled}}); #endif
diff --git a/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.cc b/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.cc index 8d329f5..e395ab0e 100644 --- a/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.cc +++ b/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.cc
@@ -159,11 +159,7 @@ bool has_more_than_one_parent = parent_permission_email_addresses.size() > 1; - if (is_extension_permission_dialog && - supervised_user:: - IsSupervisedUserSkipParentApprovalToInstallExtensionsEnabled() && - base::FeatureList::IsEnabled( - supervised_user::kUpdatedSupervisedUserExtensionApprovalStrings)) { + if (is_extension_permission_dialog) { AddExtensionParentPermissionLabels( view.get(), is_extension_permission_dialog, child_name); } else { @@ -296,11 +292,7 @@ void AddExtensionParentPermissionLabels(views::View* view, bool is_extension_permission_dialog, const std::string& child_name) { - CHECK(is_extension_permission_dialog && - supervised_user:: - IsSupervisedUserSkipParentApprovalToInstallExtensionsEnabled() && - base::FeatureList::IsEnabled( - supervised_user::kUpdatedSupervisedUserExtensionApprovalStrings)); + CHECK(is_extension_permission_dialog); auto parent_account_label = std::make_unique<views::Label>( l10n_util::GetStringUTF16(
diff --git a/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view_browsertest.cc b/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view_browsertest.cc index 39679120..f540b0a 100644 --- a/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view_browsertest.cc +++ b/chrome/browser/ui/views/supervised_user/parent_permission_dialog_view_browsertest.cc
@@ -22,7 +22,6 @@ #include "base/test/gtest_util.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/metrics/user_action_tester.h" -#include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "chrome/browser/extensions/chrome_test_extension_loader.h" #include "chrome/browser/extensions/extension_service.h" @@ -44,7 +43,6 @@ #include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/identity_test_environment.h" #include "components/supervised_user/core/browser/supervised_user_service.h" -#include "components/supervised_user/core/common/features.h" #include "components/supervised_user/core/common/supervised_user_constants.h" #include "content/public/test/browser_test.h" #include "content/public/test/test_launcher.h" @@ -588,66 +586,24 @@ ActionStatus::kWasPerformed))); } -enum class ExtensionsManagingToggle : int { - /* Extensions are managed by the dedicated - "Skip parent approval to install extensions" FL button. */ - kExtensions = 0, - /* Extensions are managed by the - "Permissions for sites, apps and extensions" FL button. */ - kPermissions = 1 -}; - // Test which labels are used in the Parent Permission Input Section // of the permission dialog based on the usage of the dialog // (extension approval, other approval). class ParentPermissionInputSectionLabelTest - : public ParentPermissionDialogViewTest, - public ::testing::WithParamInterface<ExtensionsManagingToggle> { - public: - ParentPermissionInputSectionLabelTest() { - std::vector<base::test::FeatureRef> enabled_features; - std::vector<base::test::FeatureRef> disabled_features; - if (GetParam() == ExtensionsManagingToggle::kExtensions) { - enabled_features.push_back( - supervised_user:: - kEnableSupervisedUserSkipParentApprovalToInstallExtensions); - enabled_features.push_back( - supervised_user::kUpdatedSupervisedUserExtensionApprovalStrings); - } else { - disabled_features.push_back( - supervised_user:: - kEnableSupervisedUserSkipParentApprovalToInstallExtensions); - } - enabled_features.push_back( - supervised_user:: - kEnableExtensionsPermissionsForSupervisedUsersOnDesktop); - scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features); - } + : public ParentPermissionDialogViewTest {}; - private: - base::test::ScopedFeatureList scoped_feature_list_; -}; - -IN_PROC_BROWSER_TEST_P(ParentPermissionInputSectionLabelTest, +IN_PROC_BROWSER_TEST_F(ParentPermissionInputSectionLabelTest, PermissionReceived_extension) { supervision_mixin_.SetNextReAuthStatus( GaiaAuthConsumer::ReAuthProofTokenStatus::kSuccess); - // When the feature - // `kEnableSupervisedUserSkipParentApprovalToInstallExtensions` - // is enabled and the parent approval dialog is shown for an extensions + // When the parent approval dialog is shown for an extension // approval, extension-specific labels are shown to the Permission Input // section of the dialog. Otherwise, general purpose labels are used. - auto present_parent_label_id = - GetParam() == ExtensionsManagingToggle::kExtensions - ? ParentPermissionDialog:: - kExtensionsParentApprovalVerificationTextIdForTesting - : ParentPermissionDialog::kParentAccountTextIdForTesting; + auto present_parent_label_id = ParentPermissionDialog:: + kExtensionsParentApprovalVerificationTextIdForTesting; auto non_present_parent_label_id = - GetParam() == ExtensionsManagingToggle::kExtensions - ? ParentPermissionDialog::kParentAccountTextIdForTesting - : ParentPermissionDialog:: - kExtensionsParentApprovalVerificationTextIdForTesting; + ParentPermissionDialog::kParentAccountTextIdForTesting; RunTestSequence( InAnyContext(ShowDialog(), @@ -656,7 +612,7 @@ EnsureNotPresent(non_present_parent_label_id))); } -IN_PROC_BROWSER_TEST_P(ParentPermissionInputSectionLabelTest, +IN_PROC_BROWSER_TEST_F(ParentPermissionInputSectionLabelTest, PermissionReceived_default) { supervision_mixin_.SetNextReAuthStatus( GaiaAuthConsumer::ReAuthProofTokenStatus::kSuccess); @@ -672,11 +628,4 @@ ParentPermissionDialog:: kExtensionsParentApprovalVerificationTextIdForTesting))); } - -INSTANTIATE_TEST_SUITE_P( - All, - ParentPermissionInputSectionLabelTest, - testing::Values(ExtensionsManagingToggle::kExtensions, - ExtensionsManagingToggle::kPermissions)); - } // namespace
diff --git a/chrome/browser/ui/views/supervised_user/parent_permission_extension_flow_view_browsertest.cc b/chrome/browser/ui/views/supervised_user/parent_permission_extension_flow_view_browsertest.cc index be8723fd..4afa3be 100644 --- a/chrome/browser/ui/views/supervised_user/parent_permission_extension_flow_view_browsertest.cc +++ b/chrome/browser/ui/views/supervised_user/parent_permission_extension_flow_view_browsertest.cc
@@ -12,7 +12,6 @@ #include "base/memory/scoped_refptr.h" #include "base/path_service.h" #include "base/test/metrics/histogram_tester.h" -#include "base/test/scoped_feature_list.h" #include "chrome/browser/extensions/chrome_test_extension_loader.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_util.h" @@ -36,7 +35,6 @@ #include "chrome/test/supervised_user/supervision_mixin.h" #include "components/signin/public/identity_manager/identity_test_environment.h" #include "components/supervised_user/core/browser/supervised_user_service.h" -#include "components/supervised_user/core/common/features.h" #include "content/public/test/browser_test.h" #include "content/public/test/test_launcher.h" #include "content/public/test/test_utils.h" @@ -64,20 +62,13 @@ }; ExtensionEnableFlowTestSupervised() - : TestParentPermissionDialogViewObserver(this) { - feature_list_.InitWithFeatures( - // Enable extensions for supervised users in Desktop platforms. - /*enabled_features=*/ - {supervised_user:: - kEnableExtensionsPermissionsForSupervisedUsersOnDesktop}, - /*disabled_features=*/{}); - } + : TestParentPermissionDialogViewObserver(this) {} ExtensionEnableFlowTestSupervised(const ExtensionEnableFlowTestSupervised&) = delete; ExtensionEnableFlowTestSupervised& operator=( const ExtensionEnableFlowTestSupervised&) = delete; - ~ExtensionEnableFlowTestSupervised() override { feature_list_.Reset(); } + ~ExtensionEnableFlowTestSupervised() override = default; void OnParentPermissionDialogDone(ParentPermissionDialog::Result result) { result_ = result; @@ -166,7 +157,6 @@ supervised_user_extensions_delegate_; private: - base::test::ScopedFeatureList feature_list_; raw_ptr<ParentPermissionDialogView, DanglingUntriaged> view_ = nullptr; std::unique_ptr<ParentPermissionDialog> parent_permission_dialog_; ParentPermissionDialog::Result result_;
diff --git a/chrome/browser/ui/webui/settings/sync_settings_interactive_uitest.cc b/chrome/browser/ui/webui/settings/sync_settings_interactive_uitest.cc index 2cd8d88..62e6d68 100644 --- a/chrome/browser/ui/webui/settings/sync_settings_interactive_uitest.cc +++ b/chrome/browser/ui/webui/settings/sync_settings_interactive_uitest.cc
@@ -57,11 +57,9 @@ "cr-icon-button#dropdown-arrow"}; std::unique_ptr<content::TestNavigationObserver> observer; - if (switches::IsImprovedSigninUIOnDesktopEnabled()) { - auto url = GURL(chrome::kChromeUISignoutConfirmationURL); - observer = std::make_unique<content::TestNavigationObserver>(url); - observer->StartWatchingNewWebContents(); - } + auto url = GURL(chrome::kChromeUISignoutConfirmationURL); + observer = std::make_unique<content::TestNavigationObserver>(url); + observer->StartWatchingNewWebContents(); RunTestSequence( Do([&]() {
diff --git a/chrome/browser/webdata_services/web_data_service_factory.cc b/chrome/browser/webdata_services/web_data_service_factory.cc index 7995df9..3993796 100644 --- a/chrome/browser/webdata_services/web_data_service_factory.cc +++ b/chrome/browser/webdata_services/web_data_service_factory.cc
@@ -19,7 +19,6 @@ #include "components/prefs/pref_service.h" #include "components/search_engines/keyword_web_data_service.h" #include "components/signin/public/base/signin_pref_names.h" -#include "components/signin/public/base/signin_switches.h" #include "components/signin/public/webdata/token_web_data.h" #include "components/webdata_services/web_data_service_wrapper.h" #include "content/public/browser/browser_task_traits.h" @@ -89,12 +88,6 @@ // enum that is useful for logging metrics. AutofillAccountStorageResult DetermineAutofillAccountStorage( PrefService* pref_service) { - // Historically, and before the flag rollout represented by the predicate - // below, desktop platforms have used an in-memory database for autofill - // account data. - if (!switches::IsImprovedSigninUIOnDesktopEnabled()) { - return AutofillAccountStorageResult::kInMemory_FlagDisabled; - } CHECK(pref_service); // The interpretation of the pref mimics what PrimaryAccountManager's // constructor does.
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt index b448b8b7c..e74fef0 100644 --- a/chrome/build/android-arm32.pgo.txt +++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@ -chrome-android32-main-1745819836-1eff79335ff2aefcbcbf88c39e006a2f177b20f3-8f066f0ecba490e7266e6839fbf59726d523346f.profdata +chrome-android32-main-1745841538-85aa7a93307c61d723c76c4a91b6381c7531054d-46e76664f91ef18545821ed5fd3de5263be89eae.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt index 81efe37..c8e5e74 100644 --- a/chrome/build/android-arm64.pgo.txt +++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@ -chrome-android64-main-1745824215-6f00ee1c9d84298c454a2f491255291017306c30-f4206dbe52e481fc40ae45d0b2e622594e047900.profdata +chrome-android64-main-1745846568-599551c3d9c9f53e3ede384315e4dd065165f998-806b04bf9637911ab1f6bdf569140e5ecdf7ad04.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index b8f962a..9ed4df4 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-main-1745819836-c74832881a0761337c352307cd2bab6dc45f1ae9-8f066f0ecba490e7266e6839fbf59726d523346f.profdata +chrome-linux-main-1745841538-2e446adacb46a6fe51004caa900ed991881991ba-46e76664f91ef18545821ed5fd3de5263be89eae.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index ce104ce..1683c02d 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1745826953-ba8b3ffe1b35a48c8006aa6d03f411678b171a49-cf5f46a8559c5beec7f8fc97d02a389e4438a6d4.profdata +chrome-mac-arm-main-1745848775-e3ee63a9fc2c6a5578070685da54b85493437ee1-1c9db8d05098aa4c022942f3c94ecf55045e8495.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index 4d9dd31..e980542 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-main-1745798367-ea89f1ca7e1ef153e4bcfb25d2267c3ac1e20c67-2e9bd4ca172ebbbcc5a2efa261d8c8c495cf4e5c.profdata +chrome-mac-main-1745841538-d6a43fccb2359268547d4cb098fc9126b4250fad-46e76664f91ef18545821ed5fd3de5263be89eae.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt index 5dfc400..29ce4c5 100644 --- a/chrome/build/win-arm64.pgo.txt +++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@ -chrome-win-arm64-main-1745798367-9a3357a0d3f769056374d413d21c4896a13fe583-2e9bd4ca172ebbbcc5a2efa261d8c8c495cf4e5c.profdata +chrome-win-arm64-main-1745841538-991d2008fe6a1e69cd9c573c68163cbf8fd98e94-46e76664f91ef18545821ed5fd3de5263be89eae.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index cd564f30..5c17ef70 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1745765509-1e4725ec77f765139a33b92f8ac425f899212988-f05e11c8088bdca5f7365a838557f39515226dd3.profdata +chrome-win64-main-1745819836-80d79badd8862243bb84e230d2afd037cf7013ab-8f066f0ecba490e7266e6839fbf59726d523346f.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index 6fc52b4..c99eda37 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -256,6 +256,13 @@ "DesktopPWAsTabStripSettings", base::FEATURE_DISABLED_BY_DEFAULT); +// Allows fullscreen to claim whole display area when in windowing mode +#if BUILDFLAG(IS_ANDROID) +BASE_FEATURE(kDisplayEdgeToEdgeFullscreen, + "DisplayEdgeToEdgeFullscreen", + base::FEATURE_ENABLED_BY_DEFAULT); +#endif + #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) // Controls whether Chrome Apps are supported. See https://crbug.com/1221251. // If the feature is disabled, Chrome Apps continue to work. If enabled, Chrome
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h index 7e07d92f..b08b712 100644 --- a/chrome/common/chrome_features.h +++ b/chrome/common/chrome_features.h
@@ -156,6 +156,11 @@ BASE_DECLARE_FEATURE(kShortcutsNotAppsRevealDesktop); #endif +#if BUILDFLAG(IS_ANDROID) +COMPONENT_EXPORT(CHROME_FEATURES) +BASE_DECLARE_FEATURE(kDisplayEdgeToEdgeFullscreen); +#endif + COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kDnsOverHttps); COMPONENT_EXPORT(CHROME_FEATURES) extern const base::FeatureParam<std::string> kDnsOverHttpsTemplatesParam;
diff --git a/chrome/release_scripts b/chrome/release_scripts index 51e35b4b..b21f087 160000 --- a/chrome/release_scripts +++ b/chrome/release_scripts
@@ -1 +1 @@ -Subproject commit 51e35b4bafb095a86c2108573a250e6fee9625a5 +Subproject commit b21f087218529baaa951d83544ec5d08cd54b3b8
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherSearchStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherSearchStation.java index af30dea..03423c6 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherSearchStation.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherSearchStation.java
@@ -11,7 +11,6 @@ import static androidx.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.Matchers.allOf; import static org.chromium.base.test.transit.ViewSpec.viewSpec; @@ -26,7 +25,6 @@ import org.chromium.base.test.transit.ViewElement; import org.chromium.base.test.transit.ViewSpec; import org.chromium.chrome.browser.omnibox.UrlBar; -import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionView; import org.chromium.chrome.browser.searchwidget.SearchActivity; import org.chromium.chrome.test.R; import org.chromium.chrome.test.util.OmniboxTestUtils; @@ -81,7 +79,7 @@ .descendant( allOf( withParentIndex(index), - withClassName(equalTo(BaseSuggestionView.class.getName())), + withClassName(containsString("BaseSuggestionView")), hasDescendant( allOf( withId(R.id.line_1),
diff --git a/chrome/test/data/extensions/api_test/user_scripts/migration_tests/extension_with_userscript_permission/manifest.json b/chrome/test/data/extensions/api_test/user_scripts/migration_tests/extension_with_userscript_permission/manifest.json new file mode 100644 index 0000000..384439d --- /dev/null +++ b/chrome/test/data/extensions/api_test/user_scripts/migration_tests/extension_with_userscript_permission/manifest.json
@@ -0,0 +1,6 @@ +{ + "name": "extension_with_userscript_permission", + "manifest_version": 3, + "version": "0.1", + "permissions": ["userScripts"] +}
diff --git a/chrome/test/data/extensions/api_test/user_scripts/migration_tests/extension_without_userscript_permission/manifest.json b/chrome/test/data/extensions/api_test/user_scripts/migration_tests/extension_without_userscript_permission/manifest.json new file mode 100644 index 0000000..2211cf1 --- /dev/null +++ b/chrome/test/data/extensions/api_test/user_scripts/migration_tests/extension_without_userscript_permission/manifest.json
@@ -0,0 +1,5 @@ +{ + "name": "extension_without_userscript_permission", + "manifest_version": 3, + "version": "0.1" +}
diff --git a/chrome/test/data/webui/chromeos/settings/os_printing_page/cups_printer_landing_page_test.ts b/chrome/test/data/webui/chromeos/settings/os_printing_page/cups_printer_landing_page_test.ts index fe6fd0d..a8ef8d0 100644 --- a/chrome/test/data/webui/chromeos/settings/os_printing_page/cups_printer_landing_page_test.ts +++ b/chrome/test/data/webui/chromeos/settings/os_printing_page/cups_printer_landing_page_test.ts
@@ -1679,6 +1679,15 @@ assertTrue(!!printerPPDManufacturer); assertTrue(printerPPDManufacturer.readonly); + // The "specify PDD" section should be hidden. + const browseButton = + editDialog.shadowRoot!.querySelector<HTMLButtonElement>( + '.browse-button'); + assertTrue(!!browseButton); + const parentElement = browseButton.parentElement; + assertTrue(!!parentElement); + assertTrue(parentElement.hidden); + // View printer PPD button should be visible. Help text should be hidden. const ppdLabel = editDialog.shadowRoot!.querySelector<HTMLElement>('#ppdLabel'); @@ -1693,27 +1702,6 @@ assertTrue(!!localizedLink); assertTrue(localizedLink.hidden); - // PPD textbox should be hidden. Browse button should be hidden. - const browseFileInput = - editDialog.shadowRoot!.querySelector<HTMLTextAreaElement>( - '.browse-file-input'); - assertTrue(!!browseFileInput); - assertTrue(browseFileInput.hidden); - const browseButton = - editDialog.shadowRoot!.querySelector<HTMLButtonElement>( - '.browse-button'); - assertTrue(!!browseButton); - assertTrue(browseButton.hidden); - const parentElement = browseButton.parentElement; - assertTrue(!!parentElement); - assertFalse(parentElement.hidden); - - // PPD textbox should be visible when userPPD isn't empty. - editDialog.set('userPPD_', 'custom-ppd-path'); - flush(); - assertTrue(!!browseFileInput); - assertFalse(browseFileInput.hidden); - // Save and Cancel buttons should be hidden. Close button should be // visible. const cancelButton =
diff --git a/chrome/test/data/webui/glic/test_client/index.html b/chrome/test/data/webui/glic/test_client/index.html index 2b6d9176..24b11d7 100644 --- a/chrome/test/data/webui/glic/test_client/index.html +++ b/chrome/test/data/webui/glic/test_client/index.html
@@ -430,6 +430,10 @@ <div><label>Search Range Start Node</label><select disabled id="scrollToTextFragmentSearchStart" class="scrollToNodeSelect"></select></div> </div> + <div class="scrollToSelector"> + <h2>Node Selector</h2> + <div><label>Node</label><select disabled id="scrollToNode" class="scrollToNodeSelect"></select></div> + </div> <button id="scrollToBn" style="margin-top: 12px;">Scroll!</button> </div> <div class="section">
diff --git a/chrome/test/data/webui/glic/test_client/page_element_types.ts b/chrome/test/data/webui/glic/test_client/page_element_types.ts index 24c931eb..e33e3da6 100644 --- a/chrome/test/data/webui/glic/test_client/page_element_types.ts +++ b/chrome/test/data/webui/glic/test_client/page_element_types.ts
@@ -76,6 +76,7 @@ scrollToTextFragmentTextStart: HTMLInputElement; scrollToTextFragmentTextEnd: HTMLInputElement; scrollToTextFragmentSearchStart: HTMLSelectElement; + scrollToNode: HTMLSelectElement; scrollToBn: HTMLButtonElement; fileDrop: HTMLDivElement; fileDropList: HTMLDivElement;
diff --git a/chrome/test/data/webui/glic/test_client/sections/scroll_to.ts b/chrome/test/data/webui/glic/test_client/sections/scroll_to.ts index da24a374..fa2bfce8 100644 --- a/chrome/test/data/webui/glic/test_client/sections/scroll_to.ts +++ b/chrome/test/data/webui/glic/test_client/sections/scroll_to.ts
@@ -57,8 +57,7 @@ logMessage('scrollTo succeeded!'); } -function getSearchRangeStartNodeId(selectElement: HTMLSelectElement): number| - undefined { +function getNodeId(selectElement: HTMLSelectElement): number|undefined { let searchRangeStartNodeId: number|undefined = undefined; if (!selectElement.disabled) { searchRangeStartNodeId = parseInt(selectElement.value) || undefined; @@ -94,7 +93,7 @@ for (const selectElement of [$.scrollToExactTextSearchStart, - $.scrollToTextFragmentSearchStart]) { + $.scrollToTextFragmentSearchStart, $.scrollToNode]) { selectElement.innerHTML = ''; selectElement.disabled = false; @@ -128,8 +127,7 @@ try { const exactText = $.scrollToExactText.value; if (exactText) { - const searchRangeStartNodeId = - getSearchRangeStartNodeId($.scrollToExactTextSearchStart); + const searchRangeStartNodeId = getNodeId($.scrollToExactTextSearchStart); await scrollTo({exactText: {text: exactText, searchRangeStartNodeId}}); return; } @@ -138,12 +136,18 @@ const textEnd = $.scrollToTextFragmentTextEnd.value; if (textStart && textEnd) { const searchRangeStartNodeId = - getSearchRangeStartNodeId($.scrollToTextFragmentSearchStart); + getNodeId($.scrollToTextFragmentSearchStart); await scrollTo( {textFragment: {textStart, textEnd, searchRangeStartNodeId}}); return; } + const nodeId = getNodeId($.scrollToNode); + if (nodeId) { + await scrollTo({node: {nodeId}}); + return; + } + logMessage('scrollTo: no selector specified'); } catch (error) { logMessage(`scrollTo failed: ${error}`);
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index e54eb59c..c0ee797 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@ -16266.0.0-1068489 \ No newline at end of file +16266.0.0-1068494 \ No newline at end of file
diff --git a/chromeos/crosapi/mojom/chrome_app_kiosk_service.mojom b/chromeos/crosapi/mojom/chrome_app_kiosk_service.mojom index 85e118f6c..a38e010 100644 --- a/chromeos/crosapi/mojom/chrome_app_kiosk_service.mojom +++ b/chromeos/crosapi/mojom/chrome_app_kiosk_service.mojom
@@ -44,6 +44,8 @@ kUnableToLaunch = 2, // The primary app is not offline enabled, but network is not ready kNetworkMissing = 3, + // The primary app is a deprecated Chrome App. + kChromeAppDeprecated = 4, }; // A client implemented in lacros-chrome for the DownloadController which is
diff --git a/clank b/clank index d1eb01c..3138045 160000 --- a/clank +++ b/clank
@@ -1 +1 @@ -Subproject commit d1eb01c07fa71d57ecd25f9d1bbe604bcff5fbc4 +Subproject commit 3138045236e6f126b9de2569f8f9132a1f399fed
diff --git a/components/BUILD.gn b/components/BUILD.gn index a180d8a..ab52bf0 100644 --- a/components/BUILD.gn +++ b/components/BUILD.gn
@@ -645,6 +645,7 @@ "//components/component_updater/android:embedded_component_loader_unittests", "//components/crash/android:java", "//components/crash/android:unit_tests", + "//components/credential_management:unit_tests", "//components/credential_management/android:unit_tests", "//components/data_sharing/internal:internal_java", "//components/data_sharing/internal:native_test_helper_java",
diff --git a/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.cc b/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.cc index 39329aa..9b949d77 100644 --- a/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.cc +++ b/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.cc
@@ -403,8 +403,7 @@ for (const std::u16string& format_string : it->second.format_strings) { DCHECK(data_util::IsValidDateFormat(format_string)); auto* added_format_string = added_field->add_format_string(); - added_format_string->set_type( - AutofillUploadContents_Field_FormatString_Type_DATE); + added_format_string->set_type(FormatString_Type_DATE); added_format_string->set_format_string( base::UTF16ToUTF8(format_string)); }
diff --git a/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding_unittest.cc b/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding_unittest.cc index 9383ce6..9a900893 100644 --- a/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding_unittest.cc +++ b/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding_unittest.cc
@@ -447,14 +447,12 @@ *form_structure->field(1)->GetFieldSignature()); { auto* added_format_string = upload_date_field->add_format_string(); - added_format_string->set_type( - AutofillUploadContents_Field_FormatString_Type_DATE); + added_format_string->set_type(FormatString_Type_DATE); added_format_string->set_format_string("DD/MM/YYYY"); } { auto* added_format_string = upload_date_field->add_format_string(); - added_format_string->set_type( - AutofillUploadContents_Field_FormatString_Type_DATE); + added_format_string->set_type(FormatString_Type_DATE); added_format_string->set_format_string("MM/DD/YYYY"); } }
diff --git a/components/autofill/core/browser/proto/server.proto b/components/autofill/core/browser/proto/server.proto index b9d0fcba..8149667e 100644 --- a/components/autofill/core/browser/proto/server.proto +++ b/components/autofill/core/browser/proto/server.proto
@@ -196,6 +196,17 @@ repeated AutofillRandomizedSelectOption select_option = 13; } +// A format string describes a value pattern. +message FormatString { + enum Type { + // The format string represents a date as documented + // autofill::data_util::IsValidDateFormat(). + DATE = 0; + } + optional Type type = 49; + optional string format_string = 50; +} + // This message contains information about the field types in a single form. // It is sent by the toolbar to contribute to the field type statistics. // Next available id: 49 @@ -355,15 +366,7 @@ // wasn't changed. Not set if the field didn't have a pre-filled value. optional bool initial_value_changed = 47; - // List of format strings which the field's value matches. - message FormatString { - enum Type { - // The format string represents a date. - DATE = 0; - } - optional Type type = 49; - optional string format_string = 50; - } + // List of format strings which match the field's value. repeated FormatString format_string = 51; } repeated Field field_data = 48;
diff --git a/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetController.java b/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetController.java index da3f2ac..3c9147a 100644 --- a/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetController.java +++ b/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetController.java
@@ -108,7 +108,7 @@ void hideContent( BottomSheetContent content, boolean animate, @StateChangeReason int hideReason); - void hideContent(BottomSheetContent content, boolean animate); + void hideContent(@Nullable BottomSheetContent content, boolean animate); /** @param observer The observer to add. */ void addObserver(BottomSheetObserver observer);
diff --git a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/BaseNotificationManagerProxy.java b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/BaseNotificationManagerProxy.java index 055d62a..257c9e6 100644 --- a/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/BaseNotificationManagerProxy.java +++ b/components/browser_ui/notifications/android/java/src/org/chromium/components/browser_ui/notifications/BaseNotificationManagerProxy.java
@@ -20,7 +20,7 @@ * Base interface for NofificationManagerProxy that only supports simple functionalities. Remove * this once AsyncNofificationManagerProxy is set to default. */ -@MockedInTests // Needed due to R8's computeDelayedInterfaceMethodSyntheticBridges. b/147584922 +@MockedInTests @NullMarked public interface BaseNotificationManagerProxy { /**
diff --git a/components/browser_ui/strings/android/browser_ui_strings.grd b/components/browser_ui/strings/android/browser_ui_strings.grd index 9332440..fdb35cbe 100644 --- a/components/browser_ui/strings/android/browser_ui_strings.grd +++ b/components/browser_ui/strings/android/browser_ui_strings.grd
@@ -938,6 +938,9 @@ <message name="IDS_IMMERSIVE_FULLSCREEN_GESTURE_NAVIGATION_MODE_API_NOTIFICATION" desc="Notification message when a site has entered immersive fullscreen and the directions of how to exit when the system gesture navigation mode is on."> Drag from top and swipe from the left or right edge to exit full screen. </message> + <message name="IDS_IMMERSIVE_FULLSCREEN_API_NOTIFICATION_DESKTOP" desc="Notification message when a site has entered immersive fullscreen and the directions of how to exit when is on desktop."> + Drag from top or press Esc key to exit full screen. + </message> <message name="IDS_IMMERSIVE_FULLSCREEN_AUTOMOTIVE_TOOLBAR_IMPROVEMENTS" desc="Notification message when a site has entered immersive fullscreen and the directions of how to exit by swiping left."> Drag from the side of the screen to exit full screen. </message>
diff --git a/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_IMMERSIVE_FULLSCREEN_API_NOTIFICATION_DESKTOP.png.sha1 b/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_IMMERSIVE_FULLSCREEN_API_NOTIFICATION_DESKTOP.png.sha1 new file mode 100644 index 0000000..5cd75ed --- /dev/null +++ b/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_IMMERSIVE_FULLSCREEN_API_NOTIFICATION_DESKTOP.png.sha1
@@ -0,0 +1 @@ +fe9e42c0704e434ab1c5dede75018cac67400211 \ No newline at end of file
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json index cd39fa1..61ad2447 100644 --- a/components/certificate_transparency/data/log_list.json +++ b/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@ { - "version": "53.26", - "log_list_timestamp": "2025-04-27T12:54:16Z", + "version": "53.27", + "log_list_timestamp": "2025-04-28T12:54:03Z", "operators": [ { "name": "Google",
diff --git a/components/credential_management/DEPS b/components/credential_management/DEPS index 92fdb457..4a2ad539 100644 --- a/components/credential_management/DEPS +++ b/components/credential_management/DEPS
@@ -3,4 +3,5 @@ "+third_party/blink/public/mojom", "+components/password_manager/core/common/credential_manager_types.h", "+components/password_manager/core/browser/credential_manager_impl.h", + "+third_party/jni_zero/jni_zero.h", ]
diff --git a/components/credential_management/android/BUILD.gn b/components/credential_management/android/BUILD.gn index 2f377e4..5fa7090 100644 --- a/components/credential_management/android/BUILD.gn +++ b/components/credential_management/android/BUILD.gn
@@ -17,7 +17,10 @@ generate_jni("jni_headers") { visibility = [ ":*" ] - sources = [ "java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridge.java" ] + sources = [ + "java/src/org/chromium/components/credential_management/PasswordCredentialResponse.java", + "java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridge.java", + ] } android_library("java") { @@ -30,7 +33,10 @@ ] srcjar_deps = [ ":jni_headers" ] - sources = [ "java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridge.java" ] + sources = [ + "java/src/org/chromium/components/credential_management/PasswordCredentialResponse.java", + "java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridge.java", + ] resources_package = "org.chromium.components.credential_management" } @@ -48,6 +54,15 @@ "//content/public/browser", "//content/public/common", ] + + if (is_android) { + sources += [ + "password_credential_response.cc", + "password_credential_response.h", + ] + + deps += [ "//third_party/jni_zero" ] + } } source_set("unit_tests") {
diff --git a/components/credential_management/android/java/src/org/chromium/components/credential_management/PasswordCredentialResponse.java b/components/credential_management/android/java/src/org/chromium/components/credential_management/PasswordCredentialResponse.java new file mode 100644 index 0000000..9507def --- /dev/null +++ b/components/credential_management/android/java/src/org/chromium/components/credential_management/PasswordCredentialResponse.java
@@ -0,0 +1,57 @@ +// Copyright 2025 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.credential_management; + +import org.jni_zero.CalledByNative; +import org.jni_zero.JNINamespace; +import org.jni_zero.JniType; + +import org.chromium.build.annotations.NullMarked; + +/** + * A convenience class for conwaying the results of the Credential Management API get() call result + * to C++. + */ +@NullMarked +@JNINamespace("credential_management") +public class PasswordCredentialResponse { + private boolean mSuccess; + private String mUsername; + private String mPassword; + + @CalledByNative + public PasswordCredentialResponse( + @JniType("bool") boolean success, + @JniType("std::u16string") String username, + @JniType("std::u16string") String password) { + mSuccess = success; + mUsername = username; + mPassword = password; + } + + /** + * @return Whether the call to request the credential was successful. + */ + @CalledByNative + public @JniType("bool") boolean getSuccess() { + return mSuccess; + } + + /** + * @return The username. + */ + @CalledByNative + public @JniType("std::u16string") String getUsername() { + return mUsername; + } + + /** + * @return The password. + */ + @CalledByNative + public @JniType("std::u16string") String getPassword() { + return mPassword; + } +}
diff --git a/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridge.java b/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridge.java index 67cb1b1d..8fa4d27 100644 --- a/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridge.java +++ b/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridge.java
@@ -20,8 +20,8 @@ import org.jni_zero.CalledByNative; import org.jni_zero.JNINamespace; -import org.jni_zero.NativeMethods; +import org.chromium.base.Callback; import org.chromium.base.ContextUtils; import org.chromium.base.ResettersForTesting; import org.chromium.build.annotations.NullMarked; @@ -31,13 +31,10 @@ @JNINamespace("credential_management") @NullMarked class ThirdPartyCredentialManagerBridge { - private final long mReceiverBridge; private static @Nullable CredentialManager sCredentialManagerForTesting; @CalledByNative - ThirdPartyCredentialManagerBridge(long receiverBridge) { - mReceiverBridge = receiverBridge; - } + ThirdPartyCredentialManagerBridge() {} void setCredentialManagerForTesting(CredentialManager credentialManager) { sCredentialManagerForTesting = credentialManager; @@ -45,7 +42,7 @@ } @CalledByNative - void get(String origin) { + void get(String origin, Callback<PasswordCredentialResponse> callback) { Context context = ContextUtils.getApplicationContext(); CredentialManager credentialManager = sCredentialManagerForTesting == null @@ -63,12 +60,12 @@ new CredentialManagerCallback<>() { @Override public void onError(GetCredentialException e) { - onGetCredentialError(); + callback.onResult(new PasswordCredentialResponse(false, "", "")); } @Override public void onResult(GetCredentialResponse result) { - onGetCredentialResponse(result, origin); + onGetCredentialResponse(result, callback); } }; credentialManager.getCredentialAsync( @@ -76,7 +73,7 @@ } @CalledByNative - void store(String username, String password, String origin) { + void store(String username, String password, String origin, Callback<Boolean> callback) { Context context = ContextUtils.getApplicationContext(); CredentialManager credentialManager = sCredentialManagerForTesting == null @@ -90,50 +87,27 @@ new CredentialManagerCallback<>() { @Override public void onError(CreateCredentialException e) { - onCreateCredentialResponse(false); + callback.onResult(false); } @Override public void onResult(CreateCredentialResponse response) { - onCreateCredentialResponse(true); + callback.onResult(true); } }; credentialManager.createCredentialAsync( context, createPasswordRequest, null, Runnable::run, credentialCallback); } - private void onGetCredentialResponse(GetCredentialResponse result, String origin) { + private void onGetCredentialResponse( + GetCredentialResponse result, Callback<PasswordCredentialResponse> callback) { Credential credential = result.getCredential(); assert credential instanceof PasswordCredential; PasswordCredential passwordCredential = (PasswordCredential) credential; String username = passwordCredential.getId(); String password = passwordCredential.getPassword(); - if (username != null && password != null) { - ThirdPartyCredentialManagerBridgeJni.get() - .onPasswordCredentialReceived(mReceiverBridge, username, password, origin); - } - } - - private void onCreateCredentialResponse(boolean success) { - ThirdPartyCredentialManagerBridgeJni.get() - .onCreateCredentialResponse(mReceiverBridge, success); - } - - private void onGetCredentialError() { - ThirdPartyCredentialManagerBridgeJni.get().onGetPasswordCredentialError(mReceiverBridge); - } - - @NativeMethods - interface Natives { - void onPasswordCredentialReceived( - long nativeThirdPartyCredentialManagerBridge, - String username, - String password, - String origin); - - void onCreateCredentialResponse( - long nativeThirdPartyCredentialManagerBridge, boolean success); - - void onGetPasswordCredentialError(long nativeThirdPartyCredentialManagerBridge); + PasswordCredentialResponse response = + new PasswordCredentialResponse(true, username, password); + callback.onResult(response); } }
diff --git a/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridgeTest.java b/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridgeTest.java index f4c92e185..7bf2de1 100644 --- a/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridgeTest.java +++ b/components/credential_management/android/java/src/org/chromium/components/credential_management/ThirdPartyCredentialManagerBridgeTest.java
@@ -5,6 +5,7 @@ package org.chromium.components.credential_management; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.verify; @@ -25,11 +26,13 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import org.chromium.base.Callback; import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.Batch; @@ -40,23 +43,22 @@ @Batch(Batch.PER_CLASS) public class ThirdPartyCredentialManagerBridgeTest { - private static final long FAKE_RECEIVER_BRIDGE_POINTER = 7; private static final String USERNAME = "username"; private static final String PASSWORD = "password"; private static final String ORIGIN = "www.example.com"; @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); - @Mock private ThirdPartyCredentialManagerBridge.Natives mReceiverBridgeJniMock; @Mock private CredentialManager mCredentialManager; @Mock private GetCredentialException mGetCredentialException; @Mock private CreateCredentialException mCreateCredentialException; + @Mock private Callback<PasswordCredentialResponse> mCredentialResponseCallback; + @Mock private Callback<Boolean> mStoreCallback; private ThirdPartyCredentialManagerBridge mBridge; @Before public void setUp() { - ThirdPartyCredentialManagerBridgeJni.setInstanceForTesting(mReceiverBridgeJniMock); - mBridge = new ThirdPartyCredentialManagerBridge(FAKE_RECEIVER_BRIDGE_POINTER); + mBridge = new ThirdPartyCredentialManagerBridge(); mBridge.setCredentialManagerForTesting(mCredentialManager); } @@ -74,7 +76,7 @@ any(Executor.class), any(CredentialManagerCallback.class)); - mBridge.get(ORIGIN); + mBridge.get(ORIGIN, mCredentialResponseCallback); verify(mCredentialManager) .getCredentialAsync( @@ -83,9 +85,11 @@ any(), any(Executor.class), any(CredentialManagerCallback.class)); - verify(mReceiverBridgeJniMock) - .onPasswordCredentialReceived( - FAKE_RECEIVER_BRIDGE_POINTER, USERNAME, PASSWORD, ORIGIN); + verify(mCredentialResponseCallback) + .onResult( + argThat( + new PasswordCredentialResponseMatcher( + new PasswordCredentialResponse(true, USERNAME, PASSWORD)))); } @Test @@ -99,7 +103,7 @@ any(Executor.class), any(CredentialManagerCallback.class)); - mBridge.get(ORIGIN); + mBridge.get(ORIGIN, mCredentialResponseCallback); verify(mCredentialManager) .getCredentialAsync( @@ -108,7 +112,11 @@ any(), any(Executor.class), any(CredentialManagerCallback.class)); - verify(mReceiverBridgeJniMock).onGetPasswordCredentialError(FAKE_RECEIVER_BRIDGE_POINTER); + verify(mCredentialResponseCallback) + .onResult( + argThat( + new PasswordCredentialResponseMatcher( + new PasswordCredentialResponse(false, "", "")))); } @Test @@ -123,7 +131,7 @@ any(Executor.class), any(CredentialManagerCallback.class)); - mBridge.store(USERNAME, PASSWORD, ORIGIN); + mBridge.store(USERNAME, PASSWORD, ORIGIN, mStoreCallback); verify(mCredentialManager) .createCredentialAsync( @@ -132,8 +140,7 @@ any(), any(Executor.class), any(CredentialManagerCallback.class)); - verify(mReceiverBridgeJniMock) - .onCreateCredentialResponse(FAKE_RECEIVER_BRIDGE_POINTER, true); + verify(mStoreCallback).onResult(true); } @Test @@ -147,7 +154,7 @@ any(Executor.class), any(CredentialManagerCallback.class)); - mBridge.store(USERNAME, PASSWORD, ORIGIN); + mBridge.store(USERNAME, PASSWORD, ORIGIN, mStoreCallback); verify(mCredentialManager) .createCredentialAsync( @@ -156,8 +163,7 @@ any(), any(Executor.class), any(CredentialManagerCallback.class)); - verify(mReceiverBridgeJniMock) - .onCreateCredentialResponse(FAKE_RECEIVER_BRIDGE_POINTER, false); + verify(mStoreCallback).onResult(false); } private Object respondToGetCallback( @@ -199,4 +205,21 @@ }); return null; } + + static class PasswordCredentialResponseMatcher + implements ArgumentMatcher<PasswordCredentialResponse> { + private final PasswordCredentialResponse mExpected; + + public PasswordCredentialResponseMatcher(PasswordCredentialResponse expected) { + mExpected = expected; + } + + @Override + public boolean matches(PasswordCredentialResponse actual) { + return actual != null + && actual.getSuccess() == mExpected.getSuccess() + && actual.getUsername().equals(mExpected.getUsername()) + && actual.getPassword().equals(mExpected.getPassword()); + } + } }
diff --git a/components/credential_management/android/password_credential_response.cc b/components/credential_management/android/password_credential_response.cc new file mode 100644 index 0000000..5128732 --- /dev/null +++ b/components/credential_management/android/password_credential_response.cc
@@ -0,0 +1,41 @@ +// Copyright 2025 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/credential_management/android/password_credential_response.h" + +#include <string> +#include <utility> + +#include "base/android/jni_string.h" +#include "components/credential_management/android/jni_headers/PasswordCredentialResponse_jni.h" + +namespace credential_management { + +PasswordCredentialResponse::PasswordCredentialResponse(bool success, + std::u16string username, + std::u16string password) + : success(std::move(success)), + username(std::move(username)), + password(std::move(password)) {} + +jni_zero::ScopedJavaLocalRef<jobject> PasswordCredentialResponse::Create( + JNIEnv* env, + const PasswordCredentialResponse& credential) { + return Java_PasswordCredentialResponse_Constructor( + env, credential.success, credential.username, credential.password); +} + +PasswordCredentialResponse +PasswordCredentialResponse::FromJavaPasswordCredentialResponse( + JNIEnv* env, + const jni_zero::JavaRef<jobject>& response) { + return PasswordCredentialResponse( + Java_PasswordCredentialResponse_getSuccess(env, response), + Java_PasswordCredentialResponse_getUsername(env, response), + Java_PasswordCredentialResponse_getPassword(env, response)); +} + +} // namespace credential_management + +DEFINE_JNI_FOR_PasswordCredentialResponse()
diff --git a/components/credential_management/android/password_credential_response.h b/components/credential_management/android/password_credential_response.h new file mode 100644 index 0000000..913d3b9f --- /dev/null +++ b/components/credential_management/android/password_credential_response.h
@@ -0,0 +1,65 @@ +// Copyright 2025 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_CREDENTIAL_MANAGEMENT_ANDROID_PASSWORD_CREDENTIAL_RESPONSE_H_ +#define COMPONENTS_CREDENTIAL_MANAGEMENT_ANDROID_PASSWORD_CREDENTIAL_RESPONSE_H_ + +#include <string> + +#include "base/component_export.h" +#include "third_party/jni_zero/jni_zero.h" + +namespace credential_management { + +// This class is the C++ version of the Java class +// org.chromium.components.credential_management.PasswordCredentialResponse. It +// is used to pass password credentials that were requested through Credential +// Management API. +struct COMPONENT_EXPORT(CREDENTIAL_MANAGEMENT) PasswordCredentialResponse { + public: + static jni_zero::ScopedJavaLocalRef<jobject> Create( + JNIEnv* env, + const PasswordCredentialResponse& credential); + + static PasswordCredentialResponse FromJavaPasswordCredentialResponse( + JNIEnv* env, + const jni_zero::JavaRef<jobject>& j_credential); + + PasswordCredentialResponse(bool success, + std::u16string username, + std::u16string password); + ~PasswordCredentialResponse() = default; + PasswordCredentialResponse(const PasswordCredentialResponse&) = default; + PasswordCredentialResponse(PasswordCredentialResponse&&) = default; + PasswordCredentialResponse& operator=(const PasswordCredentialResponse&) = + default; + PasswordCredentialResponse& operator=(PasswordCredentialResponse&&) = default; + + bool success; + std::u16string username; + std::u16string password; +}; + +} // namespace credential_management + +namespace jni_zero { +template <> +inline credential_management::PasswordCredentialResponse +FromJniType<credential_management::PasswordCredentialResponse>( + JNIEnv* env, + const jni_zero::JavaRef<jobject>& j_object) { + return credential_management::PasswordCredentialResponse:: + FromJavaPasswordCredentialResponse(env, j_object); +} +template <> +inline jni_zero::ScopedJavaLocalRef<jobject> +ToJniType<credential_management::PasswordCredentialResponse>( + JNIEnv* env, + const credential_management::PasswordCredentialResponse& credential) { + return credential_management::PasswordCredentialResponse::Create(env, + credential); +} +} // namespace jni_zero + +#endif // COMPONENTS_CREDENTIAL_MANAGEMENT_ANDROID_PASSWORD_CREDENTIAL_RESPONSE_H_
diff --git a/components/credential_management/android/third_party_credential_manager_bridge.cc b/components/credential_management/android/third_party_credential_manager_bridge.cc index 57b3f1e..3a36150 100644 --- a/components/credential_management/android/third_party_credential_manager_bridge.cc +++ b/components/credential_management/android/third_party_credential_manager_bridge.cc
@@ -9,9 +9,11 @@ #include <memory> #include <variant> +#include "base/android/jni_callback.h" #include "base/android/jni_string.h" #include "base/android/scoped_java_ref.h" #include "base/functional/bind.h" +#include "base/functional/callback_forward.h" #include "content/public/browser/browser_thread.h" #include "url/gurl.h" @@ -23,6 +25,35 @@ using base::android::ConvertJavaStringToUTF8; using JniDelegate = ThirdPartyCredentialManagerBridge::JniDelegate; +void OnPasswordCredentialReceived( + const std::string& origin, + GetCallback completion_callback, + PasswordCredentialResponse credential_response) { + password_manager::CredentialManagerError status = + password_manager::CredentialManagerError::UNKNOWN; + std::optional<password_manager::CredentialInfo> info; + if (credential_response.success) { + status = password_manager::CredentialManagerError::SUCCESS; + info = password_manager::CredentialInfo( + ::password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD, + /*id=*/credential_response.username, + /*name=*/credential_response.username, + /*icon=*/GURL(), + /*password=*/credential_response.password, + /*federation=*/ + url::SchemeHostPort(GURL(origin))); + } + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce(std::move(completion_callback), status, std::move(info))); +} + +void OnCreateCredentialResponse(StoreCallback completion_callback, + bool success) { + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(std::move(completion_callback))); +} + class JniDelegateImpl : public JniDelegate { public: JniDelegateImpl() = default; @@ -30,26 +61,31 @@ JniDelegateImpl& operator=(const JniDelegateImpl&) = delete; ~JniDelegateImpl() override = default; - void CreateBridge(ThirdPartyCredentialManagerBridge* bridge) override { + void CreateBridge() override { java_bridge_.Reset(Java_ThirdPartyCredentialManagerBridge_Constructor( - jni_zero::AttachCurrentThread(), reinterpret_cast<intptr_t>(bridge))); + jni_zero::AttachCurrentThread())); } - void Get(const std::string& origin) override { + void Get(const std::string& origin, + base::OnceCallback<void(PasswordCredentialResponse)> + completion_callback) override { JNIEnv* env = jni_zero::AttachCurrentThread(); Java_ThirdPartyCredentialManagerBridge_get( - env, java_bridge_, base::android::ConvertUTF8ToJavaString(env, origin)); + env, java_bridge_, base::android::ConvertUTF8ToJavaString(env, origin), + base::android::ToJniCallback(env, std::move(completion_callback))); } void Store(const std::string& username, const std::string& password, - const std::string& origin) override { + const std::string& origin, + base::OnceCallback<void(bool)> completion_callback) override { JNIEnv* env = jni_zero::AttachCurrentThread(); Java_ThirdPartyCredentialManagerBridge_store( env, java_bridge_, base::android::ConvertUTF8ToJavaString(env, username), base::android::ConvertUTF8ToJavaString(env, password), - base::android::ConvertUTF8ToJavaString(env, origin)); + base::android::ConvertUTF8ToJavaString(env, origin), + base::android::ToJniCallback(env, std::move(completion_callback))); } private: @@ -68,61 +104,26 @@ ThirdPartyCredentialManagerBridge::~ThirdPartyCredentialManagerBridge() = default; -void ThirdPartyCredentialManagerBridge::Create( - std::variant<GetCallback, StoreCallback> callback) { - callback_ = std::move(callback); - jni_delegate_->CreateBridge(this); +void ThirdPartyCredentialManagerBridge::Create() { + jni_delegate_->CreateBridge(); } -void ThirdPartyCredentialManagerBridge::Get(const std::string& origin) { - jni_delegate_->Get(origin); +void ThirdPartyCredentialManagerBridge::Get(const std::string& origin, + GetCallback completion_callback) { + base::OnceCallback<void(PasswordCredentialResponse)> on_complete = + base::BindOnce(&OnPasswordCredentialReceived, origin, + std::move(completion_callback)); + jni_delegate_->Get(origin, std::move(on_complete)); } -void ThirdPartyCredentialManagerBridge::OnPasswordCredentialReceived( - JNIEnv* env, - const base::android::JavaParamRef<jstring>& j_username, - const base::android::JavaParamRef<jstring>& j_password, - const base::android::JavaParamRef<jstring>& j_origin) { - CHECK(j_username); - CHECK(j_password); - password_manager::CredentialInfo info = password_manager::CredentialInfo( - ::password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD, - /*id=*/base::android::ConvertJavaStringToUTF16(env, j_username), - base::android::ConvertJavaStringToUTF16(env, j_username), - /*icon=*/GURL(), base::android::ConvertJavaStringToUTF16(env, j_password), - /*federation=*/ - url::SchemeHostPort( - GURL(base::android::ConvertJavaStringToUTF16(j_origin)))); - CHECK(std::holds_alternative<GetCallback>(callback_)); - content::GetUIThreadTaskRunner({})->PostTask( - FROM_HERE, - base::BindOnce(std::move(std::get<0>(callback_)), - password_manager::CredentialManagerError::SUCCESS, - std::optional(info))); -} - -void ThirdPartyCredentialManagerBridge::OnGetPasswordCredentialError( - JNIEnv* env) { - CHECK(std::holds_alternative<GetCallback>(callback_)); - content::GetUIThreadTaskRunner({})->PostTask( - FROM_HERE, - base::BindOnce(std::move(std::get<0>(callback_)), - password_manager::CredentialManagerError::UNKNOWN, - std::nullopt)); -} - -void ThirdPartyCredentialManagerBridge::Store(const std::string& username, - const std::string& password, - const std::string& origin) { - jni_delegate_->Store(username, password, origin); -} - -void ThirdPartyCredentialManagerBridge::OnCreateCredentialResponse( - JNIEnv* env, - jboolean success) { - CHECK(std::holds_alternative<StoreCallback>(callback_)); - content::GetUIThreadTaskRunner({})->PostTask( - FROM_HERE, base::BindOnce(std::move(std::get<1>(callback_)))); +void ThirdPartyCredentialManagerBridge::Store( + const std::string& username, + const std::string& password, + const std::string& origin, + StoreCallback completion_callback) { + base::OnceCallback<void(bool)> on_complete = base::BindOnce( + &OnCreateCredentialResponse, std::move(completion_callback)); + jni_delegate_->Store(username, password, origin, std::move(on_complete)); } } // namespace credential_management
diff --git a/components/credential_management/android/third_party_credential_manager_bridge.h b/components/credential_management/android/third_party_credential_manager_bridge.h index 14510132..87042e83 100644 --- a/components/credential_management/android/third_party_credential_manager_bridge.h +++ b/components/credential_management/android/third_party_credential_manager_bridge.h
@@ -12,6 +12,7 @@ #include "base/android/scoped_java_ref.h" #include "base/functional/callback.h" #include "base/types/pass_key.h" +#include "components/credential_management/android/password_credential_response.h" #include "components/password_manager/core/common/credential_manager_types.h" namespace credential_management { @@ -32,15 +33,22 @@ virtual ~JniDelegate() = default; // Creates the JNI bridge. - virtual void CreateBridge(ThirdPartyCredentialManagerBridge* bridge) = 0; + virtual void CreateBridge() = 0; // Gets a credential from the Android Credential Manager. - virtual void Get(const std::string& origin) = 0; + // The `completion_callback` should always be invoked on completion, passing + // the PasswordCredentialResponse. + virtual void Get(const std::string& origin, + base::OnceCallback<void(PasswordCredentialResponse)> + completion_callback) = 0; // Stores a credential to the Android Credential Manager. + // The `completion_callback` should always be invoked on completion, passing + // a success status. virtual void Store(const std::string& username, const std::string& password, - const std::string& origin) = 0; + const std::string& origin, + base::OnceCallback<void(bool)> completion_callback) = 0; }; ThirdPartyCredentialManagerBridge(); @@ -55,25 +63,17 @@ ~ThirdPartyCredentialManagerBridge(); - void Create(std::variant<GetCallback, StoreCallback> callback); + void Create(); - void Get(const std::string& origin); - void OnPasswordCredentialReceived( - JNIEnv* env, - const base::android::JavaParamRef<jstring>& j_username, - const base::android::JavaParamRef<jstring>& j_password, - const base::android::JavaParamRef<jstring>& j_origin); + void Get(const std::string& origin, GetCallback completion_callback); void OnGetPasswordCredentialError(JNIEnv* env); void Store(const std::string& username, const std::string& password, - const std::string& origin); - void OnCreateCredentialResponse(JNIEnv* env, jboolean success); + const std::string& origin, + StoreCallback completion_callback); private: - // TODO(crbug.com/404505860): Pass the callback to Java instead of having it - // as a member. - std::variant<GetCallback, StoreCallback> callback_; // Forwards all requests to JNI. Can be replaced in tests. std::unique_ptr<JniDelegate> jni_delegate_; };
diff --git a/components/credential_management/android/third_party_credential_manager_bridge_unittest.cc b/components/credential_management/android/third_party_credential_manager_bridge_unittest.cc index 96f5948..2703e6b 100644 --- a/components/credential_management/android/third_party_credential_manager_bridge_unittest.cc +++ b/components/credential_management/android/third_party_credential_manager_bridge_unittest.cc
@@ -12,8 +12,11 @@ #include "base/android/jni_string.h" #include "base/functional/callback_helpers.h" #include "base/run_loop.h" +#include "base/strings/utf_string_conversions.h" #include "base/test/mock_callback.h" #include "base/types/pass_key.h" +#include "components/credential_management/android/password_credential_response.h" +#include "content/public/browser/browser_thread.h" #include "content/public/test/browser_task_environment.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -33,30 +36,32 @@ FakeJniDelegate& operator=(const FakeJniDelegate&) = delete; ~FakeJniDelegate() override = default; - void CreateBridge(ThirdPartyCredentialManagerBridge* bridge) override {} + void CreateBridge() override {} - void Get(const std::string& origin) override { - JNIEnv* env = jni_zero::AttachCurrentThread(); - + void Get(const std::string& origin, + base::OnceCallback<void(PasswordCredentialResponse)> + completion_callback) override { if (simulate_errors_) { - bridge_->OnGetPasswordCredentialError(env); + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce(std::move(completion_callback), + PasswordCredentialResponse(false, u"", u""))); return; } - jstring username_java = env->NewStringUTF(kTestUsername.c_str()); - jstring password_java = env->NewStringUTF(kTestPassword.c_str()); - jstring origin_java = env->NewStringUTF(kTestOrigin.c_str()); - base::android::JavaParamRef<jstring> username_param_ref(env, username_java); - base::android::JavaParamRef<jstring> password_param_ref(env, password_java); - base::android::JavaParamRef<jstring> origin_param_ref(env, origin_java); - bridge_->OnPasswordCredentialReceived(env, username_param_ref, - password_param_ref, origin_param_ref); + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(std::move(completion_callback), + PasswordCredentialResponse( + true, base::UTF8ToUTF16(kTestUsername), + base::UTF8ToUTF16(kTestPassword)))); } void Store(const std::string& username, const std::string& password, - const std::string& origin) override { - JNIEnv* env = jni_zero::AttachCurrentThread(); - bridge_->OnCreateCredentialResponse(env, !simulate_errors_); + const std::string& origin, + base::OnceCallback<void(bool)> completion_callback) override { + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce(std::move(completion_callback), simulate_errors_)); } void set_bridge(ThirdPartyCredentialManagerBridge* bridge) { @@ -98,64 +103,92 @@ std::unique_ptr<ThirdPartyCredentialManagerBridge> bridge_; }; -TEST_F(ThirdPartyCredentialManagerBridgeTest, testSuccessfulGetCall) { +TEST_F(ThirdPartyCredentialManagerBridgeTest, TestSuccessfulGetCall) { base::RunLoop run_loop; base::MockCallback<GetCallback> mock_callback; fake_jni_delegate().set_error_simulation(false); - bridge()->Create(mock_callback.Get()); + bridge()->Create(); EXPECT_CALL( mock_callback, Run(password_manager::CredentialManagerError::SUCCESS, testing::_)) .WillOnce(testing::Invoke([&]() { run_loop.Quit(); })); - bridge()->Get(FakeJniDelegate::kTestOrigin); + bridge()->Get(FakeJniDelegate::kTestOrigin, mock_callback.Get()); run_loop.Run(); } -TEST_F(ThirdPartyCredentialManagerBridgeTest, testUnuccessfulGetCall) { +TEST_F(ThirdPartyCredentialManagerBridgeTest, TestUnuccessfulGetCall) { base::RunLoop run_loop; base::MockCallback<GetCallback> mock_callback; fake_jni_delegate().set_error_simulation(true); - bridge()->Create(mock_callback.Get()); + bridge()->Create(); EXPECT_CALL( mock_callback, Run(password_manager::CredentialManagerError::UNKNOWN, testing::_)) .WillOnce(testing::Invoke([&]() { run_loop.Quit(); })); - bridge()->Get(FakeJniDelegate::kTestOrigin); + bridge()->Get(FakeJniDelegate::kTestOrigin, mock_callback.Get()); run_loop.Run(); } -TEST_F(ThirdPartyCredentialManagerBridgeTest, testSuccessfulStoreCall) { +TEST_F(ThirdPartyCredentialManagerBridgeTest, TestSuccessfulStoreCall) { base::RunLoop run_loop; base::MockCallback<StoreCallback> mock_callback; fake_jni_delegate().set_error_simulation(false); - bridge()->Create(mock_callback.Get()); + bridge()->Create(); EXPECT_CALL(mock_callback, Run()).WillOnce(testing::Invoke([&]() { run_loop.Quit(); })); bridge()->Store(FakeJniDelegate::kTestUsername, - FakeJniDelegate::kTestPassword, FakeJniDelegate::kTestOrigin); + FakeJniDelegate::kTestPassword, FakeJniDelegate::kTestOrigin, + mock_callback.Get()); run_loop.Run(); } -TEST_F(ThirdPartyCredentialManagerBridgeTest, testUnuccessfulStoreCall) { +TEST_F(ThirdPartyCredentialManagerBridgeTest, TestUnuccessfulStoreCall) { base::RunLoop run_loop; base::MockCallback<StoreCallback> mock_callback; fake_jni_delegate().set_error_simulation(true); - bridge()->Create(mock_callback.Get()); + bridge()->Create(); EXPECT_CALL(mock_callback, Run()).WillOnce(testing::Invoke([&]() { run_loop.Quit(); })); bridge()->Store(FakeJniDelegate::kTestUsername, - FakeJniDelegate::kTestPassword, FakeJniDelegate::kTestOrigin); + FakeJniDelegate::kTestPassword, FakeJniDelegate::kTestOrigin, + mock_callback.Get()); run_loop.Run(); } +TEST_F(ThirdPartyCredentialManagerBridgeTest, TestMultipleCalls) { + base::RunLoop run_loop_store; + base::RunLoop run_loop_get; + base::MockCallback<StoreCallback> mock_store_callback; + base::MockCallback<GetCallback> mock_get_callback; + fake_jni_delegate().set_error_simulation(false); + + bridge()->Create(); + + EXPECT_CALL(mock_store_callback, Run()).WillOnce(testing::Invoke([&]() { + run_loop_store.Quit(); + })); + bridge()->Store(FakeJniDelegate::kTestUsername, + FakeJniDelegate::kTestPassword, FakeJniDelegate::kTestOrigin, + mock_store_callback.Get()); + run_loop_store.Run(); + + EXPECT_CALL( + mock_get_callback, + Run(password_manager::CredentialManagerError::SUCCESS, testing::_)) + .WillOnce(testing::Invoke([&]() { run_loop_get.Quit(); })); + + bridge()->Get(FakeJniDelegate::kTestOrigin, mock_get_callback.Get()); + run_loop_get.Run(); +} + } // namespace credential_management
diff --git a/components/cronet/tools/generate_proguard_file.py b/components/cronet/tools/generate_proguard_file.py index 5411591..e2a445c9 100755 --- a/components/cronet/tools/generate_proguard_file.py +++ b/components/cronet/tools/generate_proguard_file.py
@@ -38,12 +38,15 @@ proguard_configs_path = set() with open(build_config_path, 'r') as f: build_config = json.load(f) - proguard_configs_path.update(build_config["deps_info"].get( - "proguard_all_configs", set())) + proguard_configs_path.update(build_config.get("proguard_all_configs", + set())) + + params_path = build_config_path.replace('.build_config.json', '.params.json') + with open(params_path, 'r') as f: + params_json = json.load(f) # java_library targets don't have `proguard_all_configs` so we need # to look at `proguard_configs` field instead. - proguard_configs_path.update(build_config["deps_info"].get( - "proguard_configs", set())) + proguard_configs_path.update(params_json.get("proguard_configs", set())) return proguard_configs_path def main():
diff --git a/components/dom_distiller/content/browser/distiller_page_web_contents.cc b/components/dom_distiller/content/browser/distiller_page_web_contents.cc index a4800b2e..275d422f 100644 --- a/components/dom_distiller/content/browser/distiller_page_web_contents.cc +++ b/components/dom_distiller/content/browser/distiller_page_web_contents.cc
@@ -80,6 +80,10 @@ DistillerPageWebContents::~DistillerPageWebContents() = default; +bool DistillerPageWebContents::ShouldFetchOfflineData() { + return false; +} + void DistillerPageWebContents::DistillPageImpl(const GURL& url, const std::string& script) { DCHECK(browser_context_);
diff --git a/components/dom_distiller/content/browser/distiller_page_web_contents.h b/components/dom_distiller/content/browser/distiller_page_web_contents.h index 5223ac9e..24e09dab 100644 --- a/components/dom_distiller/content/browser/distiller_page_web_contents.h +++ b/components/dom_distiller/content/browser/distiller_page_web_contents.h
@@ -58,6 +58,9 @@ optional_web_contents_handle); ~DistillerPageWebContents() override; + // DistillerPage implementation. + bool ShouldFetchOfflineData() override; + // content::WebContentsDelegate implementation. gfx::Size GetSizeForNewRenderView( content::WebContents* web_contents) override;
diff --git a/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc b/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc index 92abe27..06689f8a 100644 --- a/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc +++ b/components/dom_distiller/content/browser/dom_distiller_viewer_source.cc
@@ -255,7 +255,7 @@ std::string unsafe_page_html = viewer::GetArticleTemplateHtml( dom_distiller_service_->GetDistilledPagePrefs()->GetTheme(), dom_distiller_service_->GetDistilledPagePrefs()->GetFontFamily(), - std::string()); + std::string(), /*use_offline_data=*/false); if (viewer_handle) { // The service returned a |ViewerHandle| and guarantees it will call
diff --git a/components/dom_distiller/content/browser/test/test_util.cc b/components/dom_distiller/content/browser/test/test_util.cc index 81752139..40655ee0 100644 --- a/components/dom_distiller/content/browser/test/test_util.cc +++ b/components/dom_distiller/content/browser/test/test_util.cc
@@ -88,7 +88,8 @@ std::string FakeDistilledPage::GetPageHtmlWithScripts() { std::string html = GetArticleTemplateHtml( - mojom::Theme::kLight, mojom::FontFamily::kSansSerif, std::string()); + mojom::Theme::kLight, mojom::FontFamily::kSansSerif, std::string(), + /*use_offline_data=*/false); for (const std::string& file : scripts_) { StrAppend(&html, {JsReplace("<script src=$1></script>", file)}); }
diff --git a/components/dom_distiller/core/distiller.cc b/components/dom_distiller/core/distiller.cc index e85678ce..2c9518c6c 100644 --- a/components/dom_distiller/core/distiller.cc +++ b/components/dom_distiller/core/distiller.cc
@@ -63,15 +63,6 @@ DCHECK(destruction_allowed_); } -bool DistillerImpl::DoesFetchImages() { -// Only iOS makes use of the fetched image data. -#if BUILDFLAG(IS_IOS) - return true; -#else - return false; -#endif -} - void DistillerImpl::SetMaxNumPagesInArticle(size_t max_num_pages) { max_pages_in_article_ = max_num_pages; } @@ -256,7 +247,7 @@ DCHECK(started_pages_index_.find(page_num) != started_pages_index_.end()); DistilledPageData* page_data = GetPageAtIndex(started_pages_index_[page_num]); - if (!DoesFetchImages()) { + if (!distiller_page_->ShouldFetchOfflineData()) { DistilledPageProto_Image* image = page_data->distilled_page_proto->data.add_image(); image->set_name(image_id);
diff --git a/components/dom_distiller/core/distiller.h b/components/dom_distiller/core/distiller.h index cf1f6040..2da72f0 100644 --- a/components/dom_distiller/core/distiller.h +++ b/components/dom_distiller/core/distiller.h
@@ -84,8 +84,6 @@ void SetMaxNumPagesInArticle(size_t max_num_pages); - static bool DoesFetchImages(); - DistillerImpl(const DistillerImpl&) = delete; DistillerImpl& operator=(const DistillerImpl&) = delete;
diff --git a/components/dom_distiller/core/distiller_page.h b/components/dom_distiller/core/distiller_page.h index d831acae..aa044a1 100644 --- a/components/dom_distiller/core/distiller_page.h +++ b/components/dom_distiller/core/distiller_page.h
@@ -48,6 +48,10 @@ virtual void OnDistillationDone(const GURL& page_url, const base::Value* value); + // Returns true if the distiller page should fetch distillation data for + // offline consumption. + virtual bool ShouldFetchOfflineData() = 0; + DistillerPage(const DistillerPage&) = delete; DistillerPage& operator=(const DistillerPage&) = delete;
diff --git a/components/dom_distiller/core/distiller_unittest.cc b/components/dom_distiller/core/distiller_unittest.cc index 450062b..0af0c815c 100644 --- a/components/dom_distiller/core/distiller_unittest.cc +++ b/components/dom_distiller/core/distiller_unittest.cc
@@ -196,6 +196,7 @@ void VerifyArticleProtoMatchesMultipageData( const dom_distiller::DistilledArticleProto* article_proto, const MultipageDistillerData* distiller_data, + bool fetches_offline_data, size_t distilled_pages_size, size_t total_pages_size, size_t start_page_offset = 0) { @@ -214,7 +215,7 @@ const std::vector<int>& image_ids_for_page = distiller_data->image_ids[page_num]; for (size_t img_num = 0; img_num < image_ids_for_page.size(); ++img_num) { - if (dom_distiller::DistillerImpl::DoesFetchImages()) { + if (fetches_offline_data) { EXPECT_EQ(kImageData[image_ids_for_page[img_num]], page.image(img_num).data()); } else { @@ -333,26 +334,41 @@ std::unique_ptr<DistillerPage> CreateMockDistillerPage( const base::Value* result, - const GURL& url) { + const GURL& url, + bool use_offline_data) { MockDistillerPage* distiller_page = new MockDistillerPage(); EXPECT_CALL(*distiller_page, DistillPageImpl(url, _)) .WillOnce(DistillerPageOnDistillationDone(distiller_page, url, result)); + EXPECT_CALL(*distiller_page, ShouldFetchOfflineData()) + .WillRepeatedly(Return(use_offline_data)); return std::unique_ptr<DistillerPage>(distiller_page); } +std::unique_ptr<DistillerPage> CreateMockDistillerPage( + const base::Value* result, + const GURL& url) { + return CreateMockDistillerPage(result, url, + /*use_offline_data=*/false); +} + std::unique_ptr<DistillerPage> CreateMockDistillerPageWithPendingJSCallback( MockDistillerPage** distiller_page_ptr, const GURL& url) { MockDistillerPage* distiller_page = new MockDistillerPage(); *distiller_page_ptr = distiller_page; EXPECT_CALL(*distiller_page, DistillPageImpl(url, _)); + EXPECT_CALL(*distiller_page, ShouldFetchOfflineData()) + .WillRepeatedly(Return(false)); return std::unique_ptr<DistillerPage>(distiller_page); } std::unique_ptr<DistillerPage> CreateMockDistillerPagesWithSequence( MultipageDistillerData* distiller_data, - const std::vector<int>& page_num_sequence) { + const std::vector<int>& page_num_sequence, + bool use_offline_data) { MockDistillerPage* distiller_page = new MockDistillerPage(); + EXPECT_CALL(*distiller_page, ShouldFetchOfflineData()) + .WillRepeatedly(Return(use_offline_data)); { testing::InSequence s; for (int page : page_num_sequence) { @@ -371,9 +387,30 @@ size_t pages_size, int start_page_num) { std::vector<int> page_nums = GetPagesInSequence(start_page_num, pages_size); - return CreateMockDistillerPagesWithSequence(distiller_data, page_nums); + return CreateMockDistillerPagesWithSequence(distiller_data, page_nums, + /*use_offline_data=*/false); } +class OfflineDistillerTest : public DistillerTest, + public ::testing::WithParamInterface<bool> { + public: + OfflineDistillerTest() : fetch_data_(GetParam()) {} + + bool FetchData() { return fetch_data_; } + + std::unique_ptr<DistillerPage> CreateMockDistillerPages( + MultipageDistillerData* distiller_data, + size_t pages_size, + int start_page_num) { + std::vector<int> page_nums = GetPagesInSequence(start_page_num, pages_size); + return CreateMockDistillerPagesWithSequence(distiller_data, page_nums, + FetchData()); + } + + private: + bool fetch_data_; +}; + TEST_F(DistillerTest, DistillPage) { base::Value result = CreateDistilledValueReturnedFromJS( kTitle, kContent, std::vector<int>(), ""); @@ -401,12 +438,7 @@ EXPECT_EQ(kDebugLog, first_page.debug_info().log()); } -void SetTimingEntry(TimingEntry* entry, const std::string& name, double time) { - entry->set_name(name); - entry->set_time(time); -} - -TEST_F(DistillerTest, DistillPageWithImages) { +TEST_P(OfflineDistillerTest, DistillPageWithImages) { std::vector<int> image_indices; image_indices.push_back(0); image_indices.push_back(1); @@ -415,7 +447,7 @@ CreateDistilledValueReturnedFromJS(kTitle, kContent, image_indices, ""); distiller_ = std::make_unique<DistillerImpl>(url_fetcher_factory_, DomDistillerOptions()); - DistillPage(kURL, CreateMockDistillerPage(&result, GURL(kURL))); + DistillPage(kURL, CreateMockDistillerPage(&result, GURL(kURL), FetchData())); base::RunLoop().RunUntilIdle(); EXPECT_EQ(kTitle, article_proto_->title()); ASSERT_EQ(article_proto_->pages_size(), 1); @@ -424,7 +456,7 @@ EXPECT_EQ(kURL, first_page.url()); ASSERT_EQ(2, first_page.image_size()); - if (DistillerImpl::DoesFetchImages()) { + if (FetchData()) { EXPECT_EQ(kImageData[0], first_page.image(0).data()); } else { EXPECT_EQ("", first_page.image(0).data()); @@ -432,7 +464,7 @@ EXPECT_EQ(kImageURLs[0], first_page.image(0).url()); EXPECT_EQ(GetImageName(1, 0), first_page.image(0).name()); - if (DistillerImpl::DoesFetchImages()) { + if (FetchData()) { EXPECT_EQ(kImageData[1], first_page.image(1).data()); } else { EXPECT_EQ("", first_page.image(1).data()); @@ -441,7 +473,7 @@ EXPECT_EQ(GetImageName(1, 1), first_page.image(1).name()); } -TEST_F(DistillerTest, DistillMultiplePages) { +TEST_P(OfflineDistillerTest, DistillMultiplePages) { const size_t kNumPages = 8; // Add images. @@ -466,8 +498,9 @@ DistillPage(distiller_data->page_urls[0], CreateMockDistillerPages(distiller_data.get(), kNumPages, 0)); base::RunLoop().RunUntilIdle(); - VerifyArticleProtoMatchesMultipageData( - article_proto_.get(), distiller_data.get(), kNumPages, kNumPages); + VerifyArticleProtoMatchesMultipageData(article_proto_.get(), + distiller_data.get(), FetchData(), + kNumPages, kNumPages); } TEST_F(DistillerTest, DistillLinkLoop) { @@ -544,7 +577,7 @@ EXPECT_EQ(0, article_proto_->pages_size()); } -TEST_F(DistillerTest, MultiplePagesDistillationFailure) { +TEST_P(OfflineDistillerTest, MultiplePagesDistillationFailure) { const size_t kNumPages = 8; std::unique_ptr<MultipageDistillerData> distiller_data = CreateMultipageDistillerDataWithoutImages(kNumPages); @@ -562,11 +595,12 @@ CreateMockDistillerPages(distiller_data.get(), failed_page_num + 1, 0)); base::RunLoop().RunUntilIdle(); EXPECT_EQ(kTitle, article_proto_->title()); - VerifyArticleProtoMatchesMultipageData( - article_proto_.get(), distiller_data.get(), failed_page_num, kNumPages); + VerifyArticleProtoMatchesMultipageData(article_proto_.get(), + distiller_data.get(), FetchData(), + failed_page_num, kNumPages); } -TEST_F(DistillerTest, DistillMultiplePagesFirstEmpty) { +TEST_P(OfflineDistillerTest, DistillMultiplePagesFirstEmpty) { const size_t kNumPages = 8; std::unique_ptr<MultipageDistillerData> distiller_data = CreateMultipageDistillerDataWithoutImages(kNumPages); @@ -589,11 +623,11 @@ base::RunLoop().RunUntilIdle(); // If the first page has no content, stop fetching the next page. EXPECT_EQ(1, article_proto_->pages_size()); - VerifyArticleProtoMatchesMultipageData(article_proto_.get(), - distiller_data.get(), 1, 1); + VerifyArticleProtoMatchesMultipageData( + article_proto_.get(), distiller_data.get(), FetchData(), 1, 1); } -TEST_F(DistillerTest, DistillMultiplePagesSecondEmpty) { +TEST_P(OfflineDistillerTest, DistillMultiplePagesSecondEmpty) { const size_t kNumPages = 8; std::unique_ptr<MultipageDistillerData> distiller_data = CreateMultipageDistillerDataWithoutImages(kNumPages); @@ -615,11 +649,12 @@ CreateMockDistillerPages(distiller_data.get(), kNumPages, 0)); base::RunLoop().RunUntilIdle(); - VerifyArticleProtoMatchesMultipageData( - article_proto_.get(), distiller_data.get(), kNumPages, kNumPages); + VerifyArticleProtoMatchesMultipageData(article_proto_.get(), + distiller_data.get(), FetchData(), + kNumPages, kNumPages); } -TEST_F(DistillerTest, DistillMultiplePagesNextDifferingOrigin) { +TEST_P(OfflineDistillerTest, DistillMultiplePagesNextDifferingOrigin) { const size_t kNumPages = 8; const size_t kActualPages = 4; std::unique_ptr<MultipageDistillerData> distiller_data = @@ -644,11 +679,12 @@ CreateMockDistillerPages(distiller_data.get(), kActualPages, 0)); base::RunLoop().RunUntilIdle(); - VerifyArticleProtoMatchesMultipageData( - article_proto_.get(), distiller_data.get(), kActualPages, kActualPages); + VerifyArticleProtoMatchesMultipageData(article_proto_.get(), + distiller_data.get(), FetchData(), + kActualPages, kActualPages); } -TEST_F(DistillerTest, DistillMultiplePagesPrevDifferingOrigin) { +TEST_P(OfflineDistillerTest, DistillMultiplePagesPrevDifferingOrigin) { const size_t kNumPages = 8; const size_t kActualPages = 6; std::vector<int> page_num_seq{3, 2, 4, 5, 6, 7}; @@ -671,15 +707,16 @@ DomDistillerOptions()); DistillPage( distiller_data->page_urls[target_page_num + 1], - CreateMockDistillerPagesWithSequence(distiller_data.get(), page_num_seq)); + CreateMockDistillerPagesWithSequence(distiller_data.get(), page_num_seq, + /*use_offline_data=*/false)); base::RunLoop().RunUntilIdle(); - VerifyArticleProtoMatchesMultipageData(article_proto_.get(), - distiller_data.get(), kActualPages, - kNumPages, target_page_num); + VerifyArticleProtoMatchesMultipageData( + article_proto_.get(), distiller_data.get(), FetchData(), kActualPages, + kNumPages, target_page_num); } -TEST_F(DistillerTest, DistillPreviousPage) { +TEST_P(OfflineDistillerTest, DistillPreviousPage) { const size_t kNumPages = 8; // The page number of the article on which distillation starts. @@ -693,8 +730,9 @@ CreateMockDistillerPages(distiller_data.get(), kNumPages, start_page_num)); base::RunLoop().RunUntilIdle(); - VerifyArticleProtoMatchesMultipageData( - article_proto_.get(), distiller_data.get(), kNumPages, kNumPages); + VerifyArticleProtoMatchesMultipageData(article_proto_.get(), + distiller_data.get(), FetchData(), + kNumPages, kNumPages); } TEST_F(DistillerTest, IncrementalUpdates) { @@ -719,7 +757,7 @@ in_sequence_updates_, start_page_num); } -TEST_F(DistillerTest, IncrementalUpdatesDoNotDeleteFinalArticle) { +TEST_P(OfflineDistillerTest, IncrementalUpdatesDoNotDeleteFinalArticle) { const size_t kNumPages = 8; int start_page_num = 3; std::unique_ptr<MultipageDistillerData> distiller_data = @@ -736,8 +774,9 @@ in_sequence_updates_.clear(); // Should still be able to access article and pages. - VerifyArticleProtoMatchesMultipageData( - article_proto_.get(), distiller_data.get(), kNumPages, kNumPages); + VerifyArticleProtoMatchesMultipageData(article_proto_.get(), + distiller_data.get(), FetchData(), + kNumPages, kNumPages); } TEST_F(DistillerTest, DeletingArticleDoesNotInterfereWithUpdates) { @@ -764,9 +803,6 @@ } TEST_F(DistillerTest, CancelWithDelayedImageFetchCallback) { - if (!DistillerImpl::DoesFetchImages()) - return; - std::vector<int> image_indices; image_indices.push_back(0); base::Value distilled_value = @@ -777,7 +813,8 @@ .WillOnce(Return(delayed_fetcher)); distiller_ = std::make_unique<DistillerImpl>(mock_url_fetcher_factory, DomDistillerOptions()); - DistillPage(kURL, CreateMockDistillerPage(&distilled_value, GURL(kURL))); + DistillPage(kURL, CreateMockDistillerPage(&distilled_value, GURL(kURL), + /*use_offline_data=*/true)); base::RunLoop().RunUntilIdle(); // Post callback from the url fetcher and then delete the distiller. @@ -805,4 +842,14 @@ base::RunLoop().RunUntilIdle(); } +std::string ParamToString(const testing::TestParamInfo<bool>& params_info) { + return params_info.param ? "ShouldFetchOfflineDataEnabled" + : "ShouldFetchOfflineDataDisabled"; +} + +INSTANTIATE_TEST_SUITE_P(All, + OfflineDistillerTest, + ::testing::Bool(), + ParamToString); + } // namespace dom_distiller
diff --git a/components/dom_distiller/core/fake_distiller_page.h b/components/dom_distiller/core/fake_distiller_page.h index d780f33..2276a71e 100644 --- a/components/dom_distiller/core/fake_distiller_page.h +++ b/components/dom_distiller/core/fake_distiller_page.h
@@ -30,6 +30,7 @@ public: MockDistillerPage(); ~MockDistillerPage() override; + MOCK_METHOD0(ShouldFetchOfflineData, bool()); MOCK_METHOD2(DistillPageImpl, void(const GURL& gurl, const std::string& script)); };
diff --git a/components/dom_distiller/core/viewer.cc b/components/dom_distiller/core/viewer.cc index 5bc4ef9..1fce996 100644 --- a/components/dom_distiller/core/viewer.cc +++ b/components/dom_distiller/core/viewer.cc
@@ -110,7 +110,8 @@ std::string ReplaceHtmlTemplateValues(const mojom::Theme theme, const mojom::FontFamily font_family, - const std::string& csp_nonce) { + const std::string& csp_nonce, + bool use_offline_data) { std::string html_template = ui::ResourceBundle::GetSharedInstance().LoadDataResourceString( IDR_DOM_DISTILLER_VIEWER_HTML); @@ -165,28 +166,27 @@ // and return the local data once a page is loaded. css << "<style>" << viewer::GetCss() << "</style>"; svg << viewer::GetLoadingImage(); - - // iOS specific CSP policy to mitigate leaking of data from different - // origins. - csp << "<meta http-equiv=\"Content-Security-Policy\" content=\""; - csp << "default-src 'none'; "; - csp << "script-src 'nonce-" << csp_nonce << "'; "; - // YouTube videos are embedded as an iframe. - csp << "frame-src http://www.youtube.com; "; - csp << "style-src 'unsafe-inline' https://fonts.googleapis.com; "; - // Allows the fallback font-face from the main stylesheet. - csp << "font-src https://fonts.gstatic.com; "; - // Images will be inlined as data-uri if they are valid. - csp << "img-src data:; "; - csp << "form-action 'none'; "; - csp << "base-uri 'none'; "; - csp << "\">"; - #else css << "<link rel=\"stylesheet\" href=\"/" << kViewerCssPath << "\">"; svg << "<img src=\"/" << kViewerLoadingImagePath << "\">"; -#endif // BUILDFLAG(IS_IOS) +#endif // BUILDFLAG(IS_IOS) && !BUILDFLAG(USE_BLINK) + if (use_offline_data) { + // CSP policy to mitigate leaking of data from different origins. + csp << "<meta http-equiv=\"Content-Security-Policy\" content=\""; + csp << "default-src 'none'; "; + csp << "script-src 'nonce-" << csp_nonce << "'; "; + // YouTube videos are embedded as an iframe. + csp << "frame-src http://www.youtube.com; "; + csp << "style-src 'unsafe-inline' https://fonts.googleapis.com; "; + // Allows the fallback font-face from the main stylesheet. + csp << "font-src https://fonts.gstatic.com; "; + // Images will be inlined as data-uri if they are valid. + csp << "img-src data:; "; + csp << "form-action 'none'; "; + csp << "base-uri 'none'; "; + csp << "\">"; + } substitutions.push_back(csp.str()); // $1 substitutions.push_back(css.str()); // $2 substitutions.push_back(GetThemeCssClass(theme) + " " + @@ -260,8 +260,10 @@ const std::string GetArticleTemplateHtml(mojom::Theme theme, mojom::FontFamily font_family, - const std::string& csp_nonce) { - return ReplaceHtmlTemplateValues(theme, font_family, csp_nonce); + const std::string& csp_nonce, + bool use_offline_data) { + return ReplaceHtmlTemplateValues(theme, font_family, csp_nonce, + use_offline_data); } const std::string GetUnsafeArticleContentJs(
diff --git a/components/dom_distiller/core/viewer.h b/components/dom_distiller/core/viewer.h index 6aa180e5..cb55b56 100644 --- a/components/dom_distiller/core/viewer.h +++ b/components/dom_distiller/core/viewer.h
@@ -28,7 +28,8 @@ // so the returned HTML should be safe. const std::string GetArticleTemplateHtml(mojom::Theme theme, mojom::FontFamily font_family, - const std::string& csp_nonce); + const std::string& csp_nonce, + bool use_offline_data); // Returns the JavaScript to place a full article's HTML on the page. The // returned HTML should be considered unsafe, so callers must ensure
diff --git a/components/ip_protection/BUILD.gn b/components/ip_protection/BUILD.gn index 64c80c8d..61d0c8d9 100644 --- a/components/ip_protection/BUILD.gn +++ b/components/ip_protection/BUILD.gn
@@ -77,11 +77,13 @@ deps = [ "//base", + "//components/ip_protection:get_probabilistic_reveal_token_proto", "//components/ip_protection/common:ip_protection_probabilistic_reveal_token_crypter", + "//components/ip_protection/common:probabilistic_reveal_token_test_issuer", + "//testing/gmock", "//third_party/abseil-cpp:absl", "//third_party/boringssl", "//third_party/fuzztest:fuzztest_gtest_main", - "//third_party/private-join-and-compute/src:ec_commutative_cipher", # Select UMA as the implementation of `ip_protection::Telemetry()`. "//components/ip_protection/common:ip_protection_telemetry_uma",
diff --git a/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter_fuzztests.cc b/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter_fuzztests.cc index 25e3441..e3a0d4d3f 100644 --- a/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter_fuzztests.cc +++ b/components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter_fuzztests.cc
@@ -3,65 +3,23 @@ // found in the LICENSE file. #include <cstddef> -#include <cstdint> #include <memory> #include <string> #include <utility> +#include "base/types/expected.h" #include "components/ip_protection/common/ip_protection_data_types.h" #include "components/ip_protection/common/ip_protection_probabilistic_reveal_token_crypter.h" +#include "components/ip_protection/common/probabilistic_reveal_token_test_issuer.h" +#include "components/ip_protection/get_probabilistic_reveal_token.pb.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/status/statusor.h" +#include "third_party/fuzztest/src/fuzztest/domain_core.h" #include "third_party/fuzztest/src/fuzztest/fuzztest.h" -#include "third_party/private-join-and-compute/src/crypto/context.h" -#include "third_party/private-join-and-compute/src/crypto/ec_group.h" -#include "third_party/private-join-and-compute/src/crypto/elgamal.h" - -using ::private_join_and_compute::Context; -using ::private_join_and_compute::ECGroup; -using ::private_join_and_compute::ECPoint; -using ::private_join_and_compute::ElGamalDecrypter; -using ::private_join_and_compute::ElGamalEncrypter; -using ::private_join_and_compute::elgamal::Ciphertext; -using ::private_join_and_compute::elgamal::PrivateKey; -using ::private_join_and_compute::elgamal::PublicKey; namespace { - -// Returns serialized g^private_key where g is group generator for curve -// NID_X9_62_prime256v1. -absl::StatusOr<std::string> GetSerializedPublicKey(uint64_t private_key) { - Context context; - PJC_ASSIGN_OR_RETURN(ECGroup group, - ECGroup::Create(NID_X9_62_prime256v1, &context)); - PJC_ASSIGN_OR_RETURN(ECPoint g, group.GetFixedGenerator()); - PJC_ASSIGN_OR_RETURN(ECPoint y, g.Mul(context.CreateBigNum(private_key))); - return y.ToBytesCompressed(); -} - -absl::StatusOr<ip_protection::ProbabilisticRevealToken> -CreateTokenFromPlaintext(uint64_t private_key, const std::string& plaintext) { - Context context; - PJC_ASSIGN_OR_RETURN(ECGroup group, - ECGroup::Create(NID_X9_62_prime256v1, &context)); - PJC_ASSIGN_OR_RETURN(ECPoint plaintext_point, - group.GetPointByHashingToCurveSha256(plaintext)); - - PJC_ASSIGN_OR_RETURN(ECPoint g, group.GetFixedGenerator()); - PJC_ASSIGN_OR_RETURN(ECPoint y, g.Mul(context.CreateBigNum(private_key))); - std::unique_ptr<PublicKey> public_key( - new PublicKey{std::move(g), std::move(y)}); - ElGamalEncrypter encrypter(&group, std::move(public_key)); - PJC_ASSIGN_OR_RETURN(Ciphertext ciphertext, - encrypter.Encrypt(plaintext_point)); - PJC_ASSIGN_OR_RETURN(std::string u_compressed, - ciphertext.u.ToBytesCompressed()); - PJC_ASSIGN_OR_RETURN(std::string e_compressed, - ciphertext.e.ToBytesCompressed()); - return ip_protection::ProbabilisticRevealToken{ - 1, std::move(u_compressed), std::move(e_compressed)}; -} - +constexpr size_t kPlaintextSize = 29; } // namespace // Fuzz test for creating probabilistic reveal token crypter. Creating crypter @@ -79,26 +37,35 @@ }); } -// Fuzz test for probabilistic reveal token randomization of ciphertexts. -// library. In chrome, ciphertext is returned by the issuer service, which is -// from a hard coded url. Fuzzer creates a valid encrypter from valid public key -// and ciphertexts before calling randomize. Ciphertexts are created by hashing -// random strings to the curve. +// Fuzztest for probabilistic reveal token randomization. In Chrome, PRTs are +// returned by the issuer service, which is from a hard coded url. Fuzzer +// creates a valid public key and a PRT before calling randomize. void RandomizeDoesNotCrash(const std::string& plaintext) { - // public key - const uint64_t private_key = 12345; - auto maybe_serialized_public_key = GetSerializedPublicKey(private_key); - ASSERT_TRUE(maybe_serialized_public_key.ok()); - auto& serialized_public_key = maybe_serialized_public_key.value(); + base::expected< + std::unique_ptr<ip_protection::ProbabilisticRevealTokenTestIssuer>, + absl::Status> + maybe_issuer = ip_protection::ProbabilisticRevealTokenTestIssuer::Create( + /*private_key=*/12345); + ASSERT_TRUE(maybe_issuer.has_value()) + << "creating test issuer failed with error: " << maybe_issuer.error(); + auto issuer = std::move(maybe_issuer).value(); + base::expected<ip_protection::GetProbabilisticRevealTokenResponse, + absl::Status> + maybe_response = issuer->Issue({plaintext}, + /*expiration=*/base::Time::Now(), + /*next_epoch_start=*/base::Time::Now(), + /*num_tokens_with_signal=*/0, + /*epoch_id=*/"epoch-id"); + ASSERT_TRUE(maybe_response.has_value()) + << "creating tokens failed with error: " << maybe_response.error(); + ASSERT_THAT(issuer->Tokens(), testing::SizeIs(1)); + const ip_protection::ProbabilisticRevealToken& token = issuer->Tokens()[0]; - // tokens - auto maybe_token = CreateTokenFromPlaintext(private_key, plaintext); - ASSERT_TRUE(maybe_token.ok()); - auto& token = maybe_token.value(); - - auto maybe_crypter = - ip_protection::IpProtectionProbabilisticRevealTokenCrypter::Create( - serialized_public_key, {token}); + absl::StatusOr<std::unique_ptr< + ip_protection::IpProtectionProbabilisticRevealTokenCrypter>> + maybe_crypter = + ip_protection::IpProtectionProbabilisticRevealTokenCrypter::Create( + issuer->GetSerializedPublicKey(), {token}); ASSERT_TRUE(maybe_crypter.ok()); auto& crypter = maybe_crypter.value(); ASSERT_TRUE(crypter->IsTokenAvailable()); @@ -110,4 +77,5 @@ CreateDoesNotCrash); FUZZ_TEST(IpProtectionProbabilisticRevealTokenCrypterFuzzTests, - RandomizeDoesNotCrash); + RandomizeDoesNotCrash) + .WithDomains(fuzztest::String().WithSize(kPlaintextSize));
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java index 0058d1b..d7c2e7ab 100644 --- a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java +++ b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java
@@ -15,6 +15,7 @@ import androidx.core.view.accessibility.AccessibilityEventCompat; import org.chromium.base.supplier.Supplier; +import org.chromium.build.annotations.MockedInTests; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.components.messages.MessageStateHandler.Position; @@ -25,6 +26,7 @@ import org.chromium.ui.util.RunnableTimer; /** Coordinator responsible for creating a message banner. */ +@MockedInTests @NullMarked class MessageBannerCoordinator { private final MessageBannerMediator mMediator;
diff --git a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteResult.java b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteResult.java index 920ebc8..a9d2150 100644 --- a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteResult.java +++ b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteResult.java
@@ -12,6 +12,7 @@ import org.jni_zero.CalledByNative; import org.jni_zero.NativeMethods; +import org.chromium.build.annotations.MockedInTests; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.components.omnibox.GroupsProto.GroupsInfo; @@ -23,6 +24,7 @@ import java.util.List; /** AutocompleteResult encompasses and manages autocomplete results. */ +@MockedInTests @NullMarked public class AutocompleteResult { @IntDef({
diff --git a/components/password_manager/core/browser/features/password_manager_features_util_unittest.cc b/components/password_manager/core/browser/features/password_manager_features_util_unittest.cc index 4fd2778..0a204b1 100644 --- a/components/password_manager/core/browser/features/password_manager_features_util_unittest.cc +++ b/components/password_manager/core/browser/features/password_manager_features_util_unittest.cc
@@ -183,6 +183,8 @@ prefs::kObsoleteAccountStoragePerAccountSettings); pref_service_.registry()->RegisterBooleanPref(::prefs::kExplicitBrowserSignin, false); + pref_service_.registry()->RegisterBooleanPref( + ::prefs::kPrefsThemesSearchEnginesAccountStorageEnabled, false); // Set up 2 account storage users, with default stores "profile" and // "account", respectively.
diff --git a/components/password_manager/core/browser/password_store/fake_password_store_backend.cc b/components/password_manager/core/browser/password_store/fake_password_store_backend.cc index 394850c..0e6aa36 100644 --- a/components/password_manager/core/browser/password_store/fake_password_store_backend.cc +++ b/components/password_manager/core/browser/password_store/fake_password_store_backend.cc
@@ -86,6 +86,11 @@ FROM_HERE, base::BindOnce(remote_form_changes_received_, std::nullopt)); } +void FakePasswordStoreBackend::ReturnErrorOnRequest( + PasswordStoreBackendError password_store_backend_error) { + password_store_backend_error_ = password_store_backend_error; +} + void FakePasswordStoreBackend::InitBackend( AffiliatedMatchHelper* affiliated_match_helper, RemoteChangesReceived remote_form_changes_received, @@ -110,11 +115,17 @@ } void FakePasswordStoreBackend::GetAllLoginsAsync(LoginsOrErrorReply callback) { - GetTaskRunner()->PostTaskAndReplyWithResult( - FROM_HERE, - base::BindOnce(&FakePasswordStoreBackend::GetAllLoginsInternal, - base::Unretained(this)), - std::move(callback)); + if (password_store_backend_error_.has_value()) { + GetTaskRunner()->PostTask( + FROM_HERE, base::BindOnce(std::move(callback), + password_store_backend_error_.value())); + } else { + GetTaskRunner()->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce(&FakePasswordStoreBackend::GetAllLoginsInternal, + base::Unretained(this)), + std::move(callback)); + } } void FakePasswordStoreBackend::GetAllLoginsWithAffiliationAndBrandingAsync(
diff --git a/components/password_manager/core/browser/password_store/fake_password_store_backend.h b/components/password_manager/core/browser/password_store/fake_password_store_backend.h index 08c2de6..03ffcd2 100644 --- a/components/password_manager/core/browser/password_store/fake_password_store_backend.h +++ b/components/password_manager/core/browser/password_store/fake_password_store_backend.h
@@ -53,6 +53,8 @@ void Clear(); void TriggerOnLoginsRetainedForAndroid( const std::vector<PasswordForm>& password_forms); + void ReturnErrorOnRequest( + PasswordStoreBackendError password_store_backend_error); const PasswordMap& stored_passwords() const { return stored_passwords_; } IsAccountStore is_account_store() const { return is_account_store_; } @@ -126,6 +128,7 @@ PasswordMap stored_passwords_; PasswordStoreBackend::RemoteChangesReceived remote_form_changes_received_; scoped_refptr<base::SequencedTaskRunner> task_runner_; + std::optional<PasswordStoreBackendError> password_store_backend_error_; base::WeakPtrFactory<FakePasswordStoreBackend> weak_ptr_factory_{this}; };
diff --git a/components/password_manager/core/browser/password_store/password_store_consumer.cc b/components/password_manager/core/browser/password_store/password_store_consumer.cc index 3b24076..2ba39c3 100644 --- a/components/password_manager/core/browser/password_store/password_store_consumer.cc +++ b/components/password_manager/core/browser/password_store/password_store_consumer.cc
@@ -11,16 +11,6 @@ namespace password_manager { -std::vector<std::unique_ptr<PasswordForm>> ConvertToUniquePtr( - std::vector<PasswordForm> forms) { - std::vector<std::unique_ptr<PasswordForm>> result; - result.reserve(forms.size()); - for (auto& form : forms) { - result.push_back(std::make_unique<PasswordForm>(std::move(form))); - } - return result; -} - PasswordStoreConsumer::PasswordStoreConsumer() = default; PasswordStoreConsumer::~PasswordStoreConsumer() = default; @@ -35,8 +25,9 @@ PasswordStoreInterface* store, LoginsResultOrError results_or_error) { OnGetPasswordStoreResultsFrom( - store, ConvertToUniquePtr(password_manager::GetLoginsOrEmptyListOnFailure( - std::move(results_or_error)))); + store, password_manager::ConvertPasswordToUniquePtr( + password_manager::GetLoginsOrEmptyListOnFailure( + std::move(results_or_error)))); } void PasswordStoreConsumer::OnGetPasswordStoreResults(
diff --git a/components/password_manager/core/browser/password_store/password_store_util.cc b/components/password_manager/core/browser/password_store/password_store_util.cc index c9bc27e..0eac4e6 100644 --- a/components/password_manager/core/browser/password_store/password_store_util.cc +++ b/components/password_manager/core/browser/password_store/password_store_util.cc
@@ -41,4 +41,14 @@ return std::move(std::get<PasswordChanges>(result)); } +std::vector<std::unique_ptr<PasswordForm>> ConvertPasswordToUniquePtr( + std::vector<PasswordForm> forms) { + std::vector<std::unique_ptr<PasswordForm>> result; + result.reserve(forms.size()); + for (auto& form : forms) { + result.push_back(std::make_unique<PasswordForm>(std::move(form))); + } + return result; +} + } // namespace password_manager
diff --git a/components/password_manager/core/browser/password_store/password_store_util.h b/components/password_manager/core/browser/password_store/password_store_util.h index 50ea5b4..eaf7a03 100644 --- a/components/password_manager/core/browser/password_store/password_store_util.h +++ b/components/password_manager/core/browser/password_store/password_store_util.h
@@ -27,6 +27,10 @@ PasswordChanges GetPasswordChangesOrNulloptOnFailure( PasswordChangesOrError result); +// Wraps all password forms in the provided vector in a unique pointer. +std::vector<std::unique_ptr<PasswordForm>> ConvertPasswordToUniquePtr( + std::vector<PasswordForm> forms); + } // namespace password_manager #endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_STORE_PASSWORD_STORE_UTIL_H_
diff --git a/components/password_manager/core/browser/password_store/test_password_store.cc b/components/password_manager/core/browser/password_store/test_password_store.cc index 60fe580..c5b56f8 100644 --- a/components/password_manager/core/browser/password_store/test_password_store.cc +++ b/components/password_manager/core/browser/password_store/test_password_store.cc
@@ -60,6 +60,11 @@ fake_backend()->TriggerOnLoginsRetainedForAndroid(password_forms); } +void TestPasswordStore::ReturnErrorOnRequest( + PasswordStoreBackendError password_store_backend_error) { + fake_backend()->ReturnErrorOnRequest(password_store_backend_error); +} + TestPasswordStore::~TestPasswordStore() = default; FakePasswordStoreBackend* TestPasswordStore::fake_backend() {
diff --git a/components/password_manager/core/browser/password_store/test_password_store.h b/components/password_manager/core/browser/password_store/test_password_store.h index 1c2a0d8..a529e77 100644 --- a/components/password_manager/core/browser/password_store/test_password_store.h +++ b/components/password_manager/core/browser/password_store/test_password_store.h
@@ -60,6 +60,9 @@ void TriggerOnLoginsRetainedForAndroid( const std::vector<PasswordForm>& password_forms); + void ReturnErrorOnRequest( + PasswordStoreBackendError password_store_backend_error); + protected: ~TestPasswordStore() override;
diff --git a/components/password_manager/core/browser/store_metrics_reporter.cc b/components/password_manager/core/browser/store_metrics_reporter.cc index cb97d985..8d080e8 100644 --- a/components/password_manager/core/browser/store_metrics_reporter.cc +++ b/components/password_manager/core/browser/store_metrics_reporter.cc
@@ -53,6 +53,7 @@ constexpr char kImportedViaCredentialExchangeSuffix[] = ".ImportedViaCredentialExchange"; constexpr char kOverallSuffix[] = ".Overall"; +constexpr char kExcludingStoreErrorsSuffix[] = ".ExcludingStoreErrors"; // Need to stay in sync with the CustomPassphraseStatus variant in // histograms.xml. @@ -118,14 +119,13 @@ base::UmaHistogramCustomCounts(name, sample, 0, 100, 10); } -int ReportNumberOfAccountsMetrics( - bool is_account_store, - bool custom_passphrase_enabled, - const std::vector<std::unique_ptr<PasswordForm>>& forms) { +int ReportNumberOfAccountsMetrics(bool is_account_store, + bool custom_passphrase_enabled, + const PasswordStoreResults& results) { base::flat_map<std::tuple<std::string, PasswordForm::Type, int>, int> accounts_per_site_map; - for (const auto& form : forms) { + for (const auto& form : results.store_results) { accounts_per_site_map[{form->signon_realm, form->type, form->blocked_by_user}]++; } @@ -229,6 +229,14 @@ kOverallSuffix}), total_accounts); + if (!results.has_error) { + LogAccountStatHiRes( + base::StrCat({kPasswordManager, store_suffix, + kTotalAccountsByTypeSuffix, kOverallSuffix, + kExcludingStoreErrorsSuffix}), + total_accounts); + } + LogAccountStatHiRes( base::StrCat({kPasswordManager, store_suffix, ".BlacklistedSitesHiRes3", custom_passphrase_suffix}), @@ -497,9 +505,12 @@ bool custom_passphrase_enabled, const std::string& sync_username, bool is_safe_browsing_enabled, - std::vector<std::unique_ptr<PasswordForm>> results) { + PasswordStoreResults password_store_results) { + std::vector<std::unique_ptr<PasswordForm>>& results = + password_store_results.store_results; + int total_accounts = ReportNumberOfAccountsMetrics( - is_account_store, custom_passphrase_enabled, results); + is_account_store, custom_passphrase_enabled, password_store_results); ReportLoginsWithSchemesMetrics(is_account_store, results); ReportTimesPasswordUsedMetrics(is_account_store, custom_passphrase_enabled, results); @@ -612,10 +623,8 @@ const std::string& sync_username, bool is_account_storage_enabled, bool is_safe_browsing_enabled, - std::optional<std::vector<std::unique_ptr<PasswordForm>>> - profile_store_results, - std::optional<std::vector<std::unique_ptr<PasswordForm>>> - account_store_results) { + std::optional<PasswordStoreResults> profile_store_results, + std::optional<PasswordStoreResults> account_store_results) { // Maps from (signon_realm, username) to password. std::unique_ptr< std::map<std::pair<std::string, std::u16string>, std::u16string>> @@ -628,7 +637,7 @@ profile_store_passwords_per_signon_and_username = std::make_unique< std::map<std::pair<std::string, std::u16string>, std::u16string>>(); for (const std::unique_ptr<PasswordForm>& form : - profile_store_results.value()) { + profile_store_results.value().store_results) { profile_store_passwords_per_signon_and_username->insert(std::make_pair( std::make_pair(form->signon_realm, form->username_value), form->password_value)); @@ -639,7 +648,7 @@ account_store_passwords_per_signon_and_username = std::make_unique< std::map<std::pair<std::string, std::u16string>, std::u16string>>(); for (const std::unique_ptr<PasswordForm>& form : - account_store_results.value()) { + account_store_results.value().store_results) { account_store_passwords_per_signon_and_username->insert(std::make_pair( std::make_pair(form->signon_realm, form->username_value), form->password_value)); @@ -696,6 +705,16 @@ } // namespace +PasswordStoreResults::PasswordStoreResults( + std::vector<std::unique_ptr<PasswordForm>> store_results, + bool has_error) + : store_results(std::move(store_results)), has_error(has_error) {} +PasswordStoreResults::~PasswordStoreResults() = default; +PasswordStoreResults::PasswordStoreResults(PasswordStoreResults&& other) = + default; +PasswordStoreResults& PasswordStoreResults::operator=( + PasswordStoreResults&& other) = default; + StoreMetricsReporter::StoreMetricsReporter( PasswordStoreInterface* profile_store, PasswordStoreInterface* account_store, @@ -786,6 +805,27 @@ void StoreMetricsReporter::OnGetPasswordStoreResultsFrom( PasswordStoreInterface* store, std::vector<std::unique_ptr<PasswordForm>> results) { + // This class overrides OnGetPasswordStoreResultsOrErrorFrom() (the version + // that also receives the error case), so the plain password form version + // never gets called. + NOTREACHED(); +} + +void StoreMetricsReporter::OnGetPasswordStoreResultsOrErrorFrom( + PasswordStoreInterface* store, + LoginsResultOrError results_or_error) { + PasswordStoreResults password_store_results{ + password_manager::ConvertPasswordToUniquePtr( + password_manager::GetLoginsOrEmptyListOnFailure( + std::move(results_or_error))), + std::holds_alternative<PasswordStoreBackendError>(results_or_error)}; + + ProcessPasswordResults(store, std::move(password_store_results)); +} + +void StoreMetricsReporter::ProcessPasswordResults( + PasswordStoreInterface* store, + PasswordStoreResults results) { if (store == account_store_) { account_store_results_ = std::move(results); } else {
diff --git a/components/password_manager/core/browser/store_metrics_reporter.h b/components/password_manager/core/browser/store_metrics_reporter.h index 79cc435..04e3412 100644 --- a/components/password_manager/core/browser/store_metrics_reporter.h +++ b/components/password_manager/core/browser/store_metrics_reporter.h
@@ -23,6 +23,16 @@ class PasswordManagerSettingsService; class PasswordReuseManager; +struct PasswordStoreResults { + PasswordStoreResults(std::vector<std::unique_ptr<PasswordForm>> store_results, + bool has_error); + ~PasswordStoreResults(); + PasswordStoreResults(PasswordStoreResults&& other); + PasswordStoreResults& operator=(PasswordStoreResults&& other); + + std::vector<std::unique_ptr<PasswordForm>> store_results; + bool has_error; +}; // Instantiate this object to report metrics about the contents of the password // store. @@ -60,6 +70,11 @@ void OnGetPasswordStoreResultsFrom( PasswordStoreInterface* store, std::vector<std::unique_ptr<PasswordForm>> results) override; + void OnGetPasswordStoreResultsOrErrorFrom( + PasswordStoreInterface* store, + LoginsResultOrError results_or_error) override; + void ProcessPasswordResults(PasswordStoreInterface* store, + PasswordStoreResults results); void OnBackgroundMetricsReportingCompleted( CredentialsCount credentials_count); @@ -82,10 +97,9 @@ // Temporarily holds the credentials stored in the profile and account stores // till the actual metric computation starts. They don't have a value until // the credentials are loaded from the storage. - std::optional<std::vector<std::unique_ptr<PasswordForm>>> - profile_store_results_; - std::optional<std::vector<std::unique_ptr<PasswordForm>>> - account_store_results_; + std::optional<PasswordStoreResults> profile_store_results_; + + std::optional<PasswordStoreResults> account_store_results_; base::OnceClosure done_callback_; base::WeakPtrFactory<StoreMetricsReporter> weak_ptr_factory_{this};
diff --git a/components/password_manager/core/browser/store_metrics_reporter_unittest.cc b/components/password_manager/core/browser/store_metrics_reporter_unittest.cc index 36d01d2..f437adb 100644 --- a/components/password_manager/core/browser/store_metrics_reporter_unittest.cc +++ b/components/password_manager/core/browser/store_metrics_reporter_unittest.cc
@@ -23,6 +23,7 @@ #include "components/password_manager/core/browser/password_manager_metrics_util.h" #include "components/password_manager/core/browser/password_manager_util.h" #include "components/password_manager/core/browser/password_store/mock_password_store_interface.h" +#include "components/password_manager/core/browser/password_store/password_store_consumer.h" #include "components/password_manager/core/browser/password_store/test_password_store.h" #include "components/password_manager/core/browser/stub_password_manager_client.h" #include "components/password_manager/core/browser/sync_username_test_base.h" @@ -463,6 +464,84 @@ RunUntilIdle(); } +TEST_F(StoreMetricsReporterTest, + PasswordStoreErrorNotReportedToExcludingStoreErrorsForProfile) { + auto profile_store = + base::MakeRefCounted<TestPasswordStore>(IsAccountStore(false)); + profile_store->Init(&prefs_, /*affiliated_match_helper=*/nullptr); + AddMetricsTestData(profile_store.get()); + profile_store->ReturnErrorOnRequest( + PasswordStoreBackendError(PasswordStoreBackendErrorType::kUncategorized)); + + auto account_store = + base::MakeRefCounted<TestPasswordStore>(IsAccountStore(true)); + account_store->Init(&prefs_, /*affiliated_match_helper=*/nullptr); + AddMetricsTestData(account_store.get()); + + base::HistogramTester histogram_tester; + StoreMetricsReporter reporter( + profile_store.get(), account_store.get(), sync_service(), &prefs_, + /*password_reuse_manager=*/nullptr, &settings_service(), + /*done_callback*/ base::DoNothing()); + // Wait for the metrics to get reported, which involves queries to the + // stores, i.e. to background task runners. + RunUntilIdle(); + + histogram_tester.ExpectTotalCount( + "PasswordManager.ProfileStore.TotalAccountsHiRes3." + "ByType.Overall.ExcludingStoreErrors", + 0); + histogram_tester.ExpectTotalCount( + "PasswordManager.AccountStore.TotalAccountsHiRes3." + "ByType.Overall.ExcludingStoreErrors", + 1); + + account_store->ShutdownOnUIThread(); + profile_store->ShutdownOnUIThread(); + // Make sure the PasswordStore destruction parts on the background sequence + // finish, otherwise we get memory leak reports. + RunUntilIdle(); +} + +TEST_F(StoreMetricsReporterTest, + PasswordStoreErrorNotReportedToExcludingStoreErrorsForAccount) { + auto profile_store = + base::MakeRefCounted<TestPasswordStore>(IsAccountStore(false)); + profile_store->Init(&prefs_, /*affiliated_match_helper=*/nullptr); + AddMetricsTestData(profile_store.get()); + + auto account_store = + base::MakeRefCounted<TestPasswordStore>(IsAccountStore(true)); + account_store->Init(&prefs_, /*affiliated_match_helper=*/nullptr); + AddMetricsTestData(account_store.get()); + account_store->ReturnErrorOnRequest( + PasswordStoreBackendError(PasswordStoreBackendErrorType::kUncategorized)); + + base::HistogramTester histogram_tester; + StoreMetricsReporter reporter( + profile_store.get(), account_store.get(), sync_service(), &prefs_, + /*password_reuse_manager=*/nullptr, &settings_service(), + /*done_callback*/ base::DoNothing()); + // Wait for the metrics to get reported, which involves queries to the + // stores, i.e. to background task runners. + RunUntilIdle(); + + histogram_tester.ExpectTotalCount( + "PasswordManager.ProfileStore.TotalAccountsHiRes3." + "ByType.Overall.ExcludingStoreErrors", + 1); + histogram_tester.ExpectTotalCount( + "PasswordManager.AccountStore.TotalAccountsHiRes3." + "ByType.Overall.ExcludingStoreErrors", + 0); + + account_store->ShutdownOnUIThread(); + profile_store->ShutdownOnUIThread(); + // Make sure the PasswordStore destruction parts on the background sequence + // finish, otherwise we get memory leak reports. + RunUntilIdle(); +} + TEST_F(StoreMetricsReporterTest, ReportAccountsPerSiteHiResMetricsTest) { auto profile_store = base::MakeRefCounted<TestPasswordStore>(IsAccountStore(false)); @@ -742,6 +821,11 @@ histogram_tester.ExpectUniqueSample( "PasswordManager.ProfileStore.TotalAccountsHiRes3." + "ByType.Overall.ExcludingStoreErrors", + 11, 1); + + histogram_tester.ExpectUniqueSample( + "PasswordManager.ProfileStore.TotalAccountsHiRes3." "ByType.Overall", 11, 1); @@ -1033,6 +1117,11 @@ histogram_tester.ExpectUniqueSample( "PasswordManager.AccountStore.TotalAccountsHiRes3." + "ByType.Overall.ExcludingStoreErrors", + 11, 1); + + histogram_tester.ExpectUniqueSample( + "PasswordManager.AccountStore.TotalAccountsHiRes3." "ByType.Overall", 11, 1);
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestParams.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestParams.java index 849f295..e35c0be 100644 --- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestParams.java +++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestParams.java
@@ -4,6 +4,7 @@ package org.chromium.components.payments; +import org.chromium.build.annotations.MockedInTests; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.payments.mojom.PaymentDetailsModifier; @@ -14,6 +15,7 @@ import java.util.Map; /** The parameters of PaymentRequest specified by the merchant. */ +@MockedInTests // Tell R8 not to break the ability to mock the class. @NullMarked public interface PaymentRequestParams { /**
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java index 29e61a7..74f50cc 100644 --- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java +++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentRequestService.java
@@ -730,12 +730,6 @@ case MethodStrings.SECURE_PAYMENT_CONFIRMATION: methodTypes.add(PaymentMethodCategory.SECURE_PAYMENT_CONFIRMATION); break; - case MethodStrings.BASIC_CARD: - // Not to record requestedMethodBasicCard because JourneyLogger ignore the case - // where the specified networks are unsupported. - // mPaymentUiService.merchantSupportsAutofillCards() better captures this group - // of interest than requestedMethodBasicCard. - break; default: // "Other" includes https url, http url(when certificate check is bypassed) and // the unlisted methods defined in {@link MethodStrings}.
diff --git a/components/payments/core/method_strings.cc b/components/payments/core/method_strings.cc index aada0456..c3efbe77 100644 --- a/components/payments/core/method_strings.cc +++ b/components/payments/core/method_strings.cc
@@ -10,7 +10,6 @@ // Please keep the list alphabetized. const char kAndroidPay[] = "https://android.com/pay"; -const char kBasicCard[] = "basic-card"; const char kGooglePay[] = "https://google.com/pay"; const char kGooglePayAuthentication[] = "https://pay.google.com/authentication"; const char kGooglePlayBilling[] = "https://play.google.com/billing";
diff --git a/components/payments/core/method_strings.h b/components/payments/core/method_strings.h index 647cc1d6..7b4d68e 100644 --- a/components/payments/core/method_strings.h +++ b/components/payments/core/method_strings.h
@@ -18,9 +18,6 @@ // Android Pay method name. extern const char kAndroidPay[]; -// Basic Card method name. https://w3c.github.io/payment-method-basic-card/ -extern const char kBasicCard[]; - // Google Pay method name. // https://developers.google.com/pay/api/web/guides/tutorial extern const char kGooglePay[];
diff --git a/components/policy/core/browser/configuration_policy_handler.cc b/components/policy/core/browser/configuration_policy_handler.cc index bc5d5c6..de950d7 100644 --- a/components/policy/core/browser/configuration_policy_handler.cc +++ b/components/policy/core/browser/configuration_policy_handler.cc
@@ -828,6 +828,54 @@ NOTREACHED(); } +// SingleDeprecatedPolicyToMultipleNewPolicyHandler implementation +// ----------------------- + +SingleDeprecatedPolicyToMultipleNewPolicyHandler:: + SingleDeprecatedPolicyToMultipleNewPolicyHandler( + std::unique_ptr<NamedPolicyHandler> legacy_policy_handler, + std::vector<std::string> new_policy_names) + : legacy_policy_handler_(std::move(legacy_policy_handler)), + new_policy_names_(std::move(new_policy_names)) {} + +SingleDeprecatedPolicyToMultipleNewPolicyHandler:: + ~SingleDeprecatedPolicyToMultipleNewPolicyHandler() = default; + +// ConfigurationPolicyHandler: +bool SingleDeprecatedPolicyToMultipleNewPolicyHandler::CheckPolicySettings( + const PolicyMap& policies, + PolicyErrorMap* errors) { + bool new_policy_set = false; + for (const auto& new_policy_name : new_policy_names_) { + if (!policies.Get(new_policy_name)) { + continue; + } + new_policy_set = true; + if (errors && policies.Get(legacy_policy_handler_->policy_name())) { + errors->AddError(legacy_policy_handler_->policy_name(), + IDS_POLICY_OVERRIDDEN, new_policy_name); + } + } + + // If none of the policies is set, fall back to legacy one. + return !new_policy_set && + legacy_policy_handler_->CheckPolicySettings(policies, errors); +} + +void SingleDeprecatedPolicyToMultipleNewPolicyHandler:: + ApplyPolicySettingsWithParameters(const PolicyMap& policies, + const PolicyHandlerParameters& parameters, + PrefValueMap* prefs) { + legacy_policy_handler_->ApplyPolicySettingsWithParameters(policies, + parameters, prefs); +} + +void SingleDeprecatedPolicyToMultipleNewPolicyHandler::ApplyPolicySettings( + const policy::PolicyMap& /* policies */, + PrefValueMap* /* prefs */) { + NOTREACHED(); +} + // CloudOnlyPolicyHandler implementation --------------------------------------- namespace {
diff --git a/components/policy/core/browser/configuration_policy_handler.h b/components/policy/core/browser/configuration_policy_handler.h index 69e20fe..612788d 100644 --- a/components/policy/core/browser/configuration_policy_handler.h +++ b/components/policy/core/browser/configuration_policy_handler.h
@@ -589,6 +589,38 @@ std::unique_ptr<NamedPolicyHandler> new_policy_handler_; }; +// A policy handler that applies a deprecated policy only if none of the new +// ones has been set. The new policies need their own handlers. +class POLICY_EXPORT SingleDeprecatedPolicyToMultipleNewPolicyHandler + : public ConfigurationPolicyHandler { + public: + SingleDeprecatedPolicyToMultipleNewPolicyHandler( + std::unique_ptr<NamedPolicyHandler> legacy_policy_handler, + std::vector<std::string> new_policy_names); + SingleDeprecatedPolicyToMultipleNewPolicyHandler( + const SingleDeprecatedPolicyToMultipleNewPolicyHandler&) = delete; + SingleDeprecatedPolicyToMultipleNewPolicyHandler& operator=( + const SingleDeprecatedPolicyToMultipleNewPolicyHandler&) = delete; + ~SingleDeprecatedPolicyToMultipleNewPolicyHandler() override; + + // ConfigurationPolicyHandler: + bool CheckPolicySettings(const PolicyMap& policies, + PolicyErrorMap* errors) override; + + void ApplyPolicySettingsWithParameters( + const PolicyMap& policies, + const PolicyHandlerParameters& parameters, + PrefValueMap* prefs) override; + + protected: + void ApplyPolicySettings(const PolicyMap& policies, + PrefValueMap* prefs) override; + + private: + std::unique_ptr<NamedPolicyHandler> legacy_policy_handler_; + std::vector<std::string> new_policy_names_; +}; + // A schema policy handler for complex policies that only accept cloud sources. class POLICY_EXPORT CloudOnlyPolicyHandler : public SchemaValidatingPolicyHandler {
diff --git a/components/safe_browsing/core/common/features.cc b/components/safe_browsing/core/common/features.cc index f1a9b942..a7826f2 100644 --- a/components/safe_browsing/core/common/features.cc +++ b/components/safe_browsing/core/common/features.cc
@@ -347,6 +347,12 @@ &kSafetyHubDisruptiveNotificationRevocation, /*name=*/"max_engagement_score", /*default_value=*/0.0}; +constexpr base::FeatureParam<int> + kSafetyHubDisruptiveNotificationRevocationNotificationTimeoutSeconds{ + &kSafetyHubDisruptiveNotificationRevocation, + /*name=*/"notification_timeout_seconds", + /*default_value=*/7 * 24 * 3600}; + BASE_FEATURE(kSavePasswordHashFromProfilePicker, "SavePasswordHashFromProfilePicker", base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/components/safe_browsing/core/common/features.h b/components/safe_browsing/core/common/features.h index c9936d8..ca7d519e 100644 --- a/components/safe_browsing/core/common/features.h +++ b/components/safe_browsing/core/common/features.h
@@ -337,6 +337,11 @@ extern const base::FeatureParam<double> kSafetyHubDisruptiveNotificationRevocationMaxEngagementScore; +// Timeout in seconds for the Safety Hub OS notification informing users about +// revoked notification permissions. +extern const base::FeatureParam<int> + kSafetyHubDisruptiveNotificationRevocationNotificationTimeoutSeconds; + // Enables saving gaia password hash from the Profile Picker sign-in flow. BASE_DECLARE_FEATURE(kSavePasswordHashFromProfilePicker);
diff --git a/components/search_engines/android/java/src/org/chromium/components/search_engines/TemplateUrl.java b/components/search_engines/android/java/src/org/chromium/components/search_engines/TemplateUrl.java index 639170d3..31ee2a8 100644 --- a/components/search_engines/android/java/src/org/chromium/components/search_engines/TemplateUrl.java +++ b/components/search_engines/android/java/src/org/chromium/components/search_engines/TemplateUrl.java
@@ -6,6 +6,7 @@ import org.jni_zero.CalledByNative; import org.jni_zero.NativeMethods; +import org.chromium.build.annotations.MockedInTests; import org.chromium.build.annotations.NullMarked; import java.util.Locale; @@ -15,6 +16,7 @@ * from native side. Any class uses this need to register a {@link TemplateUrlServiceObserver} on * {@link TemplatUrlService} to listen the native changes in case the native pointer is destroyed. */ +@MockedInTests @NullMarked public class TemplateUrl { private final long mTemplateUrlPtr;
diff --git a/components/signin/public/base/signin_switches.cc b/components/signin/public/base/signin_switches.cc index e83ce40..f435a93 100644 --- a/components/signin/public/base/signin_switches.cc +++ b/components/signin/public/base/signin_switches.cc
@@ -187,14 +187,6 @@ "InterceptBubblesDismissibleByAvatarButton", base::FEATURE_ENABLED_BY_DEFAULT); -BASE_FEATURE(kImprovedSigninUIOnDesktop, - "ImprovedSigninUIOnDesktop", - base::FEATURE_ENABLED_BY_DEFAULT); - -bool IsImprovedSigninUIOnDesktopEnabled() { - return base::FeatureList::IsEnabled(kImprovedSigninUIOnDesktop); -} - BASE_FEATURE(kImprovedSettingsUIOnDesktop, "ImprovedSettingsUIOnDesktop", #if BUILDFLAG(IS_CHROMEOS) @@ -283,10 +275,6 @@ "ProfilesReordering", base::FEATURE_DISABLED_BY_DEFAULT); -BASE_FEATURE(kOutlineSilhouetteIcon, - "OutlineSilhouetteIcon", - base::FEATURE_ENABLED_BY_DEFAULT); - #if BUILDFLAG(IS_ANDROID) BASE_FEATURE(kIgnoreMirrorHeadersInBackgoundTabs, "IgnoreMirrorHeadersInBackgoundTabs",
diff --git a/components/signin/public/base/signin_switches.h b/components/signin/public/base/signin_switches.h index c99ef15..86e50143d 100644 --- a/components/signin/public/base/signin_switches.h +++ b/components/signin/public/base/signin_switches.h
@@ -136,12 +136,6 @@ BASE_DECLARE_FEATURE(kInterceptBubblesDismissibleByAvatarButton); COMPONENT_EXPORT(SIGNIN_SWITCHES) -BASE_DECLARE_FEATURE(kImprovedSigninUIOnDesktop); - -COMPONENT_EXPORT(SIGNIN_SWITCHES) -bool IsImprovedSigninUIOnDesktopEnabled(); - -COMPONENT_EXPORT(SIGNIN_SWITCHES) BASE_DECLARE_FEATURE(kImprovedSettingsUIOnDesktop); COMPONENT_EXPORT(SIGNIN_SWITCHES) @@ -214,9 +208,6 @@ COMPONENT_EXPORT(SIGNIN_SWITCHES) BASE_DECLARE_FEATURE(kProfilesReordering); -COMPONENT_EXPORT(SIGNIN_SWITCHES) -BASE_DECLARE_FEATURE(kOutlineSilhouetteIcon); - #if BUILDFLAG(IS_ANDROID) COMPONENT_EXPORT(SIGNIN_SWITCHES) BASE_DECLARE_FEATURE(kIgnoreMirrorHeadersInBackgoundTabs);
diff --git a/components/supervised_user/core/browser/family_link_user_log_record.cc b/components/supervised_user/core/browser/family_link_user_log_record.cc index 926641d4..919e05f 100644 --- a/components/supervised_user/core/browser/family_link_user_log_record.cc +++ b/components/supervised_user/core/browser/family_link_user_log_record.cc
@@ -133,8 +133,7 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) bool SupervisedUserCanSkipExtensionParentApprovals( const PrefService& pref_service) { - return IsSupervisedUserSkipParentApprovalToInstallExtensionsEnabled() && - pref_service.GetBoolean(prefs::kSkipParentApprovalToInstallExtensions); + return pref_service.GetBoolean(prefs::kSkipParentApprovalToInstallExtensions); } #endif // BUILDFLAG(ENABLE_EXTENSIONS) @@ -142,8 +141,7 @@ std::optional<FamilyLinkUserLogRecord::Segment> supervision_status, const PrefService& pref_service) { #if BUILDFLAG(ENABLE_EXTENSIONS) - if (IsUnsupervisedStatus(supervision_status) || - !IsSupervisedUserSkipParentApprovalToInstallExtensionsEnabled()) { + if (IsUnsupervisedStatus(supervision_status)) { return std::nullopt; }
diff --git a/components/supervised_user/core/common/features.cc b/components/supervised_user/core/common/features.cc index e627789..5f288d4 100644 --- a/components/supervised_user/core/common/features.cc +++ b/components/supervised_user/core/common/features.cc
@@ -89,49 +89,6 @@ return base::FeatureList::IsEnabled(kAllowSubframeLocalWebApprovals); } -BASE_FEATURE(kEnableSupervisedUserSkipParentApprovalToInstallExtensions, - "EnableSupervisedUserSkipParentApprovalToInstallExtensions", - base::FEATURE_ENABLED_BY_DEFAULT -); - -BASE_FEATURE(kUpdatedSupervisedUserExtensionApprovalStrings, - "UpdatedSupervisedUserExtensionApprovalStrings", - base::FEATURE_ENABLED_BY_DEFAULT); - -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || \ - BUILDFLAG(IS_DESKTOP_ANDROID) -BASE_FEATURE(kEnableExtensionsPermissionsForSupervisedUsersOnDesktop, - "EnableExtensionsPermissionsForSupervisedUsersOnDesktop", - base::FEATURE_ENABLED_BY_DEFAULT); -#endif - -#if BUILDFLAG(ENABLE_EXTENSIONS_CORE) -BASE_FEATURE(kExposedParentalControlNeededForExtensionInstallation, - "ExposedParentalControlNeededForExtensionInstallation", - base::FEATURE_ENABLED_BY_DEFAULT); - -bool IsSupervisedUserSkipParentApprovalToInstallExtensionsEnabled() { -#if BUILDFLAG(IS_CHROMEOS) - return base::FeatureList::IsEnabled( - kEnableSupervisedUserSkipParentApprovalToInstallExtensions); -#elif BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || \ - BUILDFLAG(IS_DESKTOP_ANDROID) - bool skipParentApprovalEnabled = base::FeatureList::IsEnabled( - kEnableSupervisedUserSkipParentApprovalToInstallExtensions); - bool permissionExtensionsForSupervisedUsersEnabled = - base::FeatureList::IsEnabled( - kEnableExtensionsPermissionsForSupervisedUsersOnDesktop); - if (skipParentApprovalEnabled) { - DCHECK(permissionExtensionsForSupervisedUsersEnabled); - } - return skipParentApprovalEnabled && - permissionExtensionsForSupervisedUsersEnabled; -#else - NOTREACHED(); -#endif // BUILDFLAG(IS_CHROMEOS) -} -#endif // BUILDFLAG(ENABLE_EXTENSIONS_CORE) - #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) BASE_FEATURE(kCustomProfileStringsForSupervisedUsers, "CustomProfileStringsForSupervisedUsers",
diff --git a/components/supervised_user/core/common/features.h b/components/supervised_user/core/common/features.h index 569ebae..b57c6fa 100644 --- a/components/supervised_user/core/common/features.h +++ b/components/supervised_user/core/common/features.h
@@ -37,33 +37,6 @@ // Whether supervised users see an updated URL filter interstitial. BASE_DECLARE_FEATURE(kSupervisedUserBlockInterstitialV3); -// Applies the updated extension approval flow, which can skip parent-approvals -// on extension installations. -BASE_DECLARE_FEATURE( - kEnableSupervisedUserSkipParentApprovalToInstallExtensions); - -// Applies new informative strings during the parental extension approval flow. -BASE_DECLARE_FEATURE(kUpdatedSupervisedUserExtensionApprovalStrings); - -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || \ - BUILDFLAG(IS_DESKTOP_ANDROID) -BASE_DECLARE_FEATURE(kEnableExtensionsPermissionsForSupervisedUsersOnDesktop); -#endif - -#if BUILDFLAG(ENABLE_EXTENSIONS_CORE) -// Returns whether a new installation state for supervised users -// on new extension installations is offered to the Webstore. -BASE_DECLARE_FEATURE(kExposedParentalControlNeededForExtensionInstallation); - -// Returns whether the new mode for extension approval management is enabled. -// Under this mode, supervised users may request parent approval on each -// extension installation or the parent allows and approves by default all -// extension installations. -// On Win/Linux/Mac enabling the new mode requires that the feature -// `kEnableExtensionsPermissionsForSupervisedUsersOnDesktop` is also enabled. -bool IsSupervisedUserSkipParentApprovalToInstallExtensionsEnabled(); -#endif // BUILDFLAG(ENABLE_EXTENSIONS_CORE) - #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) // Enable different web sign in interception behaviour for supervised users: //
diff --git a/components/sync_bookmarks/bookmark_model_merger_comparison_metrics.cc b/components/sync_bookmarks/bookmark_model_merger_comparison_metrics.cc index 4758fb7..7ef9ae6 100644 --- a/components/sync_bookmarks/bookmark_model_merger_comparison_metrics.cc +++ b/components/sync_bookmarks/bookmark_model_merger_comparison_metrics.cc
@@ -66,12 +66,16 @@ std::string_view GroupingKeyInfixToString(GroupingKeyInfix value) { // LINT.IfChange(BookmarkComparisonGroupingKey) switch (value) { + case GroupingKeyInfix::kByUrl: + return "ByUrl"; case GroupingKeyInfix::kByUrlAndTitle: return "ByUrlAndTitle"; case GroupingKeyInfix::kByUrlAndUuid: return "ByUrlAndUuid"; case GroupingKeyInfix::kByUrlAndTitleAndPath: return "ByUrlAndTitleAndPath"; + case GroupingKeyInfix::kByUrlAndTitleAndPathAndUuid: + return "ByUrlAndTitleAndPathAndUuid"; } // LINT.ThenChange(/tools/metrics/histograms/metadata/sync/histograms.xml:BookmarkComparisonGroupingKey) NOTREACHED(); @@ -81,13 +85,13 @@ // LINT.IfChange(BookmarkComparisonBookmarkCount) switch (value) { case kZeroLocalUrlBookmarks: - return "ZeroLocalUrlBookmarks"; + return ".ZeroLocalUrlBookmarks"; case kBetween1And19LocalUrlBookmarks: - return "Between1And19LocalUrlBookmarks"; + return ".Between1And19LocalUrlBookmarks"; case kBetween20and999LocalUrlBookmarks: - return "Between20and999LocalUrlBookmarks"; + return ".Between20and999LocalUrlBookmarks"; case k1000OrMoreLocalUrlBookmarks: - return "1000OrMoreLocalUrlBookmarks"; + return ".1000OrMoreLocalUrlBookmarks"; } // LINT.ThenChange(/tools/metrics/histograms/metadata/sync/histograms.xml:BookmarkComparisonBookmarkCount) NOTREACHED(); @@ -101,16 +105,16 @@ NOTREACHED(); case syncer::PreviouslySyncingGaiaIdInfoForMetrics:: kNotEnoughInformationToTell: - return "UnknownPreviousGaiaId"; + return ".UnknownPreviousGaiaId"; case syncer::PreviouslySyncingGaiaIdInfoForMetrics:: kSyncFeatureNeverPreviouslyTurnedOn: - return "NoPreviousGaiaId"; + return ".NoPreviousGaiaId"; case syncer::PreviouslySyncingGaiaIdInfoForMetrics:: kCurrentGaiaIdMatchesPreviousWithSyncFeatureOn: - return "MatchesPreviousGaiaId"; + return ".MatchesPreviousGaiaId"; case syncer::PreviouslySyncingGaiaIdInfoForMetrics:: kCurrentGaiaIdIfDiffersPreviousWithSyncFeatureOn: - return "DiffersPreviousGaiaId"; + return ".DiffersPreviousGaiaId"; } // LINT.ThenChange(/tools/metrics/histograms/metadata/sync/histograms.xml:BookmarkComparisonPreviouslySyncingGaiaId) NOTREACHED(); @@ -230,6 +234,24 @@ std::u16string path); template <> +UrlOnly GroupingKeyFromLocalData(const bookmarks::BookmarkNode* node, + std::u16string path) { + UrlOnly key; + key.url = node->url(); + // `path` ignored but required in signture for template code. + return key; +} + +template <> +UrlOnly GroupingKeyFromAccountData(const sync_pb::BookmarkSpecifics& specifics, + std::u16string path) { + UrlOnly key; + key.url = GURL(specifics.url()); + // `path` ignored but required in signture for template code. + return key; +} + +template <> UrlAndTitle GroupingKeyFromLocalData(const bookmarks::BookmarkNode* node, std::u16string path) { UrlAndTitle key; @@ -292,6 +314,30 @@ return key; } +template <> +UrlAndTitleAndPathAndUuid GroupingKeyFromLocalData( + const bookmarks::BookmarkNode* node, + std::u16string path) { + UrlAndTitleAndPathAndUuid key; + key.url = node->url(); + key.title = node->GetTitle(); + key.path = std::move(path); + key.uuid = node->uuid(); + return key; +} + +template <> +UrlAndTitleAndPathAndUuid GroupingKeyFromAccountData( + const sync_pb::BookmarkSpecifics& specifics, + std::u16string path) { + UrlAndTitleAndPathAndUuid key; + key.url = GURL(specifics.url()); + key.title = NodeTitleFromSpecifics(specifics); + key.path = std::move(path); + key.uuid = base::Uuid::ParseLowercase(specifics.guid()); + return key; +} + // Given two sets `set1` and `set2`, returns the number of values that exist in // both. template <typename Key> @@ -449,27 +495,63 @@ const base::flat_set<Key> account_data_set = ExtractAccountDataSet<Key>(relevant_account_subtrees); - const std::string bookmark_count_suffix_string = - base::StrCat({".", BookmarkCountSuffixToString(bookmark_count_suffix)}); + // When recording the metric, always record four metrics, resulting from + // the combinatorial cases for: + // 1. With and without the infix representing + // PreviouslySyncingGaiaIdInfoForMetrics. + // 2. With and without the suffix representing the number of local URL + // bookmarks. + for (std::string_view optional_previously_syncing_gaia_id_info_infix : + {std::string_view(), + PreviouslySyncingGaiaIdInfoToInfix(previously_syncing_gaia_id_info)}) { + for (std::string_view optional_bookmark_count_suffix : + {std::string_view(), + BookmarkCountSuffixToString(bookmark_count_suffix)}) { + const std::string histogram_name = + base::StrCat({"Sync.BookmarkModelMerger.Comparison", + optional_previously_syncing_gaia_id_info_infix, ".", + SubtreeSelectionToInfix(subtree_selection), ".", + GroupingKeyInfixToString(Key::kGroupingKeyInfix), + optional_bookmark_count_suffix}); - // When recording the metric, always record two metrics, with and without the - // suffix representing the number of local URL bookmarks. - for (std::string_view optional_bookmark_count_suffix_string : - {std::string(), bookmark_count_suffix_string}) { - const std::string histogram_name = base::StrCat( - {"Sync.BookmarkModelMerger.Comparison.", - PreviouslySyncingGaiaIdInfoToInfix(previously_syncing_gaia_id_info), - ".", SubtreeSelectionToInfix(subtree_selection), ".", - GroupingKeyInfixToString(Key::kGroupingKeyInfix), - optional_bookmark_count_suffix_string}); - - base::UmaHistogramEnumeration( - histogram_name, CompareSets(local_data_set, account_data_set)); + base::UmaHistogramEnumeration( + histogram_name, CompareSets(local_data_set, account_data_set)); + } } } } // namespace +UrlAndTitleAndPathAndUuid::UrlAndTitleAndPathAndUuid() = default; + +UrlAndTitleAndPathAndUuid::UrlAndTitleAndPathAndUuid( + const GURL& url, + const std::u16string& title, + const std::u16string& path, + const base::Uuid& uuid) + : url(url), title(title), path(path), uuid(uuid) {} + +UrlAndTitleAndPathAndUuid::UrlAndTitleAndPathAndUuid( + const UrlAndTitleAndPathAndUuid&) = default; + +UrlAndTitleAndPathAndUuid::UrlAndTitleAndPathAndUuid( + UrlAndTitleAndPathAndUuid&&) = default; + +UrlAndTitleAndPathAndUuid::~UrlAndTitleAndPathAndUuid() = default; + +UrlAndTitleAndPathAndUuid& UrlAndTitleAndPathAndUuid::operator=( + const UrlAndTitleAndPathAndUuid&) = default; + +UrlAndTitleAndPathAndUuid& UrlAndTitleAndPathAndUuid::operator=( + UrlAndTitleAndPathAndUuid&&) = default; + +base::flat_set<UrlOnly> ExtractUniqueLocalNodesByUrlForTesting( + const BookmarkModelView& all_local_data, + SubtreeSelection subtree_selection) { + return ExtractLocalDataSet<UrlOnly>( + GetRelevantLocalSubtrees(all_local_data, subtree_selection)); +} + base::flat_set<UrlAndTitle> ExtractUniqueLocalNodesByUrlAndTitleForTesting( const BookmarkModelView& all_local_data, SubtreeSelection subtree_selection) { @@ -492,6 +574,21 @@ GetRelevantLocalSubtrees(all_local_data, subtree_selection)); } +base::flat_set<UrlAndTitleAndPathAndUuid> +ExtractUniqueLocalNodesByUrlAndTitleAndPathAndUuidForTesting( + const BookmarkModelView& all_local_data, + SubtreeSelection subtree_selection) { + return ExtractLocalDataSet<UrlAndTitleAndPathAndUuid>( + GetRelevantLocalSubtrees(all_local_data, subtree_selection)); +} + +base::flat_set<UrlOnly> ExtractUniqueAccountNodesByUrlForTesting( + const BookmarkModelMerger::RemoteForest& all_account_data, + SubtreeSelection subtree_selection) { + return ExtractAccountDataSet<UrlOnly>( + GetRelevantAccountSubtrees(all_account_data, subtree_selection)); +} + base::flat_set<UrlAndTitle> ExtractUniqueAccountNodesByUrlAndTitleForTesting( const BookmarkModelMerger::RemoteForest& all_account_data, SubtreeSelection subtree_selection) { @@ -514,6 +611,14 @@ GetRelevantAccountSubtrees(all_account_data, subtree_selection)); } +base::flat_set<UrlAndTitleAndPathAndUuid> +ExtractUniqueAccountNodesByUrlAndTitleAndPathAndUuidForTesting( + const BookmarkModelMerger::RemoteForest& all_account_data, + SubtreeSelection subtree_selection) { + return ExtractAccountDataSet<UrlAndTitleAndPathAndUuid>( + GetRelevantAccountSubtrees(all_account_data, subtree_selection)); +} + SetComparisonOutcome CompareSetsForTesting( const base::flat_set<int>& account_data, const base::flat_set<int>& local_data) { @@ -539,6 +644,10 @@ const BookmarkCountSuffix bookmark_count_suffix = CountLocalBookmarks(relevant_local_subtrees); + CompareAndLogHistogramsWithKey<UrlOnly>( + subtree_selection, previously_syncing_gaia_id_info, + bookmark_count_suffix, relevant_local_subtrees, + relevant_account_subtrees); CompareAndLogHistogramsWithKey<UrlAndTitle>( subtree_selection, previously_syncing_gaia_id_info, bookmark_count_suffix, relevant_local_subtrees, @@ -551,6 +660,10 @@ subtree_selection, previously_syncing_gaia_id_info, bookmark_count_suffix, relevant_local_subtrees, relevant_account_subtrees); + CompareAndLogHistogramsWithKey<UrlAndTitleAndPathAndUuid>( + subtree_selection, previously_syncing_gaia_id_info, + bookmark_count_suffix, relevant_local_subtrees, + relevant_account_subtrees); } }
diff --git a/components/sync_bookmarks/bookmark_model_merger_comparison_metrics.h b/components/sync_bookmarks/bookmark_model_merger_comparison_metrics.h index 5f7b0e2..b68cebf 100644 --- a/components/sync_bookmarks/bookmark_model_merger_comparison_metrics.h +++ b/components/sync_bookmarks/bookmark_model_merger_comparison_metrics.h
@@ -29,9 +29,11 @@ // comparing local bookmarks with account bookmarks. This enum is used as // infix when recording metrics. enum class GroupingKeyInfix { + kByUrl, kByUrlAndTitle, kByUrlAndUuid, kByUrlAndTitleAndPath, + kByUrlAndTitleAndPathAndUuid, }; // Result of comparing two datasets for the purpose of logging metrics. Note @@ -59,6 +61,15 @@ }; // LINT.ThenChange(/tools/metrics/histograms/metadata/sync/enums.xml:BookmarkSetComparisonOutcome) +struct UrlOnly { + static constexpr GroupingKeyInfix kGroupingKeyInfix = + GroupingKeyInfix::kByUrl; + + auto operator<=>(const UrlOnly&) const = default; + + GURL url; +}; + struct UrlAndTitle { static constexpr GroupingKeyInfix kGroupingKeyInfix = GroupingKeyInfix::kByUrlAndTitle; @@ -91,7 +102,35 @@ std::u16string path; }; +struct UrlAndTitleAndPathAndUuid { + static constexpr GroupingKeyInfix kGroupingKeyInfix = + GroupingKeyInfix::kByUrlAndTitleAndPathAndUuid; + + UrlAndTitleAndPathAndUuid(); + UrlAndTitleAndPathAndUuid(const GURL& url, + const std::u16string& title, + const std::u16string& path, + const base::Uuid& uuid); + UrlAndTitleAndPathAndUuid(const UrlAndTitleAndPathAndUuid&); + UrlAndTitleAndPathAndUuid(UrlAndTitleAndPathAndUuid&&); + ~UrlAndTitleAndPathAndUuid(); + + UrlAndTitleAndPathAndUuid& operator=(const UrlAndTitleAndPathAndUuid&); + UrlAndTitleAndPathAndUuid& operator=(UrlAndTitleAndPathAndUuid&&); + + auto operator<=>(const UrlAndTitleAndPathAndUuid&) const = default; + + GURL url; + std::u16string title; + // Ancestor folder titles concatenated with '/'. + std::u16string path; + base::Uuid uuid; +}; + // Test-only functions to verify the logic related to extracting local data. +base::flat_set<UrlOnly> ExtractUniqueLocalNodesByUrlForTesting( + const BookmarkModelView& all_local_data, + SubtreeSelection subtree_selection); base::flat_set<UrlAndTitle> ExtractUniqueLocalNodesByUrlAndTitleForTesting( const BookmarkModelView& all_local_data, SubtreeSelection subtree_selection); @@ -102,8 +141,15 @@ ExtractUniqueLocalNodesByUrlAndTitleAndPathForTesting( const BookmarkModelView& all_local_data, SubtreeSelection subtree_selection); +base::flat_set<UrlAndTitleAndPathAndUuid> +ExtractUniqueLocalNodesByUrlAndTitleAndPathAndUuidForTesting( + const BookmarkModelView& all_local_data, + SubtreeSelection subtree_selection); // Test-only functions to verify the logic related to extracting account data. +base::flat_set<UrlOnly> ExtractUniqueAccountNodesByUrlForTesting( + const BookmarkModelMerger::RemoteForest& all_account_data, + SubtreeSelection subtree_selection); base::flat_set<UrlAndTitle> ExtractUniqueAccountNodesByUrlAndTitleForTesting( const BookmarkModelMerger::RemoteForest& all_account_data, SubtreeSelection subtree_selection); @@ -114,6 +160,10 @@ ExtractUniqueAccountNodesByUrlAndTitleAndPathForTesting( const BookmarkModelMerger::RemoteForest& all_account_data, SubtreeSelection subtree_selection); +base::flat_set<UrlAndTitleAndPathAndUuid> +ExtractUniqueAccountNodesByUrlAndTitleAndPathAndUuidForTesting( + const BookmarkModelMerger::RemoteForest& all_account_data, + SubtreeSelection subtree_selection); // Test-only function to compare two sets. SetComparisonOutcome CompareSetsForTesting(
diff --git a/components/sync_bookmarks/bookmark_model_merger_comparison_metrics_unittest.cc b/components/sync_bookmarks/bookmark_model_merger_comparison_metrics_unittest.cc index 757afa2..6f5eaa3 100644 --- a/components/sync_bookmarks/bookmark_model_merger_comparison_metrics_unittest.cc +++ b/components/sync_bookmarks/bookmark_model_merger_comparison_metrics_unittest.cc
@@ -29,6 +29,10 @@ namespace sync_bookmarks { namespace metrics { +void PrintTo(const UrlOnly& value, std::ostream* os) { + *os << "{\"" << value.url << "\"}"; +} + void PrintTo(const UrlAndTitle& value, std::ostream* os) { *os << "{\"" << value.url << "\", \"" << base::UTF16ToUTF8(value.title) << "\"}"; @@ -43,6 +47,12 @@ << "\", \"" << base::UTF16ToUTF8(value.path) << "\"}"; } +void PrintTo(const UrlAndTitleAndPathAndUuid& value, std::ostream* os) { + *os << "{\"" << value.url << "\", \"" << base::UTF16ToUTF8(value.title) + << "\", \"" << base::UTF16ToUTF8(value.path) << "\", \"" << value.uuid + << "\"}"; +} + namespace { using RemoteTreeNode = BookmarkModelMerger::RemoteTreeNode; @@ -289,6 +299,17 @@ /*children_of_other_node=*/ {UrlBookmarkBuilder(kUrl4Title, kUrl4).SetUuid(kUrl4Uuid)}); + // By URL. + EXPECT_THAT(ExtractUniqueLocalNodesByUrlForTesting( + BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()), + SubtreeSelection::kConsideringAllBookmarks), + UnorderedElementsAre(UrlOnly{kUrl1}, UrlOnly{kUrl2}, + UrlOnly{kUrl3}, UrlOnly{kUrl4})); + EXPECT_THAT(ExtractUniqueLocalNodesByUrlForTesting( + BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()), + SubtreeSelection::kUnderBookmarksBar), + UnorderedElementsAre(UrlOnly{kUrl1}, UrlOnly{kUrl2})); + // By URL and title. EXPECT_THAT(ExtractUniqueLocalNodesByUrlAndTitleForTesting( BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()), @@ -341,6 +362,38 @@ UrlAndTitleAndPath{kUrl2, kUrl2Title, base::StrCat({u"/", kBookmarkBarFolderName, u"/", kFolder1Title})})); + + // By URL, title, path and UUID. + EXPECT_THAT( + ExtractUniqueLocalNodesByUrlAndTitleAndPathAndUuidForTesting( + BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()), + SubtreeSelection::kConsideringAllBookmarks), + UnorderedElementsAre( + UrlAndTitleAndPathAndUuid{ + kUrl1, kUrl1Title, base::StrCat({u"/", kBookmarkBarFolderName}), + kUrl1Uuid}, + UrlAndTitleAndPathAndUuid{ + kUrl2, kUrl2Title, + base::StrCat({u"/", kBookmarkBarFolderName, u"/", kFolder1Title}), + kUrl2Uuid}, + UrlAndTitleAndPathAndUuid{ + kUrl3, kUrl3Title, + base::StrCat({u"/", kMobileBookmarksFolderName}), kUrl3Uuid}, + UrlAndTitleAndPathAndUuid{ + kUrl4, kUrl4Title, + base::StrCat({u"/", kOtherBookmarksFolderName}), kUrl4Uuid})); + EXPECT_THAT( + ExtractUniqueLocalNodesByUrlAndTitleAndPathAndUuidForTesting( + BookmarkModelViewUsingLocalOrSyncableNodes(model_.get()), + SubtreeSelection::kUnderBookmarksBar), + UnorderedElementsAre( + UrlAndTitleAndPathAndUuid{ + kUrl1, kUrl1Title, base::StrCat({u"/", kBookmarkBarFolderName}), + kUrl1Uuid}, + UrlAndTitleAndPathAndUuid{ + kUrl2, kUrl2Title, + base::StrCat({u"/", kBookmarkBarFolderName, u"/", kFolder1Title}), + kUrl2Uuid})); } TEST_F(BookmarkModelMergerComparisonMetricsTest, ShouldExtractAccountNodes) { @@ -383,6 +436,15 @@ /*children_of_other_node=*/ {UrlBookmarkBuilder(kUrl4Title, kUrl4).SetUuid(kUrl4Uuid)}); + // By URL. + EXPECT_THAT(ExtractUniqueAccountNodesByUrlForTesting( + account_data, SubtreeSelection::kConsideringAllBookmarks), + UnorderedElementsAre(UrlOnly{kUrl1}, UrlOnly{kUrl2}, + UrlOnly{kUrl3}, UrlOnly{kUrl4})); + EXPECT_THAT(ExtractUniqueAccountNodesByUrlForTesting( + account_data, SubtreeSelection::kUnderBookmarksBar), + UnorderedElementsAre(UrlOnly{kUrl1}, UrlOnly{kUrl2})); + // By URL and title. EXPECT_THAT(ExtractUniqueAccountNodesByUrlAndTitleForTesting( account_data, SubtreeSelection::kConsideringAllBookmarks), @@ -429,6 +491,36 @@ UrlAndTitleAndPath{kUrl2, kUrl2Title, base::StrCat({u"/", kBookmarkBarFolderName, u"/", kFolder1Title})})); + + // By URL, title, path and UUID. + EXPECT_THAT( + ExtractUniqueAccountNodesByUrlAndTitleAndPathAndUuidForTesting( + account_data, SubtreeSelection::kConsideringAllBookmarks), + UnorderedElementsAre( + UrlAndTitleAndPathAndUuid{ + kUrl1, kUrl1Title, base::StrCat({u"/", kBookmarkBarFolderName}), + kUrl1Uuid}, + UrlAndTitleAndPathAndUuid{ + kUrl2, kUrl2Title, + base::StrCat({u"/", kBookmarkBarFolderName, u"/", kFolder1Title}), + kUrl2Uuid}, + UrlAndTitleAndPathAndUuid{ + kUrl3, kUrl3Title, + base::StrCat({u"/", kMobileBookmarksFolderName}), kUrl3Uuid}, + UrlAndTitleAndPathAndUuid{ + kUrl4, kUrl4Title, + base::StrCat({u"/", kOtherBookmarksFolderName}), kUrl4Uuid})); + EXPECT_THAT( + ExtractUniqueAccountNodesByUrlAndTitleAndPathAndUuidForTesting( + account_data, SubtreeSelection::kUnderBookmarksBar), + UnorderedElementsAre( + UrlAndTitleAndPathAndUuid{ + kUrl1, kUrl1Title, base::StrCat({u"/", kBookmarkBarFolderName}), + kUrl1Uuid}, + UrlAndTitleAndPathAndUuid{ + kUrl2, kUrl2Title, + base::StrCat({u"/", kBookmarkBarFolderName, u"/", kFolder1Title}), + kUrl2Uuid})); } TEST_F(BookmarkModelMergerComparisonMetricsTest, ShouldCompareSets) { @@ -581,6 +673,12 @@ histogram_tester.ExpectUniqueSample( "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId." + "ConsideringAllBookmarks.ByUrlAndTitleAndPathAndUuid", + /*sample=*/SetComparisonOutcome::kIntersectionEmpty, + /*expected_bucket_count=*/1); + + histogram_tester.ExpectUniqueSample( + "Sync.BookmarkModelMerger.Comparison.MatchesPreviousGaiaId." "UnderBookmarksBar.ByUrlAndTitle", /*sample=*/SetComparisonOutcome::kLocalDataIsStrictSubsetOfAccountData, /*expected_bucket_count=*/1); @@ -634,6 +732,20 @@ "UnderBookmarksBar.ByUrlAndTitleAndPath.Between1And19LocalUrlBookmarks", /*sample=*/SetComparisonOutcome::kLocalDataIsStrictSubsetOfAccountData, /*expected_bucket_count=*/1); + + // Sanity-check the recording of a few metrics without the gaia ID info + // breakdown. + histogram_tester.ExpectUniqueSample( + "Sync.BookmarkModelMerger.Comparison." + "ConsideringAllBookmarks.ByUrlAndTitle", + /*sample=*/SetComparisonOutcome::kExactMatchNonEmpty, + /*expected_bucket_count=*/1); + + histogram_tester.ExpectUniqueSample( + "Sync.BookmarkModelMerger.Comparison." + "ConsideringAllBookmarks.ByUrlAndUuid", + /*sample=*/SetComparisonOutcome::kIntersectionEmpty, + /*expected_bucket_count=*/1); } } // namespace
diff --git a/components/sync_preferences/dual_layer_user_pref_store.cc b/components/sync_preferences/dual_layer_user_pref_store.cc index 313cf0e5..d96d8d11 100644 --- a/components/sync_preferences/dual_layer_user_pref_store.cc +++ b/components/sync_preferences/dual_layer_user_pref_store.cc
@@ -11,10 +11,12 @@ #include "base/auto_reset.h" #include "base/barrier_closure.h" +#include "base/feature_list.h" #include "base/logging.h" #include "base/observer_list.h" #include "base/strings/string_util.h" #include "base/values.h" +#include "components/sync/base/features.h" #include "components/sync/service/sync_service.h" #include "components/sync/service/sync_user_settings.h" #include "components/sync_preferences/pref_model_associator_client.h" @@ -22,6 +24,16 @@ #include "components/sync_preferences/syncable_prefs_database.h" namespace sync_preferences { +namespace { + +// This is the set of user selectable types that are relevant to +// `DualLayerUserPrefStore`. This is used to detect no-op changes to the user +// selected types efficiently. +constexpr syncer::UserSelectableTypeSet kInterestingUserSelectableTypes = { + syncer::UserSelectableType::kPreferences, + syncer::UserSelectableType::kHistory}; + +} // namespace DualLayerUserPrefStore::UnderlyingPrefStoreObserver:: UnderlyingPrefStoreObserver(DualLayerUserPrefStore* outer, @@ -464,6 +476,20 @@ if (metadata->is_history_opt_in_required() && !IsHistorySyncEnabled()) { return false; } + // Priority pref type is always active. This adds check to avoid syncing them + // if the user toggle is off. This however skips all the allowlisted priority + // prefs. + // TODO(crbug.com/412602018): This blocks account priorityprefs from being + // applied before Sync is initialized. Look for another way to handle this. + if (base::FeatureList::IsEnabled( + syncer::kSyncSupportAlwaysSyncingPriorityPreferences) && + metadata->data_type() == syncer::PRIORITY_PREFERENCES && + !interesting_user_selected_types_.Has( + syncer::UserSelectableType::kPreferences) && + !pref_model_associator_client_->GetSyncablePrefsDatabase() + .IsPreferenceAlwaysSyncing(key)) { + return false; + } return true; } @@ -701,7 +727,8 @@ } bool DualLayerUserPrefStore::IsHistorySyncEnabled() const { - return is_history_sync_enabled_; + return interesting_user_selected_types_.Has( + syncer::UserSelectableType::kHistory); } bool DualLayerUserPrefStore::IsHistorySyncEnabledForTest() const { @@ -710,7 +737,17 @@ void DualLayerUserPrefStore::SetIsHistorySyncEnabledForTest( bool is_history_sync_enabled) { - is_history_sync_enabled_ = is_history_sync_enabled; + if (is_history_sync_enabled) { + interesting_user_selected_types_.Put(syncer::UserSelectableType::kHistory); + } else { + interesting_user_selected_types_.Remove( + syncer::UserSelectableType::kHistory); + } +} + +void DualLayerUserPrefStore::SetUserSelectedTypesForTest( + syncer::UserSelectableTypeSet user_selected_types) { + interesting_user_selected_types_ = user_selected_types; } void DualLayerUserPrefStore::OnSyncServiceInitialized( @@ -721,19 +758,21 @@ } void DualLayerUserPrefStore::OnStateChanged(syncer::SyncService* sync_service) { - bool is_history_sync_enabled = - sync_service->GetUserSettings()->GetSelectedTypes().Has( - syncer::UserSelectableType::kHistory); - if (is_history_sync_enabled == is_history_sync_enabled_) { + syncer::UserSelectableTypeSet user_selected_types = + sync_service->GetUserSettings()->GetSelectedTypes(); + // Only retain the concerning types. + user_selected_types.RetainAll(kInterestingUserSelectableTypes); + + if (user_selected_types == interesting_user_selected_types_) { return; } if (!pref_model_associator_client_) { - is_history_sync_enabled_ = is_history_sync_enabled; + interesting_user_selected_types_ = user_selected_types; return; } - // Store the old values for sensitive prefs in a map and only inform the + // Store the old values for account prefs in a map and only inform the // observers if the effective values change. // Note: std::optional is used as the value type since it makes the // comparison with the new values easier. @@ -742,22 +781,19 @@ auto metadata = pref_model_associator_client_->GetSyncablePrefsDatabase() .GetSyncablePrefMetadata(pref_name); CHECK(metadata.has_value()); - // Add effective value for sensitive prefs to `old_values`. - if (metadata->is_history_opt_in_required()) { - if (const base::Value* value = nullptr; GetValue(pref_name, &value)) { - old_values.emplace(pref_name, value->Clone()); - } else { - // Put in std::nullopt to mark pref not existing in the store. This - // helps avoid an extra call to GetPrefNamesInAccount() later. - old_values.emplace(pref_name, std::nullopt); - } + if (const base::Value* value = nullptr; GetValue(pref_name, &value)) { + old_values.emplace(pref_name, value->Clone()); + } else { + // Put in std::nullopt to mark pref not existing in the store. This + // helps avoid an extra call to GetPrefNamesInAccount() later. + old_values.emplace(pref_name, std::nullopt); } } - is_history_sync_enabled_ = is_history_sync_enabled; + interesting_user_selected_types_ = user_selected_types; - // The history sync state has changed. Check for any change in the effective - // values of any of the sensitive prefs as a consequence. + // The sync state change might have changed the effective value. Compare the + // old and new values and notify the observers if they change. for (const auto& [pref_name, old_value] : old_values) { std::optional<base::Value> new_value; if (const base::Value* value = nullptr; GetValue(pref_name, &value)) {
diff --git a/components/sync_preferences/dual_layer_user_pref_store.h b/components/sync_preferences/dual_layer_user_pref_store.h index 023c58b..76920d7 100644 --- a/components/sync_preferences/dual_layer_user_pref_store.h +++ b/components/sync_preferences/dual_layer_user_pref_store.h
@@ -18,6 +18,7 @@ #include "components/prefs/persistent_pref_store.h" #include "components/prefs/value_map_pref_store.h" #include "components/sync/base/data_type.h" +#include "components/sync/base/user_selectable_type.h" #include "components/sync/service/sync_service_observer.h" namespace syncer { @@ -116,6 +117,8 @@ bool IsHistorySyncEnabledForTest() const; void SetIsHistorySyncEnabledForTest(bool is_history_sync_enabled); + void SetUserSelectedTypesForTest( + syncer::UserSelectableTypeSet user_selected_types); protected: ~DualLayerUserPrefStore() override; @@ -212,13 +215,14 @@ // List of preference types currently syncing. base::flat_set<syncer::DataType> active_types_; + // Subset of user selected types that are of relevance in determining whether + // an account pref should be exposed. + syncer::UserSelectableTypeSet interesting_user_selected_types_; // Set to true while this store is setting prefs in the underlying stores. // Used to avoid self-notifications. bool is_setting_prefs_ = false; - bool is_history_sync_enabled_ = false; - base::ObserverList<PrefStore::Observer, true> observers_; const scoped_refptr<PrefModelAssociatorClient> pref_model_associator_client_;
diff --git a/components/sync_preferences/dual_layer_user_pref_store_unittest.cc b/components/sync_preferences/dual_layer_user_pref_store_unittest.cc index 37adf0c1..c939e9ad 100644 --- a/components/sync_preferences/dual_layer_user_pref_store_unittest.cc +++ b/components/sync_preferences/dual_layer_user_pref_store_unittest.cc
@@ -10,9 +10,11 @@ #include "base/memory/scoped_refptr.h" #include "base/test/mock_callback.h" +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/values.h" #include "components/prefs/testing_pref_store.h" +#include "components/sync/base/features.h" #include "components/sync/test/test_sync_service.h" #include "components/sync_preferences/pref_model_associator_client.h" #include "components/sync_preferences/test_syncable_prefs_database.h" @@ -35,6 +37,8 @@ constexpr char kMergeableDictPref1[] = "mergeable.dict.pref1"; constexpr char kMergeableDictPref2[] = "mergeable.dict.pref2"; constexpr char kCustomMergePref[] = "custom.merge.pref"; +constexpr char kAlwaysSyncingPriorityPrefName[] = + "always.syncing.priority.pref"; // Assigning an id of 0 to all the test prefs. const TestSyncablePrefsDatabase::PrefsMap kSyncablePrefsDatabase = { @@ -63,8 +67,14 @@ MergeBehavior::kMergeableDict}}, {kCustomMergePref, {0, syncer::PREFERENCES, PrefSensitivity::kNone, MergeBehavior::kCustom}}, + {kAlwaysSyncingPriorityPrefName, + {0, syncer::PRIORITY_PREFERENCES, PrefSensitivity::kNone, + MergeBehavior::kNone}}, }; +const std::set<std::string_view> kAlwaysSyncingPrefs = { + kAlwaysSyncingPriorityPrefName}; + base::Value MakeDict( const std::vector<std::pair<std::string, std::string>>& values) { base::Value::Dict dict; @@ -135,7 +145,10 @@ class TestPrefModelAssociatorClient : public PrefModelAssociatorClient { public: TestPrefModelAssociatorClient() - : syncable_prefs_database_(kSyncablePrefsDatabase) {} + : syncable_prefs_database_(kSyncablePrefsDatabase) { + syncable_prefs_database_.SetAlwaysSyncingPrefs( + {kAlwaysSyncingPriorityPrefName}); + } // PrefModelAssociatorClient implementation. base::Value MaybeMergePreferenceValues( @@ -2787,5 +2800,105 @@ store()->RemoveObserver(&observer); } +class DualLayerUserPrefStorePriorityPrefDecoupleTest + : public DualLayerUserPrefStoreTest { + base::test::ScopedFeatureList scoped_feature_list_{ + syncer::kSyncSupportAlwaysSyncingPriorityPreferences}; +}; + +TEST_F(DualLayerUserPrefStorePriorityPrefDecoupleTest, + ShouldGetAllowlistedPrefFromAccountStoreIfUserToggleIsOff) { + store()->SetUserSelectedTypesForTest(syncer::UserSelectableTypeSet()); + account_store()->SetValueSilently(kPriorityPrefName, + base::Value("account value"), 0); + // Allowlisted pref. + account_store()->SetValueSilently(kAlwaysSyncingPriorityPrefName, + base::Value("account value"), 0); + + // Check GetValue(). + EXPECT_TRUE(ValueInStoreIsAbsent(*store(), kPriorityPrefName)); + EXPECT_TRUE(ValueInStoreIs(*store(), kAlwaysSyncingPriorityPrefName, + "account value")); + + // Check GetMutableValue(). + EXPECT_FALSE(store()->GetMutableValue(kPriorityPrefName, nullptr)); + base::Value* value = nullptr; + EXPECT_TRUE(store()->GetMutableValue(kAlwaysSyncingPriorityPrefName, &value)); + EXPECT_THAT(value, testing::Pointee(testing::Eq("account value"))); + + // Check GetValues(). + base::Value::Dict values = store()->GetValues(); + EXPECT_FALSE(values.FindByDottedPath(kPriorityPrefName)); + EXPECT_THAT(values.FindByDottedPath(kAlwaysSyncingPriorityPrefName), + testing::Pointee(testing::Eq("account value"))); +} + +TEST_F(DualLayerUserPrefStorePriorityPrefDecoupleTest, + ShouldGetRegularPrefFromAccountStoreIfUserToggleIsOn) { + store()->SetUserSelectedTypesForTest(syncer::UserSelectableTypeSet( + {syncer::UserSelectableType::kPreferences})); + account_store()->SetValueSilently(kPriorityPrefName, + base::Value("account value"), 0); + // Allowlisted pref. + account_store()->SetValueSilently(kAlwaysSyncingPriorityPrefName, + base::Value("account value"), 0); + + // Check GetValue(). + EXPECT_TRUE(ValueInStoreIs(*store(), kPriorityPrefName, "account value")); + EXPECT_TRUE(ValueInStoreIs(*store(), kAlwaysSyncingPriorityPrefName, + "account value")); + + // Check GetMutableValue(). + base::Value* value = nullptr; + EXPECT_TRUE(store()->GetMutableValue(kPriorityPrefName, &value)); + EXPECT_THAT(value, testing::Pointee(testing::Eq("account value"))); + EXPECT_TRUE(store()->GetMutableValue(kAlwaysSyncingPriorityPrefName, &value)); + EXPECT_THAT(value, testing::Pointee(testing::Eq("account value"))); + + // Check GetValues(). + base::Value::Dict values = store()->GetValues(); + EXPECT_THAT(values.FindByDottedPath(kPriorityPrefName), + testing::Pointee(testing::Eq("account value"))); + EXPECT_THAT(values.FindByDottedPath(kAlwaysSyncingPriorityPrefName), + testing::Pointee(testing::Eq("account value"))); +} + +TEST_F(DualLayerUserPrefStorePriorityPrefDecoupleTest, + ShouldObserverUserToggleChange) { + syncer::TestSyncService sync_service; + sync_service.GetUserSettings()->SetSelectedTypes( + /*sync_everything=*/false, syncer::UserSelectableTypeSet()); + store()->OnSyncServiceInitialized(&sync_service); + + account_store()->SetValueSilently(kPriorityPrefName, + base::Value("account value"), 0); + // Allowlisted pref. + account_store()->SetValueSilently(kAlwaysSyncingPriorityPrefName, + base::Value("account value"), 0); + + EXPECT_TRUE(ValueInStoreIsAbsent(*store(), kPriorityPrefName)); + EXPECT_TRUE(ValueInStoreIs(*store(), kAlwaysSyncingPriorityPrefName, + "account value")); + + testing::StrictMock<MockPrefStoreObserver> observer; + store()->AddObserver(&observer); + + EXPECT_CALL(observer, OnPrefValueChanged(kPriorityPrefName)); + EXPECT_CALL(observer, OnPrefValueChanged(kAlwaysSyncingPriorityPrefName)) + .Times(0); + + sync_service.GetUserSettings()->SetSelectedTypes( + /*sync_everything=*/false, + syncer::UserSelectableTypeSet( + {syncer::UserSelectableType::kPreferences})); + sync_service.FireStateChanged(); + + EXPECT_TRUE(ValueInStoreIs(*store(), kPriorityPrefName, "account value")); + EXPECT_TRUE(ValueInStoreIs(*store(), kAlwaysSyncingPriorityPrefName, + "account value")); + + store()->RemoveObserver(&observer); +} + } // namespace } // namespace sync_preferences
diff --git a/components/sync_preferences/test_syncable_prefs_database.cc b/components/sync_preferences/test_syncable_prefs_database.cc index c738ad46..34be0a5 100644 --- a/components/sync_preferences/test_syncable_prefs_database.cc +++ b/components/sync_preferences/test_syncable_prefs_database.cc
@@ -26,7 +26,12 @@ bool TestSyncablePrefsDatabase::IsPreferenceAlwaysSyncing( std::string_view pref_name) const { - return false; + return always_syncing_prefs_.contains(pref_name); +} + +void TestSyncablePrefsDatabase::SetAlwaysSyncingPrefs( + const std::set<std::string_view>& always_syncing_prefs) { + always_syncing_prefs_ = always_syncing_prefs; } } // namespace sync_preferences
diff --git a/components/sync_preferences/test_syncable_prefs_database.h b/components/sync_preferences/test_syncable_prefs_database.h index 6c63f47..a8779de 100644 --- a/components/sync_preferences/test_syncable_prefs_database.h +++ b/components/sync_preferences/test_syncable_prefs_database.h
@@ -7,6 +7,7 @@ #include <functional> #include <map> +#include <set> #include <string> #include <string_view> @@ -27,8 +28,12 @@ bool IsPreferenceAlwaysSyncing(std::string_view pref_name) const override; + void SetAlwaysSyncingPrefs( + const std::set<std::string_view>& always_syncing_prefs); + private: PrefsMap syncable_prefs_map_; + std::set<std::string_view> always_syncing_prefs_; }; } // namespace sync_preferences
diff --git a/components/trusted_vault/icloud_keychain_recovery_factor.cc b/components/trusted_vault/icloud_keychain_recovery_factor.cc index 61afcd1..ad846165 100644 --- a/components/trusted_vault/icloud_keychain_recovery_factor.cc +++ b/components/trusted_vault/icloud_keychain_recovery_factor.cc
@@ -24,8 +24,7 @@ void ICloudKeychainRecoveryFactor::AttemptRecovery( TrustedVaultThrottlingConnection* connection, - AttemptRecoveryCallback cb, - AttemptRecoveryFailureCallback failure_cb) { + AttemptRecoveryCallback cb) { NOTIMPLEMENTED(); }
diff --git a/components/trusted_vault/icloud_keychain_recovery_factor.h b/components/trusted_vault/icloud_keychain_recovery_factor.h index 7a6b5effc..5fa5c95 100644 --- a/components/trusted_vault/icloud_keychain_recovery_factor.h +++ b/components/trusted_vault/icloud_keychain_recovery_factor.h
@@ -33,8 +33,7 @@ LocalRecoveryFactorType GetRecoveryFactorType() const override; void AttemptRecovery(TrustedVaultThrottlingConnection* connection, - AttemptRecoveryCallback cb, - AttemptRecoveryFailureCallback failure_cb) override; + AttemptRecoveryCallback cb) override; bool IsRegistered() override; void MarkAsNotRegistered() override;
diff --git a/components/trusted_vault/local_recovery_factor.h b/components/trusted_vault/local_recovery_factor.h index 60fe0cf..cf29d606 100644 --- a/components/trusted_vault/local_recovery_factor.h +++ b/components/trusted_vault/local_recovery_factor.h
@@ -27,7 +27,7 @@ // Interface for a local recovery factor. // Classes that implement this interface are used by -// StandaloneTrustedVaultBackend to retrieve keys without user interaction when +// StandaloneTrustedVaultBackend to recover keys without user interaction when // required. // StandaloneTrustedVaultBackend also makes sure to register local recovery // factors with available keys when possible. @@ -35,12 +35,19 @@ // sequence as StandaloneTrustedVaultBackend. class LocalRecoveryFactor { public: + enum class RecoveryStatus { + // Keys were successfully recovered. + kSuccess, + // Failed to recover keys. + kFailure, + // Keys were successfully recovered and verified, but no new keys exist. + kNoNewKeys, + }; + using AttemptRecoveryCallback = base::OnceCallback<void( - TrustedVaultDownloadKeysStatus /* status */, + RecoveryStatus /* status */, const std::vector<std::vector<uint8_t>>& /* new_vault_keys */, int /* last_vault_key_version */)>; - using AttemptRecoveryFailureCallback = base::OnceCallback<void( - std::optional<TrustedVaultDownloadKeysStatusForUMA> /* status */)>; using RegisterCallback = base::OnceCallback<void(TrustedVaultRegistrationStatus /* status */, int /* key_version */, @@ -56,8 +63,7 @@ // Attempts a key recovery. virtual void AttemptRecovery(TrustedVaultThrottlingConnection* connection, - AttemptRecoveryCallback cb, - AttemptRecoveryFailureCallback failure_cb) = 0; + AttemptRecoveryCallback cb) = 0; // Returns whether the recovery factor is marked as registered. virtual bool IsRegistered() = 0;
diff --git a/components/trusted_vault/physical_device_recovery_factor.cc b/components/trusted_vault/physical_device_recovery_factor.cc index f7fcc43..7825c56 100644 --- a/components/trusted_vault/physical_device_recovery_factor.cc +++ b/components/trusted_vault/physical_device_recovery_factor.cc
@@ -7,6 +7,7 @@ #include "base/task/bind_post_task.h" #include "components/trusted_vault/proto_string_bytes_conversion.h" #include "components/trusted_vault/securebox.h" +#include "components/trusted_vault/trusted_vault_connection.h" namespace trusted_vault { @@ -14,12 +15,43 @@ constexpr int kCurrentDeviceRegistrationVersion = 1; +TrustedVaultDownloadKeysStatusForUMA GetDownloadKeysStatusForUMAFromResponse( + TrustedVaultDownloadKeysStatus response_status) { + switch (response_status) { + case TrustedVaultDownloadKeysStatus::kSuccess: + return TrustedVaultDownloadKeysStatusForUMA::kSuccess; + case TrustedVaultDownloadKeysStatus::kMemberNotFound: + return TrustedVaultDownloadKeysStatusForUMA::kMemberNotFound; + case TrustedVaultDownloadKeysStatus::kMembershipNotFound: + return TrustedVaultDownloadKeysStatusForUMA::kMembershipNotFound; + case TrustedVaultDownloadKeysStatus::kMembershipCorrupted: + return TrustedVaultDownloadKeysStatusForUMA::kMembershipCorrupted; + case TrustedVaultDownloadKeysStatus::kMembershipEmpty: + return TrustedVaultDownloadKeysStatusForUMA::kMembershipEmpty; + case TrustedVaultDownloadKeysStatus::kNoNewKeys: + return TrustedVaultDownloadKeysStatusForUMA::kNoNewKeys; + case TrustedVaultDownloadKeysStatus::kKeyProofsVerificationFailed: + return TrustedVaultDownloadKeysStatusForUMA::kKeyProofsVerificationFailed; + case TrustedVaultDownloadKeysStatus::kAccessTokenFetchingFailure: + return TrustedVaultDownloadKeysStatusForUMA::kAccessTokenFetchingFailure; + case TrustedVaultDownloadKeysStatus::kNetworkError: + return TrustedVaultDownloadKeysStatusForUMA::kNetworkError; + case TrustedVaultDownloadKeysStatus::kOtherError: + return TrustedVaultDownloadKeysStatusForUMA::kOtherError; + } + + NOTREACHED(); +} + } // namespace PhysicalDeviceRecoveryFactor::PhysicalDeviceRecoveryFactor( + SecurityDomainId security_domain_id, StandaloneTrustedVaultStorage* storage, std::optional<CoreAccountInfo> primary_account) - : storage_(storage), primary_account_(primary_account) { + : security_domain_id_(security_domain_id), + storage_(storage), + primary_account_(primary_account) { CHECK(storage_); } PhysicalDeviceRecoveryFactor::~PhysicalDeviceRecoveryFactor() = default; @@ -31,27 +63,22 @@ void PhysicalDeviceRecoveryFactor::AttemptRecovery( TrustedVaultThrottlingConnection* connection, - AttemptRecoveryCallback cb, - AttemptRecoveryFailureCallback failure_cb) { + AttemptRecoveryCallback cb) { auto* per_user_vault = GetPrimaryAccountVault(); if (!GetPrimaryAccountVault() ->local_device_registration_info() .device_registered()) { - base::BindPostTaskToCurrentDefault( - base::BindOnce( - std::move(failure_cb), - TrustedVaultDownloadKeysStatusForUMA::kDeviceNotRegistered)) - .Run(); + FulfillRecoveryWithFailure( + TrustedVaultDownloadKeysStatusForUMA::kDeviceNotRegistered, + std::move(cb)); return; } if (connection->AreRequestsThrottled(*primary_account_)) { - base::BindPostTaskToCurrentDefault( - base::BindOnce( - std::move(failure_cb), - TrustedVaultDownloadKeysStatusForUMA::kThrottledClientSide)) - .Run(); + FulfillRecoveryWithFailure( + TrustedVaultDownloadKeysStatusForUMA::kThrottledClientSide, + std::move(cb)); return; } @@ -60,15 +87,12 @@ ProtoStringToBytes(per_user_vault->local_device_registration_info() .private_key_material())); if (!key_pair) { - // Corrupted state: device is registered, but `key_pair` can't be - // imported. + // Corrupted state: device is registered, but `key_pair` can't be imported. // TODO(crbug.com/40699425): restore from this state (throw away the key // and trigger device registration again). - base::BindPostTaskToCurrentDefault( - base::BindOnce(std::move(failure_cb), - TrustedVaultDownloadKeysStatusForUMA:: - kCorruptedLocalDeviceRegistration)) - .Run(); + FulfillRecoveryWithFailure( + TrustedVaultDownloadKeysStatusForUMA::kCorruptedLocalDeviceRegistration, + std::move(cb)); return; } @@ -83,7 +107,7 @@ std::move(key_pair), // `this` outlives `ongoing_request_`. base::BindOnce(&PhysicalDeviceRecoveryFactor::OnKeysDownloaded, - base::Unretained(this), std::move(cb))); + base::Unretained(this), connection, std::move(cb))); CHECK(ongoing_request_); } @@ -193,6 +217,7 @@ } void PhysicalDeviceRecoveryFactor::OnKeysDownloaded( + TrustedVaultThrottlingConnection* connection, AttemptRecoveryCallback cb, TrustedVaultDownloadKeysStatus status, const std::vector<std::vector<uint8_t>>& new_vault_keys, @@ -204,7 +229,60 @@ CHECK(ongoing_request_); ongoing_request_ = nullptr; - std::move(cb).Run(status, new_vault_keys, last_vault_key_version); + RecordTrustedVaultDownloadKeysStatus( + LocalRecoveryFactorType::kPhysicalDevice, security_domain_id_, + GetDownloadKeysStatusForUMAFromResponse(status)); + + RecoveryStatus recovery_status = RecoveryStatus::kFailure; + switch (status) { + case TrustedVaultDownloadKeysStatus::kSuccess: { + recovery_status = RecoveryStatus::kSuccess; + break; + } + case TrustedVaultDownloadKeysStatus::kMemberNotFound: + case TrustedVaultDownloadKeysStatus::kMembershipNotFound: + case TrustedVaultDownloadKeysStatus::kMembershipCorrupted: + case TrustedVaultDownloadKeysStatus::kMembershipEmpty: + case TrustedVaultDownloadKeysStatus::kKeyProofsVerificationFailed: { + // Unable to download new keys due to known protocol errors. The only way + // to go out of these states is to receive new vault keys through external + // means. It's safe to mark device as not registered regardless of the + // cause (device registration will be triggered once new vault keys are + // available). + MarkAsNotRegistered(); + break; + } + case TrustedVaultDownloadKeysStatus::kNoNewKeys: { + // The registration itself exists, but there's no additional keys to + // download. This is bad because key download attempts are triggered for + // the case where local keys have been marked as stale, which means the + // user is likely in an unrecoverable state. + connection->RecordFailedRequestForThrottling(*primary_account_); + recovery_status = RecoveryStatus::kNoNewKeys; + break; + } + case TrustedVaultDownloadKeysStatus::kAccessTokenFetchingFailure: + case TrustedVaultDownloadKeysStatus::kNetworkError: + // Request wasn't sent to the server, so there is no need for throttling. + break; + case TrustedVaultDownloadKeysStatus::kOtherError: + connection->RecordFailedRequestForThrottling(*primary_account_); + break; + } + + std::move(cb).Run(recovery_status, new_vault_keys, last_vault_key_version); +} + +void PhysicalDeviceRecoveryFactor::FulfillRecoveryWithFailure( + TrustedVaultDownloadKeysStatusForUMA status_for_uma, + AttemptRecoveryCallback cb) { + RecordTrustedVaultDownloadKeysStatus(LocalRecoveryFactorType::kPhysicalDevice, + security_domain_id_, status_for_uma); + + base::BindPostTaskToCurrentDefault( + base::BindOnce(std::move(cb), RecoveryStatus::kFailure, + std::vector<std::vector<uint8_t>>(), 0)) + .Run(); } void PhysicalDeviceRecoveryFactor::OnRegistered(
diff --git a/components/trusted_vault/physical_device_recovery_factor.h b/components/trusted_vault/physical_device_recovery_factor.h index 1c385c92..31eb5a95 100644 --- a/components/trusted_vault/physical_device_recovery_factor.h +++ b/components/trusted_vault/physical_device_recovery_factor.h
@@ -29,7 +29,8 @@ // `storage` must not be null and must outlive this object. // TODO(crbug.com/405381481): Refactor / remove the usage of // StandaloneTrustedVaultStorage in this class. - PhysicalDeviceRecoveryFactor(StandaloneTrustedVaultStorage* storage, + PhysicalDeviceRecoveryFactor(SecurityDomainId security_domain_id, + StandaloneTrustedVaultStorage* storage, std::optional<CoreAccountInfo> primary_account); PhysicalDeviceRecoveryFactor(const PhysicalDeviceRecoveryFactor&) = delete; PhysicalDeviceRecoveryFactor& operator=(PhysicalDeviceRecoveryFactor&) = @@ -39,8 +40,7 @@ LocalRecoveryFactorType GetRecoveryFactorType() const override; void AttemptRecovery(TrustedVaultThrottlingConnection* connection, - AttemptRecoveryCallback cb, - AttemptRecoveryFailureCallback failure_cb) override; + AttemptRecoveryCallback cb) override; bool IsRegistered() override; void MarkAsNotRegistered() override; @@ -54,16 +54,21 @@ private: trusted_vault_pb::LocalTrustedVaultPerUser* GetPrimaryAccountVault(); - void OnKeysDownloaded(AttemptRecoveryCallback cb, + void OnKeysDownloaded(TrustedVaultThrottlingConnection* connection, + AttemptRecoveryCallback cb, TrustedVaultDownloadKeysStatus status, const std::vector<std::vector<uint8_t>>& new_vault_keys, int last_vault_key_version); + void FulfillRecoveryWithFailure( + TrustedVaultDownloadKeysStatusForUMA status_for_uma, + AttemptRecoveryCallback cb); void OnRegistered(RegisterCallback cb, bool had_local_keys, TrustedVaultRegistrationStatus status, int key_version); + const SecurityDomainId security_domain_id_; const raw_ptr<StandaloneTrustedVaultStorage> storage_; const std::optional<CoreAccountInfo> primary_account_;
diff --git a/components/trusted_vault/physical_device_recovery_factor_unittest.cc b/components/trusted_vault/physical_device_recovery_factor_unittest.cc index 9d1e9e6..98a404e 100644 --- a/components/trusted_vault/physical_device_recovery_factor_unittest.cc +++ b/components/trusted_vault/physical_device_recovery_factor_unittest.cc
@@ -5,11 +5,13 @@ #include "components/trusted_vault/physical_device_recovery_factor.h" #include <optional> +#include <vector> #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/run_loop.h" #include "base/test/bind.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/mock_callback.h" #include "base/test/task_environment.h" #include "components/signin/public/identity_manager/account_info.h" @@ -88,7 +90,7 @@ std::make_unique<NiceMock<MockTrustedVaultThrottlingConnection>>(); recovery_factor_ = std::make_unique<PhysicalDeviceRecoveryFactor>( - storage_.get(), account_info); + SecurityDomainId::kChromeSync, storage_.get(), account_info); } CoreAccountInfo account_info() { @@ -426,18 +428,21 @@ base::MockCallback<LocalRecoveryFactor::AttemptRecoveryCallback> recovery_callback; - base::MockCallback<LocalRecoveryFactor::AttemptRecoveryFailureCallback> - recovery_failure_callback; - EXPECT_CALL(recovery_callback, Run).Times(0); - EXPECT_CALL(recovery_failure_callback, - Run(std::optional( - TrustedVaultDownloadKeysStatusForUMA::kDeviceNotRegistered))); + EXPECT_CALL(recovery_callback, Run); + base::HistogramTester histogram_tester; base::RunLoop run_loop; recovery_factor()->AttemptRecovery( - connection(), recovery_callback.Get(), - recovery_failure_callback.Get().Then(run_loop.QuitClosure())); + connection(), recovery_callback.Get().Then(run_loop.QuitClosure())); run_loop.Run(); + + histogram_tester.ExpectUniqueSample( + "TrustedVault.DownloadKeysStatus." + + GetLocalRecoveryFactorNameForUma( + LocalRecoveryFactorType::kPhysicalDevice) + + "." + GetSecurityDomainNameForUma(SecurityDomainId::kChromeSync), + /*sample=*/TrustedVaultDownloadKeysStatusForUMA::kDeviceNotRegistered, + /*expected_bucket_count=*/1); } TEST_F(PhysicalDeviceRecoveryFactorTest, @@ -453,18 +458,97 @@ base::MockCallback<LocalRecoveryFactor::AttemptRecoveryCallback> recovery_callback; - base::MockCallback<LocalRecoveryFactor::AttemptRecoveryFailureCallback> - recovery_failure_callback; - EXPECT_CALL(recovery_callback, Run).Times(0); - EXPECT_CALL(recovery_failure_callback, - Run(std::optional( - TrustedVaultDownloadKeysStatusForUMA::kThrottledClientSide))); + EXPECT_CALL(recovery_callback, Run); + base::HistogramTester histogram_tester; base::RunLoop run_loop; recovery_factor()->AttemptRecovery( - connection(), recovery_callback.Get(), - recovery_failure_callback.Get().Then(run_loop.QuitClosure())); + connection(), recovery_callback.Get().Then(run_loop.QuitClosure())); run_loop.Run(); + + histogram_tester.ExpectUniqueSample( + "TrustedVault.DownloadKeysStatus." + + GetLocalRecoveryFactorNameForUma( + LocalRecoveryFactorType::kPhysicalDevice) + + "." + GetSecurityDomainNameForUma(SecurityDomainId::kChromeSync), + /*sample=*/TrustedVaultDownloadKeysStatusForUMA::kThrottledClientSide, + /*expected_bucket_count=*/1); +} + +TEST_F(PhysicalDeviceRecoveryFactorTest, ShouldThrottleKeysDownloading) { + StoreKeysAndMimicDeviceRegistration(account_info(), {kVaultKey}, + kLastKeyVersion); + + TrustedVaultConnection::DownloadNewKeysCallback download_keys_callback; + ON_CALL(*connection(), DownloadNewKeys(account_info(), _, _, _)) + .WillByDefault( + [&](const CoreAccountInfo&, const TrustedVaultKeyAndVersion&, + std::unique_ptr<SecureBoxKeyPair> key_pair, + TrustedVaultConnection::DownloadNewKeysCallback callback) { + download_keys_callback = std::move(callback); + return std::make_unique<TrustedVaultConnection::Request>(); + }); + + base::MockCallback<LocalRecoveryFactor::AttemptRecoveryCallback> + recovery_callback; + base::HistogramTester histogram_tester; + + recovery_factor()->AttemptRecovery(connection(), recovery_callback.Get()); + + ASSERT_FALSE(download_keys_callback.is_null()); + + // Mimic failed key downloading, it should record a failed request for + // throttling. + EXPECT_CALL(*connection(), RecordFailedRequestForThrottling); + EXPECT_CALL(recovery_callback, + Run(LocalRecoveryFactor::RecoveryStatus::kFailure, _, _)); + std::move(download_keys_callback) + .Run(TrustedVaultDownloadKeysStatus::kOtherError, + std::vector<std::vector<uint8_t>>(), 0); + histogram_tester.ExpectUniqueSample( + "TrustedVault.DownloadKeysStatus." + + GetLocalRecoveryFactorNameForUma( + LocalRecoveryFactorType::kPhysicalDevice) + + "." + GetSecurityDomainNameForUma(SecurityDomainId::kChromeSync), + /*sample=*/TrustedVaultDownloadKeysStatusForUMA::kOtherError, + /*expected_bucket_count=*/1); +} + +TEST_F(PhysicalDeviceRecoveryFactorTest, + ShouldThrottleIfDownloadingReturnedNoNewKeys) { + StoreKeysAndMimicDeviceRegistration(account_info(), {kVaultKey}, + kLastKeyVersion); + + TrustedVaultConnection::DownloadNewKeysCallback download_keys_callback; + ON_CALL(*connection(), DownloadNewKeys(account_info(), _, _, _)) + .WillByDefault( + [&](const CoreAccountInfo&, const TrustedVaultKeyAndVersion&, + std::unique_ptr<SecureBoxKeyPair> key_pair, + TrustedVaultConnection::DownloadNewKeysCallback callback) { + download_keys_callback = std::move(callback); + return std::make_unique<TrustedVaultConnection::Request>(); + }); + + base::HistogramTester histogram_tester; + + recovery_factor()->AttemptRecovery(connection(), base::DoNothing()); + + ASSERT_FALSE(download_keys_callback.is_null()); + + // Mimic the server having no new keys. + EXPECT_CALL(*connection(), RecordFailedRequestForThrottling); + std::move(download_keys_callback) + .Run(TrustedVaultDownloadKeysStatus::kNoNewKeys, + std::vector<std::vector<uint8_t>>(), 0); + histogram_tester.ExpectUniqueSample( + "TrustedVault.DownloadKeysStatus." + + GetLocalRecoveryFactorNameForUma( + LocalRecoveryFactorType::kPhysicalDevice) + + "." + GetSecurityDomainNameForUma(SecurityDomainId::kChromeSync), + /*sample=*/TrustedVaultDownloadKeysStatusForUMA::kNoNewKeys, + /*expected_bucket_count=*/1); + + Mock::VerifyAndClearExpectations(connection()); } TEST_F(PhysicalDeviceRecoveryFactorTest, ShouldDownloadNewKeys) { @@ -491,10 +575,9 @@ base::MockCallback<LocalRecoveryFactor::AttemptRecoveryCallback> recovery_callback; - base::MockCallback<LocalRecoveryFactor::AttemptRecoveryFailureCallback> - recovery_failure_callback; - recovery_factor()->AttemptRecovery(connection(), recovery_callback.Get(), - recovery_failure_callback.Get()); + base::HistogramTester histogram_tester; + + recovery_factor()->AttemptRecovery(connection(), recovery_callback.Get()); ASSERT_FALSE(download_keys_callback.is_null()); @@ -504,12 +587,19 @@ {4, 5, 6}}; const int kServerLastKeyVersion = kInitialLastKeyVersion + 1; - EXPECT_CALL(recovery_callback, Run(TrustedVaultDownloadKeysStatus::kSuccess, - kNewVaultKeys, kServerLastKeyVersion)); - EXPECT_CALL(recovery_failure_callback, Run).Times(0); + EXPECT_CALL(recovery_callback, + Run(LocalRecoveryFactor::RecoveryStatus::kSuccess, kNewVaultKeys, + kServerLastKeyVersion)); std::move(download_keys_callback) .Run(TrustedVaultDownloadKeysStatus::kSuccess, kNewVaultKeys, kServerLastKeyVersion); + histogram_tester.ExpectUniqueSample( + "TrustedVault.DownloadKeysStatus." + + GetLocalRecoveryFactorNameForUma( + LocalRecoveryFactorType::kPhysicalDevice) + + "." + GetSecurityDomainNameForUma(SecurityDomainId::kChromeSync), + /*sample=*/TrustedVaultDownloadKeysStatusForUMA::kSuccess, + /*expected_bucket_count=*/1); } TEST_F(PhysicalDeviceRecoveryFactorTest, @@ -565,10 +655,7 @@ base::MockCallback<LocalRecoveryFactor::AttemptRecoveryCallback> recovery_callback; - base::MockCallback<LocalRecoveryFactor::AttemptRecoveryFailureCallback> - recovery_failure_callback; - recovery_factor()->AttemptRecovery(connection(), recovery_callback.Get(), - recovery_failure_callback.Get()); + recovery_factor()->AttemptRecovery(connection(), recovery_callback.Get()); ASSERT_FALSE(download_keys_callback.is_null()); @@ -578,9 +665,9 @@ GetConstantTrustedVaultKey(), {4, 5, 6}}; const int kServerLastKeyVersion = kInitialLastKeyVersion + 1; - EXPECT_CALL(recovery_callback, Run(TrustedVaultDownloadKeysStatus::kSuccess, - kNewVaultKeys, kServerLastKeyVersion)); - EXPECT_CALL(recovery_failure_callback, Run).Times(0); + EXPECT_CALL(recovery_callback, + Run(LocalRecoveryFactor::RecoveryStatus::kSuccess, kNewVaultKeys, + kServerLastKeyVersion)); std::move(download_keys_callback) .Run(TrustedVaultDownloadKeysStatus::kSuccess, kNewVaultKeys, kServerLastKeyVersion);
diff --git a/components/trusted_vault/standalone_trusted_vault_backend.cc b/components/trusted_vault/standalone_trusted_vault_backend.cc index 1473fd9..eb6b6f9 100644 --- a/components/trusted_vault/standalone_trusted_vault_backend.cc +++ b/components/trusted_vault/standalone_trusted_vault_backend.cc
@@ -23,6 +23,7 @@ #include "components/signin/public/identity_manager/account_info.h" #include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h" #include "components/trusted_vault/features.h" +#include "components/trusted_vault/local_recovery_factor.h" #include "components/trusted_vault/physical_device_recovery_factor.h" #include "components/trusted_vault/proto/local_trusted_vault.pb.h" #include "components/trusted_vault/proto_string_bytes_conversion.h" @@ -69,6 +70,21 @@ kNoPersistentAuthErrors; } +TrustedVaultRecoverKeysOutcomeForUMA +GetRecoverKeysOutcomeForUMAFromRecoveryStatus( + LocalRecoveryFactor::RecoveryStatus recovery_status) { + switch (recovery_status) { + case LocalRecoveryFactor::RecoveryStatus::kSuccess: + return TrustedVaultRecoverKeysOutcomeForUMA::kSuccess; + case LocalRecoveryFactor::RecoveryStatus::kNoNewKeys: + return TrustedVaultRecoverKeysOutcomeForUMA::kNoNewKeys; + case LocalRecoveryFactor::RecoveryStatus::kFailure: + return TrustedVaultRecoverKeysOutcomeForUMA::kFailure; + } + + NOTREACHED(); +} + TrustedVaultDeviceRegistrationOutcomeForUMA GetDeviceRegistrationOutcomeForUMAFromResponse( TrustedVaultRegistrationStatus response_status) { @@ -109,12 +125,13 @@ const LocalRecoveryFactorsFactoryImpl&) = delete; std::vector<std::unique_ptr<LocalRecoveryFactor>> CreateLocalRecoveryFactors( + SecurityDomainId security_domain_id, StandaloneTrustedVaultStorage* storage, const std::optional<CoreAccountInfo>& primary_account) override { std::vector<std::unique_ptr<LocalRecoveryFactor>> local_recovery_factors; local_recovery_factors.emplace_back( - std::make_unique<PhysicalDeviceRecoveryFactor>(storage, - primary_account)); + std::make_unique<PhysicalDeviceRecoveryFactor>( + security_domain_id, storage, primary_account)); #if BUILDFLAG(IS_MAC) if (base::FeatureList::IsEnabled(kEnableICloudKeychainRecoveryFactor)) { // Note: The iCloud Keychain recovery factor needs to come after the @@ -173,36 +190,6 @@ StandaloneTrustedVaultBackend::OngoingFetchKeys::~OngoingFetchKeys() = default; -// static -TrustedVaultDownloadKeysStatusForUMA -StandaloneTrustedVaultBackend::GetDownloadKeysStatusForUMAFromResponse( - TrustedVaultDownloadKeysStatus response_status) { - switch (response_status) { - case TrustedVaultDownloadKeysStatus::kSuccess: - return TrustedVaultDownloadKeysStatusForUMA::kSuccess; - case TrustedVaultDownloadKeysStatus::kMemberNotFound: - return TrustedVaultDownloadKeysStatusForUMA::kMemberNotFound; - case TrustedVaultDownloadKeysStatus::kMembershipNotFound: - return TrustedVaultDownloadKeysStatusForUMA::kMembershipNotFound; - case TrustedVaultDownloadKeysStatus::kMembershipCorrupted: - return TrustedVaultDownloadKeysStatusForUMA::kMembershipCorrupted; - case TrustedVaultDownloadKeysStatus::kMembershipEmpty: - return TrustedVaultDownloadKeysStatusForUMA::kMembershipEmpty; - case TrustedVaultDownloadKeysStatus::kNoNewKeys: - return TrustedVaultDownloadKeysStatusForUMA::kNoNewKeys; - case TrustedVaultDownloadKeysStatus::kKeyProofsVerificationFailed: - return TrustedVaultDownloadKeysStatusForUMA::kKeyProofsVerificationFailed; - case TrustedVaultDownloadKeysStatus::kAccessTokenFetchingFailure: - return TrustedVaultDownloadKeysStatusForUMA::kAccessTokenFetchingFailure; - case TrustedVaultDownloadKeysStatus::kNetworkError: - return TrustedVaultDownloadKeysStatusForUMA::kNetworkError; - case TrustedVaultDownloadKeysStatus::kOtherError: - return TrustedVaultDownloadKeysStatusForUMA::kOtherError; - } - - NOTREACHED(); -} - StandaloneTrustedVaultBackend::StandaloneTrustedVaultBackend( SecurityDomainId security_domain_id, std::unique_ptr<StandaloneTrustedVaultStorage> storage, @@ -296,7 +283,7 @@ // Keys download attempt is not possible because there is no primary // account. FulfillFetchKeys(account_info.gaia, std::move(callback), - TrustedVaultDownloadKeysStatusForUMA::kNoPrimaryAccount); + TrustedVaultRecoverKeysOutcomeForUMA::kNoPrimaryAccount); return; } if (ongoing_fetch_keys_.has_value()) { @@ -327,32 +314,10 @@ connection_.get(), // |this| outlives |local_recovery_factors_|, and destroying // |local_recovery_factors_| guarantees cancellation of all callbacks. - base::BindOnce(&StandaloneTrustedVaultBackend::OnKeysDownloaded, - base::Unretained(this), local_recovery_factor), - base::BindOnce(&StandaloneTrustedVaultBackend::AttemptNextRecoveryFactor, + base::BindOnce(&StandaloneTrustedVaultBackend::OnKeysRecovered, base::Unretained(this), local_recovery_factor)); } -void StandaloneTrustedVaultBackend::AttemptNextRecoveryFactor( - size_t current_local_recovery_factor, - std::optional<TrustedVaultDownloadKeysStatusForUMA> status_for_uma) { - // Callbacks are cancelled if |primary_account_| changes before they're - // executed. - CHECK(ongoing_fetch_keys_); - CHECK(primary_account_.has_value() && - ongoing_fetch_keys_->gaia_id == primary_account_->gaia); - - const size_t next_local_recovery_factor = current_local_recovery_factor + 1; - if (next_local_recovery_factor < local_recovery_factors_.size()) { - AttemptRecoveryFactor(next_local_recovery_factor); - return; - } - - // We ran out of local recovery factors to try, give up with the status from - // the last recovery factor. - FulfillOngoingFetchKeys(status_for_uma); -} - void StandaloneTrustedVaultBackend::StoreKeys( const GaiaId& gaia_id, const std::vector<std::vector<uint8_t>>& keys, @@ -419,7 +384,7 @@ RemoveNonPrimaryAccountKeysIfMarkedForDeletion(); // Make sure to call pending callbacks, now that ongoing recoveries were // aborted. - FulfillOngoingFetchKeys(TrustedVaultDownloadKeysStatusForUMA::kAborted); + FulfillOngoingFetchKeys(TrustedVaultRecoverKeysOutcomeForUMA::kAborted); if (!primary_account_.has_value()) { return; @@ -651,7 +616,7 @@ // ok. local_recovery_factors_ = local_recovery_factors_factory_->CreateLocalRecoveryFactors( - storage_.get(), primary_account_); + security_domain_id_, storage_.get(), primary_account_); } void StandaloneTrustedVaultBackend::MaybeRegisterLocalRecoveryFactors() { @@ -764,16 +729,20 @@ } } -void StandaloneTrustedVaultBackend::OnKeysDownloaded( +void StandaloneTrustedVaultBackend::OnKeysRecovered( size_t current_local_recovery_factor, - TrustedVaultDownloadKeysStatus status, + LocalRecoveryFactor::RecoveryStatus recovery_status, const std::vector<std::vector<uint8_t>>& downloaded_vault_keys, int last_vault_key_version) { - DCHECK(primary_account_.has_value()); + CHECK(primary_account_.has_value()); + // This method should be called only as a result of fetching keys attributed + // to current |ongoing_fetch_keys_|. + CHECK(ongoing_fetch_keys_); + CHECK_EQ(ongoing_fetch_keys_->gaia_id, primary_account_->gaia); bool should_attempt_next_recovery_factor = true; - switch (status) { - case TrustedVaultDownloadKeysStatus::kSuccess: { + switch (recovery_status) { + case LocalRecoveryFactor::RecoveryStatus::kSuccess: { // |downloaded_vault_keys| doesn't necessary have all keys known to the // backend, because some old keys may have been deleted from the server // already. Not preserving old keys is acceptable and desired here, since @@ -784,28 +753,9 @@ should_attempt_next_recovery_factor = false; break; } - case TrustedVaultDownloadKeysStatus::kMemberNotFound: - case TrustedVaultDownloadKeysStatus::kMembershipNotFound: - case TrustedVaultDownloadKeysStatus::kMembershipCorrupted: - case TrustedVaultDownloadKeysStatus::kMembershipEmpty: - case TrustedVaultDownloadKeysStatus::kKeyProofsVerificationFailed: { - // Unable to download new keys due to known protocol errors. The only way - // to go out of these states is to receive new vault keys through external - // StoreKeys() call. It's safe to mark device as not registered regardless - // of the cause (device registration will be triggered once new vault keys - // are available). - local_recovery_factors_[current_local_recovery_factor] - ->MarkAsNotRegistered(); - break; - } - case TrustedVaultDownloadKeysStatus::kNoNewKeys: { - // The registration itself exists, but there's no additional keys to - // download. This is bad because key download attempts are triggered for - // the case where local keys have been marked as stale, which means the - // user is likely in an unrecoverable state. - connection_->RecordFailedRequestForThrottling(*primary_account_); - // Persist the keys anyway, since some old keys could be removed from the - // server. + case LocalRecoveryFactor::RecoveryStatus::kNoNewKeys: { + // Persist the keys even though there are no new ones, since some old keys + // could be removed from the server. StoreKeys(primary_account_->gaia, downloaded_vault_keys, last_vault_key_version); // The server state for different recovery factors is guaranteed to be the @@ -814,26 +764,23 @@ should_attempt_next_recovery_factor = false; break; } - case TrustedVaultDownloadKeysStatus::kAccessTokenFetchingFailure: - case TrustedVaultDownloadKeysStatus::kNetworkError: - // Request wasn't sent to the server, so there is no need for throttling. - break; - case TrustedVaultDownloadKeysStatus::kOtherError: - connection_->RecordFailedRequestForThrottling(*primary_account_); + case LocalRecoveryFactor::RecoveryStatus::kFailure: break; } - // This method should be called only as a result of keys downloading - // attributed to current |ongoing_fetch_keys_|. - DCHECK(ongoing_fetch_keys_); - DCHECK_EQ(ongoing_fetch_keys_->gaia_id, primary_account_->gaia); - if (should_attempt_next_recovery_factor) { - AttemptNextRecoveryFactor(current_local_recovery_factor, - GetDownloadKeysStatusForUMAFromResponse(status)); - } else { - FulfillOngoingFetchKeys(GetDownloadKeysStatusForUMAFromResponse(status)); + const size_t next_local_recovery_factor = current_local_recovery_factor + 1; + if (next_local_recovery_factor < local_recovery_factors_.size()) { + AttemptRecoveryFactor(next_local_recovery_factor); + return; + } } + + // We don't want to attempt the next recovery factor, or we ran out of local + // recovery factors to try. Give up with the status from the last recovery + // factor. + FulfillOngoingFetchKeys( + GetRecoverKeysOutcomeForUMAFromRecoveryStatus(recovery_status)); } void StandaloneTrustedVaultBackend::OnTrustedRecoveryMethodAdded( @@ -849,7 +796,7 @@ } void StandaloneTrustedVaultBackend::FulfillOngoingFetchKeys( - std::optional<TrustedVaultDownloadKeysStatusForUMA> status_for_uma) { + std::optional<TrustedVaultRecoverKeysOutcomeForUMA> status_for_uma) { if (!ongoing_fetch_keys_.has_value()) { return; } @@ -868,12 +815,12 @@ void StandaloneTrustedVaultBackend::FulfillFetchKeys( const GaiaId& gaia_id, FetchKeysCallback callback, - std::optional<TrustedVaultDownloadKeysStatusForUMA> status_for_uma) { + std::optional<TrustedVaultRecoverKeysOutcomeForUMA> status_for_uma) { const trusted_vault_pb::LocalTrustedVaultPerUser* per_user_vault = storage_->FindUserVault(gaia_id); if (status_for_uma.has_value()) { - RecordTrustedVaultDownloadKeysStatus(security_domain_id_, *status_for_uma); + RecordTrustedVaultRecoverKeysOutcome(security_domain_id_, *status_for_uma); } std::vector<std::vector<uint8_t>> vault_keys;
diff --git a/components/trusted_vault/standalone_trusted_vault_backend.h b/components/trusted_vault/standalone_trusted_vault_backend.h index 955cc7d..db9bf8eb 100644 --- a/components/trusted_vault/standalone_trusted_vault_backend.h +++ b/components/trusted_vault/standalone_trusted_vault_backend.h
@@ -68,6 +68,7 @@ // |storage_|. virtual std::vector<std::unique_ptr<LocalRecoveryFactor>> CreateLocalRecoveryFactors( + SecurityDomainId security_domain_id, StandaloneTrustedVaultStorage* storage, const std::optional<CoreAccountInfo>& primary_account) = 0; }; @@ -205,13 +206,10 @@ bool had_local_keys); void AttemptRecoveryFactor(size_t local_recovery_factor); - void AttemptNextRecoveryFactor( - size_t current_local_recovery_factor, - std::optional<TrustedVaultDownloadKeysStatusForUMA> status_for_uma); - void OnKeysDownloaded(size_t current_local_recovery_factor, - TrustedVaultDownloadKeysStatus status, - const std::vector<std::vector<uint8_t>>& new_vault_keys, - int last_vault_key_version); + void OnKeysRecovered(size_t current_local_recovery_factor, + LocalRecoveryFactor::RecoveryStatus status, + const std::vector<std::vector<uint8_t>>& new_vault_keys, + int last_vault_key_version); void OnTrustedRecoveryMethodAdded(base::OnceClosure cb); @@ -219,12 +217,12 @@ void FulfillFetchKeys( const GaiaId& gaia_id, FetchKeysCallback callback, - std::optional<TrustedVaultDownloadKeysStatusForUMA> status_for_uma); + std::optional<TrustedVaultRecoverKeysOutcomeForUMA> status_for_uma); // Same as above, but takes parameters from |ongoing_fetch_keys|, used when // keys are fetched asynchronously, after keys downloading attempt. void FulfillOngoingFetchKeys( - std::optional<TrustedVaultDownloadKeysStatusForUMA> status_for_uma); + std::optional<TrustedVaultRecoverKeysOutcomeForUMA> status_for_uma); // Removes all data for non-primary accounts if they were previously marked // for deletion due to accounts in cookie jar changes.
diff --git a/components/trusted_vault/standalone_trusted_vault_backend_unittest.cc b/components/trusted_vault/standalone_trusted_vault_backend_unittest.cc index cd0ba6c..7e7383c 100644 --- a/components/trusted_vault/standalone_trusted_vault_backend_unittest.cc +++ b/components/trusted_vault/standalone_trusted_vault_backend_unittest.cc
@@ -25,6 +25,7 @@ #include "components/signin/public/identity_manager/account_info.h" #include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h" #include "components/trusted_vault/features.h" +#include "components/trusted_vault/local_recovery_factor.h" #include "components/trusted_vault/proto/local_trusted_vault.pb.h" #include "components/trusted_vault/proto_string_bytes_conversion.h" #include "components/trusted_vault/securebox.h" @@ -121,27 +122,21 @@ return LocalRecoveryFactorType::kPhysicalDevice; } - void AttemptRecovery( - TrustedVaultThrottlingConnection* connection, - AttemptRecoveryCallback callback, - AttemptRecoveryFailureCallback failure_callback) override { + void AttemptRecovery(TrustedVaultThrottlingConnection* connection, + AttemptRecoveryCallback callback) override { CHECK(recovery_callback_.is_null()); attempt_recovery_was_called_ = true; if (!is_registered_) { - std::move(failure_callback) - .Run(TrustedVaultDownloadKeysStatusForUMA::kDeviceNotRegistered); + std::move(callback).Run(RecoveryStatus::kFailure, + /*new_vault_keys=*/{}, + /*last_vault_key_version=*/0); return; } if (connection->AreRequestsThrottled(*account_)) { - std::move(failure_callback) - .Run(TrustedVaultDownloadKeysStatusForUMA::kThrottledClientSide); - return; - } - if (key_pair_corrupt_) { - std::move(failure_callback) - .Run(TrustedVaultDownloadKeysStatusForUMA:: - kCorruptedLocalDeviceRegistration); + std::move(callback).Run(RecoveryStatus::kFailure, + /*new_vault_keys=*/{}, + /*last_vault_key_version=*/0); return; } @@ -204,7 +199,7 @@ bool MaybeRegisterWasCalled() const { return maybe_register_was_called_; } void ExpectAttemptRecoveryAndRunCallback( - TrustedVaultDownloadKeysStatus status, + RecoveryStatus status, const std::vector<std::vector<uint8_t>>& new_vault_keys, int last_vault_key_version) { ASSERT_FALSE(recovery_callback_.is_null()); @@ -238,7 +233,6 @@ bool is_registered_ = false; bool last_registration_returned_local_data_obsolete_ = false; bool key_pair_exists_ = false; - bool key_pair_corrupt_ = false; bool attempt_recovery_was_called_ = false; bool maybe_register_was_called_ = false; AttemptRecoveryCallback recovery_callback_; @@ -265,12 +259,9 @@ LocalRecoveryFactorType GetRecoveryFactorType() const override { return delegate_->GetRecoveryFactorType(); } - void AttemptRecovery( - TrustedVaultThrottlingConnection* connection, - AttemptRecoveryCallback callback, - AttemptRecoveryFailureCallback failure_callback) override { - delegate_->AttemptRecovery(connection, std::move(callback), - std::move(failure_callback)); + void AttemptRecovery(TrustedVaultThrottlingConnection* connection, + AttemptRecoveryCallback callback) override { + delegate_->AttemptRecovery(connection, std::move(callback)); } bool IsRegistered() override { return delegate_->IsRegistered(); } void MarkAsNotRegistered() override { delegate_->MarkAsNotRegistered(); } @@ -301,6 +292,7 @@ ~TestLocalRecoveryFactorsFactory() override = default; std::vector<std::unique_ptr<LocalRecoveryFactor>> CreateLocalRecoveryFactors( + SecurityDomainId security_domain_id, StandaloneTrustedVaultStorage* storage, const std::optional<CoreAccountInfo>& account) override { std::vector<FakeLocalRecoveryFactor*> fake_recovery_factors = @@ -1170,7 +1162,7 @@ GetOrCreateRecoveryFactor(kAccountInfo) ->ExpectAttemptRecoveryAndRunCallback( - TrustedVaultDownloadKeysStatus::kSuccess, {kNewVaultKey}, + LocalRecoveryFactor::RecoveryStatus::kSuccess, {kNewVaultKey}, kInitialLastKeyVersion + 1); } @@ -1208,79 +1200,16 @@ base::HistogramTester histogram_tester; GetOrCreateRecoveryFactor(kAccountInfo) ->ExpectAttemptRecoveryAndRunCallback( - TrustedVaultDownloadKeysStatus::kSuccess, + LocalRecoveryFactor::RecoveryStatus::kSuccess, {kInitialVaultKey, kNewVaultKey}, kInitialLastKeyVersion + 1); - // Download keys status should be recorded for every fetch. + // Recover keys status should be recorded for every fetch. histogram_tester.ExpectUniqueSample( - "TrustedVault.DownloadKeysStatus." + security_domain_name_for_uma(), - /*sample=*/TrustedVaultDownloadKeysStatusForUMA::kSuccess, + "TrustedVault.RecoverKeysOutcome." + security_domain_name_for_uma(), + /*sample=*/TrustedVaultRecoverKeysOutcomeForUMA::kSuccess, /*expected_bucket_count=*/2); } -TEST_F(StandaloneTrustedVaultBackendTest, ShouldThrottleKeysDownloading) { - const CoreAccountInfo kAccountInfo = MakeAccountInfoWithGaiaId("user"); - const std::vector<uint8_t> kInitialVaultKey = {1, 2, 3}; - const int kInitialLastKeyVersion = 1; - - StoreKeysAndMimicDeviceRegistration({kInitialVaultKey}, - kInitialLastKeyVersion, kAccountInfo); - EXPECT_TRUE(backend()->MarkLocalKeysAsStale(kAccountInfo)); - SetPrimaryAccountWithUnknownAuthError(kAccountInfo); - - // FetchKeys() should trigger keys downloading. - backend()->FetchKeys(kAccountInfo, base::DoNothing()); - - // Mimic transient failure. - EXPECT_CALL(*connection(), RecordFailedRequestForThrottling); - base::HistogramTester histogram_tester; - GetOrCreateRecoveryFactor(kAccountInfo) - ->ExpectAttemptRecoveryAndRunCallback( - TrustedVaultDownloadKeysStatus::kOtherError, - std::vector<std::vector<uint8_t>>(), 0); - - histogram_tester.ExpectUniqueSample( - "TrustedVault.DownloadKeysStatus." + security_domain_name_for_uma(), - /*sample=*/TrustedVaultDownloadKeysStatusForUMA::kOtherError, - /*expected_bucket_count=*/1); - Mock::VerifyAndClearExpectations(connection()); - - // The backend doesn't check for throttling itself, but the fake recovery - // factor does so. The backend is expected to call AttemptRecovery() again. - EXPECT_CALL(*connection(), AreRequestsThrottled); - GetOrCreateRecoveryFactor(kAccountInfo)->ResetCallInfo(); - backend()->FetchKeys(kAccountInfo, base::DoNothing()); - EXPECT_TRUE( - GetOrCreateRecoveryFactor(kAccountInfo)->AttemptRecoveryWasCalled()); -} - -TEST_F(StandaloneTrustedVaultBackendTest, - ShouldThrottleIfDownloadingReturnedNoNewKeys) { - const CoreAccountInfo kAccountInfo = MakeAccountInfoWithGaiaId("user"); - const std::vector<uint8_t> kInitialVaultKey = {1, 2, 3}; - const int kInitialLastKeyVersion = 1; - - StoreKeysAndMimicDeviceRegistration({kInitialVaultKey}, - kInitialLastKeyVersion, kAccountInfo); - EXPECT_TRUE(backend()->MarkLocalKeysAsStale(kAccountInfo)); - SetPrimaryAccountWithUnknownAuthError(kAccountInfo); - - // FetchKeys() should trigger keys downloading. - backend()->FetchKeys(kAccountInfo, /*callback=*/base::DoNothing()); - - // Mimic the server having no new keys. - EXPECT_CALL(*connection(), RecordFailedRequestForThrottling); - base::HistogramTester histogram_tester; - GetOrCreateRecoveryFactor(kAccountInfo) - ->ExpectAttemptRecoveryAndRunCallback( - TrustedVaultDownloadKeysStatus::kNoNewKeys, - std::vector<std::vector<uint8_t>>(), 0); - histogram_tester.ExpectUniqueSample( - "TrustedVault.DownloadKeysStatus." + security_domain_name_for_uma(), - /*sample=*/TrustedVaultDownloadKeysStatusForUMA::kNoNewKeys, - /*expected_bucket_count=*/1); -} - TEST_F(StandaloneTrustedVaultBackendTest, ShouldDownloadKeysFromFirstRecoveryFactorFirst) { const CoreAccountInfo kAccountInfo = MakeAccountInfoWithGaiaId("user"); @@ -1309,7 +1238,7 @@ ASSERT_THAT(recovery_factors, SizeIs(2)); // First recovery factor should have been called to attempt recovery. recovery_factors[0]->ExpectAttemptRecoveryAndRunCallback( - TrustedVaultDownloadKeysStatus::kSuccess, {kVaultKey, kNewVaultKey}, + LocalRecoveryFactor::RecoveryStatus::kSuccess, {kVaultKey, kNewVaultKey}, kInitialLastKeyVersion + 1); // Second recovery factor should not have been called. EXPECT_FALSE(recovery_factors[1]->AttemptRecoveryWasCalled()); @@ -1344,10 +1273,11 @@ // First recovery factor should have been called to attempt recovery. Let it // fail. recovery_factors[0]->ExpectAttemptRecoveryAndRunCallback( - TrustedVaultDownloadKeysStatus::kMemberNotFound, {}, 0); + LocalRecoveryFactor::RecoveryStatus::kFailure, /*new_vault_keys=*/{}, + /*last_vault_key_version=*/0); // Second recovery factor should have been called. recovery_factors[1]->ExpectAttemptRecoveryAndRunCallback( - TrustedVaultDownloadKeysStatus::kSuccess, {kVaultKey, kNewVaultKey}, + LocalRecoveryFactor::RecoveryStatus::kSuccess, {kVaultKey, kNewVaultKey}, kInitialLastKeyVersion + 1); } @@ -1379,14 +1309,16 @@ // First recovery factor should have been called to attempt recovery. Let it // fail. recovery_factors[0]->ExpectAttemptRecoveryAndRunCallback( - TrustedVaultDownloadKeysStatus::kMemberNotFound, {}, 0); + LocalRecoveryFactor::RecoveryStatus::kFailure, /*new_vault_keys=*/{}, + /*last_vault_key_version=*/0); // Second recovery factor should have been called. Let it also fail. recovery_factors[1]->ExpectAttemptRecoveryAndRunCallback( - TrustedVaultDownloadKeysStatus::kNetworkError, {}, 0); + LocalRecoveryFactor::RecoveryStatus::kFailure, /*new_vault_keys=*/{}, + /*last_vault_key_version=*/0); // Status of the last recovery factor should be recorded. histogram_tester.ExpectUniqueSample( - "TrustedVault.DownloadKeysStatus." + security_domain_name_for_uma(), - /*sample=*/TrustedVaultDownloadKeysStatusForUMA::kNetworkError, + "TrustedVault.RecoverKeysOutcome." + security_domain_name_for_uma(), + /*sample=*/TrustedVaultRecoverKeysOutcomeForUMA::kFailure, /*expected_bucket_count=*/1); } @@ -1404,7 +1336,7 @@ GetOrCreateRecoveryFactor(kAccountInfo) ->ExpectMaybeRegisterAndRunCallback( TrustedVaultRegistrationStatus::kSuccess, kServerConstantKeyVersion, - false); + /*had_local_keys=*/false); // Now the device should be registered. EXPECT_TRUE(GetOrCreateRecoveryFactor(kAccountInfo)->IsRegistered()); @@ -1422,7 +1354,7 @@ EXPECT_CALL(fetch_keys_callback, Run(/*keys=*/kNewVaultKeys)); GetOrCreateRecoveryFactor(kAccountInfo) ->ExpectAttemptRecoveryAndRunCallback( - TrustedVaultDownloadKeysStatus::kSuccess, kNewVaultKeys, + LocalRecoveryFactor::RecoveryStatus::kSuccess, kNewVaultKeys, kServerConstantKeyVersion + 1); }
diff --git a/components/trusted_vault/trusted_vault_histograms.cc b/components/trusted_vault/trusted_vault_histograms.cc index 9506fc8d..4ad9d6c0 100644 --- a/components/trusted_vault/trusted_vault_histograms.cc +++ b/components/trusted_vault/trusted_vault_histograms.cc
@@ -171,17 +171,30 @@ } void RecordTrustedVaultDownloadKeysStatus( + LocalRecoveryFactorType local_recovery_factor_type, SecurityDomainId security_domain_id, TrustedVaultDownloadKeysStatusForUMA status) { base::UmaHistogramEnumeration( - "TrustedVault.DownloadKeysStatus." + - GetSecurityDomainNameForUma(security_domain_id), + base::StrCat( + {"TrustedVault.DownloadKeysStatus.", + GetLocalRecoveryFactorNameForUma(local_recovery_factor_type), ".", + GetSecurityDomainNameForUma(security_domain_id)}), status); } void RecordTrustedVaultDownloadKeysStatus( TrustedVaultDownloadKeysStatusForUMA status) { - RecordTrustedVaultDownloadKeysStatus(SecurityDomainId::kChromeSync, status); + RecordTrustedVaultDownloadKeysStatus(LocalRecoveryFactorType::kPhysicalDevice, + SecurityDomainId::kChromeSync, status); +} + +void RecordTrustedVaultRecoverKeysOutcome( + SecurityDomainId security_domain_id, + TrustedVaultRecoverKeysOutcomeForUMA status) { + base::UmaHistogramEnumeration( + base::StrCat({"TrustedVault.RecoverKeysOutcome.", + GetSecurityDomainNameForUma(security_domain_id)}), + status); } void RecordTrustedVaultFileReadStatus(SecurityDomainId security_domain_id,
diff --git a/components/trusted_vault/trusted_vault_histograms.h b/components/trusted_vault/trusted_vault_histograms.h index a635be4..e55a0b1c 100644 --- a/components/trusted_vault/trusted_vault_histograms.h +++ b/components/trusted_vault/trusted_vault_histograms.h
@@ -102,6 +102,19 @@ // These values are persisted to logs. Entries should not be renumbered and // numeric values should never be reused. +// LINT.IfChange(TrustedVaultRecoverKeysOutcome) +enum class TrustedVaultRecoverKeysOutcomeForUMA { + kSuccess = 0, + kNoNewKeys = 1, + kFailure = 2, + kNoPrimaryAccount = 3, + kAborted = 4, + kMaxValue = kAborted +}; +// LINT.ThenChange(/tools/metrics/histograms/metadata/trusted_vault/enums.xml:TrustedVaultRecoverKeysOutcome) + +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. // LINT.IfChange(TrustedVaultFileReadStatus) enum class TrustedVaultFileReadStatusForUMA { kSuccess = 0, @@ -164,6 +177,7 @@ // Records the outcome of trying to download keys from the server. void RecordTrustedVaultDownloadKeysStatus( + LocalRecoveryFactorType local_recovery_factor_type, SecurityDomainId security_domain_id, TrustedVaultDownloadKeysStatusForUMA status); @@ -172,6 +186,10 @@ void RecordTrustedVaultDownloadKeysStatus( TrustedVaultDownloadKeysStatusForUMA status); +void RecordTrustedVaultRecoverKeysOutcome( + SecurityDomainId security_domain_id, + TrustedVaultRecoverKeysOutcomeForUMA status); + void RecordTrustedVaultFileReadStatus(SecurityDomainId security_domain_id, TrustedVaultFileReadStatusForUMA status);
diff --git a/content/browser/accessibility/browser_accessibility_state_impl.cc b/content/browser/accessibility/browser_accessibility_state_impl.cc index b835936..9a16834 100644 --- a/content/browser/accessibility/browser_accessibility_state_impl.cc +++ b/content/browser/accessibility/browser_accessibility_state_impl.cc
@@ -232,6 +232,10 @@ } } + if (::features::IsAccessibilityOnScreenAXModeEnabled()) { + initial_mode |= ui::kAXModeOnScreen; + } + // Create an initial process-wide ScopedAccessibilityMode whether any flags // are enabled or not. Always creating a ScopedAccessibilityMode // (even if it holds a mode with all flags off) allows us to avoid null @@ -508,9 +512,6 @@ std::unique_ptr<ScopedAccessibilityMode> BrowserAccessibilityStateImpl::CreateScopedModeForProcess(ui::AXMode mode) { - if (::features::IsAccessibilityOnScreenAXModeEnabled()) { - mode = ui::kAXModeOnScreen; - } return scoped_modes_for_process_.Add(mode); }
diff --git a/content/browser/btm/btm_service_impl.cc b/content/browser/btm/btm_service_impl.cc index 9458e7e..ace49629 100644 --- a/content/browser/btm/btm_service_impl.cc +++ b/content/browser/btm/btm_service_impl.cc
@@ -56,11 +56,6 @@ namespace { -// Controls whether the database requests are executed on a foreground sequence. -BASE_FEATURE(kDipsOnForegroundSequence, - "DipsOnForegroundSequence", - base::FEATURE_DISABLED_BY_DEFAULT); - BtmRedirectCategory ClassifyRedirect(BtmDataAccessType access, bool has_user_activation) { using enum BtmRedirectCategory; @@ -335,9 +330,6 @@ } scoped_refptr<base::SequencedTaskRunner> BtmServiceImpl::CreateTaskRunner() { - if (base::FeatureList::IsEnabled(kDipsOnForegroundSequence)) { - return base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}); - } return base::ThreadPool::CreateSequencedTaskRunner( {base::MayBlock(), base::TaskPriority::BEST_EFFORT, base::ThreadPolicy::PREFER_BACKGROUND}); @@ -345,10 +337,6 @@ scoped_refptr<base::SequencedTaskRunner> BtmServiceImpl::CreateTaskRunnerForResource(const base::FilePath& path) { - if (base::FeatureList::IsEnabled(kDipsOnForegroundSequence)) { - return base::ThreadPool::CreateSequencedTaskRunnerForResource( - {base::MayBlock()}, path); - } return base::ThreadPool::CreateSequencedTaskRunnerForResource( {base::MayBlock(), base::TaskPriority::BEST_EFFORT, base::ThreadPolicy::PREFER_BACKGROUND},
diff --git a/content/public/browser/desktop_capture.cc b/content/public/browser/desktop_capture.cc index b33b03fa..400190e 100644 --- a/content/public/browser/desktop_capture.cc +++ b/content/public/browser/desktop_capture.cc
@@ -37,6 +37,15 @@ } #endif // BUILDFLAG(IS_MAC) +// Enabled-by-default, but exists as a kill-switch. +// TODO(crbug.com/409473386): Remove this flag once it has been in stable for a +// few milestones. +#if BUILDFLAG(IS_WIN) +BASE_FEATURE(kUseHeuristicForWindowsFullScreenPowerPoint, + "UseHeuristicForWindowsFullScreenPowerPoint", + base::FEATURE_ENABLED_BY_DEFAULT); +#endif + namespace content::desktop_capture { webrtc::DesktopCaptureOptions CreateDesktopCaptureOptions() { @@ -44,6 +53,10 @@ // Leave desktop effects enabled during WebRTC captures. options.set_disable_effects(false); #if BUILDFLAG(IS_WIN) + options.full_screen_window_detector() + ->SetUseHeuristicFullscreenPowerPointWindows(base::FeatureList::IsEnabled( + kUseHeuristicForWindowsFullScreenPowerPoint)); + // TODO(crbug.com/webrtc/15045): Possibly remove this flag. Keeping for now // to force fallback to GDI. static BASE_FEATURE(kDirectXCapturer, "DirectXCapturer",
diff --git a/content/public/test/test_devtools_protocol_client.cc b/content/public/test/test_devtools_protocol_client.cc index a1692fa..0170cc1 100644 --- a/content/public/test/test_devtools_protocol_client.cc +++ b/content/public/test/test_devtools_protocol_client.cc
@@ -153,7 +153,7 @@ } void TestDevToolsProtocolClient::RunLoopUpdatingQuitClosure() { - base::RunLoop run_loop; + base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); run_loop_quit_closure_ = run_loop.QuitClosure(); run_loop.Run(); }
diff --git a/extensions/browser/api/management/management_api.cc b/extensions/browser/api/management/management_api.cc index 70204b5..4edf48d 100644 --- a/extensions/browser/api/management/management_api.cc +++ b/extensions/browser/api/management/management_api.cc
@@ -283,12 +283,9 @@ } bool PlatformSupportsApprovalFlowForExtensions() { -#if BUILDFLAG(IS_CHROMEOS) - // ChromeOS devices have this feature already shipped. +#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ + BUILDFLAG(IS_WIN) return true; -#elif BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) - return base::FeatureList::IsEnabled( - supervised_user::kEnableExtensionsPermissionsForSupervisedUsersOnDesktop); #else return false; #endif
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc index 06bbe07..8b6a4d8e 100644 --- a/extensions/browser/extension_prefs.cc +++ b/extensions/browser/extension_prefs.cc
@@ -49,6 +49,7 @@ #include "extensions/browser/install_flag.h" #include "extensions/browser/install_prefs_helper.h" #include "extensions/browser/pref_names.h" +#include "extensions/browser/user_script_manager.h" #include "extensions/common/api/types.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" @@ -2293,6 +2294,10 @@ registry->RegisterBooleanPref( kMV2DeprecationUnsupportedAcknowledgedGloballyPref.name, false); registry->RegisterStringPref(pref_names::kGlobalShortcutsUuid, std::string()); + + registry->RegisterBooleanPref( + UserScriptManager::kUserScriptsToggleMigratedPref.name, + /*default_value=*/false); } template <class ExtensionIdContainer>
diff --git a/extensions/browser/renderer_startup_helper.cc b/extensions/browser/renderer_startup_helper.cc index 8ce739b..640c969 100644 --- a/extensions/browser/renderer_startup_helper.cc +++ b/extensions/browser/renderer_startup_helper.cc
@@ -128,14 +128,14 @@ // TODO(crbug.com/390138269): Optimize by only setting the value for the // process(es) that host an extension that can use the userScripts API. - const UserScriptManager* user_script_manager = + UserScriptManager* user_script_manager = ExtensionSystem::Get(browser_context)->user_script_manager(); if (!user_script_manager) { CHECK_IS_TEST(); } bool user_scripts_allowed = user_script_manager && - user_script_manager->AreUserScriptsAllowed(extension, browser_context); + user_script_manager->AreUserScriptsAllowed(extension); return mojom::ExtensionLoadedParams::New( extension.manifest()->value()->Clone(), extension.location(),
diff --git a/extensions/browser/user_script_manager.cc b/extensions/browser/user_script_manager.cc index 3b9da6ad..42f5545 100644 --- a/extensions/browser/user_script_manager.cc +++ b/extensions/browser/user_script_manager.cc
@@ -5,6 +5,8 @@ #include "extensions/browser/user_script_manager.h" #include "base/containers/contains.h" +#include "base/feature_list.h" +#include "base/one_shot_event.h" #include "content/public/browser/browser_context.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry.h" @@ -31,15 +33,6 @@ namespace extensions { -namespace { - -// Key corresponding to whether the user has allowed user scripts to run for the -// extension. -constexpr PrefMap kUserScriptsAllowedPref = { - "user_scripts_enabled", PrefType::kBool, PrefScope::kExtensionSpecific}; - -} // namespace - UserScriptManager::UserScriptManager(content::BrowserContext* browser_context) : browser_context_(browser_context) { extension_registry_observation_.Observe( @@ -50,6 +43,22 @@ if (store) { store->RegisterKey(scripting::kRegisteredScriptsStorageKey); } + + // The preference migrated is for any instance of an extension so we don't + // need to also migrate for any off the record contexts. + if (!browser_context_->IsOffTheRecord() && + // Additionally only migrate if the feature is enabled and we haven't + // already completed the one-time migration. + base::FeatureList::IsEnabled( + extensions_features::kUserScriptUserExtensionToggle) && + !ExtensionPrefs::Get(browser_context_) + ->GetPrefAsBoolean(kUserScriptsToggleMigratedPref)) { + ExtensionSystem::Get(browser_context_) + ->ready() + .Post(FROM_HERE, + base::BindOnce(&UserScriptManager::MigrateUserScriptExtensions, + weak_factory_.GetWeakPtr())); + } } UserScriptManager::~UserScriptManager() = default; @@ -103,26 +112,42 @@ } } -bool UserScriptManager::AreUserScriptsAllowed( - const Extension& extension, - content::BrowserContext* browser_context) const { +void UserScriptManager::InitializeUserScriptState(const Extension& extension) { + // If the one-time migration for all extensions hasn't completed yet (e.g. + // this is during startup), migrate this extension. + if (!ExtensionPrefs::Get(browser_context_) + ->GetPrefAsBoolean(kUserScriptsToggleMigratedPref)) { + MigrateUserScriptExtension(extension); + } + + SetCurrentUserScriptAllowedState(util::GetBrowserContextId(browser_context_), + extension.id(), + IsUserScriptPrefEnabled(extension.id())); +} + +bool UserScriptManager::AreUserScriptsAllowed(const Extension& extension) { + if (!base::FeatureList::IsEnabled( + extensions_features::kUserScriptUserExtensionToggle)) { + return GetCurrentDeveloperMode(util::GetBrowserContextId(browser_context_)); + } + + std::optional<bool> allowed_state = GetCurrentUserScriptAllowedState( + util::GetBrowserContextId(browser_context_), extension.id()); + if (!allowed_state.has_value()) { + InitializeUserScriptState(extension); + allowed_state = GetCurrentUserScriptAllowedState( + util::GetBrowserContextId(browser_context_), extension.id()); + } + CHECK(allowed_state.has_value()); + return IsUserScriptsAPIPermissionAvailable(extension) && - // We check the pref directly (instead of - // GetCurrentUserScriptAllowedState() because this method can be called - // before the allowed state is set. - IsUserScriptPrefEnabled(extension.id()); + *GetCurrentUserScriptAllowedState( + util::GetBrowserContextId(browser_context_), extension.id()); } // static bool UserScriptManager::IsUserScriptsAPIPermissionAvailable( const Extension& extension) { - // TODO(crbug.com/390138269): Once finch flag is default, remove the - // feature restriction. - if (!base::FeatureList::IsEnabled( - extensions_features::kUserScriptUserExtensionToggle)) { - return false; - } - return extension.permissions_data()->HasAPIPermission( mojom::APIPermissionID::kUserScripts) || PermissionsParser::GetOptionalPermissions(&extension) @@ -178,14 +203,6 @@ content::BrowserContext* browser_context, const Extension* extension) { CHECK(extension); - // Seed the browser's user script allowed state in case this is the first time - // we are creating the loader. - if (IsUserScriptsAPIPermissionAvailable(*extension)) { - SetCurrentUserScriptAllowedState( - util::GetBrowserContextId(browser_context_), extension->id(), - IsUserScriptPrefEnabled(extension->id())); - } - ExtensionUserScriptLoader* loader = GetUserScriptLoaderForExtension(extension->id()); @@ -239,17 +256,8 @@ /*listen_for_extension_system_loaded=*/true)) .first->second.get(); - if (base::FeatureList::IsEnabled( - extensions_features::kUserScriptUserExtensionToggle)) { - loader->SetSourceEnabled( - UserScript::Source::kDynamicUserScript, - GetCurrentUserScriptAllowedState( - util::GetBrowserContextId(browser_context_), extension->id())); - } else { - loader->SetSourceEnabled( - UserScript::Source::kDynamicUserScript, - GetCurrentDeveloperMode(util::GetBrowserContextId(browser_context_))); - } + loader->SetSourceEnabled(UserScript::Source::kDynamicUserScript, + AreUserScriptsAllowed(*extension)); return loader; } @@ -277,4 +285,28 @@ return user_scripts_pref_allowed; } +void UserScriptManager::MigrateUserScriptExtension(const Extension& extension) { + // If extension can't use the API, it doesn't need to be migrated. + if (!IsUserScriptsAPIPermissionAvailable(extension)) { + return; + } + + // If the permission is *granted* and dev mode is on then user scripts allowed + // pref is set to true, otherwise false. + bool permission_granted = extension.permissions_data()->HasAPIPermission( + mojom::APIPermissionID::kUserScripts); + bool dev_mode_on = + GetCurrentDeveloperMode(util::GetBrowserContextId(browser_context_)); + SetUserScriptPrefEnabled(extension.id(), permission_granted && dev_mode_on); +} + +void UserScriptManager::MigrateUserScriptExtensions() { + for (auto& installed_extension : ExtensionRegistry::Get(browser_context_) + ->GenerateInstalledExtensionsSet()) { + MigrateUserScriptExtension(*installed_extension); + } + ExtensionPrefs::Get(browser_context_) + ->SetBooleanPref(kUserScriptsToggleMigratedPref, /*value=*/true); +} + } // namespace extensions
diff --git a/extensions/browser/user_script_manager.h b/extensions/browser/user_script_manager.h index 22372733..9a440b91 100644 --- a/extensions/browser/user_script_manager.h +++ b/extensions/browser/user_script_manager.h
@@ -18,6 +18,7 @@ #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_registry_observer.h" #include "extensions/browser/extension_user_script_loader.h" +#include "extensions/browser/pref_types.h" #include "extensions/common/extension.h" #include "extensions/common/mojom/host_id.mojom.h" #include "extensions/common/user_script.h" @@ -41,6 +42,16 @@ UserScriptManager(const UserScriptManager& other) = delete; UserScriptManager& operator=(const UserScriptManager& other) = delete; + // Key corresponding to whether the user has allowed user scripts to run for + // the extension. + static constexpr PrefMap kUserScriptsAllowedPref = { + "user_scripts_enabled", PrefType::kBool, PrefScope::kExtensionSpecific}; + // Key corresponding to whether the migration from using the dev mode toggle + // to the per-extension toggle for enabling dynamic user scripts usage has + // completed. + static constexpr PrefMap kUserScriptsToggleMigratedPref = { + "migrated_user_scripts_toggle", PrefType::kBool, PrefScope::kProfile}; + UserScriptLoader* GetUserScriptLoaderByID(const mojom::HostID& host_id); ExtensionUserScriptLoader* GetUserScriptLoaderForExtension( @@ -55,10 +66,9 @@ bool enabled); // Returns true if the extension is allowed to use the userScripts API. - // TODO(crbug.com/390138269): Test this behavior once this preference is - // controlling user script injection. - bool AreUserScriptsAllowed(const Extension& extension, - content::BrowserContext* browser_context) const; + // Note: this may also seed feature availability state the first time it is + // called so that it is always accurate. + bool AreUserScriptsAllowed(const Extension& extension); // Returns whether the extension has permission to run user scripts or can // request permission to do so. @@ -101,9 +111,20 @@ EmbedderUserScriptLoader* CreateEmbedderUserScriptLoader( const mojom::HostID& host_id); + // Migrate an extension from dev mode toggle to per-extension toggle if not + // done, otherwise just set the allowed state from the current allowed + // preference. + void InitializeUserScriptState(const Extension& extension); + // Get extension preference for userScripts API being allowed. bool IsUserScriptPrefEnabled(const ExtensionId& extension_id) const; + // Migrates an eligible extension to use the per-extension toggle. + void MigrateUserScriptExtension(const Extension& extension); + + // Migrates all non-enabled extensions to use the per-extension toggle. + void MigrateUserScriptExtensions(); + // A map of ExtensionUserScriptLoader for each extension host, with one loader // per extension. Currently, each loader is lazily initialized and contains // scripts from APIs webview tags.
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn index 08aed54..331a552 100644 --- a/extensions/common/BUILD.gn +++ b/extensions/common/BUILD.gn
@@ -691,6 +691,7 @@ "manifest_handlers/update_url_unittest.cc", "manifest_unittest.cc", "message_bundle_unittest.cc", + "mojom/event_router_mojom_unittest.cc", "mojom/extension_id_mojom_traits_unittest.cc", "mojom/message_port_mojom_traits_unittest.cc", "mojom/permission_set_mojom_traits_unittest.cc",
diff --git a/extensions/common/mojom/event_router_mojom_unittest.cc b/extensions/common/mojom/event_router_mojom_unittest.cc new file mode 100644 index 0000000..7c0a491 --- /dev/null +++ b/extensions/common/mojom/event_router_mojom_unittest.cc
@@ -0,0 +1,265 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string> +#include <utility> + +#include "base/test/task_environment.h" +#include "extensions/common/extension_id.h" +#include "extensions/common/mojom/event_router.mojom.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +namespace { + +// Test implementation of mojom::EventRouter. +class TestEventRouterImpl : public mojom::EventRouter { + public: + explicit TestEventRouterImpl( + mojo::PendingReceiver<mojom::EventRouter> receiver) + : receiver_(this, std::move(receiver)) {} + TestEventRouterImpl(const TestEventRouterImpl&) = delete; + TestEventRouterImpl& operator=(const TestEventRouterImpl&) = delete; + + // mojom::EventRouter: + void AddListenerForMainThread( + mojom::EventListenerPtr event_listener) override {} + void AddListenerForServiceWorker( + mojom::EventListenerPtr event_listener) override {} + void AddLazyListenerForMainThread(const ExtensionId& extension_id, + const std::string& name) override {} + void AddLazyListenerForServiceWorker(const ExtensionId& extension_id, + const GURL& worker_scope_url, + const std::string& name) override {} + void AddFilteredListenerForMainThread( + mojom::EventListenerOwnerPtr listener_owner, + const std::string& name, + base::Value::Dict filter, + bool add_lazy_listener) override {} + void AddFilteredListenerForServiceWorker( + const ExtensionId& extension_id, + const std::string& name, + mojom::ServiceWorkerContextPtr service_worker_context, + base::Value::Dict filter, + bool add_lazy_listener) override {} + void RemoveListenerForMainThread( + mojom::EventListenerPtr event_listener) override {} + void RemoveListenerForServiceWorker( + mojom::EventListenerPtr event_listener) override {} + void RemoveLazyListenerForMainThread(const ExtensionId& extension_id, + const std::string& name) override {} + void RemoveLazyListenerForServiceWorker(const ExtensionId& extension_id, + const GURL& worker_scope_url, + const std::string& name) override {} + void RemoveFilteredListenerForMainThread( + mojom::EventListenerOwnerPtr listener_owner, + const std::string& name, + base::Value::Dict filter, + bool remove_lazy_listener) override {} + void RemoveFilteredListenerForServiceWorker( + const ExtensionId& extension_id, + const std::string& name, + mojom::ServiceWorkerContextPtr service_worker_context, + base::Value::Dict filter, + bool remove_lazy_listener) override {} + + private: + mojo::Receiver<mojom::EventRouter> receiver_; +}; + +class EventRouterMojomExtensionIdTest : public testing::Test { + public: + EventRouterMojomExtensionIdTest() { + event_router_impl_ = std::make_unique<TestEventRouterImpl>( + event_router_remote_.BindNewPipeAndPassReceiver()); + } + + void AddListenerForMainThread(const ExtensionId& extension_id) { + auto event_listener = CreateEventListener(extension_id); + event_router_remote_->AddListenerForMainThread(std::move(event_listener)); + event_router_remote_.FlushForTesting(); + } + + void AddListenerForServiceWorker(const ExtensionId& extension_id) { + auto event_listener = CreateEventListener(extension_id); + event_router_remote_->AddListenerForServiceWorker( + std::move(event_listener)); + event_router_remote_.FlushForTesting(); + } + + void AddLazyListenerForMainThread(const ExtensionId& extension_id) { + event_router_remote_->AddLazyListenerForMainThread(extension_id, + "test_listener_name"); + event_router_remote_.FlushForTesting(); + } + + void AddLazyListenerForServiceWorker(const ExtensionId& extension_id) { + event_router_remote_->AddLazyListenerForServiceWorker( + extension_id, GURL("test_worker_scope"), "test_event_name"); + event_router_remote_.FlushForTesting(); + } + + void AddFilteredListenerForMainThread(const ExtensionId& extension_id) { + auto event_listener = CreateEventListener(extension_id); + event_router_remote_->AddFilteredListenerForMainThread( + std::move(event_listener->listener_owner), "test_event_name", + /*filter=*/base::Value::Dict(), /*add_lazy_listener=*/true); + event_router_remote_.FlushForTesting(); + } + + void RemoveListenerForMainThread(const ExtensionId& extension_id) { + auto event_listener = CreateEventListener(extension_id); + event_router_remote_->RemoveListenerForMainThread( + std::move(event_listener)); + event_router_remote_.FlushForTesting(); + } + + void RemoveListenerForServiceWorker(const ExtensionId& extension_id) { + auto event_listener = CreateEventListener(extension_id); + event_router_remote_->RemoveListenerForServiceWorker( + std::move(event_listener)); + event_router_remote_.FlushForTesting(); + } + + void RemoveLazyListenerForMainThread(const ExtensionId& extension_id) { + event_router_remote_->RemoveLazyListenerForMainThread(extension_id, + "test_listener_name"); + event_router_remote_.FlushForTesting(); + } + + void RemoveLazyListenerForServiceWorker(const ExtensionId& extension_id) { + event_router_remote_->RemoveLazyListenerForServiceWorker( + extension_id, GURL("test_worker_scope"), "test_event_name"); + event_router_remote_.FlushForTesting(); + } + + void RemoveFilteredListenerForMainThread(const ExtensionId& extension_id) { + auto event_listener = CreateEventListener(extension_id); + event_router_remote_->RemoveFilteredListenerForMainThread( + std::move(event_listener->listener_owner), "test_event_name", + /*filter=*/base::Value::Dict(), /*remove_lazy_listener=*/true); + event_router_remote_.FlushForTesting(); + } + + bool PipeConnected() { return event_router_remote_.is_connected(); } + + void RebindReceiver() { + event_router_impl_.reset(); + event_router_remote_.reset(); + event_router_impl_ = std::make_unique<TestEventRouterImpl>( + event_router_remote_.BindNewPipeAndPassReceiver()); + } + + private: + mojom::EventListenerPtr CreateEventListener(const ExtensionId& extension_id) { + return mojom::EventListenerPtr(mojom::EventListener::New( + mojom::EventListenerOwner::NewExtensionId(extension_id), + "test_event_name", + mojom::ServiceWorkerContext::New(GURL("test_worker_scope"), + /*version_id=*/0, /*thread_id=*/0), + /*event_filter=*/std::nullopt)); + } + + base::test::SingleThreadTaskEnvironment task_environment; + mojo::Remote<mojom::EventRouter> event_router_remote_; + std::unique_ptr<TestEventRouterImpl> event_router_impl_; +}; + +// Tests that passing valid extension IDs to mojom::EventRouter implementations +// pass message validation and keep the mojom pipe connected. +TEST_F(EventRouterMojomExtensionIdTest, ValidExtensionId) { + // Create a valid ExtensionId. + ExtensionId valid_extension_id(32, 'a'); + + AddListenerForMainThread(valid_extension_id); + ASSERT_TRUE(PipeConnected()); + + AddListenerForServiceWorker(valid_extension_id); + ASSERT_TRUE(PipeConnected()); + + AddLazyListenerForMainThread(valid_extension_id); + ASSERT_TRUE(PipeConnected()); + + AddLazyListenerForServiceWorker(valid_extension_id); + ASSERT_TRUE(PipeConnected()); + + AddFilteredListenerForMainThread(valid_extension_id); + ASSERT_TRUE(PipeConnected()); + + RemoveListenerForMainThread(valid_extension_id); + ASSERT_TRUE(PipeConnected()); + + RemoveListenerForServiceWorker(valid_extension_id); + ASSERT_TRUE(PipeConnected()); + + RemoveLazyListenerForMainThread(valid_extension_id); + ASSERT_TRUE(PipeConnected()); + + RemoveLazyListenerForServiceWorker(valid_extension_id); + ASSERT_TRUE(PipeConnected()); + + RemoveFilteredListenerForMainThread(valid_extension_id); + ASSERT_TRUE(PipeConnected()); +} + +// Tests that passing invalid extension IDs to mojom::EventRouter +// implementations fail message validation and close the mojom pipe. +TEST_F(EventRouterMojomExtensionIdTest, InvalidExtensionId) { + // Create an invalid ExtensionId. + ExtensionId invalid_extension_id = "invalid_id"; + + AddListenerForMainThread(invalid_extension_id); + ASSERT_FALSE(PipeConnected()); + + RebindReceiver(); + + AddListenerForServiceWorker(invalid_extension_id); + ASSERT_FALSE(PipeConnected()); + + RebindReceiver(); + + AddLazyListenerForMainThread(invalid_extension_id); + ASSERT_FALSE(PipeConnected()); + + RebindReceiver(); + + AddLazyListenerForServiceWorker(invalid_extension_id); + ASSERT_FALSE(PipeConnected()); + + RebindReceiver(); + + AddFilteredListenerForMainThread(invalid_extension_id); + ASSERT_FALSE(PipeConnected()); + + RebindReceiver(); + + RemoveListenerForMainThread(invalid_extension_id); + ASSERT_FALSE(PipeConnected()); + + RebindReceiver(); + + RemoveListenerForServiceWorker(invalid_extension_id); + ASSERT_FALSE(PipeConnected()); + + RebindReceiver(); + + RemoveLazyListenerForMainThread(invalid_extension_id); + ASSERT_FALSE(PipeConnected()); + + RebindReceiver(); + + RemoveLazyListenerForServiceWorker(invalid_extension_id); + ASSERT_FALSE(PipeConnected()); + + RebindReceiver(); + + RemoveFilteredListenerForMainThread(invalid_extension_id); + ASSERT_FALSE(PipeConnected()); +} + +} // namespace +} // namespace extensions
diff --git a/extensions/common/user_scripts_allowed_state.cc b/extensions/common/user_scripts_allowed_state.cc index b267196..1a4f986 100644 --- a/extensions/common/user_scripts_allowed_state.cc +++ b/extensions/common/user_scripts_allowed_state.cc
@@ -17,7 +17,7 @@ // A map of context ids to sets of extension ids. An extension id being present // in the set indicates the user scripts API is allowed for the extension (in // that context only). -using CurrentUserScriptAllowedMap = std::map<int, std::set<ExtensionId>>; +using CurrentUserScriptAllowedMap = std::map<int, std::map<ExtensionId, bool>>; namespace { @@ -37,12 +37,25 @@ } // namespace -bool GetCurrentUserScriptAllowedState(int context_id, - const ExtensionId& extension_id) { +std::optional<bool> GetCurrentUserScriptAllowedState( + int context_id, + const ExtensionId& extension_id) { base::AutoLock lock(GetUserScriptAllowedMapLock()); CurrentUserScriptAllowedMap& map = GetUserScriptAllowedMap(); - auto iter = map.find(context_id); - return iter != map.end() && iter->second.contains(extension_id); + auto context_id_iter = map.find(context_id); + + if (context_id_iter == map.end()) { + return std::nullopt; + } + + auto extension_id_map = context_id_iter->second; + auto extension_id_iter = extension_id_map.find(extension_id); + + if (extension_id_iter == extension_id_map.end()) { + return std::nullopt; + } + + return extension_id_iter->second; } void SetCurrentUserScriptAllowedState(int context_id, @@ -50,28 +63,7 @@ bool user_script_allowed_state) { base::AutoLock lock(GetUserScriptAllowedMapLock()); CurrentUserScriptAllowedMap& map = GetUserScriptAllowedMap(); - - // Adding an extension. - if (user_script_allowed_state) { - map[context_id].insert(extension_id); - return; - } - - // Removing an extension. - auto iter = map.find(context_id); - - // If key not found then the extension isn't allowed so there's nothing to do. - if (iter == map.end()) { - return; - } - - // Delete the extension so it is no longer allowed. - iter->second.erase(extension_id); - - // If no more extensions to track delete the (now unused) key. - if (iter->second.empty()) { - map.erase(iter); - } + map[context_id][extension_id] = user_script_allowed_state; } } // namespace extensions
diff --git a/extensions/common/user_scripts_allowed_state.h b/extensions/common/user_scripts_allowed_state.h index b2aebbe..cd45923 100644 --- a/extensions/common/user_scripts_allowed_state.h +++ b/extensions/common/user_scripts_allowed_state.h
@@ -10,10 +10,11 @@ namespace extensions { // Returns the current user script allowed state for the given extension ID for -// the specific context. -// Defaults to false if there is no entry. -bool GetCurrentUserScriptAllowedState(int context_id, - const ExtensionId& extension_id); +// the specific context. If the state has never been set then the optional will +// not have a value. +std::optional<bool> GetCurrentUserScriptAllowedState( + int context_id, + const ExtensionId& extension_id); // Sets the user script allowed state for the given extension ID in the specific // context.
diff --git a/extensions/common/user_scripts_availability.cc b/extensions/common/user_scripts_availability.cc index 854788c..269e4a3 100644 --- a/extensions/common/user_scripts_availability.cc +++ b/extensions/common/user_scripts_availability.cc
@@ -54,7 +54,8 @@ return false; } - return GetCurrentUserScriptAllowedState(context_id, extension->id()); + return GetCurrentUserScriptAllowedState(context_id, extension->id()) + .value_or(false); } } // namespace
diff --git a/infra/config/generated/builders/ci/android-cronet-x64-dbg-16-tests/properties.json b/infra/config/generated/builders/ci/android-cronet-x64-dbg-16-tests/properties.json index 019d1450..db7b24ab 100644 --- a/infra/config/generated/builders/ci/android-cronet-x64-dbg-16-tests/properties.json +++ b/infra/config/generated/builders/ci/android-cronet-x64-dbg-16-tests/properties.json
@@ -97,5 +97,11 @@ ] }, "builder_group": "chromium.android", - "recipe": "chromium" + "gardener_rotations": [ + "cronet" + ], + "recipe": "chromium", + "sheriff_rotations": [ + "cronet" + ] } \ No newline at end of file
diff --git a/infra/config/generated/builders/try/android-cronet-x64-dbg-15-tests/gn-args.json b/infra/config/generated/builders/try/android-cronet-x64-dbg-15-tests/gn-args.json index aad12c8..3c20a3d 100644 --- a/infra/config/generated/builders/try/android-cronet-x64-dbg-15-tests/gn-args.json +++ b/infra/config/generated/builders/try/android-cronet-x64-dbg-15-tests/gn-args.json
@@ -2,7 +2,6 @@ "gn_args": { "android_static_analysis": "on", "clang_use_default_sample_profile": false, - "coverage_instrumentation_input_file": "//.code-coverage/files_to_instrument.txt", "debuggable_apks": false, "default_min_sdk_version": 23, "disable_file_support": true, @@ -15,9 +14,7 @@ "symbol_level": 1, "target_cpu": "x64", "target_os": "android", - "use_clang_coverage": true, "use_hashed_jni_names": true, - "use_jacoco_coverage": true, "use_partition_alloc": false, "use_platform_icu_alternatives": true, "use_reclient": false,
diff --git a/infra/config/generated/builders/try/android-cronet-x64-dbg-15-tests/properties.json b/infra/config/generated/builders/try/android-cronet-x64-dbg-15-tests/properties.json index 6c1be07..c4d62c22 100644 --- a/infra/config/generated/builders/try/android-cronet-x64-dbg-15-tests/properties.json +++ b/infra/config/generated/builders/try/android-cronet-x64-dbg-15-tests/properties.json
@@ -94,18 +94,6 @@ "targets_spec_directory": "src/infra/config/generated/builders/try/android-cronet-x64-dbg-15-tests/targets" } }, - "$build/code_coverage": { - "coverage_test_types": [ - "unit", - "overall" - ], - "use_clang_coverage": true, - "use_java_coverage": true - }, - "$build/flakiness": { - "check_for_flakiness": true, - "check_for_flakiness_with_resultdb": true - }, "$build/siso": { "configs": [ "builder", @@ -128,6 +116,5 @@ ] }, "builder_group": "tryserver.chromium.android", - "cq": "path-based", "recipe": "chromium_trybot" } \ No newline at end of file
diff --git a/infra/config/generated/builders/try/android-cronet-x64-dbg-16-tests/gn-args.json b/infra/config/generated/builders/try/android-cronet-x64-dbg-16-tests/gn-args.json index 3c20a3d..aad12c8 100644 --- a/infra/config/generated/builders/try/android-cronet-x64-dbg-16-tests/gn-args.json +++ b/infra/config/generated/builders/try/android-cronet-x64-dbg-16-tests/gn-args.json
@@ -2,6 +2,7 @@ "gn_args": { "android_static_analysis": "on", "clang_use_default_sample_profile": false, + "coverage_instrumentation_input_file": "//.code-coverage/files_to_instrument.txt", "debuggable_apks": false, "default_min_sdk_version": 23, "disable_file_support": true, @@ -14,7 +15,9 @@ "symbol_level": 1, "target_cpu": "x64", "target_os": "android", + "use_clang_coverage": true, "use_hashed_jni_names": true, + "use_jacoco_coverage": true, "use_partition_alloc": false, "use_platform_icu_alternatives": true, "use_reclient": false,
diff --git a/infra/config/generated/builders/try/android-cronet-x64-dbg-16-tests/properties.json b/infra/config/generated/builders/try/android-cronet-x64-dbg-16-tests/properties.json index 2ff76d0c..1422344 100644 --- a/infra/config/generated/builders/try/android-cronet-x64-dbg-16-tests/properties.json +++ b/infra/config/generated/builders/try/android-cronet-x64-dbg-16-tests/properties.json
@@ -93,6 +93,18 @@ "targets_spec_directory": "src/infra/config/generated/builders/try/android-cronet-x64-dbg-16-tests/targets" } }, + "$build/code_coverage": { + "coverage_test_types": [ + "unit", + "overall" + ], + "use_clang_coverage": true, + "use_java_coverage": true + }, + "$build/flakiness": { + "check_for_flakiness": true, + "check_for_flakiness_with_resultdb": true + }, "$build/siso": { "configs": [ "builder", @@ -115,5 +127,6 @@ ] }, "builder_group": "tryserver.chromium.android", + "cq": "path-based", "recipe": "chromium_trybot" } \ No newline at end of file
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md index 66df9a55..7c1dc79 100644 --- a/infra/config/generated/cq-builders.md +++ b/infra/config/generated/cq-builders.md
@@ -186,7 +186,7 @@ * [`//build/android/.+`](https://cs.chromium.org/chromium/src/build/android/) * [`//build/config/android/.+`](https://cs.chromium.org/chromium/src/build/config/android/) -* [android-cronet-x64-dbg-15-tests](https://ci.chromium.org/p/chromium/builders/try/android-cronet-x64-dbg-15-tests) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""android-cronet-x64-dbg-15-tests"")) +* [android-cronet-x64-dbg-16-tests](https://ci.chromium.org/p/chromium/builders/try/android-cronet-x64-dbg-16-tests) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""android-cronet-x64-dbg-16-tests"")) Location filters: * [`//components/cronet/.+`](https://cs.chromium.org/chromium/src/components/cronet/)
diff --git a/infra/config/generated/cq-usage/full.cfg b/infra/config/generated/cq-usage/full.cfg index 9eff314..c8c8b65b 100644 --- a/infra/config/generated/cq-usage/full.cfg +++ b/infra/config/generated/cq-usage/full.cfg
@@ -632,7 +632,7 @@ } } builders { - name: "chromium/try/android-cronet-x64-dbg-15-tests" + name: "chromium/try/android-cronet-x64-dbg-16-tests" location_filters { gerrit_host_regexp: ".*" gerrit_project_regexp: ".*" @@ -675,7 +675,7 @@ gerrit_host_regexp: ".*" gerrit_project_regexp: ".*" gerrit_ref_regexp: ".*" - path_regexp: "infra/config/generated/builders/try/android-cronet-x64-dbg-15-tests/.+" + path_regexp: "infra/config/generated/builders/try/android-cronet-x64-dbg-16-tests/.+" } } builders {
diff --git a/infra/config/generated/cq-usage/mega_cq_bots.txt b/infra/config/generated/cq-usage/mega_cq_bots.txt index bb9a274..7a846420 100644 --- a/infra/config/generated/cq-usage/mega_cq_bots.txt +++ b/infra/config/generated/cq-usage/mega_cq_bots.txt
@@ -29,6 +29,7 @@ chromium/try/android-cronet-x64-dbg-13-tests chromium/try/android-cronet-x64-dbg-14-tests chromium/try/android-cronet-x64-dbg-15-tests +chromium/try/android-cronet-x64-dbg-16-tests chromium/try/android-cronet-x64-rel chromium/try/android-cronet-x86-dbg chromium/try/android-cronet-x86-dbg-10-tests
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg index cec16c3..af2ef64 100644 --- a/infra/config/generated/luci/commit-queue.cfg +++ b/infra/config/generated/luci/commit-queue.cfg
@@ -1466,6 +1466,11 @@ } builders { name: "chromium/try/android-cronet-x64-dbg-15-tests" + includable_only: true + disable_reuse_footers: "Include-Ci-Only-Tests" + } + builders { + name: "chromium/try/android-cronet-x64-dbg-16-tests" disable_reuse_footers: "Include-Ci-Only-Tests" location_filters { gerrit_host_regexp: ".*" @@ -1509,17 +1514,12 @@ gerrit_host_regexp: ".*" gerrit_project_regexp: ".*" gerrit_ref_regexp: ".*" - path_regexp: "infra/config/generated/builders/try/android-cronet-x64-dbg-15-tests/.+" + path_regexp: "infra/config/generated/builders/try/android-cronet-x64-dbg-16-tests/.+" } mode_allowlist: "DRY_RUN" mode_allowlist: "FULL_RUN" } builders { - name: "chromium/try/android-cronet-x64-dbg-16-tests" - includable_only: true - disable_reuse_footers: "Include-Ci-Only-Tests" - } - builders { name: "chromium/try/android-cronet-x64-rel" disable_reuse_footers: "Include-Ci-Only-Tests" location_filters {
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg index 30abc4c1..0b20f903 100644 --- a/infra/config/generated/luci/cr-buildbucket.cfg +++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -43375,8 +43375,14 @@ ' }' ' },' ' "builder_group": "chromium.android",' + ' "gardener_rotations": [' + ' "cronet"' + ' ],' ' "led_builder_is_bootstrapped": true,' - ' "recipe": "chromium"' + ' "recipe": "chromium",' + ' "sheriff_rotations": [' + ' "cronet"' + ' ]' '}' execution_timeout_secs: 10800 build_numbers: YES @@ -86216,7 +86222,6 @@ ' }' ' },' ' "builder_group": "tryserver.chromium.android",' - ' "cq": "path-based",' ' "led_builder_is_bootstrapped": true,' ' "recipe": "chromium_trybot"' '}' @@ -86329,6 +86334,7 @@ ' }' ' },' ' "builder_group": "tryserver.chromium.android",' + ' "cq": "path-based",' ' "led_builder_is_bootstrapped": true,' ' "recipe": "chromium_trybot"' '}'
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg index 34eb4722..04f2c51 100644 --- a/infra/config/generated/luci/luci-milo.cfg +++ b/infra/config/generated/luci/luci-milo.cfg
@@ -3090,7 +3090,7 @@ name: "buildbucket/luci.chromium.try/android-cronet-arm-rel" } builders { - name: "buildbucket/luci.chromium.try/android-cronet-x64-dbg-15-tests" + name: "buildbucket/luci.chromium.try/android-cronet-x64-dbg-16-tests" } builders { name: "buildbucket/luci.chromium.try/android-cronet-x86-dbg-marshmallow-tests" @@ -23772,6 +23772,11 @@ short_name: "15" } builders { + name: "buildbucket/luci.chromium.ci/android-cronet-x64-dbg-16-tests" + category: "chromium.android|cronet|test" + short_name: "16" + } + builders { name: "buildbucket/luci.chromium.ci/android-cronet-x86-dbg-marshmallow-tests" category: "chromium.android|cronet|test" short_name: "m"
diff --git a/infra/config/generated/luci/realms.cfg b/infra/config/generated/luci/realms.cfg index 4d09cc2..453fe08 100644 --- a/infra/config/generated/luci/realms.cfg +++ b/infra/config/generated/luci/realms.cfg
@@ -566,6 +566,7 @@ bindings { role: "role/swarming.poolUser" principals: "group:mdb/chrome-build-access-sphinx" + principals: "project:angle" principals: "project:chromium-m132" principals: "project:chromium-m134" principals: "project:chromium-m135" @@ -619,6 +620,7 @@ role: "role/swarming.poolUser" principals: "group:chromium-led-users" principals: "group:mdb/chrome-build-access-sphinx" + principals: "project:angle" principals: "project:chromium-m132" principals: "project:chromium-m134" principals: "project:chromium-m135"
diff --git a/infra/config/generated/sheriff-rotations/cronet.txt b/infra/config/generated/sheriff-rotations/cronet.txt index 4e63b41..00aea1fa 100644 --- a/infra/config/generated/sheriff-rotations/cronet.txt +++ b/infra/config/generated/sheriff-rotations/cronet.txt
@@ -10,6 +10,7 @@ ci/android-cronet-x64-dbg-13-tests ci/android-cronet-x64-dbg-14-tests ci/android-cronet-x64-dbg-15-tests +ci/android-cronet-x64-dbg-16-tests ci/android-cronet-x64-rel ci/android-cronet-x86-dbg ci/android-cronet-x86-dbg-10-tests
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.star b/infra/config/subprojects/chromium/ci/chromium.android.star index 2b5a42fb..3971fe6 100644 --- a/infra/config/subprojects/chromium/ci/chromium.android.star +++ b/infra/config/subprojects/chromium/ci/chromium.android.star
@@ -2078,8 +2078,7 @@ "x86-64", ], ), - # TODO(crbug.com/406838181): Promote when stable. - gardener_rotations = args.ignore_default(None), + gardener_rotations = args.ignore_default(gardener_rotations.CRONET), console_view_entry = consoles.console_view_entry( category = "cronet|test", short_name = "16",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star index 46e2b5d3..00441bf7 100644 --- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star +++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -732,8 +732,18 @@ "ci/android-cronet-x64-dbg", "ci/android-cronet-x64-dbg-15-tests", ], - # Replicates "ci/android-cronet-x64-dbg", with code coverage related - # arguments appended. + gn_args = "ci/android-cronet-x64-dbg", + contact_team_email = "cronet-team@google.com", + siso_remote_jobs = siso.remote_jobs.LOW_JOBS_FOR_CQ, +) + +try_.builder( + name = "android-cronet-x64-dbg-16-tests", + description_html = "Tests Cronet against Android 16", + mirrors = [ + "ci/android-cronet-x64-dbg", + "ci/android-cronet-x64-dbg-16-tests", + ], gn_args = gn_args.config( configs = [ "ci/android-cronet-x64-dbg", @@ -759,18 +769,6 @@ ) try_.builder( - name = "android-cronet-x64-dbg-16-tests", - description_html = "Tests Cronet against Android 16", - mirrors = [ - "ci/android-cronet-x64-dbg", - "ci/android-cronet-x64-dbg-16-tests", - ], - gn_args = "ci/android-cronet-x64-dbg", - contact_team_email = "cronet-team@google.com", - siso_remote_jobs = siso.remote_jobs.LOW_JOBS_FOR_CQ, -) - -try_.builder( name = "android-cronet-x86-dbg", mirrors = ["ci/android-cronet-x86-dbg"], gn_args = "ci/android-cronet-x86-dbg",
diff --git a/infra/config/swarming.star b/infra/config/swarming.star index fa11f44..0603d6d4 100644 --- a/infra/config/swarming.star +++ b/infra/config/swarming.star
@@ -58,7 +58,7 @@ # with Chromium). swarming.pool_realm( name = "pools/ci", - user_projects = ["dawn"] + [details.project for details in ACTIVE_MILESTONES.values()], + user_projects = ["angle", "dawn"] + [details.project for details in ACTIVE_MILESTONES.values()], owner_groups = [ "mdb/chrome-infra-eng", ], @@ -83,7 +83,7 @@ # The tasks here are also triggered via Buildbucket. See comment above. swarming.pool_realm( name = "pools/try", - user_projects = ["dawn"] + [details.project for details in ACTIVE_MILESTONES.values()], + user_projects = ["angle", "dawn"] + [details.project for details in ACTIVE_MILESTONES.values()], owner_groups = [ "mdb/chrome-infra-eng", ],
diff --git a/ios/chrome/app/background_refresh/app_refresh_provider_unittest.mm b/ios/chrome/app/background_refresh/app_refresh_provider_unittest.mm index 5196e36bf..b0c4679 100644 --- a/ios/chrome/app/background_refresh/app_refresh_provider_unittest.mm +++ b/ios/chrome/app/background_refresh/app_refresh_provider_unittest.mm
@@ -11,7 +11,7 @@ @interface TestAppRefreshProvider : AppRefreshProvider // Make identifier writable. -@property(nonatomic, strong, readwrite) NSString* identifier; +@property(nonatomic, copy, readwrite) NSString* identifier; @end @implementation TestAppRefreshProvider
diff --git a/ios/chrome/browser/alert_view/ui_bundled/alert_view_controller.mm b/ios/chrome/browser/alert_view/ui_bundled/alert_view_controller.mm index f57fce1..e1a8afc6 100644 --- a/ios/chrome/browser/alert_view/ui_bundled/alert_view_controller.mm +++ b/ios/chrome/browser/alert_view/ui_bundled/alert_view_controller.mm
@@ -232,8 +232,8 @@ @property(nonatomic, assign) BOOL actionButtonsAreInitiallyDisabled; // The Lottie image names for the image in the alert. -@property(nonatomic, strong) NSString* imageLottieName; -@property(nonatomic, strong) NSString* imageDarkModeLottieName; +@property(nonatomic, copy) NSString* imageLottieName; +@property(nonatomic, copy) NSString* imageDarkModeLottieName; // Custom animation view used for the image in this alert. @property(nonatomic, strong) id<LottieAnimation> animationViewWrapper; @@ -562,12 +562,12 @@ return _buttonAlertActionsDictionary; } -#pragma mark - ALertConsumer +#pragma mark - AlertConsumer - (void)setImageLottieName:(NSString*)imageLottieName darkModeLottieName:imageDarkModeLottieName { - _imageLottieName = imageLottieName; - _imageDarkModeLottieName = imageDarkModeLottieName; + _imageLottieName = [imageLottieName copy]; + _imageDarkModeLottieName = [imageDarkModeLottieName copy]; } #pragma mark - UIGestureRecognizerDelegate
diff --git a/ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow_in_profile.mm b/ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow_in_profile.mm index f56efda..143d9b6e 100644 --- a/ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow_in_profile.mm +++ b/ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow_in_profile.mm
@@ -19,6 +19,7 @@ #import "ios/chrome/browser/shared/coordinator/scene/scene_state.h" #import "ios/chrome/browser/shared/model/application_context/application_context.h" #import "ios/chrome/browser/shared/model/browser/browser.h" +#import "ios/chrome/browser/shared/model/browser/browser_observer_bridge.h" #import "ios/chrome/browser/shared/model/profile/profile_attributes_storage_ios.h" #import "ios/chrome/browser/shared/model/profile/profile_ios.h" #import "ios/chrome/browser/shared/model/profile/profile_manager_ios.h" @@ -48,7 +49,8 @@ } // namespace -@interface AuthenticationFlowInProfile () <AuthenticationFlowPerformerDelegate> +@interface AuthenticationFlowInProfile () <AuthenticationFlowPerformerDelegate, + BrowserObserving> @end @implementation AuthenticationFlowInProfile { @@ -65,6 +67,7 @@ BOOL _isManagedIdentity; AuthenticationFlowPerformer* _performer; raw_ptr<Browser> _browser; + std::unique_ptr<BrowserObserverBridge> _browserObserver; signin_metrics::AccessPoint _accessPoint; BOOL _precedingHistorySync; PostSignInActionSet _postSignInActions; @@ -94,6 +97,7 @@ CHECK(browser); CHECK(identity); _browser = browser; + _browserObserver = std::make_unique<BrowserObserverBridge>(_browser, self); _identityToSignIn = identity; _isManagedIdentity = isManagedIdentity; _accessPoint = accessPoint; @@ -376,6 +380,13 @@ } - (void)switchBackToPersonalProfileIfNeededStep { + if (!_browser) { + // Browser was destroyed in the meantime. This can happen if a switch is + // already in progress, or if the window/scene got closed. Either way, no + // switching necessary here. + [self continueFlow]; + return; + } // Note: It's theoretically possible that the originating profile was not the // personal one, but rather another managed profile. In that case, switching // back to that managed profile would be "more correct". However, that would @@ -399,10 +410,11 @@ } - (void)failureCompleteFlowStep { - // None of the steps after signin can fail. If any failable steps after the - // signin step get added in the future, then a call to + // None of the steps after signin can fail (except for the case of the browser + // going away, which is more "abort" than "fail)"). If any failable steps + // after the signin step get added in the future, then a call to // `[_performer signOutImmediatelyFromProfile:...]` should be added here. - CHECK(!_didSignIn); + CHECK(!_browser || !_didSignIn, base::NotFatalUntil::M140); CHECK(_signInCompletion); signin_ui::SigninCompletionCallback signInCompletion = _signInCompletion; _signInCompletion = nil; @@ -512,4 +524,12 @@ [self continueFlow]; } +#pragma mark - BrowserObserving + +- (void)browserDestroyed:(Browser*)browser { + CHECK_EQ(browser, _browser); + _browser = nullptr; + _error = ios::provider::CreateUserCancelledSigninError(); +} + @end
diff --git a/ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow_in_profile_unittest.mm b/ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow_in_profile_unittest.mm index 7e603b4..8b43229 100644 --- a/ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow_in_profile_unittest.mm +++ b/ios/chrome/browser/authentication/ui_bundled/authentication_flow/authentication_flow_in_profile_unittest.mm
@@ -97,14 +97,15 @@ void CreateAuthenticationFlowInProfile( PostSignInActionSet post_sign_in_actions, id<SystemIdentity> identity, - signin_metrics::AccessPoint access_point) { + signin_metrics::AccessPoint access_point, + bool preceding_history_sync = false) { BOOL is_managed_identity = identity == managed_identity_; authentication_flow_in_profile_ = [[AuthenticationFlowInProfile alloc] initWithBrowser:browser_.get() identity:identity isManagedIdentity:is_managed_identity accessPoint:access_point - precedingHistorySync:NO + precedingHistorySync:preceding_history_sync postSignInActions:post_sign_in_actions]; id<AuthenticationFlowPerformerDelegate> performer_delegate = GetAuthenticationFlowPerformerDelegate(); @@ -290,6 +291,133 @@ EXPECT_TRUE(future.Wait()); } +// Tests that there is no crash if the browser is destroyed in the middle of the +// flow, during `registerForUserPolicyIfNeededStep`. +TEST_P(AuthenticationFlowInProfileTest, + BrowserDestroyedDuringRegisterForUserPolicy) { + const signin_metrics::AccessPoint access_point = + signin_metrics::AccessPoint::kStartPage; + CreateAuthenticationFlowInProfile(PostSignInActionSet(), managed_identity_, + access_point); + // Start `authentication_flow_in_profile_` for `managed_identity_`. + base::test::TestFuture<SigninCoordinatorResult> future; + [authentication_flow_in_profile_ + startSignInWithCompletion:base::CallbackToBlock(future.GetCallback())]; + // Expect to call the performer to sign-in. + OCMExpect([performer_mock_ signInIdentity:managed_identity_ + atAccessPoint:access_point + currentProfile:profile_.get()]); + // Expect user policy register request. + __block auto run_loop = std::make_unique<base::RunLoop>(); + OCMExpect([performer_mock_ registerUserPolicy:profile_.get() + forIdentity:managed_identity_]) + .andDo(^(NSInvocation* invocation) { + // While the policy registration is ongoing, the browser gets destroyed. + browser_ = nil; + run_loop->Quit(); + }); + run_loop->Run(); + // Simulate the user policy register request finishing. + [GetAuthenticationFlowPerformerDelegate() + didRegisterForUserPolicyWithDMToken:kFakeDMToken + clientID:kFakeClientID + userAffiliationIDs:@[ kFakeUserAffiliationID ]]; + // Since the browser was destroyed, no other steps should happen (e.g. no + // policy fetch, no post-signin actions). + EXPECT_TRUE(future.Wait()); + EXPECT_EQ(future.Get(), + SigninCoordinatorResult::SigninCoordinatorResultInterrupted); +} + +// Tests that there is no crash if the browser is destroyed in the middle of the +// flow, during `fetchUserPolicyIfNeededStep`. +TEST_P(AuthenticationFlowInProfileTest, BrowserDestroyedDuringFetchUserPolicy) { + const signin_metrics::AccessPoint access_point = + signin_metrics::AccessPoint::kStartPage; + CreateAuthenticationFlowInProfile(PostSignInActionSet(), managed_identity_, + access_point); + // Start `authentication_flow_in_profile_` for `managed_identity_`. + base::test::TestFuture<SigninCoordinatorResult> future; + [authentication_flow_in_profile_ + startSignInWithCompletion:base::CallbackToBlock(future.GetCallback())]; + // Expect to call the performer to sign-in. + OCMExpect([performer_mock_ signInIdentity:managed_identity_ + atAccessPoint:access_point + currentProfile:profile_.get()]); + // Expect user policy register request. + __block auto run_loop = std::make_unique<base::RunLoop>(); + OCMExpect([performer_mock_ registerUserPolicy:profile_.get() + forIdentity:managed_identity_]) + .andDo(^(NSInvocation* invocation) { + run_loop->Quit(); + }); + run_loop->Run(); + + // Expect user policy fetch request. + OCMExpect([performer_mock_ fetchUserPolicy:profile_.get() + withDmToken:kFakeDMToken + clientID:kFakeClientID + userAffiliationIDs:@[ kFakeUserAffiliationID ] + identity:managed_identity_]) + .andDo(^(NSInvocation* invocation) { + // While the policy fetch is ongoing, the browser gets destroyed. + browser_ = nil; + }); + + // Simulate the user policy register request finishing. + [GetAuthenticationFlowPerformerDelegate() + didRegisterForUserPolicyWithDMToken:kFakeDMToken + clientID:kFakeClientID + userAffiliationIDs:@[ kFakeUserAffiliationID ]]; + + // Simulate the user policy fetch request finishing. + [GetAuthenticationFlowPerformerDelegate() didFetchUserPolicyWithSuccess:NO]; + + // Since the browser was destroyed, no other steps should happen (e.g. no + // post-signin actions). + EXPECT_TRUE(future.Wait()); + EXPECT_EQ(future.Get(), + SigninCoordinatorResult::SigninCoordinatorResultInterrupted); +} + +// Tests that there is no crash if the browser is destroyed in the middle of the +// flow, during `fetchCapabilitiesIfNeededStep`. +TEST_P(AuthenticationFlowInProfileTest, + BrowserDestroyedDuringFetchCapabilities) { + const signin_metrics::AccessPoint access_point = + signin_metrics::AccessPoint::kStartPage; + CreateAuthenticationFlowInProfile(PostSignInActionSet(), identity1_, + access_point, + /*preceding_history_sync=*/true); + // Start `authentication_flow_in_profile_` for `identity1_`. + base::test::TestFuture<SigninCoordinatorResult> future; + [authentication_flow_in_profile_ + startSignInWithCompletion:base::CallbackToBlock(future.GetCallback())]; + // Expect to call the performer to sign-in. + OCMExpect([performer_mock_ signInIdentity:identity1_ + atAccessPoint:access_point + currentProfile:profile_.get()]); + + // Expect capabilities fetch request, and grab the completion callback. + __block auto run_loop = std::make_unique<base::RunLoop>(); + OCMExpect([performer_mock_ fetchAccountCapabilities:profile_.get()]) + .andDo(^(NSInvocation* invocation) { + // While the capabilities fetch is ongoing, the browser gets destroyed. + browser_ = nil; + run_loop->Quit(); + }); + run_loop->Run(); + + // Simulate the capabilities fetch request finishing. + [GetAuthenticationFlowPerformerDelegate() didFetchAccountCapabilities]; + + // Since the browser was destroyed, no other steps should happen (e.g. no + // post-signin actions). + EXPECT_TRUE(future.Wait()); + EXPECT_EQ(future.Get(), + SigninCoordinatorResult::SigninCoordinatorResultInterrupted); +} + INSTANTIATE_TEST_SUITE_P(, AuthenticationFlowInProfileTest, testing::Bool(),
diff --git a/ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator/BUILD.gn index 4190556..c138dd5 100644 --- a/ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator/BUILD.gn +++ b/ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator/BUILD.gn
@@ -26,7 +26,6 @@ "//ios/chrome/browser/authentication/ui_bundled/identity_chooser", "//ios/chrome/browser/authentication/ui_bundled/signin:constants", "//ios/chrome/browser/authentication/ui_bundled/signin:signin_headers", - "//ios/chrome/browser/authentication/ui_bundled/signin:stop_animated_chrome_coordinator", "//ios/chrome/browser/authentication/ui_bundled/signin/logging", "//ios/chrome/browser/first_run/model", "//ios/chrome/browser/first_run/ui_bundled:constants", @@ -36,6 +35,7 @@ "//ios/chrome/browser/first_run/ui_bundled/uma", "//ios/chrome/browser/policy/model:policy_util", "//ios/chrome/browser/shared/coordinator/chrome_coordinator", + "//ios/chrome/browser/shared/coordinator/chrome_coordinator:animated_coordinator", "//ios/chrome/browser/shared/model/application_context", "//ios/chrome/browser/shared/model/browser", "//ios/chrome/browser/shared/model/profile",
diff --git a/ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator/fullscreen_signin_screen_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator/fullscreen_signin_screen_coordinator.h index 7e9c0dc5..118f258 100644 --- a/ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator/fullscreen_signin_screen_coordinator.h +++ b/ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator/fullscreen_signin_screen_coordinator.h
@@ -6,7 +6,7 @@ #define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_FULLSCREEN_SIGNIN_SCREEN_COORDINATOR_FULLSCREEN_SIGNIN_SCREEN_COORDINATOR_H_ #import "ios/chrome/browser/authentication/ui_bundled/change_profile_continuation_provider.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" +#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h" #import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h" @protocol FirstRunScreenDelegate; @@ -18,8 +18,7 @@ // Coordinator responsible for presenting the fullscreen sign-in UI. // It is a child coordinator managed by the FullscreenSigninCoordinator. -@interface FullscreenSigninScreenCoordinator - : ChromeCoordinator <StopAnimatedChromeCoordinator> +@interface FullscreenSigninScreenCoordinator : AnimatedCoordinator // Initiates a FullscreenSigninScreenCoordinator with `navigationController`, // `browser` and `delegate`.
diff --git a/ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator/fullscreen_signin_screen_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator/fullscreen_signin_screen_coordinator.mm index d586899..feaa52d 100644 --- a/ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator/fullscreen_signin_screen_coordinator.mm +++ b/ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator/fullscreen_signin_screen_coordinator.mm
@@ -16,13 +16,13 @@ #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_context_style.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" #import "ios/chrome/browser/first_run/model/first_run_metrics.h" #import "ios/chrome/browser/first_run/ui_bundled/first_run_constants.h" #import "ios/chrome/browser/first_run/ui_bundled/first_run_screen_delegate.h" #import "ios/chrome/browser/first_run/ui_bundled/first_run_util.h" #import "ios/chrome/browser/first_run/ui_bundled/tos/tos_coordinator.h" #import "ios/chrome/browser/first_run/ui_bundled/uma/uma_coordinator.h" +#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h" #import "ios/chrome/browser/shared/model/application_context/application_context.h" #import "ios/chrome/browser/shared/model/browser/browser.h" #import "ios/chrome/browser/shared/model/profile/profile_ios.h" @@ -62,8 +62,7 @@ @property(nonatomic, strong) IdentityChooserCoordinator* identityChooserCoordinator; // Coordinator to add an identity. -@property(nonatomic, strong) SigninCoordinator<StopAnimatedChromeCoordinator>* - addAccountSigninCoordinator; +@property(nonatomic, strong) SigninCoordinator* addAccountSigninCoordinator; @property(nonatomic, assign) BOOL UMAReportingUserChoice; @end @@ -153,11 +152,7 @@ animated:animated]; } -- (void)stop { - [self stopAnimated:NO]; -} - -#pragma mark - StopAnimatedChromeCoordinator +#pragma mark - AnimatedCoordinator - (void)stopAnimated:(BOOL)animated { [self.browser->GetCommandDispatcher() @@ -170,7 +165,7 @@ self.mediator = nil; self.accountManagerService = nil; self.authenticationService = nil; - [super stop]; + [super stopAnimated:animated]; } #pragma mark - UIAdaptivePresentationControllerDelegate
diff --git a/ios/chrome/browser/authentication/ui_bundled/history_sync/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/history_sync/BUILD.gn index bdd7cec..6dedee9 100644 --- a/ios/chrome/browser/authentication/ui_bundled/history_sync/BUILD.gn +++ b/ios/chrome/browser/authentication/ui_bundled/history_sync/BUILD.gn
@@ -33,8 +33,8 @@ "//ios/chrome/browser/authentication/ui_bundled/history_sync/resources", "//ios/chrome/browser/authentication/ui_bundled/signin:constants", "//ios/chrome/browser/authentication/ui_bundled/signin:signin_headers", - "//ios/chrome/browser/authentication/ui_bundled/signin:stop_animated_chrome_coordinator", "//ios/chrome/browser/first_run/model", + "//ios/chrome/browser/shared/coordinator/chrome_coordinator:animated_coordinator", "//ios/chrome/browser/shared/model/browser", "//ios/chrome/browser/shared/model/profile", "//ios/chrome/browser/shared/public/features",
diff --git a/ios/chrome/browser/authentication/ui_bundled/history_sync/history_sync_popup_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/history_sync/history_sync_popup_coordinator.h index a86aca6..db7dfbe 100644 --- a/ios/chrome/browser/authentication/ui_bundled/history_sync/history_sync_popup_coordinator.h +++ b/ios/chrome/browser/authentication/ui_bundled/history_sync/history_sync_popup_coordinator.h
@@ -5,7 +5,7 @@ #ifndef IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_HISTORY_SYNC_HISTORY_SYNC_POPUP_COORDINATOR_H_ #define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_HISTORY_SYNC_HISTORY_SYNC_POPUP_COORDINATOR_H_ -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" +#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h" #import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h" enum class SigninContextStyle; @@ -28,8 +28,7 @@ @end // Coordinator to present the History Sync Opt-In screen. -@interface HistorySyncPopupCoordinator - : ChromeCoordinator <StopAnimatedChromeCoordinator> +@interface HistorySyncPopupCoordinator : AnimatedCoordinator // delegate for HistorySyncCoordinator. @property(nonatomic, weak) id<HistorySyncPopupCoordinatorDelegate> delegate;
diff --git a/ios/chrome/browser/authentication/ui_bundled/history_sync/history_sync_popup_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/history_sync/history_sync_popup_coordinator.mm index 352951d..d62812a 100644 --- a/ios/chrome/browser/authentication/ui_bundled/history_sync/history_sync_popup_coordinator.mm +++ b/ios/chrome/browser/authentication/ui_bundled/history_sync/history_sync_popup_coordinator.mm
@@ -69,6 +69,13 @@ return self; } +- (void)dealloc { + // TODO(crbug.com/40272467) + DUMP_WILL_BE_CHECK(!_historySyncCoordinator); +} + +#pragma mark - ChromeCoordinator + - (void)start { [super start]; ProfileIOS* profile = self.profile; @@ -101,21 +108,14 @@ completion:nil]; } -- (void)dealloc { - // TODO(crbug.com/40272467) - DUMP_WILL_BE_CHECK(!_historySyncCoordinator); -} - -- (void)stop { - [self stopAnimated:NO]; -} +#pragma mark - AnimatedCoordinator - (void)stopAnimated:(BOOL)animated { [self stopHistorySyncCoordinator]; _navigationController.presentationController.delegate = nil; [_navigationController dismissViewControllerAnimated:animated completion:nil]; _navigationController = nil; - [super stop]; + [super stopAnimated:animated]; } #pragma mark - Private
diff --git a/ios/chrome/browser/authentication/ui_bundled/separate_profiles_egtest.mm b/ios/chrome/browser/authentication/ui_bundled/separate_profiles_egtest.mm index b152549c..0cdb50c 100644 --- a/ios/chrome/browser/authentication/ui_bundled/separate_profiles_egtest.mm +++ b/ios/chrome/browser/authentication/ui_bundled/separate_profiles_egtest.mm
@@ -142,22 +142,22 @@ ManagedProfileCreationBrowsingDataButtonMatcher()] performAction:grey_tap()]; - // Wait for browsing data management screen + // Wait for the browsing data management screen. [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher: BrowsingDataManagementScreenMatcher()]; [[EarlGrey selectElementWithMatcher:BrowsingDataManagementScreenMatcher()] assertWithMatcher:grey_sufficientlyVisible()]; - // Browsing data kept separate by default. [[EarlGrey selectElementWithMatcher:SeparateBrowsingDataCellMatcher()] assertWithMatcher:grey_selected()]; - + // Close the browsing data management screen by going back. [[EarlGrey selectElementWithMatcher: chrome_test_util::ManagedProfileCreationNavigationBarBackButton()] performAction:grey_tap()]; - // Wait for the browsing data management screen to disappear. + // Wait for the browsing data management screen to disappear, and the + // enteprise onboarding screen to appear again. [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher: ManagedProfileCreationScreenMatcher()]; @@ -169,6 +169,8 @@ PromoScreenPrimaryButtonMatcher()] performAction:grey_tap()]; + // Note: The profile switch happens here. + // History sync screen: Decline the promo (irrelevant here). [ChromeEarlGrey waitForMatcher:HistoryScreenMatcher()]; [[EarlGrey selectElementWithMatcher:chrome_test_util:: @@ -177,17 +179,18 @@ [SigninEarlGrey verifySignedInWithFakeIdentity:managedIdentity]; - // We should still be in a new managed profile. + // We should be in a new managed profile. NSString* newProfileName = [ChromeEarlGrey currentProfileName]; GREYAssert(![originalProfileName isEqualToString:newProfileName], - @"Profile name should be unchanged"); + @"Profile name should be changed"); + // Sign out - that should cause a switch back to the personal profile. [SigninEarlGreyUI signOut]; NSString* personalProfileName = [ChromeEarlGrey currentProfileName]; GREYAssert([personalProfileName isEqualToString:originalProfileName], @"Profile name should be the personal one"); - // Signin again + // Sign in again. TapIdentityDisc(); [[EarlGrey selectElementWithMatcher:ContinueButtonWithIdentityMatcher( managedIdentity)] @@ -197,7 +200,7 @@ // TODO(crbug.com/399033938): Find a better way to wait for this. GREYWaitForAppToIdle(@"App failed to idle"); - // Use should be signed in without having to see the managed profile + // The user should be signed in without having to see the managed profile // onboarding a second time. [SigninEarlGrey verifySignedInWithFakeIdentity:managedIdentity]; } @@ -236,8 +239,7 @@ // Wait for enterprise onboarding screen. [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher: ManagedProfileCreationScreenMatcher()]; - - // Verifies that the subtitle is the right one. + // Verify that the subtitle is the right one. [[EarlGrey selectElementWithMatcher:ManagedProfileCreationSubtitleMatcher()] assertWithMatcher:grey_sufficientlyVisible()]; @@ -251,8 +253,6 @@ [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher: BrowsingDataManagementScreenMatcher()]; - [[EarlGrey selectElementWithMatcher:BrowsingDataManagementScreenMatcher()] - assertWithMatcher:grey_sufficientlyVisible()]; [[EarlGrey selectElementWithMatcher:SeparateBrowsingDataCellMatcher()] assertWithMatcher:grey_selected()]; @@ -268,7 +268,8 @@ chrome_test_util::ManagedProfileCreationNavigationBarBackButton()] performAction:grey_tap()]; - // Wait for the browsing data management screen to disappear. + // Wait for the browsing data management screen to disappear, and the + // enteprise onboarding screen to appear again. [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher: ManagedProfileCreationScreenMatcher()]; @@ -280,6 +281,8 @@ PromoScreenPrimaryButtonMatcher()] performAction:grey_tap()]; + // Note: The profile switch happens here. + // History sync screen: Decline the promo (irrelevant here). [ChromeEarlGrey waitForMatcher:HistoryScreenMatcher()]; [[EarlGrey selectElementWithMatcher:chrome_test_util:: @@ -294,12 +297,13 @@ GREYAssert([originalProfileName isEqualToString:newProfileName], @"Profile name should be unchanged"); + // Sign out - this should cause a switch back to the personal profile. [SigninEarlGreyUI signOut]; NSString* personalProfileName = [ChromeEarlGrey currentProfileName]; GREYAssert(![personalProfileName isEqualToString:newProfileName], @"Profile name should be the personal one"); - // Signin again + // Sign in again TapIdentityDisc(); [[EarlGrey selectElementWithMatcher:ContinueButtonWithIdentityMatcher( managedIdentity)] @@ -309,14 +313,14 @@ // TODO(crbug.com/399033938): Find a better way to wait for this. GREYWaitForAppToIdle(@"App failed to idle"); - // Use should be signed in without having to see the managed profile + // The user should be signed in without having to see the managed profile // onboarding a second time. [SigninEarlGrey verifySignedInWithFakeIdentity:managedIdentity]; } -// Tests that signing in from a signed out state with a managed account -// shows the enterprise onboarding only the first time. And The user cannot -// merge existing browsing data because it is disabled by policy. +// Tests that signing in from a signed out state with a managed account shows +// the enterprise onboarding only the first time. And the user cannot merge +// existing browsing data because it is disabled by policy. // TODO(crbug.com/411035267): Fix this flaky test on simulator. #if TARGET_OS_SIMULATOR #define MAYBE_testSigninWithManagedAccountFromUnsignedStateWithDataMigrationDisabled \ @@ -339,7 +343,7 @@ [FakeSystemIdentity fakeManagedIdentity]; [SigninEarlGrey addFakeIdentity:managedIdentity]; - // Disabled browsing data migration + // Browsing data migration is disabled by policy. [ChromeEarlGrey setIntegerValue:policy::ALWAYS_SEPARATE forUserPref:prefs::kProfileSeparationDataMigrationSettings]; @@ -348,8 +352,7 @@ [ChromeEarlGrey userIntegerPref:prefs::kProfileSeparationDataMigrationSettings], @"Profile separation data migration settings not properly set."); - - // Enabled on account level, but the strictest value should apply. + // It's enabled on account level, but the strictest value should apply. [SigninEarlGrey setPolicyResponseForNextProfileSeparationPolicyRequest: policy::USER_OPT_IN]; @@ -363,7 +366,7 @@ [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher: ManagedProfileCreationScreenMatcher()]; - // The data migration disabled message should be shown + // The data migration disabled message should be shown. [[EarlGrey selectElementWithMatcher: ManagedProfileCreationDataMigrationDisabledSubtitleMatcher()] assertWithMatcher:grey_sufficientlyVisible()]; @@ -376,6 +379,8 @@ PromoScreenPrimaryButtonMatcher()] performAction:grey_tap()]; + // Note: The profile switch happens here. + // History sync screen: Decline the promo (irrelevant here). [ChromeEarlGrey waitForMatcher:HistoryScreenMatcher()]; [[EarlGrey selectElementWithMatcher:chrome_test_util:: @@ -389,25 +394,26 @@ GREYAssert(![originalProfileName isEqualToString:newProfileName], @"Profile name should be unchanged"); + // Sign out - this should cause a switch back to the personal profile. [SigninEarlGreyUI signOut]; NSString* personalProfileName = [ChromeEarlGrey currentProfileName]; GREYAssert([personalProfileName isEqualToString:originalProfileName], @"Profile name should be the personal one"); - // Signin again + // Sign in again. TapIdentityDisc(); [[EarlGrey selectElementWithMatcher:ContinueButtonWithIdentityMatcher( managedIdentity)] performAction:grey_tap()]; - // Use should be signed in without having to see the managed profile + // The user should be signed in without having to see the managed profile // onboarding a second time. [SigninEarlGrey verifySignedInWithFakeIdentity:managedIdentity]; } -// Tests that signing in from a signed out state with a managed account -// shows the enterprise onboarding. And The user cannot merge existing -// browsing data because it is disabled by policy. +// Tests that signing in from a signed out state with a managed account shows +// the enterprise onboarding. And the user cannot merge existing browsing data +// because it is disabled by policy. - (void) testSigninWithManagedAccountFromUnsignedStateWithDataMigrationDisabledOnAccount { // Separate profiles are only available in iOS 17+. @@ -419,7 +425,7 @@ [FakeSystemIdentity fakeManagedIdentity]; [SigninEarlGrey addFakeIdentity:managedIdentity]; - // Disabled browsing data migration + // Browsing data migration is disabled by policy. [SigninEarlGrey setPolicyResponseForNextProfileSeparationPolicyRequest: policy::ALWAYS_SEPARATE]; @@ -435,11 +441,11 @@ managedIdentity)] performAction:grey_tap()]; - // Wait for enterprise onboarding screen. + // Wait for the enterprise onboarding screen. [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher: ManagedProfileCreationScreenMatcher()]; - // The data migration disabled message should be shown + // The data migration disabled message should be shown. [[EarlGrey selectElementWithMatcher: ManagedProfileCreationDataMigrationDisabledSubtitleMatcher()] assertWithMatcher:grey_sufficientlyVisible()]; @@ -452,6 +458,8 @@ PromoScreenPrimaryButtonMatcher()] performAction:grey_tap()]; + // Note: The profile switch happens here. + // History sync screen: Decline the promo (irrelevant here). [ChromeEarlGrey waitForMatcher:HistoryScreenMatcher()]; [[EarlGrey selectElementWithMatcher:chrome_test_util:: @@ -479,7 +487,7 @@ [FakeSystemIdentity fakeManagedIdentity]; [SigninEarlGrey addFakeIdentity:managedIdentity]; - // Disabled browsing data migration + // Browsing data migration is suggested by policy. [ChromeEarlGrey setIntegerValue:policy::USER_OPT_OUT forUserPref:prefs::kProfileSeparationDataMigrationSettings]; @@ -495,7 +503,7 @@ managedIdentity)] performAction:grey_tap()]; - // Wait for enterprise onboarding screen. + // Wait for the enterprise onboarding screen. [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher: ManagedProfileCreationScreenMatcher()]; @@ -506,11 +514,10 @@ [[EarlGrey selectElementWithMatcher: ManagedProfileCreationBrowsingDataButtonMatcher()] performAction:grey_tap()]; - [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher: BrowsingDataManagementScreenMatcher()]; - // Browsing data merged by default. + // Browsing data should be merged by default due to the policy. [[EarlGrey selectElementWithMatcher:MergeBrowsingDataCellMatcher()] assertWithMatcher:grey_selected()]; @@ -531,6 +538,8 @@ PromoScreenPrimaryButtonMatcher()] performAction:grey_tap()]; + // Note: The profile switch happens here. + // History sync screen: Decline the promo (irrelevant here). [ChromeEarlGrey waitForMatcher:HistoryScreenMatcher()]; [[EarlGrey selectElementWithMatcher:chrome_test_util:: @@ -539,30 +548,32 @@ [SigninEarlGrey verifySignedInWithFakeIdentity:managedIdentity]; - // We should be in the same managed profile. + // We should be in the same profile (personal profile got converted to + // managed). NSString* newProfileName = [ChromeEarlGrey currentProfileName]; GREYAssert([originalProfileName isEqualToString:newProfileName], @"Profile name should be unchanged"); + // Sign out - this should cause a switch to a new personal profile. [SigninEarlGreyUI signOut]; NSString* personalProfileName = [ChromeEarlGrey currentProfileName]; GREYAssert(![personalProfileName isEqualToString:originalProfileName], - @"Profile name should be the personal one"); + @"Personal profile should be different"); - // Signin again + // Sign in again. TapIdentityDisc(); [[EarlGrey selectElementWithMatcher:ContinueButtonWithIdentityMatcher( managedIdentity)] performAction:grey_tap()]; - // Use should be signed in without having to see the managed profile + // The user should be signed in without having to see the managed profile // onboarding a second time. [SigninEarlGrey verifySignedInWithFakeIdentity:managedIdentity]; } -// Tests that signing in from a signed out state with a managed account -// shows the enterprise onboarding and when merging browsing data is suggested -// by policy on the account, it does not actually suggest merging the data since +// Tests that signing in from a signed out state with a managed account shows +// the enterprise onboarding and when merging browsing data is suggested by +// policy on the account, it does not actually suggest merging the data since // that value is not supported at account level. - (void) testSigninWithManagedAccountFromUnsignedStateWithDataMergingSuggestedOnAccount { @@ -599,7 +610,6 @@ [[EarlGrey selectElementWithMatcher: ManagedProfileCreationBrowsingDataButtonMatcher()] performAction:grey_tap()]; - [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher: BrowsingDataManagementScreenMatcher()]; @@ -625,6 +635,8 @@ PromoScreenPrimaryButtonMatcher()] performAction:grey_tap()]; + // Note: The profile switch happens here. + // History sync screen: Decline the promo (irrelevant here). [ChromeEarlGrey waitForMatcher:HistoryScreenMatcher()]; [[EarlGrey selectElementWithMatcher:chrome_test_util:: @@ -663,27 +675,27 @@ selectElementWithMatcher:AccountMenuSecondaryAccountsButtonMatcher()] performAction:grey_tap()]; - // Wait for enterprise onboarding screen. + // Wait for the enterprise onboarding screen. [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher: ManagedProfileCreationScreenMatcher()]; - - // No merge browsing data button shown + // No merge browsing data button shown. [[EarlGrey selectElementWithMatcher: ManagedProfileCreationBrowsingDataButtonMatcher()] assertWithMatcher:grey_notVisible()]; - // Confirm the enterprise onboarding screen. [[EarlGrey selectElementWithMatcher:chrome_test_util:: PromoScreenPrimaryButtonMatcher()] performAction:grey_tap()]; + // Note: The profile switch happens here. + // History sync screen: Decline the promo (irrelevant here). [ChromeEarlGrey waitForMatcher:HistoryScreenMatcher()]; [[EarlGrey selectElementWithMatcher:chrome_test_util:: PromoScreenSecondaryButtonMatcher()] performAction:grey_tap()]; - // Ensure the enterprise onboarding screen did disapepar. + // Ensure the New Tab page appeared in the new profile. [[EarlGrey selectElementWithMatcher:IdentityDiscMatcher()] assertWithMatcher:grey_sufficientlyVisible()]; @@ -742,7 +754,7 @@ selectElementWithMatcher:AccountMenuSecondaryAccountsButtonMatcher()] performAction:grey_tap()]; - // Wait for enterprise onboarding screen. + // Wait for the enterprise onboarding screen. [ChromeEarlGrey waitForSufficientlyVisibleElementWithMatcher: ManagedProfileCreationScreenMatcher()]; @@ -761,7 +773,7 @@ @"Profile should stay the same"); } -// Tests switching to a managed account and refuse the enterprise onboard +// Tests switching to a managed account but refusing the enterprise onboarding // screen. // TODO(crbug.com/399015648): Test is flaky on device. #if TARGET_OS_SIMULATOR @@ -807,27 +819,16 @@ PromoScreenSecondaryButtonMatcher()] performAction:grey_tap()]; - // Wait for the new profile to finish loading. - // TODO(crbug.com/399033938): Find a better way to wait for this. GREYWaitForAppToIdle(@"App failed to idle"); [SigninEarlGrey verifySignedInWithFakeIdentity:personalIdentity]; - // Verify that the profile was actually switched back. + // Verify that the profile was not switched. GREYAssert( [[ChromeEarlGrey currentProfileName] isEqualToString:personalProfileName], - @"Profile should have been switched"); + @"Profile should not have been switched"); } - -// TODO(crbug.com/411035267): Fix this flaky test on simulator. -#if TARGET_OS_SIMULATOR -#define MAYBE_testProfileNotDeletedOnRemovePersonalAccount \ - FLAKY_testProfileNotDeletedOnRemovePersonalAccount -#else -#define MAYBE_testProfileNotDeletedOnRemovePersonalAccount \ - testProfileNotDeletedOnRemovePersonalAccount -#endif -- (void)MAYBE_testProfileNotDeletedOnRemovePersonalAccount { +- (void)testProfileNotDeletedOnRemovePersonalAccount { // Separate profiles are only available in iOS 17+. if (!@available(iOS 17, *)) { return; @@ -919,6 +920,8 @@ PromoScreenPrimaryButtonMatcher()] performAction:grey_tap()]; + // Note: The profile switch happens here. + // History sync screen: Decline the promo (irrelevant here). [ChromeEarlGrey waitForMatcher:HistoryScreenMatcher()]; [[EarlGrey selectElementWithMatcher:chrome_test_util:: @@ -993,6 +996,8 @@ PromoScreenPrimaryButtonMatcher()] performAction:grey_tap()]; + // Note: The profile switch happens here. + // History sync screen: Decline the promo (irrelevant here). [ChromeEarlGrey waitForMatcher:HistoryScreenMatcher()]; [[EarlGrey selectElementWithMatcher:chrome_test_util:: @@ -1013,15 +1018,7 @@ @"Profile should have been switched back to personal"); } -// TODO(crbug.com/411035267): Fix this flaky test on simulator. -#if TARGET_OS_SIMULATOR -#define MAYBE_testProfileDeletedOnManagedAccountGone \ - FLAKY_testProfileDeletedOnManagedAccountGone -#else -#define MAYBE_testProfileDeletedOnManagedAccountGone \ - testProfileDeletedOnManagedAccountGone -#endif -- (void)MAYBE_testProfileDeletedOnManagedAccountGone { +- (void)testProfileDeletedOnManagedAccountGone { // Separate profiles are only available in iOS 17+. if (!@available(iOS 17, *)) { return; @@ -1185,12 +1182,11 @@ // Managed profile creation/confirmation screen: Accept. [ChromeEarlGrey waitForMatcher:ManagedProfileCreationScreenMatcher()]; - - // No merge browsing data button shown + // No merge browsing data button shown. [[EarlGrey selectElementWithMatcher: ManagedProfileCreationBrowsingDataButtonMatcher()] assertWithMatcher:grey_notVisible()]; - + // Confirm the screen. [[EarlGrey selectElementWithMatcher:chrome_test_util:: PromoScreenPrimaryButtonMatcher()] performAction:grey_tap()];
diff --git a/ios/chrome/browser/authentication/ui_bundled/separate_profiles_util.mm b/ios/chrome/browser/authentication/ui_bundled/separate_profiles_util.mm index b006383..08f7f1c 100644 --- a/ios/chrome/browser/authentication/ui_bundled/separate_profiles_util.mm +++ b/ios/chrome/browser/authentication/ui_bundled/separate_profiles_util.mm
@@ -52,7 +52,7 @@ IDS_IOS_ACCOUNT_MENU_EDIT_ACCOUNT_LIST)), grey_interactable(), nil)] performAction:grey_tap()]; - // Checks the manage accounts view is shown + // Checks the manage accounts view is shown. [[EarlGrey selectElementWithMatcher:grey_accessibilityID( kSettingsEditAccountListTableViewId)] assertWithMatcher:grey_sufficientlyVisible()];
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/signin/BUILD.gn index 6250bae2..f78b5c7 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/BUILD.gn +++ b/ios/chrome/browser/authentication/ui_bundled/signin/BUILD.gn
@@ -23,19 +23,14 @@ "//components/signin/public/identity_manager", "//components/sync/base", "//ios/chrome/browser/authentication/ui_bundled:continuation", - "//ios/chrome/browser/authentication/ui_bundled/signin:stop_animated_chrome_coordinator", "//ios/chrome/browser/shared/coordinator/chrome_coordinator", + "//ios/chrome/browser/shared/coordinator/chrome_coordinator:animated_coordinator", "//ios/chrome/browser/shared/model/profile", "//ios/chrome/browser/signin/model:capabilities_types", "//ios/chrome/browser/signin/model:system_identity", ] } -source_set("stop_animated_chrome_coordinator") { - sources = [ "stop_animated_chrome_coordinator.h" ] - frameworks = [ "UIKit.framework" ] -} - source_set("signin_protected") { sources = [ "signin_coordinator+protected.h" ] deps = [ ":signin_headers" ] @@ -52,7 +47,6 @@ ":signin_headers", ":signin_protected", ":signin_screen_provider", - ":stop_animated_chrome_coordinator", "//components/feature_engagement/public", "//components/feature_engagement/public:feature_constants", "//components/policy:generated", @@ -80,6 +74,7 @@ "//ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication", "//ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin", "//ios/chrome/browser/feature_engagement/model", + "//ios/chrome/browser/shared/coordinator/chrome_coordinator:animated_coordinator", "//ios/chrome/browser/shared/coordinator/scene:scene_state_header", "//ios/chrome/browser/shared/model/application_context", "//ios/chrome/browser/shared/model/browser",
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/BUILD.gn index dd5da9c..1659d8f 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/BUILD.gn +++ b/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/BUILD.gn
@@ -31,7 +31,6 @@ "//ios/chrome/browser/authentication/ui_bundled/enterprise:enterprise_utils", "//ios/chrome/browser/authentication/ui_bundled/signin:signin_headers", "//ios/chrome/browser/authentication/ui_bundled/signin:signin_protected", - "//ios/chrome/browser/authentication/ui_bundled/signin:stop_animated_chrome_coordinator", "//ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin", "//ios/chrome/browser/authentication/ui_bundled/signout_action_sheet", "//ios/chrome/browser/ntp/ui_bundled:feature_flags", @@ -45,6 +44,7 @@ "//ios/chrome/browser/settings/ui_bundled/google_services/manage_accounts", "//ios/chrome/browser/settings/ui_bundled/sync", "//ios/chrome/browser/shared/coordinator/chrome_coordinator", + "//ios/chrome/browser/shared/coordinator/chrome_coordinator:animated_coordinator", "//ios/chrome/browser/shared/coordinator/scene:scene_state_header", "//ios/chrome/browser/shared/model/application_context", "//ios/chrome/browser/shared/model/browser",
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.h index a4c70495..94985da 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.h +++ b/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.h
@@ -8,14 +8,12 @@ #import <UIKit/UIKit.h> #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" enum class AccountMenuAccessPoint; @protocol AccountMenuCoordinatorDelegate; // Coordinator to display the fast account menu view controller. -@interface AccountMenuCoordinator - : SigninCoordinator <StopAnimatedChromeCoordinator> +@interface AccountMenuCoordinator : SigninCoordinator // `anchorView`: Clicked view, used to anchor the menu to it when using // UIModalPresentationPopover mode.
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.mm index 7e0a872..ba7b037 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_coordinator.mm
@@ -31,7 +31,6 @@ #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator+protected.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" #import "ios/chrome/browser/authentication/ui_bundled/signout_action_sheet/signout_action_sheet_coordinator.h" #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h" #import "ios/chrome/browser/push_notification/model/push_notification_service.h" @@ -41,6 +40,7 @@ #import "ios/chrome/browser/settings/ui_bundled/settings_root_view_controlling.h" #import "ios/chrome/browser/settings/ui_bundled/sync/sync_encryption_passphrase_table_view_controller.h" #import "ios/chrome/browser/settings/ui_bundled/sync/sync_encryption_table_view_controller.h" +#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h" #import "ios/chrome/browser/shared/coordinator/scene/scene_state.h" #import "ios/chrome/browser/shared/model/application_context/application_context.h" #import "ios/chrome/browser/shared/model/browser/browser.h" @@ -96,7 +96,7 @@ base::ScopedClosureRunner _activityOverlayCallback; // The child signin coordinator if it’s open. It may be presented by the // Manage Account’s coordinator view controller. - SigninCoordinator<StopAnimatedChromeCoordinator>* _signinCoordinator; + SigninCoordinator* _signinCoordinator; // Clicked view, used to anchor the menu to it when using // UIModalPresentationPopover mode UIView* _anchorView; @@ -136,7 +136,7 @@ _identityManager = IdentityManagerFactory::GetForProfile(profile); _viewController = [[AccountMenuViewController alloc] - initWithHideEllipsisMenu:IdentityDiscAccountMenuEnabledWithoutEllipsis() + initWithHideEllipsisMenu:_accessPoint == AccountMenuAccessPoint::kWeb showSettingsButton:IdentityDiscAccountMenuEnabledWithSettings()]; _navigationController = [[UINavigationController alloc] @@ -175,6 +175,8 @@ completion:nil]; } +#pragma mark - AnimatedCoordinator + - (void)stopAnimated:(BOOL)animated { // TODO(crbug.com/336719423): Change condition to CHECK(_mediator). But // first inform the parent coordinator at didTapClose that this view was @@ -198,7 +200,7 @@ _identityManager = nil; _syncService = nullptr; _accountManagerService = nullptr; - [super stop]; + [super stopAnimated:animated]; } #pragma mark - UIAdaptivePresentationControllerDelegate
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.mm b/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.mm index 81f0d68e..817a29a 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller.mm
@@ -162,6 +162,7 @@ - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; + [self updateCloseButton]; // Update the bottom sheet height. [self resize]; } @@ -259,27 +260,17 @@ // Sets up the buttons. - (void)setUpTopButtons { + // Close button + [self updateCloseButton]; + + // Ellipsis button + if (_hideEllipsisMenu) { + return; + } UIImageSymbolConfiguration* symbolConfiguration = [UIImageSymbolConfiguration configurationWithPointSize:kButtonSize weight:UIImageSymbolWeightRegular scale:UIImageSymbolScaleMedium]; - // Stop button - UIUserInterfaceIdiom idiom = [[UIDevice currentDevice] userInterfaceIdiom]; - if (idiom != UIUserInterfaceIdiomPad) { - _closeButton = [self addTopButtonWithSymbolName:kXMarkCircleFillSymbol - symbolConfiguration:symbolConfiguration - isLeading:NO - accessibilityIdentifier:kAccountMenuCloseButtonId]; - [_closeButton addTarget:self - action:@selector(userTappedOnClose) - forControlEvents:UIControlEventTouchUpInside]; - } - - if (_hideEllipsisMenu) { - return; - } - - // Ellipsis button UIAction* manageYourAccountAction = [UIAction actionWithTitle: l10n_util::GetNSString( @@ -320,6 +311,42 @@ l10n_util::GetNSString(IDS_IOS_ICON_OPTION_MENU); } +// Decides if the Close button should be shown. +- (BOOL)shouldShowCloseButton { + UIUserInterfaceIdiom idiom = [[UIDevice currentDevice] userInterfaceIdiom]; + return idiom == UIUserInterfaceIdiomPhone || + self.presentingViewController.traitCollection.horizontalSizeClass == + UIUserInterfaceSizeClassCompact; +} + +// Adds or removes the Close button based on the device type and collection. +- (void)updateCloseButton { + BOOL isCloseButtonShown = _closeButton; + BOOL shouldShowCloseButton = [self shouldShowCloseButton]; + if (shouldShowCloseButton == isCloseButtonShown) { + return; + } + if (shouldShowCloseButton) { + // Add the Close button. + UIImageSymbolConfiguration* symbolConfiguration = + [UIImageSymbolConfiguration + configurationWithPointSize:kButtonSize + weight:UIImageSymbolWeightRegular + scale:UIImageSymbolScaleMedium]; + _closeButton = [self addTopButtonWithSymbolName:kXMarkCircleFillSymbol + symbolConfiguration:symbolConfiguration + isLeading:NO + accessibilityIdentifier:kAccountMenuCloseButtonId]; + [_closeButton addTarget:self + action:@selector(userTappedOnClose) + forControlEvents:UIControlEventTouchUpInside]; + } else { + // Remove the Close button. + [_closeButton removeFromSuperview]; + _closeButton = nil; + } +} + // Configures and returns a cell. - (UITableViewCell*)cellForTableView:(UITableView*)tableView indexPath:(NSIndexPath*)indexPath @@ -446,16 +473,22 @@ presentationController.prefersEdgeAttachedInCompactHeight = YES; presentationController.widthFollowsPreferredContentSizeWhenEdgeAttached = YES; presentationController.preferredCornerRadius = kHalfSheetCornerRadius; - __weak __typeof(self) weakSelf = self; - auto preferredHeightForSheetContent = ^CGFloat( - id<UISheetPresentationControllerDetentResolutionContext> context) { - return [weakSelf preferredHeightForSheetContent]; - }; - UISheetPresentationControllerDetent* customDetent = - [UISheetPresentationControllerDetent - customDetentWithIdentifier:kCustomMinimizedDetentIdentifier - resolver:preferredHeightForSheetContent]; - presentationController.detents = @[ customDetent ]; + + // In case of compact width only, adjust detents. + if (self.traitCollection.horizontalSizeClass == + UIUserInterfaceSizeClassCompact) { + __weak __typeof(self) weakSelf = self; + auto preferredHeightForSheetContent = ^CGFloat( + id<UISheetPresentationControllerDetentResolutionContext> context) { + return [weakSelf preferredHeightForSheetContent]; + }; + UISheetPresentationControllerDetent* customDetent = + [UISheetPresentationControllerDetent + customDetentWithIdentifier:kCustomMinimizedDetentIdentifier + resolver:preferredHeightForSheetContent]; + presentationController.detents = @[ customDetent ]; + } + presentationController.selectedDetentIdentifier = kCustomMinimizedDetentIdentifier; } @@ -504,11 +537,6 @@ [accountsIdentifiers addObject:@(RowIdentifierAddAccount)]; [snapshot appendItemsWithIdentifiers:accountsIdentifiers intoSectionWithIdentifier:@(AccountsSectionIdentifier)]; - if (_hideEllipsisMenu) { - [accountsIdentifiers addObject:@(RowIdentifierManageAccounts)]; - [snapshot appendItemsWithIdentifiers:accountsIdentifiers - intoSectionWithIdentifier:@(AccountsSectionIdentifier)]; - } if (_showSettingsButton) { // The sign-out button is grouped with the accounts section. @@ -518,8 +546,12 @@ [snapshot appendItemsWithIdentifiers:@[ @(RowIdentifierSettings) ] intoSectionWithIdentifier:@(SettingsSectionIdentifier)]; } else { - // The sign-out button has its own section. [snapshot appendSectionsWithIdentifiers:@[ @(SignOutSectionIdentifier) ]]; + // The sign-out button has its own section. + if (_hideEllipsisMenu) { + [snapshot appendItemsWithIdentifiers:@[ @(RowIdentifierManageAccounts) ] + intoSectionWithIdentifier:@(SignOutSectionIdentifier)]; + } [snapshot appendItemsWithIdentifiers:@[ @(RowIdentifierSignOut) ] intoSectionWithIdentifier:@(SignOutSectionIdentifier)]; } @@ -527,8 +559,11 @@ [_accountMenuDataSource applySnapshot:snapshot animatingDifferences:YES]; } -// Returns the sheet presentation controller if it exists. +// Returns the sheet presentation controller of the used presentation style. - (UISheetPresentationController*)sheetPresentationController { + if (self.navigationController.sheetPresentationController) { + return self.navigationController.sheetPresentationController; + } return self.navigationController.popoverPresentationController .adaptiveSheetPresentationController; }
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller_unittests.mm b/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller_unittests.mm index 5976591..b14b2aa 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller_unittests.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin/account_menu/account_menu_view_controller_unittests.mm
@@ -279,14 +279,17 @@ [view_controller_ updatePrimaryAccount]; ExpectTextAtPath( + l10n_util::GetNSString(IDS_IOS_ACCOUNT_MENU_EDIT_ACCOUNT_LIST), + [NSIndexPath indexPathForRow:0 inSection:1]); + ExpectTextAtPath( l10n_util::GetNSString(IDS_IOS_GOOGLE_ACCOUNT_SETTINGS_SIGN_OUT_ITEM), - path_for_sign_out_); + [NSIndexPath indexPathForRow:1 inSection:1]); EXPECT_EQ(2, TableView().numberOfSections); - // The secondary account, Add Account..., and Manage Accounts. - EXPECT_EQ(3, [TableView() numberOfRowsInSection:0]); - // Sign Out - EXPECT_EQ(1, [TableView() numberOfRowsInSection:1]); + // The secondary account, and Add Account.... + EXPECT_EQ(2, [TableView() numberOfRowsInSection:0]); + // Manage Accounts, and Sign Out + EXPECT_EQ(2, [TableView() numberOfRowsInSection:1]); } // Test the account menu with Settings button.
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/BUILD.gn index 780e150..099697f 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/BUILD.gn +++ b/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/BUILD.gn
@@ -22,9 +22,9 @@ "//ios/chrome/browser/authentication/ui_bundled:continuation", "//ios/chrome/browser/authentication/ui_bundled/history_sync", "//ios/chrome/browser/authentication/ui_bundled/signin:signin_protected", - "//ios/chrome/browser/authentication/ui_bundled/signin:stop_animated_chrome_coordinator", "//ios/chrome/browser/main/model", "//ios/chrome/browser/shared/coordinator/alert", + "//ios/chrome/browser/shared/coordinator/chrome_coordinator:animated_coordinator", "//ios/chrome/browser/shared/model/application_context", "//ios/chrome/browser/shared/public/features", "//ios/chrome/browser/signin/model",
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_coordinator.h index 924e4b2..ff6c1ee 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_coordinator.h +++ b/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_coordinator.h
@@ -7,13 +7,11 @@ #import "ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_enums.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" // Coordinates adding an account with different intents: // + adding account from the settings // + reauthentication -@interface AddAccountSigninCoordinator - : SigninCoordinator <StopAnimatedChromeCoordinator> +@interface AddAccountSigninCoordinator : SigninCoordinator - (instancetype)initWithBaseViewController:(UIViewController*)viewController browser:(Browser*)browser
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_coordinator.mm index 4532600..90871b4 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_coordinator.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin/add_account_signin/add_account_signin_coordinator.mm
@@ -44,8 +44,7 @@ @property(nonatomic, strong) AlertCoordinator* alertCoordinator; // Coordinator to handle additional steps after the identity is added, i.e. // after `addAccountSigninManager` does its job. -@property(nonatomic, strong) SigninCoordinator<StopAnimatedChromeCoordinator>* - postSigninManagerCoordinator; +@property(nonatomic, strong) SigninCoordinator* postSigninManagerCoordinator; // Coordinator for history sync opt-in. @property(nonatomic, strong) HistorySyncPopupCoordinator* historySyncPopupCoordinator; @@ -119,7 +118,7 @@ [self.addAccountSigninManager showSigninWithIntent:self.signinIntent]; } -#pragma mark - StopAnimatedChromeCoordinator +#pragma mark - AnimatedCoordinator - (void)stopAnimated:(BOOL)animated { [super stopAnimated:animated];
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_coordinator.h index c74d796..a9e2fa0f 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_coordinator.h +++ b/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_coordinator.h
@@ -13,8 +13,7 @@ // Coordinates various Identity options in Chrome including signing in // using accounts on the device, opening Incognito, and adding an account. -@interface ConsistencyPromoSigninCoordinator - : SigninCoordinator <StopAnimatedChromeCoordinator> +@interface ConsistencyPromoSigninCoordinator : SigninCoordinator - (instancetype) initWithBaseViewController:(UIViewController*)viewController
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm index 5cfbf85..3ab8343 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_promo_signin_coordinator.mm
@@ -27,8 +27,8 @@ #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator+protected.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_utils.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" #import "ios/chrome/browser/shared/coordinator/alert/alert_coordinator.h" +#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h" #import "ios/chrome/browser/shared/model/browser/browser.h" #import "ios/chrome/browser/shared/model/prefs/pref_names.h" #import "ios/chrome/browser/shared/model/profile/profile_ios.h" @@ -67,8 +67,7 @@ // `self.defaultAccountCoordinator.selectedIdentity`. @property(nonatomic, strong, readonly) id<SystemIdentity> selectedIdentity; // Coordinator to add an account to the device. -@property(nonatomic, strong) - SigninCoordinator<StopAnimatedChromeCoordinator>* addAccountCoordinator; +@property(nonatomic, strong) SigninCoordinator* addAccountCoordinator; @property(nonatomic, strong) ConsistencyPromoSigninMediator* consistencyPromoSigninMediator; @@ -82,6 +81,7 @@ } #pragma mark - Public + - (instancetype) initWithBaseViewController:(UIViewController*)viewController browser:(Browser*)browser @@ -133,7 +133,7 @@ continuationProvider:continuationProvider]; } -#pragma mark - SigninCoordinator +#pragma mark - ChromeCoordinator - (void)start { [super start]; @@ -186,6 +186,8 @@ completion:nil]; } +#pragma mark - SigninCoordinator + - (void)runCompletionWithSigninResult:(SigninCoordinatorResult)signinResult completionIdentity:(id<SystemIdentity>)completionIdentity { switch (signinResult) {
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/coordinator/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/coordinator/BUILD.gn index 200d0d6c7e..a9fe955 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/coordinator/BUILD.gn +++ b/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/coordinator/BUILD.gn
@@ -13,12 +13,12 @@ "//ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator", "//ios/chrome/browser/authentication/ui_bundled/signin:signin_headers", "//ios/chrome/browser/authentication/ui_bundled/signin:signin_protected", - "//ios/chrome/browser/authentication/ui_bundled/signin:stop_animated_chrome_coordinator", "//ios/chrome/browser/first_run/ui_bundled:screen_delegate", "//ios/chrome/browser/first_run/ui_bundled:utils", "//ios/chrome/browser/main/model", "//ios/chrome/browser/screen/ui_bundled:screen_provider", "//ios/chrome/browser/screen/ui_bundled:screen_type", + "//ios/chrome/browser/shared/coordinator/chrome_coordinator:animated_coordinator", "//ios/chrome/browser/signin/model:authentication_service", "//ios/chrome/browser/signin/model:authentication_service_factory", ]
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/coordinator/fullscreen_signin_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/coordinator/fullscreen_signin_coordinator.h index aebe536..2c34131 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/coordinator/fullscreen_signin_coordinator.h +++ b/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/coordinator/fullscreen_signin_coordinator.h
@@ -6,15 +6,14 @@ #define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_FULLSCREEN_SIGNIN_COORDINATOR_FULLSCREEN_SIGNIN_COORDINATOR_H_ #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" +#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h" @class ScreenProvider; // Main coordinator that manages the overall fullscreen sign-in flow. // It contains a child coordinator, FullscreenSigninScreenCoordinator, // which is responsible for presenting the actual fullscreen sign-in screen. -@interface FullscreenSigninCoordinator - : SigninCoordinator <StopAnimatedChromeCoordinator> +@interface FullscreenSigninCoordinator : SigninCoordinator // Initiate the coordinator. // `browser` used for authentication. It must not be off the record (incognito).
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/coordinator/fullscreen_signin_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/coordinator/fullscreen_signin_coordinator.mm index a23c906d..785d15e 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/coordinator/fullscreen_signin_coordinator.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin/fullscreen_signin/coordinator/fullscreen_signin_coordinator.mm
@@ -11,11 +11,11 @@ #import "ios/chrome/browser/authentication/ui_bundled/fullscreen_signin_screen/coordinator/fullscreen_signin_screen_coordinator.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator+protected.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" #import "ios/chrome/browser/first_run/ui_bundled/first_run_screen_delegate.h" #import "ios/chrome/browser/first_run/ui_bundled/first_run_util.h" #import "ios/chrome/browser/screen/ui_bundled/screen_provider.h" #import "ios/chrome/browser/screen/ui_bundled/screen_type.h" +#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h" #import "ios/chrome/browser/shared/model/browser/browser.h" #import "ios/chrome/browser/shared/model/profile/profile_ios.h" #import "ios/chrome/browser/signin/model/authentication_service.h" @@ -24,8 +24,7 @@ @interface FullscreenSigninCoordinator () <FirstRunScreenDelegate> @property(nonatomic, strong) ScreenProvider* screenProvider; -@property(nonatomic, strong) - ChromeCoordinator<StopAnimatedChromeCoordinator>* childCoordinator; +@property(nonatomic, strong) AnimatedCoordinator* childCoordinator; // The view controller used by FullscreenSigninCoordinator. @property(nonatomic, strong) UINavigationController* navigationController; @@ -75,7 +74,7 @@ completion:nil]; } -#pragma mark - StopAnimatedChromeCoordinator +#pragma mark - AnimatedCoordinator - (void)stopAnimated:(BOOL)animated { // Stop the child coordinator UI first before dismissing the forced @@ -130,8 +129,7 @@ } // Creates a screen coordinator according to `type`. -- (ChromeCoordinator<StopAnimatedChromeCoordinator>*) - createChildCoordinatorWithScreenType:(ScreenType)type { +- (AnimatedCoordinator*)createChildCoordinatorWithScreenType:(ScreenType)type { switch (type) { case kSignIn: return [[FullscreenSigninScreenCoordinator alloc]
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/history_sync/history_sync_signin_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/signin/history_sync/history_sync_signin_coordinator.h index 509d689..2e995de 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/history_sync/history_sync_signin_coordinator.h +++ b/ios/chrome/browser/authentication/ui_bundled/signin/history_sync/history_sync_signin_coordinator.h
@@ -9,8 +9,7 @@ // Coordinator to present the History Sync Opt-In screen. // This requires the user to be signed in already. -@interface HistorySyncSigninCoordinator - : SigninCoordinator <StopAnimatedChromeCoordinator> +@interface HistorySyncSigninCoordinator : SigninCoordinator @end
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/history_sync/history_sync_signin_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/history_sync/history_sync_signin_coordinator.mm index 9b5496a..0a772bf 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/history_sync/history_sync_signin_coordinator.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin/history_sync/history_sync_signin_coordinator.mm
@@ -50,7 +50,7 @@ [_syncPopupCoordinator start]; } -#pragma mark - StopAnimatedChromeCoordinator +#pragma mark - AnimatedCoordinator - (void)stopAnimated:(BOOL)animated { [_syncPopupCoordinator stopAnimated:animated];
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/instant_signin/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/signin/instant_signin/BUILD.gn index 0ee11b5..b4f194d 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/instant_signin/BUILD.gn +++ b/ios/chrome/browser/authentication/ui_bundled/signin/instant_signin/BUILD.gn
@@ -19,10 +19,10 @@ "//ios/chrome/browser/authentication/ui_bundled/authentication_flow:request_helper", "//ios/chrome/browser/authentication/ui_bundled/identity_chooser", "//ios/chrome/browser/authentication/ui_bundled/signin:signin_protected", - "//ios/chrome/browser/authentication/ui_bundled/signin:stop_animated_chrome_coordinator", "//ios/chrome/browser/authentication/ui_bundled/signin/logging", "//ios/chrome/browser/main/model", "//ios/chrome/browser/shared/coordinator/alert", + "//ios/chrome/browser/shared/coordinator/chrome_coordinator:animated_coordinator", "//ios/chrome/browser/shared/model/application_context", "//ios/chrome/browser/shared/public/features", "//ios/chrome/browser/shared/ui/elements:activity_overlay",
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/instant_signin/instant_signin_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/signin/instant_signin/instant_signin_coordinator.h index 22029a8..61c84a2 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/instant_signin/instant_signin_coordinator.h +++ b/ios/chrome/browser/authentication/ui_bundled/signin/instant_signin/instant_signin_coordinator.h
@@ -7,7 +7,6 @@ #import "ios/chrome/browser/authentication/ui_bundled/change_profile_continuation_provider.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" namespace signin_metrics { enum class AccessPoint; @@ -22,8 +21,7 @@ // * one selected by the identity chooser that gets immediately opened, if the // device has identities, or // * otherwise, one obtained through the add account dialog. -@interface InstantSigninCoordinator - : SigninCoordinator <StopAnimatedChromeCoordinator> +@interface InstantSigninCoordinator : SigninCoordinator - (instancetype)initWithBaseViewController:(UIViewController*)viewController browser:(Browser*)browser
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/instant_signin/instant_signin_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/instant_signin/instant_signin_coordinator.mm index fd055c52..1f7218c 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/instant_signin/instant_signin_coordinator.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin/instant_signin/instant_signin_coordinator.mm
@@ -16,8 +16,8 @@ #import "ios/chrome/browser/authentication/ui_bundled/signin/logging/user_signin_logger.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator+protected.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" #import "ios/chrome/browser/shared/coordinator/alert/alert_coordinator.h" +#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h" #import "ios/chrome/browser/shared/model/browser/browser.h" #import "ios/chrome/browser/shared/public/features/features.h" #import "ios/chrome/browser/shared/ui/elements/activity_overlay_coordinator.h" @@ -40,8 +40,7 @@ // Coordinator for the user to select an account. IdentityChooserCoordinator* _identityChooserCoordinator; // Coordinator to add an account. - SigninCoordinator<StopAnimatedChromeCoordinator>* - _addAccountSigninCoordinator; + SigninCoordinator* _addAccountSigninCoordinator; // Overlay to block the current window while the sign-in is in progress. ActivityOverlayCoordinator* _activityOverlayCoordinator; // Action recorded if sign-in succeeded. @@ -137,6 +136,8 @@ [_identityChooserCoordinator start]; } +#pragma mark - AnimatedCoordinator + - (void)stopAnimated:(BOOL)animated { if (_addAccountSigninCoordinator) { CHECK(!_identityChooserCoordinator);
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator+protected.h b/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator+protected.h index 14c5b2f..18516cc 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator+protected.h +++ b/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator+protected.h
@@ -19,11 +19,6 @@ completionIdentity:(id<SystemIdentity>)completionIdentity NS_REQUIRES_SUPER; -// TODO(crbug.com/381444097): implements StopAnimatedChromeCoordinator in the -// header file once each class inheriting SigninCoordinator implements this -// protocol. -- (void)stopAnimated:(BOOL)animated; - @end #endif // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_SIGNIN_COORDINATOR_PROTECTED_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h index 676fa0e..3f1c89d 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h +++ b/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h
@@ -12,7 +12,7 @@ #import "ios/chrome/browser/authentication/ui_bundled/change_profile_continuation_provider.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_context_style.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" +#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h" #import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h" enum class AccountMenuAccessPoint; @@ -30,7 +30,7 @@ // Main class for sign-in coordinator. This class should not be instantiated // directly, this should be done using the class methods. -@interface SigninCoordinator : ChromeCoordinator +@interface SigninCoordinator : AnimatedCoordinator // Called when the sign-in dialog is interrupted, canceled or successful. // This completion needs to be set before calling -[SigninCoordinator start]. @@ -66,7 +66,7 @@ // an identity, and starts the sign-in flow. If there is no identity on the // device, the add account dialog will be displayed, and then the sign-in flow // is started with the newly added identity. -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) instantSigninCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -83,7 +83,7 @@ // Returns a coordinator for fullscreen sign-in workflow. // `viewController` presents the sign-in. -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) fullscreenSigninCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -99,7 +99,7 @@ // Returns a coordinator for upgrade sign-in workflow. // `viewController` presents the sign-in. // `contextStyle` is used to customize content on screens. -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) upgradeSigninPromoCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -113,7 +113,7 @@ // `viewController` presents the sign-in. // `contextStyle` is used to customize content on screens. // `accessPoint` access point from the sign-in where is started. -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) addAccountCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -130,7 +130,7 @@ // `contextStyle` is used to customize content on screens. // `accessPoint` access point from the sign-in where is started. // `promoAction` is promo button used to trigger the sign-in. -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) primaryAccountReauthCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -152,7 +152,7 @@ // `contextStyle` is used to customize content on screens. // `accessPoint` access point from the sign-in where is started. // `promoAction` is promo button used to trigger the sign-in. -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) signinAndSyncReauthCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -176,7 +176,7 @@ // `securityDomainID` Identifies a particular security domain. // `trigger` UI elements where the trusted vault reauth has been triggered. // `accessPoint` Identifies where the dialog is initiated from. -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) trustedVaultReAuthenticationCoordinatorWithBaseViewController: (UIViewController*)viewController browser: @@ -202,7 +202,7 @@ // `viewController` presents the promo. // This method can return nil if sign-in is not authorized or if there is no // account on the device. -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) consistencyPromoSigninCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -224,7 +224,7 @@ // if the user hasn't already approved it. // `fullscreenPromo`: whether the promo should be displayed in a fullscreen // modal. -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) signinAndHistorySyncCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -244,7 +244,7 @@ continuationProvider; // Returns a coordinator to switch account. -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) accountMenuCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -255,7 +255,7 @@ (AccountMenuAccessPoint)accessPoint; // Returns a coordinator to show the history sync. -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) historySyncCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -268,7 +268,6 @@ // ChromeCoordinator. - (void)start NS_REQUIRES_SUPER; -- (void)stop NS_REQUIRES_SUPER; @end
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.mm index 5d87761d..1d702e5e 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.mm
@@ -20,9 +20,9 @@ #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_screen_provider.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator.h" +#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h" #import "ios/chrome/browser/shared/model/browser/browser.h" #import "ios/chrome/browser/shared/model/prefs/pref_names.h" #import "ios/chrome/browser/shared/model/profile/profile_ios.h" @@ -54,7 +54,7 @@ registry->RegisterDictionaryPref(prefs::kSigninHasAcceptedManagementDialog); } -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) instantSigninCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -79,7 +79,7 @@ continuationProvider:continuationProvider]; } -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) fullscreenSigninCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -101,7 +101,7 @@ changeProfileContinuationProvider:changeProfileContinuationProvider]; } -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) upgradeSigninPromoCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -122,7 +122,7 @@ continuationProvider:changeProfileContinuationProvider]; } -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) addAccountCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -142,7 +142,7 @@ continuationProvider:continuationProvider]; } -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) primaryAccountReauthCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -166,7 +166,7 @@ continuationProvider:continuationProvider]; } -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) signinAndSyncReauthCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -190,7 +190,7 @@ continuationProvider:continuationProvider]; } -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) trustedVaultReAuthenticationCoordinatorWithBaseViewController: (UIViewController*)viewController browser: @@ -220,7 +220,7 @@ accessPoint:accessPoint]; } -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) consistencyPromoSigninCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -243,7 +243,7 @@ continuationProvider:continuationProvider]; } -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) signinAndHistorySyncCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -272,7 +272,7 @@ continuationProvider:continuationProvider]; } -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) accountMenuCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -289,7 +289,7 @@ accessPoint:accessPoint]; } -+ (SigninCoordinator<StopAnimatedChromeCoordinator>*) ++ (SigninCoordinator*) historySyncCoordinatorWithBaseViewController: (UIViewController*)viewController browser:(Browser*)browser @@ -314,25 +314,8 @@ DCHECK(self.signinCompletion); } -- (void)stop { - // If you are an user of a SigninCoordinator<StopAnimatedChromeCoordinator> - // subclass, you can stop it by calling -stop or -stopAnimated. - CHECK([self conformsToProtocol:@protocol(StopAnimatedChromeCoordinator)], - base::NotFatalUntil::M145); - SigninCoordinator<StopAnimatedChromeCoordinator>* stopAnimatedSelf = - base::apple::ObjCCast<SigninCoordinator<StopAnimatedChromeCoordinator>>( - self); - [stopAnimatedSelf stopAnimated:NO]; -} - #pragma mark - Protected -// TODO(crbug.com/381444097): implements this protocol in the header file once -// each class inheriting SigninCoordinator implements this protocol. -- (void)stopAnimated:(BOOL)animated { - [super stop]; -} - - (void)runCompletionWithSigninResult:(SigninCoordinatorResult)signinResult completionIdentity:(id<SystemIdentity>)completionIdentity { // `identity` is set, if and only if the sign-in is successful.
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.h index 3921947..f10410a 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.h +++ b/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.h
@@ -6,7 +6,6 @@ #define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_SIGNIN_HISTORY_SYNC_SIGNIN_AND_HISTORY_SYNC_COORDINATOR_H_ #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" namespace signin_metrics { enum class AccessPoint : int; @@ -16,8 +15,7 @@ // Coordinator to present the Sign-In then the History Sync Opt-In screen. // If there is no identity on the device, the SSO add account is displayed to // sign-in and then the history sync is displayed. -@interface SignInAndHistorySyncCoordinator - : SigninCoordinator <StopAnimatedChromeCoordinator> +@interface SignInAndHistorySyncCoordinator : SigninCoordinator // Init the coordinator with its base `viewController`, the `browser`, from // which `accessPoint` the sign in flow was initialized, using which
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.mm index d3bbf9e..2ba6d5d 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.mm
@@ -55,7 +55,7 @@ @implementation SignInAndHistorySyncCoordinator { // Sign-in or history sync coordinator, according to `_currentStep`. - ChromeCoordinator<StopAnimatedChromeCoordinator>* _childCoordinator; + AnimatedCoordinator* _childCoordinator; // The current step. SignInHistorySyncStep _currentStep; // Promo button used to trigger the sign-in. @@ -108,7 +108,7 @@ [self presentNextStepWithPreviousResult:SigninCoordinatorResultSuccess]; } -#pragma mark - StopAnimatedChromeCoordinator +#pragma mark - AnimatedCoordinator - (void)stopAnimated:(BOOL)animated { [_childCoordinator stopAnimated:animated]; @@ -181,20 +181,17 @@ } // Creates the current step coordinator according to `_currentStep`. -- (ChromeCoordinator<StopAnimatedChromeCoordinator>*) - createPresentStepChildCoordinator { +- (AnimatedCoordinator*)createPresentStepChildCoordinator { switch (_currentStep) { case SignInHistorySyncStep::kFullscreenSignin: { // TODO(crbug.com/375605572) Sends an actual continuation. - SigninCoordinator<StopAnimatedChromeCoordinator>* coordinator = - [[FullscreenSigninCoordinator alloc] - initWithBaseViewController:self.baseViewController - browser:self.browser - screenProvider:[[SigninScreenProvider alloc] - init] - contextStyle:self.contextStyle - accessPoint:self.accessPoint - changeProfileContinuationProvider:_continuationProvider]; + SigninCoordinator* coordinator = [[FullscreenSigninCoordinator alloc] + initWithBaseViewController:self.baseViewController + browser:self.browser + screenProvider:[[SigninScreenProvider alloc] init] + contextStyle:self.contextStyle + accessPoint:self.accessPoint + changeProfileContinuationProvider:_continuationProvider]; __weak __typeof(self) weakSelf = self; coordinator.signinCompletion = ^(SigninCoordinatorResult result, id<SystemIdentity>) { @@ -203,7 +200,7 @@ return coordinator; } case SignInHistorySyncStep::kBottomSheetSignin: { - SigninCoordinator<StopAnimatedChromeCoordinator>* coordinator = + SigninCoordinator* coordinator = [[ConsistencyPromoSigninCoordinator alloc] initWithBaseViewController:self.baseViewController browser:self.browser @@ -220,15 +217,14 @@ } case SignInHistorySyncStep::kInstantSignin: { // TODO(crbug.com/375605572) Sends an actual continuation. - SigninCoordinator<StopAnimatedChromeCoordinator>* coordinator = - [[InstantSigninCoordinator alloc] - initWithBaseViewController:self.baseViewController - browser:self.browser - identity:nil - contextStyle:self.contextStyle - accessPoint:self.accessPoint - promoAction:_promoAction - continuationProvider:_continuationProvider]; + SigninCoordinator* coordinator = [[InstantSigninCoordinator alloc] + initWithBaseViewController:self.baseViewController + browser:self.browser + identity:nil + contextStyle:self.contextStyle + accessPoint:self.accessPoint + promoAction:_promoAction + continuationProvider:_continuationProvider]; __weak __typeof(self) weakSelf = self; coordinator.signinCompletion = ^(SigninCoordinatorResult result, id<SystemIdentity>) {
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h deleted file mode 100644 index 0fd194f..0000000 --- a/ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h +++ /dev/null
@@ -1,21 +0,0 @@ -// Copyright 2025 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_AUTHENTICATION_UI_BUNDLED_SIGNIN_STOP_ANIMATED_CHROME_COORDINATOR_H_ -#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_STOP_ANIMATED_CHROME_COORDINATOR_H_ - -#import <Foundation/Foundation.h> - -// Interface for a ChromeCoordinator that can be stopped with or without -// animated. It is expected that the `stop` method of classes implementing this -// protocol is defined as `[self stopAnimated:NO]`. -@protocol StopAnimatedChromeCoordinator <NSObject> - -// Same as `ChromeCoordinator`’s stop, but can be animated or not. Must call -// `[super stop]`. -- (void)stopAnimated:(BOOL)animated; - -@end - -#endif // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_STOP_ANIMATED_CHROME_COORDINATOR_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/BUILD.gn index a24ef7f..7cfaddc2 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/BUILD.gn +++ b/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/BUILD.gn
@@ -14,8 +14,8 @@ "//components/sync", "//ios/chrome/app/strings", "//ios/chrome/browser/authentication/ui_bundled/signin:signin_protected", - "//ios/chrome/browser/authentication/ui_bundled/signin:stop_animated_chrome_coordinator", "//ios/chrome/browser/shared/coordinator/alert", + "//ios/chrome/browser/shared/coordinator/chrome_coordinator:animated_coordinator", "//ios/chrome/browser/shared/model/browser", "//ios/chrome/browser/signin/model:authentication_service", "//ios/chrome/browser/signin/model:authentication_service_factory",
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator.h index d6b8abab..7b24bdc 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator.h +++ b/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator.h
@@ -6,7 +6,6 @@ #define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_TRUSTED_VAULT_REAUTHENTICATION_TRUSTED_VAULT_REAUTHENTICATION_COORDINATOR_H_ #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" namespace syncer { enum class TrustedVaultUserActionTriggerForUMA; @@ -17,8 +16,7 @@ // Coordinates the Trusted Vault re-authentication dialog. Trusted Valut is // managed by IOSTrustedValueClient. -@interface TrustedVaultReauthenticationCoordinator - : SigninCoordinator <StopAnimatedChromeCoordinator> +@interface TrustedVaultReauthenticationCoordinator : SigninCoordinator - (instancetype)initWithBaseViewController:(UIViewController*)viewController browser:(Browser*)browser
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator.mm index 97608423..7779cfe 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator.mm
@@ -9,8 +9,8 @@ #import "components/sync/service/sync_service_utils.h" #import "components/trusted_vault/trusted_vault_server_constants.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator+protected.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" #import "ios/chrome/browser/shared/coordinator/alert/alert_coordinator.h" +#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h" #import "ios/chrome/browser/shared/model/browser/browser.h" #import "ios/chrome/browser/shared/model/profile/profile_ios.h" #import "ios/chrome/browser/signin/model/authentication_service.h" @@ -66,7 +66,7 @@ return self; } -#pragma mark - StopAnimatedChromeCoordinator +#pragma mark - AnimatedCoordinator - (void)stopAnimated:(BOOL)animated { // This coordinator should be either showing an error dialog or the trusted
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator_unittest.mm b/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator_unittest.mm index dc36202..3b2aede 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator_unittest.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin/trusted_vault_reauthentication/trusted_vault_reauthentication_coordinator_unittest.mm
@@ -79,8 +79,7 @@ syncer::TrustedVaultUserActionTriggerForUMA::kSettings; signin_metrics::AccessPoint accessPoint = signin_metrics::AccessPoint::kStartPage; - SigninCoordinator< - StopAnimatedChromeCoordinator>* signinCoordinator = [SigninCoordinator + SigninCoordinator* signinCoordinator = [SigninCoordinator trustedVaultReAuthenticationCoordinatorWithBaseViewController: base_view_controller_ browser:browser()
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/BUILD.gn index 1995edd7..a43601b 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/BUILD.gn +++ b/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/BUILD.gn
@@ -16,12 +16,12 @@ "//ios/chrome/browser/authentication/ui_bundled/signin:signin_headers", "//ios/chrome/browser/authentication/ui_bundled/signin:signin_protected", "//ios/chrome/browser/authentication/ui_bundled/signin:signin_screen_provider", - "//ios/chrome/browser/authentication/ui_bundled/signin:stop_animated_chrome_coordinator", "//ios/chrome/browser/authentication/ui_bundled/signin/logging", "//ios/chrome/browser/first_run/ui_bundled:screen_delegate", "//ios/chrome/browser/first_run/ui_bundled:utils", "//ios/chrome/browser/screen/ui_bundled:screen_provider", "//ios/chrome/browser/screen/ui_bundled:screen_type", + "//ios/chrome/browser/shared/coordinator/chrome_coordinator:animated_coordinator", "//ios/chrome/browser/shared/model/browser", "//ios/chrome/browser/signin/model", "//ios/chrome/browser/signin/model:authentication_service",
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator.h b/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator.h index efe5106..7c808e59 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator.h +++ b/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator.h
@@ -6,8 +6,8 @@ #define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_SIGNIN_TWO_SCREENS_SIGNIN_TWO_SCREENS_SIGNIN_COORDINATOR_H_ #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" #import "ios/chrome/browser/first_run/ui_bundled/first_run_screen_delegate.h" +#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h" namespace signin_metrics { enum class AccessPoint : int; @@ -16,7 +16,7 @@ // Coordinator to present the sign-in and sync first run screens. @interface TwoScreensSigninCoordinator - : SigninCoordinator <FirstRunScreenDelegate, StopAnimatedChromeCoordinator> + : SigninCoordinator <FirstRunScreenDelegate> // Initiate the coordinator. // `browser` used for authentication. It must not be off the record (incognito).
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator.mm index 33c4287..5d3574d8 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin/two_screens_signin/two_screens_signin_coordinator.mm
@@ -16,11 +16,11 @@ #import "ios/chrome/browser/authentication/ui_bundled/signin/logging/upgrade_signin_logger.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/signin_coordinator+protected.h" -#import "ios/chrome/browser/authentication/ui_bundled/signin/stop_animated_chrome_coordinator.h" #import "ios/chrome/browser/authentication/ui_bundled/signin/uno_signin_screen_provider.h" #import "ios/chrome/browser/first_run/ui_bundled/first_run_util.h" #import "ios/chrome/browser/screen/ui_bundled/screen_provider.h" #import "ios/chrome/browser/screen/ui_bundled/screen_type.h" +#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h" #import "ios/chrome/browser/shared/model/browser/browser.h" #import "ios/chrome/browser/shared/model/profile/profile_ios.h" #import "ios/chrome/browser/signin/model/authentication_service.h" @@ -112,7 +112,7 @@ completion:nil]; } -#pragma mark - StopAnimatedChromeCoordinator +#pragma mark - AnimatedCoordinator - (void)stopAnimated:(BOOL)animated { [_navigationController.presentingViewController
diff --git a/ios/chrome/browser/autofill/model/credit_card/credit_card_data.h b/ios/chrome/browser/autofill/model/credit_card/credit_card_data.h index 571d0ba..090af528 100644 --- a/ios/chrome/browser/autofill/model/credit_card/credit_card_data.h +++ b/ios/chrome/browser/autofill/model/credit_card/credit_card_data.h
@@ -14,16 +14,16 @@ // The credit card's name and last four digits of the card or the credit card's // nickname if it has one. -@property(readonly, strong) NSString* cardNameAndLastFourDigits; +@property(readonly, copy) NSString* cardNameAndLastFourDigits; // The credit card's expiration date or type. -@property(readonly, strong) NSString* cardDetails; +@property(readonly, copy) NSString* cardDetails; // The credit card's backend identifier. -@property(readonly, strong) NSString* backendIdentifier; +@property(readonly, copy) NSString* backendIdentifier; // The accessible card name description. -@property(readonly, strong) NSString* accessibleCardName; +@property(readonly, copy) NSString* accessibleCardName; // The icon associated with this credit card. @property(readonly, strong) UIImage* icon;
diff --git a/ios/chrome/browser/autofill/ui_bundled/address_editor/cells/autofill_edit_profile_button_footer_item.h b/ios/chrome/browser/autofill/ui_bundled/address_editor/cells/autofill_edit_profile_button_footer_item.h index f0821f8..c8e3ab3e 100644 --- a/ios/chrome/browser/autofill/ui_bundled/address_editor/cells/autofill_edit_profile_button_footer_item.h +++ b/ios/chrome/browser/autofill/ui_bundled/address_editor/cells/autofill_edit_profile_button_footer_item.h
@@ -19,7 +19,7 @@ @interface AutofillEditProfileButtonFooterItem : TableViewHeaderFooterItem // Text for cell button. -@property(nonatomic, strong) NSString* buttonText; +@property(nonatomic, copy) NSString* buttonText; // Enabled state of the button. @property(nonatomic, assign) BOOL enabled;
diff --git a/ios/chrome/browser/autofill/ui_bundled/cells/target_account_item.h b/ios/chrome/browser/autofill/ui_bundled/cells/target_account_item.h index a9b86ee3..bbbeb00f 100644 --- a/ios/chrome/browser/autofill/ui_bundled/cells/target_account_item.h +++ b/ios/chrome/browser/autofill/ui_bundled/cells/target_account_item.h
@@ -16,7 +16,7 @@ // Avatar displayed by the item's cell. @property(nonatomic, strong) UIImage* avatar; // Email displayed by the item's cell. -@property(nonatomic, strong) NSString* email; +@property(nonatomic, copy) NSString* email; @end
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_address_cell.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_address_cell.mm index a7f47d90..1d5b417 100644 --- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_address_cell.mm +++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_address_cell.mm
@@ -32,7 +32,7 @@ // The cell's accessibility label. Indicates the index at which the address // represented by this item is positioned in the list of addresses to show. -@property(nonatomic, strong) NSString* cellIndexAccessibilityLabel; +@property(nonatomic, copy) NSString* cellIndexAccessibilityLabel; @end @@ -56,7 +56,7 @@ _address = address; _menuActions = menuActions; _cellIndex = cellIndex; - _cellIndexAccessibilityLabel = cellIndexAccessibilityLabel; + _cellIndexAccessibilityLabel = [cellIndexAccessibilityLabel copy]; _showAutofillFormButton = showAutofillFormButton; self.cellClass = [ManualFillAddressCell class]; }
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_cell+Testing.h b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_cell+Testing.h index 4a43d179..73c18fa8 100644 --- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_cell+Testing.h +++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_cell+Testing.h
@@ -17,7 +17,7 @@ // The part of the cell's accessibility label that is used to indicate the // 1-based index at which the payment method represented by this item is // positioned in the list of payment methods to show. -@property(nonatomic, strong, readonly) NSString* cellIndexAccessibilityLabel; +@property(nonatomic, copy, readonly) NSString* cellIndexAccessibilityLabel; @end
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_cell.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_cell.mm index aeea0993..c6c6122 100644 --- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_cell.mm +++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_cell.mm
@@ -62,7 +62,7 @@ // The part of the cell's accessibility label that is used to indicate the // 1-based index at which the payment method represented by this item is // positioned in the list of payment methods to show. -@property(nonatomic, strong) NSString* cellIndexAccessibilityLabel; +@property(nonatomic, copy) NSString* cellIndexAccessibilityLabel; @end @@ -86,7 +86,7 @@ _card = card; _menuActions = menuActions; _cellIndex = cellIndex; - _cellIndexAccessibilityLabel = cellIndexAccessibilityLabel; + _cellIndexAccessibilityLabel = [cellIndexAccessibilityLabel copy]; _showAutofillFormButton = showAutofillFormButton; self.cellClass = [ManualFillCardCell class]; }
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_password_cell.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_password_cell.mm index bc70cd7..51b29be 100644 --- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_password_cell.mm +++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_password_cell.mm
@@ -62,7 +62,7 @@ // The part of the cell's accessibility label that is used to indicate the index // at which the password represented by this item is positioned in the list of // passwords to show. -@property(nonatomic, strong) NSString* cellIndexAccessibilityLabel; +@property(nonatomic, copy) NSString* cellIndexAccessibilityLabel; @end @@ -97,7 +97,7 @@ _contentInjector = contentInjector; _menuActions = menuActions; _cellIndex = cellIndex; - _cellIndexAccessibilityLabel = cellIndexAccessibilityLabel; + _cellIndexAccessibilityLabel = [cellIndexAccessibilityLabel copy]; _showAutofillFormButton = showAutofillFormButton; _fromAllPasswordsContext = fromAllPasswordsContext; self.cellClass = [ManualFillPasswordCell class];
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_cell.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_cell.mm index 4022ac1..5a55016 100644 --- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_cell.mm +++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_plus_address_cell.mm
@@ -50,7 +50,7 @@ // The part of the cell's accessibility label that is used to indicate the index // at which the plus address represented by this item is positioned in the list // of plus addresses to show. -@property(nonatomic, strong) NSString* cellIndexAccessibilityLabel; +@property(nonatomic, copy) NSString* cellIndexAccessibilityLabel; @end @@ -71,7 +71,7 @@ _manualFillPlusAddress = plusAddress; _contentInjector = contentInjector; _menuActions = menuActions; - _cellIndexAccessibilityLabel = cellIndexAccessibilityLabel; + _cellIndexAccessibilityLabel = [cellIndexAccessibilityLabel copy]; _isAddressManualFallbackUI = isAddressManualFallbackUI; self.cellClass = [ManualFillPlusAddressCell class]; }
diff --git a/ios/chrome/browser/collaboration/OWNERS b/ios/chrome/browser/collaboration/OWNERS new file mode 100644 index 0000000..2f2abb0 --- /dev/null +++ b/ios/chrome/browser/collaboration/OWNERS
@@ -0,0 +1,3 @@ +gambard@chromium.org +ewannpv@chromium.org +lpromero@google.com
diff --git a/ios/chrome/browser/collaboration/model/messaging/infobar/collaboration_group_infobar_delegate.mm b/ios/chrome/browser/collaboration/model/messaging/infobar/collaboration_group_infobar_delegate.mm index 38ff209..a47fcb1 100644 --- a/ios/chrome/browser/collaboration/model/messaging/infobar/collaboration_group_infobar_delegate.mm +++ b/ios/chrome/browser/collaboration/model/messaging/infobar/collaboration_group_infobar_delegate.mm
@@ -162,11 +162,7 @@ switch (instant_message_.collaboration_event) { case CollaborationEvent::TAB_UPDATED: case CollaborationEvent::TAB_REMOVED: - return l10n_util::GetStringUTF16( - IDS_IOS_COLLABORATION_GROUP_TAB_REOPEN_PRIMARY_TOOLBAR_BUTTON); case CollaborationEvent::COLLABORATION_MEMBER_ADDED: - return l10n_util::GetStringUTF16( - IDS_IOS_COLLABORATION_GROUP_MEMBER_ADDED_PRIMARY_TOOLBAR_BUTTON); case CollaborationEvent::UNDEFINED: case CollaborationEvent::TAB_ADDED: case CollaborationEvent::TAB_GROUP_REMOVED:
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/ntp_home_egtest.mm b/ios/chrome/browser/content_suggestions/ui_bundled/ntp_home_egtest.mm index d735b72..ab2a67d 100644 --- a/ios/chrome/browser/content_suggestions/ui_bundled/ntp_home_egtest.mm +++ b/ios/chrome/browser/content_suggestions/ui_bundled/ntp_home_egtest.mm
@@ -137,7 +137,7 @@ // the elements after interacting with the device. @interface NTPHomeTestCase : ChromeTestCase -@property(nonatomic, strong) NSString* defaultSearchEngine; +@property(nonatomic, copy) NSString* defaultSearchEngine; @end
diff --git a/ios/chrome/browser/context_menu/ui_bundled/context_menu_egtest.mm b/ios/chrome/browser/context_menu/ui_bundled/context_menu_egtest.mm index b93a71b..dfd642bd 100644 --- a/ios/chrome/browser/context_menu/ui_bundled/context_menu_egtest.mm +++ b/ios/chrome/browser/context_menu/ui_bundled/context_menu_egtest.mm
@@ -92,13 +92,22 @@ // The text of the long link to the destination page. const char kInitialPageDestinationLongLinkText[] = "LongLink"; -// ElementSelector for long press action. -ElementSelector* kLogoPageChromiumImageIdSelector = - [ElementSelector selectorWithElementID:kLogoPageChromiumImageId]; -ElementSelector* kInitialPageDestinationLinkIdSelector = - [ElementSelector selectorWithElementID:kInitialPageDestinationLinkId]; -ElementSelector* kInitialPageDestinationLongLinkIDSelector = - [ElementSelector selectorWithElementID:kInitialPageDestinationLongLinkID]; +// Returns an ElementSelector for the chromium image on the logo page. +ElementSelector* LogoPageChromiumImageIdSelector() { + return [ElementSelector selectorWithElementID:kLogoPageChromiumImageId]; +} + +// Returns an ElementSelector for the link to the destination page on the +// initial page. +ElementSelector* InitialPageDestinationLinkIdSelector() { + return [ElementSelector selectorWithElementID:kInitialPageDestinationLinkId]; +} + +// Returns an ElementSelector for the long link to the destination page. +ElementSelector* InitialPageDestinationLongLinkIDSelector() { + return + [ElementSelector selectorWithElementID:kInitialPageDestinationLongLinkID]; +} // URL to a page with a link with a javascript: scheme. const char kJavaScriptPageUrl[] = "/scenarionContextMenuJavaScript"; @@ -338,7 +347,8 @@ [ChromeEarlGrey loadURL:pageURL]; [ChromeEarlGrey waitForWebStateContainingText:kLogoPageText]; - [ChromeEarlGreyUI longPressElementOnWebView:kLogoPageChromiumImageIdSelector]; + [ChromeEarlGreyUI + longPressElementOnWebView:LogoPageChromiumImageIdSelector()]; TapOnContextMenuButton(OpenImageButton()); [ChromeEarlGrey waitForPageToFinishLoading]; @@ -356,7 +366,8 @@ [ChromeEarlGrey loadURL:pageURL]; [ChromeEarlGrey waitForWebStateContainingText:kLogoPageText]; - [ChromeEarlGreyUI longPressElementOnWebView:kLogoPageChromiumImageIdSelector]; + [ChromeEarlGreyUI + longPressElementOnWebView:LogoPageChromiumImageIdSelector()]; TapOnContextMenuButton(OpenImageInNewTabButton()); @@ -383,7 +394,7 @@ [ChromeEarlGrey waitForWebStateZoomScale:1.0]; [ChromeEarlGreyUI - longPressElementOnWebView:kInitialPageDestinationLinkIdSelector]; + longPressElementOnWebView:InitialPageDestinationLinkIdSelector()]; TapOnContextMenuButton(OpenLinkInNewTabButton()); @@ -436,14 +447,15 @@ [ChromeEarlGrey waitForPageToFinishLoading]; [ChromeEarlGrey waitForWebStateZoomScale:1.0]; - [ChromeEarlGreyUI longPressElementOnWebView:kLogoPageChromiumImageIdSelector]; + [ChromeEarlGreyUI + longPressElementOnWebView:LogoPageChromiumImageIdSelector()]; [[EarlGrey selectElementWithMatcher:grey_text(kShortImgTitle)] assertWithMatcher:grey_notNil()]; ClearContextMenu(); [ChromeEarlGreyUI - longPressElementOnWebView:kInitialPageDestinationLinkIdSelector]; + longPressElementOnWebView:InitialPageDestinationLinkIdSelector()]; // Links get prefixed with the hostname, so check for partial text match [[EarlGrey selectElementWithMatcher:chrome_test_util::ContainsPartialText( @@ -456,14 +468,15 @@ [ChromeEarlGrey waitForPageToFinishLoading]; [ChromeEarlGrey waitForWebStateZoomScale:1.0]; - [ChromeEarlGreyUI longPressElementOnWebView:kLogoPageChromiumImageIdSelector]; + [ChromeEarlGreyUI + longPressElementOnWebView:LogoPageChromiumImageIdSelector()]; [[EarlGrey selectElementWithMatcher:grey_text(kLongImgTitle)] assertWithMatcher:grey_notNil()]; ClearContextMenu(); [ChromeEarlGreyUI - longPressElementOnWebView:kInitialPageDestinationLinkIdSelector]; + longPressElementOnWebView:InitialPageDestinationLinkIdSelector()]; // But expect that some of the link is visible in the title. NSString* startOfTitle = [kLongLinkHref substringToIndex:30]; @@ -478,7 +491,8 @@ [ChromeEarlGrey loadURL:pageURL]; [ChromeEarlGrey waitForWebStateContainingText:kLogoPageText]; - [ChromeEarlGreyUI longPressElementOnWebView:kLogoPageChromiumImageIdSelector]; + [ChromeEarlGreyUI + longPressElementOnWebView:LogoPageChromiumImageIdSelector()]; TapOnContextMenuButton(OpenImageButton()); [ChromeEarlGrey waitForPageToFinishLoading]; @@ -534,7 +548,7 @@ // Display the context menu twice. for (NSInteger i = 0; i < 2; i++) { [ChromeEarlGreyUI - longPressElementOnWebView:kInitialPageDestinationLinkIdSelector]; + longPressElementOnWebView:InitialPageDestinationLinkIdSelector()]; // Make sure the context menu appeared. [[EarlGrey selectElementWithMatcher:OpenLinkInNewTabButton()] @@ -564,7 +578,7 @@ // Display the context menu one last time. [ChromeEarlGreyUI - longPressElementOnWebView:kInitialPageDestinationLinkIdSelector]; + longPressElementOnWebView:InitialPageDestinationLinkIdSelector()]; // Make sure the context menu appeared. [[EarlGrey selectElementWithMatcher:OpenLinkInNewTabButton()] @@ -580,7 +594,7 @@ [ChromeEarlGrey waitForWebStateZoomScale:1.0]; [ChromeEarlGreyUI - longPressElementOnWebView:kInitialPageDestinationLinkIdSelector]; + longPressElementOnWebView:InitialPageDestinationLinkIdSelector()]; // Check the different buttons. [[EarlGrey @@ -633,7 +647,7 @@ // Display the context menu. [ChromeEarlGreyUI - longPressElementOnWebView:kInitialPageDestinationLinkIdSelector]; + longPressElementOnWebView:InitialPageDestinationLinkIdSelector()]; // Open link in new window. [[EarlGrey @@ -666,7 +680,7 @@ // Display the context menu. [ChromeEarlGreyUI - longPressElementOnWebView:kInitialPageDestinationLinkIdSelector]; + longPressElementOnWebView:InitialPageDestinationLinkIdSelector()]; // Open link in new window. [[EarlGrey @@ -690,7 +704,7 @@ [ChromeEarlGrey loadURL:initialURL]; [ChromeEarlGreyUI - longPressElementOnWebView:kInitialPageDestinationLinkIdSelector]; + longPressElementOnWebView:InitialPageDestinationLinkIdSelector()]; // Check the different buttons. [[EarlGrey selectElementWithMatcher:ContextMenuItemWithAccessibilityLabelId( @@ -709,7 +723,7 @@ [ChromeEarlGrey loadURL:initialURL]; [ChromeEarlGreyUI - longPressElementOnWebView:kInitialPageDestinationLinkIdSelector]; + longPressElementOnWebView:InitialPageDestinationLinkIdSelector()]; // Check the different buttons. [[EarlGrey selectElementWithMatcher:ContextMenuItemWithAccessibilityLabelId( @@ -729,7 +743,7 @@ [ChromeEarlGrey waitForWebStateZoomScale:1.0]; [ChromeEarlGreyUI - longPressElementOnWebView:kInitialPageDestinationLinkIdSelector]; + longPressElementOnWebView:InitialPageDestinationLinkIdSelector()]; TapOnContextMenuButton(OpenLinkInNewTabButton()); @@ -773,7 +787,7 @@ [ChromeEarlGrey waitForWebStateZoomScale:1.0]; [ChromeEarlGreyUI - longPressElementOnWebView:kInitialPageDestinationLinkIdSelector]; + longPressElementOnWebView:InitialPageDestinationLinkIdSelector()]; TapOnContextMenuButton(OpenLinkInNewGroupButton()); @@ -797,7 +811,7 @@ waitForWebStateContainingText:kInitialPageDestinationLinkText]; [ChromeEarlGrey waitForWebStateZoomScale:1.0]; [ChromeEarlGreyUI - longPressElementOnWebView:kInitialPageDestinationLinkIdSelector]; + longPressElementOnWebView:InitialPageDestinationLinkIdSelector()]; TapOnContextMenuButton(OpenLinkInGroupButton()); TapOnContextMenuButton(OpenLinkInOneTabGroupButton()); @@ -827,7 +841,7 @@ [ChromeEarlGrey waitForWebStateZoomScale:1.0]; [ChromeEarlGreyUI - longPressElementOnWebView:kInitialPageDestinationLinkIdSelector]; + longPressElementOnWebView:InitialPageDestinationLinkIdSelector()]; [ChromeEarlGrey verifyShareActionWithURL:pageURL pageTitle:pageTitle]; @@ -856,7 +870,7 @@ [ChromeEarlGrey waitForWebStateZoomScale:1.0]; [ChromeEarlGreyUI - longPressElementOnWebView:kInitialPageDestinationLongLinkIDSelector]; + longPressElementOnWebView:InitialPageDestinationLongLinkIDSelector()]; std::u16string formattedURL = url_formatter::FormatUrl(longURL); NSString* stringURL = base::SysUTF16ToNSString(formattedURL); @@ -887,7 +901,8 @@ [ChromeEarlGrey waitForPageToFinishLoading]; [ChromeEarlGrey waitForWebStateZoomScale:1.0]; - [ChromeEarlGreyUI longPressElementOnWebView:kLogoPageChromiumImageIdSelector]; + [ChromeEarlGreyUI + longPressElementOnWebView:LogoPageChromiumImageIdSelector()]; [ChromeEarlGrey waitForForegroundWindowCount:1]; [[EarlGrey selectElementWithMatcher:grey_text(kShortImgTitle)]
diff --git a/ios/chrome/browser/dom_distiller/model/distiller_viewer.cc b/ios/chrome/browser/dom_distiller/model/distiller_viewer.cc index 3d63125..31ca2b3 100644 --- a/ios/chrome/browser/dom_distiller/model/distiller_viewer.cc +++ b/ios/chrome/browser/dom_distiller/model/distiller_viewer.cc
@@ -28,6 +28,7 @@ : DistillerViewerInterface(prefs), url_(url), csp_nonce_(base::Base64Encode(base::RandBytesAsVector(16))), + use_offline_data_(page->ShouldFetchOfflineData()), callback_(std::move(callback)) { DCHECK(url.is_valid()); SendCommonJavaScript(); @@ -66,7 +67,7 @@ const std::string html = dom_distiller::viewer::GetArticleTemplateHtml( distilled_page_prefs_->GetTheme(), - distilled_page_prefs_->GetFontFamily(), csp_nonce_); + distilled_page_prefs_->GetFontFamily(), csp_nonce_, use_offline_data_); std::string html_and_script(html); html_and_script += "<script nonce=\"" + csp_nonce_ + "\">" +
diff --git a/ios/chrome/browser/dom_distiller/model/distiller_viewer.h b/ios/chrome/browser/dom_distiller/model/distiller_viewer.h index 2fd67f9..b37bd9d2 100644 --- a/ios/chrome/browser/dom_distiller/model/distiller_viewer.h +++ b/ios/chrome/browser/dom_distiller/model/distiller_viewer.h
@@ -96,6 +96,8 @@ std::string js_buffer_; // CSP nonce value. std::string csp_nonce_; + // Whether offline data should be injected in the viewer. + bool use_offline_data_; // Callback to run once distillation is complete. DistillationFinishedCallback callback_;
diff --git a/ios/chrome/browser/explain_with_gemini/coordinator/explain_with_gemini_mediator_egtest.mm b/ios/chrome/browser/explain_with_gemini/coordinator/explain_with_gemini_mediator_egtest.mm index bb5ab7c..51f122a 100644 --- a/ios/chrome/browser/explain_with_gemini/coordinator/explain_with_gemini_mediator_egtest.mm +++ b/ios/chrome/browser/explain_with_gemini/coordinator/explain_with_gemini_mediator_egtest.mm
@@ -41,8 +41,11 @@ namespace { const char kElementToLongPress[] = "selectid"; -ElementSelector* kElementToLongPressSelector = - [ElementSelector selectorWithElementID:kElementToLongPress]; + +// Returns an ElementSelector for `ElementToLongPress`. +ElementSelector* ElementToLongPressSelector() { + return [ElementSelector selectorWithElementID:kElementToLongPress]; +} // An HTML template that puts some text in a simple span element. const char kBasicSelectionUrl[] = "/basic"; @@ -144,7 +147,7 @@ // `kCanUseModelExecutionFeaturesName` capability. - (void)testExplainWithGemini { [self loadPage]; - [ChromeEarlGreyUI triggerEditMenu:kElementToLongPressSelector]; + [ChromeEarlGreyUI triggerEditMenu:ElementToLongPressSelector()]; bool found = FindEditMenuAction([NSString stringWithFormat:@"✦ %@", l10n_util::GetNSString( IDS_IOS_EXPLAIN_GEMINI_EDIT_MENU)]); @@ -184,7 +187,7 @@ - (void)testExplainWithGeminiIncognito { [ChromeEarlGrey openNewIncognitoTab]; [self loadPage]; - [ChromeEarlGreyUI triggerEditMenu:kElementToLongPressSelector]; + [ChromeEarlGreyUI triggerEditMenu:ElementToLongPressSelector()]; bool found = FindEditMenuAction([NSString stringWithFormat:@"✦ %@", l10n_util::GetNSString( IDS_IOS_EXPLAIN_GEMINI_EDIT_MENU)]); @@ -197,7 +200,7 @@ [SigninEarlGrey signOut]; [SigninEarlGrey verifySignedOut]; [self loadPage]; - [ChromeEarlGreyUI triggerEditMenu:kElementToLongPressSelector]; + [ChromeEarlGreyUI triggerEditMenu:ElementToLongPressSelector()]; bool found = FindEditMenuAction([NSString stringWithFormat:@"✦ %@", l10n_util::GetNSString( IDS_IOS_EXPLAIN_GEMINI_EDIT_MENU)]); @@ -216,7 +219,7 @@ [SigninEarlGrey signinWithFakeIdentity:fakeIdentity]; [self loadPage]; - [ChromeEarlGreyUI triggerEditMenu:kElementToLongPressSelector]; + [ChromeEarlGreyUI triggerEditMenu:ElementToLongPressSelector()]; bool found = FindEditMenuAction([NSString stringWithFormat:@"✦ %@", l10n_util::GetNSString( IDS_IOS_EXPLAIN_GEMINI_EDIT_MENU)]); @@ -234,7 +237,7 @@ [SigninEarlGrey signinWithFakeManagedIdentityInPersonalProfile:fakeManagedIdentity]; [self loadPage]; - [ChromeEarlGreyUI triggerEditMenu:kElementToLongPressSelector]; + [ChromeEarlGreyUI triggerEditMenu:ElementToLongPressSelector()]; bool found = FindEditMenuAction([NSString stringWithFormat:@"✦ %@", l10n_util::GetNSString( IDS_IOS_EXPLAIN_GEMINI_EDIT_MENU)]);
diff --git a/ios/chrome/browser/first_run/ui_bundled/features.h b/ios/chrome/browser/first_run/ui_bundled/features.h index c09866a..3b92e90fa 100644 --- a/ios/chrome/browser/first_run/ui_bundled/features.h +++ b/ios/chrome/browser/first_run/ui_bundled/features.h
@@ -121,6 +121,10 @@ // `kDisabled` if the feature is disabled. WelcomeBackScreenVariationType GetWelcomeBackScreenVariationType(); +// Whether `kWelcomeBackInFirstRun` is enabled. This experiment is disabled when +// `kBestFeaturesScreenInFirstRun` is enabled. +bool IsWelcomeBackInFirstRunEnabled(); + // Whether the Default Browser Experiment in the FRE is enabled. This feature is // disabled when kUpdatedFirstRunSequence is enabled. bool IsAnimatedDefaultBrowserPromoInFREEnabled();
diff --git a/ios/chrome/browser/first_run/ui_bundled/features.mm b/ios/chrome/browser/first_run/ui_bundled/features.mm index 99fcb92..cd8dfe0 100644 --- a/ios/chrome/browser/first_run/ui_bundled/features.mm +++ b/ios/chrome/browser/first_run/ui_bundled/features.mm
@@ -73,6 +73,11 @@ kWelcomeBackInFirstRunParam, 1)); } +bool IsWelcomeBackInFirstRunEnabled() { + return base::FeatureList::IsEnabled(kWelcomeBackInFirstRun) && + !base::FeatureList::IsEnabled(kBestFeaturesScreenInFirstRun); +} + bool IsAnimatedDefaultBrowserPromoInFREEnabled() { return base::FeatureList::IsEnabled(kAnimatedDefaultBrowserPromoInFRE) && !base::FeatureList::IsEnabled(first_run::kUpdatedFirstRunSequence);
diff --git a/ios/chrome/browser/first_run/ui_bundled/welcome_back/DEPS b/ios/chrome/browser/first_run/ui_bundled/welcome_back/DEPS new file mode 100644 index 0000000..a33d45a --- /dev/null +++ b/ios/chrome/browser/first_run/ui_bundled/welcome_back/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+ios/chrome/browser/promos_manager/ui_bundled/standard_promo_display_handler.h", +]
diff --git a/ios/chrome/browser/first_run/ui_bundled/welcome_back/ui/BUILD.gn b/ios/chrome/browser/first_run/ui_bundled/welcome_back/ui/BUILD.gn new file mode 100644 index 0000000..db5bbc0c --- /dev/null +++ b/ios/chrome/browser/first_run/ui_bundled/welcome_back/ui/BUILD.gn
@@ -0,0 +1,16 @@ +# Copyright 2025 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("ui") { + sources = [ + "welcome_back_display_handler.h", + "welcome_back_display_handler.mm", + ] + public_deps = [ "//ios/chrome/browser/promos_manager/ui_bundled:promos" ] + deps = [ + "//components/feature_engagement/public", + "//ios/chrome/browser/promos_manager/model:types", + ] + frameworks = [ "Foundation.framework" ] +}
diff --git a/ios/chrome/browser/first_run/ui_bundled/welcome_back/ui/welcome_back_display_handler.h b/ios/chrome/browser/first_run/ui_bundled/welcome_back/ui/welcome_back_display_handler.h new file mode 100644 index 0000000..a1f223e --- /dev/null +++ b/ios/chrome/browser/first_run/ui_bundled/welcome_back/ui/welcome_back_display_handler.h
@@ -0,0 +1,26 @@ +// Copyright 2025 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_FIRST_RUN_UI_BUNDLED_WELCOME_BACK_UI_WELCOME_BACK_DISPLAY_HANDLER_H_ +#define IOS_CHROME_BROWSER_FIRST_RUN_UI_BUNDLED_WELCOME_BACK_UI_WELCOME_BACK_DISPLAY_HANDLER_H_ + +#import <Foundation/Foundation.h> + +#import "ios/chrome/browser/promos_manager/ui_bundled/standard_promo_display_handler.h" + +// Handler for displaying the Welcome Back promo. +// +// This handler is called by the Promos Manager and presents the Welcome Back +// promo to eligible users. Users are considered eligible if they return +// to the app after being away for >28 days. +@interface WelcomeBackDisplayHandler : NSObject <StandardPromoDisplayHandler> + +#pragma mark - PromoProtocol + +// `PromosManagerCommands` handler. +@property(nonatomic, weak) id<PromosManagerCommands> handler; + +@end + +#endif // IOS_CHROME_BROWSER_FIRST_RUN_UI_BUNDLED_WELCOME_BACK_UI_WELCOME_BACK_DISPLAY_HANDLER_H_
diff --git a/ios/chrome/browser/first_run/ui_bundled/welcome_back/ui/welcome_back_display_handler.mm b/ios/chrome/browser/first_run/ui_bundled/welcome_back/ui/welcome_back_display_handler.mm new file mode 100644 index 0000000..a3909e2 --- /dev/null +++ b/ios/chrome/browser/first_run/ui_bundled/welcome_back/ui/welcome_back_display_handler.mm
@@ -0,0 +1,25 @@ +// Copyright 2025 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/first_run/ui_bundled/welcome_back/ui/welcome_back_display_handler.h" + +#import "components/feature_engagement/public/feature_constants.h" +#import "ios/chrome/browser/promos_manager/model/promo_config.h" + +@implementation WelcomeBackDisplayHandler + +#pragma mark - StandardPromoDisplayHandler + +- (void)handleDisplay { + // TODO(crbug.com/407963758): Implement the Welcome Back half sheet view. +} + +#pragma mark - PromoProtocol + +- (PromoConfig)config { + return PromoConfig(promos_manager::Promo::WelcomeBack, + &feature_engagement::kIPHiOSWelcomeBackFeature); +} + +@end
diff --git a/ios/chrome/browser/flags/BUILD.gn b/ios/chrome/browser/flags/BUILD.gn index 1b177af8..1809ca36 100644 --- a/ios/chrome/browser/flags/BUILD.gn +++ b/ios/chrome/browser/flags/BUILD.gn
@@ -88,6 +88,7 @@ "//ios/chrome/browser/ntp/ui_bundled:feature_flags", "//ios/chrome/browser/omnibox/public:features", "//ios/chrome/browser/page_info/ui_bundled:features", + "//ios/chrome/browser/passwords/model:features", "//ios/chrome/browser/policy/model", "//ios/chrome/browser/policy/model:constants", "//ios/chrome/browser/policy/model:policy_util",
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm index 04e54277..a126cdb 100644 --- a/ios/chrome/browser/flags/about_flags.mm +++ b/ios/chrome/browser/flags/about_flags.mm
@@ -98,6 +98,7 @@ #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h" #import "ios/chrome/browser/omnibox/public/omnibox_ui_features.h" #import "ios/chrome/browser/page_info/ui_bundled/features.h" +#import "ios/chrome/browser/passwords/model/features.h" #import "ios/chrome/browser/policy/model/policy_util.h" #import "ios/chrome/browser/popup_menu/ui_bundled/overflow_menu/feature_flags.h" #import "ios/chrome/browser/price_insights/model/price_insights_feature.h" @@ -825,15 +826,10 @@ std::size(kContextualPanelSmallIPHWithBlueHighlightArm), nullptr}, }; -const FeatureEntry::FeatureParam kIdentityDiscAccountMenuNoEllipsis[] = { - {kDisableAccountMenuEllipsisParam, "true"}, -}; const FeatureEntry::FeatureParam kIdentityDiscAccountMenuWithSettings[] = { {kShowSettingsInAccountMenuParam, "true"}, }; const FeatureEntry::FeatureVariation kIdentityDiscAccountMenuVariations[] = { - {" - without account menu ellipsis", kIdentityDiscAccountMenuNoEllipsis, - std::size(kIdentityDiscAccountMenuNoEllipsis), nullptr}, {" - with settings button", kIdentityDiscAccountMenuWithSettings, std::size(kIdentityDiscAccountMenuWithSettings), nullptr}, }; @@ -2747,6 +2743,10 @@ flag_descriptions::kUseFeedEligibilityServiceName, flag_descriptions::kUseFeedEligibilityServiceDescription, flags_ui::kOsIos, FEATURE_VALUE_TYPE(kUseFeedEligibilityService)}, + {"import-passwords-from-safari", + flag_descriptions::kImportPasswordsFromSafariName, + flag_descriptions::kImportPasswordsFromSafariDescription, flags_ui::kOsIos, + FEATURE_VALUE_TYPE(kImportPasswordsFromSafari)}, };
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc index f4ecb8c..9352386 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -565,6 +565,10 @@ const char kIdentityConfirmationSnackbarDescription[] = "When enabled, the identity confirmation snackbar will show on startup."; +const char kImportPasswordsFromSafariName[] = "Import Passwords From Safari"; +const char kImportPasswordsFromSafariDescription[] = + "When enabled, allows users to import passwords from Safari."; + const char kIndicateIdentityErrorInOverflowMenuName[] = "Indicate Identity Error in Overflow Menu"; const char kIndicateIdentityErrorInOverflowMenuDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h index 6c1cc30..66b1090 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -327,6 +327,9 @@ extern const char kIdentityConfirmationSnackbarName[]; extern const char kIdentityConfirmationSnackbarDescription[]; +extern const char kImportPasswordsFromSafariName[]; +extern const char kImportPasswordsFromSafariDescription[]; + extern const char kIndicateIdentityErrorInOverflowMenuName[]; extern const char kIndicateIdentityErrorInOverflowMenuDescription[];
diff --git a/ios/chrome/browser/home_customization/model/background_customization_configuration.h b/ios/chrome/browser/home_customization/model/background_customization_configuration.h index 57cb6264..7d6c203b 100644 --- a/ios/chrome/browser/home_customization/model/background_customization_configuration.h +++ b/ios/chrome/browser/home_customization/model/background_customization_configuration.h
@@ -16,7 +16,7 @@ @interface BackgroundCustomizationConfiguration : NSObject // A unique identifier for the background configuration. -@property(nonatomic, strong) NSString* configurationID; +@property(nonatomic, copy) NSString* configurationID; // A pointer to a GURL that points to the low-resolution version (thumbnail) // of the background image.
diff --git a/ios/chrome/browser/infobars/ui_bundled/modals/infobar_save_card_table_view_controller.mm b/ios/chrome/browser/infobars/ui_bundled/modals/infobar_save_card_table_view_controller.mm index 8c643bc..b2260a4 100644 --- a/ios/chrome/browser/infobars/ui_bundled/modals/infobar_save_card_table_view_controller.mm +++ b/ios/chrome/browser/infobars/ui_bundled/modals/infobar_save_card_table_view_controller.mm
@@ -68,7 +68,7 @@ // Card Issuer icon image to be displayed. @property(nonatomic, strong) UIImage* cardIssuerIcon; // Card Network for accessibility label. -@property(nonatomic, strong) NSString* cardNetwork; +@property(nonatomic, copy) NSString* cardNetwork; // Card Number to be displayed. @property(nonatomic, copy) NSString* cardNumber; // Card Expiration Month to be displayed
diff --git a/ios/chrome/browser/location_bar/ui_bundled/test/fake_location_bar_steady_view_consumer.h b/ios/chrome/browser/location_bar/ui_bundled/test/fake_location_bar_steady_view_consumer.h index ee8bde0..6a76ce2 100644 --- a/ios/chrome/browser/location_bar/ui_bundled/test/fake_location_bar_steady_view_consumer.h +++ b/ios/chrome/browser/location_bar/ui_bundled/test/fake_location_bar_steady_view_consumer.h
@@ -11,10 +11,10 @@ @interface FakeLocationBarSteadyViewConsumer : NSObject <LocationBarSteadyViewConsumer> -@property(nonatomic, strong, readonly) NSString* locationText; +@property(nonatomic, copy, readonly) NSString* locationText; @property(nonatomic, assign, readonly) BOOL clipTail; @property(nonatomic, strong, readonly) UIImage* icon; -@property(nonatomic, strong, readonly) NSString* statusText; +@property(nonatomic, copy, readonly) NSString* statusText; @property(nonatomic, assign, readonly, getter=isLocationShareable) BOOL locationShareable; @end
diff --git a/ios/chrome/browser/location_bar/ui_bundled/test/fake_location_bar_steady_view_consumer.mm b/ios/chrome/browser/location_bar/ui_bundled/test/fake_location_bar_steady_view_consumer.mm index 6101de8..392a20f 100644 --- a/ios/chrome/browser/location_bar/ui_bundled/test/fake_location_bar_steady_view_consumer.mm +++ b/ios/chrome/browser/location_bar/ui_bundled/test/fake_location_bar_steady_view_consumer.mm
@@ -7,14 +7,14 @@ @implementation FakeLocationBarSteadyViewConsumer - (void)updateLocationText:(NSString*)string clipTail:(BOOL)clipTail { - _locationText = string; + _locationText = [string copy]; _clipTail = clipTail; } - (void)updateLocationIcon:(UIImage*)icon securityStatusText:(NSString*)statusText { _icon = icon; - _statusText = statusText; + _statusText = [statusText copy]; } - (void)updateLocationShareable:(BOOL)shareable {
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm index 355bfa2..2d59e0ad 100644 --- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm +++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm
@@ -265,7 +265,7 @@ // Indicates whether the fakebox was tapped as part of an omnibox focus event. BOOL _fakeboxTapped; // The account menu coordinator. - SigninCoordinator<StopAnimatedChromeCoordinator>* _accountMenuCoordinator; + SigninCoordinator* _accountMenuCoordinator; // Whether the signin menu is displayed on top of this NTP. BOOL _showSigninCommandInProgress; }
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h index d20ab8e0..d22ccd8 100644 --- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h +++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h
@@ -101,8 +101,6 @@ extern const char kDeprecateFeedHeaderParameterSpaceBetweenModules[]; extern const char kDeprecateFeedHeaderParameterHeaderBottomPadding[]; -// Parameter to remove the three-dot menu from the account menu. -extern const char kDisableAccountMenuEllipsisParam[]; // Parameter to show the settings button in the account menu. extern const char kShowSettingsInAccountMenuParam[]; @@ -140,9 +138,6 @@ const std::string& param_name, double default_value); -// YES if the account menu is enabled without the three-dot menu. -bool IdentityDiscAccountMenuEnabledWithoutEllipsis(); - // YES if the account menu is enabled with the settings button. bool IdentityDiscAccountMenuEnabledWithSettings();
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.mm index ab65443..28751dd 100644 --- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.mm +++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.mm
@@ -88,9 +88,7 @@ const char kFeedSettingDiscoverReferrerParameter[] = "DiscoverReferrerParameter"; -// Feature parameters for `kIdentityDiscAccountMenu`. -const char kDisableAccountMenuEllipsisParam[] = - "identity-disc-account-menu-without-ellipsis"; +// Feature parameter for `kIdentityDiscAccountMenu`. const char kShowSettingsInAccountMenuParam[] = "identity-disc-account-menu-with-settings-button"; @@ -160,14 +158,6 @@ param_name, default_value); } -bool IdentityDiscAccountMenuEnabledWithoutEllipsis() { - if (base::FeatureList::IsEnabled(kIdentityDiscAccountMenu)) { - return base::GetFieldTrialParamByFeatureAsBool( - kIdentityDiscAccountMenu, kDisableAccountMenuEllipsisParam, false); - } - return false; -} - bool IdentityDiscAccountMenuEnabledWithSettings() { if (base::FeatureList::IsEnabled(kIdentityDiscAccountMenu)) { return base::GetFieldTrialParamByFeatureAsBool(
diff --git a/ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.h b/ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.h index 90f5726..0a8a4ce1 100644 --- a/ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.h +++ b/ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.h
@@ -83,8 +83,8 @@ #pragma mark - OmniboxText events -/// Ends omnibox edit. Closes the omnibox popup. -- (void)endEditing; +/// Closes the omnibox popup. +- (void)closeOmniboxPopup; /// Updates the popup text alignment. - (void)setTextAlignment:(NSTextAlignment)alignment;
diff --git a/ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.mm b/ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.mm index 3376aa8..5a981c9 100644 --- a/ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.mm +++ b/ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.mm
@@ -227,7 +227,7 @@ #pragma mark - OmniboxText events -- (void)endEditing { +- (void)closeOmniboxPopup { if (_omniboxController) { _omniboxController->StopAutocomplete(/*clear_result=*/true); }
diff --git a/ios/chrome/browser/omnibox/model/omnibox_text_controller.h b/ios/chrome/browser/omnibox/model/omnibox_text_controller.h index 42abd462..d734058 100644 --- a/ios/chrome/browser/omnibox/model/omnibox_text_controller.h +++ b/ios/chrome/browser/omnibox/model/omnibox_text_controller.h
@@ -133,6 +133,8 @@ - (void)updatePopupLayoutDirection; +- (void)setCaretPos:(NSUInteger)caretPos; + @end #endif // IOS_CHROME_BROWSER_OMNIBOX_MODEL_OMNIBOX_TEXT_CONTROLLER_H_
diff --git a/ios/chrome/browser/omnibox/model/omnibox_text_controller.mm b/ios/chrome/browser/omnibox/model/omnibox_text_controller.mm index 81c770f..26a6cd3 100644 --- a/ios/chrome/browser/omnibox/model/omnibox_text_controller.mm +++ b/ios/chrome/browser/omnibox/model/omnibox_text_controller.mm
@@ -93,7 +93,7 @@ if (!_omniboxEditModel || !_omniboxEditModel->has_focus()) { return; } - [self.omniboxAutocompleteController endEditing]; + [self.omniboxAutocompleteController closeOmniboxPopup]; if (OmniboxClient* client = self.client) { RecordSuggestionsListScrolled( @@ -169,9 +169,7 @@ /*prevent_inline_autocomplete=*/true); } } else { - if (_omniboxViewIOS) { - _omniboxViewIOS->CloseOmniboxPopup(); - } + [self.omniboxAutocompleteController closeOmniboxPopup]; } } @@ -431,7 +429,7 @@ [textField becomeFirstResponder]; if (@available(iOS 17, *)) { // Set the caret pos to the end of the text (crbug.com/331622199). - _omniboxViewIOS->SetCaretPos(text.length()); + [self setCaretPos:text.length()]; } } @@ -457,6 +455,19 @@ setSemanticContentAttribute:[textField bestSemanticContentAttribute]]; } +/// Sets the caret position. Removes any selection. Clamps the requested caret +/// position to the length of the current text. +- (void)setCaretPos:(NSUInteger)caretPos { + OmniboxTextFieldIOS* textField = self.textField; + DCHECK(caretPos <= textField.text.length || caretPos == 0); + UITextPosition* start = textField.beginningOfDocument; + UITextPosition* newPosition = [textField positionFromPosition:start + offset:caretPos]; + textField.selectedTextRange = [textField textRangeFromPosition:newPosition + toPosition:newPosition]; +} + +/// Returns the omnibox client. - (OmniboxClient*)client { return _omniboxController ? _omniboxController->client() : nullptr; }
diff --git a/ios/chrome/browser/omnibox/ui_bundled/omnibox_view_ios.mm b/ios/chrome/browser/omnibox/ui_bundled/omnibox_view_ios.mm index 82dd9c38..0b5cd41 100644 --- a/ios/chrome/browser/omnibox/ui_bundled/omnibox_view_ios.mm +++ b/ios/chrome/browser/omnibox/ui_bundled/omnibox_view_ios.mm
@@ -92,12 +92,7 @@ } void OmniboxViewIOS::SetCaretPos(size_t caret_pos) { - DCHECK(caret_pos <= field_.text.length || caret_pos == 0); - UITextPosition* start = field_.beginningOfDocument; - UITextPosition* newPosition = [field_ positionFromPosition:start - offset:caret_pos]; - field_.selectedTextRange = [field_ textRangeFromPosition:newPosition - toPosition:newPosition]; + [omnibox_text_controller_ setCaretPos:caret_pos]; } void OmniboxViewIOS::RevertAll() {
diff --git a/ios/chrome/browser/passwords/model/features.h b/ios/chrome/browser/passwords/model/features.h index f0a5410..1669e67 100644 --- a/ios/chrome/browser/passwords/model/features.h +++ b/ios/chrome/browser/passwords/model/features.h
@@ -7,6 +7,9 @@ #import "base/feature_list.h" +// Feature flag to allow users to import passwords from Safari. +BASE_DECLARE_FEATURE(kImportPasswordsFromSafari); + // Enable crowdsourcing uploads for the Password Manager. Used as a kill switch, // enabled by default. BASE_DECLARE_FEATURE(kPasswordManagerEnableCrowdsourcingUploads);
diff --git a/ios/chrome/browser/passwords/model/features.mm b/ios/chrome/browser/passwords/model/features.mm index c570d49..332431c 100644 --- a/ios/chrome/browser/passwords/model/features.mm +++ b/ios/chrome/browser/passwords/model/features.mm
@@ -6,6 +6,10 @@ #import "base/feature_list.h" +BASE_FEATURE(kImportPasswordsFromSafari, + "ImportPasswordsFromSafari", + base::FEATURE_DISABLED_BY_DEFAULT); + BASE_FEATURE(kPasswordManagerEnableCrowdsourcingUploads, "PasswordManagerEnableCrowdsourcingUploads", base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/ios/chrome/browser/popup_menu/ui_bundled/cells/popup_menu_tools_item.h b/ios/chrome/browser/popup_menu/ui_bundled/cells/popup_menu_tools_item.h index ce2d1cd..0557850 100644 --- a/ios/chrome/browser/popup_menu/ui_bundled/cells/popup_menu_tools_item.h +++ b/ios/chrome/browser/popup_menu/ui_bundled/cells/popup_menu_tools_item.h
@@ -28,7 +28,7 @@ // specific styling is applied. @property(nonatomic, assign) BOOL destructiveAction; // Additional label. Read after `title` if not nil. -@property(nonatomic, strong) NSString* additionalAccessibilityLabel; +@property(nonatomic, copy) NSString* additionalAccessibilityLabel; @end @@ -46,7 +46,7 @@ @property(nonatomic, assign) BOOL destructiveAction; // Additional label. Read after `title` if not nil. -@property(nonatomic, strong) NSString* additionalAccessibilityLabel; +@property(nonatomic, copy) NSString* additionalAccessibilityLabel; // Sets the number on the badge number. - (void)setBadgeNumber:(NSInteger)badgeNumber;
diff --git a/ios/chrome/browser/promos_manager/ui_bundled/BUILD.gn b/ios/chrome/browser/promos_manager/ui_bundled/BUILD.gn index db01bda..a8dedc42 100644 --- a/ios/chrome/browser/promos_manager/ui_bundled/BUILD.gn +++ b/ios/chrome/browser/promos_manager/ui_bundled/BUILD.gn
@@ -99,6 +99,8 @@ "//ios/chrome/browser/default_promo/ui_bundled/post_restore:post_restore_default_browser", "//ios/chrome/browser/default_promo/ui_bundled/promo_handler", "//ios/chrome/browser/docking_promo/ui", + "//ios/chrome/browser/first_run/ui_bundled:features", + "//ios/chrome/browser/first_run/ui_bundled/welcome_back/ui", "//ios/chrome/browser/post_restore_signin/ui_bundled", "//ios/chrome/browser/promos_manager/model:constants", "//ios/chrome/browser/shared/model/application_context",
diff --git a/ios/chrome/browser/promos_manager/ui_bundled/DEPS b/ios/chrome/browser/promos_manager/ui_bundled/DEPS index ce61536..c438ca9 100644 --- a/ios/chrome/browser/promos_manager/ui_bundled/DEPS +++ b/ios/chrome/browser/promos_manager/ui_bundled/DEPS
@@ -10,6 +10,8 @@ "+ios/chrome/browser/sync/model/sync_service_factory.h", "+ios/chrome/browser/whats_new/coordinator", "+ios/chrome/browser/authentication/ui_bundled/signin/promo", + "+ios/chrome/browser/first_run/ui_bundled/welcome_back/ui/welcome_back_display_handler.h", + "+ios/chrome/browser/first_run/ui_bundled/features.h", ] specific_include_rules = {
diff --git a/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm b/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm index 529fa9a6..98bac6e1 100644 --- a/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm +++ b/ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.mm
@@ -34,6 +34,8 @@ #import "ios/chrome/browser/default_promo/ui_bundled/stay_safe_default_browser_promo_view_provider.h" #import "ios/chrome/browser/docking_promo/ui/docking_promo_display_handler.h" #import "ios/chrome/browser/feature_engagement/model/tracker_factory.h" +#import "ios/chrome/browser/first_run/ui_bundled/features.h" +#import "ios/chrome/browser/first_run/ui_bundled/welcome_back/ui/welcome_back_display_handler.h" #import "ios/chrome/browser/post_restore_signin/ui_bundled/post_restore_signin_provider.h" #import "ios/chrome/browser/promos_manager/model/features.h" #import "ios/chrome/browser/promos_manager/model/promo_config.h" @@ -607,6 +609,12 @@ // Sign-in fullscreen promo handler. _displayHandlerPromos[promos_manager::Promo::SigninFullscreen] = [[SigninFullscreenPromoDisplayHandler alloc] init]; + + // Welcome Back promo handler. + if (first_run::IsWelcomeBackInFirstRunEnabled()) { + _displayHandlerPromos[promos_manager::Promo::WelcomeBack] = + [[WelcomeBackDisplayHandler alloc] init]; + } } - (void)registerStandardPromoViewProviderPromos {
diff --git a/ios/chrome/browser/reading_list/model/reading_list_distiller_page.h b/ios/chrome/browser/reading_list/model/reading_list_distiller_page.h index 2d57ba5..f8c1bf1f 100644 --- a/ios/chrome/browser/reading_list/model/reading_list_distiller_page.h +++ b/ios/chrome/browser/reading_list/model/reading_list_distiller_page.h
@@ -62,6 +62,9 @@ ~ReadingListDistillerPage() override; + // dom_distiller::DistillerPage implementation. + bool ShouldFetchOfflineData() override; + protected: void DistillPageImpl(const GURL& url, const std::string& script) override; void OnDistillationDone(const GURL& page_url,
diff --git a/ios/chrome/browser/reading_list/model/reading_list_distiller_page.mm b/ios/chrome/browser/reading_list/model/reading_list_distiller_page.mm index 7f70896..d8a8053 100644 --- a/ios/chrome/browser/reading_list/model/reading_list_distiller_page.mm +++ b/ios/chrome/browser/reading_list/model/reading_list_distiller_page.mm
@@ -75,6 +75,10 @@ ReadingListDistillerPage::~ReadingListDistillerPage() {} +bool ReadingListDistillerPage::ShouldFetchOfflineData() { + return true; +} + void ReadingListDistillerPage::DistillPageImpl(const GURL& url, const std::string& script) { std::unique_ptr<web::WebState> old_web_state = DetachWebState();
diff --git a/ios/chrome/browser/scanner/ui_bundled/scanner_view_controller.h b/ios/chrome/browser/scanner/ui_bundled/scanner_view_controller.h index 80495c6d..b61c51a 100644 --- a/ios/chrome/browser/scanner/ui_bundled/scanner_view_controller.h +++ b/ios/chrome/browser/scanner/ui_bundled/scanner_view_controller.h
@@ -38,7 +38,7 @@ @property(nonatomic, readwrite) ScannerView* scannerView; // The scanned result. -@property(nonatomic, strong) NSString* result; +@property(nonatomic, copy) NSString* result; // Whether the scanned result should be immediately loaded. @property(nonatomic, assign) bool loadResultImmediately;
diff --git a/ios/chrome/browser/search_engines/model/ios_search_engine_choice_service_client_unittest.mm b/ios/chrome/browser/search_engines/model/ios_search_engine_choice_service_client_unittest.mm index 0bba888..4a4662b 100644 --- a/ios/chrome/browser/search_engines/model/ios_search_engine_choice_service_client_unittest.mm +++ b/ios/chrome/browser/search_engines/model/ios_search_engine_choice_service_client_unittest.mm
@@ -61,16 +61,18 @@ class IOSSearchEngineChoiceServiceClientTest : public PlatformTest { public: - IOSSearchEngineChoiceServiceClientTest() {} + IOSSearchEngineChoiceServiceClientTest() = default; void SetUp() override { PlatformTest::SetUp(); + ResetDeviceRestoreDataForTesting(); ResetSentinelFiles(); } void TearDown() override { - PlatformTest::TearDown(); ResetSentinelFiles(); + ResetDeviceRestoreDataForTesting(); + PlatformTest::TearDown(); } protected: @@ -97,7 +99,6 @@ backed_up_creation_timestamp); CreateDeviceRestoreSentinelFile(GetSentinelThatIsNotBackedUpURLPath(), not_backed_up_creation_timestamp); - ResetDeviceRestoreDataForTesting(); base::RunLoop run_loop; // Call IsFirstSessionAfterDeviceRestore() explicitly to make sure sentinel // files related to backup/restore are fully created before the end of the @@ -130,7 +131,6 @@ backed_up_creation_timestamp); CreateDeviceRestoreSentinelFile(GetSentinelThatIsNotBackedUpURLPath(), not_backed_up_creation_timestamp); - ResetDeviceRestoreDataForTesting(); base::RunLoop run_loop; // Call IsFirstSessionAfterDeviceRestore() explicitly to make sure sentinel // files related to backup/restore are fully created before the end of the @@ -159,7 +159,6 @@ CreateDeviceRestoreSentinelFile(GetSentinelThatIsBackedUpURLPath(), backed_up_creation_timestamp); - ResetDeviceRestoreDataForTesting(); base::RunLoop run_loop; // Call IsFirstSessionAfterDeviceRestore() explicitly to make sure sentinel // files related to backup/restore are fully created before the end of the
diff --git a/ios/chrome/browser/search_with/ui_bundled/search_with_mediator_egtest.mm b/ios/chrome/browser/search_with/ui_bundled/search_with_mediator_egtest.mm index d468ac0..629476b8 100644 --- a/ios/chrome/browser/search_with/ui_bundled/search_with_mediator_egtest.mm +++ b/ios/chrome/browser/search_with/ui_bundled/search_with_mediator_egtest.mm
@@ -25,8 +25,11 @@ namespace { const char kElementToLongPress[] = "selectid"; -ElementSelector* kElementToLongPressSelector = - [ElementSelector selectorWithElementID:kElementToLongPress]; + +// Returns an ElementSelector for `ElementToLongPress`. +ElementSelector* ElementToLongPressSelector() { + return [ElementSelector selectorWithElementID:kElementToLongPress]; +} // An HTML template that puts some text in a simple span element. const char kBasicSelectionUrl[] = "/basic"; @@ -115,7 +118,7 @@ // Tests for the Search With Edit menu entry. @interface SearchWithMediatorTestCase : ChromeTestCase -@property(nonatomic, strong) NSString* defaultSearchEngine; +@property(nonatomic, copy) NSString* defaultSearchEngine; @end @implementation SearchWithMediatorTestCase @@ -150,7 +153,7 @@ - (void)testSearchWith { [self loadPage]; - [ChromeEarlGreyUI triggerEditMenu:kElementToLongPressSelector]; + [ChromeEarlGreyUI triggerEditMenu:ElementToLongPressSelector()]; bool found = FindEditMenuAction(@"Search with test"); GREYAssertTrue(found, @"Search Web button not found"); [[EarlGrey selectElementWithMatcher: @@ -166,7 +169,7 @@ - (void)testSearchWithIncognito { [ChromeEarlGrey openNewIncognitoTab]; [self loadPage]; - [ChromeEarlGreyUI triggerEditMenu:kElementToLongPressSelector]; + [ChromeEarlGreyUI triggerEditMenu:ElementToLongPressSelector()]; bool found = FindEditMenuAction(@"Search with test"); GREYAssertTrue(found, @"Search Web button not found"); [[EarlGrey selectElementWithMatcher:
diff --git a/ios/chrome/browser/settings/ui_bundled/autofill/cells/autofill_profile_item.h b/ios/chrome/browser/settings/ui_bundled/autofill/cells/autofill_profile_item.h index 7f4b0124..c9530bb 100644 --- a/ios/chrome/browser/settings/ui_bundled/autofill/cells/autofill_profile_item.h +++ b/ios/chrome/browser/settings/ui_bundled/autofill/cells/autofill_profile_item.h
@@ -21,7 +21,7 @@ // Detail text to be displayed. The detail text label is configured with // multiline (no limit). -@property(nonatomic, strong) NSString* detailText; +@property(nonatomic, copy) NSString* detailText; // The GUID used by the PersonalDataManager to identify profiles. @property(nonatomic, assign) std::string GUID;
diff --git a/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_coordinator.mm b/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_coordinator.mm index 63f9f52..6d9fe6a4 100644 --- a/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_coordinator.mm +++ b/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_coordinator.mm
@@ -91,7 +91,7 @@ SyncEncryptionPassphraseTableViewController* _syncEncryptionPassphraseTableViewController; // Account menu coordinator. - SigninCoordinator<StopAnimatedChromeCoordinator>* _accountMenuCoordinator; + SigninCoordinator* _accountMenuCoordinator; } // View controller. @@ -490,7 +490,7 @@ accountMenuCoordinatorWithBaseViewController:self.viewController browser:self.browser contextStyle:SigninContextStyle::kDefault - anchorView:_viewController.view + anchorView:nil accessPoint:AccountMenuAccessPoint:: kSettings];
diff --git a/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_egtest.mm b/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_egtest.mm index 73e53b5d..6de4c63 100644 --- a/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_egtest.mm +++ b/ios/chrome/browser/settings/ui_bundled/google_services/manage_sync_settings_egtest.mm
@@ -1779,11 +1779,6 @@ // Test signing out from the account menu. - (void)testSignOutFromAccountFromAccountMenu { - // TODO(crbug.com/404180896): Test fails on iPad. - if ([ChromeEarlGrey isIPadIdiom]) { - EARL_GREY_TEST_DISABLED(@"Fails on iPad."); - } - // Separate profiles are only available in iOS 17+. if (!@available(iOS 17, *)) { return;
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_details/add_password_view_controller_unittest.mm b/ios/chrome/browser/settings/ui_bundled/password/password_details/add_password_view_controller_unittest.mm index 71161573..aec1e27 100644 --- a/ios/chrome/browser/settings/ui_bundled/password/password_details/add_password_view_controller_unittest.mm +++ b/ios/chrome/browser/settings/ui_bundled/password/password_details/add_password_view_controller_unittest.mm
@@ -53,7 +53,7 @@ // Whether `showExistingCredential` was called. @property(nonatomic) BOOL showExistingCredentialCalled; -@property(nonatomic, strong) NSString* suggestedPassword; +@property(nonatomic, copy) NSString* suggestedPassword; @property(nonatomic, strong) TableViewTextEditItem* passwordCell;
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_details/password_details_mediator.mm b/ios/chrome/browser/settings/ui_bundled/password/password_details/password_details_mediator.mm index a9cb466..e3680bc 100644 --- a/ios/chrome/browser/settings/ui_bundled/password/password_details/password_details_mediator.mm +++ b/ios/chrome/browser/settings/ui_bundled/password/password_details/password_details_mediator.mm
@@ -205,7 +205,7 @@ usernamesWithSameDomainDict; // Display name to use for the Password Details view. -@property(nonatomic, strong) NSString* displayName; +@property(nonatomic, copy) NSString* displayName; // The context in which the password details are accessed. @property(nonatomic, assign) DetailsContext context; @@ -233,7 +233,7 @@ _passwordCheckObserver = std::make_unique<PasswordCheckObserverBridge>(self, _manager.get()); _credentials = credentials; - _displayName = displayName; + _displayName = [displayName copy]; _context = context; _prefService = prefService; _syncService = syncService;
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_details/password_details_table_view_controller.mm b/ios/chrome/browser/settings/ui_bundled/password/password_details/password_details_table_view_controller.mm index 0ea15548..41852dc 100644 --- a/ios/chrome/browser/settings/ui_bundled/password/password_details/password_details_table_view_controller.mm +++ b/ios/chrome/browser/settings/ui_bundled/password/password_details/password_details_table_view_controller.mm
@@ -157,7 +157,7 @@ // Array of credentials that are shown on the screen. @property(nonatomic, strong) NSArray<CredentialDetails*>* credentials; -@property(nonatomic, strong) NSString* pageTitle; +@property(nonatomic, copy) NSString* pageTitle; // Whether the password is shown in plain text form or in masked form. @property(nonatomic, assign, getter=isPasswordShown) BOOL passwordShown; @@ -886,7 +886,7 @@ andTitle:(NSString*)title { BOOL hadCredentials = [_credentials count]; _credentials = credentials; - _pageTitle = title; + _pageTitle = [title copy]; [self updateNavigationTitle]; // Update the model even if all credentials are deleted and the view
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller_items.h b/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller_items.h index 9daa9d6..7dd7e9ef 100644 --- a/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller_items.h +++ b/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller_items.h
@@ -34,9 +34,9 @@ @property(nonatomic, assign) BOOL showLocalOnlyIcon; -@property(nonatomic, strong, readonly) NSString* title; +@property(nonatomic, copy, readonly) NSString* title; -@property(nonatomic, strong, readonly) NSString* detailText; +@property(nonatomic, copy, readonly) NSString* detailText; @end @@ -49,7 +49,7 @@ @property(nonatomic, assign) password_manager::CredentialUIEntry credential; -@property(nonatomic, strong, readonly) NSString* title; +@property(nonatomic, copy, readonly) NSString* title; @end
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_sharing/sharing_status_mediator_unittest.mm b/ios/chrome/browser/settings/ui_bundled/password/password_sharing/sharing_status_mediator_unittest.mm index 5acd959..3b8d679 100644 --- a/ios/chrome/browser/settings/ui_bundled/password/password_sharing/sharing_status_mediator_unittest.mm +++ b/ios/chrome/browser/settings/ui_bundled/password/password_sharing/sharing_status_mediator_unittest.mm
@@ -55,8 +55,8 @@ @property(nonatomic, strong) UIImage* senderImage; @property(nonatomic, strong) UIImage* recipientImage; -@property(nonatomic, strong) NSString* subtitleString; -@property(nonatomic, strong) NSString* footerString; +@property(nonatomic, copy) NSString* subtitleString; +@property(nonatomic, copy) NSString* footerString; @property(nonatomic, readonly) GURL URL; @end @@ -72,11 +72,11 @@ } - (void)setSubtitleString:(NSString*)subtitleString { - _subtitleString = subtitleString; + _subtitleString = [subtitleString copy]; } - (void)setFooterString:(NSString*)footerString { - _footerString = footerString; + _footerString = [footerString copy]; } - (void)setURL:(const GURL&)URL {
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_sharing/sharing_status_view_controller.mm b/ios/chrome/browser/settings/ui_bundled/password/password_sharing/sharing_status_view_controller.mm index 7ade5fc..caa75a12 100644 --- a/ios/chrome/browser/settings/ui_bundled/password/password_sharing/sharing_status_view_controller.mm +++ b/ios/chrome/browser/settings/ui_bundled/password/password_sharing/sharing_status_view_controller.mm
@@ -123,10 +123,10 @@ @property(nonatomic, strong) UILabel* titleLabel; // Subtitle string that will be displayed when the sharing is succesful. -@property(nonatomic, strong) NSString* subtitleString; +@property(nonatomic, copy) NSString* subtitleString; // Footer string that will be displayed when the sharing is succesful. -@property(nonatomic, strong) NSString* footerString; +@property(nonatomic, copy) NSString* footerString; // The button that cancels the sharing process. @property(nonatomic, strong) UIButton* cancelButton; @@ -231,11 +231,11 @@ } - (void)setSubtitleString:(NSString*)subtitleString { - _subtitleString = subtitleString; + _subtitleString = [subtitleString copy]; } - (void)setFooterString:(NSString*)footerString { - _footerString = footerString; + _footerString = [footerString copy]; } - (void)setURL:(const GURL&)URL {
diff --git a/ios/chrome/browser/shared/coordinator/chrome_coordinator/BUILD.gn b/ios/chrome/browser/shared/coordinator/chrome_coordinator/BUILD.gn index 1b57f54..9c988ef 100644 --- a/ios/chrome/browser/shared/coordinator/chrome_coordinator/BUILD.gn +++ b/ios/chrome/browser/shared/coordinator/chrome_coordinator/BUILD.gn
@@ -16,3 +16,12 @@ "//ios/chrome/browser/shared/public/commands", ] } + +source_set("animated_coordinator") { + sources = [ + "animated_coordinator.h", + "animated_coordinator.mm", + ] + deps = [ ":chrome_coordinator" ] + frameworks = [ "UIKit.framework" ] +}
diff --git a/ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h b/ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h new file mode 100644 index 0000000..3d5b125 --- /dev/null +++ b/ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.h
@@ -0,0 +1,25 @@ +// Copyright 2025 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_SHARED_COORDINATOR_CHROME_COORDINATOR_ANIMATED_COORDINATOR_H_ +#define IOS_CHROME_BROWSER_SHARED_COORDINATOR_CHROME_COORDINATOR_ANIMATED_COORDINATOR_H_ + +#import <Foundation/Foundation.h> + +#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h" + +// Class for a ChromeCoordinator that can be stopped with or without +// animated. It is expected that the `stop` method of classes implementing this +// protocol is defined as `[self stopAnimated:NO]`. +// Subclasses should not reimplement `stop`. Instead, they should implement +// `-stopAnimated` which should call `[super stopAnimated]`. +@interface AnimatedCoordinator : ChromeCoordinator + +// Same as `ChromeCoordinator`’s stop, but can be animated or not. Must call +// `[super stop]`. +- (void)stopAnimated:(BOOL)animated; + +@end + +#endif // IOS_CHROME_BROWSER_SHARED_COORDINATOR_CHROME_COORDINATOR_ANIMATED_COORDINATOR_H_
diff --git a/ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.mm b/ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.mm new file mode 100644 index 0000000..5235e89 --- /dev/null +++ b/ios/chrome/browser/shared/coordinator/chrome_coordinator/animated_coordinator.mm
@@ -0,0 +1,19 @@ +// Copyright 2025 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/shared/coordinator/chrome_coordinator/animated_coordinator.h" + +@implementation AnimatedCoordinator + +- (void)stopAnimated:(BOOL)animated { + [super stop]; +} + +#pragma mark - ChromeCoordinator + +- (void)stop { + [self stopAnimated:NO]; +} + +@end
diff --git a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm index 0ed9c94..4c295fe 100644 --- a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm +++ b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
@@ -465,8 +465,7 @@ // The coordinator used to control sign-in UI flows. Lazily created the first // time it is accessed. Use -[startSigninCoordinatorWithCompletion:] to start // the coordinator. -@property(nonatomic, strong) - SigninCoordinator<StopAnimatedChromeCoordinator>* signinCoordinator; +@property(nonatomic, strong) SigninCoordinator* signinCoordinator; // YES if the process of dismissing the sign-in prompt is from an external // trigger and is currently ongoing. An external trigger isn't done from the @@ -1439,9 +1438,6 @@ - (void)teardownUI { // The UI should be stopped before the models they observe are stopped. [self stopSigninCoordinatorAnimated:NO fromExternalTrigger:NO]; - // `self.signinCoordinator.signinCompletion()` was called in the interrupt - // method. Therefore now `self.signinCoordinator` is now stopped, and - // `self.signinCoordinator` is now nil. DCHECK(!self.signinCoordinator) << base::SysNSStringToUTF8([self.signinCoordinator description]); @@ -2234,14 +2230,12 @@ << base::SysNSStringToUTF8([self.signinCoordinator description]); Browser* browser = self.mainInterface.browser; UIViewController* baseViewController = self.mainInterface.viewController; - SigninCoordinator<StopAnimatedChromeCoordinator>* accountMenuCoordinator = - [SigninCoordinator - accountMenuCoordinatorWithBaseViewController:baseViewController - browser:browser - contextStyle:SigninContextStyle:: - kDefault - anchorView:nil - accessPoint:accessPoint]; + SigninCoordinator* accountMenuCoordinator = [SigninCoordinator + accountMenuCoordinatorWithBaseViewController:baseViewController + browser:browser + contextStyle:SigninContextStyle::kDefault + anchorView:nil + accessPoint:accessPoint]; self.signinCoordinator = accountMenuCoordinator; // TODO(crbug.com/336719423): Record signin metrics based on the // selected action from the account switcher. @@ -4015,7 +4009,7 @@ } } -// Interrupts the sign-in coordinator actions and dismisses its views either +// Stops the sign-in coordinator actions and dismisses its views either // with or without animation. - (void)stopSigninCoordinatorAnimated:(BOOL)animated fromExternalTrigger:(BOOL)external { @@ -4026,8 +4020,6 @@ self.dismissingSigninPromptFromExternalTrigger = YES; } - CHECK([self.signinCoordinator - conformsToProtocol:@protocol(StopAnimatedChromeCoordinator)]); [self.signinCoordinator stopAnimated:animated]; SigninCoordinatorCompletionCallback signinCompletion = self.signinCoordinator.signinCompletion;
diff --git a/ios/chrome/browser/shared/coordinator/scene/scene_util_test_support.mm b/ios/chrome/browser/shared/coordinator/scene/scene_util_test_support.mm index ddf5cc4..a91877f4 100644 --- a/ios/chrome/browser/shared/coordinator/scene/scene_util_test_support.mm +++ b/ios/chrome/browser/shared/coordinator/scene/scene_util_test_support.mm
@@ -10,7 +10,7 @@ - (instancetype)initWithIdentifier:(NSString*)identifier; -@property(nonatomic, strong, readonly) NSString* persistentIdentifier; +@property(nonatomic, copy, readonly) NSString* persistentIdentifier; @property(nonatomic, copy) NSDictionary<NSString*, id>* userInfo;
diff --git a/ios/chrome/browser/sharing/ui_bundled/activity_services/data/chrome_activity_image_source.mm b/ios/chrome/browser/sharing/ui_bundled/activity_services/data/chrome_activity_image_source.mm index faf0ec7d..0823480 100644 --- a/ios/chrome/browser/sharing/ui_bundled/activity_services/data/chrome_activity_image_source.mm +++ b/ios/chrome/browser/sharing/ui_bundled/activity_services/data/chrome_activity_image_source.mm
@@ -15,7 +15,7 @@ @property(nonatomic, strong) UIImage* image; // The image's title. -@property(nonatomic, strong) NSString* title; +@property(nonatomic, copy) NSString* title; @end @@ -26,7 +26,7 @@ DCHECK(title); if ((self = [super init])) { _image = image; - _title = title; + _title = [title copy]; } return self; }
diff --git a/ios/chrome/browser/sharing/ui_bundled/activity_services/data/chrome_activity_text_source.mm b/ios/chrome/browser/sharing/ui_bundled/activity_services/data/chrome_activity_text_source.mm index e0599032..01e8925 100644 --- a/ios/chrome/browser/sharing/ui_bundled/activity_services/data/chrome_activity_text_source.mm +++ b/ios/chrome/browser/sharing/ui_bundled/activity_services/data/chrome_activity_text_source.mm
@@ -11,7 +11,7 @@ @interface ChromeActivityTextSource () // Text to be shared with share extensions. -@property(nonatomic, strong) NSString* text; +@property(nonatomic, copy) NSString* text; @end
diff --git a/ios/chrome/browser/sharing/ui_bundled/sharing_coordinator.mm b/ios/chrome/browser/sharing/ui_bundled/sharing_coordinator.mm index d4e6333..e2a1704b 100644 --- a/ios/chrome/browser/sharing/ui_bundled/sharing_coordinator.mm +++ b/ios/chrome/browser/sharing/ui_bundled/sharing_coordinator.mm
@@ -143,7 +143,7 @@ @property(nonatomic, strong) NSURL* fileNSURL; // String where the downloaded file is saved. -@property(nonatomic, strong) NSString* filePath; +@property(nonatomic, copy) NSString* filePath; // CRWWebViewDownload instance that handle download interactions. @property(nonatomic, strong) id<CRWWebViewDownload> download;
diff --git a/ios/chrome/browser/signin/model/account_consistency_browser_agent.mm b/ios/chrome/browser/signin/model/account_consistency_browser_agent.mm index ff77883..26aefa0 100644 --- a/ios/chrome/browser/signin/model/account_consistency_browser_agent.mm +++ b/ios/chrome/browser/signin/model/account_consistency_browser_agent.mm
@@ -157,9 +157,6 @@ void AccountConsistencyBrowserAgent::ShowAccountMenu() { CHECK(AreSeparateProfilesForManagedAccountsEnabled()); - // TODO(crbug.com/375605412): Adjust the account menu shown here so that it - // has "Manage accounts on this device" as a top-level button, and no overflow - // menu. // TODO(crbug.com/411614444): Open the account menu here instead of going // through the handler. [application_handler_
diff --git a/ios/chrome/browser/signin/model/authentication_service_unittest.mm b/ios/chrome/browser/signin/model/authentication_service_unittest.mm index 1d6bbff..e560ea98 100644 --- a/ios/chrome/browser/signin/model/authentication_service_unittest.mm +++ b/ios/chrome/browser/signin/model/authentication_service_unittest.mm
@@ -357,9 +357,7 @@ // The reauth prompt should be shown if the primary identity is removed from an // other app when the user was signed in. -// TODO(crbug.com/413588295): Fix flaky test. -TEST_P(AuthenticationServiceTest, - FLAKY_TestHandleForgottenIdentityPromptSignIn) { +TEST_P(AuthenticationServiceTest, TestHandleForgottenIdentityPromptSignIn) { // Sign in. authentication_service()->SignIn(identity(0), signin_metrics::AccessPoint::kUnknown);
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/tab_group_activity_summary_cell.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/tab_group_activity_summary_cell.h index 2500120..b5545c45 100644 --- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/tab_group_activity_summary_cell.h +++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/tab_group_activity_summary_cell.h
@@ -27,7 +27,7 @@ @property(nonatomic, weak) id<TabGroupActivitySummaryCellDelegate> delegate; // The text of summary. -@property(nonatomic, strong) NSString* text; +@property(nonatomic, copy) NSString* text; @end
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/BUILD.gn b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/BUILD.gn index 2e6aa47..72d83bc 100644 --- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/BUILD.gn +++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/BUILD.gn
@@ -181,6 +181,7 @@ "//ios/chrome/browser/toolbar/ui_bundled/tab_groups:features_utils", "//ios/chrome/common", "//ios/chrome/common/ui/colors", + "//ios/chrome/common/ui/elements", "//ios/chrome/common/ui/favicon", "//ios/chrome/common/ui/table_view:cells_constants", "//ios/chrome/common/ui/util",
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/recent_activity_log_item.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/recent_activity_log_item.h index e383d05..c73e6c6 100644 --- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/recent_activity_log_item.h +++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/recent_activity_log_item.h
@@ -36,13 +36,13 @@ @property(nonatomic, strong) id<ShareKitAvatarPrimitive> avatarPrimitive; // Title of the item. -@property(nonatomic, strong) NSString* title; +@property(nonatomic, copy) NSString* title; // Description of the item. -@property(nonatomic, strong) NSString* actionDescription; +@property(nonatomic, copy) NSString* actionDescription; // Elapsed time since the action occurred (e.g., "6h ago", "just now"). -@property(nonatomic, strong) NSString* elapsedTime; +@property(nonatomic, copy) NSString* elapsedTime; // The type of action to be taken when this activity row is clicked. // Not to be used by the UI.
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_view_controller.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_view_controller.mm index 72dca7f..1bebdf8 100644 --- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_view_controller.mm +++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_view_controller.mm
@@ -31,6 +31,7 @@ #import "ios/chrome/browser/tab_switcher/ui_bundled/tab_group_action_type.h" #import "ios/chrome/browser/toolbar/ui_bundled/tab_groups/tab_group_indicator_features_utils.h" #import "ios/chrome/common/ui/colors/semantic_color_names.h" +#import "ios/chrome/common/ui/elements/gradient_view.h" #import "ios/chrome/common/ui/util/constraints_ui_util.h" #import "ios/chrome/grit/ios_strings.h" #import "ui/base/l10n/l10n_util_mac.h" @@ -44,10 +45,13 @@ // Background. constexpr CGFloat kBackgroundAlpha = 0.6; -// Top toolbar +// Top toolbar. constexpr CGFloat kTopToolbarHeight = 58; constexpr CGFloat kTopToolbarMargin = 16; +// Bottom toolbar. +constexpr CGFloat kGradientHeight = 86; + // Button. constexpr CGFloat kButtonSpacing = 10; constexpr CGFloat kLegacyMenuImageSize = 20; @@ -152,6 +156,10 @@ SharingState _sharingState; // The bottom toolbar. TabGridBottomToolbar* _bottomToolbar; + // Gradient displayed at the bottom to show that there are other tabs below. + UIView* _bottomGradient; + // The button containing the facepile. + UIButton* _facePileContainer; // The face pile view that displays the share button or the face pile. UIView* _facePileView; // Constraints for the container on narrow vs large windows. @@ -196,6 +204,8 @@ - (void)contentWillAppearAnimated:(BOOL)animated { [self.view layoutIfNeeded]; [_gridViewController contentWillAppearAnimated:YES]; + // To be able to handle keyboard shortcuts. + [self becomeFirstResponder]; } - (void)prepareForPresentation { @@ -203,6 +213,10 @@ [self contentWillAppearAnimated:YES]; + // Provide a change for the top/bottom toolbar to react to the real content + // size of the collection view. + [self gridViewControllerDidScroll]; + if (IsContainedTabGroupEnabled()) { _topToolbar.alpha = 0; _containerBackground.alpha = 0; @@ -278,8 +292,12 @@ } - (void)gridViewControllerDidScroll { - [_bottomToolbar - setScrollViewScrolledToEdge:self.gridViewController.scrolledToBottom]; + if (IsContainedTabGroupEnabled()) { + _bottomGradient.hidden = self.gridViewController.scrolledToBottom; + } else { + [_bottomToolbar + setScrollViewScrolledToEdge:self.gridViewController.scrolledToBottom]; + } _topToolbarBackground.hidden = self.gridViewController.scrolledToTop; } @@ -307,6 +325,20 @@ AddSameConstraints(self.view, _blurView); } + if (IsContainedTabGroupEnabled()) { + // Add it after the blur to be sure the tap goes through. + UIButton* backgroundButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [backgroundButton addTarget:self + action:@selector(didTapCloseButton) + forControlEvents:UIControlEventTouchUpInside]; + // The background is not selectable by voice over as there is an explicit + // close button. + backgroundButton.accessibilityElementsHidden = YES; + backgroundButton.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:backgroundButton]; + AddSameConstraints(backgroundButton, self.view); + } + [self fadeBlurIn]; _container = [[UIView alloc] init]; @@ -354,6 +386,14 @@ _topToolbar = [self configuredTopToolbar]; [_container addSubview:_topToolbar]; + _facePileContainer = [self configuredFacePileContainer]; + if (_facePileView) { + CHECK(_topToolbarButtonsStackView); + [_topToolbarButtonsStackView insertArrangedSubview:_facePileContainer + atIndex:0]; + } + [self updateFacePileAccessibilityLabel]; + [NSLayoutConstraint activateConstraints:@[ [_topToolbar.topAnchor constraintEqualToAnchor:_container.topAnchor], [_topToolbar.leadingAnchor @@ -393,6 +433,22 @@ } // Add the toolbar after the grid to make sure it is above it. + if (IsContainedTabGroupEnabled()) { + _bottomGradient = + [[GradientView alloc] initWithTopColor:UIColor.clearColor + bottomColor:UIColor.blackColor]; + _bottomGradient.translatesAutoresizingMaskIntoConstraints = NO; + _bottomGradient.userInteractionEnabled = NO; + [_container addSubview:_bottomGradient]; + AddSameConstraintsToSides( + _container, _bottomGradient, + LayoutSides::kBottom | LayoutSides::kLeading | LayoutSides::kTrailing); + [_bottomGradient.heightAnchor constraintEqualToConstant:kGradientHeight] + .active = YES; + + // Hide the default background of the bottom toolbar. + [_bottomToolbar setScrollViewScrolledToEdge:YES]; + } [self configureBottomToolbar]; if (@available(iOS 17, *)) { @@ -482,6 +538,7 @@ _gridViewController.shared = _sharingState != SharingState::kNotShared; if (IsContainedTabGroupEnabled()) { _menuButton.menu = [self configuredTabGroupMenu]; + [self updateFacePileAccessibilityLabel]; } else { [self configureNavigationBarItems]; } @@ -493,13 +550,12 @@ } if (IsContainedTabGroupEnabled()) { - if (_facePileView.superview == _topToolbarButtonsStackView) { + if (_facePileView.superview == _facePileContainer) { [_facePileView removeFromSuperview]; } + [_facePileContainer removeFromSuperview]; } else { - if (_facePileView.superview == self.view) { - [_facePileView removeFromSuperview]; - } + [_facePileView removeFromSuperview]; } _facePileView = facePileView; @@ -509,7 +565,13 @@ } if (IsContainedTabGroupEnabled()) { - [_topToolbarButtonsStackView insertArrangedSubview:_facePileView atIndex:0]; + if (!_facePileContainer) { + return; + } + [self updateFacePileContainer:_facePileContainer + withFacePile:_facePileView]; + [_topToolbarButtonsStackView insertArrangedSubview:_facePileContainer + atIndex:0]; } else { [self configureNavigationBarItems]; } @@ -561,13 +623,28 @@ return button; } +// Returns the UIButton container for the facepile. +- (UIButton*)configuredFacePileContainer { + UIButtonConfiguration* facePileContainerConfiguration = + [UIButtonConfiguration plainButtonConfiguration]; + facePileContainerConfiguration.cornerStyle = + UIButtonConfigurationCornerStyleCapsule; + __weak __typeof(self) weakSelf = self; + UIButton* container = [UIButton + buttonWithConfiguration:facePileContainerConfiguration + primaryAction:[UIAction actionWithHandler:^(UIAction* action) { + [weakSelf didTapFacePileButton]; + }]]; + [self updateFacePileContainer:container withFacePile:_facePileView]; + return container; +} + // Returns the stack view containing the top toolbar buttons. - (UIStackView*)configuredTopToolbarStackView { CHECK(IsContainedTabGroupEnabled()); UIStackView* stackView = [[UIStackView alloc] init]; stackView.translatesAutoresizingMaskIntoConstraints = NO; stackView.alignment = UIStackViewAlignmentCenter; - stackView.distribution = UIStackViewDistributionFill; stackView.spacing = kButtonSpacing; if (_facePileView) { @@ -1112,6 +1189,29 @@ } } +// Updates the facepile accessibility label based on sharing state. +- (void)updateFacePileAccessibilityLabel { + if (_sharingState == SharingState::kNotShared) { + _facePileContainer.accessibilityLabel = + l10n_util::GetNSString(IDS_IOS_SHARED_GROUP_SHARE_GROUP); + } else { + _facePileContainer.accessibilityLabel = + l10n_util::GetNSString(IDS_IOS_SHARED_GROUP_MANAGE_GROUP); + } +} + +// Updates the `facePileContainer` by adding the `facePile` to it. +- (void)updateFacePileContainer:(UIButton*)facePileContainer + withFacePile:(UIView*)facePile { + if (!facePile) { + return; + } + facePile.userInteractionEnabled = NO; + facePile.translatesAutoresizingMaskIntoConstraints = NO; + [facePileContainer addSubview:facePile]; + AddSameConstraints(facePile, facePileContainer); +} + // Starts managing the shared group. - (void)manageGroup { CHECK(_gridViewController.shared); @@ -1208,9 +1308,15 @@ } - (void)keyCommand_close { - _backButtonTapped = YES; base::RecordAction(base::UserMetricsAction("MobileKeyCommandClose")); - [_handler hideTabGroup]; + [self didTapCloseButton]; +} + +#pragma mark - UIAccessibilityAction + +- (BOOL)accessibilityPerformEscape { + [self didTapCloseButton]; + return YES; } #pragma mark - GridViewDelegate
diff --git a/ios/chrome/browser/whats_new/ui/cells/whats_new_table_view_subtitle_item.h b/ios/chrome/browser/whats_new/ui/cells/whats_new_table_view_subtitle_item.h index f2221e3b..716985a 100644 --- a/ios/chrome/browser/whats_new/ui/cells/whats_new_table_view_subtitle_item.h +++ b/ios/chrome/browser/whats_new/ui/cells/whats_new_table_view_subtitle_item.h
@@ -14,7 +14,7 @@ @interface WhatsNewTableViewSubtitleItem : TableViewHeaderFooterItem // The title text string. -@property(nonatomic, strong) NSString* title; +@property(nonatomic, copy) NSString* title; @end
diff --git a/ios/chrome/common/app_group/app_group_command.mm b/ios/chrome/common/app_group/app_group_command.mm index bb86239..b060ef63 100644 --- a/ios/chrome/common/app_group/app_group_command.mm +++ b/ios/chrome/common/app_group/app_group_command.mm
@@ -28,7 +28,7 @@ @interface AppGroupCommand () // The identifier of the extension that sent the order. -@property(nonatomic, strong) NSString* sourceApp; +@property(nonatomic, copy) NSString* sourceApp; // A block that can be used to open a URL. @property(nonatomic, strong) URLOpenerBlock opener; @@ -39,7 +39,7 @@ URLOpenerBlock:(URLOpenerBlock)opener { self = [super init]; if (self) { - _sourceApp = sourceApp; + _sourceApp = [sourceApp copy]; _opener = opener; } return self;
diff --git a/ios/chrome/common/ntp_tile/ntp_tile.h b/ios/chrome/common/ntp_tile/ntp_tile.h index c6ec1156..956cf46 100644 --- a/ios/chrome/common/ntp_tile/ntp_tile.h +++ b/ios/chrome/common/ntp_tile/ntp_tile.h
@@ -16,7 +16,7 @@ // The most visited site's URL. @property(readonly, atomic) NSURL* URL; // The filename of the most visited site's favicon on disk, if it exists. -@property(strong, atomic) NSString* faviconFileName; +@property(copy, atomic) NSString* faviconFileName; // The fallback text color for the most visited site, if it exists. @property(strong, atomic) UIColor* fallbackTextColor; // The fallback background color for the most visited site, if it exists. @@ -25,7 +25,7 @@ // default color. @property(assign, atomic) BOOL fallbackIsDefaultColor; // The monogram to use on the fallback icon. -@property(strong, atomic) NSString* fallbackMonogram; +@property(copy, atomic) NSString* fallbackMonogram; // Index of the site's position in the most visited list. @property(assign, atomic) NSUInteger position;
diff --git a/ios/chrome/common/ui/promo_style/promo_style_view_controller.h b/ios/chrome/common/ui/promo_style/promo_style_view_controller.h index a3ffee7..0e5188f 100644 --- a/ios/chrome/common/ui/promo_style/promo_style_view_controller.h +++ b/ios/chrome/common/ui/promo_style/promo_style_view_controller.h
@@ -53,7 +53,7 @@ - (instancetype)initWithCoder:(NSCoder*)coder NS_UNAVAILABLE; // The name of the banner image. Must be set before the view is loaded. -@property(nonatomic, strong) NSString* bannerName; +@property(nonatomic, copy) NSString* bannerName; // The ratio of the view covered by the banner. // - kExtraShort: 15%,
diff --git a/ios/chrome/common/ui/table_view/favicon_table_view_cell.h b/ios/chrome/common/ui/table_view/favicon_table_view_cell.h index 0912ac5..8814d68 100644 --- a/ios/chrome/common/ui/table_view/favicon_table_view_cell.h +++ b/ios/chrome/common/ui/table_view/favicon_table_view_cell.h
@@ -27,7 +27,7 @@ @property(nonatomic, readonly, strong) UILabel* detailTextLabel; // Identifier use to match an object with its cell. -@property(nonatomic, strong) NSString* uniqueIdentifier; +@property(nonatomic, copy) NSString* uniqueIdentifier; @end
diff --git a/ios/chrome/credential_provider_extension/passkey_request_details.mm b/ios/chrome/credential_provider_extension/passkey_request_details.mm index 9ceab456..2ca57ee1 100644 --- a/ios/chrome/credential_provider_extension/passkey_request_details.mm +++ b/ios/chrome/credential_provider_extension/passkey_request_details.mm
@@ -22,7 +22,7 @@ @property(nonatomic, readwrite) BOOL userVerificationRequired; // The relying party identifier for this request. -@property(strong, nonatomic, readwrite) NSString* relyingPartyIdentifier; +@property(copy, nonatomic, readwrite) NSString* relyingPartyIdentifier; // A list of allowed credential IDs for this request. An empty list means all // credentials are allowed. @@ -33,7 +33,7 @@ @property(nonatomic, readwrite) BOOL algorithmIsSupported; // The user name of the passkey credential. -@property(strong, nonatomic, readwrite) NSString* userName; +@property(copy, nonatomic, readwrite) NSString* userName; // The user handle of the passkey credential. @property(strong, nonatomic, readwrite) NSData* userHandle;
diff --git a/ios/chrome/credential_provider_extension/ui/credential_details_view_controller.mm b/ios/chrome/credential_provider_extension/ui/credential_details_view_controller.mm index 697fd66..adbd0ea 100644 --- a/ios/chrome/credential_provider_extension/ui/credential_details_view_controller.mm +++ b/ios/chrome/credential_provider_extension/ui/credential_details_view_controller.mm
@@ -47,7 +47,7 @@ @property(nonatomic, weak) id<Credential> credential; // Current clear password or nil (while locked). -@property(nonatomic, strong) NSString* clearPassword; +@property(nonatomic, copy) NSString* clearPassword; @end
diff --git a/ios/chrome/credential_provider_extension/ui/new_password_view_controller.h b/ios/chrome/credential_provider_extension/ui/new_password_view_controller.h index ea60335b..5332423 100644 --- a/ios/chrome/credential_provider_extension/ui/new_password_view_controller.h +++ b/ios/chrome/credential_provider_extension/ui/new_password_view_controller.h
@@ -51,7 +51,7 @@ @property(nonatomic, weak) id<NewCredentialHandler> credentialHandler; // The host for the password being generated. -@property(nonatomic, strong) NSString* currentHost; +@property(nonatomic, copy) NSString* currentHost; @end
diff --git a/ios/chrome/credential_provider_extension/ui/new_password_view_controller.mm b/ios/chrome/credential_provider_extension/ui/new_password_view_controller.mm index 08c17ffb..842e389 100644 --- a/ios/chrome/credential_provider_extension/ui/new_password_view_controller.mm +++ b/ios/chrome/credential_provider_extension/ui/new_password_view_controller.mm
@@ -61,13 +61,13 @@ @property(nonatomic, readonly) PasswordNoteCell* noteCell; // The value of the username text. -@property(nonatomic, strong) NSString* usernameText; +@property(nonatomic, copy) NSString* usernameText; // The value of the password text. -@property(nonatomic, strong) NSString* passwordText; +@property(nonatomic, copy) NSString* passwordText; // The value of the note text. -@property(nonatomic, strong) NSString* noteText; +@property(nonatomic, copy) NSString* noteText; // If yes, the footer informing about the max note length is shown. @property(nonatomic, assign) BOOL isNoteFooterShown;
diff --git a/ios/web/navigation/crw_error_page_helper.mm b/ios/web/navigation/crw_error_page_helper.mm index 87660375..0945ca4 100644 --- a/ios/web/navigation/crw_error_page_helper.mm +++ b/ios/web/navigation/crw_error_page_helper.mm
@@ -47,8 +47,8 @@ @interface CRWErrorPageHelper () @property(nonatomic, strong) NSError* error; // The error page HTML to be injected into existing page. -@property(nonatomic, strong) NSString* automaticReloadJavaScript; -@property(nonatomic, strong, readonly) NSString* failedNavigationURLString; +@property(nonatomic, copy) NSString* automaticReloadJavaScript; +@property(nonatomic, copy, readonly) NSString* failedNavigationURLString; @end @implementation CRWErrorPageHelper
diff --git a/ios/web/test/fakes/fake_native_task_bridge.h b/ios/web/test/fakes/fake_native_task_bridge.h index 044a059..6535b8b 100644 --- a/ios/web/test/fakes/fake_native_task_bridge.h +++ b/ios/web/test/fakes/fake_native_task_bridge.h
@@ -18,7 +18,7 @@ @property(nonatomic, readwrite, strong) WKDownload* download; @property(nonatomic, readwrite, strong) NSProgress* progress; @property(nonatomic, readwrite, strong) NSURLResponse* response; -@property(nonatomic, readwrite, strong) NSString* suggestedFilename; +@property(nonatomic, readwrite, copy) NSString* suggestedFilename; @end
diff --git a/ios_internal b/ios_internal index c5e5694..e2dcc74 160000 --- a/ios_internal +++ b/ios_internal
@@ -1 +1 @@ -Subproject commit c5e56941476cea4381eff1c195f7d16edf3996d0 +Subproject commit e2dcc744d3e17a3e0736d9ceecba5dd4a69e7f31
diff --git a/net/http/transport_security_state_static.pins b/net/http/transport_security_state_static.pins index 18f1c5a..f5e2fec 100644 --- a/net/http/transport_security_state_static.pins +++ b/net/http/transport_security_state_static.pins
@@ -43,9 +43,9 @@ # hash function for preloaded entries again (we have already done so once). # -# Last updated: 2025-04-27 12:54 UTC +# Last updated: 2025-04-28 12:54 UTC PinsListTimestamp -1745758456 +1745844843 TestSPKI sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
diff --git a/net/http/transport_security_state_static_pins.json b/net/http/transport_security_state_static_pins.json index 4d30a0e..db5a384 100644 --- a/net/http/transport_security_state_static_pins.json +++ b/net/http/transport_security_state_static_pins.json
@@ -31,7 +31,7 @@ // the 'static_spki_hashes' and 'bad_static_spki_hashes' fields in 'pinsets' // refer to, and the timestamp at which the pins list was last updated. // -// Last updated: 2025-04-27 12:54 UTC +// Last updated: 2025-04-28 12:54 UTC // { "pinsets": [
diff --git a/testing/buildbot/filters/ios.use_blink.components_unittests.filter b/testing/buildbot/filters/ios.use_blink.components_unittests.filter index 928a43e..95b1540 100644 --- a/testing/buildbot/filters/ios.use_blink.components_unittests.filter +++ b/testing/buildbot/filters/ios.use_blink.components_unittests.filter
@@ -406,4 +406,7 @@ # https://crrev.com/c/6185554. -MlEmbedderTest.EmbedderRunningStatus -MlEmbedderTest.ReturnsEmbeddings --MlEmbedderTest.ReturnsExecutionFailure \ No newline at end of file +-MlEmbedderTest.ReturnsExecutionFailure + +# TODO(crbug.com/413598265): The test is flaky both on simulator and on device. +-FeaturedSearchProviderTest.FLAKY_StarterPackExpansionRelevance
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 6e2b88e..e00599f 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -8166,21 +8166,6 @@ ] } ], - "DipsOnForegroundSequence": [ - { - "platforms": [ - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "DipsOnForegroundSequence" - ] - } - ] - } - ], "DirectCompositorThreadIpcMacLinuxChromeOS": [ { "platforms": [ @@ -10113,6 +10098,24 @@ ] } ], + "FieldRankServerClassification": [ + { + "platforms": [ + "android", + "android_webview", + "chromeos", + "ios", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "FieldRankServerClassification_Experiment_20250422" + } + ] + } + ], "FindRegistrationImprovements": [ { "platforms": [ @@ -25477,6 +25480,21 @@ ] } ], + "UseHeuristicForWindowsFullScreenPowerPoint": [ + { + "platforms": [ + "windows" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "UseHeuristicForWindowsFullScreenPowerPoint" + ] + } + ] + } + ], "UseLowQualityInterpolation": [ { "platforms": [
diff --git a/third_party/android_deps/autorolled/BUILD.gn b/third_party/android_deps/autorolled/BUILD.gn index 96ae5d4..e34c54b 100644 --- a/third_party/android_deps/autorolled/BUILD.gn +++ b/third_party/android_deps/autorolled/BUILD.gn
@@ -373,7 +373,7 @@ # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. java_prebuilt("org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm_java") { - jar_path = "autorolled/cipd/libs/org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm/kotlinx-coroutines-core-jvm-1.7.3.jar" + jar_path = "autorolled/cipd/libs/org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm/kotlinx-coroutines-core-jvm-1.8.1.jar" output_name = "org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm" supports_android = true requires_android = true
diff --git a/third_party/android_deps/autorolled/VERSION.txt b/third_party/android_deps/autorolled/VERSION.txt index 1709594..b91f29f4 100644 --- a/third_party/android_deps/autorolled/VERSION.txt +++ b/third_party/android_deps/autorolled/VERSION.txt
@@ -1 +1 @@ -f3c3db4a1251e78.1557f7c225575c0 \ No newline at end of file +91a03a231555fd7.ebe61f4d29b70ff \ No newline at end of file
diff --git a/third_party/android_deps/autorolled/bill_of_materials.json b/third_party/android_deps/autorolled/bill_of_materials.json index ccffbb1..bd95ddb 100644 --- a/third_party/android_deps/autorolled/bill_of_materials.json +++ b/third_party/android_deps/autorolled/bill_of_materials.json
@@ -942,12 +942,12 @@ { "name": "espresso-core", "group": "androidx.test.espresso", - "version": "3.7.0-alpha02" + "version": "3.7.0-alpha03" }, { "name": "espresso-idling-resource", "group": "androidx.test.espresso", - "version": "3.7.0-alpha02" + "version": "3.7.0-alpha03" }, { "name": "espresso-intents", @@ -962,12 +962,12 @@ { "name": "junit", "group": "androidx.test.ext", - "version": "1.3.0-alpha02" + "version": "1.3.0-alpha03" }, { "name": "storage", "group": "androidx.test.services", - "version": "1.6.0-alpha03" + "version": "1.6.0-alpha04" }, { "name": "uiautomator", @@ -982,22 +982,22 @@ { "name": "core", "group": "androidx.test", - "version": "1.7.0-alpha02" + "version": "1.7.0-alpha03" }, { "name": "monitor", "group": "androidx.test", - "version": "1.8.0-alpha02" + "version": "1.8.0-alpha03" }, { "name": "rules", "group": "androidx.test", - "version": "1.7.0-alpha02" + "version": "1.7.0-alpha03" }, { "name": "runner", "group": "androidx.test", - "version": "1.7.0-alpha02" + "version": "1.7.0-alpha03" }, { "name": "tracing", @@ -1647,7 +1647,7 @@ { "name": "kotlinx-coroutines-core-jvm", "group": "org.jetbrains.kotlinx", - "version": "1.7.3" + "version": "1.8.1" }, { "name": "kotlinx-coroutines-guava",
diff --git a/third_party/android_deps/autorolled/build.gradle b/third_party/android_deps/autorolled/build.gradle index 56d48ce3..583c4b9 100644 --- a/third_party/android_deps/autorolled/build.gradle +++ b/third_party/android_deps/autorolled/build.gradle
@@ -202,18 +202,18 @@ versionCache['androidx.startup:startup-runtime'] = '1.2.0' versionCache['androidx.swiperefreshlayout:swiperefreshlayout'] = '1.2.0-SNAPSHOT' versionCache['androidx.test.espresso:espresso-contrib'] = '3.5.1' -versionCache['androidx.test.espresso:espresso-core'] = '3.7.0-alpha02' -versionCache['androidx.test.espresso:espresso-idling-resource'] = '3.7.0-alpha02' +versionCache['androidx.test.espresso:espresso-core'] = '3.7.0-alpha03' +versionCache['androidx.test.espresso:espresso-idling-resource'] = '3.7.0-alpha03' versionCache['androidx.test.espresso:espresso-intents'] = '3.5.1' versionCache['androidx.test.espresso:espresso-web'] = '3.5.1' -versionCache['androidx.test.ext:junit'] = '1.3.0-alpha02' -versionCache['androidx.test.services:storage'] = '1.6.0-alpha03' +versionCache['androidx.test.ext:junit'] = '1.3.0-alpha03' +versionCache['androidx.test.services:storage'] = '1.6.0-alpha04' versionCache['androidx.test.uiautomator:uiautomator'] = '2.4.0-SNAPSHOT' versionCache['androidx.test:annotation'] = '1.0.1' -versionCache['androidx.test:core'] = '1.7.0-alpha02' -versionCache['androidx.test:monitor'] = '1.8.0-alpha02' -versionCache['androidx.test:rules'] = '1.7.0-alpha02' -versionCache['androidx.test:runner'] = '1.7.0-alpha02' +versionCache['androidx.test:core'] = '1.7.0-alpha03' +versionCache['androidx.test:monitor'] = '1.8.0-alpha03' +versionCache['androidx.test:rules'] = '1.7.0-alpha03' +versionCache['androidx.test:runner'] = '1.7.0-alpha03' versionCache['androidx.tracing:tracing'] = '1.3.0' versionCache['androidx.tracing:tracing-android'] = '1.3.0' versionCache['androidx.tracing:tracing-ktx'] = '1.3.0' @@ -343,7 +343,7 @@ versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-android'] = '1.8.1' versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-bom'] = '1.10.1' versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-core'] = '1.10.1' -versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm'] = '1.7.3' +versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm'] = '1.8.1' versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-guava'] = '1.8.1' versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-play-services'] = '1.10.1' versionCache['org.jetbrains.kotlinx:kotlinx-coroutines-reactive'] = '1.7.3'
diff --git a/third_party/android_deps/autorolled/committed/libs/org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm/README.chromium b/third_party/android_deps/autorolled/committed/libs/org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm/README.chromium index 49dad863..52fec17 100644 --- a/third_party/android_deps/autorolled/committed/libs/org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm/README.chromium +++ b/third_party/android_deps/autorolled/committed/libs/org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm/README.chromium
@@ -1,7 +1,7 @@ Name: kotlinx-coroutines-core Short Name: kotlinx-coroutines-core-jvm URL: https://github.com/Kotlin/kotlinx.coroutines -Version: 1.7.3 +Version: 1.8.1 License: Apache-2.0 License File: LICENSE CPEPrefix: unknown
diff --git a/third_party/angle b/third_party/angle index cdb3402..7e1b79c 160000 --- a/third_party/angle +++ b/third_party/angle
@@ -1 +1 @@ -Subproject commit cdb34025f286a0ded80c34c4edae180539e24dc1 +Subproject commit 7e1b79c7156eff744ecc6073a90ab2612668fe83
diff --git a/third_party/blink/public/mojom/annotation/annotation.mojom b/third_party/blink/public/mojom/annotation/annotation.mojom index 6e17617..00f54447 100644 --- a/third_party/blink/public/mojom/annotation/annotation.mojom +++ b/third_party/blink/public/mojom/annotation/annotation.mojom
@@ -74,6 +74,15 @@ kRangeInvalid, }; +union Selector { + // A text fragment selector (that uses the syntax defined in + // https://wicg.github.io/scroll-to-text-fragment/#syntax). + string serialized_selector; + // DOMNodeId corresponding to a node (which will be converted to a Range + // encompassing its subtree). + int32 node_id; +}; + // Allows the browser process to receive events for a single annotation in the // renderer. interface AnnotationAgentHost { @@ -98,15 +107,15 @@ // `agent_receiver`: A binding that the caller will use to send messages to // and control the life time of the agent. // `type`: The use case the annotation agent is being used for. - // `serialized_selector`: A selector that will be used to attach the agent to - // a Range of DOM in the Document. + // `selector`: A selector that will be used to attach the agent to a Range of + // DOM in the Document. // `search_range_start_node_id`: The DOMNodeId of the start node for the // search range to the end of the document. If DOMNodeId is not given, the // search range will be the whole document. CreateAgent(pending_remote<blink.mojom.AnnotationAgentHost> host_remote, pending_receiver<blink.mojom.AnnotationAgent> agent_receiver, AnnotationType type, - string serialized_selector, + Selector selector, int32? search_range_start_node_id); // Creates an agent from the document's current text selection and returns
diff --git a/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.cc b/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.cc index a5365ef..e497614 100644 --- a/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.cc +++ b/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.cc
@@ -11,6 +11,7 @@ #include "third_party/blink/renderer/core/annotation/annotation_agent_generator.h" #include "third_party/blink/renderer/core/annotation/annotation_agent_impl.h" #include "third_party/blink/renderer/core/annotation/annotation_selector.h" +#include "third_party/blink/renderer/core/annotation/node_annotation_selector.h" #include "third_party/blink/renderer/core/annotation/text_annotation_selector.h" #include "third_party/blink/renderer/core/editing/frame_selection.h" #include "third_party/blink/renderer/core/editing/visible_selection.h" @@ -20,6 +21,7 @@ #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/page/chrome_client.h" #include "third_party/blink/renderer/core/page/page.h" +#include "third_party/blink/renderer/platform/heap/garbage_collected.h" namespace blink { @@ -36,6 +38,15 @@ return "Glic"; } } + +String ToString(const mojom::blink::Selector& selector) { + switch (selector.which()) { + case mojom::blink::Selector::Tag::kSerializedSelector: + return selector.get_serialized_selector(); + case mojom::blink::Selector::Tag::kNodeId: + return String::Number(selector.get_node_id()); + } +} } // namespace // static @@ -180,26 +191,34 @@ mojo::PendingRemote<mojom::blink::AnnotationAgentHost> host_remote, mojo::PendingReceiver<mojom::blink::AnnotationAgent> agent_receiver, mojom::blink::AnnotationType type, - const String& serialized_selector, + mojom::blink::SelectorPtr selector, std::optional<DOMNodeId> search_range_start_node_id) { TRACE_EVENT("blink", "AnnotationAgentContainerImpl::CreateAgent", "type", - ToString(type), "selector", serialized_selector); + ToString(type), "selector", ToString(*selector)); DCHECK(GetSupplementable()); - AnnotationSelector* selector = - AnnotationSelector::Deserialize(serialized_selector); - - // If the selector was invalid, we should drop the bindings which the host - // will see as a disconnect. - // TODO(bokan): We could support more graceful fallback/error reporting by - // calling an error method on the host. - if (!selector) { - TRACE_EVENT_INSTANT("blink", "Failed to deserialize selector"); - return; + AnnotationSelector* annotation_selector; + switch (selector->which()) { + case mojom::blink::Selector::Tag::kSerializedSelector: + annotation_selector = + AnnotationSelector::Deserialize(selector->get_serialized_selector()); + // If the selector was invalid, we should drop the bindings which the host + // will see as a disconnect. + // TODO(bokan): We could support more graceful fallback/error reporting by + // calling an error method on the host. + if (!annotation_selector) { + TRACE_EVENT_INSTANT("blink", "Failed to deserialize selector"); + return; + } + break; + case mojom::blink::Selector::Tag::kNodeId: + annotation_selector = + MakeGarbageCollected<NodeAnnotationSelector>(selector->get_node_id()); + break; } - auto* agent_impl = - CreateUnboundAgent(type, *selector, search_range_start_node_id); + auto* agent_impl = CreateUnboundAgent(type, *annotation_selector, + search_range_start_node_id); agent_impl->Bind(std::move(host_remote), std::move(agent_receiver)); }
diff --git a/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.h b/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.h index ddfac80..ec27817b 100644 --- a/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.h +++ b/third_party/blink/renderer/core/annotation/annotation_agent_container_impl.h
@@ -106,7 +106,7 @@ mojo::PendingRemote<mojom::blink::AnnotationAgentHost> host_remote, mojo::PendingReceiver<mojom::blink::AnnotationAgent> agent_receiver, mojom::blink::AnnotationType type, - const String& serialized_selector, + mojom::blink::SelectorPtr selector, std::optional<DOMNodeId> search_range_start_node_id = std::nullopt) override; void CreateAgentFromSelection(
diff --git a/third_party/blink/renderer/core/annotation/annotation_agent_container_impl_test.cc b/third_party/blink/renderer/core/annotation/annotation_agent_container_impl_test.cc index a6d27e8..5f6e691 100644 --- a/third_party/blink/renderer/core/annotation/annotation_agent_container_impl_test.cc +++ b/third_party/blink/renderer/core/annotation/annotation_agent_container_impl_test.cc
@@ -21,6 +21,7 @@ #include "third_party/blink/renderer/core/html/html_element.h" #include "third_party/blink/renderer/core/testing/sim/sim_request.h" #include "third_party/blink/renderer/core/testing/sim/sim_test.h" +#include "third_party/blink/renderer/platform/graphics/dom_node_id.h" namespace blink { @@ -216,10 +217,11 @@ auto* container = AnnotationAgentContainerImpl::CreateIfNeeded(GetDocument()); ASSERT_TRUE(container); - container->CreateAgent(std::move(remote_receiver_pair.first), - std::move(remote_receiver_pair.second), - mojom::blink::AnnotationType::kSharedHighlight, - "MockAnnotationSelector"); + container->CreateAgent( + std::move(remote_receiver_pair.first), + std::move(remote_receiver_pair.second), + mojom::blink::AnnotationType::kSharedHighlight, + mojom::blink::Selector::NewSerializedSelector("MockAnnotationSelector")); EXPECT_EQ(GetAgentCount(*container), 1ul); @@ -252,10 +254,11 @@ auto* container = AnnotationAgentContainerImpl::CreateIfNeeded(GetDocument()); ASSERT_TRUE(container); - container->CreateAgent(std::move(remote_receiver_pair.first), - std::move(remote_receiver_pair.second), - mojom::blink::AnnotationType::kUserNote, - "MockAnnotationSelector"); + container->CreateAgent( + std::move(remote_receiver_pair.first), + std::move(remote_receiver_pair.second), + mojom::blink::AnnotationType::kUserNote, + mojom::blink::Selector::NewSerializedSelector("MockAnnotationSelector")); // The agent should be created and bound. EXPECT_EQ(GetAgentCount(*container), 1ul); @@ -327,10 +330,11 @@ auto remote_receiver_pair = host.BindForCreateAgent(); auto* container = AnnotationAgentContainerImpl::CreateIfNeeded(GetDocument()); - container->CreateAgent(std::move(remote_receiver_pair.first), - std::move(remote_receiver_pair.second), - mojom::blink::AnnotationType::kSharedHighlight, - "MockAnnotationSelector"); + container->CreateAgent( + std::move(remote_receiver_pair.first), + std::move(remote_receiver_pair.second), + mojom::blink::AnnotationType::kSharedHighlight, + mojom::blink::Selector::NewSerializedSelector("MockAnnotationSelector")); ASSERT_EQ(GetAgentCount(*container), 1ul); ASSERT_FALSE(host.did_disconnect_); @@ -690,4 +694,69 @@ EXPECT_EQ(GetAgentCount(*new_container), 0ul); } +// Test that the container can create a bound agent using a node id selector. It +// should automatically perform attachment at creation time. +TEST_F(AnnotationAgentContainerImplTest, CreateBoundAgentWithNodeId) { + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML( + <!DOCTYPE html> + <body> + <p id="text">some text</p> + </body> + )HTML"); + Compositor().BeginFrame(); + + MockAnnotationAgentHost host; + auto remote_receiver_pair = host.BindForCreateAgent(); + + auto* container = AnnotationAgentContainerImpl::CreateIfNeeded(GetDocument()); + ASSERT_TRUE(container); + container->CreateAgent( + std::move(remote_receiver_pair.first), + std::move(remote_receiver_pair.second), + mojom::blink::AnnotationType::kSharedHighlight, + mojom::blink::Selector::NewNodeId(DOMNodeIds::IdForNode( + GetDocument().getElementById(AtomicString("text"))))); + + EXPECT_EQ(GetAgentCount(*container), 1ul); + EXPECT_TRUE(host.agent_.is_connected()); + + Compositor().BeginFrame(); + host.FlushForTesting(); + EXPECT_EQ(host.attachment_result_, mojom::blink::AttachmentResult::kSuccess); + EXPECT_FALSE(host.did_finish_attachment_rect_->IsEmpty()); + EXPECT_FALSE(host.did_disconnect_); +} + +TEST_F(AnnotationAgentContainerImplTest, + CreateBoundAgentWithNonExistentNodeId) { + SimRequest request("https://example.com/test.html", "text/html"); + LoadURL("https://example.com/test.html"); + request.Complete(R"HTML()HTML"); + Compositor().BeginFrame(); + + MockAnnotationAgentHost host; + auto remote_receiver_pair = host.BindForCreateAgent(); + + auto* container = AnnotationAgentContainerImpl::CreateIfNeeded(GetDocument()); + ASSERT_TRUE(container); + const int non_existent_node_id = 9999; + container->CreateAgent( + std::move(remote_receiver_pair.first), + std::move(remote_receiver_pair.second), + mojom::blink::AnnotationType::kSharedHighlight, + mojom::blink::Selector::NewNodeId(non_existent_node_id)); + + EXPECT_EQ(GetAgentCount(*container), 1ul); + EXPECT_TRUE(host.agent_.is_connected()); + + Compositor().BeginFrame(); + host.FlushForTesting(); + EXPECT_EQ(host.attachment_result_, + mojom::blink::AttachmentResult::kSelectorNotMatched); + EXPECT_TRUE(host.did_finish_attachment_rect_->IsEmpty()); + EXPECT_FALSE(host.did_disconnect_); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/annotation/annotation_test_utils.h b/third_party/blink/renderer/core/annotation/annotation_test_utils.h index 1bdb2b64..63b77dcaf 100644 --- a/third_party/blink/renderer/core/annotation/annotation_test_utils.h +++ b/third_party/blink/renderer/core/annotation/annotation_test_utils.h
@@ -10,6 +10,7 @@ #include "base/check_op.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" +#include "third_party/blink/public/mojom/annotation/annotation.mojom-blink-forward.h" #include "third_party/blink/public/mojom/annotation/annotation.mojom-blink.h" #include "third_party/blink/renderer/core/annotation/annotation_agent_impl.h" #include "third_party/blink/renderer/core/annotation/annotation_selector.h" @@ -90,6 +91,7 @@ void DidFinishAttachment(const gfx::Rect& rect, mojom::blink::AttachmentResult result) override { did_finish_attachment_rect_ = rect; + attachment_result_ = result; } void BindToAgent(AnnotationAgentImpl& agent) { @@ -136,6 +138,7 @@ mojo::Receiver<mojom::blink::AnnotationAgentHost> receiver_; mojo::Remote<mojom::blink::AnnotationAgent> agent_; std::optional<gfx::Rect> did_finish_attachment_rect_; + std::optional<mojom::blink::AttachmentResult> attachment_result_; bool did_disconnect_ = false; };
diff --git a/third_party/blink/renderer/core/css/css_math_function_value.h b/third_party/blink/renderer/core/css/css_math_function_value.h index fd96256..42ef6aa 100644 --- a/third_party/blink/renderer/core/css/css_math_function_value.h +++ b/third_party/blink/renderer/core/css/css_math_function_value.h
@@ -88,7 +88,7 @@ std::optional<double> GetValueIfKnown() const { std::optional<double> val = expression_->GetValueIfKnown(); if (val.has_value()) { - return ClampToPermittedRange(*val); + return ClampToPermittedRange(CSSValueClampingUtils::ClampDouble(*val)); } else { return val; }
diff --git a/third_party/blink/renderer/core/css/css_primitive_value.h b/third_party/blink/renderer/core/css/css_primitive_value.h index 90340e1..76256f1 100644 --- a/third_party/blink/renderer/core/css/css_primitive_value.h +++ b/third_party/blink/renderer/core/css/css_primitive_value.h
@@ -441,9 +441,6 @@ // Returns Double Value including infinity, -infinity, and NaN. double GetDoubleValueWithoutClamping() const; - float GetFloatValue() const { return GetValue<float>(); } - int GetIntValue() const { return GetValue<int>(); } - template <typename T> requires std::integral<T> || std::floating_point<T> inline T ConvertTo(const CSSLengthResolver& length_resolver) const {
diff --git a/third_party/blink/renderer/core/css/css_style_rule.cc b/third_party/blink/renderer/core/css/css_style_rule.cc index a981091..b10ebb9 100644 --- a/third_party/blink/renderer/core/css/css_style_rule.cc +++ b/third_party/blink/renderer/core/css/css_style_rule.cc
@@ -259,7 +259,7 @@ return 0; } else { CSSStyleSheet::RuleMutationScope mutation_scope(this); - style_rule_->WrapperInsertRule(index, new_rule); + style_rule_->WrapperInsertRule(parentStyleSheet(), index, new_rule); child_rule_cssom_wrappers_.insert(index, Member<CSSRule>(nullptr)); return index; } @@ -280,7 +280,7 @@ CSSStyleSheet::RuleMutationScope mutation_scope(this); - style_rule_->WrapperRemoveRule(index); + style_rule_->WrapperRemoveRule(parentStyleSheet(), index); if (child_rule_cssom_wrappers_[index]) { child_rule_cssom_wrappers_[index]->SetParentRule(nullptr);
diff --git a/third_party/blink/renderer/core/css/parser/at_rule_font_palette_values_descriptor_parser.cc b/third_party/blink/renderer/core/css/parser/at_rule_font_palette_values_descriptor_parser.cc index 02cd2c08..d116162 100644 --- a/third_party/blink/renderer/core/css/parser/at_rule_font_palette_values_descriptor_parser.cc +++ b/third_party/blink/renderer/core/css/parser/at_rule_font_palette_values_descriptor_parser.cc
@@ -28,7 +28,15 @@ return ident; } - return css_parsing_utils::ConsumeInteger(stream, context, 0); + CSSPrimitiveValue* palette_index = + css_parsing_utils::ConsumeInteger(stream, context, 0); + if (palette_index && palette_index->GetValueIfKnown().has_value()) { + // Only calc() expressions that can be fully simplified at parse time are + // valid. If not, the rely on an element context, and @font-palette-values + // descriptors are not in an element context. + return palette_index; + } + return nullptr; } CSSValue* ConsumeColorOverride(CSSParserTokenStream& stream,
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc index a7ed20c..1d2acbd3 100644 --- a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc +++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
@@ -1646,7 +1646,11 @@ if (!number_value) { return nullptr; } - parsed_numbers.push_back(number_value->GetIntValue()); + std::optional<double> number = number_value->GetValueIfKnown(); + if (!number.has_value()) { + return nullptr; + } + parsed_numbers.push_back(ClampTo<int>(number.value())); } const CSSParserToken& expected_semicolon = stream.Peek();
diff --git a/third_party/blink/renderer/core/css/style_rule.cc b/third_party/blink/renderer/core/css/style_rule.cc index 21b3137..ccc27676 100644 --- a/third_party/blink/renderer/core/css/style_rule.cc +++ b/third_party/blink/renderer/core/css/style_rule.cc
@@ -534,6 +534,23 @@ return *To<MutableCSSPropertyValueSet>(properties_.Get()); } +void StyleRule::WrapperInsertRule(CSSStyleSheet* parent_sheet, + unsigned index, + StyleRuleBase* rule) { + EnsureChildRules(); + child_rules_->insert(index, rule); + if (parent_sheet) { + parent_sheet->Contents()->NotifyRuleChanged(rule); + } +} + +void StyleRule::WrapperRemoveRule(CSSStyleSheet* parent_sheet, unsigned index) { + if (parent_sheet) { + parent_sheet->Contents()->NotifyRuleChanged((*child_rules_)[index]); + } + child_rules_->erase(UNSAFE_TODO(child_rules_->begin() + index)); +} + bool StyleRule::PropertiesHaveFailedOrCanceledSubresources() const { return properties_ && properties_->HasFailedOrCanceledSubresources(); }
diff --git a/third_party/blink/renderer/core/css/style_rule.h b/third_party/blink/renderer/core/css/style_rule.h index de08b37..9fa547c2 100644 --- a/third_party/blink/renderer/core/css/style_rule.h +++ b/third_party/blink/renderer/core/css/style_rule.h
@@ -304,13 +304,10 @@ EnsureChildRules(); child_rules_->push_back(child); } - void WrapperInsertRule(unsigned index, StyleRuleBase* rule) { - EnsureChildRules(); - child_rules_->insert(index, rule); - } - void WrapperRemoveRule(unsigned index) { - child_rules_->erase(UNSAFE_TODO(child_rules_->begin() + index)); - } + void WrapperInsertRule(CSSStyleSheet* parent_sheet, + unsigned index, + StyleRuleBase* rule); + void WrapperRemoveRule(CSSStyleSheet* parent_sheet, unsigned index); private: friend class StyleRuleBase;
diff --git a/third_party/blink/renderer/core/css/style_rule_font_palette_values.cc b/third_party/blink/renderer/core/css/style_rule_font_palette_values.cc index 94c0fdf..f7731d8 100644 --- a/third_party/blink/renderer/core/css/style_rule_font_palette_values.cc +++ b/third_party/blink/renderer/core/css/style_rule_font_palette_values.cc
@@ -62,10 +62,12 @@ } } - const CSSPrimitiveValue& palette_primitive = - To<CSSPrimitiveValue>(*base_palette); + std::optional<double> index = + To<CSSPrimitiveValue>(*base_palette).GetValueIfKnown(); + CHECK(index.has_value()) + << "Non-simplified calc() expressions should be dropped at parse time"; return FontPalette::BasePaletteValue( - {FontPalette::kIndexBasePalette, palette_primitive.GetIntValue()}); + {FontPalette::kIndexBasePalette, ClampTo<int>(index.value())}); } Vector<FontPalette::FontPaletteOverride>
diff --git a/third_party/blink/renderer/core/editing/commands/apply_style_command.cc b/third_party/blink/renderer/core/editing/commands/apply_style_command.cc index 1ae46f96..917382b9 100644 --- a/third_party/blink/renderer/core/editing/commands/apply_style_command.cc +++ b/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
@@ -2065,17 +2065,20 @@ } auto* style = MakeGarbageCollected<CSSComputedStyleDeclaration>(element); - if (!style) + if (!style) { return 0; + } - const auto* value = To<CSSPrimitiveValue>( + const auto* value = DynamicTo<CSSPrimitiveValue>( style->GetPropertyCSSValue(CSSPropertyID::kFontSize)); - if (!value) + if (!value) { return 0; + } // TODO(yosin): We should have printer for |CSSPrimitiveValue::UnitType|. DCHECK(value->IsPx()); - return value->GetFloatValue(); + std::optional<double> font_size = value->GetValueIfKnown(); + return font_size.value_or(0); } void ApplyStyleCommand::JoinChildTextNodes(ContainerNode* node,
diff --git a/third_party/blink/renderer/core/editing/editing_style.cc b/third_party/blink/renderer/core/editing/editing_style.cc index 434075d..dc5a12c 100644 --- a/third_party/blink/renderer/core/editing/editing_style.cc +++ b/third_party/blink/renderer/core/editing/editing_style.cc
@@ -707,16 +707,16 @@ const CSSValue* value = mutable_style_->GetPropertyCSSValue( CSSPropertyID::kInternalFontSizeDelta); const auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value); - if (!primitive_value) - return; - // Only PX handled now. If we handle more types in the future, perhaps // a switch statement here would be more appropriate. - if (!primitive_value->IsPx()) + if (!primitive_value || !primitive_value->IsPx()) { return; - - font_size_delta_ = primitive_value->GetFloatValue(); - mutable_style_->RemoveProperty(CSSPropertyID::kInternalFontSizeDelta); + } + std::optional<double> font_size_delta = primitive_value->GetValueIfKnown(); + if (font_size_delta.has_value()) { + font_size_delta_ = ClampTo<float>(font_size_delta.value()); + mutable_style_->RemoveProperty(CSSPropertyID::kInternalFontSizeDelta); + } } bool EditingStyle::IsEmpty() const { @@ -1823,30 +1823,28 @@ } } -static bool GetPrimitiveValueNumber(CSSPropertyValueSet* style, - CSSPropertyID property_id, - float& number) { - if (!style) - return false; +static std::optional<double> GetPrimitiveValueNumber( + CSSPropertyValueSet* style, + CSSPropertyID property_id) { + if (!style) { + return std::nullopt; + } const CSSValue* value = style->GetPropertyCSSValue(property_id); - const auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value); - if (!primitive_value) - return false; - number = primitive_value->GetFloatValue(); - return true; + if (const auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value)) { + return primitive_value->GetValueIfKnown(); + } + return std::nullopt; } void StyleChange::ExtractTextStyles(Document* document, MutableCSSPropertyValueSet* style, bool is_monospace_font) { - DCHECK(style); - - float weight = 0; - bool is_number = - GetPrimitiveValueNumber(style, CSSPropertyID::kFontWeight, weight); - if (GetIdentifierValue(style, CSSPropertyID::kFontWeight) == - CSSValueID::kBold || - (is_number && weight >= kBoldThreshold)) { + CHECK(style); + std::optional<double> weight = + GetPrimitiveValueNumber(style, CSSPropertyID::kFontWeight); + if ((weight.has_value() && weight.value() >= kBoldThreshold) || + GetIdentifierValue(style, CSSPropertyID::kFontWeight) == + CSSValueID::kBold) { style->RemoveProperty(CSSPropertyID::kFontWeight); apply_bold_ = true; } @@ -1961,8 +1959,12 @@ } } - CHECK(To<CSSPrimitiveValue>(font_weight)->IsNumber()); - return To<CSSPrimitiveValue>(font_weight)->GetFloatValue() >= kBoldThreshold; + if (const auto* primitive_value = DynamicTo<CSSPrimitiveValue>(font_weight)) { + CHECK(primitive_value->IsNumber()); + std::optional<double> weight = primitive_value->GetValueIfKnown(); + return weight.has_value() && weight.value() >= kBoldThreshold; + } + return false; } static bool FontWeightNeedsResolving(const CSSValue* font_weight) {
diff --git a/third_party/blink/renderer/core/html/fenced_frame/OWNERS b/third_party/blink/renderer/core/html/fenced_frame/OWNERS index 50a509f..32a7e80 100644 --- a/third_party/blink/renderer/core/html/fenced_frame/OWNERS +++ b/third_party/blink/renderer/core/html/fenced_frame/OWNERS
@@ -1,3 +1,4 @@ +averge@chromium.org dom@chromium.org -lbrady@google.com shivanisha@chromium.org +xiaochenzh@chromium.org
diff --git a/third_party/blink/renderer/core/svg/svg_length.h b/third_party/blink/renderer/core/svg/svg_length.h index bec8a0a..8549962c 100644 --- a/third_party/blink/renderer/core/svg/svg_length.h +++ b/third_party/blink/renderer/core/svg/svg_length.h
@@ -83,7 +83,7 @@ float Value(const SVGLengthConversionData&, float dimension) const; float Value(const SVGLengthContext&) const; float ValueInSpecifiedUnits() const { - return To<CSSNumericLiteralValue>(*value_).GetFloatValue(); + return ClampTo<float>(To<CSSNumericLiteralValue>(*value_).GetDoubleValue()); } void SetValueAsNumber(float);
diff --git a/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.cc b/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.cc index 3022035..00357aa 100644 --- a/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.cc +++ b/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.cc
@@ -19,13 +19,9 @@ * Boston, MA 02110-1301, USA. */ -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.h" +#include "base/containers/span.h" #include "base/notreached.h" #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" @@ -57,106 +53,124 @@ } template <typename CharType> -SVGParsingError SVGPreserveAspectRatio::ParseInternal(const CharType*& ptr, - const CharType* end, - bool validate) { +SVGParsingError SVGPreserveAspectRatio::ParseInternal( + base::span<CharType>& span_inout, + bool validate) { SVGPreserveAspectRatioType align = kSvgPreserveaspectratioXmidymid; SVGMeetOrSliceType meet_or_slice = kSvgMeetorsliceMeet; SetAlign(align); SetMeetOrSlice(meet_or_slice); - const CharType* start = ptr; - if (!SkipOptionalSVGSpaces(ptr, end)) - return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start); + auto* start = span_inout.data(); - if (*ptr == 'n') { - if (!SkipToken(ptr, end, "none")) - return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start); + if (!SkipOptionalSVGSpaces(span_inout)) { + return SVGParsingError(SVGParseStatus::kExpectedEnumeration, + span_inout.data() - start); + } + + if (span_inout[0] == 'n') { + if (!SkipToken(span_inout, "none")) { + return SVGParsingError(SVGParseStatus::kExpectedEnumeration, + span_inout.data() - start); + } align = kSvgPreserveaspectratioNone; - SkipOptionalSVGSpaces(ptr, end); - } else if (*ptr == 'x') { - if ((end - ptr) < 8) - return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start); - if (ptr[1] != 'M' || ptr[4] != 'Y' || ptr[5] != 'M') - return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start); - if (ptr[2] == 'i') { - if (ptr[3] == 'n') { - if (ptr[6] == 'i') { - if (ptr[7] == 'n') + SkipOptionalSVGSpaces(span_inout); + } else if (span_inout[0] == 'x') { + if (span_inout.size() < 8) { + return SVGParsingError(SVGParseStatus::kExpectedEnumeration, + span_inout.data() - start); + } + if (span_inout[1] != 'M' || span_inout[4] != 'Y' || span_inout[5] != 'M') { + return SVGParsingError(SVGParseStatus::kExpectedEnumeration, + span_inout.data() - start); + } + if (span_inout[2] == 'i') { + if (span_inout[3] == 'n') { + if (span_inout[6] == 'i') { + if (span_inout[7] == 'n') { align = kSvgPreserveaspectratioXminymin; - else if (ptr[7] == 'd') + } else if (span_inout[7] == 'd') { align = kSvgPreserveaspectratioXminymid; - else + } else { return SVGParsingError(SVGParseStatus::kExpectedEnumeration, - ptr - start); - } else if (ptr[6] == 'a' && ptr[7] == 'x') { + span_inout.data() - start); + } + } else if (span_inout[6] == 'a' && span_inout[7] == 'x') { align = kSvgPreserveaspectratioXminymax; } else { return SVGParsingError(SVGParseStatus::kExpectedEnumeration, - ptr - start); + span_inout.data() - start); } - } else if (ptr[3] == 'd') { - if (ptr[6] == 'i') { - if (ptr[7] == 'n') + } else if (span_inout[3] == 'd') { + if (span_inout[6] == 'i') { + if (span_inout[7] == 'n') { align = kSvgPreserveaspectratioXmidymin; - else if (ptr[7] == 'd') + } else if (span_inout[7] == 'd') { align = kSvgPreserveaspectratioXmidymid; - else + } else { return SVGParsingError(SVGParseStatus::kExpectedEnumeration, - ptr - start); - } else if (ptr[6] == 'a' && ptr[7] == 'x') { + span_inout.data() - start); + } + } else if (span_inout[6] == 'a' && span_inout[7] == 'x') { align = kSvgPreserveaspectratioXmidymax; } else { return SVGParsingError(SVGParseStatus::kExpectedEnumeration, - ptr - start); + span_inout.data() - start); } } else { return SVGParsingError(SVGParseStatus::kExpectedEnumeration, - ptr - start); + span_inout.data() - start); } - } else if (ptr[2] == 'a' && ptr[3] == 'x') { - if (ptr[6] == 'i') { - if (ptr[7] == 'n') + } else if (span_inout[2] == 'a' && span_inout[3] == 'x') { + if (span_inout[6] == 'i') { + if (span_inout[7] == 'n') { align = kSvgPreserveaspectratioXmaxymin; - else if (ptr[7] == 'd') + } else if (span_inout[7] == 'd') { align = kSvgPreserveaspectratioXmaxymid; - else + } else { return SVGParsingError(SVGParseStatus::kExpectedEnumeration, - ptr - start); - } else if (ptr[6] == 'a' && ptr[7] == 'x') { + span_inout.data() - start); + } + } else if (span_inout[6] == 'a' && span_inout[7] == 'x') { align = kSvgPreserveaspectratioXmaxymax; } else { return SVGParsingError(SVGParseStatus::kExpectedEnumeration, - ptr - start); + span_inout.data() - start); } } else { - return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start); + return SVGParsingError(SVGParseStatus::kExpectedEnumeration, + span_inout.data() - start); } - ptr += 8; - SkipOptionalSVGSpaces(ptr, end); + span_inout = span_inout.subspan(8ul); + SkipOptionalSVGSpaces(span_inout); } else { - return SVGParsingError(SVGParseStatus::kExpectedEnumeration, ptr - start); + return SVGParsingError(SVGParseStatus::kExpectedEnumeration, + span_inout.data() - start); } - if (ptr < end) { - if (*ptr == 'm') { - if (!SkipToken(ptr, end, "meet")) + if (!span_inout.empty()) { + if (span_inout[0] == 'm') { + if (!SkipToken(span_inout, "meet")) { return SVGParsingError(SVGParseStatus::kExpectedEnumeration, - ptr - start); - SkipOptionalSVGSpaces(ptr, end); - } else if (*ptr == 's') { - if (!SkipToken(ptr, end, "slice")) + span_inout.data() - start); + } + SkipOptionalSVGSpaces(span_inout); + } else if (span_inout[0] == 's') { + if (!SkipToken(span_inout, "slice")) { return SVGParsingError(SVGParseStatus::kExpectedEnumeration, - ptr - start); - SkipOptionalSVGSpaces(ptr, end); + span_inout.data() - start); + } + SkipOptionalSVGSpaces(span_inout); if (align != kSvgPreserveaspectratioNone) meet_or_slice = kSvgMeetorsliceSlice; } } - if (end != ptr && validate) - return SVGParsingError(SVGParseStatus::kTrailingGarbage, ptr - start); + if (!span_inout.empty() && validate) { + return SVGParsingError(SVGParseStatus::kTrailingGarbage, + span_inout.data() - start); + } SetAlign(align); SetMeetOrSlice(meet_or_slice); @@ -170,22 +184,18 @@ if (string.empty()) return SVGParseStatus::kNoError; - return WTF::VisitCharacters(string, [&](auto chars) { - const auto* start = chars.data(); - return ParseInternal(start, start + chars.size(), true); - }); + return WTF::VisitCharacters( + string, [&](auto chars) { return ParseInternal(chars, true); }); } -bool SVGPreserveAspectRatio::Parse(const LChar*& ptr, - const LChar* end, +bool SVGPreserveAspectRatio::Parse(base::span<const UChar>& span, bool validate) { - return ParseInternal(ptr, end, validate) == SVGParseStatus::kNoError; + return ParseInternal(span, validate) == SVGParseStatus::kNoError; } -bool SVGPreserveAspectRatio::Parse(const UChar*& ptr, - const UChar* end, +bool SVGPreserveAspectRatio::Parse(base::span<const LChar>& span, bool validate) { - return ParseInternal(ptr, end, validate) == SVGParseStatus::kNoError; + return ParseInternal(span, validate) == SVGParseStatus::kNoError; } void SVGPreserveAspectRatio::TransformRect(gfx::RectF& dest_rect,
diff --git a/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.h b/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.h index 7c7a3385..a5804e3 100644 --- a/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.h +++ b/third_party/blink/renderer/core/svg/svg_preserve_aspect_ratio.h
@@ -83,8 +83,8 @@ String ValueAsString() const override; SVGParsingError SetValueAsString(const String&); - bool Parse(const UChar*& ptr, const UChar* end, bool validate); - bool Parse(const LChar*& ptr, const LChar* end, bool validate); + bool Parse(base::span<const UChar>& span, bool validate); + bool Parse(base::span<const LChar>& span, bool validate); void Add(const SVGPropertyBase*, const SVGElement*) override; void CalculateAnimatedValue( @@ -107,9 +107,7 @@ private: template <typename CharType> - SVGParsingError ParseInternal(const CharType*& ptr, - const CharType* end, - bool validate); + SVGParsingError ParseInternal(base::span<CharType>& span, bool validate); SVGPreserveAspectRatioType align_; SVGMeetOrSliceType meet_or_slice_;
diff --git a/third_party/blink/renderer/core/svg/svg_view_spec.cc b/third_party/blink/renderer/core/svg/svg_view_spec.cc index 5ec396a..b1ecf8b 100644 --- a/third_party/blink/renderer/core/svg/svg_view_spec.cc +++ b/third_party/blink/renderer/core/svg/svg_view_spec.cc
@@ -19,6 +19,7 @@ #include "third_party/blink/renderer/core/svg/svg_view_spec.h" +#include "base/containers/span.h" #include "third_party/blink/renderer/core/svg/svg_animated_preserve_aspect_ratio.h" #include "third_party/blink/renderer/core/svg/svg_animated_rect.h" #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h" @@ -168,9 +169,14 @@ case kPreserveAspectRatio: { auto* preserve_aspect_ratio = MakeGarbageCollected<SVGPreserveAspectRatio>(); - if (!preserve_aspect_ratio->Parse(ptr, end, false)) { + // TODO(crbug.com/351564777): This file is relying on the + // behavior of the `preserve_aspect_ratio->Parse` mutating the + // first argument. Set ptr to span.data() for now. + auto span = UNSAFE_TODO(base::span(ptr, end)); + if (!preserve_aspect_ratio->Parse(span, false)) { return false; } + ptr = span.data(); preserve_aspect_ratio_ = preserve_aspect_ratio; break; }
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc index ffb929cd..9ba6b9e 100644 --- a/third_party/blink/renderer/core/testing/internals.cc +++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -24,11 +24,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "third_party/blink/renderer/core/testing/internals.h" #include <atomic> @@ -3152,7 +3147,7 @@ DOMArrayBuffer* buffer = DOMArrayBuffer::CreateUninitializedOrNull( base::checked_cast<uint32_t>(span.size()), sizeof(uint8_t)); if (buffer) - memcpy(buffer->Data(), span.data(), span.size()); + buffer->ByteSpan().copy_from(span); return buffer; }
diff --git a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc index 3a62ec8..a062d70 100644 --- a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc +++ b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc
@@ -24,11 +24,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h" #include <cstring> @@ -81,12 +76,14 @@ auto deleter = [](void* buffer, size_t length, void* data) { size_t offset = reinterpret_cast<uintptr_t>(buffer) % base::SysInfo::VMAllocationGranularity(); - uint8_t* base = static_cast<uint8_t*>(buffer) - offset; - auto mapping = base::span(base, length + offset); + // SAFETY: Memory that was allocated is VMAllocationGranularity() aligned. + // the overallocated bytes are at the beginning of the buffer. + uint8_t* base = UNSAFE_BUFFERS(static_cast<uint8_t*>(buffer) - offset); + auto mapping = UNSAFE_BUFFERS(base::span(base, length + offset)); auto* mapper = gin::GetSharedMemoryMapperForArrayBuffers(); base::subtle::PlatformSharedMemoryRegion::Unmap(mapping, mapper); }; - void* base = result.value().data() + offset_rounding; + uint8_t* base = &result.value()[offset_rounding]; backing_store_ = v8::ArrayBuffer::NewBackingStore(base, length, deleter, nullptr); } @@ -211,7 +208,7 @@ DataLength(), 1, IsShared() ? kShared : kNotShared, kDontInitialize); if (!IsValid() || !other.IsValid()) return; - std::memcpy(other.Data(), Data(), DataLength()); + other.ByteSpan().copy_from(ByteSpan()); } template <partition_alloc::AllocFlags flags>
diff --git a/third_party/blink/renderer/modules/webgl/BUILD.gn b/third_party/blink/renderer/modules/webgl/BUILD.gn index 54d8f85..29cc583 100644 --- a/third_party/blink/renderer/modules/webgl/BUILD.gn +++ b/third_party/blink/renderer/modules/webgl/BUILD.gn
@@ -104,6 +104,10 @@ "webgl_context_attribute_helpers.h", "webgl_context_event.cc", "webgl_context_event.h", + "webgl_context_group.cc", + "webgl_context_group.h", + "webgl_context_object.cc", + "webgl_context_object.h", "webgl_debug_renderer_info.cc", "webgl_debug_renderer_info.h", "webgl_debug_shaders.cc", @@ -155,6 +159,10 @@ "webgl_shader_pixel_local_storage.h", "webgl_shader_precision_format.cc", "webgl_shader_precision_format.h", + "webgl_shared_object.cc", + "webgl_shared_object.h", + "webgl_shared_platform_3d_object.cc", + "webgl_shared_platform_3d_object.h", "webgl_stencil_texturing.cc", "webgl_stencil_texturing.h", "webgl_sync.cc",
diff --git a/third_party/blink/renderer/modules/webgl/ext_disjoint_timer_query.cc b/third_party/blink/renderer/modules/webgl/ext_disjoint_timer_query.cc index ac20a1a0..79805cf1 100644 --- a/third_party/blink/renderer/modules/webgl/ext_disjoint_timer_query.cc +++ b/third_party/blink/renderer/modules/webgl/ext_disjoint_timer_query.cc
@@ -38,7 +38,7 @@ if (!query || scoped.IsLost()) return; - if (!query->Validate(scoped.Context())) { + if (!query->Validate(nullptr, scoped.Context())) { scoped.Context()->SynthesizeGLError( GL_INVALID_OPERATION, "delete", "object does not belong to this context"); @@ -61,7 +61,7 @@ bool EXTDisjointTimerQuery::isQueryEXT(WebGLTimerQueryEXT* query) { WebGLExtensionScopedContext scoped(this); if (!query || scoped.IsLost() || query->MarkedForDeletion() || - !query->Validate(scoped.Context())) { + !query->Validate(nullptr, scoped.Context())) { return false; }
diff --git a/third_party/blink/renderer/modules/webgl/oes_vertex_array_object.cc b/third_party/blink/renderer/modules/webgl/oes_vertex_array_object.cc index 82cec872..ff038d2 100644 --- a/third_party/blink/renderer/modules/webgl/oes_vertex_array_object.cc +++ b/third_party/blink/renderer/modules/webgl/oes_vertex_array_object.cc
@@ -65,7 +65,8 @@ // ValidateWebGLObject generates an error if the object has already been // deleted, so we must replicate most of its checks here. - if (!array_object->Validate(scoped.Context())) { + if (!array_object->Validate(scoped.Context()->ContextGroup(), + scoped.Context())) { scoped.Context()->SynthesizeGLError( GL_INVALID_OPERATION, "deleteVertexArrayOES", "object does not belong to this context"); @@ -86,9 +87,9 @@ WebGLVertexArrayObjectOES* array_object) { WebGLExtensionScopedContext scoped(this); if (scoped.IsLost() || !array_object || - !array_object->Validate(scoped.Context())) { + !array_object->Validate(scoped.Context()->ContextGroup(), + scoped.Context())) return false; - } if (!array_object->HasEverBeenBound()) return false;
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc index 8895174..7d7335b 100644 --- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc +++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
@@ -3539,9 +3539,8 @@ } bool WebGL2RenderingContextBase::isQuery(WebGLQuery* query) { - if (!query || isContextLost() || !query->Validate(this)) { + if (!query || isContextLost() || !query->Validate(ContextGroup(), this)) return false; - } if (query->MarkedForDeletion()) return false; @@ -3758,9 +3757,8 @@ } bool WebGL2RenderingContextBase::isSampler(WebGLSampler* sampler) { - if (!sampler || isContextLost() || !sampler->Validate(this)) { + if (!sampler || isContextLost() || !sampler->Validate(ContextGroup(), this)) return false; - } if (sampler->MarkedForDeletion()) return false; @@ -3967,9 +3965,8 @@ } bool WebGL2RenderingContextBase::isSync(WebGLSync* sync) { - if (!sync || isContextLost() || !sync->Validate(this)) { + if (!sync || isContextLost() || !sync->Validate(ContextGroup(), this)) return false; - } if (sync->MarkedForDeletion()) return false; @@ -4065,7 +4062,8 @@ WebGLTransformFeedback* feedback) { // We have to short-circuit the deletion process if the transform feedback is // active. This requires duplication of some validation logic. - if (!isContextLost() && feedback && feedback->Validate(this)) { + if (!isContextLost() && feedback && + feedback->Validate(ContextGroup(), this)) { if (feedback->active()) { SynthesizeGLError( GL_INVALID_OPERATION, "deleteTransformFeedback", @@ -4083,9 +4081,8 @@ bool WebGL2RenderingContextBase::isTransformFeedback( WebGLTransformFeedback* feedback) { - if (!feedback || isContextLost() || !feedback->Validate(this)) { + if (!feedback || isContextLost() || !feedback->Validate(ContextGroup(), this)) return false; - } if (!feedback->HasEverBeenBound()) return false; @@ -4695,7 +4692,7 @@ // deleted, so we must replicate most of its checks here. if (isContextLost() || !vertex_array) return; - if (!vertex_array->Validate(this)) { + if (!vertex_array->Validate(ContextGroup(), this)) { SynthesizeGLError(GL_INVALID_OPERATION, "deleteVertexArray", "object does not belong to this context"); return; @@ -4712,9 +4709,9 @@ bool WebGL2RenderingContextBase::isVertexArray( WebGLVertexArrayObject* vertex_array) { - if (isContextLost() || !vertex_array || !vertex_array->Validate(this)) { + if (isContextLost() || !vertex_array || + !vertex_array->Validate(ContextGroup(), this)) return false; - } if (!vertex_array->HasEverBeenBound()) return false; @@ -5472,11 +5469,11 @@ } } - WebGLObject* attachment_object = nullptr; + WebGLSharedObject* attachment_object = nullptr; if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { - WebGLObject* depth_attachment = + WebGLSharedObject* depth_attachment = framebuffer_binding->GetAttachmentObject(GL_DEPTH_ATTACHMENT); - WebGLObject* stencil_attachment = + WebGLSharedObject* stencil_attachment = framebuffer_binding->GetAttachmentObject(GL_STENCIL_ATTACHMENT); if (depth_attachment != stencil_attachment) { SynthesizeGLError(
diff --git a/third_party/blink/renderer/modules/webgl/webgl_buffer.cc b/third_party/blink/renderer/modules/webgl/webgl_buffer.cc index a567d13..e6f0ffc1 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_buffer.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_buffer.cc
@@ -31,7 +31,7 @@ namespace blink { WebGLBuffer::WebGLBuffer(WebGLRenderingContextBase* ctx) - : WebGLObject(ctx), initial_target_(0), size_(0) { + : WebGLSharedPlatform3DObject(ctx), initial_target_(0), size_(0) { if (!ctx->isContextLost()) { GLuint buffer; ctx->ContextGL()->GenBuffers(1, &buffer); @@ -42,7 +42,8 @@ WebGLBuffer::~WebGLBuffer() = default; void WebGLBuffer::DeleteObjectImpl(gpu::gles2::GLES2Interface* gl) { - gl->DeleteBuffers(1, &Object()); + gl->DeleteBuffers(1, &object_); + object_ = 0; } void WebGLBuffer::SetInitialTarget(GLenum target) {
diff --git a/third_party/blink/renderer/modules/webgl/webgl_buffer.h b/third_party/blink/renderer/modules/webgl/webgl_buffer.h index 2f195497..b524381 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_buffer.h +++ b/third_party/blink/renderer/modules/webgl/webgl_buffer.h
@@ -26,11 +26,11 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_BUFFER_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_BUFFER_H_ -#include "third_party/blink/renderer/modules/webgl/webgl_object.h" +#include "third_party/blink/renderer/modules/webgl/webgl_shared_platform_3d_object.h" namespace blink { -class WebGLBuffer final : public WebGLObject { +class WebGLBuffer final : public WebGLSharedPlatform3DObject { DEFINE_WRAPPERTYPEINFO(); public: @@ -49,6 +49,8 @@ void DeleteObjectImpl(gpu::gles2::GLES2Interface*) override; private: + bool IsBuffer() const override { return true; } + GLenum initial_target_; int64_t size_; };
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_group.cc b/third_party/blink/renderer/modules/webgl/webgl_context_group.cc new file mode 100644 index 0000000..a054373 --- /dev/null +++ b/third_party/blink/renderer/modules/webgl/webgl_context_group.cc
@@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "third_party/blink/renderer/modules/webgl/webgl_context_group.h" + +namespace blink { + +WebGLContextGroup::WebGLContextGroup() : number_of_context_losses_(0) {} + +gpu::gles2::GLES2Interface* WebGLContextGroup::GetAGLInterface() { + DCHECK(!contexts_.empty()); + return (*contexts_.begin())->ContextGL(); +} + +void WebGLContextGroup::AddContext(WebGLRenderingContextBase* context) { + contexts_.insert(context); +} + +void WebGLContextGroup::LoseContextGroup( + WebGLRenderingContextBase::LostContextMode mode, + WebGLRenderingContextBase::AutoRecoveryMethod auto_recovery_method) { + ++number_of_context_losses_; + for (WebGLRenderingContextBase* const context : contexts_) + context->LoseContextImpl(mode, auto_recovery_method); +} + +uint32_t WebGLContextGroup::NumberOfContextLosses() const { + return number_of_context_losses_; +} + +} // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_group.h b/third_party/blink/renderer/modules/webgl/webgl_context_group.h new file mode 100644 index 0000000..4829553 --- /dev/null +++ b/third_party/blink/renderer/modules/webgl/webgl_context_group.h
@@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_CONTEXT_GROUP_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_CONTEXT_GROUP_H_ + +#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h" +#include "third_party/blink/renderer/platform/bindings/name_client.h" +#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h" + +namespace blink { + +class WebGLContextGroup final : public GarbageCollected<WebGLContextGroup>, + public NameClient { + public: + WebGLContextGroup(); + + WebGLContextGroup(const WebGLContextGroup&) = delete; + WebGLContextGroup& operator=(const WebGLContextGroup&) = delete; + + ~WebGLContextGroup() final = default; + + void AddContext(WebGLRenderingContextBase*); + + // There's no point in having a removeContext method any more now that + // the context group is GarbageCollected. The only time it would be + // called would be during WebGLRenderingContext destruction, and at that + // time, the context is not allowed to refer back to the context group + // since both are on the Oilpan heap. + + gpu::gles2::GLES2Interface* GetAGLInterface(); + + void LoseContextGroup(WebGLRenderingContextBase::LostContextMode, + WebGLRenderingContextBase::AutoRecoveryMethod); + + // This counter gets incremented every time context loss is + // triggered. Because there's no longer any explicit enumeration of + // the objects in a given context group upon context loss, each + // object needs to keep track of the context loss count when it was + // created, in order to validate itself. + uint32_t NumberOfContextLosses() const; + + void Trace(Visitor* visitor) const { visitor->Trace(contexts_); } + const char* NameInHeapSnapshot() const override { + return "WebGLContextGroup"; + } + + private: + friend class WebGLObject; + + uint32_t number_of_context_losses_; + + HeapHashSet<Member<WebGLRenderingContextBase>> contexts_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_CONTEXT_GROUP_H_
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_object.cc b/third_party/blink/renderer/modules/webgl/webgl_context_object.cc new file mode 100644 index 0000000..6d2642e --- /dev/null +++ b/third_party/blink/renderer/modules/webgl/webgl_context_object.cc
@@ -0,0 +1,66 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "third_party/blink/renderer/modules/webgl/webgl_context_object.h" + +#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h" + +namespace blink { + +WebGLContextObject::WebGLContextObject(WebGLRenderingContextBase* context) + : WebGLObject(context), context_(context) {} + +bool WebGLContextObject::Validate( + const WebGLContextGroup*, + const WebGLRenderingContextBase* context) const { + // The contexts and context groups no longer maintain references to all + // the objects they ever created, so there's no way to invalidate them + // eagerly during context loss. The invalidation is discovered lazily. + return (context == context_ && context_ != nullptr && + CachedNumberOfContextLosses() == context->NumberOfContextLosses()); +} + +uint32_t WebGLContextObject::CurrentNumberOfContextLosses() const { + if (!context_) { + return 0; + } + + return context_->NumberOfContextLosses(); +} + +gpu::gles2::GLES2Interface* WebGLContextObject::GetAGLInterface() const { + if (!context_) { + return nullptr; + } + + return context_->ContextGL(); +} + +void WebGLContextObject::Trace(Visitor* visitor) const { + visitor->Trace(context_); + WebGLObject::Trace(visitor); +} + +} // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_context_object.h b/third_party/blink/renderer/modules/webgl/webgl_context_object.h new file mode 100644 index 0000000..c3b4e1b --- /dev/null +++ b/third_party/blink/renderer/modules/webgl/webgl_context_object.h
@@ -0,0 +1,61 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_CONTEXT_OBJECT_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_CONTEXT_OBJECT_H_ + +#include "third_party/blink/renderer/modules/webgl/webgl_object.h" + +namespace blink { + +class WebGLRenderingContextBase; + +// WebGLContextObject the base class for objects that are owned by a specific +// WebGLRenderingContextBase. +class WebGLContextObject : public WebGLObject { + public: + WebGLRenderingContextBase* Context() const { return context_.Get(); } + + bool Validate(const WebGLContextGroup*, + const WebGLRenderingContextBase*) const final; + + void Trace(Visitor*) const override; + + protected: + explicit WebGLContextObject(WebGLRenderingContextBase*); + + bool HasGroupOrContext() const final { return context_ != nullptr; } + + uint32_t CurrentNumberOfContextLosses() const final; + + gpu::gles2::GLES2Interface* GetAGLInterface() const final; + + private: + Member<WebGLRenderingContextBase> context_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_CONTEXT_OBJECT_H_
diff --git a/third_party/blink/renderer/modules/webgl/webgl_framebuffer.cc b/third_party/blink/renderer/modules/webgl/webgl_framebuffer.cc index 9b37698..ce656f7 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_framebuffer.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_framebuffer.cc
@@ -47,8 +47,8 @@ const char* NameInHeapSnapshot() const override { return "WebGLAttachment"; } private: - WebGLObject* Object() const override; - bool IsObject(WebGLObject*) const override; + WebGLSharedObject* Object() const override; + bool IsSharedObject(WebGLSharedObject*) const override; bool Valid() const override; void OnDetached(gpu::gles2::GLES2Interface*) override; void Attach(gpu::gles2::GLES2Interface*, @@ -70,11 +70,12 @@ WebGLRenderbuffer* renderbuffer) : renderbuffer_(renderbuffer) {} -WebGLObject* WebGLRenderbufferAttachment::Object() const { +WebGLSharedObject* WebGLRenderbufferAttachment::Object() const { return renderbuffer_->Object() ? renderbuffer_.Get() : nullptr; } -bool WebGLRenderbufferAttachment::IsObject(WebGLObject* object) const { +bool WebGLRenderbufferAttachment::IsSharedObject( + WebGLSharedObject* object) const { return object == renderbuffer_; } @@ -116,8 +117,8 @@ } private: - WebGLObject* Object() const override; - bool IsObject(WebGLObject*) const override; + WebGLSharedObject* Object() const override; + bool IsSharedObject(WebGLSharedObject*) const override; bool Valid() const override; void OnDetached(gpu::gles2::GLES2Interface*) override; void Attach(gpu::gles2::GLES2Interface*, @@ -144,11 +145,11 @@ GLint layer) : texture_(texture), target_(target), level_(level), layer_(layer) {} -WebGLObject* WebGLTextureAttachment::Object() const { +WebGLSharedObject* WebGLTextureAttachment::Object() const { return texture_->Object() ? texture_.Get() : nullptr; } -bool WebGLTextureAttachment::IsObject(WebGLObject* object) const { +bool WebGLTextureAttachment::IsSharedObject(WebGLSharedObject* object) const { return object == texture_; } @@ -207,15 +208,14 @@ } WebGLFramebuffer::WebGLFramebuffer(WebGLRenderingContextBase* ctx, bool opaque) - : WebGLObject(ctx), + : WebGLContextObject(ctx), + object_(0), has_ever_been_bound_(false), web_gl1_depth_stencil_consistent_(true), opaque_(opaque), read_buffer_(GL_COLOR_ATTACHMENT0) { if (!ctx->isContextLost()) { - GLuint fbo; - ctx->ContextGL()->GenFramebuffers(1, &fbo); - SetObject(fbo); + ctx->ContextGL()->GenFramebuffers(1, &object_); } } @@ -228,7 +228,7 @@ GLint level, GLint layer, GLsizei num_views) { - DCHECK(HasObject()); + DCHECK(object_); DCHECK(IsBound(target)); if (Context()->isContextLost()) { @@ -291,7 +291,7 @@ GLenum target, GLenum attachment, WebGLRenderbuffer* renderbuffer) { - DCHECK(HasObject()); + DCHECK(object_); DCHECK(IsBound(target)); if (Context()->isContextLost()) { @@ -323,10 +323,10 @@ } } -WebGLObject* WebGLFramebuffer::GetAttachmentObject(GLenum attachment) const { - if (!HasObject()) { +WebGLSharedObject* WebGLFramebuffer::GetAttachmentObject( + GLenum attachment) const { + if (!object_) return nullptr; - } WebGLAttachment* attachment_object = GetAttachment(attachment); return attachment_object ? attachment_object->Object() : nullptr; } @@ -339,11 +339,10 @@ void WebGLFramebuffer::RemoveAttachmentFromBoundFramebuffer( GLenum target, - WebGLObject* attachment) { + WebGLSharedObject* attachment) { DCHECK(IsBound(target)); - if (!HasObject()) { + if (!object_) return; - } if (!attachment) return; @@ -354,7 +353,7 @@ check_more = false; for (const auto& it : attachments_) { WebGLAttachment* attachment_object = it.value.Get(); - if (attachment_object->IsObject(attachment)) { + if (attachment_object->IsSharedObject(attachment)) { GLenum attachment_type = it.key; switch (attachment_type) { case GL_DEPTH_ATTACHMENT: @@ -435,7 +434,8 @@ } // "gl" is null-checked at higher levels. - gl->DeleteFramebuffers(1, &Object()); + gl->DeleteFramebuffers(1, &object_); + object_ = 0; } bool WebGLFramebuffer::IsBound(GLenum target) const { @@ -452,7 +452,7 @@ default: NOTREACHED(); } - return GLuint(bound_fbo) == Object(); + return GLuint(bound_fbo) == object_; } void WebGLFramebuffer::DrawBuffers(const Vector<GLenum>& bufs) { @@ -499,7 +499,7 @@ GLint level, GLint layer) { DCHECK(IsBound(target)); - DCHECK(HasObject()); + DCHECK(object_); RemoveAttachmentInternal(target, attachment); if (texture && texture->Object()) { attachments_.insert(attachment, @@ -514,7 +514,7 @@ GLenum attachment, WebGLRenderbuffer* renderbuffer) { DCHECK(IsBound(target)); - DCHECK(HasObject()); + DCHECK(object_); RemoveAttachmentInternal(target, attachment); if (renderbuffer && renderbuffer->Object()) { attachments_.insert( @@ -528,7 +528,7 @@ void WebGLFramebuffer::RemoveAttachmentInternal(GLenum target, GLenum attachment) { DCHECK(IsBound(target)); - DCHECK(HasObject()); + DCHECK(object_); WebGLAttachment* attachment_object = GetAttachment(attachment); if (attachment_object) { @@ -639,7 +639,7 @@ void WebGLFramebuffer::Trace(Visitor* visitor) const { visitor->Trace(attachments_); visitor->Trace(pls_textures_); - WebGLObject::Trace(visitor); + WebGLContextObject::Trace(visitor); } } // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_framebuffer.h b/third_party/blink/renderer/modules/webgl/webgl_framebuffer.h index b56d82b..f4fb774 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_framebuffer.h +++ b/third_party/blink/renderer/modules/webgl/webgl_framebuffer.h
@@ -26,7 +26,8 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_FRAMEBUFFER_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_FRAMEBUFFER_H_ -#include "third_party/blink/renderer/modules/webgl/webgl_object.h" +#include "third_party/blink/renderer/modules/webgl/webgl_context_object.h" +#include "third_party/blink/renderer/modules/webgl/webgl_shared_object.h" #include "third_party/blink/renderer/platform/bindings/name_client.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h" #include "third_party/blink/renderer/platform/wtf/vector.h" @@ -42,7 +43,7 @@ class WebGLRenderbuffer; class WebGLTexture; -class WebGLFramebuffer final : public WebGLObject { +class WebGLFramebuffer final : public WebGLContextObject { DEFINE_WRAPPERTYPEINFO(); public: @@ -51,8 +52,8 @@ public: ~WebGLAttachment() override = default; - virtual WebGLObject* Object() const = 0; - virtual bool IsObject(WebGLObject*) const = 0; + virtual WebGLSharedObject* Object() const = 0; + virtual bool IsSharedObject(WebGLSharedObject*) const = 0; virtual bool Valid() const = 0; virtual void OnDetached(gpu::gles2::GLES2Interface*) = 0; virtual void Attach(gpu::gles2::GLES2Interface*, @@ -78,6 +79,8 @@ bool has_depth, bool has_stencil); + GLuint Object() const { return object_; } + // For a non-multiview attachment, set the num_views parameter to 0. For a // multiview attachment, set the layer to the base view index. void SetAttachmentForBoundFramebuffer(GLenum target, @@ -91,8 +94,8 @@ GLenum attachment, WebGLRenderbuffer*); // If an object is attached to the currently bound framebuffer, remove it. - void RemoveAttachmentFromBoundFramebuffer(GLenum target, WebGLObject*); - WebGLObject* GetAttachmentObject(GLenum) const; + void RemoveAttachmentFromBoundFramebuffer(GLenum target, WebGLSharedObject*); + WebGLSharedObject* GetAttachmentObject(GLenum) const; // WebGL 1 specific: // 1) can't allow depth_stencil for depth/stencil attachments, and vice @@ -134,6 +137,7 @@ const char* NameInHeapSnapshot() const override { return "WebGLFramebuffer"; } protected: + bool HasObject() const override { return object_ != 0; } void DeleteObjectImpl(gpu::gles2::GLES2Interface*) override; private: @@ -161,6 +165,8 @@ void CommitWebGL1DepthStencilIfConsistent(GLenum target); + GLuint object_; + typedef HeapHashMap<GLenum, Member<WebGLAttachment>> AttachmentMap; AttachmentMap attachments_;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_object.cc b/third_party/blink/renderer/modules/webgl/webgl_object.cc index b2f14157..c8354fd 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_object.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_object.cc
@@ -32,29 +32,19 @@ namespace blink { WebGLObject::WebGLObject(WebGLRenderingContextBase* context) - : context_(context), - cached_number_of_context_losses_(std::numeric_limits<uint32_t>::max()) { - if (context_) { + : cached_number_of_context_losses_(std::numeric_limits<uint32_t>::max()), + attachment_count_(0), + marked_for_deletion_(false), + destruction_in_progress_(false) { + if (context) { cached_number_of_context_losses_ = context->NumberOfContextLosses(); } } WebGLObject::~WebGLObject() = default; -bool WebGLObject::Validate(const WebGLRenderingContextBase* context) const { - // The contexts and context groups no longer maintain references to all - // the objects they ever created, so there's no way to invalidate them - // eagerly during context loss. The invalidation is discovered lazily. - return (context == context_ && context_ != nullptr && - cached_number_of_context_losses_ == context->NumberOfContextLosses()); -} - -void WebGLObject::SetObject(GLuint object) { - // SetObject may only be called when this container is in the - // uninitialized state: object==0 && marked_for_deletion==false. - DCHECK(!object_); - DCHECK(!MarkedForDeletion()); - object_ = object; +uint32_t WebGLObject::CachedNumberOfContextLosses() const { + return cached_number_of_context_losses_; } void WebGLObject::DeleteObject(gpu::gles2::GLES2Interface* gl) { @@ -62,21 +52,21 @@ if (!HasObject()) return; - if (!context_) { + if (!HasGroupOrContext()) return; - } - if (context_->NumberOfContextLosses() != cached_number_of_context_losses_) { + if (CurrentNumberOfContextLosses() != cached_number_of_context_losses_) { // This object has been invalidated. return; } if (!attachment_count_) { if (!gl) - gl = context_->ContextGL(); + gl = GetAGLInterface(); if (gl) { DeleteObjectImpl(gl); - object_ = 0; + // Ensure the inherited class no longer claims to have a valid object + DCHECK(!HasObject()); } } } @@ -112,9 +102,4 @@ DeleteObject(gl); } -void WebGLObject::Trace(Visitor* visitor) const { - visitor->Trace(context_); - ScriptWrappable::Trace(visitor); -} - } // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_object.h b/third_party/blink/renderer/modules/webgl/webgl_object.h index d11d1cb..25afec6 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_object.h +++ b/third_party/blink/renderer/modules/webgl/webgl_object.h
@@ -39,6 +39,7 @@ namespace blink { +class WebGLContextGroup; class WebGLRenderingContextBase; template <typename T> @@ -74,10 +75,9 @@ // subclasses via Dispose(). ~WebGLObject() override; - WebGLRenderingContextBase* Context() const { return context_.Get(); } - // deleteObject may not always delete the OpenGL resource. For programs and // shaders, deletion is delayed until they are no longer attached. + // FIXME: revisit this when resource sharing between contexts are implemented. void DeleteObject(gpu::gles2::GLES2Interface*); void OnAttached() { ++attachment_count_; } @@ -89,29 +89,31 @@ bool MarkedForDeletion() { return marked_for_deletion_; } // True if this object belongs to the group or context. - bool Validate(const WebGLRenderingContextBase*) const; - - // A reference is returned so it can be made a pointer for glDelete* calls - const GLuint& Object() const { return object_; } - bool HasObject() const { return object_ != 0; } - - virtual bool IsRenderbuffer() const { return false; } - virtual bool IsTexture() const { return false; } - - void Trace(Visitor*) const override; + virtual bool Validate(const WebGLContextGroup*, + const WebGLRenderingContextBase*) const = 0; + virtual bool HasObject() const = 0; protected: explicit WebGLObject(WebGLRenderingContextBase*); - // Must be called only once to set the GL object this JS wrapper wraps. - void SetObject(GLuint object); // deleteObjectImpl should be only called once to delete the OpenGL resource. // After calling deleteObjectImpl, hasObject() should return false. virtual void DeleteObjectImpl(gpu::gles2::GLES2Interface*) = 0; + virtual bool HasGroupOrContext() const = 0; + + // Return the current number of context losses associated with this + // object's context group (if it's a shared object), or its + // context's context group (if it's a per-context object). + virtual uint32_t CurrentNumberOfContextLosses() const = 0; + + uint32_t CachedNumberOfContextLosses() const; + void Detach(); void DetachAndDeleteObject(); + virtual gpu::gles2::GLES2Interface* GetAGLInterface() const = 0; + // Runs the pre-finalization sequence -- what would be in the destructor // of the base class, if it could be. Must be called no more than once. void Dispose(); @@ -120,25 +122,23 @@ bool DestructionInProgress() const; private: - Member<WebGLRenderingContextBase> context_; - - GLuint object_ = 0; - // This was the number of context losses of the object's associated - // WebGLContext at the time this object was created. + // WebGLContextGroup at the time this object was created. Contexts + // no longer refer to all the objects that they ever created, so + // it's necessary to check this count when validating each object. uint32_t cached_number_of_context_losses_; - unsigned attachment_count_ = 0; + unsigned attachment_count_; // Indicates whether the WebGL context's deletion function for this object // (deleteBuffer, deleteTexture, etc.) has been called. It does *not* indicate // whether the underlying OpenGL resource has been destroyed; !HasObject() // indicates that. - bool marked_for_deletion_ = false; + bool marked_for_deletion_; // Indicates whether the destructor has been entered and we therefore // need to be careful in subclasses to not touch other on-heap objects. - bool destruction_in_progress_ = false; + bool destruction_in_progress_; }; } // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_program.cc b/third_party/blink/renderer/modules/webgl/webgl_program.cc index 0acc2d7..2da14aeb 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_program.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_program.cc
@@ -26,12 +26,13 @@ #include "third_party/blink/renderer/modules/webgl/webgl_program.h" #include "gpu/command_buffer/client/gles2_interface.h" +#include "third_party/blink/renderer/modules/webgl/webgl_context_group.h" #include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h" namespace blink { WebGLProgram::WebGLProgram(WebGLRenderingContextBase* ctx) - : WebGLObject(ctx), + : WebGLSharedPlatform3DObject(ctx), link_status_(false), link_count_(0), active_transform_feedback_count_(0), @@ -46,7 +47,8 @@ WebGLProgram::~WebGLProgram() = default; void WebGLProgram::DeleteObjectImpl(gpu::gles2::GLES2Interface* gl) { - gl->DeleteProgram(Object()); + gl->DeleteProgram(object_); + object_ = 0; if (!DestructionInProgress()) { if (vertex_shader_) { vertex_shader_->OnDetached(gl); @@ -69,7 +71,7 @@ gpu::gles2::GLES2Interface* gl = context->ContextGL(); // If gl is nullptr, context has been lost. if (gl) { - gl->GetProgramiv(Object(), GL_COMPLETION_STATUS_KHR, &completed); + gl->GetProgramiv(object_, GL_COMPLETION_STATUS_KHR, &completed); } return completed; @@ -140,16 +142,15 @@ void WebGLProgram::CacheInfoIfNeeded(WebGLRenderingContextBase* context) { if (info_valid_) return; - if (!HasObject()) { + if (!object_) return; - } gpu::gles2::GLES2Interface* gl = context->ContextGL(); if (!gl) { // Context has been lost. return; } GLint link_status = 0; - gl->GetProgramiv(Object(), GL_LINK_STATUS, &link_status); + gl->GetProgramiv(object_, GL_LINK_STATUS, &link_status); setLinkStatus(link_status); } @@ -168,7 +169,7 @@ void WebGLProgram::Trace(Visitor* visitor) const { visitor->Trace(vertex_shader_); visitor->Trace(fragment_shader_); - WebGLObject::Trace(visitor); + WebGLSharedPlatform3DObject::Trace(visitor); } } // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_program.h b/third_party/blink/renderer/modules/webgl/webgl_program.h index f49c5ba..784b77eb0 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_program.h +++ b/third_party/blink/renderer/modules/webgl/webgl_program.h
@@ -26,12 +26,12 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_PROGRAM_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_PROGRAM_H_ -#include "third_party/blink/renderer/modules/webgl/webgl_object.h" #include "third_party/blink/renderer/modules/webgl/webgl_shader.h" +#include "third_party/blink/renderer/modules/webgl/webgl_shared_platform_3d_object.h" namespace blink { -class WebGLProgram final : public WebGLObject { +class WebGLProgram final : public WebGLSharedPlatform3DObject { DEFINE_WRAPPERTYPEINFO(); public: @@ -76,6 +76,8 @@ void DeleteObjectImpl(gpu::gles2::GLES2Interface*) override; private: + bool IsProgram() const override { return true; } + void CacheInfoIfNeeded(WebGLRenderingContextBase*); GLint link_status_;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_query.cc b/third_party/blink/renderer/modules/webgl/webgl_query.cc index 161e09bd..8955420 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_query.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_query.cc
@@ -12,7 +12,7 @@ namespace blink { WebGLQuery::WebGLQuery(WebGL2RenderingContextBase* ctx) - : WebGLObject(ctx), + : WebGLSharedPlatform3DObject(ctx), target_(0), can_update_availability_(false), query_result_available_(false), @@ -28,13 +28,14 @@ WebGLQuery::~WebGLQuery() = default; void WebGLQuery::SetTarget(GLenum target) { - DCHECK(HasObject()); + DCHECK(Object()); DCHECK(!target_); target_ = target; } void WebGLQuery::DeleteObjectImpl(gpu::gles2::GLES2Interface* gl) { - gl->DeleteQueriesEXT(1, &Object()); + gl->DeleteQueriesEXT(1, &object_); + object_ = 0; } void WebGLQuery::ResetCachedResult() {
diff --git a/third_party/blink/renderer/modules/webgl/webgl_query.h b/third_party/blink/renderer/modules/webgl/webgl_query.h index bf4648e..b1b81e0d 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_query.h +++ b/third_party/blink/renderer/modules/webgl/webgl_query.h
@@ -6,7 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_QUERY_H_ #include "base/task/single_thread_task_runner.h" -#include "third_party/blink/renderer/modules/webgl/webgl_object.h" +#include "third_party/blink/renderer/modules/webgl/webgl_shared_platform_3d_object.h" #include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h" namespace gpu { @@ -19,7 +19,7 @@ class WebGL2RenderingContextBase; -class WebGLQuery : public WebGLObject { +class WebGLQuery : public WebGLSharedPlatform3DObject { DEFINE_WRAPPERTYPEINFO(); public: @@ -40,6 +40,8 @@ void DeleteObjectImpl(gpu::gles2::GLES2Interface*) override; private: + bool IsQuery() const override { return true; } + void ScheduleAllowAvailabilityUpdate(); void AllowAvailabilityUpdate();
diff --git a/third_party/blink/renderer/modules/webgl/webgl_renderbuffer.cc b/third_party/blink/renderer/modules/webgl/webgl_renderbuffer.cc index efca7fb..6898177 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_renderbuffer.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_renderbuffer.cc
@@ -31,7 +31,7 @@ namespace blink { WebGLRenderbuffer::WebGLRenderbuffer(WebGLRenderingContextBase* ctx) - : WebGLObject(ctx), + : WebGLSharedPlatform3DObject(ctx), internal_format_(GL_RGBA4), width_(0), height_(0), @@ -47,7 +47,8 @@ WebGLRenderbuffer::~WebGLRenderbuffer() = default; void WebGLRenderbuffer::DeleteObjectImpl(gpu::gles2::GLES2Interface* gl) { - gl->DeleteRenderbuffers(1, &Object()); + gl->DeleteRenderbuffers(1, &object_); + object_ = 0; } int WebGLRenderbuffer::UpdateMultisampleState(bool multisampled) { @@ -61,7 +62,7 @@ } void WebGLRenderbuffer::Trace(Visitor* visitor) const { - WebGLObject::Trace(visitor); + WebGLSharedPlatform3DObject::Trace(visitor); } } // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_renderbuffer.h b/third_party/blink/renderer/modules/webgl/webgl_renderbuffer.h index 8f07e376..af8814d 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_renderbuffer.h +++ b/third_party/blink/renderer/modules/webgl/webgl_renderbuffer.h
@@ -26,11 +26,11 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_RENDERBUFFER_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_RENDERBUFFER_H_ -#include "third_party/blink/renderer/modules/webgl/webgl_object.h" +#include "third_party/blink/renderer/modules/webgl/webgl_shared_platform_3d_object.h" namespace blink { -class WebGLRenderbuffer final : public WebGLObject { +class WebGLRenderbuffer final : public WebGLSharedPlatform3DObject { DEFINE_WRAPPERTYPEINFO(); public:
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc index 1ef7fa4..5cf40332 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -104,6 +104,7 @@ #include "third_party/blink/renderer/modules/webgl/webgl_compressed_texture_s3tc_srgb.h" #include "third_party/blink/renderer/modules/webgl/webgl_context_attribute_helpers.h" #include "third_party/blink/renderer/modules/webgl/webgl_context_event.h" +#include "third_party/blink/renderer/modules/webgl/webgl_context_group.h" #include "third_party/blink/renderer/modules/webgl/webgl_debug_renderer_info.h" #include "third_party/blink/renderer/modules/webgl/webgl_debug_shaders.h" #include "third_party/blink/renderer/modules/webgl/webgl_depth_texture.h" @@ -1263,6 +1264,7 @@ context_type == Platform::kWebGL2ContextType ? CanvasRenderingAPI::kWebgl2 : CanvasRenderingAPI::kWebgl), + context_group_(MakeGarbageCollected<WebGLContextGroup>()), dispatch_context_lost_event_timer_( task_runner, this, @@ -1278,6 +1280,8 @@ xr_compatible_ = requested_attributes.xr_compatible; + context_group_->AddContext(this); + max_viewport_dims_ = {}; context_provider->ContextGL()->GetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport_dims_.data()); @@ -2792,7 +2796,7 @@ bool WebGLRenderingContextBase::DeleteObject(WebGLObject* object) { if (isContextLost() || !object) return false; - if (!object->Validate(this)) { + if (!object->Validate(ContextGroup(), this)) { SynthesizeGLError(GL_INVALID_OPERATION, "delete", "object does not belong to this context"); return false; @@ -3013,7 +3017,7 @@ "attempt to use a deleted object"); return false; } - if (!object->Validate(this)) { + if (!object->Validate(ContextGroup(), this)) { SynthesizeGLError(GL_INVALID_OPERATION, function_name, "object does not belong to this context"); return false; @@ -3041,7 +3045,7 @@ "attempt to use a deleted object"); return false; } - if (!object->Validate(this)) { + if (!object->Validate(ContextGroup(), this)) { SynthesizeGLError(GL_INVALID_OPERATION, function_name, "object does not belong to this context"); return false; @@ -3498,7 +3502,7 @@ return ScriptValue::CreateNull(script_state->GetIsolate()); } - WebGLObject* attachment_object = + WebGLSharedObject* attachment_object = framebuffer_binding_->GetAttachmentObject(attachment); if (!attachment_object) { if (pname == GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE) @@ -4662,9 +4666,8 @@ } bool WebGLRenderingContextBase::isBuffer(WebGLBuffer* buffer) { - if (!buffer || isContextLost() || !buffer->Validate(this)) { + if (!buffer || isContextLost() || !buffer->Validate(ContextGroup(), this)) return false; - } if (!buffer->HasEverBeenBound()) return false; @@ -4691,9 +4694,9 @@ } bool WebGLRenderingContextBase::isFramebuffer(WebGLFramebuffer* framebuffer) { - if (!framebuffer || isContextLost() || !framebuffer->Validate(this)) { + if (!framebuffer || isContextLost() || + !framebuffer->Validate(ContextGroup(), this)) return false; - } if (!framebuffer->HasEverBeenBound()) return false; @@ -4704,9 +4707,8 @@ } bool WebGLRenderingContextBase::isProgram(WebGLProgram* program) { - if (!program || isContextLost() || !program->Validate(this)) { + if (!program || isContextLost() || !program->Validate(ContextGroup(), this)) return false; - } // OpenGL ES special-cases the behavior of program objects; if they're deleted // while attached to the current context state, glIsProgram is supposed to @@ -4717,9 +4719,9 @@ bool WebGLRenderingContextBase::isRenderbuffer( WebGLRenderbuffer* renderbuffer) { - if (!renderbuffer || isContextLost() || !renderbuffer->Validate(this)) { + if (!renderbuffer || isContextLost() || + !renderbuffer->Validate(ContextGroup(), this)) return false; - } if (!renderbuffer->HasEverBeenBound()) return false; @@ -4730,9 +4732,8 @@ } bool WebGLRenderingContextBase::isShader(WebGLShader* shader) { - if (!shader || isContextLost() || !shader->Validate(this)) { + if (!shader || isContextLost() || !shader->Validate(ContextGroup(), this)) return false; - } // OpenGL ES special-cases the behavior of shader objects; if they're deleted // while attached to a program, glIsShader is supposed to still return true. @@ -4742,9 +4743,8 @@ } bool WebGLRenderingContextBase::isTexture(WebGLTexture* texture) { - if (!texture || isContextLost() || !texture->Validate(this)) { + if (!texture || isContextLost() || !texture->Validate(ContextGroup(), this)) return false; - } if (!texture->HasEverBeenBound()) return false; @@ -7118,14 +7118,12 @@ return; } - LoseContextImpl(mode, auto_recovery_method); + context_group_->LoseContextGroup(mode, auto_recovery_method); } void WebGLRenderingContextBase::LoseContextImpl( WebGLRenderingContextBase::LostContextMode mode, AutoRecoveryMethod auto_recovery_method) { - number_of_context_losses_++; - if (isContextLost()) return; @@ -7217,7 +7215,7 @@ } uint32_t WebGLRenderingContextBase::NumberOfContextLosses() const { - return number_of_context_losses_; + return context_group_->NumberOfContextLosses(); } cc::Layer* WebGLRenderingContextBase::CcLayer() const { @@ -8875,6 +8873,7 @@ } void WebGLRenderingContextBase::Trace(Visitor* visitor) const { + visitor->Trace(context_group_); visitor->Trace(dispatch_context_lost_event_timer_); visitor->Trace(restore_timer_); visitor->Trace(bound_array_buffer_);
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h index 3e74a3b..2e0a148 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h +++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
@@ -103,6 +103,7 @@ class WebGLCompressedTexturePVRTC; class WebGLCompressedTextureS3TC; class WebGLCompressedTextureS3TCsRGB; +class WebGLContextGroup; class WebGLContextObject; class WebGLDebugShaders; class WebGLDrawBuffers; @@ -596,6 +597,7 @@ return d->ContextProvider()->SharedImageInterface(); } + WebGLContextGroup* ContextGroup() const { return context_group_.Get(); } Extensions3DUtil* ExtensionsUtil(); void Reshape(int width, int height) override; @@ -833,6 +835,8 @@ // to the back-buffer of m_context. scoped_refptr<DrawingBuffer> drawing_buffer_; + Member<WebGLContextGroup> context_group_; + LostContextMode context_lost_mode_ = kNotLostContext; AutoRecoveryMethod auto_recovery_method_ = kManual; // Dispatches a context lost event once it is determined that one is needed. @@ -2020,8 +2024,6 @@ bool has_been_drawn_to_ = false; - uint32_t number_of_context_losses_ = 0; - // Tracks if the context has ever called glBeginPixelLocalStorageANGLE. If it // has, we need to start using the pixel local storage interrupt mechanism // when we take over the client's context.
diff --git a/third_party/blink/renderer/modules/webgl/webgl_sampler.cc b/third_party/blink/renderer/modules/webgl/webgl_sampler.cc index 2ed1a52..d53bea7 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_sampler.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_sampler.cc
@@ -9,7 +9,8 @@ namespace blink { -WebGLSampler::WebGLSampler(WebGL2RenderingContextBase* ctx) : WebGLObject(ctx) { +WebGLSampler::WebGLSampler(WebGL2RenderingContextBase* ctx) + : WebGLSharedPlatform3DObject(ctx) { GLuint sampler; if (!ctx->isContextLost()) { ctx->ContextGL()->GenSamplers(1, &sampler); @@ -20,7 +21,8 @@ WebGLSampler::~WebGLSampler() = default; void WebGLSampler::DeleteObjectImpl(gpu::gles2::GLES2Interface* gl) { - gl->DeleteSamplers(1, &Object()); + gl->DeleteSamplers(1, &object_); + object_ = 0; } } // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_sampler.h b/third_party/blink/renderer/modules/webgl/webgl_sampler.h index 625a632..c3ef588 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_sampler.h +++ b/third_party/blink/renderer/modules/webgl/webgl_sampler.h
@@ -5,13 +5,13 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_SAMPLER_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_SAMPLER_H_ -#include "third_party/blink/renderer/modules/webgl/webgl_object.h" +#include "third_party/blink/renderer/modules/webgl/webgl_shared_platform_3d_object.h" namespace blink { class WebGL2RenderingContextBase; -class WebGLSampler : public WebGLObject { +class WebGLSampler : public WebGLSharedPlatform3DObject { DEFINE_WRAPPERTYPEINFO(); public: @@ -20,6 +20,9 @@ protected: void DeleteObjectImpl(gpu::gles2::GLES2Interface*) override; + + private: + bool IsSampler() const override { return true; } }; } // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_shader.cc b/third_party/blink/renderer/modules/webgl/webgl_shader.cc index 0f2b17d..a9f9514 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_shader.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_shader.cc
@@ -31,7 +31,7 @@ namespace blink { WebGLShader::WebGLShader(WebGLRenderingContextBase* ctx, GLenum type) - : WebGLObject(ctx), type_(type), source_("") { + : WebGLSharedPlatform3DObject(ctx), type_(type), source_("") { if (!ctx->isContextLost()) { SetObject(ctx->ContextGL()->CreateShader(type)); } @@ -40,7 +40,8 @@ WebGLShader::~WebGLShader() = default; void WebGLShader::DeleteObjectImpl(gpu::gles2::GLES2Interface* gl) { - gl->DeleteShader(Object()); + gl->DeleteShader(object_); + object_ = 0; } } // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_shader.h b/third_party/blink/renderer/modules/webgl/webgl_shader.h index 0ef9e0f..5344a70 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_shader.h +++ b/third_party/blink/renderer/modules/webgl/webgl_shader.h
@@ -26,12 +26,12 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_SHADER_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_SHADER_H_ -#include "third_party/blink/renderer/modules/webgl/webgl_object.h" +#include "third_party/blink/renderer/modules/webgl/webgl_shared_platform_3d_object.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" namespace blink { -class WebGLShader final : public WebGLObject { +class WebGLShader final : public WebGLSharedPlatform3DObject { DEFINE_WRAPPERTYPEINFO(); public: @@ -47,6 +47,8 @@ private: void DeleteObjectImpl(gpu::gles2::GLES2Interface*) override; + bool IsShader() const override { return true; } + GLenum type_; String source_; };
diff --git a/third_party/blink/renderer/modules/webgl/webgl_shared_object.cc b/third_party/blink/renderer/modules/webgl/webgl_shared_object.cc new file mode 100644 index 0000000..7aea8ca --- /dev/null +++ b/third_party/blink/renderer/modules/webgl/webgl_shared_object.cc
@@ -0,0 +1,71 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "third_party/blink/renderer/modules/webgl/webgl_shared_object.h" + +#include "third_party/blink/renderer/modules/webgl/webgl_context_group.h" +#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h" + +namespace blink { + +WebGLSharedObject::WebGLSharedObject(WebGLRenderingContextBase* context) + : WebGLObject(context), context_group_(nullptr) { + if (context) { + context_group_ = context->ContextGroup(); + } +} + +bool WebGLSharedObject::Validate(const WebGLContextGroup* context_group, + const WebGLRenderingContextBase*) const { + // The contexts and context groups no longer maintain references to all + // the objects they ever created, so there's no way to invalidate them + // eagerly during context loss. The invalidation is discovered lazily. + return (context_group == context_group_ && context_group != nullptr && + CachedNumberOfContextLosses() == + context_group->NumberOfContextLosses()); +} + +uint32_t WebGLSharedObject::CurrentNumberOfContextLosses() const { + if (!context_group_) { + return 0; + } + + return context_group_->NumberOfContextLosses(); +} + +gpu::gles2::GLES2Interface* WebGLSharedObject::GetAGLInterface() const { + if (!context_group_) { + return nullptr; + } + + return context_group_->GetAGLInterface(); +} + +void WebGLSharedObject::Trace(Visitor* visitor) const { + visitor->Trace(context_group_); + WebGLObject::Trace(visitor); +} + +} // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_shared_object.h b/third_party/blink/renderer/modules/webgl/webgl_shared_object.h new file mode 100644 index 0000000..280405f --- /dev/null +++ b/third_party/blink/renderer/modules/webgl/webgl_shared_object.h
@@ -0,0 +1,71 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_SHARED_OBJECT_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_SHARED_OBJECT_H_ + +#include "third_party/blink/renderer/modules/webgl/webgl_object.h" + +namespace blink { + +class WebGLContextGroup; +class WebGLRenderingContextBase; + +// WebGLSharedObject is the base class for objects that can be shared by +// multiple WebGLRenderingContexts. +class WebGLSharedObject : public WebGLObject { + public: + WebGLContextGroup* ContextGroup() const { return context_group_.Get(); } + + virtual bool IsBuffer() const { return false; } + virtual bool IsProgram() const { return false; } + virtual bool IsQuery() const { return false; } + virtual bool IsRenderbuffer() const { return false; } + virtual bool IsSampler() const { return false; } + virtual bool IsShader() const { return false; } + virtual bool IsSync() const { return false; } + virtual bool IsTexture() const { return false; } + + bool Validate(const WebGLContextGroup* context_group, + const WebGLRenderingContextBase*) const final; + + void Trace(Visitor*) const override; + + protected: + explicit WebGLSharedObject(WebGLRenderingContextBase*); + + bool HasGroupOrContext() const final { return context_group_ != nullptr; } + + uint32_t CurrentNumberOfContextLosses() const final; + + gpu::gles2::GLES2Interface* GetAGLInterface() const final; + + private: + Member<WebGLContextGroup> context_group_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_SHARED_OBJECT_H_
diff --git a/third_party/blink/renderer/modules/webgl/webgl_shared_platform_3d_object.cc b/third_party/blink/renderer/modules/webgl/webgl_shared_platform_3d_object.cc new file mode 100644 index 0000000..32bc3f9 --- /dev/null +++ b/third_party/blink/renderer/modules/webgl/webgl_shared_platform_3d_object.cc
@@ -0,0 +1,27 @@ +// 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. + +#include "third_party/blink/renderer/modules/webgl/webgl_shared_platform_3d_object.h" + +#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h" + +namespace blink { + +WebGLSharedPlatform3DObject::WebGLSharedPlatform3DObject( + WebGLRenderingContextBase* ctx) + : WebGLSharedObject(ctx), object_(0) {} + +void WebGLSharedPlatform3DObject::SetObject(GLuint object) { + // SetObject may only be called when this container is in the + // uninitialized state: object==0 && marked_for_deletion==false. + DCHECK(!object_); + DCHECK(!MarkedForDeletion()); + object_ = object; +} + +bool WebGLSharedPlatform3DObject::HasObject() const { + return object_ != 0; +} + +} // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_shared_platform_3d_object.h b/third_party/blink/renderer/modules/webgl/webgl_shared_platform_3d_object.h new file mode 100644 index 0000000..8534f4738 --- /dev/null +++ b/third_party/blink/renderer/modules/webgl/webgl_shared_platform_3d_object.h
@@ -0,0 +1,29 @@ +// 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 THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_SHARED_PLATFORM_3D_OBJECT_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_SHARED_PLATFORM_3D_OBJECT_H_ + +#include "third_party/blink/renderer/modules/webgl/webgl_shared_object.h" + +namespace blink { + +class WebGLRenderingContextBase; + +class WebGLSharedPlatform3DObject : public WebGLSharedObject { + public: + GLuint Object() const { return object_; } + void SetObject(GLuint); + + protected: + explicit WebGLSharedPlatform3DObject(WebGLRenderingContextBase*); + + bool HasObject() const override; + + GLuint object_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_SHARED_PLATFORM_3D_OBJECT_H_
diff --git a/third_party/blink/renderer/modules/webgl/webgl_sync.cc b/third_party/blink/renderer/modules/webgl/webgl_sync.cc index ca4f88b..4c5ac9b 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_sync.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_sync.cc
@@ -14,11 +14,11 @@ WebGLSync::WebGLSync(WebGL2RenderingContextBase* ctx, GLuint object, GLenum object_type) - : WebGLObject(ctx), + : WebGLSharedObject(ctx), sync_status_(GL_UNSIGNALED), + object_(object), object_type_(object_type), task_runner_(ctx->GetContextTaskRunner()) { - SetObject(object); ScheduleAllowCacheUpdate(); } @@ -38,7 +38,7 @@ // We can only update the cached result when control returns to the browser. allow_cache_update_ = false; GLuint value = 0; - gl->GetQueryObjectuivEXT(Object(), GL_QUERY_RESULT_AVAILABLE, &value); + gl->GetQueryObjectuivEXT(object_, GL_QUERY_RESULT_AVAILABLE, &value); if (value == GL_TRUE) { sync_status_ = GL_SIGNALED; } else { @@ -79,7 +79,8 @@ } void WebGLSync::DeleteObjectImpl(gpu::gles2::GLES2Interface* gl) { - gl->DeleteQueriesEXT(1, &Object()); + gl->DeleteQueriesEXT(1, &object_); + object_ = 0; } } // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_sync.h b/third_party/blink/renderer/modules/webgl/webgl_sync.h index 5e0f6e8..2e92e777 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_sync.h +++ b/third_party/blink/renderer/modules/webgl/webgl_sync.h
@@ -6,7 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_SYNC_H_ #include "base/task/single_thread_task_runner.h" -#include "third_party/blink/renderer/modules/webgl/webgl_object.h" +#include "third_party/blink/renderer/modules/webgl/webgl_shared_object.h" #include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h" namespace gpu { @@ -19,12 +19,14 @@ class WebGL2RenderingContextBase; -class WebGLSync : public WebGLObject { +class WebGLSync : public WebGLSharedObject { DEFINE_WRAPPERTYPEINFO(); public: ~WebGLSync() override; + GLuint Object() const { return object_; } + void UpdateCache(gpu::gles2::GLES2Interface*); GLint GetCachedResult(GLenum pname); bool IsSignaled() const; @@ -32,9 +34,14 @@ protected: WebGLSync(WebGL2RenderingContextBase*, GLuint, GLenum object_type); + bool HasObject() const override { return object_ != 0; } void DeleteObjectImpl(gpu::gles2::GLES2Interface*) override; + GLenum ObjectType() const { return object_type_; } + private: + bool IsSync() const override { return true; } + void ScheduleAllowCacheUpdate(); void AllowCacheUpdate(); @@ -42,6 +49,7 @@ // Initialized in cpp file to avoid including gl3.h in this header. GLint sync_status_; + GLuint object_; GLenum object_type_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_texture.cc b/third_party/blink/renderer/modules/webgl/webgl_texture.cc index 282bbc2c..1bff9e5 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_texture.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_texture.cc
@@ -31,7 +31,7 @@ namespace blink { WebGLTexture::WebGLTexture(WebGLRenderingContextBase* ctx) - : WebGLObject(ctx), target_(0) { + : WebGLSharedPlatform3DObject(ctx), target_(0) { if (!ctx->isContextLost()) { GLuint texture; ctx->ContextGL()->GenTextures(1, &texture); @@ -42,7 +42,7 @@ WebGLTexture::WebGLTexture(WebGLRenderingContextBase* ctx, GLuint texture, GLenum target) - : WebGLObject(ctx), target_(target) { + : WebGLSharedPlatform3DObject(ctx), target_(target) { SetObject(texture); } @@ -58,7 +58,8 @@ } void WebGLTexture::DeleteObjectImpl(gpu::gles2::GLES2Interface* gl) { - gl->DeleteTextures(1, &Object()); + gl->DeleteTextures(1, &object_); + object_ = 0; } int WebGLTexture::MapTargetToIndex(GLenum target) const {
diff --git a/third_party/blink/renderer/modules/webgl/webgl_texture.h b/third_party/blink/renderer/modules/webgl/webgl_texture.h index 8f30095..df94247 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_texture.h +++ b/third_party/blink/renderer/modules/webgl/webgl_texture.h
@@ -29,12 +29,12 @@ #include "base/time/time.h" #include "media/base/video_frame.h" #include "third_party/blink/public/platform/web_media_player.h" -#include "third_party/blink/renderer/modules/webgl/webgl_object.h" +#include "third_party/blink/renderer/modules/webgl/webgl_shared_platform_3d_object.h" #include "ui/gfx/geometry/rect.h" namespace blink { -class WebGLTexture : public WebGLObject { +class WebGLTexture : public WebGLSharedPlatform3DObject { DEFINE_WRAPPERTYPEINFO(); public:
diff --git a/third_party/blink/renderer/modules/webgl/webgl_timer_query_ext.cc b/third_party/blink/renderer/modules/webgl/webgl_timer_query_ext.cc index 5586583..1307fdef 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_timer_query_ext.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_timer_query_ext.cc
@@ -12,8 +12,9 @@ namespace blink { WebGLTimerQueryEXT::WebGLTimerQueryEXT(WebGLRenderingContextBase* ctx) - : WebGLObject(ctx), + : WebGLContextObject(ctx), target_(0), + query_id_(0), can_update_availability_(false), query_result_available_(false), query_result_(0), @@ -22,9 +23,7 @@ return; } - GLuint query = 0; - ctx->ContextGL()->GenQueriesEXT(1, &query); - SetObject(query); + Context()->ContextGL()->GenQueriesEXT(1, &query_id_); } WebGLTimerQueryEXT::~WebGLTimerQueryEXT() = default; @@ -75,7 +74,8 @@ } void WebGLTimerQueryEXT::DeleteObjectImpl(gpu::gles2::GLES2Interface* gl) { - gl->DeleteQueriesEXT(1, &Object()); + gl->DeleteQueriesEXT(1, &query_id_); + query_id_ = 0; } void WebGLTimerQueryEXT::ScheduleAllowAvailabilityUpdate() {
diff --git a/third_party/blink/renderer/modules/webgl/webgl_timer_query_ext.h b/third_party/blink/renderer/modules/webgl/webgl_timer_query_ext.h index 759bc36..4571fa4 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_timer_query_ext.h +++ b/third_party/blink/renderer/modules/webgl/webgl_timer_query_ext.h
@@ -6,7 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_TIMER_QUERY_EXT_H_ #include "base/task/single_thread_task_runner.h" -#include "third_party/blink/renderer/modules/webgl/webgl_object.h" +#include "third_party/blink/renderer/modules/webgl/webgl_context_object.h" #include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h" namespace gpu { @@ -17,7 +17,7 @@ namespace blink { -class WebGLTimerQueryEXT : public WebGLObject { +class WebGLTimerQueryEXT : public WebGLContextObject { DEFINE_WRAPPERTYPEINFO(); public: @@ -25,6 +25,8 @@ ~WebGLTimerQueryEXT() override; void SetTarget(GLenum target) { target_ = target; } + + GLuint Object() const { return query_id_; } bool HasTarget() const { return target_ != 0; } GLenum Target() const { return target_; } @@ -35,12 +37,14 @@ GLuint64 GetQueryResult(); private: + bool HasObject() const override { return query_id_ != 0; } void DeleteObjectImpl(gpu::gles2::GLES2Interface*) override; void ScheduleAllowAvailabilityUpdate(); void AllowAvailabilityUpdate(); GLenum target_; + GLuint query_id_; bool can_update_availability_; bool query_result_available_;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_transform_feedback.cc b/third_party/blink/renderer/modules/webgl/webgl_transform_feedback.cc index e904eca..5f493f0 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_transform_feedback.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_transform_feedback.cc
@@ -13,7 +13,8 @@ WebGL2RenderingContextBase* ctx, TFType type, GLint max_transform_feedback_separate_attribs) - : WebGLObject(ctx), + : WebGLContextObject(ctx), + object_(0), type_(type), target_(0), program_(nullptr), @@ -33,7 +34,7 @@ case TFType::kUser: { GLuint tf; ctx->ContextGL()->GenTransformFeedbacks(1, &tf); - SetObject(tf); + object_ = tf; break; } } @@ -55,7 +56,8 @@ case TFType::kDefault: break; case TFType::kUser: - gl->DeleteTransformFeedbacks(1, &Object()); + gl->DeleteTransformFeedbacks(1, &object_); + object_ = 0; break; } @@ -142,7 +144,7 @@ void WebGLTransformFeedback::Trace(Visitor* visitor) const { visitor->Trace(bound_indexed_transform_feedback_buffers_); visitor->Trace(program_); - WebGLObject::Trace(visitor); + WebGLContextObject::Trace(visitor); } } // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_transform_feedback.h b/third_party/blink/renderer/modules/webgl/webgl_transform_feedback.h index 39af2a1..3e77dbd 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_transform_feedback.h +++ b/third_party/blink/renderer/modules/webgl/webgl_transform_feedback.h
@@ -5,7 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_TRANSFORM_FEEDBACK_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_TRANSFORM_FEEDBACK_H_ -#include "third_party/blink/renderer/modules/webgl/webgl_object.h" +#include "third_party/blink/renderer/modules/webgl/webgl_context_object.h" #include "third_party/blink/renderer/modules/webgl/webgl_program.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h" @@ -14,7 +14,7 @@ class WebGL2RenderingContextBase; class WebGLBuffer; -class WebGLTransformFeedback : public WebGLObject { +class WebGLTransformFeedback : public WebGLContextObject { DEFINE_WRAPPERTYPEINFO(); public: @@ -29,12 +29,14 @@ GLint max_transform_feedback_separate_attribs); ~WebGLTransformFeedback() override; + GLuint Object() const { return object_; } + bool IsDefaultObject() const { return type_ == TFType::kDefault; } GLenum GetTarget() const { return target_; } void SetTarget(GLenum); - bool HasEverBeenBound() const { return HasObject() && target_; } + bool HasEverBeenBound() const { return object_ && target_; } WebGLProgram* GetProgram() const { return program_.Get(); } void SetProgram(WebGLProgram*); @@ -75,8 +77,11 @@ private: void DispatchDetached(gpu::gles2::GLES2Interface*); + bool HasObject() const override { return object_ != 0; } void DeleteObjectImpl(gpu::gles2::GLES2Interface*) override; + GLuint object_; + TFType type_; GLenum target_;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_unowned_texture.cc b/third_party/blink/renderer/modules/webgl/webgl_unowned_texture.cc index 94cbef1..5799290 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_unowned_texture.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_unowned_texture.cc
@@ -20,12 +20,13 @@ // Note that this will suppress the rest of the logic found in // WebGLObject::DeleteObject(), since one of the first things that the method // does is a check to see if |object_| is valid. - SetObject(0); + object_ = 0; } void WebGLUnownedTexture::DeleteObjectImpl(gpu::gles2::GLES2Interface* gl) { // Normally, we would invoke gl->DeleteTextures() here, but // WebGLUnownedTexture does not own its texture name. Just zero it out. + object_ = 0; } WebGLUnownedTexture::~WebGLUnownedTexture() = default;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_vertex_array_object_base.cc b/third_party/blink/renderer/modules/webgl/webgl_vertex_array_object_base.cc index 1fddc23..e71c9da 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_vertex_array_object_base.cc +++ b/third_party/blink/renderer/modules/webgl/webgl_vertex_array_object_base.cc
@@ -13,7 +13,8 @@ WebGLRenderingContextBase* ctx, VaoType type, GLint max_vertex_attribs) - : WebGLObject(ctx), + : WebGLContextObject(ctx), + object_(0), type_(type), has_ever_been_bound_(false), is_all_enabled_attrib_buffer_bound_(true) { @@ -30,12 +31,9 @@ switch (type_) { case kVaoTypeDefault: break; - default: { - GLuint vao = 0; - ctx->ContextGL()->GenVertexArraysOES(1, &vao); - SetObject(vao); + default: + Context()->ContextGL()->GenVertexArraysOES(1, &object_); break; - } } } @@ -58,7 +56,8 @@ case kVaoTypeDefault: break; default: - gl->DeleteVertexArraysOES(1, &Object()); + gl->DeleteVertexArraysOES(1, &object_); + object_ = 0; break; } @@ -128,7 +127,7 @@ void WebGLVertexArrayObjectBase::Trace(Visitor* visitor) const { visitor->Trace(bound_element_array_buffer_); visitor->Trace(array_buffer_list_); - WebGLObject::Trace(visitor); + WebGLContextObject::Trace(visitor); } } // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_vertex_array_object_base.h b/third_party/blink/renderer/modules/webgl/webgl_vertex_array_object_base.h index 10407f4..7bd54f01 100644 --- a/third_party/blink/renderer/modules/webgl/webgl_vertex_array_object_base.h +++ b/third_party/blink/renderer/modules/webgl/webgl_vertex_array_object_base.h
@@ -6,13 +6,13 @@ #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGL_WEBGL_VERTEX_ARRAY_OBJECT_BASE_H_ #include "third_party/blink/renderer/modules/webgl/webgl_buffer.h" -#include "third_party/blink/renderer/modules/webgl/webgl_object.h" +#include "third_party/blink/renderer/modules/webgl/webgl_context_object.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" namespace blink { -class WebGLVertexArrayObjectBase : public WebGLObject { +class WebGLVertexArrayObjectBase : public WebGLContextObject { public: enum VaoType { kVaoTypeDefault, @@ -21,6 +21,8 @@ ~WebGLVertexArrayObjectBase() override; + GLuint Object() const { return object_; } + bool IsDefaultObject() const { return type_ == kVaoTypeDefault; } bool HasEverBeenBound() const { return Object() && has_ever_been_bound_; } @@ -51,10 +53,13 @@ private: void DispatchDetached(gpu::gles2::GLES2Interface*); + bool HasObject() const override { return object_ != 0; } void DeleteObjectImpl(gpu::gles2::GLES2Interface*) override; void UpdateAttribBufferBoundStatus(); + GLuint object_; + VaoType type_; bool has_ever_been_bound_; Member<WebGLBuffer> bound_element_array_buffer_;
diff --git a/third_party/blink/renderer/modules/xr/xr_cube_map.cc b/third_party/blink/renderer/modules/xr/xr_cube_map.cc index c323a2e31..ecd50e5 100644 --- a/third_party/blink/renderer/modules/xr/xr_cube_map.cc +++ b/third_party/blink/renderer/modules/xr/xr_cube_map.cc
@@ -91,6 +91,7 @@ DCHECK(texture); DCHECK(!texture->HasEverBeenBound() || texture->GetTarget() == GL_TEXTURE_CUBE_MAP); + DCHECK(texture->ContextGroup() == context->ContextGroup()); auto* gl = context->ContextGL(); texture->SetTarget(GL_TEXTURE_CUBE_MAP);
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc index 99796f9..6f341b57 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.cc
@@ -109,7 +109,40 @@ namespace { +void ReleaseFrameToDispatcher( + base::WeakPtr<CanvasResourceDispatcher> dispatcher, + scoped_refptr<CanvasResource> oldImage, + viz::ResourceId resourceId) { + if (dispatcher) { + dispatcher->OnPlaceholderReleasedResource(resourceId, std::move(oldImage)); + } +} + +// This function gets called when the last outstanding reference to a +// CanvasResource that was sent to the OffscreenCanvasPlaceholder is released. +// When that last reference is released, we need to keep the resource alive to +// send it back to its thread of origin, where it will be held by FrameResource +// to be safely destroyed or recycled once the compositor has also finished +// accessing the resource. +void FrameLastUnrefCallback( + base::WeakPtr<CanvasResourceDispatcher> frame_dispatcher, + scoped_refptr<base::SingleThreadTaskRunner> frame_dispatcher_task_runner, + viz::ResourceId placeholder_frame_resource_id, + scoped_refptr<CanvasResource> placeholder_frame) { + DCHECK(placeholder_frame); + DCHECK(placeholder_frame->HasOneRef()); + DCHECK(frame_dispatcher_task_runner); + placeholder_frame->Transfer(); + PostCrossThreadTask( + *frame_dispatcher_task_runner, FROM_HERE, + CrossThreadBindOnce(ReleaseFrameToDispatcher, frame_dispatcher, + std::move(placeholder_frame), + placeholder_frame_resource_id)); +} + void UpdatePlaceholderImage( + base::WeakPtr<CanvasResourceDispatcher> dispatcher, + scoped_refptr<base::SingleThreadTaskRunner> task_runner, int placeholder_canvas_id, scoped_refptr<blink::CanvasResource>&& canvas_resource, viz::ResourceId resource_id) { @@ -118,6 +151,8 @@ OffscreenCanvasPlaceholder::GetPlaceholderCanvasById( placeholder_canvas_id); if (placeholder_canvas) { + canvas_resource->SetLastUnrefCallback(base::BindOnce( + FrameLastUnrefCallback, dispatcher, task_runner, resource_id)); placeholder_canvas->SetOffscreenCanvasResource(std::move(canvas_resource), resource_id); } @@ -177,8 +212,9 @@ CHECK(agent_group_scheduler_compositor_task_runner_); PostCrossThreadTask( *agent_group_scheduler_compositor_task_runner_, FROM_HERE, - CrossThreadBindOnce(UpdatePlaceholderImage, placeholder_canvas_id_, - std::move(canvas_resource), resource_id)); + CrossThreadBindOnce(UpdatePlaceholderImage, GetWeakPtr(), task_runner_, + placeholder_canvas_id_, std::move(canvas_resource), + resource_id)); } void CanvasResourceDispatcher::DispatchFrameSync(
diff --git a/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.cc b/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.cc index a158a1e..ee9a115 100644 --- a/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.cc +++ b/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.cc
@@ -24,15 +24,6 @@ return s_placeholderRegistry; } -void ReleaseFrameToDispatcher( - base::WeakPtr<CanvasResourceDispatcher> dispatcher, - scoped_refptr<CanvasResource> oldImage, - viz::ResourceId resourceId) { - if (dispatcher) { - dispatcher->OnPlaceholderReleasedResource(resourceId, std::move(oldImage)); - } -} - void SetAnimationState( base::WeakPtr<CanvasResourceDispatcher> dispatcher, CanvasResourceDispatcher::AnimationState animation_state) { @@ -49,37 +40,6 @@ namespace { -// This function gets called when the last outstanding reference to a -// CanvasResource is released. This callback is only registered on -// resources received via SetOffscreenCanvasResource(). When the resource -// is received, its ref count may be 2 because the CanvasResourceProvider -// that created it may be holding a cached snapshot that will be released when -// copy-on-write kicks in. This is okay even if the resource provider is on a -// different thread because concurrent read access is safe. By the time the -// next frame is received by OffscreenCanvasPlaceholder, the reference held by -// CanvasResourceProvider will have been released (otherwise there wouldn't be -// a new frame). This means that all outstanding references are held on the -// same thread as the OffscreenCanvasPlaceholder at the time when -// 'placeholder_frame_' is assigned a new value. Therefore, when the last -// reference is released, we need to temporarily keep the object alive and send -// it back to its thread of origin, where it can be safely destroyed or -// recycled. -void FrameLastUnrefCallback( - base::WeakPtr<CanvasResourceDispatcher> frame_dispatcher, - scoped_refptr<base::SingleThreadTaskRunner> frame_dispatcher_task_runner, - viz::ResourceId placeholder_frame_resource_id, - scoped_refptr<CanvasResource> placeholder_frame) { - DCHECK(placeholder_frame); - DCHECK(placeholder_frame->HasOneRef()); - DCHECK(frame_dispatcher_task_runner); - placeholder_frame->Transfer(); - PostCrossThreadTask( - *frame_dispatcher_task_runner, FROM_HERE, - CrossThreadBindOnce(ReleaseFrameToDispatcher, frame_dispatcher, - std::move(placeholder_frame), - placeholder_frame_resource_id)); -} - } // unnamed namespace void OffscreenCanvasPlaceholder::SetOffscreenCanvasResource( @@ -91,9 +51,6 @@ // CanvasResourceDispatcher, via FrameLastUnrefCallback if it was // the last outstanding reference on this thread. placeholder_frame_ = std::move(new_frame); - placeholder_frame_->SetLastUnrefCallback( - base::BindOnce(FrameLastUnrefCallback, frame_dispatcher_, - frame_dispatcher_task_runner_, resource_id)); if (deferred_animation_state_ && current_animation_state_ != *deferred_animation_state_) {
diff --git a/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h b/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h index 35c894d..d0e42c6 100644 --- a/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h +++ b/third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h
@@ -49,7 +49,6 @@ SkAlphaType GetAlphaType() const override { return GetSkImageInfo().alphaType(); } - SkColorType GetSkColorType() const { return GetSkImageInfo().colorType(); } sk_sp<SkColorSpace> GetSkColorSpace() const override { return GetSkImageInfo().refColorSpace(); } @@ -57,7 +56,8 @@ return SkColorSpaceToGfxColorSpace(GetSkColorSpace()); } viz::SharedImageFormat GetSharedImageFormat() const override { - return viz::SkColorTypeToSinglePlaneSharedImageFormat(GetSkColorType()); + return viz::SkColorTypeToSinglePlaneSharedImageFormat( + GetSkImageInfo().colorType()); } private:
diff --git a/third_party/blink/renderer/platform/wtf/text/parsing_utilities.h b/third_party/blink/renderer/platform/wtf/text/parsing_utilities.h index c066575..2330aa9 100644 --- a/third_party/blink/renderer/platform/wtf/text/parsing_utilities.h +++ b/third_party/blink/renderer/platform/wtf/text/parsing_utilities.h
@@ -36,6 +36,8 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_PARSING_UTILITIES_H_ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_PARSING_UTILITIES_H_ +#include <string_view> + #include "base/containers/span.h" namespace WTF { @@ -88,6 +90,19 @@ } template <typename CharType> +bool SkipToken(base::span<const CharType>& chars, std::string_view token) { + if (chars.size() < token.size()) { + return false; + } + if (chars.first(token.size()) != base::span(token)) { + return false; + } + + chars = chars.subspan(token.size()); + return true; +} + +template <typename CharType> void SkipUntil(const CharType*& position, const CharType* end, CharType delimiter) {
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json index 410a392..9f24cdff 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -380784,7 +380784,7 @@ ], "include": { "editor-test-utils.js": [ - "9c5600af77bf22c936cbe383a9667790d2cd8c10", + "f0c4f58399263a453b11a37e54d7152d935adec5", [] ], "implementation.js": [ @@ -499393,7 +499393,7 @@ ] ], "linear-timing-functions-syntax.html": [ - "0c949e9ea7dc50c86cd6877da1a10f7b2ba01625", + "408264b58dfe41dd7744d20419bae3a5fc7fd3eb", [ null, {} @@ -499407,7 +499407,7 @@ ] ], "step-timing-functions-syntax.html": [ - "4e8b21e4413f8000ae584396355ed7df1c44a447", + "e8465ff7a3304184046f7eb81f93d51a61c19dad", [ null, {} @@ -511069,7 +511069,7 @@ ] ], "overscroll-behavior-root.html": [ - "a116ead01b71db7b72bfd483b10b35b2e83ea1b8", + "71c5f6573d6f31f1ef872c09302063919597d8da", [ null, { @@ -539816,6 +539816,13 @@ {} ] ], + "Node-childNodes-cache-2.html": [ + "9079fc6ea7a146cb854389bf4f1b8c302a102746", + [ + null, + {} + ] + ], "Node-childNodes-cache.html": [ "da9e32c6a9cf579f3d7c6ce2e3208e04f90d7105", [ @@ -541860,7 +541867,7 @@ ] ], "XMLSerializer-serializeToString.html": [ - "c3b704bf18825c27e74c5db8177fa178925c9b26", + "6c294e464a5dc787abd4d10281ab2fe0555a0a3c", [ null, {} @@ -545522,7 +545529,7 @@ ] ], "selection-change-not-fired-if-selection-set-to-root.html": [ - "39e8b46e69b2c469b4b5d4ed40314bd67a2b3524", + "cac621ad3cdd3b1b8c9fcbbdf09cac739f9756ff", [ null, { @@ -833001,7 +833008,7 @@ ] ], "user_prompt_closed.py": [ - "fdb9e8b3ca7d5cc642ec69517f065887b16cdc24", + "8322829ebaf078152fd1874773ce0d110a0aac8e", [ null, {} @@ -833024,7 +833031,7 @@ ] ], "user_prompt_opened.py": [ - "c24128004f73a8b09a61991837b1dad103f91789", + "a9051f662c8124b6ee4a8c1fc657a5a1aaf2a2b9", [ null, {} @@ -833049,7 +833056,7 @@ ] ], "invalid.py": [ - "79241287fbc2b9a0dbd3c48e455d938e6bce7ad0", + "e804848e0be6c483340dcd8b3b4def10dddedce9", [ null, {}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-easing/linear-timing-functions-syntax.html b/third_party/blink/web_tests/external/wpt/css/css-easing/linear-timing-functions-syntax.html index 0c949e9..408264b 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-easing/linear-timing-functions-syntax.html +++ b/third_party/blink/web_tests/external/wpt/css/css-easing/linear-timing-functions-syntax.html
@@ -26,6 +26,7 @@ test_valid_value("animation-timing-function", "linear(0, 1.3, 1, 0.92, 1, 0.99, 1, 0.998, 1 100% 100%)"); test_valid_value("animation-timing-function", "linear(0, 0 40%, 1, 0.5, 1)"); test_valid_value("animation-timing-function", "linear(0, 1.3, 1, 0.92, 1, 0.99, 1, 1.004, 0.998, 1 100% 100%)"); +test_valid_value("animation-timing-function", "linear(calc(0/0), 1)", "linear(0 0%, 1 100%)"); test_invalid_value("animation-timing-function", "linear()"); test_invalid_value("animation-timing-function", "linear(0)");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-easing/step-timing-functions-syntax.html b/third_party/blink/web_tests/external/wpt/css/css-easing/step-timing-functions-syntax.html index 4e8b21e4..e8465ff7 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-easing/step-timing-functions-syntax.html +++ b/third_party/blink/web_tests/external/wpt/css/css-easing/step-timing-functions-syntax.html
@@ -29,6 +29,7 @@ test_invalid_value("animation-timing-function", "steps(0, jump-end)"); test_invalid_value("animation-timing-function", "steps(0, jump-both)"); test_invalid_value("animation-timing-function", "steps(1, jump-none)"); +test_invalid_value("animation-timing-function", "steps(calc(0/0), jump-none)"); </script> </body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-palette-values-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-palette-values-invalid.html index a3a0a88..32c9c4c 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-palette-values-invalid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/parsing/font-palette-values-invalid.html
@@ -140,13 +140,18 @@ @font-palette-values --A { override-colors: 0 color-mix(in lch, red, color-mix(in lch, currentcolor, black)); } + +/* 24 */ +@font-palette-values --A { + base-palette: sibling-index(); +} </style> </head> <body> <script> let rules = document.getElementById("style").sheet.cssRules; test(function() { - assert_equals(rules.length, 24); + assert_equals(rules.length, 25); }); test(function() { @@ -343,6 +348,13 @@ assert_equals(text.indexOf("override-colors"), -1); assert_equals(rule.overrideColors, ""); }); + +test(function() { + let text = rules[24].cssText; + let rule = rules[24]; + assert_equals(text.indexOf("base-palette"), -1); + assert_equals(rule.basePalette, ""); +}, "sibling-index() is invalid in base-palette descriptor"); </script> </body> </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/test_font_feature_values_parsing.html b/third_party/blink/web_tests/external/wpt/css/css-fonts/test_font_feature_values_parsing.html index 7a5844d..08c20142 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-fonts/test_font_feature_values_parsing.html +++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/test_font_feature_values_parsing.html
@@ -81,6 +81,7 @@ { rule: _("@styleset { ok-1: 1; }"), serializationSame: true }, { rule: _("@annotation { ok-1: 3; }"), serializationSame: true }, { rule: _("@stylistic { blah: 3; }"), serializationSame: true }, + { rule: _("@stylistic { blah: sibling-index(); }"), serializationNoValueDefn: true }, { rule: makeRule("bongo", "\n@styleset\n { blah: 3; super-blah: 4 5;\n more-blah: 5 6 7;\n }"), serializationSame: true }, { rule: makeRule("bongo", "\n@styleset\n {\n blah:\n 3\n;\n super-blah:\n 4\n 5\n;\n more-blah:\n 5 6\n 7;\n }"), serializationSame: true },
diff --git a/third_party/blink/web_tests/external/wpt/css/css-nesting/cssom-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-nesting/cssom-expected.txt index 83dc6c5..6ac7e38 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-nesting/cssom-expected.txt +++ b/third_party/blink/web_tests/external/wpt/css/css-nesting/cssom-expected.txt
@@ -1,7 +1,5 @@ This is a testharness.js-based test. [FAIL] CSSStyleRule is a CSSGroupingRule assert_equals: expected function "function CSSGroupingRule() { [native code] }" but got function "function CSSRule() { [native code] }" -[FAIL] Manipulation of nested declarations through CSSOM - assert_equals: expected "3" but got "1" Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/overscroll-behavior-root.html b/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/overscroll-behavior-root.html index a116ead..71c5f65 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/overscroll-behavior-root.html +++ b/third_party/blink/web_tests/external/wpt/css/css-overscroll-behavior/overscroll-behavior-root.html
@@ -33,6 +33,10 @@ Math.round(bounds.top + bounds.height / 2), 0, -100) .send(); + // Await two animation frames to give a chance to scroll. + await new Promise(resolve => requestAnimationFrame(resolve)); + await new Promise(resolve => requestAnimationFrame(resolve)); + assert_equals(window.scrollY, originalScrollPos, "overscroll-behavior should work on the root"); }); </script>
diff --git a/third_party/blink/web_tests/external/wpt/dom/nodes/Node-childNodes-cache-2.html b/third_party/blink/web_tests/external/wpt/dom/nodes/Node-childNodes-cache-2.html new file mode 100644 index 0000000..9079fc6 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/dom/nodes/Node-childNodes-cache-2.html
@@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Node.childNodes caching bug with replaceChild</title> +<link rel=help href="https://dom.spec.whatwg.org/#dom-node-childnodes"> +<link rel=author title="Xiaocheng Hu" href="mailto:xiaochengh.work@gmail.com"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="target"><div id="first"></div><div id="second"></div><div id="third"></div><div id="last"></div></div> +<script> +test(function() { + let target = document.getElementById("target"); + assert_array_equals(Array.from(target.childNodes).map(node => node.id), ["first", "second", "third", "last"]); + target.replaceChild(target.childNodes[2], target.childNodes[1]); + assert_array_equals(Array.from(target.childNodes).map(node => node.id), ["first", "third", "last"]); +}); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/user_prompt_closed/user_prompt_closed.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/user_prompt_closed/user_prompt_closed.py index fdb9e8b..8322829 100644 --- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/user_prompt_closed/user_prompt_closed.py +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/user_prompt_closed/user_prompt_closed.py
@@ -318,7 +318,7 @@ event = await wait_for_future_safe(on_prompt_closed) assert event == { - "context": new_tab["context"], + "context": frame["context"], "accepted": True, "type": "alert", }
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/user_prompt_opened/user_prompt_opened.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/user_prompt_opened/user_prompt_opened.py index c241280..a9051f6 100644 --- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/user_prompt_opened/user_prompt_opened.py +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/browsing_context/user_prompt_opened/user_prompt_opened.py
@@ -197,7 +197,7 @@ event = await wait_for_future_safe(on_entry) assert event == { - "context": new_tab["context"], + "context": frame["context"], "type": "alert", "handler": "dismiss", "message": "in iframe",
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/invalid.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/invalid.py index 7924128..e804848 100644 --- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/invalid.py +++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/emulation/set_geolocation_override/invalid.py
@@ -114,6 +114,18 @@ ) +@pytest.mark.parametrize("value", [-90.1, 90.1]) +async def test_params_coordinates_latitude_invalid_value(bidi_session, top_context, value): + with pytest.raises(error.InvalidArgumentException): + await bidi_session.emulation.set_geolocation_override( + contexts=[top_context["context"]], + coordinates=CoordinatesOptions( + latitude=value, + longitude=10, + ), + ) + + async def test_params_coordinates_longitude_missing(bidi_session, top_context): with pytest.raises(error.InvalidArgumentException): await bidi_session.emulation.set_geolocation_override( @@ -136,6 +148,18 @@ ) +@pytest.mark.parametrize("value", [-180.5, 180.5]) +async def test_params_coordinates_longitude_invalid_value(bidi_session, top_context, value): + with pytest.raises(error.InvalidArgumentException): + await bidi_session.emulation.set_geolocation_override( + contexts=[top_context["context"]], + coordinates=CoordinatesOptions( + latitude=10, + longitude=value, + ), + ) + + @pytest.mark.parametrize("value", [False, "foo", [], {}]) async def test_params_coordinates_accuracy_invalid_type(bidi_session, top_context, value): with pytest.raises(error.InvalidArgumentException): @@ -149,6 +173,18 @@ ) +async def test_params_coordinates_accuracy_invalid_value(bidi_session, top_context): + with pytest.raises(error.InvalidArgumentException): + await bidi_session.emulation.set_geolocation_override( + contexts=[top_context["context"]], + coordinates=CoordinatesOptions( + latitude=10, + longitude=10, + accuracy=-1, + ), + ) + + @pytest.mark.parametrize("value", [False, "foo", [], {}]) async def test_params_coordinates_altitude_invalid_type(bidi_session, top_context, value): with pytest.raises(error.InvalidArgumentException): @@ -176,6 +212,19 @@ ) +async def test_params_coordinates_altitude_accuracy_invalid_value(bidi_session, top_context): + with pytest.raises(error.InvalidArgumentException): + await bidi_session.emulation.set_geolocation_override( + contexts=[top_context["context"]], + coordinates=CoordinatesOptions( + latitude=10, + longitude=10, + altitude=10, + altitude_accuracy=-1, + ), + ) + + async def test_params_coordinates_altitude_accuracy_without_altitude(bidi_session, top_context): with pytest.raises(error.InvalidArgumentException): await bidi_session.emulation.set_geolocation_override( @@ -201,6 +250,19 @@ ) +@pytest.mark.parametrize("value", [-0.5, 360, 360.5]) +async def test_params_coordinates_heading_invalid_value(bidi_session, top_context, value): + with pytest.raises(error.InvalidArgumentException): + await bidi_session.emulation.set_geolocation_override( + contexts=[top_context["context"]], + coordinates=CoordinatesOptions( + latitude=10, + longitude=10, + heading=value, + ), + ) + + @pytest.mark.parametrize("value", [False, "foo", [], {}]) async def test_params_coordinates_speed_invalid_type(bidi_session, top_context, value): with pytest.raises(error.InvalidArgumentException): @@ -214,6 +276,18 @@ ) +async def test_params_coordinates_speed_invalid_value(bidi_session, top_context): + with pytest.raises(error.InvalidArgumentException): + await bidi_session.emulation.set_geolocation_override( + contexts=[top_context["context"]], + coordinates=CoordinatesOptions( + latitude=10, + longitude=10, + speed=-1.5, + ), + ) + + @pytest.mark.parametrize("value", [True, "foo", 42, {}]) async def test_params_user_contexts_invalid_type(bidi_session, value): with pytest.raises(error.InvalidArgumentException):
diff --git a/third_party/crossbench b/third_party/crossbench index ce39b8b..34be6a0 160000 --- a/third_party/crossbench +++ b/third_party/crossbench
@@ -1 +1 @@ -Subproject commit ce39b8b26f68bc3abb5d75abf7b6358ec7d0d2ff +Subproject commit 34be6a0627d00863c2bc46bcdba070d5cc9b05b2
diff --git a/third_party/dawn b/third_party/dawn index ffb09b2..0ef8995 160000 --- a/third_party/dawn +++ b/third_party/dawn
@@ -1 +1 @@ -Subproject commit ffb09b2dee453a5121d21d79ae9a9dc91ceaf1fa +Subproject commit 0ef89959a16e947d4d73f33c2f33d39818d95459
diff --git a/third_party/depot_tools b/third_party/depot_tools index fa8fc85..1fcc527 160000 --- a/third_party/depot_tools +++ b/third_party/depot_tools
@@ -1 +1 @@ -Subproject commit fa8fc854e1766b86f10c9a15902cf3cc23adaac2 +Subproject commit 1fcc527019d786502b02f71b8b764ee674a40953
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src index 4283539..d5def03 160000 --- a/third_party/devtools-frontend/src +++ b/third_party/devtools-frontend/src
@@ -1 +1 @@ -Subproject commit 4283539e427e270bad107202fbc752a5f23e8141 +Subproject commit d5def0337ba13fa0b63937a94837bd0281f91751
diff --git a/third_party/perfetto b/third_party/perfetto index a54dd38..220d71b 160000 --- a/third_party/perfetto +++ b/third_party/perfetto
@@ -1 +1 @@ -Subproject commit a54dd38d60593129ae56d400f1a72860670abea4 +Subproject commit 220d71b5a8e54a558cb10e70518c8b22b2c57b46
diff --git a/third_party/rust/serde_json_lenient/v0_2/wrapper/lib.rs b/third_party/rust/serde_json_lenient/v0_2/wrapper/lib.rs index 89088fd..c5e3913 100644 --- a/third_party/rust/serde_json_lenient/v0_2/wrapper/lib.rs +++ b/third_party/rust/serde_json_lenient/v0_2/wrapper/lib.rs
@@ -71,9 +71,6 @@ /// Permits unescaped \r and \n in strings. This is a subset of what /// allow_control_chars allows. allow_newlines: bool, - /// Permits unescaped ASCII control characters (such as unescaped \b, - /// \r, or \n) in the range [0x00,0x1F]. - allow_control_chars: bool, /// Permits \\v vertical tab escapes. allow_vert_tab: bool, /// Permits \\xNN escapes as described above. @@ -112,13 +109,8 @@ let mut deserializer = serde_json_lenient::Deserializer::new(SliceRead::new( if json.starts_with(&UTF8_BOM) { &json[3..] } else { json }, options.replace_invalid_characters, - // On the C++ side, allow_control_chars means "allow all control chars, - // including \r and \n", while in serde_json_lenient, - // allow_control_chars means "allow all controls chars, except \r and - // \n". To give the behavior that C++ client code is expecting, enable - // allow_newlines as well when allow_control_chars is supplied. - options.allow_newlines || options.allow_control_chars, - options.allow_control_chars, + options.allow_newlines, + /*allow_control_chars_in_string=*/false, options.allow_vert_tab, options.allow_x_escapes, ));
diff --git a/third_party/skia b/third_party/skia index b5ad540..a8e9b6a 160000 --- a/third_party/skia +++ b/third_party/skia
@@ -1 +1 @@ -Subproject commit b5ad54043bed93367fbf4a399424fca635d1d7a4 +Subproject commit a8e9b6aa2b406b35760cc3abd768a88c43ad783b
diff --git a/third_party/subresource-filter-ruleset/README.chromium b/third_party/subresource-filter-ruleset/README.chromium index 55309ed..11fbc8c0 100644 --- a/third_party/subresource-filter-ruleset/README.chromium +++ b/third_party/subresource-filter-ruleset/README.chromium
@@ -1,6 +1,6 @@ Name: EasyList URL: https://easylist.to/easylist/easylist.txt -Version: 202502121808 +Version: 202504081327 License: GPL-3.0, CC-BY-SA-3.0 License Android Compatible: yes License File: LICENSE
diff --git a/third_party/subresource-filter-ruleset/manifest.json b/third_party/subresource-filter-ruleset/manifest.json index e4ec961..2f3e9d2 100644 --- a/third_party/subresource-filter-ruleset/manifest.json +++ b/third_party/subresource-filter-ruleset/manifest.json
@@ -2,5 +2,5 @@ "manifest_version": 2, "name": "Subresource Filtering Rules", "ruleset_format": 1, - "version": "9.55.0" + "version": "9.56.0" }
diff --git a/third_party/webrtc b/third_party/webrtc index 7a56cbb..dc428bd 160000 --- a/third_party/webrtc +++ b/third_party/webrtc
@@ -1 +1 @@ -Subproject commit 7a56cbb05d89dee09ff67d00595bf9fb1ed78650 +Subproject commit dc428bd75f2ca847b165b2556970c9bcd5037c5b
diff --git a/tools/android/dependency_analysis/generate_json_dependency_graph.py b/tools/android/dependency_analysis/generate_json_dependency_graph.py index 4ab1f8e..5dca187 100755 --- a/tools/android/dependency_analysis/generate_json_dependency_graph.py +++ b/tools/android/dependency_analysis/generate_json_dependency_graph.py
@@ -141,7 +141,7 @@ str(build_output_dir), '--gn-labels', # Adds the // prefix. '--query', - 'deps_info.unprocessed_jar_path', + 'unprocessed_jar_path', ] if not show_ninja: cmd.append('-q')
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 432f77f..aa7b21c 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -11389,6 +11389,7 @@ <int value="-1137442543" label="enable-slimming-paint"/> <int value="-1136627751" label="ignore-autocomplete-off-autofill"/> <int value="-1136509631" label="ssl-interstitial-v1"/> + <int value="-1135637622" label="DisplayEdgeToEdgeFullscreen:disabled"/> <int value="-1135200357" label="ViewTransitionOnNavigation:disabled"/> <int value="-1134420065" label="CriticalPersistedTabData:disabled"/> <int value="-1134412904" label="PrivacySandboxSettings:disabled"/> @@ -13834,6 +13835,7 @@ <int value="-188439713" label="disable-video-capture-use-gpu-memory-buffer"/> <int value="-187536302" label="TabstripComboButton:enabled"/> <int value="-187506528" label="(Obsolete) CrOSSuspendToDisk:enabled"/> + <int value="-187125635" label="DisplayEdgeToEdgeFullscreen:enabled"/> <int value="-186707397" label="HideArcMediaNotifications:enabled"/> <int value="-185849961" label="IOSPromoPasswordBubble:disabled"/> <int value="-185162926" label="IncreaseInputAudioBufferSize:enabled"/>
diff --git a/tools/metrics/histograms/metadata/custom_tabs/enums.xml b/tools/metrics/histograms/metadata/custom_tabs/enums.xml index 8496793..a6fdaca 100644 --- a/tools/metrics/histograms/metadata/custom_tabs/enums.xml +++ b/tools/metrics/histograms/metadata/custom_tabs/enums.xml
@@ -184,6 +184,16 @@ memory"/> </enum> +<enum name="EarlyNavFailureReason"> + <int value="0" label="Success"/> + <int value="1" label="No session"/> + <int value="2" label="No speculation"/> + <int value="3" label="Wrong URL, valid session"/> + <int value="4" label="Wrong URL, invalid session"/> + <int value="5" label="Wrong referrer, valid session"/> + <int value="6" label="Wrong referrer, invalid session"/> +</enum> + <enum name="GoogleBottomBarButtonEvent"> <int value="0" label="Unknown button"/> <int value="1" label="Chrome Page Insights button"/>
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml index 3d3fa9f..3ae79aa 100644 --- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml +++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -727,6 +727,18 @@ </summary> </histogram> +<histogram name="CustomTabs.Startup.EarlyNavFailureReason" + enum="EarlyNavFailureReason" expires_after="2025-10-01"> + <owner>mthiesse@chromium.org</owner> + <owner>yfriedman@chromium.org</owner> + <summary> + Records the result of a launch that attempted to perform an Early Navigation + when the Custom Tab starts up and either takes the Hidden tab or doesn't. + + For debugging duplicate navigations seen in the wild. + </summary> +</histogram> + <histogram name="CustomTabs.Startup.StartedNavigationEarly" enum="Boolean" expires_after="2025-08-24"> <owner>mthiesse@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml index 848e437..6e9a7469 100644 --- a/tools/metrics/histograms/metadata/ios/histograms.xml +++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -3827,7 +3827,7 @@ </histogram> <histogram name="IOS.PasswordManager.BulkSavePasswordsInAccountCount" - units="passwords" expires_after="2025-06-01"> + units="passwords" expires_after="2025-11-01"> <owner>nicolasmacbeth@google.com</owner> <owner>tmartino@chromium.org</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/password/histograms.xml b/tools/metrics/histograms/metadata/password/histograms.xml index ab12d2a..e44d8a1b 100644 --- a/tools/metrics/histograms/metadata/password/histograms.xml +++ b/tools/metrics/histograms/metadata/password/histograms.xml
@@ -158,6 +158,12 @@ <variant name="ProfileStore." summary="for profile-scoped store"/> </variants> +<variants name="StoreErrorStatus"> + <variant name=""/> + <variant name=".ExcludingStoreErrors" + summary="errors returned from the underlying store are not reported."/> +</variants> + <variants name="UserSyncingType"> <variant name="" summary="all users."/> <variant name=".SignedInAccountStoreUser" @@ -4979,7 +4985,7 @@ </histogram> <histogram - name="PasswordManager.{Store}{Metric3}.{PasswordType}{CustomPassphraseStatus}" + name="PasswordManager.{Store}{Metric3}.{PasswordType}{CustomPassphraseStatus}{StoreErrorStatus}" units="units" expires_after="2025-09-14"> <owner>kazinova@google.com</owner> <owner>vasilii@chromium.org</owner> @@ -4999,6 +5005,7 @@ </token> <token key="PasswordType" variants="PasswordType"/> <token key="CustomPassphraseStatus" variants="CustomPassphraseStatus"/> + <token key="StoreErrorStatus" variants="StoreErrorStatus"/> </histogram> <histogram
diff --git a/tools/metrics/histograms/metadata/signin/histograms.xml b/tools/metrics/histograms/metadata/signin/histograms.xml index 2d49612..a0aeedc 100644 --- a/tools/metrics/histograms/metadata/signin/histograms.xml +++ b/tools/metrics/histograms/metadata/signin/histograms.xml
@@ -2616,10 +2616,6 @@ Notes: - - This histogram is still in development and has a limited coverage of the - Signin access points for now. See go/chrome-signin-metrics-revamp for more - context on the planned work. - - For the First Run, we log it once for the whole first run flow, it does not get recorded multiple times if the user steps back in the flow and chooses again to sign in. @@ -2661,6 +2657,11 @@ <summary> Logs the original access point of each completed sign in. {SigninAccountStatus} + + Note: See also more modern metrics such as Signin.SignIn.Completed and + Signin.SyncOptIn.Completed that provide similar insights (but exclude + reauth). More information about modernization can be found in + http://go/chrome-signin-metrics-revamp. </summary> <token key="SigninAccountStatus" variants="SigninAccountStatus"> <variant name=""/> @@ -2768,6 +2769,11 @@ <summary> Logs the original access point that displayed the signin or reauth Gaia page, before the page is displayed. {SigninAccountStatus} + + Note: See also more modern metrics such as Signin.SignIn.Started and + Signin.SyncOptIn.Started that provide similar insights (but exclude reauth). + More information about modernization can be found in + http://go/chrome-signin-metrics-revamp. </summary> <token key="SigninAccountStatus" variants="SigninAccountStatus"> <variant name=""/>
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml index 2494e580..2beae7f 100644 --- a/tools/metrics/histograms/metadata/sync/histograms.xml +++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -301,7 +301,7 @@ </histogram> <histogram - name="Sync.BookmarkModelMerger.Comparison.{PreviouslySyncingGaiaId}.{SubtreeSelection}.{GroupingKey}{OptionalBookmarkCount}" + name="Sync.BookmarkModelMerger.Comparison{OptionalPreviouslySyncingGaiaId}.{SubtreeSelection}.{GroupingKey}{OptionalBookmarkCount}" enum="BookmarkSetComparisonOutcome" expires_after="2025-11-23"> <owner>mastiz@chromium.org</owner> <owner>droger@chromium.org</owner> @@ -314,21 +314,22 @@ Recorded on desktop platforms (excluding ChromeOS) only when bookmark sync (legacy Full Sync) is turned on and after account bookmarks are downloaded from the Sync server, for the case where - {PreviouslySyncingGaiaId}{OptionalBookmarkCount}. + {OptionalPreviouslySyncingGaiaId}{OptionalBookmarkCount}. </summary> <!-- LINT.IfChange(BookmarkComparisonPreviouslySyncingGaiaId) --> - <token key="PreviouslySyncingGaiaId"> - <variant name="DiffersPreviousGaiaId" + <token key="OptionalPreviouslySyncingGaiaId"> + <variant name="" summary="all users and"/> + <variant name=".DiffersPreviousGaiaId" summary="the user (gaia ID) currently turning on bookmark sync is different to the one that had previously turned sync on"/> - <variant name="MatchesPreviousGaiaId" + <variant name=".MatchesPreviousGaiaId" summary="the user (gaia ID) currently turning on bookmark sync is the same as the one that had previously turned sync on"/> - <variant name="NoPreviousGaiaId" + <variant name=".NoPreviousGaiaId" summary="sync was never turned on before in the current browser profile"/> - <variant name="UnknownPreviousGaiaId" + <variant name=".UnknownPreviousGaiaId" summary="it is impossible to determine which user (gaia ID), if any, had turned sync on previously"/> </token> @@ -346,10 +347,14 @@ <!-- LINT.IfChange(BookmarkComparisonGroupingKey) --> <token key="GroupingKey"> + <variant name="ByUrl" summary="the bookmark's URL"/> <variant name="ByUrlAndTitle" summary="<URL, title>"/> <variant name="ByUrlAndTitleAndPath" summary="<URL, title, path> (where the path contains ancestor's titles)"/> + <variant name="ByUrlAndTitleAndPathAndUuid" + summary="<URL, title, path, UUID> (where the path contains + ancestor's titles)"/> <variant name="ByUrlAndUuid" summary="<URL, UUID>"/> </token> <!-- LINT.ThenChange(//components/sync_bookmarks/bookmark_model_merger_comparison_metrics.cc:BookmarkComparisonGroupingKey) -->
diff --git a/tools/metrics/histograms/metadata/trusted_vault/enums.xml b/tools/metrics/histograms/metadata/trusted_vault/enums.xml index bdac4b0..933290b 100644 --- a/tools/metrics/histograms/metadata/trusted_vault/enums.xml +++ b/tools/metrics/histograms/metadata/trusted_vault/enums.xml
@@ -120,6 +120,18 @@ <!-- LINT.ThenChange(/components/trusted_vault/trusted_vault_connection.h:TrustedVaultRecoverabilityStatus) --> +<!-- LINT.IfChange(TrustedVaultRecoverKeysOutcome) --> + +<enum name="TrustedVaultRecoverKeysOutcome"> + <int value="0" label="Success"/> + <int value="1" label="No new keys"/> + <int value="2" label="Failure"/> + <int value="3" label="No primary account"/> + <int value="4" label="Aborted"/> +</enum> + +<!-- LINT.ThenChange(/components/trusted_vault/trusted_vault_histograms.h:TrustedVaultRecoverKeysOutcome) --> + </enums> </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/trusted_vault/histograms.xml b/tools/metrics/histograms/metadata/trusted_vault/histograms.xml index 9851428..2934611 100644 --- a/tools/metrics/histograms/metadata/trusted_vault/histograms.xml +++ b/tools/metrics/histograms/metadata/trusted_vault/histograms.xml
@@ -51,10 +51,17 @@ <owner>mastiz@chromium.org</owner> <owner>thomasth@google.com</owner> <summary> - Simplified version of TrustedVault.DeviceRegistrationState. Records whether - a recovery factor is registered (and ignores whether re-registration was - completed/pending/in-flight) upon startup (if signed in) or upon first - signin. + Simplified version of TrustedVault.DeviceRegistrationState. Records whether, + according to information on this device, a recovery factor is registered + (and ignores whether re-registration was completed/pending/in-flight) upon + startup (if signed in) or upon first signin. + + Recorded at most once for every recovery factor / security domain + combination per profile within the browser process lifetime. + + Recovery factor: {LocalRecoveryFactorType} + + Security domain: {SecurityDomainId} </summary> <token key="LocalRecoveryFactorType" variants="LocalRecoveryFactorType"/> <token key="SecurityDomainId" variants="SecurityDomainId"/> @@ -67,8 +74,10 @@ <owner>mastiz@chromium.org</owner> <owner>thomasth@google.com</owner> <summary> - Records the outcome of device registration attempt upon request completion - or failure. + Records the outcome of a device registration attempt for a private key + stored in/on {LocalRecoveryFactorType} upon request completion or failure. + + Security domain: {SecurityDomainId} </summary> <token key="LocalRecoveryFactorType" variants="LocalRecoveryFactorType"/> <token key="SecurityDomainId" variants="SecurityDomainId"/> @@ -79,9 +88,14 @@ <owner>mmoskvitin@google.com</owner> <owner>mastiz@chromium.org</owner> <summary> - Records whether the local device is registered on the server upon startup - (if signed in) or upon first signin, and if not registered, provides - insights into why. + Records whether, according to information on this device, a recovery factor + is registered on the server upon startup (if signed in) or upon first + signin, and if not registered, provides insights into why. + + Recorded at most once for every recovery factor / security domain + combination per browser process lifetime. + + Security domain: {SecurityDomainId} </summary> <token key="SecurityDomainId" variants="SecurityDomainId"/> </histogram> @@ -99,18 +113,27 @@ It is recorded whenever DownloadAuthenticationFactorsRegistrationState is called. For passkeys this happens during WebAuthn calls, in some cases without a Google Password Manager passkey being selected, only present. + + Security domain: {SecurityDomainId} </summary> <token key="SecurityDomainId" variants="SecurityDomainId"/> </histogram> -<histogram name="TrustedVault.DownloadKeysStatus{SecurityDomainId}" - enum="TrustedVaultDownloadKeysStatus" expires_after="2025-09-14"> +<histogram + name="TrustedVault.DownloadKeysStatus{LocalRecoveryFactorType}{SecurityDomainId}" + enum="TrustedVaultDownloadKeysStatus" expires_after="2026-05-05"> <owner>mmoskvitin@google.com</owner> <owner>mastiz@chromium.org</owner> + <owner>thomasth@google.com</owner> <summary> Records the result of an attempt to download trusted vault keys from the - server (includes all registration versions). + server for {SecurityDomainId}. "Downloading" here refers to + fetching a private member key from {LocalRecoveryFactorType}, fetching + wrapped member keys from the backend and validating / decrypting the keys. + + Note: This histogram covers all registration versions. </summary> + <token key="LocalRecoveryFactorType" variants="LocalRecoveryFactorType"/> <token key="SecurityDomainId" variants="SecurityDomainId"/> </histogram> @@ -118,7 +141,9 @@ enum="TrustedVaultFileReadStatus" expires_after="2025-09-28"> <owner>mmoskvitin@google.com</owner> <owner>mastiz@chromium.org</owner> - <summary>Recorded when reading local trusted vault file.</summary> + <summary> + Recorded when reading local trusted vault file for {SecurityDomainId}. + </summary> <token key="SecurityDomainId" variants="SecurityDomainId"/> </histogram> @@ -127,7 +152,8 @@ <owner>mmoskvitin@google.com</owner> <owner>mastiz@chromium.org</owner> <summary> - Records whether writing local trusted vault file upon each write. + Records whether writing local trusted vault file for {SecurityDomainId} was + successful upon each write. </summary> <token key="SecurityDomainId" variants="SecurityDomainId"/> </histogram> @@ -182,6 +208,20 @@ </summary> </histogram> +<histogram name="TrustedVault.RecoverKeysOutcome{SecurityDomainId}" + enum="TrustedVaultRecoverKeysOutcome" expires_after="2026-05-05"> + <owner>mmoskvitin@google.com</owner> + <owner>mastiz@chromium.org</owner> + <owner>thomasth@google.com</owner> + <summary> + Records the outcome of an attempt to recover trusted vault keys for + {SecurityDomainId} using all available local recovery factors. + + Note: If this fails, then user interaction is required to fetch keys. + </summary> + <token key="SecurityDomainId" variants="SecurityDomainId"/> +</histogram> + <histogram name="TrustedVault.RecoveryKeyStoreURLFetchResponse{Reason}" enum="CombinedHttpResponseAndNetErrorCode" expires_after="2025-07-31"> <owner>mastiz@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index f560f02..6cbcc87 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@ "full_remote_path": "perfetto-luci-artifacts/v50.1/linux-arm64/trace_processor_shell" }, "win": { - "hash": "6006abb15753be307c57debcc709995c055ae996", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/a54dd38d60593129ae56d400f1a72860670abea4/trace_processor_shell.exe" + "hash": "0d668287cd1d3b9c0d49ea6ff78be42962657e4f", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/4417e1970f2c99f6d50d592ff071e87730fbd2c4/trace_processor_shell.exe" }, "linux_arm": { "hash": "99f971ca131f6d11c73f4b918099d434bdd8093c", @@ -21,8 +21,8 @@ "full_remote_path": "perfetto-luci-artifacts/v50.1/mac-arm64/trace_processor_shell" }, "linux": { - "hash": "a0b53da9cbeb8a24c724f92b3037cf3f9e5d8f4e", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/a54dd38d60593129ae56d400f1a72860670abea4/trace_processor_shell" + "hash": "0d148a6f990e6248d23cff066ff8bc062e2a3cff", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/4417e1970f2c99f6d50d592ff071e87730fbd2c4/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/tools/perf/crossbench_result_converter.py b/tools/perf/crossbench_result_converter.py index 54b95e4..ddfb087 100755 --- a/tools/perf/crossbench_result_converter.py +++ b/tools/perf/crossbench_result_converter.py
@@ -42,30 +42,40 @@ raise FileNotFoundError( f'Missing crossbench results file: {cb_results_json_path}') + debug_info = '' with cb_results_json_path.open() as f: results_info = json.load(f) + debug_info += f'results_info={results_info}\n' browsers = results_info.get('browsers', {}) if len(browsers) != 1: raise ValueError( - f'Expected to have one "browsers" in {cb_results_json_path}') + f'Expected to have one "browsers" in {cb_results_json_path}, ' + f'debug_info={debug_info}') browser_info = list(browsers.values())[0] + debug_info += f'browser_info={browser_info}\n' probe_json_path = None - for probe, probe_data in browser_info.get('probes', {}).items(): - if probe.startswith('cb.'): - continue - candidates = probe_data.get('json', []) - if len(candidates) > 1: - raise ValueError(f'Probe {probe} generated multiple json files') - if len(candidates) == 1: - if probe_json_path: - raise ValueError( - f'Multiple output json files found in {cb_results_json_path}') - probe_json_path = pathlib.Path(candidates[0]) + try: + for probe, probe_data in browser_info.get('probes', {}).items(): + if probe.startswith('cb.') or not probe_data: + continue + candidates = probe_data.get('json', []) + if len(candidates) > 1: + raise ValueError(f'Probe {probe} generated multiple json files, ' + f'debug_info={debug_info}') + if len(candidates) == 1: + if probe_json_path: + raise ValueError( + f'Multiple output json files found in {cb_results_json_path}, ' + f'debug_info={debug_info}') + probe_json_path = pathlib.Path(candidates[0]) + except AttributeError as e: + raise AttributeError(f'debug_info={debug_info}') from e if not probe_json_path: - raise ValueError(f'No output json file found in {cb_results_json_path}') + raise ValueError(f'No output json file found in {cb_results_json_path}, ' + f'debug_info={debug_info}') return probe_json_path