diff --git a/DEPS b/DEPS index be67900..063c174a 100644 --- a/DEPS +++ b/DEPS
@@ -312,7 +312,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': '0e28c030a2625e3022a9adeec9746519af4217a2', + 'angle_revision': '6e992fa20b4bbcc5c7892ce085d95b0549c9cfa8', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -320,11 +320,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': 'eeaea7e7fae274a49c3f4686eebc1abbadc4861a', + 'pdfium_revision': 'a4b11d6beb5b3293c6e69e36afc928e738272dd6', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. - 'boringssl_revision': 'd5440dd2c2c500ac2d3bba4afec47a054b4d99ae', + 'boringssl_revision': 'ef839bf397fb4ecdb66ef2679a08ac7b3563c50b', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Fuchsia sdk # and whatever else without interference from each other. @@ -376,11 +376,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': 'd451fed9573b5eb485dc364514d1ec95f20eadb0', + 'catapult_revision': 'afb0c8b9082937de851ec953e07b0410db640b50', # 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': 'f5a24fd88bd5137b412f59477878d0782e19ff15', + 'crossbench_revision': '7848090f51d703417a9488f01ef7c10d2da8da2d', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -420,7 +420,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': '36be4620f07d464268815b4933e91db34c99fee6', + 'dawn_revision': '4cfedf0f36940477134b3eff352cf396b74e8e3b', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -532,7 +532,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling llvm-libc # and whatever else without interference from each other. - 'compiler_rt_revision': '4727d6a1939e3f2af6affe74234f46930ef5321c', + 'compiler_rt_revision': 'a0beb0c8bd142da77c77eca551a968f9ff0036db', # If you change this, also update the libc++ revision in # //buildtools/deps_revisions.gni. @@ -994,31 +994,31 @@ 'bucket': 'chromium-browser-clang', 'objects': [ { - 'object_name': 'Linux_x64/rust-toolchain-f7b43542838f0a4a6cfdb17fbeadf45002042a77-1-llvmorg-21-init-6681-g5b36835d.tar.xz', - 'sha256sum': '0e8b2cd2b6d19b69340313a8428c9b2519ca1fefc9b3d118e4ac6237e480cb45', - 'size_bytes': 118342552, - 'generation': 1743614581813017, + 'object_name': 'Linux_x64/rust-toolchain-3f690c2257b7080cd3a8cce64e082fc972148990-1-llvmorg-21-init-6681-g5b36835d.tar.xz', + 'sha256sum': 'f3132b0b1fa412c2fb35e068e47b3fade679a1446d9a08b67c403d143931f2ec', + 'size_bytes': 118335848, + 'generation': 1743178941814849, 'condition': 'host_os == "linux" and non_git_source', }, { - 'object_name': 'Mac/rust-toolchain-f7b43542838f0a4a6cfdb17fbeadf45002042a77-1-llvmorg-21-init-6681-g5b36835d.tar.xz', - 'sha256sum': 'bd7a881da32a7e18cb356fae8f160fc47dfe65f7361eef82a4d90ecd9cf19938', - 'size_bytes': 111543836, - 'generation': 1743614583291263, + 'object_name': 'Mac/rust-toolchain-3f690c2257b7080cd3a8cce64e082fc972148990-1-llvmorg-21-init-6681-g5b36835d.tar.xz', + 'sha256sum': '093ad50477633cb98719ace92995ad0e11dfa491b97835997a30fc1078d63cbe', + 'size_bytes': 111607824, + 'generation': 1743178943094518, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac_arm64/rust-toolchain-f7b43542838f0a4a6cfdb17fbeadf45002042a77-1-llvmorg-21-init-6681-g5b36835d.tar.xz', - 'sha256sum': '9f6eb1c85b63708682dd9c3af58d296be25044c11c363a9de97e9fc32c964484', - 'size_bytes': 101199692, - 'generation': 1743614584780481, + 'object_name': 'Mac_arm64/rust-toolchain-3f690c2257b7080cd3a8cce64e082fc972148990-1-llvmorg-21-init-6681-g5b36835d.tar.xz', + 'sha256sum': 'e5db937e64017390f52c1cb68445ed5101855e0b9318c7de71dfaea37649b4cb', + 'size_bytes': 101058976, + 'generation': 1743178944397528, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Win/rust-toolchain-f7b43542838f0a4a6cfdb17fbeadf45002042a77-1-llvmorg-21-init-6681-g5b36835d.tar.xz', - 'sha256sum': 'beb9c3ee764c6554aeea973fd774c4828984f043517847f787b6aa22a0c449f5', - 'size_bytes': 186229164, - 'generation': 1743614586634187, + 'object_name': 'Win/rust-toolchain-3f690c2257b7080cd3a8cce64e082fc972148990-1-llvmorg-21-init-6681-g5b36835d.tar.xz', + 'sha256sum': '3f8d34895c880301e2805164b7b5e4e487769b65d5fcadb4384a10228f559c85', + 'size_bytes': 192615112, + 'generation': 1743178945657202, 'condition': 'host_os == "win"', }, ], @@ -1158,7 +1158,7 @@ }, 'src/chrome/release_scripts': { - 'url': Var('chrome_git') + '/chrome/tools/release/scripts' + '@' + 'b0e8d98f5141c3b50870406a7cf1e090806b2f8e', + 'url': Var('chrome_git') + '/chrome/tools/release/scripts' + '@' + 'a8b04e95dc407615a7d2bdc70903376809093548', 'condition': 'checkout_chrome_release_scripts', }, @@ -1501,7 +1501,7 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - '618a8ebb6ca63dee8c6b5066c777e88ec88596d2', + '599d7806ce1185408289232b25620f00947b7c6a', 'condition': 'checkout_android and checkout_src_internal', }, @@ -1660,7 +1660,7 @@ 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': 'YUagVb2LLm046A1cjxcNeAtcDxBYd0vu1VxD0Eth_BUC', + 'version': 'MxOklR3uqOTkCS4tAVAfhzBjAdmVbT0N1LtrxIouxD4C', }, ], 'condition': 'checkout_android and non_git_source', @@ -2515,7 +2515,7 @@ Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '652bdb7719f30b52b08e506645a7322ff1b2cc6f', 'src/third_party/openscreen/src': - Var('chromium_git') + '/openscreen' + '@' + 'ddc89a049295f3fa6ff01646c0a02eb747eba6af', + Var('chromium_git') + '/openscreen' + '@' + '9c99d9f3c0b7029b25b07bed43fbc73901839dc6', 'src/third_party/openxr/src': { 'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '781f2eab3698d653c804ecbd11e0aed47eaad1c6', @@ -2855,16 +2855,16 @@ 'dep_type': 'cipd', }, - 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@8ede18e2d91dfd40c5e71c25f42761519142dd1a', + 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@ddb9ba0fdcc3193af3c60f811fa4524d54943890', 'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@4a038eafdf9e9f3e0ac2e200127df969f3a51ddb', 'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3', 'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@8e82b7cfeca98baae9a01a53511483da7194f854', 'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@1a811fd69871da36d9cca84586d8a7799be893ec', 'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@5ceb9ed481e58e705d0d9b5326537daedd06b97d', - 'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@b8eb2b901835497b91db7bd7005f4d6ddba2bf1e', + 'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@7b6539f24633096c25631bab9fd572bd1ad9b27b', 'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@59dc56aa3023434317a5197d77be51855b5fd2fb', 'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@abc2498bde8d65841f463431a6220701fad44c64', - 'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@2d41063e90346d2fceca5f40b90e43a7a3368f7b', + 'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@5cf5ba49fd12c048e4192b150eb7528253a887ba', 'src/third_party/vulkan_memory_allocator': Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '56300b29fbfcc693ee6609ddad3fdd5b7a449a21', @@ -2903,7 +2903,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'c01b768bce4a143e152c1870b6ba99ea6267d2b0', 'src/third_party/webgpu-cts/src': - Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'de07a4258079a3a7065a0c2aa8d39fe20f8ab5c2', + Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '75af759c3c6bc4cb77a0df7c735a8a2061acaa37', 'src/third_party/webpagereplay': Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'), @@ -4445,7 +4445,7 @@ 'src/chrome/browser/glic/resources/internal': { 'url': Var('chrome_git') + '/chrome/browser/glic/resources/internal.git' + '@' + - '92c43ad104487e9b5e8814dee4be94e18656fa27', + '4fd0ec8889b4f3eda472f992d20957a077bc202e', 'condition': 'checkout_src_internal', }, @@ -4635,7 +4635,7 @@ 'src/components/optimization_guide/internal': { 'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' + - '6200c82b11623748653bcac9e5293f347bf072fb', + 'b9de30fc7bc555c84f54acac23b132a3fc37aad0', 'condition': 'checkout_src_internal', },
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSelectionDropdownMenuDelegate.java b/android_webview/java/src/org/chromium/android_webview/AwSelectionDropdownMenuDelegate.java index 7daef1c..8f7edcf 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwSelectionDropdownMenuDelegate.java +++ b/android_webview/java/src/org/chromium/android_webview/AwSelectionDropdownMenuDelegate.java
@@ -166,7 +166,7 @@ @Override public ListItem getDivider() { - return BasicListMenu.buildMenuDivider(); + return BasicListMenu.buildMenuDivider(/* isIncognito= */ false); } @Override
diff --git a/android_webview/java/src/org/chromium/android_webview/variations/VariationsSeedLoader.java b/android_webview/java/src/org/chromium/android_webview/variations/VariationsSeedLoader.java index eeb367a..60171a8d 100644 --- a/android_webview/java/src/org/chromium/android_webview/variations/VariationsSeedLoader.java +++ b/android_webview/java/src/org/chromium/android_webview/variations/VariationsSeedLoader.java
@@ -40,6 +40,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.util.Date; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; @@ -116,6 +117,7 @@ private FutureTask<SeedLoadResult> mLoadTask; private final SeedServerCallback mSeedServerCallback = new SeedServerCallback(); + private AtomicBoolean mIsServiceBound = new AtomicBoolean(); private static void recordLoadSeedResult(@LoadSeedResult int result) { RecordHistogram.recordEnumeratedHistogram( @@ -374,6 +376,7 @@ public void onServiceConnectedImpl(ComponentName name, IBinder service) { // onServiceConnected is called on the app's main thread. Punt this back to the // background thread as this work is not time critical. + mIsServiceBound.set(true); PostTask.postTask( TaskTraits.BEST_EFFORT_MAY_BLOCK, () -> { @@ -385,14 +388,18 @@ } catch (RemoteException e) { Log.e(TAG, "Faild requesting seed", e); } finally { - ContextUtils.getApplicationContext().unbindService(this); + if (mIsServiceBound.get()) { + ContextUtils.getApplicationContext().unbindService(this); + } VariationsUtils.closeSafely(mNewSeedFd); } }); } @Override - public void onServiceDisconnected(ComponentName name) {} + public void onServiceDisconnected(ComponentName name) { + mIsServiceBound.set(false); + } } private static class SeedServerCallback extends IVariationsSeedServerCallback.Stub {
diff --git a/build/rust/allocator/BUILD.gn b/build/rust/allocator/BUILD.gn index f09314a..ca58163 100644 --- a/build/rust/allocator/BUILD.gn +++ b/build/rust/allocator/BUILD.gn
@@ -32,6 +32,10 @@ sources = [ "lib.rs" ] crate_root = "lib.rs" cxx_bindings = [ "lib.rs" ] + rustflags = [ + "--cfg", + "mangle_alloc_error_handler", + ] deps = [ ":allocator_impls" ] @@ -40,7 +44,7 @@ allow_unsafe = true if (use_cpp_allocator_impls) { - rustflags = [ + rustflags += [ "--cfg", "use_cpp_allocator_impls", ]
diff --git a/build/rust/allocator/lib.rs b/build/rust/allocator/lib.rs index b8b67d9..4e2dad3 100644 --- a/build/rust/allocator/lib.rs +++ b/build/rust/allocator/lib.rs
@@ -57,13 +57,17 @@ #[linkage = "weak"] static __rust_no_alloc_shim_is_unstable: u8 = 0; -#[no_mangle] +// Mangle the symbol name as rustc expects. +#[cfg_attr(mangle_alloc_error_handler, rustc_std_internal_symbol)] +#[cfg_attr(not(mangle_alloc_error_handler), no_mangle)] +#[allow(non_upper_case_globals)] #[linkage = "weak"] static __rust_alloc_error_handler_should_panic: u8 = 0; // Mangle the symbol name as rustc expects. #[cfg_attr(mangle_alloc_error_handler, rustc_std_internal_symbol)] #[cfg_attr(not(mangle_alloc_error_handler), no_mangle)] +#[allow(non_upper_case_globals)] #[linkage = "weak"] fn __rust_alloc_error_handler(_size: usize, _align: usize) { unsafe { ffi::crash_immediately() }
diff --git a/build/rust/std/rules/BUILD.gn b/build/rust/std/rules/BUILD.gn index 6a82343..ba1bfe86 100644 --- a/build/rust/std/rules/BUILD.gn +++ b/build/rust/std/rules/BUILD.gn
@@ -288,232 +288,232 @@ } cargo_crate("compiler_builtins") { crate_type = "rlib" - crate_root = "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/lib.rs" + crate_root = "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/lib.rs" sources = [ - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/aarch64.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/aarch64_linux.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/arm.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/arm_linux.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/float/add.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/float/cmp.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/float/conv.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/float/div.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/float/extend.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/float/mod.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/float/mul.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/float/pow.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/float/sub.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/float/traits.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/float/trunc.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/hexagon.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/addsub.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/big.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/bswap.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/leading_zeros.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/mod.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/mul.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/sdiv.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/shift.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/specialized_div_rem/asymmetric.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/specialized_div_rem/binary_long.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/specialized_div_rem/delegate.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/specialized_div_rem/mod.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/specialized_div_rem/norm_shift.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/specialized_div_rem/trifecta.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/trailing_zeros.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/traits.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/int/udiv.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/lib.miri.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/lib.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/macros.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/math.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/mem/impls.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/mem/mod.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/mem/x86_64.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/probestack.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/riscv.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/x86.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/x86_64.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/aarch64.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/aarch64_linux.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/arm.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/arm_linux.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/float/add.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/float/cmp.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/float/conv.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/float/div.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/float/extend.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/float/mod.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/float/mul.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/float/pow.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/float/sub.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/float/traits.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/float/trunc.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/hexagon.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/addsub.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/big.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/bswap.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/leading_zeros.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/mod.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/mul.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/sdiv.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/shift.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/specialized_div_rem/asymmetric.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/specialized_div_rem/binary_long.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/specialized_div_rem/delegate.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/specialized_div_rem/mod.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/specialized_div_rem/norm_shift.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/specialized_div_rem/trifecta.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/trailing_zeros.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/traits.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/int/udiv.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/lib.miri.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/lib.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/macros.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/math.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/mem/impls.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/mem/mod.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/mem/x86_64.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/probestack.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/riscv.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/x86.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/x86_64.rs", ] inputs = [ - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/acos.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/acosf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/acosh.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/acoshf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/arch/aarch64.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/arch/i586.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/arch/i686.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/arch/mod.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/arch/wasm32.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/asin.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/asinf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/asinh.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/asinhf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/atan.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/atan2.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/atan2f.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/atanf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/atanh.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/atanhf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/cbrt.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/cbrtf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/ceil.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/ceilf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/ceilf128.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/ceilf16.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/copysign.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/copysignf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/copysignf128.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/copysignf16.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/cos.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/cosf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/cosh.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/coshf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/erf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/erff.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/exp.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/exp10.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/exp10f.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/exp2.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/exp2f.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/expf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/expm1.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/expm1f.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/expo2.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fabs.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fabsf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fabsf128.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fabsf16.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fdim.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fdimf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fdimf128.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fdimf16.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/floor.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/floorf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/floorf128.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/floorf16.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fma.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fma_wide.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fmin_fmax.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fminimum_fmaximum.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fminimum_fmaximum_num.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fmod.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fmodf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fmodf128.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/fmodf16.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/frexp.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/frexpf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/ceil.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/copysign.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/fabs.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/fdim.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/floor.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/fmax.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/fmaximum.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/fmaximum_num.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/fmin.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/fminimum.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/fminimum_num.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/fmod.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/mod.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/rint.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/round.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/scalbn.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/sqrt.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/generic/trunc.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/hypot.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/hypotf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/ilogb.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/ilogbf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/j0.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/j0f.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/j1.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/j1f.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/jn.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/jnf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/k_cos.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/k_cosf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/k_expo2.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/k_expo2f.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/k_sin.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/k_sinf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/k_tan.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/k_tanf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/ldexp.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/ldexpf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/ldexpf128.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/ldexpf16.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/lgamma.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/lgamma_r.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/lgammaf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/lgammaf_r.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/log.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/log10.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/log10f.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/log1p.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/log1pf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/log2.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/log2f.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/logf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/mod.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/modf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/modff.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/nextafter.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/nextafterf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/pow.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/powf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/rem_pio2.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/rem_pio2_large.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/rem_pio2f.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/remainder.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/remainderf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/remquo.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/remquof.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/rint.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/round.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/roundeven.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/roundf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/roundf128.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/roundf16.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/scalbn.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/scalbnf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/scalbnf128.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/scalbnf16.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/sin.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/sincos.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/sincosf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/sinf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/sinh.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/sinhf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/sqrt.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/sqrtf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/sqrtf128.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/sqrtf16.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/support/big/tests.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/support/big.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/support/env.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/support/float_traits.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/support/hex_float.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/support/int_traits.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/support/macros.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/support/mod.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/tan.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/tanf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/tanh.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/tanhf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/tgamma.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/tgammaf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/trunc.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/truncf.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/truncf128.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../libm/src/math/truncf16.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/acos.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/acosf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/acosh.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/acoshf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/arch/aarch64.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/arch/i586.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/arch/i686.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/arch/mod.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/arch/wasm32.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/asin.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/asinf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/asinh.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/asinhf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/atan.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/atan2.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/atan2f.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/atanf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/atanh.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/atanhf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/cbrt.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/cbrtf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/ceil.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/ceilf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/ceilf128.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/ceilf16.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/copysign.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/copysignf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/copysignf128.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/copysignf16.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/cos.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/cosf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/cosh.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/coshf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/erf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/erff.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/exp.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/exp10.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/exp10f.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/exp2.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/exp2f.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/expf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/expm1.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/expm1f.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/expo2.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fabs.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fabsf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fabsf128.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fabsf16.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fdim.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fdimf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fdimf128.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fdimf16.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/floor.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/floorf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/floorf128.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/floorf16.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fma.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fma_wide.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fmin_fmax.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fminimum_fmaximum.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fminimum_fmaximum_num.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fmod.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fmodf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fmodf128.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/fmodf16.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/frexp.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/frexpf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/ceil.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/copysign.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/fabs.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/fdim.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/floor.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/fmax.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/fmaximum.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/fmaximum_num.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/fmin.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/fminimum.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/fminimum_num.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/fmod.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/mod.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/rint.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/round.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/scalbn.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/sqrt.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/generic/trunc.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/hypot.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/hypotf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/ilogb.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/ilogbf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/j0.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/j0f.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/j1.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/j1f.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/jn.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/jnf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/k_cos.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/k_cosf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/k_expo2.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/k_expo2f.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/k_sin.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/k_sinf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/k_tan.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/k_tanf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/ldexp.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/ldexpf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/ldexpf128.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/ldexpf16.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/lgamma.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/lgamma_r.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/lgammaf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/lgammaf_r.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/log.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/log10.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/log10f.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/log1p.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/log1pf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/log2.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/log2f.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/logf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/mod.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/modf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/modff.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/nextafter.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/nextafterf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/pow.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/powf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/rem_pio2.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/rem_pio2_large.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/rem_pio2f.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/remainder.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/remainderf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/remquo.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/remquof.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/rint.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/round.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/roundeven.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/roundf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/roundf128.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/roundf16.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/scalbn.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/scalbnf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/scalbnf128.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/scalbnf16.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/sin.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/sincos.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/sincosf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/sinf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/sinh.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/sinhf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/sqrt.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/sqrtf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/sqrtf128.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/sqrtf16.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/support/big/tests.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/support/big.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/support/env.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/support/float_traits.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/support/hex_float.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/support/int_traits.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/support/macros.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/support/mod.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/tan.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/tanf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/tanh.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/tanhf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/tgamma.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/tgammaf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/trunc.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/truncf.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/truncf128.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../libm/src/math/truncf16.rs", ] no_std = true # Unit tests skipped. Generate with --with-tests to include them. build_native_rust_unit_tests = false edition = "2021" - cargo_pkg_version = "0.1.151" + cargo_pkg_version = "0.1.152" cargo_pkg_authors = "Jorge Aparicio <japaricious@gmail.com>" cargo_pkg_name = "compiler_builtins" cargo_pkg_description = "Compiler intrinsics used by the Rust compiler. Also available for other targets if necessary!" @@ -540,9 +540,9 @@ "core", "rustc-dep-of-std", ] - build_root = "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/build.rs" - build_sources = [ "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/build.rs" ] - build_script_inputs = [ "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.151/src/../configure.rs" ] + build_root = "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/build.rs" + build_sources = [ "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/build.rs" ] + build_script_inputs = [ "//third_party/rust-toolchain/lib/rustlib/src/rust/library/vendor/compiler_builtins-0.1.152/src/../configure.rs" ] rustenv = [ "CFG_DISABLE_UNSTABLE_FEATURES=0", "STD_ENV_ARCH=$rust_target_arch", @@ -2310,6 +2310,9 @@ "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/os/android/mod.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/os/android/net.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/os/android/raw.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/os/cygwin/fs.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/os/cygwin/mod.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/os/cygwin/raw.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/os/darwin/fs.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/os/darwin/mod.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/os/darwin/raw.rs", @@ -2611,7 +2614,6 @@ "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/uefi/helpers.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/uefi/mod.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/uefi/os.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/uefi/process.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/uefi/tests.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/uefi/thread.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/uefi/time.rs", @@ -2619,6 +2621,7 @@ "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/env.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/fd.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/fd/tests.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/fuchsia.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/futex.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/kernel_copy.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/kernel_copy/tests.rs", @@ -2629,17 +2632,6 @@ "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/os.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/os/tests.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/pipe.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/process/mod.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/process/process_common.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/process/process_common/tests.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/process/process_fuchsia.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/process/process_unix.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/process/process_unix/tests.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/process/process_unsupported.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/process/process_unsupported/wait_status.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/process/process_unsupported/wait_status/tests.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/process/process_vxworks.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/process/zircon.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/stack_overflow.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/sync/condvar.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unix/sync/mod.rs", @@ -2654,7 +2646,6 @@ "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unsupported/mod.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unsupported/os.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unsupported/pipe.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unsupported/process.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unsupported/thread.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/unsupported/time.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/wasi/args.rs", @@ -2686,8 +2677,6 @@ "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/windows/os.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/windows/os/tests.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/windows/pipe.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/windows/process.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/windows/process/tests.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/windows/stack_overflow.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/windows/stack_overflow_uwp.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/pal/windows/thread.rs", @@ -2717,13 +2706,28 @@ "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/personality/emcc.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/personality/gcc.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/personality/mod.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/process/mod.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/process/uefi.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/process/unix/common.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/process/unix/common/tests.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/process/unix/fuchsia.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/process/unix/mod.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/process/unix/unix.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/process/unix/unix/tests.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/process/unix/unsupported.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/process/unix/unsupported/wait_status.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/process/unix/unsupported/wait_status/tests.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/process/unix/vxworks.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/process/unsupported.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/process/windows.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/process/windows/tests.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/random/apple.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/random/arc4random.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/random/espidf.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/random/fuchsia.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/random/getentropy.rs", + "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/random/getrandom.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/random/hermit.rs", - "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/random/horizon.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/random/linux.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/random/mod.rs", "//third_party/rust-toolchain/lib/rustlib/src/rust/library/std/src/sys/random/redox.rs",
diff --git a/chrome/android/features/tab_ui/public/android/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeUtil.java b/chrome/android/features/tab_ui/public/android/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeUtil.java index 1e91dc0..8e1ea0c3 100644 --- a/chrome/android/features/tab_ui/public/android/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeUtil.java +++ b/chrome/android/features/tab_ui/public/android/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeUtil.java
@@ -12,15 +12,17 @@ import androidx.annotation.DrawableRes; import androidx.core.content.res.ResourcesCompat; +import org.chromium.build.annotations.NullMarked; import org.chromium.chrome.browser.ui.theme.ChromeSemanticColorUtils; import org.chromium.components.browser_ui.styles.ChromeColors; import org.chromium.components.browser_ui.styles.SemanticColorUtils; import org.chromium.ui.util.ColorUtils; /** - * Common tab UI theme utils for public use. - * Internal themes are provided via @{@link TabUiThemeProvider} + * Common tab UI theme utils for public use. Internal themes are provided via @{@link + * TabUiThemeProvider} */ +@NullMarked public class TabUiThemeUtil { private static final float MAX_TAB_STRIP_TAB_WIDTH_DP = 265.f; private static final float DIVIDER_FOLIO_LIGHT_OPACITY = 0.3f;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinator.java index 27db07c6..adb8f9d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinator.java
@@ -178,45 +178,50 @@ protected void buildMenuActionItems(ModelList itemList, Integer id) { var tab = mTabModelSupplier.get().getTabById(id); if (tab == null) return; + boolean isIncognito = mTabModelSupplier.get().isIncognitoBranded(); itemList.add( - BrowserUiListMenuUtils.buildMenuListItem( - R.string.add_tab_to_group, R.id.add_to_tab_group, /* startIconId= */ 0)); + BrowserUiListMenuUtils.buildMenuListItemWithIncognitoBranding( + R.string.add_tab_to_group, + R.id.add_to_tab_group, + isIncognito, + /* enabled= */ true)); if (tab.getTabGroupId() != null) { // Show the option to remove the tab from its group iff the tab is already in a group. itemList.add( - BrowserUiListMenuUtils.buildMenuListItem( + BrowserUiListMenuUtils.buildMenuListItemWithIncognitoBranding( R.string.remove_tab_from_group, R.id.remove_from_tab_group, - /* startIconId= */ 0)); + isIncognito, + /* enabled= */ true)); } if (tab.getTabGroupId() == null && MultiWindowUtils.isMultiInstanceApi31Enabled()) { // Show the option to move the tab to another window iff the tab is not in a group. Activity activity = mWindowAndroid.getActivity().get(); itemList.add( - BrowserUiListMenuUtils.buildMenuListItem( + BrowserUiListMenuUtils.buildMenuListItemWithIncognitoBranding( activity.getResources() .getQuantityString( R.plurals.move_tab_to_another_window, MultiWindowUtils.getInstanceCount()), R.id.move_to_other_window_menu_id, - /* startIconId= */ 0, + isIncognito, /* enabled= */ true)); } - itemList.add(buildMenuDivider()); + itemList.add(buildMenuDivider(isIncognito)); if (ShareUtils.shouldEnableShare(tab)) { itemList.add( - BrowserUiListMenuUtils.buildMenuListItem( - R.string.share, R.id.share_tab, /* startIconId= */ 0)); + BrowserUiListMenuUtils.buildMenuListItemWithIncognitoBranding( + R.string.share, R.id.share_tab, isIncognito, /* enabled= */ true)); } itemList.add( - BrowserUiListMenuUtils.buildMenuListItem( - R.string.close_tab, R.id.close_tab, /* startIconId= */ 0)); + BrowserUiListMenuUtils.buildMenuListItemWithIncognitoBranding( + R.string.close_tab, R.id.close_tab, isIncognito, /* enabled= */ true)); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java index 47ed65a..70979e2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
@@ -566,6 +566,10 @@ .with( ListSectionDividerProperties.RIGHT_PADDING_DIMEN_ID, R.dimen.list_menu_item_horizontal_padding); + if (mTabModelSupplier.get().isIncognitoBranded()) { + builder.with( + ListSectionDividerProperties.COLOR_ID, R.color.divider_line_bg_color_light); + } return new ListItem(ListMenuItemType.DIVIDER, builder.build()); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java index 078584e..b8b825ffb 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
@@ -17,7 +17,6 @@ import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; import static androidx.test.espresso.matcher.ViewMatchers.hasSibling; import static androidx.test.espresso.matcher.ViewMatchers.isChecked; -import static androidx.test.espresso.matcher.ViewMatchers.isClickable; import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.withChild; @@ -25,7 +24,6 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.emptyIterable; @@ -3292,83 +3290,6 @@ settingsActivity, "site_settings_rws_grouped_website_settings_delete_dialog"); } - @Test - @SmallTest - public void deleteSingleSiteDataRemovesRowSingleWebsiteSettings() throws Exception { - final String currentSiteUrl = "one-test.com"; - final String rwsMemberUrl = "two-test.com"; - final SettingsActivity settingsActivity = - SiteSettingsTestUtils.startSingleWebsitePreferences( - getRwsOwnerSiteForUrls(currentSiteUrl, rwsMemberUrl)); - - onView( - allOf( - withText(currentSiteUrl), - hasSibling(withText("http")), - not(isDescendantOfA(isClickable())))) - .check(matches(isDisplayed())); - onView(withText(rwsMemberUrl)).check(matches(isDisplayed())); - - onView( - allOf( - withId(R.id.image_view_widget), - withContentDescription(containsString(rwsMemberUrl)))) - .check(matches(isDisplayed())) - .perform(click()); - onView(withText(R.string.website_reset_confirmation)).check(matches(isDisplayed())); - onView(withText(R.string.website_reset)) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - .perform(click()); - onView(withText(rwsMemberUrl)).check(doesNotExist()); - onView( - allOf( - withText(currentSiteUrl), - hasSibling(withText("http")), - not(isDescendantOfA(isClickable())))) - .check(matches(isDisplayed())); - } - - @Test - @SmallTest - public void deleteSingleSiteDataRemovesRowGroupedWebsiteSettings() throws Exception { - final String currentSiteUrl = "one-test.com"; - final String rwsMemberUrl = "two-test.com"; - - final SettingsActivity settingsActivity = - SiteSettingsTestUtils.startGroupedWebsitesPreferences( - getRwsSiteGroupForUrls(currentSiteUrl, rwsMemberUrl)); - - // RWS row and "Sites under" row will have equivalent views in the view hierarchy. - // The RWS row is higher up on the page and therefore be the first matched. - onView( - allOf( - withText(currentSiteUrl), - hasSibling(withText("http")), - not(isDescendantOfA(isClickable())))) - .check(matches(isDisplayed())); - onView(withText(rwsMemberUrl)).check(matches(isDisplayed())); - - onView( - allOf( - withId(R.id.image_view_widget), - withContentDescription(containsString(rwsMemberUrl)))) - .check(matches(isDisplayed())) - .perform(click()); - onView(withText(R.string.website_reset_confirmation)).check(matches(isDisplayed())); - onView(withText(R.string.website_reset)) - .inRoot(isDialog()) - .check(matches(isDisplayed())) - .perform(click()); - onView(withText(rwsMemberUrl)).check(doesNotExist()); - onView( - allOf( - withText(currentSiteUrl), - hasSibling(withText("http")), - not(isDescendantOfA(isClickable())))) - .check(matches(isDisplayed())); - } - // TODO(crbug.com/396463421): Remove once RWS UI V2 launched. @Test @SmallTest
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinatorUnitTest.java index 76bcc43..501576f 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinatorUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabContextMenuCoordinatorUnitTest.java
@@ -12,6 +12,7 @@ import static org.chromium.chrome.browser.contextmenu.ContextMenuCoordinator.ListItemType.DIVIDER; import static org.chromium.chrome.browser.share.ShareDelegate.ShareOrigin.TAB_STRIP_CONTEXT_MENU; +import static org.chromium.ui.listmenu.ListSectionDividerProperties.COLOR_ID; import android.app.Activity; @@ -114,7 +115,6 @@ when(mWindowAndroid.getActivity()).thenReturn(mWeakReferenceActivity); when(mWeakReferenceActivity.get()).thenReturn(activity); mTabModel = spy(new MockTabModel(mProfile, null)); - when(mTabModel.isIncognito()).thenReturn(false); when(mTabModel.getTabById(TAB_ID)).thenReturn(mTab1); when(mTabModel.getTabById(TAB_OUTSIDE_OF_GROUP_ID)).thenReturn(mTabOutsideOfGroup); when(mTabModel.getTabById(NON_URL_TAB_ID)).thenReturn(mNonUrlTab); @@ -128,8 +128,18 @@ when(mNonUrlTab.getUrl()).thenReturn(CHROME_SCHEME_URL); when(mTabGroupModelFilter.getTabModel()).thenReturn(mTabModel); when(mTabGroupModelFilter.getTabUngrouper()).thenReturn(mTabUngrouper); - when(mProfile.isOffTheRecord()).thenReturn(true); mSavedTabGroup.collaborationId = COLLABORATION_ID; + setupWithIncognito(/* incognito= */ false); // Most tests will run not in incognito mode + initializeCoordinator(); + } + + private void setupWithIncognito(boolean incognito) { + when(mTabModel.isIncognito()).thenReturn(incognito); + when(mTabModel.isIncognitoBranded()).thenReturn(incognito); + when(mProfile.isOffTheRecord()).thenReturn(incognito); + } + + private void initializeCoordinator() { mOnItemClickedCallback = TabContextMenuCoordinator.getMenuItemClickedCallback( () -> mTabModel, @@ -173,6 +183,10 @@ // List item 3 assertEquals(DIVIDER, modelList.get(2).type); + assertEquals( + "Expected divider to have have COLOR_ID unset when not in incognito mode", + 0, + modelList.get(2).model.get(COLOR_ID)); // List item 4 assertEquals(R.string.share, modelList.get(3).model.get(ListMenuItemProperties.TITLE_ID)); @@ -344,6 +358,71 @@ } @Test + public void testListMenuItems_incognito() { + setupWithIncognito(/* incognito= */ true); + initializeCoordinator(); + var modelList = new ModelList(); + mTabContextMenuCoordinator.buildMenuActionItems(modelList, TAB_ID); + + assertEquals("Number of items in the list menu is incorrect", 5, modelList.size()); + + // List item 1 + assertEquals( + R.string.add_tab_to_group, + modelList.get(0).model.get(ListMenuItemProperties.TITLE_ID)); + assertEquals( + R.id.add_to_tab_group, + modelList.get(0).model.get(ListMenuItemProperties.MENU_ITEM_ID)); + assertEquals( + "Expected text appearance ID to be set to" + + " R.style.TextAppearance_TextLarge_Primary_Baseline_Light in incognito", + R.style.TextAppearance_TextLarge_Primary_Baseline_Light, + modelList.get(0).model.get(ListMenuItemProperties.TEXT_APPEARANCE_ID)); + + // List item 2 + assertEquals( + R.string.remove_tab_from_group, + modelList.get(1).model.get(ListMenuItemProperties.TITLE_ID)); + assertEquals( + R.id.remove_from_tab_group, + modelList.get(1).model.get(ListMenuItemProperties.MENU_ITEM_ID)); + assertEquals( + "Expected text appearance ID to be set to" + + " R.style.TextAppearance_TextLarge_Primary_Baseline_Light in incognito", + R.style.TextAppearance_TextLarge_Primary_Baseline_Light, + modelList.get(1).model.get(ListMenuItemProperties.TEXT_APPEARANCE_ID)); + + // List item 3 + assertEquals(DIVIDER, modelList.get(2).type); + assertEquals( + "Expected divider to have COLOR_ID set to R.color.divider_line_bg_color_light in" + + " incognito mode", + R.color.divider_line_bg_color_light, + modelList.get(2).model.get(COLOR_ID)); + + // List item 4 + assertEquals(R.string.share, modelList.get(3).model.get(ListMenuItemProperties.TITLE_ID)); + assertEquals( + R.id.share_tab, modelList.get(3).model.get(ListMenuItemProperties.MENU_ITEM_ID)); + assertEquals( + "Expected text appearance ID to be set to" + + " R.style.TextAppearance_TextLarge_Primary_Baseline_Light in incognito", + R.style.TextAppearance_TextLarge_Primary_Baseline_Light, + modelList.get(3).model.get(ListMenuItemProperties.TEXT_APPEARANCE_ID)); + + // List item 5 + assertEquals( + R.string.close_tab, modelList.get(4).model.get(ListMenuItemProperties.TITLE_ID)); + assertEquals( + R.id.close_tab, modelList.get(4).model.get(ListMenuItemProperties.MENU_ITEM_ID)); + assertEquals( + "Expected text appearance ID to be set to" + + " R.style.TextAppearance_TextLarge_Primary_Baseline_Light in incognito", + R.style.TextAppearance_TextLarge_Primary_Baseline_Light, + modelList.get(4).model.get(ListMenuItemProperties.TEXT_APPEARANCE_ID)); + } + + @Test @Feature("Tab Strip Context Menu") public void testRemoveFromGroup() { mOnItemClickedCallback.onClick(R.id.remove_from_tab_group, TAB_ID, COLLABORATION_ID);
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 83eb6d1a..ccb0cc1 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -1360,13 +1360,26 @@ "https://discoveryengine.googleapis.com/v1alpha/projects/301214329925/" "locations/global/collections/default_collection/engines/" "neuravibeblendedsearch_1727383849310/completionConfig:completeQuery"}}; +const FeatureEntry::FeatureParam kOmniboxSearchAggregatorDemoParams[] = { + {"name", "NeuraVibe"}, + {"shortcut", "nv"}, + {"icon_url", "https://gstatic.com/vertexaisearch/favicon.png"}, + {"search_url", + "https://vertexaisearch.cloud.google.com/home/cid/" + "f6cd931b-d10e-475f-b424-6270982c15f6?q={searchTerms}"}, + {"suggest_url", + "https://discoveryengine.googleapis.com/v1alpha/projects/121968733869/" + "locations/global/collections/default_collection/engines/" + "neuravibeapp_1738849257936/completionConfig:completeQuery"}}; const FeatureEntry::FeatureVariation kOmniboxSearchAggregatorVariations[] = { {"prod", kOmniboxSearchAggregatorProdParams, std::size(kOmniboxSearchAggregatorProdParams), nullptr}, {"staging", kOmniboxSearchAggregatorStagingParams, std::size(kOmniboxSearchAggregatorStagingParams), nullptr}, {"alternate", kOmniboxSearchAggregatorAlternateParams, - std::size(kOmniboxSearchAggregatorAlternateParams), nullptr}}; + std::size(kOmniboxSearchAggregatorAlternateParams), nullptr}, + {"demo", kOmniboxSearchAggregatorDemoParams, + std::size(kOmniboxSearchAggregatorDemoParams), nullptr}}; const FeatureEntry::FeatureParam kOmniboxUrlSuggestionsOnFocusTwoDayWindow[] = { {"OnFocusMostVisitedRecencyWindow", "1"},
diff --git a/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc index 144c868..1fb6a20 100644 --- a/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc +++ b/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc
@@ -135,7 +135,6 @@ #include "chrome/browser/browser_process_platform_part.h" #include "chrome/browser/component_updater/smart_dim_component_installer.h" #include "chrome/browser/extensions/component_loader.h" -#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/policy/chrome_policy_conversions_client.h" #include "chrome/browser/profiles/profile.h" @@ -220,8 +219,8 @@ #include "extensions/browser/extension_action_manager.h" #include "extensions/browser/extension_function.h" #include "extensions/browser/extension_function_registry.h" +#include "extensions/browser/extension_registrar.h" #include "extensions/browser/extension_registry.h" -#include "extensions/browser/extension_system.h" #include "extensions/browser/extension_util.h" #include "extensions/common/api/extension_action/action_info.h" #include "extensions/common/manifest_handlers/background_info.h" @@ -6495,9 +6494,8 @@ args()); EXTENSION_FUNCTION_VALIDATE(params); - extensions::ExtensionService* extension_service = - extensions::ExtensionSystem::Get(browser_context())->extension_service(); - extension_service->component_loader()->Remove(params->extension_id); + extensions::ComponentLoader::Get(browser_context()) + ->Remove(params->extension_id); return RespondNow(NoArguments()); }
diff --git a/chrome/browser/ash/file_manager/file_manager_test_util.cc b/chrome/browser/ash/file_manager/file_manager_test_util.cc index 2fd5513d..51c9d4cc 100644 --- a/chrome/browser/ash/file_manager/file_manager_test_util.cc +++ b/chrome/browser/ash/file_manager/file_manager_test_util.cc
@@ -136,16 +136,15 @@ CHECK(profile); extensions::ComponentLoader::EnableBackgroundExtensionsForTesting(); - extensions::ExtensionService* service = - extensions::ExtensionSystem::Get(profile)->extension_service(); - service->component_loader()->AddDefaultComponentExtensions(false); + auto* component_loader = extensions::ComponentLoader::Get(profile); + component_loader->AddDefaultComponentExtensions(false); #if BUILDFLAG(GOOGLE_CHROME_BRANDING) // QuickOffice loads from rootfs at /usr/share/chromeos-assets/quickoffce // which does not exist on bots for tests, so load test version. base::FilePath data_dir; CHECK(base::PathService::Get(chrome::DIR_TEST_DATA, &data_dir)); base::RunLoop run_loop; - service->component_loader()->AddComponentFromDirWithManifestFilename( + component_loader->AddComponentFromDirWithManifestFilename( data_dir.Append("chromeos/file_manager/quickoffice"), extension_misc::kQuickOfficeComponentExtensionId, extensions::kManifestFilename, extensions::kManifestFilename, @@ -157,6 +156,8 @@ // Invoke it here as well, otherwise migrated extensions will remain installed // for the duration of the test. Note this may result in immediately // uninstalling an extension just installed above. + extensions::ExtensionService* service = + extensions::ExtensionSystem::Get(profile)->extension_service(); service->UninstallMigratedExtensionsForTest(); }
diff --git a/chrome/browser/ash/input_method/component_extension_ime_manager_delegate_impl.cc b/chrome/browser/ash/input_method/component_extension_ime_manager_delegate_impl.cc index 4ae36bb..1397d84 100644 --- a/chrome/browser/ash/input_method/component_extension_ime_manager_delegate_impl.cc +++ b/chrome/browser/ash/input_method/component_extension_ime_manager_delegate_impl.cc
@@ -24,7 +24,6 @@ #include "base/trace_event/trace_event.h" #include "build/branding_buildflags.h" #include "chrome/browser/extensions/component_loader.h" -#include "chrome/browser/extensions/extension_service.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/grit/browser_resources.h" @@ -36,7 +35,6 @@ #include "extensions/browser/extension_pref_value_map_factory.h" #include "extensions/browser/extension_registrar.h" #include "extensions/browser/extension_registry.h" -#include "extensions/browser/extension_system.h" #include "extensions/common/extension.h" #include "extensions/common/manifest_constants.h" #include "net/base/url_util.h" @@ -105,15 +103,6 @@ const char kImePathKeyName[] = "ime_path"; -extensions::ComponentLoader* GetComponentLoader( - content::BrowserContext* context) { - extensions::ExtensionSystem* extension_system = - extensions::ExtensionSystem::Get(context); - extensions::ExtensionService* extension_service = - extension_system->extension_service(); - return extension_service->component_loader(); -} - void DoLoadExtension(content::BrowserContext* context, const std::string& extension_id, const std::string& manifest, @@ -128,7 +117,7 @@ return; } const std::string loaded_extension_id = - GetComponentLoader(context)->Add(manifest, file_path); + extensions::ComponentLoader::Get(context)->Add(manifest, file_path); if (loaded_extension_id.empty()) { LOG(ERROR) << "Failed to add an IME extension(id=\"" << extension_id << ", path=\"" << file_path.LossyDisplayName()
diff --git a/chrome/browser/ash/profiles/signin_profile_handler.cc b/chrome/browser/ash/profiles/signin_profile_handler.cc index 3b7fb1f..ed239cd 100644 --- a/chrome/browser/ash/profiles/signin_profile_handler.cc +++ b/chrome/browser/ash/profiles/signin_profile_handler.cc
@@ -16,7 +16,6 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h" #include "chrome/browser/extensions/component_loader.h" -#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser_list.h" @@ -24,7 +23,6 @@ #include "chromeos/ash/components/browser_context_helper/browser_context_types.h" #include "components/crx_file/id_util.h" #include "components/user_manager/user_manager.h" -#include "extensions/browser/extension_system.h" namespace ash { namespace { @@ -145,9 +143,7 @@ const std::set<std::string> allowed_ids_hashes( std::begin(kNonRiskyExtensionsIdsHashes), std::end(kNonRiskyExtensionsIdsHashes)); - auto* component_loader = extensions::ExtensionSystem::Get(signin_profile) - ->extension_service() - ->component_loader(); + auto* component_loader = extensions::ComponentLoader::Get(signin_profile); const std::vector<std::string> loaded_extensions = component_loader->GetRegisteredComponentExtensionsIds(); for (const auto& el : loaded_extensions) {
diff --git a/chrome/browser/autocomplete/keyword_extensions_delegate_impl.cc b/chrome/browser/autocomplete/keyword_extensions_delegate_impl.cc index ab6fd75..07edcb8 100644 --- a/chrome/browser/autocomplete/keyword_extensions_delegate_impl.cc +++ b/chrome/browser/autocomplete/keyword_extensions_delegate_impl.cc
@@ -138,15 +138,15 @@ } void KeywordExtensionsDelegateImpl::OnOmniboxSuggestionsReady( - omnibox_api::SendSuggestions::Params* suggestions, + const std::vector<ExtensionSuggestion>& suggestions, + const int request_id, const std::string& extension_id) { - DCHECK(suggestions); - // Ignore result if it's old or if the provider is done. Checking if the // provider is done prevents the extension from sending multiple sets of // suggestions for the same request. - if (suggestions->request_id != current_input_id_ || provider_->done()) + if (request_id != current_input_id_ || provider_->done()) { return; + } TemplateURLService* model = provider_->GetTemplateURLService(); DCHECK(model); @@ -165,30 +165,31 @@ const TemplateURL* template_url = model->GetTemplateURLForKeyword(keyword); DCHECK_EQ(extension_id, template_url->GetExtensionId()); - for (size_t i = 0; i < suggestions->suggest_results.size(); ++i) { - const omnibox_api::SuggestResult& suggestion = - suggestions->suggest_results[i]; - // We want to order these suggestions in descending order, so start with - // the relevance of the first result (added synchronously in Start()), - // and subtract 1 for each subsequent suggestion from the extension. - // We recompute the first match's relevance; we know that |complete| - // is true, because we wouldn't get results from the extension unless - // the full keyword had been typed. - int first_relevance = KeywordProvider::CalculateRelevance( - input.type(), true, true, input.prefer_keyword(), - input.allow_exact_keyword_match()); + // We want to order these suggestions in descending order, so start with + // the relevance of the first result, added synchronously in + // KeywordProvider::Start(), and subtract 1 for each subsequent suggestion + // from the extension. We recompute the first match's relevance; we know that + // |complete| is true, because we wouldn't get results from the extension + // unless the full keyword had been typed. + int first_relevance = KeywordProvider::CalculateRelevance( + input.type(), /*complete=*/true, /*support_replacement=*/true, + input.prefer_keyword(), input.allow_exact_keyword_match()); + + for (const ExtensionSuggestion& suggestion : suggestions) { // Because these matches are async, we should never let them become the // default match, lest we introduce race conditions in the omnibox user // interaction. extension_suggest_matches_.push_back(provider_->CreateAutocompleteMatch( template_url, input, keyword.length(), - base::UTF8ToUTF16(suggestion.content), false, first_relevance - (i + 1), - suggestion.deletable && *suggestion.deletable)); + base::UTF8ToUTF16(suggestion.content), false, --first_relevance, + suggestion.deletable)); AutocompleteMatch* match = &extension_suggest_matches_.back(); match->contents.assign(base::UTF8ToUTF16(suggestion.description)); - match->contents_class = - extensions::StyleTypesToACMatchClassifications(suggestion); + + // No match should have empty classifications. + CHECK(!suggestion.match_classifications.empty()); + match->contents_class = suggestion.match_classifications; } set_done(true);
diff --git a/chrome/browser/autocomplete/keyword_extensions_delegate_impl.h b/chrome/browser/autocomplete/keyword_extensions_delegate_impl.h index d4d6ffe9..0369dfa 100644 --- a/chrome/browser/autocomplete/keyword_extensions_delegate_impl.h +++ b/chrome/browser/autocomplete/keyword_extensions_delegate_impl.h
@@ -59,7 +59,8 @@ void OnOmniboxInputEntered() override; // OmniboxSuggestionsWatcher::Observer: void OnOmniboxSuggestionsReady( - extensions::api::omnibox::SendSuggestions::Params* suggestions, + const std::vector<ExtensionSuggestion>& suggestions, + const int request_id, const std::string& extension_id) override; void OnOmniboxDefaultSuggestionChanged() override;
diff --git a/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.cc b/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.cc index 33139f7c..372ad570 100644 --- a/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.cc +++ b/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.cc
@@ -99,19 +99,18 @@ } void UnscopedExtensionProviderDelegateImpl::OnOmniboxSuggestionsReady( - extensions::api::omnibox::SendSuggestions::Params* suggestions, + const std::vector<ExtensionSuggestion>& suggestions, + const int request_id, const std::string& extension_id) { - CHECK(suggestions); - // Discard suggestions // 1) with a stale request ID's. // 2) that come from an extension that has already returned suggestions. - // 4) if the provider is done. since this provider allows post done updates, + // 3) if the provider is done. since this provider allows post done updates, // it will only be done if the user closes the omnibox, arrows down in the // omnibox, or if all extensions have returned suggestions. - if (suggestions->request_id != current_request_id_ || + if (request_id != current_request_id_ || base::Contains(extension_id_to_group_id_map_, extension_id) || - provider_->done()) { + provider_->done() || suggestions.empty()) { return; } @@ -138,24 +137,19 @@ provider_->AddToSuggestionGroupsMap(current_group_id, std::move(group)); int first_relevance = 10000000; - int relevance_increment = 1; - - // If the number of suggestions already sent from the extension is greater - // than the allowed limit, resize the extension suggestion results. - if (suggestions->suggest_results.size() > kMaxSuggestionsPerExtension) { - suggestions->suggest_results.resize(kMaxSuggestionsPerExtension); - } - - for (const auto& suggestion : suggestions->suggest_results) { - // TODO(379141010): calculate relevance. - extension_suggest_matches_.push_back(CreateAutocompleteMatch( - suggestion, first_relevance - relevance_increment, extension_id)); - relevance_increment++; + for (const auto& suggestion : suggestions) { + extension_suggest_matches_.push_back( + CreateAutocompleteMatch(suggestion, --first_relevance, extension_id)); } ACMatches* matches = provider_->matches(); + // If the number of suggestions already sent from the extension is greater + // than the allowed limit, only show the first `kMaxSuggestionsPerExtension` + // suggestions . matches->insert(matches->end(), extension_suggest_matches_.begin(), - extension_suggest_matches_.end()); + std::min(extension_suggest_matches_.end(), + extension_suggest_matches_.begin() + + kMaxSuggestionsPerExtension)); // The only case where done can be be true is when all extensions have // returned suggestions. if (next_available_group_index_ == kReservedGroupIdMap.size() || @@ -176,11 +170,10 @@ AutocompleteMatch UnscopedExtensionProviderDelegateImpl::CreateAutocompleteMatch( - const omnibox_api::SuggestResult& suggestion, + const ExtensionSuggestion& suggestion, int relevance, const std::string& extension_id) { - AutocompleteMatch match(provider_.get(), relevance, - suggestion.deletable.value_or(false), + AutocompleteMatch match(provider_.get(), relevance, suggestion.deletable, AutocompleteMatchType::SEARCH_OTHER_ENGINE); std::u16string trimmed_suggestion_content; // Prevents DCHECK in `SplitKeywordFromInput` in AutocompleteInput which @@ -210,8 +203,9 @@ search_terms_args, provider_->GetTemplateURLService()->search_terms_data())); - match.contents_class = - extensions::StyleTypesToACMatchClassifications(suggestion); + // No match should have empty classifications. + CHECK(!suggestion.match_classifications.empty()); + match.contents_class = suggestion.match_classifications; match.suggestion_group_id = extension_id_to_group_id_map_[extension_id]; if (suggestion.actions) { @@ -222,7 +216,8 @@ base::BindRepeating( &UnscopedExtensionProviderDelegateImpl::OnActionExecuted, weak_factory_.GetWeakPtr(), extension_id, action.name, - suggestion.content))); + suggestion.content), + action.icon)); } }
diff --git a/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.h b/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.h index c9320b23..5572b6fe 100644 --- a/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.h +++ b/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.h
@@ -56,13 +56,14 @@ void OnOmniboxInputEntered() override; // OmniboxSuggestionsWatcher::Observer: void OnOmniboxSuggestionsReady( - omnibox_api::SendSuggestions::Params* suggestions, + const std::vector<ExtensionSuggestion>& suggestions, + const int request_id, const std::string& extension_id) override; private: // Creates an `AutocompleteMatch` for the suggestion. AutocompleteMatch CreateAutocompleteMatch( - const omnibox_api::SuggestResult& suggestion, + const ExtensionSuggestion& suggestion, int relevance, const std::string& extension_id);
diff --git a/chrome/browser/chromeos/extensions/contact_center_insights/contact_center_insights_extension_manager_factory.cc b/chrome/browser/chromeos/extensions/contact_center_insights/contact_center_insights_extension_manager_factory.cc index a69b414..418564e 100644 --- a/chrome/browser/chromeos/extensions/contact_center_insights/contact_center_insights_extension_manager_factory.cc +++ b/chrome/browser/chromeos/extensions/contact_center_insights/contact_center_insights_extension_manager_factory.cc
@@ -8,10 +8,9 @@ #include "base/check.h" #include "chrome/browser/chromeos/extensions/contact_center_insights/contact_center_insights_extension_manager.h" -#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/component_loader.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_selections.h" -#include "extensions/browser/extension_system.h" namespace chromeos { @@ -52,9 +51,7 @@ BuildServiceInstanceForBrowserContext( content::BrowserContext* context) const { auto* const profile = Profile::FromBrowserContext(context); - auto* const component_loader = ::extensions::ExtensionSystem::Get(profile) - ->extension_service() - ->component_loader(); + auto* const component_loader = ::extensions::ComponentLoader::Get(profile); return std::make_unique<ContactCenterInsightsExtensionManager>( component_loader, profile, std::make_unique<ContactCenterInsightsExtensionManager::Delegate>());
diff --git a/chrome/browser/chromeos/extensions/desk_api/desk_api_extension_manager_factory.cc b/chrome/browser/chromeos/extensions/desk_api/desk_api_extension_manager_factory.cc index 2d580bf4..16d3ca5 100644 --- a/chrome/browser/chromeos/extensions/desk_api/desk_api_extension_manager_factory.cc +++ b/chrome/browser/chromeos/extensions/desk_api/desk_api_extension_manager_factory.cc
@@ -5,10 +5,9 @@ #include "chrome/browser/chromeos/extensions/desk_api/desk_api_extension_manager_factory.h" #include "chrome/browser/chromeos/extensions/desk_api/desk_api_extension_manager.h" -#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/component_loader.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_selections.h" -#include "extensions/browser/extension_system.h" namespace chromeos { @@ -45,9 +44,7 @@ DeskApiExtensionManagerFactory::BuildServiceInstanceForBrowserContext( content::BrowserContext* context) const { auto* const profile = Profile::FromBrowserContext(context); - auto* const component_loader = ::extensions::ExtensionSystem::Get(profile) - ->extension_service() - ->component_loader(); + auto* const component_loader = ::extensions::ComponentLoader::Get(profile); return std::make_unique<DeskApiExtensionManager>( component_loader, profile, std::make_unique<DeskApiExtensionManager::Delegate>());
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler.cc b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler.cc index d127a151..1767227 100644 --- a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler.cc +++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler.cc
@@ -127,7 +127,7 @@ ->ReinstallProviderExtensions(); // Reinstall component extensions. - extension_service_->component_loader()->AddDefaultComponentExtensions( + extensions::ComponentLoader::Get(profile_)->AddDefaultComponentExtensions( /*skip_session_components=*/false); std::move(callback_).Run(std::nullopt);
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler_browsertest.cc b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler_browsertest.cc index 21e47e0..2b5587f 100644 --- a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler_browsertest.cc +++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler_browsertest.cc
@@ -17,7 +17,6 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/chrome_test_extension_loader.h" #include "chrome/browser/extensions/component_loader.h" -#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/pending_extension_manager.h" #include "chrome/browser/extensions/policy_test_utils.h" #include "chrome/browser/policy/extension_force_install_mixin.h" @@ -29,7 +28,6 @@ #include "content/public/test/test_utils.h" #include "extensions/browser/extension_registrar.h" #include "extensions/browser/extension_registry.h" -#include "extensions/browser/extension_system.h" #include "extensions/browser/test_extension_registry_observer.h" #include "extensions/common/extension.h" #include "extensions/common/extension_builder.h" @@ -173,12 +171,10 @@ } void WaitForComponentExtensionsInstall() { - extensions::ExtensionService* extension_service = - extensions::ExtensionSystem::Get(GetActiveUserProfile()) - ->extension_service(); + auto* component_loader = + extensions::ComponentLoader::Get(GetActiveUserProfile()); std::vector<std::string> registered_component_extensions = - extension_service->component_loader() - ->GetRegisteredComponentExtensionsIds(); + component_loader->GetRegisteredComponentExtensionsIds(); std::unordered_set< std::unique_ptr<extensions::TestExtensionRegistryObserver>> extension_observers;
diff --git a/chrome/browser/collaboration/android/collaboration_controller_delegate_android.cc b/chrome/browser/collaboration/android/collaboration_controller_delegate_android.cc index 59b2269..59beb3cc 100644 --- a/chrome/browser/collaboration/android/collaboration_controller_delegate_android.cc +++ b/chrome/browser/collaboration/android/collaboration_controller_delegate_android.cc
@@ -205,6 +205,46 @@ conversion::GetJavaResultCallbackPtr(std::move(result))); } +void CollaborationControllerDelegateAndroid::ShowLeaveDialog( + const tab_groups::EitherGroupID& either_id, + ResultCallback result) { + JNIEnv* env = base::android::AttachCurrentThread(); + + if (std::holds_alternative<base::Uuid>(either_id)) { + Java_CollaborationControllerDelegateImpl_showLeaveDialog( + env, java_obj_, + tab_groups::UuidToJavaString(env, std::get<base::Uuid>(either_id)), + /*localId=*/nullptr, + conversion::GetJavaResultCallbackPtr(std::move(result))); + return; + } + Java_CollaborationControllerDelegateImpl_showLeaveDialog( + env, java_obj_, /*syncId=*/nullptr, + tab_groups::TabGroupSyncConversionsBridge::ToJavaTabGroupId( + env, std::get<tab_groups::LocalTabGroupID>(either_id)), + conversion::GetJavaResultCallbackPtr(std::move(result))); +} + +void CollaborationControllerDelegateAndroid::ShowDeleteDialog( + const tab_groups::EitherGroupID& either_id, + ResultCallback result) { + JNIEnv* env = base::android::AttachCurrentThread(); + + if (std::holds_alternative<base::Uuid>(either_id)) { + Java_CollaborationControllerDelegateImpl_showDeleteDialog( + env, java_obj_, + tab_groups::UuidToJavaString(env, std::get<base::Uuid>(either_id)), + /*localId=*/nullptr, + conversion::GetJavaResultCallbackPtr(std::move(result))); + return; + } + Java_CollaborationControllerDelegateImpl_showDeleteDialog( + env, java_obj_, /*syncId=*/nullptr, + tab_groups::TabGroupSyncConversionsBridge::ToJavaTabGroupId( + env, std::get<tab_groups::LocalTabGroupID>(either_id)), + conversion::GetJavaResultCallbackPtr(std::move(result))); +} + void CollaborationControllerDelegateAndroid::PromoteTabGroup( const data_sharing::GroupId& group_id, ResultCallback result) {
diff --git a/chrome/browser/collaboration/android/collaboration_controller_delegate_android.h b/chrome/browser/collaboration/android/collaboration_controller_delegate_android.h index b4caba0..edfa57e 100644 --- a/chrome/browser/collaboration/android/collaboration_controller_delegate_android.h +++ b/chrome/browser/collaboration/android/collaboration_controller_delegate_android.h
@@ -40,6 +40,10 @@ ResultCallback result) override; void ShowManageDialog(const tab_groups::EitherGroupID& either_id, ResultCallback result) override; + void ShowLeaveDialog(const tab_groups::EitherGroupID& either_id, + ResultCallback result) override; + void ShowDeleteDialog(const tab_groups::EitherGroupID& either_id, + ResultCallback result) override; void PromoteTabGroup(const data_sharing::GroupId& group_id, ResultCallback result) override; void PromoteCurrentScreen() override;
diff --git a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java index 9da76718..25687e2 100644 --- a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java +++ b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
@@ -239,6 +239,7 @@ intent = createFullscreenSigninIntent(); break; case FlowType.SHARE_OR_MANAGE: + case FlowType.LEAVE_OR_DELETE: intent = createBottomSheetSigninIntent(); break; default: @@ -345,11 +346,14 @@ .historySyncTitleId(R.string.collaboration_sync_title) .historySyncSubtitleId(R.string.collaboration_sync_description) .build(); + @SigninAccessPoint int accessPoint; + if (mFlowType == FlowType.SHARE_OR_MANAGE) { + accessPoint = SigninAccessPoint.COLLABORATION_SHARE_TAB_GROUP; + } else { + accessPoint = SigninAccessPoint.COLLABORATION_LEAVE_OR_DELETE_TAB_GROUP; + } return mSigninAndHistorySyncActivityLauncher.createBottomSheetSigninIntentOrShowError( - mActivity, - mDataSharingTabManager.getProfile(), - bottomSheetConfig, - SigninAccessPoint.COLLABORATION_SHARE_TAB_GROUP); + mActivity, mDataSharingTabManager.getProfile(), bottomSheetConfig, accessPoint); } private Intent createFullscreenSigninIntent() { @@ -564,6 +568,32 @@ } /** + * Show the leave dialog screen. + * + * @param syncId The sync id of the tab group + * @param localId The local id of the tab group. + * @param resultCallback The callback to notify the outcome of the UI screen. + */ + @CalledByNative + void showLeaveDialog(String syncId, LocalTabGroupId localId, long resultCallback) { + CollaborationControllerDelegateImplJni.get() + .runResultCallback(Outcome.FAILURE, resultCallback); + } + + /** + * Show the delete dialog screen. + * + * @param syncId The sync id of the tab group + * @param localId The local id of the tab group. + * @param resultCallback The callback to notify the outcome of the UI screen. + */ + @CalledByNative + void showDeleteDialog(String syncId, LocalTabGroupId localId, long resultCallback) { + CollaborationControllerDelegateImplJni.get() + .runResultCallback(Outcome.FAILURE, resultCallback); + } + + /** * Open and show the local tab group. * * @param collaborationId The collaboration id of the tab group to promote.
diff --git a/chrome/browser/component_updater/pki_metadata_component_installer_browsertest.cc b/chrome/browser/component_updater/pki_metadata_component_installer_browsertest.cc index ab57444a..92cf8b0 100644 --- a/chrome/browser/component_updater/pki_metadata_component_installer_browsertest.cc +++ b/chrome/browser/component_updater/pki_metadata_component_installer_browsertest.cc
@@ -24,7 +24,9 @@ #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" #include "components/certificate_transparency/certificate_transparency_config.pb.h" +#include "content/public/browser/navigation_entry.h" #include "content/public/browser/network_service_util.h" +#include "content/public/browser/ssl_status.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" #include "crypto/ec_private_key.h" @@ -844,6 +846,133 @@ ssl_test_util::AuthState::SHOWING_INTERSTITIAL); } +class PKIMetadataComponentChromeRootStoreUpdateQwacTest + : public PKIMetadataComponentChromeRootStoreUpdateTest, + public testing::WithParamInterface<bool> { + public: + PKIMetadataComponentChromeRootStoreUpdateQwacTest() { + if (GetParam()) { + feature_list_.InitAndEnableFeature(net::features::kVerifyQWACs); + } else { + feature_list_.InitAndDisableFeature(net::features::kVerifyQWACs); + } + } + + private: + base::test::ScopedFeatureList feature_list_; +}; + +INSTANTIATE_TEST_SUITE_P(, + PKIMetadataComponentChromeRootStoreUpdateQwacTest, + testing::Bool()); + +IN_PROC_BROWSER_TEST_P(PKIMetadataComponentChromeRootStoreUpdateQwacTest, + CheckCrsEutlUpdate) { + net::EmbeddedTestServer https_server_ok(net::EmbeddedTestServer::TYPE_HTTPS); + net::EmbeddedTestServer::ServerCertificateConfig server_config; + server_config.dns_names = {"*.example.com"}; + // Set policy OIDs and QWAC QC types on the leaf so that it will validate as + // a QWAC. Also include an intermediate so we can set the intermediate as + // part of the EUTL trust store in the CRS update. + // OIDs: CABF OV, ETSI QNCP-w + server_config.policy_oids = {"2.23.140.1.2.2", "0.4.0.194112.1.5"}; + server_config.qwac_qc_types = {bssl::der::Input(net::kEtsiQctWebOid)}; + server_config.intermediate = + net::EmbeddedTestServer::IntermediateType::kInHandshake; + https_server_ok.SetSSLConfig(server_config); + https_server_ok.ServeFilesFromSourceDirectory("chrome/test/data"); + + // Install only the root cert as a trust anchor in CRS and check that the + // page load is successful but the cert is not a valid QWAC. + net::TestRootCerts::GetInstance()->Clear(); + int64_t crs_version = net::CompiledChromeRootStoreVersion(); + { + scoped_refptr<net::X509Certificate> root_cert = + net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath()); + ASSERT_TRUE(root_cert); + chrome_root_store::RootStore root_store_proto; + root_store_proto.set_version_major(++crs_version); + auto* trust_anchor = root_store_proto.add_trust_anchors(); + trust_anchor->set_der( + net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())); + InstallCRSUpdate(std::move(root_store_proto)); + } + + ASSERT_TRUE(https_server_ok.Start()); + ASSERT_TRUE(ui_test_utils::NavigateToURL( + browser(), https_server_ok.GetURL("a.example.com", "/simple.html"))); + + // Check that the page's cert status is not a QWAC. + content::WebContents* tab = chrome_test_utils::GetActiveWebContents(this); + ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame())); + EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK"); + ssl_test_util::CheckAuthenticatedState(tab, ssl_test_util::AuthState::NONE); + content::NavigationEntry* entry = tab->GetController().GetVisibleEntry(); + net::CertStatus cert_status = entry->GetSSL().cert_status; + EXPECT_FALSE(cert_status & net::CERT_STATUS_IS_QWAC); + + // Install CRS update that has the root as a trust anchor in CRS and the + // intermediate as a QWAC issuer. + { + scoped_refptr<net::X509Certificate> root_cert = + net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath()); + ASSERT_TRUE(root_cert); + scoped_refptr<net::X509Certificate> intermediate_cert = + https_server_ok.GetGeneratedIntermediate(); + ASSERT_TRUE(intermediate_cert); + + chrome_root_store::RootStore root_store_proto; + root_store_proto.set_version_major(++crs_version); + auto* trust_anchor = root_store_proto.add_trust_anchors(); + trust_anchor->set_der( + net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())); + auto* additional_cert = root_store_proto.add_additional_certs(); + additional_cert->set_der(net::x509_util::CryptoBufferAsStringPiece( + intermediate_cert->cert_buffer())); + additional_cert->set_eutl(true); + InstallCRSUpdate(std::move(root_store_proto)); + } + + ASSERT_TRUE(ui_test_utils::NavigateToURL( + browser(), https_server_ok.GetURL("b.example.com", "/simple.html"))); + + // Check the page's cert status is a QWAC (if net::features::kVerifyQWACs is + // enabled). + tab = chrome_test_utils::GetActiveWebContents(this); + ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame())); + EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK"); + ssl_test_util::CheckAuthenticatedState(tab, ssl_test_util::AuthState::NONE); + cert_status = tab->GetController().GetVisibleEntry()->GetSSL().cert_status; + EXPECT_EQ(GetParam(), !!(cert_status & net::CERT_STATUS_IS_QWAC)); + + // Install a CRS update that has the root as both a trust anchor in CRS and + // a QWAC issuer + { + scoped_refptr<net::X509Certificate> root_cert = + net::ImportCertFromFile(net::EmbeddedTestServer::GetRootCertPemPath()); + ASSERT_TRUE(root_cert); + chrome_root_store::RootStore root_store_proto; + root_store_proto.set_version_major(++crs_version); + auto* trust_anchor = root_store_proto.add_trust_anchors(); + trust_anchor->set_der( + net::x509_util::CryptoBufferAsStringPiece(root_cert->cert_buffer())); + trust_anchor->set_eutl(true); + InstallCRSUpdate(std::move(root_store_proto)); + } + + ASSERT_TRUE(ui_test_utils::NavigateToURL( + browser(), https_server_ok.GetURL("c.example.com", "/simple.html"))); + + // Check the page's cert status is a QWAC (if net::features::kVerifyQWACs is + // enabled). + tab = chrome_test_utils::GetActiveWebContents(this); + ASSERT_TRUE(WaitForRenderFrameReady(tab->GetPrimaryMainFrame())); + EXPECT_EQ(chrome_test_utils::GetActiveWebContents(this)->GetTitle(), u"OK"); + ssl_test_util::CheckAuthenticatedState(tab, ssl_test_util::AuthState::NONE); + cert_status = tab->GetController().GetVisibleEntry()->GetSSL().cert_status; + EXPECT_EQ(GetParam(), !!(cert_status & net::CERT_STATUS_IS_QWAC)); +} + // Test suite for tests that depend on both Certificate Transparency and Chrome // Root Store updates. class PKIMetadataComponentCtAndCrsUpdaterTest
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java index a7376087..0f41ec5 100644 --- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java +++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java
@@ -786,7 +786,7 @@ // TODO(haileywang): remove assert if we don't observe any crash assert callback != null; if (callback != null) { - callback.onResult(Outcome.DELETE_OR_LEAVE_GROUP); + callback.onResult(Outcome.GROUP_LEFT_OR_DELETED); } }
diff --git a/chrome/browser/devtools/devtools_browsertest.cc b/chrome/browser/devtools/devtools_browsertest.cc index 3a84e112..1613301d7 100644 --- a/chrome/browser/devtools/devtools_browsertest.cc +++ b/chrome/browser/devtools/devtools_browsertest.cc
@@ -689,11 +689,8 @@ } std::string BuildComponentExtension() { - extensions::ExtensionService* extension_service = - extensions::ExtensionSystem::Get(browser()->profile()) - ->extension_service(); extensions::ComponentLoader* component_loader = - extension_service->component_loader(); + extensions::ComponentLoader::Get(browser()->profile()); extensions::ExtensionRegistry* extension_registry = extensions::ExtensionRegistry::Get(browser()->profile());
diff --git a/chrome/browser/enterprise/remote_commands/clear_browsing_data_job.cc b/chrome/browser/enterprise/remote_commands/clear_browsing_data_job.cc index 6044eec..8268595a 100644 --- a/chrome/browser/enterprise/remote_commands/clear_browsing_data_job.cc +++ b/chrome/browser/enterprise/remote_commands/clear_browsing_data_job.cc
@@ -75,7 +75,7 @@ bool ClearBrowsingDataJob::ParseCommandPayload( const std::string& command_payload) { VLOG_POLICY(2, REMOTE_COMMANDS) - << "ClearBrowsingDataJob::ParseCommandPayload " << command_payload; + << "Clear browsing data command payload: " << command_payload; std::optional<base::Value::Dict> root = base::JSONReader::ReadDict(command_payload); if (!root) @@ -93,8 +93,6 @@ } void ClearBrowsingDataJob::RunImpl(CallbackWithResult result_callback) { - VLOG_POLICY(2, REMOTE_COMMANDS) - << "ClearBrowsingDataJob::Run " << clear_cache_ << " " << clear_cookies_; uint64_t types = 0; if (clear_cache_) types |= content::BrowsingDataRemover::DATA_TYPE_CACHE; @@ -117,6 +115,10 @@ result_callback_ = std::move(result_callback); if (types == 0) { + LOG_POLICY(WARNING, REMOTE_COMMANDS) + << "Clear browsing data command has not specified any " + "data types. Please double check the payload to " + "make sure everything is set as required."; // There's nothing to clear, invoke the callback with success result and be // done. base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
diff --git a/chrome/browser/extensions/api/omnibox/omnibox_api.cc b/chrome/browser/extensions/api/omnibox/omnibox_api.cc index 79c1a63..5659e61 100644 --- a/chrome/browser/extensions/api/omnibox/omnibox_api.cc +++ b/chrome/browser/extensions/api/omnibox/omnibox_api.cc
@@ -31,6 +31,7 @@ #include "components/search_engines/template_url.h" #include "components/search_engines/template_url_service.h" #include "extensions/browser/event_router.h" +#include "extensions/browser/extension_action.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_prefs_factory.h" #include "extensions/browser/install_prefs_helper.h" @@ -344,13 +345,16 @@ OmniboxSendSuggestionsFunction::~OmniboxSendSuggestionsFunction() = default; ExtensionFunction::ResponseAction OmniboxSendSuggestionsFunction::Run() { - params_ = SendSuggestions::Params::Create(args()); - EXTENSION_FUNCTION_VALIDATE(params_); + std::optional<api::omnibox::SendSuggestions::Params> params = + SendSuggestions::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + request_id_ = params->request_id; - if (is_from_service_worker() && !params_->suggest_results.empty()) { + if (!params->suggest_results.empty()) { std::vector<std::string_view> inputs; - inputs.reserve(params_->suggest_results.size()); - for (const auto& suggestion : params_->suggest_results) { + inputs.reserve(params->suggest_results.size()); + for (const auto& suggestion : params->suggest_results) { + std::vector<ExtensionSuggestion::Action> actions; inputs.push_back(suggestion.description); if (suggestion.actions) { if (!IsUnscopedModeAllowed(extension())) { @@ -365,15 +369,51 @@ suggestion.actions->size(), ExtensionOmniboxEventRouter::kMaxSuggestionActions))); } + actions.reserve(suggestion.actions->size()); + for (const auto& action : *suggestion.actions) { + base::Value::Dict canvas_set = + action.icon ? action.icon->ToValue() : base::Value::Dict(); + gfx::ImageSkia image_skia; + if (!canvas_set.empty()) { + base::Value::Dict& image_data = *canvas_set.FindDict("imageData"); + // The image data should have been verified by the pre-validation + // param update. + CHECK(!image_data.empty()); + // TODO(crbug.com/408069174): Move ParseIconFromCanvasDictionary + // outside `ExtensionAction` into a common file. + if (ExtensionAction::ParseIconFromCanvasDictionary(image_data, + &image_skia) != + ExtensionAction::IconParseResult::kSuccess) { + return RespondNow(Error(base::StringPrintf( + ExtensionOmniboxEventRouter::kActionIconError, + suggestion.description, action.name))); + } + } + actions.emplace_back(action.name, action.label, action.tooltip_text, + gfx::Image(image_skia)); + } } + + const std::vector<api::omnibox::MatchClassification> empty_styles; + const std::vector<api::omnibox::MatchClassification>* styles_ptr = + suggestion.description_styles ? &suggestion.description_styles.value() + : &empty_styles; + extension_suggestions_.emplace_back( + suggestion.content, suggestion.description, + suggestion.deletable.value_or(false), + StyleTypesToACMatchClassifications(styles_ptr, + suggestion.description), + std::move(actions), suggestion.icon_url); } - ParseDescriptionsAndStyles( - inputs, - base::BindOnce( - &OmniboxSendSuggestionsFunction::OnParsedDescriptionsAndStyles, - this)); - return RespondLater(); + if (is_from_service_worker()) { + ParseDescriptionsAndStyles( + inputs, + base::BindOnce( + &OmniboxSendSuggestionsFunction::OnParsedDescriptionsAndStyles, + this)); + return RespondLater(); + } } NotifySuggestionsReady(); @@ -382,7 +422,7 @@ void OmniboxSendSuggestionsFunction::OnParsedDescriptionsAndStyles( DescriptionAndStylesResult result) { - DCHECK(params_); + DCHECK_NE(0u, extension_suggestions_.size()); // Since the XML parsing happens asynchronously, the browser context can be // torn down in the interim. If this happens, early-out. if (!browser_context()) { @@ -394,8 +434,7 @@ return; } - if (result.descriptions_and_styles.size() != - params_->suggest_results.size()) { + if (result.descriptions_and_styles.size() != extension_suggestions_.size()) { // This can technically happen if the extension provided input that mucked // with our XML parsing (see suggestion_parser_unittest.cc). This isn't a // security concern, but would mean that our mapping to record the other @@ -405,11 +444,13 @@ return; } - for (size_t i = 0; i < params_->suggest_results.size(); ++i) { - params_->suggest_results[i].description = + for (size_t i = 0; i < extension_suggestions_.size(); ++i) { + extension_suggestions_[i].description = base::UTF16ToUTF8(result.descriptions_and_styles[i].description); - params_->suggest_results[i].description_styles = - std::move(result.descriptions_and_styles[i].styles); + extension_suggestions_[i].match_classifications = + StyleTypesToACMatchClassifications( + &result.descriptions_and_styles[i].styles, + extension_suggestions_[i].description); } NotifySuggestionsReady(); @@ -420,7 +461,8 @@ Profile* profile = Profile::FromBrowserContext(browser_context())->GetOriginalProfile(); OmniboxSuggestionsWatcherFactory::GetForBrowserContext(profile) - ->NotifySuggestionsReady(&*params_, extension_id()); + ->NotifySuggestionsReady(extension_suggestions_, request_id_, + extension_id()); } ExtensionFunction::ResponseAction OmniboxSetDefaultSuggestionFunction::Run() { @@ -472,14 +514,14 @@ // This function converts style information populated by the JSON schema // compiler into an ACMatchClassifications object. ACMatchClassifications StyleTypesToACMatchClassifications( - const omnibox::SuggestResult &suggestion) { + const std::vector<omnibox::MatchClassification>* description_styles, + const std::string& suggestion_description) { ACMatchClassifications match_classifications; - if (suggestion.description_styles) { - std::u16string description = base::UTF8ToUTF16(suggestion.description); + if (!description_styles->empty()) { + std::u16string description = base::UTF8ToUTF16(suggestion_description); std::vector<int> styles(description.length(), 0); - for (const omnibox::MatchClassification& style : - *suggestion.description_styles) { + for (const omnibox::MatchClassification& style : *description_styles) { int length = style.length ? *style.length : description.length(); size_t offset = style.offset >= 0 ? style.offset @@ -536,7 +578,13 @@ std::u16string description = base::UTF8ToUTF16(suggestion->description); ACMatchClassifications& description_styles = match->contents_class; - description_styles = StyleTypesToACMatchClassifications(*suggestion); + + const std::vector<api::omnibox::MatchClassification> empty_styles; + const std::vector<api::omnibox::MatchClassification>* styles_list = + suggestion->description_styles ? &suggestion->description_styles.value() + : &empty_styles; + description_styles = + StyleTypesToACMatchClassifications(styles_list, suggestion->description); // Replace "%s" with the user's input and adjust the style offsets to the // new length of the description.
diff --git a/chrome/browser/extensions/api/omnibox/omnibox_api.h b/chrome/browser/extensions/api/omnibox/omnibox_api.h index 8d96d4cf..37be5f9 100644 --- a/chrome/browser/extensions/api/omnibox/omnibox_api.h +++ b/chrome/browser/extensions/api/omnibox/omnibox_api.h
@@ -13,6 +13,7 @@ #include "chrome/browser/extensions/api/omnibox/suggestion_parser.h" #include "chrome/common/extensions/api/omnibox.h" #include "components/omnibox/browser/autocomplete_match.h" +#include "components/omnibox/browser/extension_suggestion.h" #include "components/search_engines/template_url_service.h" #include "extensions/browser/browser_context_keyed_api_factory.h" #include "extensions/browser/extension_function.h" @@ -47,6 +48,9 @@ "actions per suggestion."; static constexpr char kActionsRequireDirectInputPermissionError[] = "Actions in suggest results require omnibox.directInput permission."; + static constexpr char kActionIconError[] = + "Action icon failed to parse for suggestion description: %s and action " + "name: %s."; ExtensionOmniboxEventRouter(const ExtensionOmniboxEventRouter&) = delete; ExtensionOmniboxEventRouter& operator=(const ExtensionOmniboxEventRouter&) = @@ -106,8 +110,10 @@ // Notifies the omnibox that the suggestions have been prepared. void NotifySuggestionsReady(); - // The suggestion parameters passed by the extension API call. - std::optional<api::omnibox::SendSuggestions::Params> params_; + // The parsed `params_.suggest_results`. + std::vector<ExtensionSuggestion> extension_suggestions_; + + int request_id_; }; class OmniboxAPI : public BrowserContextKeyedAPI, @@ -217,7 +223,8 @@ // This function converts style information populated by the JSON schema // // compiler into an ACMatchClassifications object. ACMatchClassifications StyleTypesToACMatchClassifications( - const api::omnibox::SuggestResult &suggestion); + const std::vector<api::omnibox::MatchClassification>* description_styles, + const std::string& suggestion_description); } // namespace extensions
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 722bedc..04b183d6 100644 --- a/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc +++ b/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
@@ -43,6 +43,7 @@ #include "third_party/metrics_proto/omnibox_event.pb.h" #include "third_party/metrics_proto/omnibox_focus_type.pb.h" #include "ui/base/window_open_disposition.h" +#include "ui/gfx/image/image_unittest_util.h" namespace extensions { @@ -228,6 +229,7 @@ // should be to invoke with suggestions from the extension. const AutocompleteResult& result = autocomplete_controller->result(); ASSERT_EQ(4U, result.size()) << AutocompleteResultAsString(result); + int first_match_relevance = result.match_at(0).relevance; // Invoke the keyword with what we typed. EXPECT_EQ(u"alpha", result.match_at(0).keyword); @@ -247,6 +249,8 @@ std::u16string rich_description = u"Description with style: <match>, [dim], (url)"; EXPECT_EQ(rich_description, result.match_at(1).contents); + EXPECT_EQ(first_match_relevance - 1, result.match_at(1).relevance); + const ExpectedMatchComponents expected_components = { {u"Description with style: ", ACMatchClassification::NONE}, {u"<match>", ACMatchClassification::MATCH}, @@ -270,6 +274,7 @@ EXPECT_EQ(AutocompleteProvider::TYPE_KEYWORD, result.match_at(2).provider->type()); EXPECT_EQ(simple_description, result.match_at(2).contents); + EXPECT_EQ(first_match_relevance - 2, result.match_at(2).relevance); VerifyMatchComponents(expected_components, result.match_at(2)); EXPECT_EQ(u"alpha", result.match_at(3).keyword); @@ -277,6 +282,7 @@ EXPECT_EQ(AutocompleteProvider::TYPE_KEYWORD, result.match_at(3).provider->type()); EXPECT_EQ(simple_description, result.match_at(3).contents); + EXPECT_EQ(first_match_relevance - 3, result.match_at(3).relevance); VerifyMatchComponents(expected_components, result.match_at(3)); } } @@ -1433,6 +1439,78 @@ EXPECT_TRUE(listener.had_user_gesture()); } +// Tests that extensions can add actions with custom icons to Omnibox +// suggestions. +IN_PROC_BROWSER_TEST_P(UnscopedOmniboxApiTest, ActionIconAppliedToMatch) { + constexpr char kManifest[] = + R"({ + "name": "Basic Action with icon", + "manifest_version": 2, + "version": "0.1", + "omnibox": { "keyword": "alpha" }, + "background": { "scripts": [ "background.js" ], "persistent": true }, + "permissions" : [ "omnibox.directInput" ] + })"; + // This extension will create a suggestion with an action that has a green + // icon. + constexpr char kBackground[] = + R"( + chrome.omnibox.onInputChanged.addListener((text, suggest) => { + const canvas = new OffscreenCanvas(16, 16); + const context = canvas.getContext('2d'); + context.fillStyle = '#00FF00'; + context.fillRect(0, 0, 16, 16); + suggest([ + { + content: text, + description: 'description', + actions: [{ + name: 'do_something', + label: 'Do something', + tooltipText: 'Do something the user wants', + icon: context.getImageData(0, 0, 16, 16) + }] + } + ]); + }); + + chrome.omnibox.onActionExecuted.addListener((actionExecution) => { + chrome.test.sendMessage( + actionExecution.actionName + "-" + actionExecution.content); + });)"; + + TestExtensionDir test_dir; + test_dir.WriteManifest(kManifest); + test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackground); + const Extension* extension = LoadExtension(test_dir.UnpackedPath()); + ASSERT_TRUE(extension); + + ExtensionTestMessageListener listener("do_something-sending input"); + AutocompleteController* autocomplete_controller = GetAutocompleteController(); + chrome::FocusLocationBar(browser()); + + // Send an input to the extension and wait for the sggestion to arrive before + // we can select it. + AutocompleteInput input(u"sending input", metrics::OmniboxEventProto::NTP, + ChromeAutocompleteSchemeClassifier(profile())); + autocomplete_controller->Start(input); + WaitForAutocompleteDone(browser()); + ASSERT_TRUE(autocomplete_controller->done()); + + { + const AutocompleteResult& result = autocomplete_controller->result(); + // First match is for the default search entry, so directly check the + // second match. + AutocompleteMatch match = result.match_at(1); + // Manually construct an all-green icon and compare to the action icon. + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 16); + bitmap.eraseColor(SK_ColorGREEN); + gfx::test::AreImagesEqual(match.actions[0]->GetIconImage(), + gfx::Image::CreateFrom1xBitmap(bitmap)); + } +} + // Tests that multiple unscoped extensions work at the same time and are // displayed with different headers. IN_PROC_BROWSER_TEST_P(UnscopedOmniboxApiTest, MultipleUnscopedExtensions) {
diff --git a/chrome/browser/extensions/api/omnibox/omnibox_unittest.cc b/chrome/browser/extensions/api/omnibox/omnibox_unittest.cc index bedfb752..44eea0fd 100644 --- a/chrome/browser/extensions/api/omnibox/omnibox_unittest.cc +++ b/chrome/browser/extensions/api/omnibox/omnibox_unittest.cc
@@ -70,8 +70,11 @@ SendSuggestions::Params::Create(list); EXPECT_TRUE(params); ASSERT_FALSE(params->suggest_results.empty()); - CompareClassification(styles_expected, StyleTypesToACMatchClassifications( - params->suggest_results[0])); + CompareClassification( + styles_expected, + StyleTypesToACMatchClassifications( + ¶ms->suggest_results[0].description_styles.value(), + params->suggest_results[0].description)); // Same input, but swap the order. Ensure it still works. base::Value::List swap_list = @@ -95,7 +98,9 @@ ASSERT_FALSE(swapped_params->suggest_results.empty()); CompareClassification( styles_expected, - StyleTypesToACMatchClassifications(swapped_params->suggest_results[0])); + StyleTypesToACMatchClassifications( + &swapped_params->suggest_results[0].description_styles.value(), + swapped_params->suggest_results[0].description)); } // 0123456789 @@ -145,8 +150,11 @@ SendSuggestions::Params::Create(list); EXPECT_TRUE(params); ASSERT_FALSE(params->suggest_results.empty()); - CompareClassification(styles_expected, StyleTypesToACMatchClassifications( - params->suggest_results[0])); + CompareClassification( + styles_expected, + StyleTypesToACMatchClassifications( + ¶ms->suggest_results[0].description_styles.value(), + params->suggest_results[0].description)); // Try moving the "dim/match" style pair at offset 9. Output should be the // same. @@ -181,8 +189,11 @@ SendSuggestions::Params::Create(moved_list); EXPECT_TRUE(moved_params); ASSERT_FALSE(moved_params->suggest_results.empty()); - CompareClassification(styles_expected, StyleTypesToACMatchClassifications( - moved_params->suggest_results[0])); + CompareClassification( + styles_expected, + StyleTypesToACMatchClassifications( + &moved_params->suggest_results[0].description_styles.value(), + moved_params->suggest_results[0].description)); } // 0123456789 @@ -228,8 +239,11 @@ SendSuggestions::Params::Create(list); EXPECT_TRUE(params); ASSERT_FALSE(params->suggest_results.empty()); - CompareClassification(styles_expected, StyleTypesToACMatchClassifications( - params->suggest_results[0])); + CompareClassification( + styles_expected, + StyleTypesToACMatchClassifications( + ¶ms->suggest_results[0].description_styles.value(), + params->suggest_results[0].description)); } // 0123456789
diff --git a/chrome/browser/extensions/api/user_scripts/user_scripts_uitest.cc b/chrome/browser/extensions/api/user_scripts/user_scripts_uitest.cc new file mode 100644 index 0000000..c082827 --- /dev/null +++ b/chrome/browser/extensions/api/user_scripts/user_scripts_uitest.cc
@@ -0,0 +1,170 @@ +// 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 "base/strings/strcat.h" +#include "chrome/browser/extensions/api/user_scripts/user_scripts_apitest.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/webui_url_constants.h" +#include "chrome/test/interaction/interactive_browser_test.h" +#include "content/public/test/browser_test.h" +#include "extensions/browser/background_script_executor.h" +#include "extensions/common/extension_id.h" +#include "extensions/test/extension_test_message_listener.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/interaction/element_identifier.h" + +namespace extensions { + +class UserScriptsUITest : public InteractiveBrowserTestT<UserScriptsAPITest> { + public: + // Checks that toggle is `!enabled` and then toggles it to `enabled` state. + auto CheckCurrentToggleStateAndThenToggleItInUI( + ui::ElementIdentifier page_id, + const DeepQuery toggle_dom_path, + bool enabled) { + return Steps( + // ClickElement() won't click something off screen so scroll the toggle + // into view in case it is not. + ScrollIntoView(page_id, toggle_dom_path), + // Check the toggle is `!enabled`. + EnsurePresent(page_id, toggle_dom_path), + CheckJsResultAt(page_id, toggle_dom_path, "(el) => el.checked", + !enabled), + // Click the toggle and check it is `enabled`. + ClickElement(page_id, toggle_dom_path), + CheckJsResultAt(page_id, toggle_dom_path, "(el) => el.checked", + enabled)); + } + + // Toggles the extensions_features::kUserScriptUserExtensionToggle toggle to + // `enabled` state. + auto TogglePerExtensionToggleInUI(ui::ElementIdentifier page_id, + const ExtensionId& extension_id, + bool enabled) { + const DeepQuery kPathToUserScriptsToggle{ + "extensions-manager", + "extensions-detail-view", + "extensions-toggle-row#allow-user-scripts", + "cr-toggle#crToggle", + }; + + // Enable the per-extension toggle in the UI. + return Steps( + // Navigate to the extensions detail page for the extension (where the + // toggle lives). + NavigateWebContents(page_id, + GURL(base::StrCat({chrome::kChromeUIExtensionsURL, + "?id=", extension_id.c_str()}))), + CheckCurrentToggleStateAndThenToggleItInUI( + page_id, kPathToUserScriptsToggle, enabled)); + } + + // Toggles the (non extensions_features::kUserScriptUserExtensionToggle state) + // dev mode toggle `enabled` state. + auto ToggleDevModeInUI(ui::ElementIdentifier page_id, bool enabled) { + const DeepQuery kPathToDevModeToggle{ + "extensions-manager extensions-toolbar", + "cr-toggle#devMode", + }; + + // Enable dev mode toggle in the UI. + return Steps( + // Navigate to the extensions detail page for the extension. + NavigateWebContents(page_id, GURL(chrome::kChromeUIExtensionsURL)), + CheckCurrentToggleStateAndThenToggleItInUI( + page_id, kPathToDevModeToggle, enabled)); + } + + // Checks that the chrome.userScripts API is not available in the background + // script. + auto VerifyUserScriptsIsNotAvailable(const ExtensionId& extension_id) { + // Register the user script in the extension background script and confirm + // it registered successfully. + return CheckResult( + [this, &extension_id]() -> bool { + return BackgroundScriptExecutor::ExecuteScript( + profile(), extension_id, "verifyApiIsNotAvailable();", + BackgroundScriptExecutor::ResultCapture:: + kSendScriptResult) == "success"; + }, + true, "Checking that the userScripts API is not available"); + } + + // Registers a dynamic user script with the chrome.userScripts API. + auto RegisterUserScript(const ExtensionId& extension_id) { + // Register the user script in the extension background script and confirm + // it registered successfully. + return CheckResult( + [this, &extension_id]() -> bool { + return BackgroundScriptExecutor::ExecuteScript( + profile(), extension_id, "registerUserScripts();", + BackgroundScriptExecutor::ResultCapture:: + kSendScriptResult) == "success"; + }, + true, "Registering dynamic user script and checking that it completed"); + } +}; + +// Tests the toggling the UI toggle (dependent on feature) controls whether the +// user has allowed userScripts API usage. +IN_PROC_BROWSER_TEST_P(UserScriptsUITest, ToggleControls_UserScriptsAPIUsage) { + // Load extension that has API permission to use the userScripts API, but not + // the per-extension toggle for userScripts enabled. + ExtensionTestMessageListener extension_background_started_listener = + ExtensionTestMessageListener("started"); + const Extension* extension = + LoadExtension(test_data_dir_.AppendASCII("user_scripts/allowed_tests")); + ASSERT_TRUE(extension); + ASSERT_TRUE(extension_background_started_listener.WaitUntilSatisfied()); + + const DeepQuery kPathToUserScriptInjectedDiv{ + "#user-script-code", + }; + + // This test verifies that: + // 1) The userScripts API is initially unavailable. + // 2) Enabling the toggle allows for a dynamic user script can be registered + // and injected. + // 3) Disabling the toggle causes user scripts to no longer inject + DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTab); + RunTestSequence( + InstrumentTab(kTab), + + VerifyUserScriptsIsNotAvailable(extension->id()), + + // Enable the toggle depending on feature state. + GetParam() ? TogglePerExtensionToggleInUI(kTab, extension->id(), + /*enabled=*/true) + : ToggleDevModeInUI(kTab, /*enabled=*/true), + + RegisterUserScript(extension->id()), + + // Navigate tab to a webpage where the user script should inject a <div>. + NavigateWebContents( + kTab, embedded_test_server()->GetURL("example.com", "/simple.html")), + // Ensure the user script injected its <div>. + EnsurePresent(kTab, kPathToUserScriptInjectedDiv), + + // Disable the toggle depending on feature state. + GetParam() ? TogglePerExtensionToggleInUI(kTab, extension->id(), + /*enabled=*/false) + : ToggleDevModeInUI(kTab, /*enabled=*/false), + // Navigate tab to a webpage where the user script should no longer inject + // a <div>. + NavigateWebContents( + kTab, embedded_test_server()->GetURL("example.com", "/simple.html")), + // Ensure the user script no longer injects its <div>. + EnsureNotPresent(kTab, kPathToUserScriptInjectedDiv)); +} + +INSTANTIATE_TEST_SUITE_P(PerExtensionToggle, + UserScriptsUITest, + // extensions_features::kUserScriptUserExtensionToggle + testing::Values("true")); +INSTANTIATE_TEST_SUITE_P(DevModeToggle, + UserScriptsUITest, + // extensions_features::kUserScriptUserExtensionToggle + testing::Values("false")); + +} // namespace extensions
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc index 942f903..bab7242 100644 --- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc +++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -37,6 +37,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/devtools/protocol/devtools_protocol_test_support.h" #include "chrome/browser/devtools/url_constants.h" +#include "chrome/browser/extensions/chrome_test_extension_loader.h" #include "chrome/browser/extensions/error_console/error_console.h" #include "chrome/browser/extensions/error_console/error_console_test_observer.h" #include "chrome/browser/extensions/extension_apitest.h" @@ -128,6 +129,7 @@ #include "services/network/test/test_url_loader_client.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/input/web_input_event.h" +#include "third_party/blink/public/common/service_worker/service_worker_status_code.h" #include "ui/webui/untrusted_web_ui_browsertest_util.h" // nogncheck #include "url/origin.h" @@ -2609,7 +2611,7 @@ content::EvalJs(web_contents, "document.body.textContent.trim();")); } - // A callback allow waiting for responses to complete with an expected status + // A callback allow waiting for a response to complete with an expected status // and given content. auto make_browser_request = [](network::mojom::URLLoaderFactory* url_loader_factory, const GURL& url, @@ -7553,5 +7555,189 @@ } } } + +// Allows test to wait for the failure of a worker registration. +class WorkerRegistrationFailureObserver + : public ServiceWorkerTaskQueue::TestObserver { + public: + explicit WorkerRegistrationFailureObserver(const ExtensionId extension_id) + : extension_id_(extension_id) { + ServiceWorkerTaskQueue::SetObserverForTest(this); + } + ~WorkerRegistrationFailureObserver() override { + ServiceWorkerTaskQueue::SetObserverForTest(nullptr); + } + + blink::ServiceWorkerStatusCode WaitForWorkerRegistrationFailure() { + if (!status_code_) { + SCOPED_TRACE("Waiting for worker registration to fail"); + failure_loop_.Run(); + } + return *status_code_; + } + + private: + void OnWorkerRegistrationFailed( + const ExtensionId& extension_id, + blink::ServiceWorkerStatusCode status_code) override { + if (extension_id == extension_id_) { + status_code_ = status_code; + failure_loop_.Quit(); + } + } + + ExtensionId extension_id_; + base::RunLoop failure_loop_; + std::optional<blink::ServiceWorkerStatusCode> status_code_; +}; + +// Allows test to wait for the call of `ResetURLLoaderFactories()` in +// WebRequestAPI. +class URLLoaderFactoriesResetWaiter : public WebRequestAPI::TestObserver { + public: + URLLoaderFactoriesResetWaiter() { WebRequestAPI::SetObserverForTest(this); } + ~URLLoaderFactoriesResetWaiter() override { + WebRequestAPI::SetObserverForTest(nullptr); + } + + URLLoaderFactoriesResetWaiter(const URLLoaderFactoriesResetWaiter&) = delete; + URLLoaderFactoriesResetWaiter& operator=( + const URLLoaderFactoriesResetWaiter&) = delete; + + void WaitForResetURLLoaderFactoriesCalled() { + SCOPED_TRACE("Waiting for ResetURLLoaderFactories to be called"); + url_loader_factory_reset_runloop_.Run(); + } + + private: + void OnDidResetURLLoaderFactories() override { + url_loader_factory_reset_runloop_.Quit(); + } + + base::RunLoop url_loader_factory_reset_runloop_; +}; + +class ManifestV3WebRequestApiTestWithDeferResetURLLoaderFactories + : public ManifestV3WebRequestApiTest, + public testing::WithParamInterface<bool> { + public: + ManifestV3WebRequestApiTestWithDeferResetURLLoaderFactories() { + feature_list_.InitWithFeatureState( + extensions_features::kDeferResetURLLoaderFactories, GetParam()); + } + ~ManifestV3WebRequestApiTestWithDeferResetURLLoaderFactories() override = + default; + + private: + base::test::ScopedFeatureList feature_list_; +}; + +// Tests that the call to `ResetURLLoaderFactories()` performed by WebRequestAPI +// doesn't break the registration process of other extensions. +// Regression test for https://crbug.com/394523691. +IN_PROC_BROWSER_TEST_P( + ManifestV3WebRequestApiTestWithDeferResetURLLoaderFactories, + ResetURLLoaderFactoryDoesntBreakRegistration) { + bool feature_enabled = GetParam(); + ASSERT_TRUE(StartEmbeddedTestServer()); + + // A simple extension that sends a message and waits for a response in its + // background script. + const ExtensionId extension_id("iegclhlplifhodhkoafiokenjoapiobj"); + static constexpr const char kKey[] = + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjzv7dI7Ygyh67VHE1DdidudpYf8P" + "Ffv8iucWvzO+3xpF/Dm5xNo7aQhPNiEaNfHwJQ7lsp4gc+C+4bbaVewBFspTruoSJhZc5uEf" + "qxwovJwN+v1/SUFXTXQmQBv6gs0qZB4gBbl4caNQBlqrFwAMNisnu1V6UROna8rOJQ90D7Nv" + "7TCwoVPKBfVshpFjdDOTeBg4iLctO3S/06QYqaTDrwVceSyHkVkvzBY6tc6mnYX0RZu78J9i" + "L8bdqwfllOhs69cqoHHgrLdI6JdOyiuh6pBP6vxMlzSKWJ3YTNjaQTPwfOYaLMuzdl0v+Ydz" + "afIzV9zwe4Xiskk+5JNGt8b2rQIDAQAB"; + static constexpr char kManifest[] = + R"({ + "name": "TestExtension", + "manifest_version": 3, + "version": "0.1", + "key": "%s", + "background": {"service_worker": "background.js"} + })"; + static constexpr char kBackgroundJs[] = + R"(chrome.test.sendMessage('will_receive').then(() => { + console.log('received'); + }))"; + TestExtensionDir extension_dir; + extension_dir.WriteManifest(base::StringPrintf(kManifest, kKey)); + extension_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundJs); + + ServiceWorkerTaskQueue* task_queue = ServiceWorkerTaskQueue::Get(profile()); + ASSERT_TRUE(task_queue); + WebRequestAPI* web_request_api = + BrowserContextKeyedAPIFactory<WebRequestAPI>::Get(profile()); + ASSERT_TRUE(web_request_api); + + // Listen to "will_receive" message from the extension. + ExtensionTestMessageListener will_receive_listener("will_receive", + ReplyBehavior::kWillReply); + // Listen to the completion of the registration storage. + service_worker_test_utils::TestServiceWorkerContextObserver + registration_observer(profile()); + // Listen for a failure in the worker registration. + WorkerRegistrationFailureObserver worker_failure_observer(extension_id); + + // Asynchronously load the extension so we can wait for a step in the loading + // process in the test. + std::optional<base::UnguessableToken> activation_token; + ChromeTestExtensionLoader(profile()).LoadUnpackedExtensionAsync( + extension_dir.UnpackedPath(), + base::BindLambdaForTesting([&](const Extension* extension) { + ASSERT_TRUE(extension); + activation_token = + task_queue->GetCurrentActivationToken(extension->id()); + ASSERT_TRUE(activation_token.has_value()); + })); + // ...and wait for the moment right after the worker is requested to start + // during the registration process. + registration_observer.WaitForStartWorkerMessageSent(); + URLLoaderFactoriesResetWaiter url_loader_factories_reset_waiter; + // Simulate the effect of loading an extension with WebRequestAPI permissions. + // In other words, make sure we're proxying for the current profile. + // This will cause WebRequestAPI to attempt calling + // `ResetURLLoaderFactories()`. Because the extension is still in the early + // phases of starting its worker here, this would break its registration + // before it has a chance of being completed and stored. + // Instead, the call to `ResetURLLoaderFactories()` will be deferred. + // NOTE: We simulate the call to `ResetURLLoaderFactories()` rather + // than loading an extension with WebRequestAPI permissions, as that + // would take too long and won't trigger the bug in all cases. + web_request_api->ForceProxyForTesting(); + + if (feature_enabled) { + // DeferResetURLLoaderFactories feature enabled: expect successful + // execution. Check that the worker is still running and functional. + registration_observer.WaitForWorkerStarted(); + std::optional<WorkerId> worker_id = GetWorkerIdForExtension(extension_id); + EXPECT_TRUE(worker_id); + SCOPED_TRACE( + "Waiting for extension background to signal that it can send messages"); + ASSERT_TRUE(will_receive_listener.WaitUntilSatisfied()); + will_receive_listener.Reply("go"); + // Check `ResetURLLoaderFactories()` is called after registration is stored. + registration_observer.WaitForRegistrationStored(); + url_loader_factories_reset_waiter.WaitForResetURLLoaderFactoriesCalled(); + } else { + // DeferResetURLLoaderFactories feature disabled: expect worker registration + // to fail. We have observed that the registration can fail with either + // `kErrorStartWorkerFailed` or `kErrorNetwork` depending on when exactly + // it's interrupted. + auto status_code = + worker_failure_observer.WaitForWorkerRegistrationFailure(); + EXPECT_NE(status_code, blink::ServiceWorkerStatusCode::kOk); + } +} + +INSTANTIATE_TEST_SUITE_P( + All, + ManifestV3WebRequestApiTestWithDeferResetURLLoaderFactories, + testing::Bool()); + #endif // !BUILDFLAG(IS_ANDROID) + } // namespace extensions
diff --git a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc index 468bb0a..d6e25ca 100644 --- a/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc +++ b/chrome/browser/extensions/chrome_content_browser_client_extensions_part.cc
@@ -50,7 +50,6 @@ #include "extensions/browser/bad_message.h" #include "extensions/browser/extension_host.h" #include "extensions/browser/extension_registry.h" -#include "extensions/browser/extension_system.h" #include "extensions/browser/process_manager.h" #include "extensions/browser/process_map.h" #include "extensions/browser/renderer_startup_helper.h" @@ -80,7 +79,6 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) #include "chrome/browser/extensions/component_loader.h" -#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" #endif @@ -745,10 +743,7 @@ #endif // Check if the component is a loaded component extension. - return ExtensionSystem::Get(browser_context) - ->extension_service() - ->component_loader() - ->Exists(extension_id); + return ComponentLoader::Get(browser_context)->Exists(extension_id); #endif // BUILDFLAG(ENABLE_EXTENSIONS) }
diff --git a/chrome/browser/extensions/chrome_extension_system.cc b/chrome/browser/extensions/chrome_extension_system.cc index 39baa2e..4203d93 100644 --- a/chrome/browser/extensions/chrome_extension_system.cc +++ b/chrome/browser/extensions/chrome_extension_system.cc
@@ -254,6 +254,7 @@ quota_service_ = std::make_unique<QuotaService>(); bool skip_session_extensions = false; + auto* component_loader = ComponentLoader::Get(profile_); #if BUILDFLAG(IS_CHROMEOS) // Skip loading session extensions if we are not in a user session or if the // profile is the sign-in or lock screen app profile, which don't correspond @@ -261,15 +262,13 @@ skip_session_extensions = !ash::LoginState::Get()->IsUserLoggedIn() || !ash::ProfileHelper::IsUserProfile(profile_); if (IsRunningInForcedAppMode()) { - extension_service_->component_loader() - ->AddDefaultComponentExtensionsForKioskMode(skip_session_extensions); - } else { - extension_service_->component_loader()->AddDefaultComponentExtensions( + component_loader->AddDefaultComponentExtensionsForKioskMode( skip_session_extensions); + } else { + component_loader->AddDefaultComponentExtensions(skip_session_extensions); } #else - extension_service_->component_loader()->AddDefaultComponentExtensions( - skip_session_extensions); + component_loader->AddDefaultComponentExtensions(skip_session_extensions); #endif app_sorting_ = std::make_unique<ChromeAppSorting>(profile_);
diff --git a/chrome/browser/extensions/component_loader_unittest.cc b/chrome/browser/extensions/component_loader_unittest.cc index ca05ebe6..89b0f97 100644 --- a/chrome/browser/extensions/component_loader_unittest.cc +++ b/chrome/browser/extensions/component_loader_unittest.cc
@@ -78,10 +78,19 @@ ASSERT_TRUE(base::ReadFileToString( extension_path_.Append(kManifestFilename), &manifest_contents_)); + + component_loader_ = ComponentLoader::Get(profile()); + } + + void TearDown() override { + component_loader_ = nullptr; + extension_system_ = nullptr; + ExtensionServiceUserTestBase::TearDown(); } protected: - raw_ptr<TestExtensionSystem> extension_system_; + raw_ptr<TestExtensionSystem> extension_system_ = nullptr; + raw_ptr<ComponentLoader> component_loader_ = nullptr; // The root directory of the text extension. base::FilePath extension_path_; @@ -99,9 +108,9 @@ // (users for ChromeOS Ash). void RunEmitUserHistogramsTest(int nonuser_expected_total_count, int user_expected_total_count) { - service_->component_loader()->set_profile_for_testing(profile()); + component_loader_->set_profile_for_testing(profile()); base::HistogramTester histograms; - service_->component_loader()->LoadAll(); + component_loader_->LoadAll(); histograms.ExpectTotalCount("Extensions.LoadAllComponentTime", 1); histograms.ExpectTotalCount("Extensions.LoadAllComponentTime.NonUser", nonuser_expected_total_count); @@ -114,43 +123,42 @@ std::optional<base::Value::Dict> manifest; // Test invalid JSON. - manifest = - service_->component_loader()->ParseManifest("{ 'test': 3 } invalid"); + manifest = component_loader_->ParseManifest("{ 'test': 3 } invalid"); EXPECT_FALSE(manifest); // Test manifests that are valid JSON, but don't have an object literal // at the root. ParseManifest() should always return NULL. - manifest = service_->component_loader()->ParseManifest(std::string()); + manifest = component_loader_->ParseManifest(std::string()); EXPECT_FALSE(manifest); - manifest = service_->component_loader()->ParseManifest("[{ \"foo\": 3 }]"); + manifest = component_loader_->ParseManifest("[{ \"foo\": 3 }]"); EXPECT_FALSE(manifest); - manifest = service_->component_loader()->ParseManifest("\"Test\""); + manifest = component_loader_->ParseManifest("\"Test\""); EXPECT_FALSE(manifest); - manifest = service_->component_loader()->ParseManifest("42"); + manifest = component_loader_->ParseManifest("42"); EXPECT_FALSE(manifest); - manifest = service_->component_loader()->ParseManifest("true"); + manifest = component_loader_->ParseManifest("true"); EXPECT_FALSE(manifest); - manifest = service_->component_loader()->ParseManifest("false"); + manifest = component_loader_->ParseManifest("false"); EXPECT_FALSE(manifest); - manifest = service_->component_loader()->ParseManifest("null"); + manifest = component_loader_->ParseManifest("null"); EXPECT_FALSE(manifest); // Test parsing valid JSON. - manifest = service_->component_loader()->ParseManifest( + manifest = component_loader_->ParseManifest( "{ \"test\": { \"one\": 1 }, \"two\": 2 }"); ASSERT_TRUE(manifest); EXPECT_EQ(1, manifest->FindIntByDottedPath("test.one")); EXPECT_EQ(2, manifest->FindInt("two")); - manifest = service_->component_loader()->ParseManifest(manifest_contents_); + manifest = component_loader_->ParseManifest(manifest_contents_); const std::string* string_value = manifest->FindStringByDottedPath("background.page"); ASSERT_TRUE(string_value); @@ -160,7 +168,7 @@ // Test that the extension isn't loaded if the extension service isn't ready. TEST_F(ComponentLoaderTest, AddWhenNotReady) { std::string extension_id = - service_->component_loader()->Add(manifest_contents_, extension_path_); + component_loader_->Add(manifest_contents_, extension_path_); EXPECT_NE("", extension_id); ExtensionRegistry* registry = ExtensionRegistry::Get(profile()); EXPECT_EQ(0u, registry->enabled_extensions().size()); @@ -170,7 +178,7 @@ TEST_F(ComponentLoaderTest, AddWhenReady) { extension_system_->SetReady(); std::string extension_id = - service_->component_loader()->Add(manifest_contents_, extension_path_); + component_loader_->Add(manifest_contents_, extension_path_); EXPECT_NE("", extension_id); ExtensionRegistry* registry = ExtensionRegistry::Get(profile()); EXPECT_EQ(1u, registry->enabled_extensions().size()); @@ -181,25 +189,25 @@ ExtensionRegistry* registry = ExtensionRegistry::Get(profile()); // Removing an extension that was never added should be ok. - service_->component_loader()->Remove(extension_path_); + component_loader_->Remove(extension_path_); EXPECT_EQ(0u, registry->enabled_extensions().size()); // Try adding and removing before LoadAll() is called. - service_->component_loader()->Add(manifest_contents_, extension_path_); - service_->component_loader()->Remove(extension_path_); - service_->component_loader()->LoadAll(); + component_loader_->Add(manifest_contents_, extension_path_); + component_loader_->Remove(extension_path_); + component_loader_->LoadAll(); EXPECT_EQ(0u, registry->enabled_extensions().size()); // Load an extension, and check that it's unloaded when Remove() is called. extension_system_->SetReady(); std::string extension_id = - service_->component_loader()->Add(manifest_contents_, extension_path_); + component_loader_->Add(manifest_contents_, extension_path_); EXPECT_EQ(1u, registry->enabled_extensions().size()); - service_->component_loader()->Remove(extension_path_); + component_loader_->Remove(extension_path_); EXPECT_EQ(0u, registry->enabled_extensions().size()); // And after calling LoadAll(), it shouldn't get loaded. - service_->component_loader()->LoadAll(); + component_loader_->LoadAll(); EXPECT_EQ(0u, registry->enabled_extensions().size()); } @@ -207,18 +215,18 @@ ExtensionRegistry* registry = ExtensionRegistry::Get(profile()); // No extensions should be loaded if none were added. - service_->component_loader()->LoadAll(); + component_loader_->LoadAll(); EXPECT_EQ(0u, registry->enabled_extensions().size()); // Use LoadAll() to load the default extensions. - service_->component_loader()->AddDefaultComponentExtensions(false); - service_->component_loader()->LoadAll(); + component_loader_->AddDefaultComponentExtensions(false); + component_loader_->LoadAll(); unsigned int default_count = registry->enabled_extensions().size(); // Clear the list of loaded extensions, and reload with one more. extension_system_->extension_service()->UnloadAllExtensionsForTest(); - service_->component_loader()->Add(manifest_contents_, extension_path_); - service_->component_loader()->LoadAll(); + component_loader_->Add(manifest_contents_, extension_path_); + component_loader_->LoadAll(); EXPECT_EQ(default_count + 1, registry->enabled_extensions().size()); } @@ -241,14 +249,13 @@ TEST_F(ComponentLoaderTest, DISABLED_AddOrReplace) { ExtensionRegistry* registry = ExtensionRegistry::Get(profile()); ExtensionUnloadedObserver unload_observer(registry); - EXPECT_EQ(0u, service_->component_loader()->registered_extensions_count()); + EXPECT_EQ(0u, component_loader_->registered_extensions_count()); // Allow the Feedback extension, which has a background page, to be loaded. - service_->component_loader()->EnableBackgroundExtensionsForTesting(); + component_loader_->EnableBackgroundExtensionsForTesting(); - service_->component_loader()->AddDefaultComponentExtensions(false); - size_t const default_count = - service_->component_loader()->registered_extensions_count(); + component_loader_->AddDefaultComponentExtensions(false); + size_t const default_count = component_loader_->registered_extensions_count(); base::FilePath known_extension = GetBasePath() .AppendASCII("override_component_extension"); base::FilePath unknown_extension = extension_path_; @@ -256,29 +263,27 @@ .AppendASCII("this_path_does_not_exist"); // Replace a default component extension. - service_->component_loader()->AddOrReplace(known_extension); - EXPECT_EQ(default_count, - service_->component_loader()->registered_extensions_count()); + component_loader_->AddOrReplace(known_extension); + EXPECT_EQ(default_count, component_loader_->registered_extensions_count()); // Add a new component extension. - service_->component_loader()->AddOrReplace(unknown_extension); + component_loader_->AddOrReplace(unknown_extension); EXPECT_EQ(default_count + 1, - service_->component_loader()->registered_extensions_count()); + component_loader_->registered_extensions_count()); extension_system_->SetReady(); - service_->component_loader()->LoadAll(); + component_loader_->LoadAll(); EXPECT_EQ(default_count + 1, registry->enabled_extensions().size()); EXPECT_EQ(0u, unload_observer.unloaded_count()); // replace loaded component extension. - service_->component_loader()->AddOrReplace(known_extension); + component_loader_->AddOrReplace(known_extension); EXPECT_EQ(default_count + 1, registry->enabled_extensions().size()); EXPECT_EQ(1u, unload_observer.unloaded_count()); // Add an invalid component extension. - std::string extension_id = - service_->component_loader()->AddOrReplace(invalid_extension); + std::string extension_id = component_loader_->AddOrReplace(invalid_extension); EXPECT_TRUE(extension_id.empty()); }
diff --git a/chrome/browser/extensions/extension_browsertest.cc b/chrome/browser/extensions/extension_browsertest.cc index 575a926..9b68536 100644 --- a/chrome/browser/extensions/extension_browsertest.cc +++ b/chrome/browser/extensions/extension_browsertest.cc
@@ -55,7 +55,6 @@ #include "chrome/browser/ui/webui/theme_source.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" -#include "components/crx_file/crx_verifier.h" #include "components/services/app_service/public/cpp/app_launch_util.h" #include "components/sync/model/string_ordinal.h" #include "components/version_info/version_info.h" @@ -74,7 +73,6 @@ #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" #include "extensions/browser/service_worker/service_worker_test_utils.h" -#include "extensions/browser/test_extension_registry_observer.h" #include "extensions/browser/uninstall_reason.h" #include "extensions/browser/updater/extension_cache_fake.h" #include "extensions/common/api/web_accessible_resources.h" @@ -105,15 +103,16 @@ #endif override_prompt_for_external_extensions_( FeatureSwitch::prompt_for_external_extensions(), - false), + false) #if BUILDFLAG(IS_WIN) + , user_desktop_override_(base::DIR_USER_DESKTOP), common_desktop_override_(base::DIR_COMMON_DESKTOP), user_quick_launch_override_(base::DIR_USER_QUICK_LAUNCH), start_menu_override_(base::DIR_START_MENU), - common_start_menu_override_(base::DIR_COMMON_START_MENU), + common_start_menu_override_(base::DIR_COMMON_START_MENU) #endif - verifier_format_override_(crx_file::VerifierFormat::CRX3) { +{ EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); } @@ -123,6 +122,14 @@ return ExtensionSystem::Get(profile())->extension_service(); } +std::unique_ptr<ExtensionTestNotificationObserver> +ExtensionBrowserTest::CreateTestNotificationObserver() { + return browser() ? std::make_unique<ChromeExtensionTestNotificationObserver>( + browser()) + : std::make_unique<ChromeExtensionTestNotificationObserver>( + profile()); +} + Profile* ExtensionBrowserTest::profile() { if (!profile_) { if (browser()) { @@ -134,33 +141,6 @@ return profile_; } -bool ExtensionBrowserTest::ShouldEnableContentVerification() { - return false; -} - -bool ExtensionBrowserTest::ShouldEnableInstallVerification() { - return false; -} - -bool ExtensionBrowserTest::ShouldAllowMV2Extensions() { - return true; -} - -// static -const Extension* ExtensionBrowserTest::GetExtensionByPath( - const ExtensionSet& extensions, - const base::FilePath& path) { - base::ScopedAllowBlockingForTesting allow_blocking; - base::FilePath extension_path = base::MakeAbsoluteFilePath(path); - EXPECT_TRUE(!extension_path.empty()); - for (const scoped_refptr<const Extension>& extension : extensions) { - if (extension->path() == extension_path) { - return extension.get(); - } - } - return nullptr; -} - void ExtensionBrowserTest::SetUp() { test_extension_cache_ = std::make_unique<ExtensionCacheFake>(); ExtensionPlatformBrowserTest::SetUp(); @@ -169,11 +149,6 @@ void ExtensionBrowserTest::SetUpCommandLine(base::CommandLine* command_line) { ExtensionPlatformBrowserTest::SetUpCommandLine(command_line); - if (!ShouldEnableContentVerification()) { - ignore_content_verification_ = - std::make_unique<ScopedIgnoreContentVerifierForTest>(); - } - if (!ShouldEnableInstallVerification()) { ignore_install_verification_ = std::make_unique<ScopedInstallVerifierBypassForTest>(); @@ -198,11 +173,6 @@ void ExtensionBrowserTest::SetUpOnMainThread() { ExtensionPlatformBrowserTest::SetUpOnMainThread(); - observer_ = - browser() - ? std::make_unique<ChromeExtensionTestNotificationObserver>(browser()) - : std::make_unique<ChromeExtensionTestNotificationObserver>( - profile()); ExtensionUpdater* updater = ExtensionUpdater::Get(profile()); if (updater->enabled()) { updater->SetExtensionCacheForTesting(test_extension_cache_.get()); @@ -278,10 +248,9 @@ return nullptr; } - extension_service()->component_loader()->set_ignore_allowlist_for_testing( - true); - extensions::ExtensionId extension_id = - extension_service()->component_loader()->Add(manifest, path); + auto* component_loader = ComponentLoader::Get(profile()); + component_loader->set_ignore_allowlist_for_testing(true); + extensions::ExtensionId extension_id = component_loader->Add(manifest, path); const Extension* extension = extension_registry()->enabled_extensions().GetByID(extension_id); if (!extension) { @@ -486,7 +455,7 @@ .WaitForBackgroundInitialized(); } - if (!observer_->WaitForExtensionViewsToLoad()) { + if (!test_notification_observer()->WaitForExtensionViewsToLoad()) { return nullptr; } @@ -499,47 +468,21 @@ return registry->enabled_extensions().GetByID(installer->extension()->id()); } -void ExtensionBrowserTest::ReloadExtension( - const extensions::ExtensionId& extension_id) { - scoped_refptr<const Extension> extension = - extension_registry()->GetInstalledExtension(extension_id); - ASSERT_TRUE(extension); - TestExtensionRegistryObserver observer(extension_registry(), extension_id); - extension_service()->ReloadExtension(extension_id); - // Re-grab the extension after the reload to get the updated copy. - extension = observer.WaitForExtensionLoaded(); - // We need to let other ExtensionRegistryObservers handle the extension load - // in order to finish initialization. - base::RunLoop().RunUntilIdle(); - - // Wait for the background context, if any, to start up. - std::string reason_unused; - if (extension_registry()->enabled_extensions().Contains(extension_id) && - ExtensionBackgroundPageWaiter::CanWaitFor(*extension, reason_unused)) { - ExtensionBackgroundPageWaiter(profile(), *extension) - .WaitForBackgroundInitialized(); - } - - // Wait for any additionally-registered extension views to load. - observer_->WaitForExtensionViewsToLoad(); -} - bool ExtensionBrowserTest::WaitForPageActionVisibilityChangeTo(int count) { - return observer_->WaitForPageActionVisibilityChangeTo(count); -} - -bool ExtensionBrowserTest::WaitForExtensionViewsToLoad() { - return observer_->WaitForExtensionViewsToLoad(); + return GetChromeExtensionTestNotificationObserver() + ->WaitForPageActionVisibilityChangeTo(count); } bool ExtensionBrowserTest::WaitForExtensionIdle( const extensions::ExtensionId& extension_id) { - return observer_->WaitForExtensionIdle(extension_id); + return GetChromeExtensionTestNotificationObserver()->WaitForExtensionIdle( + extension_id); } bool ExtensionBrowserTest::WaitForExtensionNotIdle( const extensions::ExtensionId& extension_id) { - return observer_->WaitForExtensionNotIdle(extension_id); + return GetChromeExtensionTestNotificationObserver()->WaitForExtensionNotIdle( + extension_id); } WindowController* ExtensionBrowserTest::GetWindowController() { @@ -551,4 +494,10 @@ #endif } +ChromeExtensionTestNotificationObserver* +ExtensionBrowserTest::GetChromeExtensionTestNotificationObserver() { + return static_cast<ChromeExtensionTestNotificationObserver*>( + test_notification_observer()); +} + } // namespace extensions
diff --git a/chrome/browser/extensions/extension_browsertest.h b/chrome/browser/extensions/extension_browsertest.h index 44d32b20..74277776 100644 --- a/chrome/browser/extensions/extension_browsertest.h +++ b/chrome/browser/extensions/extension_browsertest.h
@@ -26,8 +26,6 @@ #include "extensions/browser/extension_host.h" #include "extensions/browser/extension_protocols.h" #include "extensions/browser/extension_system.h" -#include "extensions/browser/sandboxed_unpacker.h" -#include "extensions/browser/scoped_ignore_content_verifier_for_test.h" #include "extensions/common/extension.h" #include "extensions/common/extension_id.h" #include "extensions/common/feature_switch.h" @@ -41,7 +39,6 @@ class ExtensionBrowserTestPlatformDelegate; class ExtensionCacheFake; class ExtensionService; -class ExtensionSet; class WindowController; // Base class for extension browser tests. Provides utilities for loading, @@ -69,31 +66,14 @@ // Useful accessors. ExtensionService* extension_service(); - // Extensions used in tests are typically not from the web store and will have - // missing content verification hashes. The default implementation disables - // content verification; this should be overridden by derived tests which care - // about content verification. - virtual bool ShouldEnableContentVerification(); - - // Extensions used in tests are typically not from the web store and will fail - // install verification. The default implementation disables install - // verification; this should be overridden by derived tests which care - // about install verification. - virtual bool ShouldEnableInstallVerification(); - - // Whether MV2 extensions should be allowed. Defaults to true for testing - // (since many tests are parameterized to exercise both MV2 + MV3 logic). - virtual bool ShouldAllowMV2Extensions(); - - static const Extension* GetExtensionByPath(const ExtensionSet& extensions, - const base::FilePath& path); - // InProcessBrowserTest void SetUp() override; void SetUpCommandLine(base::CommandLine* command_line) override; void SetUpOnMainThread() override; // ExtensionPlatformBrowserTest: + std::unique_ptr<ExtensionTestNotificationObserver> + CreateTestNotificationObserver() final; Profile* profile() final; // These functions intentionally shadow the versions in the base class @@ -199,14 +179,9 @@ 0); } - void ReloadExtension(const extensions::ExtensionId& extension_id); - // Wait for the number of visible page actions to change to |count|. bool WaitForPageActionVisibilityChangeTo(int count); - // Wait for all extension views to load. - bool WaitForExtensionViewsToLoad(); - // Wait for the extension to be idle. bool WaitForExtensionIdle(const extensions::ExtensionId& extension_id); @@ -219,8 +194,6 @@ bool set_chromeos_user_; #endif - std::unique_ptr<ChromeExtensionTestNotificationObserver> observer_; - private: // Temporary directory for testing. base::ScopedTempDir temp_dir_; @@ -265,6 +238,11 @@ // Returns the WindowController for this test's browser window. WindowController* GetWindowController(); + // A convenience method to get the ExtensionTestNotificationObserver as its + // Chrome-side implementation. + ChromeExtensionTestNotificationObserver* + GetChromeExtensionTestNotificationObserver(); + // Disable external install UI. FeatureSwitch::ScopedOverride override_prompt_for_external_extensions_; @@ -283,18 +261,10 @@ // Cache cache implementation. std::unique_ptr<ExtensionCacheFake> test_extension_cache_; - // Conditionally disable content verification. - std::unique_ptr<ScopedIgnoreContentVerifierForTest> - ignore_content_verification_; - // Conditionally disable install verification. std::unique_ptr<ScopedInstallVerifierBypassForTest> ignore_install_verification_; - // Used to disable CRX publisher signature checking. - SandboxedUnpacker::ScopedVerifierFormatOverrideForTest - verifier_format_override_; - ExtensionUpdater::ScopedSkipScheduledCheckForTest skip_scheduled_check_; // Allows MV2 extensions to be loaded.
diff --git a/chrome/browser/extensions/extension_keybinding_apitest.cc b/chrome/browser/extensions/extension_keybinding_apitest.cc index 8c654b2..e7fa9d9 100644 --- a/chrome/browser/extensions/extension_keybinding_apitest.cc +++ b/chrome/browser/extensions/extension_keybinding_apitest.cc
@@ -12,7 +12,6 @@ #include "chrome/browser/extensions/commands/command_service.h" #include "chrome/browser/extensions/component_loader.h" #include "chrome/browser/extensions/extension_apitest.h" -#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/permissions/active_tab_permission_granter.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" @@ -998,9 +997,7 @@ RunExtensionTest("keybinding/component", {}, {.load_as_component = true})) << message_; - extensions::ExtensionSystem::Get(browser()->profile()) - ->extension_service() - ->component_loader() + extensions::ComponentLoader::Get(browser()->profile()) ->Remove("pkplfbidichfdicaijlchgnapepdginl"); ASSERT_TRUE(
diff --git a/chrome/browser/extensions/extension_platform_browsertest.cc b/chrome/browser/extensions/extension_platform_browsertest.cc index a2f468a..b8f496db2 100644 --- a/chrome/browser/extensions/extension_platform_browsertest.cc +++ b/chrome/browser/extensions/extension_platform_browsertest.cc
@@ -13,6 +13,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/common/chrome_paths.h" #include "chrome/test/base/chrome_test_utils.h" +#include "components/crx_file/crx_verifier.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test_utils.h" @@ -22,12 +23,16 @@ #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" #include "extensions/browser/extension_util.h" +#include "extensions/browser/scoped_ignore_content_verifier_for_test.h" #include "extensions/browser/service_worker/service_worker_test_utils.h" +#include "extensions/browser/test_extension_registry_observer.h" #include "extensions/buildflags/buildflags.h" #include "extensions/common/extension_id.h" #include "extensions/common/extension_paths.h" #include "extensions/common/features/feature_channel.h" #include "extensions/common/manifest_handlers/background_info.h" +#include "extensions/test/extension_background_page_waiter.h" +#include "extensions/test/extension_test_notification_observer.h" #if BUILDFLAG(ENABLE_EXTENSIONS) #include "chrome/browser/extensions/chrome_test_extension_loader.h" @@ -198,12 +203,40 @@ : context_type_(context_type), // TODO(crbug.com/40261741): Move this ScopedCurrentChannel down into // tests that specifically require it. - current_channel_(version_info::Channel::UNKNOWN) { + current_channel_(version_info::Channel::UNKNOWN), + verifier_format_override_(crx_file::VerifierFormat::CRX3) { EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); } ExtensionPlatformBrowserTest::~ExtensionPlatformBrowserTest() = default; +bool ExtensionPlatformBrowserTest::ShouldEnableContentVerification() { + return false; +} + +bool ExtensionPlatformBrowserTest::ShouldEnableInstallVerification() { + return false; +} + +bool ExtensionPlatformBrowserTest::ShouldAllowMV2Extensions() { + return true; +} + +// static +const Extension* ExtensionPlatformBrowserTest::GetExtensionByPath( + const ExtensionSet& extensions, + const base::FilePath& path) { + base::ScopedAllowBlockingForTesting allow_blocking; + base::FilePath extension_path = base::MakeAbsoluteFilePath(path); + EXPECT_TRUE(!extension_path.empty()); + for (const scoped_refptr<const Extension>& extension : extensions) { + if (extension->path() == extension_path) { + return extension.get(); + } + } + return nullptr; +} + void ExtensionPlatformBrowserTest::SetUp() { EnsureBrowserContextKeyedServiceFactoriesBuilt(); PlatformBrowserTest::SetUp(); @@ -218,6 +251,11 @@ base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_); test_data_dir_ = test_data_dir_.AppendASCII("extensions"); #endif + + if (!ShouldEnableContentVerification()) { + ignore_content_verification_ = + std::make_unique<ScopedIgnoreContentVerifierForTest>(); + } } void ExtensionPlatformBrowserTest::SetUpOnMainThread() { @@ -238,6 +276,8 @@ if (web_contents) { web_contents_ = web_contents->GetWeakPtr(); } + + test_notification_observer_ = CreateTestNotificationObserver(); } void ExtensionPlatformBrowserTest::TearDown() { @@ -364,6 +404,32 @@ extension_registrar()->EnableExtension(extension_id); } +void ExtensionPlatformBrowserTest::ReloadExtension( + const extensions::ExtensionId& extension_id) { + scoped_refptr<const Extension> extension = + extension_registry()->GetInstalledExtension(extension_id); + ASSERT_TRUE(extension); + TestExtensionRegistryObserver observer(extension_registry(), extension_id); + extension_registrar()->ReloadExtension( + extension_id, ExtensionRegistrar::LoadErrorBehavior::kNoisy); + // Re-grab the extension after the reload to get the updated copy. + extension = observer.WaitForExtensionLoaded(); + // We need to let other ExtensionRegistryObservers handle the extension load + // in order to finish initialization. + base::RunLoop().RunUntilIdle(); + + // Wait for the background context, if any, to start up. + std::string reason_unused; + if (extension_registry()->enabled_extensions().Contains(extension_id) && + ExtensionBackgroundPageWaiter::CanWaitFor(*extension, reason_unused)) { + ExtensionBackgroundPageWaiter(profile(), *extension) + .WaitForBackgroundInitialized(); + } + + // Wait for any additionally-registered extension views to load. + test_notification_observer_->WaitForExtensionViewsToLoad(); +} + content::WebContents* ExtensionPlatformBrowserTest::GetActiveWebContents() const { #if !BUILDFLAG(IS_ANDROID) @@ -647,6 +713,15 @@ SetExtensionProtocolTestHandler(nullptr); } +bool ExtensionPlatformBrowserTest::WaitForExtensionViewsToLoad() { + return test_notification_observer_->WaitForExtensionViewsToLoad(); +} + +std::unique_ptr<ExtensionTestNotificationObserver> +ExtensionPlatformBrowserTest::CreateTestNotificationObserver() { + return std::make_unique<ExtensionTestNotificationObserver>(profile()); +} + Profile* ExtensionPlatformBrowserTest::profile() { return chrome_test_utils::GetProfile(this); }
diff --git a/chrome/browser/extensions/extension_platform_browsertest.h b/chrome/browser/extensions/extension_platform_browsertest.h index badeb99..792879af 100644 --- a/chrome/browser/extensions/extension_platform_browsertest.h +++ b/chrome/browser/extensions/extension_platform_browsertest.h
@@ -16,6 +16,7 @@ #include "extensions/browser/extension_protocols.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_registry_observer.h" +#include "extensions/browser/sandboxed_unpacker.h" #include "extensions/buildflags/buildflags.h" #include "extensions/common/extension_id.h" #include "extensions/common/features/feature_channel.h" @@ -30,10 +31,13 @@ namespace extensions { class Extension; -class ExtensionHost; class ExtensionBrowserTestPlatformDelegate; +class ExtensionHost; class ExtensionRegistrar; +class ExtensionSet; +class ExtensionTestNotificationObserver; class ProcessManager; +class ScopedIgnoreContentVerifierForTest; // A cross-platform base class for extensions-related browser tests. // `PlatformBrowserTest` inherits from different test suites based on the @@ -57,6 +61,26 @@ // and should be able to access anything any general test would access. friend class ExtensionBrowserTestPlatformDelegate; + // Extensions used in tests are typically not from the web store and will have + // missing content verification hashes. The default implementation disables + // content verification; this should be overridden by derived tests which care + // about content verification. + virtual bool ShouldEnableContentVerification(); + + // Extensions used in tests are typically not from the web store and will fail + // install verification. The default implementation disables install + // verification; this should be overridden by derived tests which care + // about install verification. + virtual bool ShouldEnableInstallVerification(); + + // Whether MV2 extensions should be allowed. Defaults to true for testing + // (since many tests are parameterized to exercise both MV2 + MV3 logic). + virtual bool ShouldAllowMV2Extensions(); + + // Returns the extension in `extensions` with the given `path`, if one exists. + static const Extension* GetExtensionByPath(const ExtensionSet& extensions, + const base::FilePath& path); + // content::BrowserTestBase: void SetUp() override; void SetUpCommandLine(base::CommandLine* command_line) override; @@ -95,6 +119,9 @@ // Enables the extension with the given `extension_id`. void EnableExtension(const ExtensionId& extension_id); + // Reloads the extension with the given `extension_id`. + void ReloadExtension(const ExtensionId& extension_id); + // Returns the WebContents of the currently active tab. // Note that when the test first launches, this will be the same as the // default tab's web_contents(). However, if the test creates new tabs and @@ -208,6 +235,14 @@ // Tears down test protocol handler. void TearDownTestProtocolHandler(); + // Wait for all extension views to load. + bool WaitForExtensionViewsToLoad(); + + // Creates the ExtensionTestNotificationObserver to use; this allows other + // implementations to use a more specialized variant. + virtual std::unique_ptr<ExtensionTestNotificationObserver> + CreateTestNotificationObserver(); + // Lower case to match the style of InProcessBrowserTest. virtual Profile* profile(); @@ -221,6 +256,10 @@ last_loaded_extension_id_ = std::move(id); } + ExtensionTestNotificationObserver* test_notification_observer() { + return test_notification_observer_.get(); + } + // Set to "chrome/test/data/extensions". Derived classes may override. base::FilePath test_data_dir_; @@ -253,6 +292,17 @@ // have non-trunk coverage for most extension browser tests. ScopedCurrentChannel current_channel_; + // Conditionally disable content verification. + std::unique_ptr<ScopedIgnoreContentVerifierForTest> + ignore_content_verification_; + + // Used to disable CRX publisher signature checking. + SandboxedUnpacker::ScopedVerifierFormatOverrideForTest + verifier_format_override_; + + std::unique_ptr<ExtensionTestNotificationObserver> + test_notification_observer_; + // Listens to extension loaded notifications. base::ScopedObservation<ExtensionRegistry, ExtensionRegistryObserver> registry_observation_{this};
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h index fc79a47..08f93af 100644 --- a/chrome/browser/extensions/extension_service.h +++ b/chrome/browser/extensions/extension_service.h
@@ -347,10 +347,6 @@ Profile* profile() { return profile_; } - // TODO(crbug.com/408495366): Delete this method. Use ComponentLoader::Get() - // instead. - ComponentLoader* component_loader() { return component_loader_; } - SharedModuleService* shared_module_service() { return shared_module_service_.get(); }
diff --git a/chrome/browser/extensions/extension_service_test_base.cc b/chrome/browser/extensions/extension_service_test_base.cc index 0e2d360..1e2cd914 100644 --- a/chrome/browser/extensions/extension_service_test_base.cc +++ b/chrome/browser/extensions/extension_service_test_base.cc
@@ -492,7 +492,7 @@ base::CommandLine::ForCurrentProcess(), extensions_install_dir_, unpacked_install_dir_, autoupdate_enabled, extensions_enabled); - service_->component_loader()->set_ignore_allowlist_for_testing(true); + ComponentLoader::Get(profile())->set_ignore_allowlist_for_testing(true); // When we start up, we want to make sure there is no external provider, // since the ExtensionService on Windows will use the Registry as a default
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc index e54ea542..c751327 100644 --- a/chrome/browser/extensions/extension_service_unittest.cc +++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -3010,7 +3010,7 @@ // Ensures that the CWS has properly initialized ordinals. TEST_F(ExtensionServiceTest, EnsureCWSOrdinalsInitialized) { InitializeEmptyExtensionService(); - service()->component_loader()->Add( + ComponentLoader::Get(profile())->Add( IDR_WEBSTORE_MANIFEST, base::FilePath(FILE_PATH_LITERAL("web_store"))); service()->Init(); @@ -4095,7 +4095,7 @@ std::string manifest; ASSERT_TRUE( base::ReadFileToString(path.Append(kManifestFilename), &manifest)); - service()->component_loader()->Add(manifest, path); + ComponentLoader::Get(profile())->Add(manifest, path); service()->Init(); // Component extension should never block. @@ -4202,7 +4202,7 @@ std::string manifest; ASSERT_TRUE( base::ReadFileToString(path.Append(kManifestFilename), &manifest)); - service()->component_loader()->Add(manifest, path); + ComponentLoader::Get(profile())->Add(manifest, path); service()->Init(); // Extension should be installed despite blocklist. @@ -4238,7 +4238,7 @@ std::string manifest; ASSERT_TRUE( base::ReadFileToString(path.Append(kManifestFilename), &manifest)); - service()->component_loader()->Add(manifest, path); + ComponentLoader::Get(profile())->Add(manifest, path); service()->Init(); // Extension should have the "tabs" permission. @@ -6949,7 +6949,7 @@ ASSERT_TRUE( base::ReadFileToString(path.Append(kManifestFilename), &manifest)); - service()->component_loader()->Add(manifest, path); + ComponentLoader::Get(profile())->Add(manifest, path); service()->Init(); // Note that we do not pump messages -- the extension should be loaded
diff --git a/chrome/browser/extensions/extension_sync_service_unittest.cc b/chrome/browser/extensions/extension_sync_service_unittest.cc index 1f6f538..8be3a5f 100644 --- a/chrome/browser/extensions/extension_sync_service_unittest.cc +++ b/chrome/browser/extensions/extension_sync_service_unittest.cc
@@ -68,6 +68,7 @@ using extensions::AccountExtensionTracker; using extensions::AppSorting; +using extensions::ComponentLoader; using extensions::Extension; using extensions::ExtensionPrefs; using extensions::ExtensionRegistry; @@ -262,7 +263,7 @@ std::string manifest; ASSERT_TRUE(base::ReadFileToString( good0_path().Append(extensions::kManifestFilename), &manifest)); - service()->component_loader()->Add(manifest, good0_path()); + ComponentLoader::Get(profile())->Add(manifest, good0_path()); ASSERT_FALSE(extension_system()->is_ready()); service()->Init(); ASSERT_TRUE(extension_system()->is_ready());
diff --git a/chrome/browser/glic/BUILD.gn b/chrome/browser/glic/BUILD.gn index 816f5fb..f4d0e58e 100644 --- a/chrome/browser/glic/BUILD.gn +++ b/chrome/browser/glic/BUILD.gn
@@ -86,6 +86,8 @@ "browser_ui/theme_util.h", "fre/fre_util.cc", "fre/fre_util.h", + "fre/fre_webui_contents_container.cc", + "fre/fre_webui_contents_container.h", "fre/glic_fre_controller.cc", "fre/glic_fre_controller.h", "fre/glic_fre_dialog_view.cc",
diff --git a/chrome/browser/glic/fre/fre_webui_contents_container.cc b/chrome/browser/glic/fre/fre_webui_contents_container.cc new file mode 100644 index 0000000..834e910 --- /dev/null +++ b/chrome/browser/glic/fre/fre_webui_contents_container.cc
@@ -0,0 +1,40 @@ +// 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/glic/fre/fre_webui_contents_container.h" + +#include "chrome/browser/glic/fre/glic_fre_controller.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/webui_url_constants.h" + +namespace glic { + +FreWebUIContentsContainer::FreWebUIContentsContainer( + Profile* profile, + views::WebView* web_view, + GlicFreController* fre_controller) + : web_contents_(content::WebContents::Create( + content::WebContents::CreateParams(profile))), + fre_web_view_(web_view), + fre_controller_(fre_controller) { + CHECK(web_contents_); + web_contents_->SetDelegate(this); + web_contents_->SetPageBaseBackgroundColor(SK_ColorTRANSPARENT); + + web_contents_->GetController().LoadURLWithParams( + content::NavigationController::LoadURLParams( + GURL{chrome::kChromeUIGlicFreURL})); +} + +FreWebUIContentsContainer::~FreWebUIContentsContainer() { + web_contents_->ClosePage(); +} + +void FreWebUIContentsContainer::SetContentsBounds(content::WebContents* source, + const gfx::Rect& bounds) { + fre_web_view_->SetPreferredSize(bounds.size()); + fre_controller_->UpdateFreWidgetBounds(bounds); +} + +} // namespace glic
diff --git a/chrome/browser/glic/fre/fre_webui_contents_container.h b/chrome/browser/glic/fre/fre_webui_contents_container.h new file mode 100644 index 0000000..45d22be --- /dev/null +++ b/chrome/browser/glic/fre/fre_webui_contents_container.h
@@ -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. + +#ifndef CHROME_BROWSER_GLIC_FRE_FRE_WEBUI_CONTENTS_CONTAINER_H_ +#define CHROME_BROWSER_GLIC_FRE_FRE_WEBUI_CONTENTS_CONTAINER_H_ + +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_delegate.h" +#include "ui/views/controls/webview/webview.h" + +namespace glic { +class GlicFreController; + +// Owns the `WebContents` that houses Glic's FRE WebUI. +class FreWebUIContentsContainer : public content::WebContentsDelegate { + public: + FreWebUIContentsContainer(Profile* profile, + views::WebView* web_view, + GlicFreController* fre_controller); + ~FreWebUIContentsContainer() override; + + FreWebUIContentsContainer(const FreWebUIContentsContainer&) = delete; + FreWebUIContentsContainer& operator=(const FreWebUIContentsContainer&) = + delete; + + // content::WebContentsDelegate implementation: + void SetContentsBounds(content::WebContents* source, + const gfx::Rect& bounds) override; + + content::WebContents* web_contents() { return web_contents_.get(); } + + private: + const std::unique_ptr<content::WebContents> web_contents_; + raw_ptr<views::WebView> fre_web_view_; + raw_ptr<GlicFreController> fre_controller_; +}; + +} // namespace glic + +#endif // CHROME_BROWSER_GLIC_FRE_FRE_WEBUI_CONTENTS_CONTAINER_H_
diff --git a/chrome/browser/glic/fre/glic_fre_controller.cc b/chrome/browser/glic/fre/glic_fre_controller.cc index 0c9cf8f..b1cc6215 100644 --- a/chrome/browser/glic/fre/glic_fre_controller.cc +++ b/chrome/browser/glic/fre/glic_fre_controller.cc
@@ -391,11 +391,9 @@ return; } - gfx::Size initial_size(features::kGlicFreInitialWidth.Get(), - features::kGlicFreInitialHeight.Get()); - fre_view_ = std::make_unique<GlicFreDialogView>(profile_, initial_size); + fre_view_ = std::make_unique<GlicFreDialogView>(profile_, this); web_contents_ = fre_view_->web_contents(); - web_contents_->Resize(gfx::Rect(initial_size)); + web_contents_->Resize(gfx::Rect(GetFreInitialSize())); auto* service = GlicKeyedServiceFactory::GetGlicKeyedService(profile_); GlicProfileManager::GetInstance()->OnLoadingClientForService(service); } @@ -414,4 +412,17 @@ return !!fre_widget_; } +gfx::Size GlicFreController::GetFreInitialSize() { + return gfx::Size(features::kGlicFreInitialWidth.Get(), + features::kGlicFreInitialHeight.Get()); +} + +void GlicFreController::UpdateFreWidgetBounds(const gfx::Rect& bounds) { + if (!fre_widget_) { + return; + } + + fre_widget_->SetBounds(bounds); +} + } // namespace glic
diff --git a/chrome/browser/glic/fre/glic_fre_controller.h b/chrome/browser/glic/fre/glic_fre_controller.h index ebb107e..c72999e 100644 --- a/chrome/browser/glic/fre/glic_fre_controller.h +++ b/chrome/browser/glic/fre/glic_fre_controller.h
@@ -101,6 +101,10 @@ bool IsShowingDialog() const; + gfx::Size GetFreInitialSize(); + + void UpdateFreWidgetBounds(const gfx::Rect& bounds); + AuthController& GetAuthControllerForTesting() { return auth_controller_; } base::WeakPtr<GlicFreController> GetWeakPtr() {
diff --git a/chrome/browser/glic/fre/glic_fre_dialog_view.cc b/chrome/browser/glic/fre/glic_fre_dialog_view.cc index d5f1220e..6a90920 100644 --- a/chrome/browser/glic/fre/glic_fre_dialog_view.cc +++ b/chrome/browser/glic/fre/glic_fre_dialog_view.cc
@@ -6,7 +6,9 @@ #include <memory> +#include "chrome/browser/glic/fre/glic_fre_controller.h" #include "chrome/browser/glic/resources/grit/glic_browser_resources.h" +#include "chrome/browser/glic/widget/glic_view.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/common/webui_url_constants.h" @@ -24,7 +26,7 @@ kWebViewElementIdForTesting); GlicFreDialogView::GlicFreDialogView(Profile* profile, - const gfx::Size& initial_size) { + GlicFreController* fre_controller) { SetShowCloseButton(false); SetButtons(static_cast<int>(ui::mojom::DialogButton::kNone)); SetModalType(ui::mojom::ModalType::kChild); @@ -35,22 +37,17 @@ set_corner_radius(12); SetAccessibleTitle(l10n_util::GetStringUTF16(IDS_GLIC_WINDOW_TITLE)); - SetUseDefaultFillLayout(true); + SetLayoutManager(std::make_unique<views::FillLayout>()); auto web_view = std::make_unique<views::WebView>(profile); web_view->SetProperty(views::kElementIdentifierKey, kWebViewElementIdForTesting); - web_view->SetSize(initial_size); - web_view->SetPreferredSize(initial_size); + web_view_ = web_view.get(); + web_view->SetPreferredSize(fre_controller->GetFreInitialSize()); - web_contents_ = - content::WebContents::Create(content::WebContents::CreateParams(profile)); - DCHECK(web_contents_); - web_contents_->SetPageBaseBackgroundColor(SK_ColorTRANSPARENT); - web_contents_->GetController().LoadURLWithParams( - content::NavigationController::LoadURLParams( - GURL{chrome::kChromeUIGlicFreURL})); - web_view->SetWebContents(web_contents_.get()); + contents_ = std::make_unique<FreWebUIContentsContainer>(profile, web_view_, + fre_controller); + web_view->SetWebContents(contents_->web_contents()); AddChildView(std::move(web_view)); }
diff --git a/chrome/browser/glic/fre/glic_fre_dialog_view.h b/chrome/browser/glic/fre/glic_fre_dialog_view.h index f3fbf4f..18477fe 100644 --- a/chrome/browser/glic/fre/glic_fre_dialog_view.h +++ b/chrome/browser/glic/fre/glic_fre_dialog_view.h
@@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_GLIC_FRE_GLIC_FRE_DIALOG_VIEW_H_ #define CHROME_BROWSER_GLIC_FRE_GLIC_FRE_DIALOG_VIEW_H_ +#include "chrome/browser/glic/fre/fre_webui_contents_container.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" #include "ui/views/window/dialog_delegate.h" @@ -17,17 +18,21 @@ // View, which is the preferred structure. class GlicFreDialogView : public views::DialogDelegateView { public: - GlicFreDialogView(Profile* profile, const gfx::Size& initial_size); + GlicFreDialogView(Profile* profile, GlicFreController* fre_controller); GlicFreDialogView(const GlicFreDialogView&) = delete; GlicFreDialogView& operator=(const GlicFreDialogView&) = delete; ~GlicFreDialogView() override; DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kWebViewElementIdForTesting); - content::WebContents* web_contents() { return web_contents_.get(); } + content::WebContents* web_contents() { return contents_->web_contents(); } + + views::WebView* web_view() { return web_view_; } private: - std::unique_ptr<content::WebContents> web_contents_; + std::unique_ptr<FreWebUIContentsContainer> contents_; + + raw_ptr<views::WebView> web_view_; }; } // namespace glic
diff --git a/chrome/browser/glic/resources/browser_resources.grd b/chrome/browser/glic/resources/browser_resources.grd index 24f2adc..a1bf16c 100644 --- a/chrome/browser/glic/resources/browser_resources.grd +++ b/chrome/browser/glic/resources/browser_resources.grd
@@ -260,7 +260,7 @@ Unpin Glic </message> <message name="IDS_GLIC_CONTEXTUAL_CUEING_ANNOUNCEMENT" desc="Polite announcement that happens when a contextual nudge is shown next to the Glic button." translateable="false"> - To open Glic in Chrome, press <ph name="GLOBAL_HOTKEY">⌘+G</ph> + To open Glic in Chrome, press <ph name="GLOBAL_HOTKEY">$1<ex>⌘+G</ex></ph> </message> <!-- Screenpicker Menu String --> @@ -282,10 +282,10 @@ Glic </message> <message name="IDS_GLIC_WINDOW_TITLE_FIRST_LOAD" desc="Accessible title for the Glic Window. Only included on first load." translateable="false"> - Glic. To return to where you were on the page press <ph name="FOCUS_TOGGLE_HOTKEY">Ctrl+⌘+G</ph> + Glic. To return to where you were on the page press <ph name="FOCUS_TOGGLE_HOTKEY">$1<ex>Ctrl+⌘+G</ex></ph> </message> <message name="IDS_GLIC_WINDOW_FIRST_FOCUS_LOST_ANNOUNCEMENT" desc="Announcement triggered the first time the Glic window loses focus." translateable="false"> - To return to Glic, press <ph name="FOCUS_TOGGLE_HOTKEY">Ctrl+⌘+G</ph> + To return to Glic, press <ph name="FOCUS_TOGGLE_HOTKEY">$1<ex>Ctrl+⌘+G</ex></ph> </message> <!-- Task Manager -->
diff --git a/chrome/browser/glic/resources/internal b/chrome/browser/glic/resources/internal index 92c43ad..4fd0ec8 160000 --- a/chrome/browser/glic/resources/internal +++ b/chrome/browser/glic/resources/internal
@@ -1 +1 @@ -Subproject commit 92c43ad104487e9b5e8814dee4be94e18656fa27 +Subproject commit 4fd0ec8889b4f3eda472f992d20957a077bc202e
diff --git a/chrome/browser/glic/test_support/glic_internal_test_headers.h b/chrome/browser/glic/test_support/glic_internal_test_headers.h index 0f8d29e2..32b2727 100644 --- a/chrome/browser/glic/test_support/glic_internal_test_headers.h +++ b/chrome/browser/glic/test_support/glic_internal_test_headers.h
@@ -17,6 +17,7 @@ #include "chrome/browser/glic/fre/fre_util.h" #include "chrome/browser/glic/glic_keyed_service.h" #include "chrome/browser/glic/glic_keyed_service_factory.h" +#include "chrome/browser/glic/glic_pref_names.h" #include "chrome/browser/glic/host/guest_util.h" #include "chrome/browser/glic/test_support/glic_test_util.h" #include "chrome/browser/glic/test_support/interactive_test_util.h"
diff --git a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/FadeHubLayoutAnimationFactory.java b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/FadeHubLayoutAnimationFactory.java index b990e999..84ae9c7 100644 --- a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/FadeHubLayoutAnimationFactory.java +++ b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/FadeHubLayoutAnimationFactory.java
@@ -4,7 +4,7 @@ package org.chromium.chrome.browser.hub; -import androidx.annotation.NonNull; +import org.chromium.build.annotations.NullMarked; import java.util.function.DoubleConsumer; @@ -22,6 +22,7 @@ * long)} or {@link #createFadeOutAnimator(HubContainerView, long)} to prepare an animator when * {@link HubLayoutAnimatorProvider#supplyAnimatorNow()} is invoked. */ +@NullMarked public class FadeHubLayoutAnimationFactory { /** * Create a fade in {@link HubLayoutAnimator}. @@ -32,9 +33,7 @@ * @return the requested animator. */ public static HubLayoutAnimator createFadeInAnimator( - @NonNull HubContainerView hubContainerView, - long durationMs, - @NonNull DoubleConsumer onAlphaChange) { + HubContainerView hubContainerView, long durationMs, DoubleConsumer onAlphaChange) { return FadeHubLayoutAnimationFactoryImpl.createFadeInAnimator( hubContainerView, durationMs, onAlphaChange); } @@ -48,9 +47,7 @@ * @return the requested animator. */ public static HubLayoutAnimator createFadeOutAnimator( - @NonNull HubContainerView hubContainerView, - long durationMs, - @NonNull DoubleConsumer onAlphaChange) { + HubContainerView hubContainerView, long durationMs, DoubleConsumer onAlphaChange) { return FadeHubLayoutAnimationFactoryImpl.createFadeOutAnimator( hubContainerView, durationMs, onAlphaChange); } @@ -64,9 +61,7 @@ * @return the requested animator provider */ public static HubLayoutAnimatorProvider createFadeInAnimatorProvider( - @NonNull HubContainerView hubContainerView, - long durationMs, - @NonNull DoubleConsumer onAlphaChange) { + HubContainerView hubContainerView, long durationMs, DoubleConsumer onAlphaChange) { return FadeHubLayoutAnimationFactoryImpl.createFadeInAnimatorProvider( hubContainerView, durationMs, onAlphaChange); } @@ -80,9 +75,7 @@ * @return the requested animator provider. */ public static HubLayoutAnimatorProvider createFadeOutAnimatorProvider( - @NonNull HubContainerView hubContainerView, - long durationMs, - @NonNull DoubleConsumer onAlphaChange) { + HubContainerView hubContainerView, long durationMs, DoubleConsumer onAlphaChange) { return FadeHubLayoutAnimationFactoryImpl.createFadeOutAnimatorProvider( hubContainerView, durationMs, onAlphaChange); }
diff --git a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubLayoutAnimationRunnerFactory.java b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubLayoutAnimationRunnerFactory.java index 6d28b2f..36b72776 100644 --- a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubLayoutAnimationRunnerFactory.java +++ b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubLayoutAnimationRunnerFactory.java
@@ -4,7 +4,10 @@ package org.chromium.chrome.browser.hub; +import org.chromium.build.annotations.NullMarked; + /** Factory for creating {@link HubLayoutAnimationRunner}s. */ +@NullMarked public class HubLayoutAnimationRunnerFactory { /** * Creates a new instance of {@link HubLayoutAnimatorRunner} to run the provided animation.
diff --git a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubManagerFactory.java b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubManagerFactory.java index 1e0bd960..31b4b74 100644 --- a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubManagerFactory.java +++ b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubManagerFactory.java
@@ -6,10 +6,9 @@ import android.app.Activity; -import androidx.annotation.NonNull; - import org.chromium.base.supplier.ObservableSupplier; import org.chromium.base.supplier.OneshotSupplier; +import org.chromium.build.annotations.NullMarked; import org.chromium.chrome.browser.back_press.BackPressManager; import org.chromium.chrome.browser.profiles.ProfileProvider; import org.chromium.chrome.browser.tab.Tab; @@ -20,6 +19,7 @@ import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController; /** Factory for creating {@link HubManager}. */ +@NullMarked public class HubManagerFactory { /** * Creates a new instance of {@link HubManagerImpl}. @@ -38,17 +38,17 @@ * @return an instance of {@link HubManagerImpl}. */ public static HubManager createHubManager( - @NonNull Activity activity, - @NonNull OneshotSupplier<ProfileProvider> profileProviderSupplier, - @NonNull PaneListBuilder paneListBuilder, - @NonNull BackPressManager backPressManager, - @NonNull MenuOrKeyboardActionController menuOrKeyboardActionController, - @NonNull SnackbarManager snackbarManager, - @NonNull ObservableSupplier<Tab> tabSupplier, - @NonNull MenuButtonCoordinator menuButtonCoordinator, - @NonNull HubShowPaneHelper hubShowPaneHelper, - @NonNull ObservableSupplier<EdgeToEdgeController> edgeToEdgeSupplier, - @NonNull SearchActivityClient searchActivityClient) { + Activity activity, + OneshotSupplier<ProfileProvider> profileProviderSupplier, + PaneListBuilder paneListBuilder, + BackPressManager backPressManager, + MenuOrKeyboardActionController menuOrKeyboardActionController, + SnackbarManager snackbarManager, + ObservableSupplier<Tab> tabSupplier, + MenuButtonCoordinator menuButtonCoordinator, + HubShowPaneHelper hubShowPaneHelper, + ObservableSupplier<EdgeToEdgeController> edgeToEdgeSupplier, + SearchActivityClient searchActivityClient) { return new HubManagerImpl( activity, profileProviderSupplier,
diff --git a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/ShrinkExpandHubLayoutAnimationFactory.java b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/ShrinkExpandHubLayoutAnimationFactory.java index b2ae74be..e49bef97 100644 --- a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/ShrinkExpandHubLayoutAnimationFactory.java +++ b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/ShrinkExpandHubLayoutAnimationFactory.java
@@ -5,9 +5,9 @@ package org.chromium.chrome.browser.hub; import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; import org.chromium.base.supplier.SyncOneshotSupplier; +import org.chromium.build.annotations.NullMarked; import java.util.function.DoubleConsumer; @@ -15,6 +15,7 @@ * Factory for creating {@link HubLayoutAnimatorProvider}s for shrink, expand, and new tab * animations. These will fallback to fade animations if dependencies aren't fulfilled in time. */ +@NullMarked public class ShrinkExpandHubLayoutAnimationFactory { /** * Creates an animation to use when creating a non-background new tab from Hub. This animation @@ -29,11 +30,11 @@ * @param onAlphaChange Observer to notify when alpha changes during animations. */ public static HubLayoutAnimatorProvider createNewTabAnimatorProvider( - @NonNull HubContainerView hubContainerView, - @NonNull SyncOneshotSupplier<ShrinkExpandAnimationData> animationDataSupplier, + HubContainerView hubContainerView, + SyncOneshotSupplier<ShrinkExpandAnimationData> animationDataSupplier, @ColorInt int backgroundColor, long durationMs, - @NonNull DoubleConsumer onAlphaChange) { + DoubleConsumer onAlphaChange) { return new ShrinkExpandHubLayoutAnimatorProvider( HubLayoutAnimationType.EXPAND_NEW_TAB, /* needsBitmap= */ false, @@ -56,11 +57,11 @@ * @param onAlphaChange Observer to notify when alpha changes during animations. */ public static HubLayoutAnimatorProvider createShrinkTabAnimatorProvider( - @NonNull HubContainerView hubContainerView, - @NonNull SyncOneshotSupplier<ShrinkExpandAnimationData> animationDataSupplier, + HubContainerView hubContainerView, + SyncOneshotSupplier<ShrinkExpandAnimationData> animationDataSupplier, @ColorInt int backgroundColor, long durationMs, - @NonNull DoubleConsumer onAlphaChange) { + DoubleConsumer onAlphaChange) { return new ShrinkExpandHubLayoutAnimatorProvider( HubLayoutAnimationType.SHRINK_TAB, /* needsBitmap= */ true, @@ -84,11 +85,11 @@ * @param mOnAlphaChange Observer to notify when alpha changes during animations. */ public static HubLayoutAnimatorProvider createExpandTabAnimatorProvider( - @NonNull HubContainerView hubContainerView, - @NonNull SyncOneshotSupplier<ShrinkExpandAnimationData> animationDataSupplier, + HubContainerView hubContainerView, + SyncOneshotSupplier<ShrinkExpandAnimationData> animationDataSupplier, @ColorInt int backgroundColor, long durationMs, - @NonNull DoubleConsumer mOnAlphaChange) { + DoubleConsumer mOnAlphaChange) { return new ShrinkExpandHubLayoutAnimatorProvider( HubLayoutAnimationType.EXPAND_TAB, /* needsBitmap= */ true,
diff --git a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/TranslateHubLayoutAnimationFactory.java b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/TranslateHubLayoutAnimationFactory.java index e698485..9e471e29 100644 --- a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/TranslateHubLayoutAnimationFactory.java +++ b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/TranslateHubLayoutAnimationFactory.java
@@ -4,12 +4,13 @@ package org.chromium.chrome.browser.hub; -import androidx.annotation.NonNull; +import org.chromium.build.annotations.NullMarked; /** * Factory for creating {@link HubLayoutAnimatorProvider}s for translate animations. These * animations are primiarly used for large form factor devices. */ +@NullMarked public class TranslateHubLayoutAnimationFactory { /** @@ -22,8 +23,8 @@ * @return a {@link HubLayoutAnimatorProvider} that provides the animation. */ public static HubLayoutAnimatorProvider createTranslateUpAnimatorProvider( - @NonNull HubContainerView hubContainerView, - @NonNull ScrimController scrimController, + HubContainerView hubContainerView, + ScrimController scrimController, long durationMs, float yOffset) { return TranslateHubLayoutAnimationFactoryImpl.createTranslateUpAnimatorProvider( @@ -40,8 +41,8 @@ * @return a {@link HubLayoutAnimatorProvider} that provides the animation. */ public static HubLayoutAnimatorProvider createTranslateDownAnimatorProvider( - @NonNull HubContainerView hubContainerView, - @NonNull ScrimController scrimController, + HubContainerView hubContainerView, + ScrimController scrimController, long durationMs, float yOffset) { return TranslateHubLayoutAnimationFactoryImpl.createTranslateDownAnimatorProvider(
diff --git a/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.cc index 230bb2f2..b00bce1 100644 --- a/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.cc +++ b/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.cc
@@ -6,6 +6,7 @@ #include <algorithm> +#include "base/check_is_test.h" #include "base/notreached.h" #include "base/trace_event/base_tracing.h" #include "chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.h" @@ -619,10 +620,16 @@ content::NavigationHandle* navigation_handle) { const blink::mojom::LCPCriticalPathPredictorNavigationTimeHintPtr& hint = navigation_handle->GetLCPPNavigationHint(); - if (hint && (!hint->lcp_element_locators.empty() || - !hint->lcp_influencer_scripts.empty() || - !hint->preconnect_origins.empty())) { - is_lcpp_hinted_navigation_ = true; + if (hint) { + if (!hint->lcp_element_locators.empty() || + !hint->lcp_influencer_scripts.empty() || + !hint->preconnect_origins.empty()) { + is_lcpp_hinted_navigation_ = true; + } + if (hint->for_testing) { + CHECK_IS_TEST(); + is_testing_ = true; + } } initiator_origin_ = navigation_handle->GetInitiatorOrigin(); @@ -668,6 +675,20 @@ return STOP_OBSERVING; } +predictors::ResourcePrefetchPredictor* +LcpCriticalPathPredictorPageLoadMetricsObserver::GetPredictor() { + // `loading_predictor` is nullptr in + // `LcpCriticalPathPredictorPageLoadMetricsObserverTest`, or if the profile + // `IsOffTheRecord`. + if (auto* loading_predictor = + predictors::LoadingPredictorFactory::GetForProfile( + Profile::FromBrowserContext( + GetDelegate().GetWebContents()->GetBrowserContext()))) { + return loading_predictor->resource_prefetch_predictor(); + } + return nullptr; +} + void LcpCriticalPathPredictorPageLoadMetricsObserver::FinalizeLCP() { if (!commit_url_) { return; @@ -685,16 +706,7 @@ } // * Finalize the staged LCPP signals to the database. - predictors::ResourcePrefetchPredictor* predictor = nullptr; - // `loading_predictor` is nullptr in - // `LcpCriticalPathPredictorPageLoadMetricsObserverTest`, or if the profile - // `IsOffTheRecord`. - if (auto* loading_predictor = - predictors::LoadingPredictorFactory::GetForProfile( - Profile::FromBrowserContext( - GetDelegate().GetWebContents()->GetBrowserContext()))) { - predictor = loading_predictor->resource_prefetch_predictor(); - } + predictors::ResourcePrefetchPredictor* predictor = GetPredictor(); // Take the learned LCPP here so that we can report it after overwriting it // with the new data below. std::optional<predictors::LcppStat> lcpp_stat_prelearn = @@ -749,17 +761,27 @@ } void LcpCriticalPathPredictorPageLoadMetricsObserver::OnLcpUpdated( - const std::optional<std::string>& lcp_element_locator, - bool is_image_element, - std::optional<uint32_t> predicted_lcp_index) { - if (lcp_element_locator) { + blink::mojom::LcpElementPtr lcp_element) { + if (lcp_element->locator) { if (!lcpp_data_inputs_) { lcpp_data_inputs_.emplace(); } - lcpp_data_inputs_->lcp_element_locator = *lcp_element_locator; + lcpp_data_inputs_->lcp_element_locator = *lcp_element->locator; } - is_lcp_element_image_ = is_image_element; - predicted_lcp_indexes_.push_back(predicted_lcp_index); + is_lcp_element_image_ = lcp_element->is_image; + predicted_lcp_indexes_.push_back(lcp_element->predicted_index); + + if (is_testing_) { + CHECK_IS_TEST(); + GetPredictor()->OnLcpUpdatedForTesting(lcp_element->locator); + } +} + +void LcpCriticalPathPredictorPageLoadMetricsObserver:: + OnLcpTimingPredictedForTesting( + const std::optional<std::string>& element_locator) { + CHECK_IS_TEST(); + GetPredictor()->OnLcpTimingPredictedForTesting(element_locator); } void LcpCriticalPathPredictorPageLoadMetricsObserver::AppendFetchedFontUrl(
diff --git a/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.h index cbb34cfd..c2895f4 100644 --- a/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.h +++ b/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.h
@@ -11,8 +11,13 @@ #include "components/page_load_metrics/browser/page_load_metrics_observer.h" #include "content/public/browser/page_user_data.h" #include "services/network/public/mojom/fetch_api.mojom.h" +#include "third_party/blink/public/mojom/lcp_critical_path_predictor/lcp_critical_path_predictor.mojom.h" #include "url/origin.h" +namespace predictors { +class ResourcePrefetchPredictor; +} // namespace predictors + namespace internal { // Expose metrics for tests. @@ -113,9 +118,7 @@ const LcpCriticalPathPredictorPageLoadMetricsObserver&) = delete; ~LcpCriticalPathPredictorPageLoadMetricsObserver() override; - void OnLcpUpdated(const std::optional<std::string>& lcp_element_locator, - bool is_image_element, - std::optional<uint32_t> predicted_lcp_index); + void OnLcpUpdated(blink::mojom::LcpElementPtr); void SetLcpInfluencerScriptUrls( const std::vector<GURL>& lcp_influencer_scripts); void SetPreconnectOrigins(const std::vector<GURL>& origins); @@ -127,6 +130,9 @@ const base::TimeDelta& subresource_load_start, network::mojom::RequestDestination request_destination); + void OnLcpTimingPredictedForTesting( + const std::optional<std::string>& element_locator); + private: // PageLoadMetricsObserver implementation: ObservePolicy OnStart(content::NavigationHandle* navigation_handle, @@ -143,6 +149,7 @@ PageLoadMetricsObserver::ObservePolicy FlushMetricsOnAppEnterBackground( const page_load_metrics::mojom::PageLoadTiming& timing) override; void RecordTimingHistograms(); + predictors::ResourcePrefetchPredictor* GetPredictor(); void FinalizeLCP(); void OnFirstContentfulPaintInPage( const page_load_metrics::mojom::PageLoadTiming& timing) override; @@ -170,6 +177,8 @@ // std::nullopt value means the LCP didn't hit any of `lcp_element_locators`. std::vector<std::optional<uint32_t>> predicted_lcp_indexes_; + bool is_testing_ = false; + base::WeakPtrFactory<LcpCriticalPathPredictorPageLoadMetricsObserver> weak_factory_{this}; };
diff --git a/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer_unittest.cc index b23d422e..057decaa 100644 --- a/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer_unittest.cc +++ b/chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer_unittest.cc
@@ -104,8 +104,8 @@ const std::string& mock_element_locator = "foo", bool is_image_element = true, std::optional<uint32_t> mock_predicted_index = std::nullopt) { - lcpp_observers_[url]->OnLcpUpdated(mock_element_locator, is_image_element, - mock_predicted_index); + lcpp_observers_[url]->OnLcpUpdated(blink::mojom::LcpElement::New( + mock_element_locator, is_image_element, mock_predicted_index)); } void ExpectNoHistogram(const char* name, @@ -195,7 +195,8 @@ static const uint32_t kNotFound = static_cast<uint32_t>(-1); void TestLCPPrediction(std::vector<uint32_t> predicted_lcp_indexes, - internal::LCPPPredictResult expect) { + internal::LCPPPredictResult expect, + const base::Location& location = FROM_HERE) { const GURL main_frame_url("https://test.example"); // Let predictor learn pseudo("lcp_previous") LCP locator predictors::ResourcePrefetchPredictor* predictor = @@ -217,9 +218,8 @@ index == kNotFound ? std::nullopt : std::optional<uint32_t>(index)); } tester()->NavigateToUntrackedUrl(); - EXPECT_THAT(tester()->histogram_tester().GetAllSamples( - internal::kHistogramLCPPPredictResult), - base::BucketsAre(base::Bucket(expect, 1))); + tester()->histogram_tester().ExpectUniqueSample( + internal::kHistogramLCPPPredictResult, expect, 1, location); } template <class T>
diff --git a/chrome/browser/policy/extension_policy_browsertest.cc b/chrome/browser/policy/extension_policy_browsertest.cc index a2f605d..fa5f7f16 100644 --- a/chrome/browser/policy/extension_policy_browsertest.cc +++ b/chrome/browser/policy/extension_policy_browsertest.cc
@@ -391,7 +391,8 @@ ExtensionInstallBlocklistComponentApps) { // Load all component extensions. extensions::ComponentLoader::EnableBackgroundExtensionsForTesting(); - extension_service()->component_loader()->AddDefaultComponentExtensions(false); + auto* loader = extensions::ComponentLoader::Get(browser()->profile()); + loader->AddDefaultComponentExtensions(false); base::RunLoop().RunUntilIdle(); extensions::ExtensionRegistry* registry = extension_registry();
diff --git a/chrome/browser/policy/test/system_features_policy_browsertest.cc b/chrome/browser/policy/test/system_features_policy_browsertest.cc index f8b792f0..900b0e2 100644 --- a/chrome/browser/policy/test/system_features_policy_browsertest.cc +++ b/chrome/browser/policy/test/system_features_policy_browsertest.cc
@@ -14,7 +14,6 @@ #include "chrome/browser/ash/guest_os/guest_os_terminal.h" #include "chrome/browser/ash/system_web_apps/system_web_app_manager.h" #include "chrome/browser/extensions/component_loader.h" -#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/policy/policy_test_utils.h" #include "chrome/browser/policy/system_features_disable_list_policy_handler.h" #include "chrome/browser/profiles/profile.h" @@ -31,7 +30,7 @@ #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_navigation_observer.h" -#include "extensions/browser/extension_system.h" +#include "extensions/browser/extension_registry.h" #include "extensions/common/constants.h" #include "ui/base/l10n/l10n_util.h" @@ -77,10 +76,8 @@ void EnableExtensions(bool skip_session_components) { auto* profile = browser()->profile(); extensions::ComponentLoader::EnableBackgroundExtensionsForTesting(); - extensions::ExtensionSystem::Get(profile) - ->extension_service() - ->component_loader() - ->AddDefaultComponentExtensions(skip_session_components); + extensions::ComponentLoader::Get(profile)->AddDefaultComponentExtensions( + skip_session_components); base::RunLoop().RunUntilIdle(); }
diff --git a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.cc b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.cc index 2eb7321..9f7a603 100644 --- a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.cc +++ b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.cc
@@ -53,12 +53,16 @@ } void LCPCriticalPathPredictorHost::OnLcpUpdated( - const std::optional<std::string>& lcp_element_locator, - bool is_image_element, - std::optional<uint32_t> predicted_lcp_index) { + blink::mojom::LcpElementPtr lcp_element) { if (auto* plmo = GetLcpCriticalPathPredictorPageLoadMetricsObserver()) { - plmo->OnLcpUpdated(lcp_element_locator, is_image_element, - predicted_lcp_index); + plmo->OnLcpUpdated(std::move(lcp_element)); + } +} + +void LCPCriticalPathPredictorHost::OnLcpTimingPredictedForTesting( + const std::optional<std::string>& element_locator) { + if (auto* plmo = GetLcpCriticalPathPredictorPageLoadMetricsObserver()) { + plmo->OnLcpTimingPredictedForTesting(element_locator); } }
diff --git a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.h b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.h index fd30e9c..36bf9751 100644 --- a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.h +++ b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.h
@@ -44,9 +44,9 @@ GetLcpCriticalPathPredictorPageLoadMetricsObserver() const; // Implements blink::mojom::LCPCriticalPathPredictorHost. - void OnLcpUpdated(const std::optional<std::string>& lcp_element_locator, - bool is_image_element, - std::optional<uint32_t> predicted_lcp_index) override; + void OnLcpUpdated(blink::mojom::LcpElementPtr) override; + void OnLcpTimingPredictedForTesting( + const std::optional<std::string>& element_locator) override; void SetLcpInfluencerScriptUrls( const std::vector<GURL>& lcp_influencer_scripts) override; void SetPreconnectOrigins(const std::vector<GURL>& origins) override;
diff --git a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc index 1d61dd4b..bc234ac 100644 --- a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc +++ b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc
@@ -810,7 +810,7 @@ return blink::mojom::LCPCriticalPathPredictorNavigationTimeHint( std::move(lcp_element_locators), std::move(lcp_influencer_scripts), std::move(fetched_fonts), std::move(preconnect_origins), - std::move(unused_preloads)); + std::move(unused_preloads), false); } return std::nullopt; }
diff --git a/chrome/browser/predictors/loading_predictor.h b/chrome/browser/predictors/loading_predictor.h index 36086771..379fa81 100644 --- a/chrome/browser/predictors/loading_predictor.h +++ b/chrome/browser/predictors/loading_predictor.h
@@ -145,6 +145,8 @@ void MaybePrewarmResources(const std::optional<url::Origin>& initiator_origin, const GURL& top_frame_main_resource_url); + bool IsLCPPTestingEnabled() const { return is_lcpp_testing_enabled_; } + private: // Stores the information necessary to keep track of the active navigations. struct NavigationInfo { @@ -199,6 +201,9 @@ loading_data_collector_ = std::move(loading_data_collector); } + // For LCPP testing. + void EnableLCPPTesting() { is_lcpp_testing_enabled_ = true; } + LoadingPredictorConfig config_; raw_ptr<Profile, DanglingUntriaged> profile_; std::unique_ptr<ResourcePrefetchPredictor> resource_prefetch_predictor_; @@ -219,10 +224,13 @@ PreconnectData new_tab_page_preconnect_data_; + bool is_lcpp_testing_enabled_ = false; + friend class LoadingPredictorTest; friend class LoadingPredictorPreconnectTest; friend class LoadingPredictorTabHelperTest; friend class LoadingPredictorTabHelperTestCollectorTest; + friend class LCPPTimingPredictorTestBase; FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest, TestMainFrameResponseCancelsHint); FRIEND_TEST_ALL_PREFIXES(LoadingPredictorTest,
diff --git a/chrome/browser/predictors/loading_predictor_browsertest.cc b/chrome/browser/predictors/loading_predictor_browsertest.cc index a93e14b..966c9131 100644 --- a/chrome/browser/predictors/loading_predictor_browsertest.cc +++ b/chrome/browser/predictors/loading_predictor_browsertest.cc
@@ -163,6 +163,49 @@ base::RunLoop run_loop_; }; +class LcpUpdatedWaiter : public TestObserver { + public: + explicit LcpUpdatedWaiter(ResourcePrefetchPredictor* predictor, + size_t wait_lcp_count) + : TestObserver(predictor), wait_lcp_count_(wait_lcp_count) {} + const std::vector<std::optional<std::string>>& Wait() { + run_loop_.Run(); + return element_locators_; + } + + private: + void OnLcpUpdated( + const std::optional<std::string>& element_locator) override { + element_locators_.push_back(element_locator); + if (++lcp_count_ >= wait_lcp_count_) { + run_loop_.Quit(); + } + } + base::RunLoop run_loop_; + const size_t wait_lcp_count_; + size_t lcp_count_ = 0u; + std::vector<std::optional<std::string>> element_locators_; +}; + +class LcpTimingPredictedWaiter : public TestObserver { + public: + explicit LcpTimingPredictedWaiter(ResourcePrefetchPredictor* predictor) + : TestObserver(predictor) {} + std::optional<std::string>& Wait() { + run_loop_.Run(); + return lcp_element_locator_; + } + + private: + void OnLcpTimingPredicted( + const std::optional<std::string>& lcp_element_locator) override { + lcp_element_locator_ = lcp_element_locator; + run_loop_.Quit(); + } + base::RunLoop run_loop_; + std::optional<std::string> lcp_element_locator_; +}; + class TestPreconnectManagerObserver : public PreconnectManager::Observer { public: explicit TestPreconnectManagerObserver( @@ -1176,6 +1219,122 @@ ::internal::kHistogramLCPPSubresourceCountSameSiteRatio, 100, 1); } +class LCPPTimingPredictorTestBase : public InProcessBrowserTest { + public: + ~LCPPTimingPredictorTestBase() override = default; + + void SetUp() override { + embedded_test_server()->RegisterRequestHandler(base::BindRepeating( + &LCPPTimingPredictorTestBase::RequestHandler, base::Unretained(this))); + + ASSERT_TRUE(embedded_test_server()->InitializeAndListen()); + InProcessBrowserTest::SetUp(); + } + void SetUpOnMainThread() override { + host_resolver()->AddRule("*", "127.0.0.1"); + embedded_test_server()->StartAcceptingConnections(); + + loading_predictor_ = + LoadingPredictorFactory::GetForProfile(browser()->profile()); + ASSERT_TRUE(loading_predictor_); + loading_predictor_->EnableLCPPTesting(); + PredictorInitializer initializer( + loading_predictor_->resource_prefetch_predictor()); + initializer.EnsurePredictorInitialized(); + } + void TearDownOnMainThread() override { loading_predictor_ = nullptr; } + + void NavigateAndWaitForLcpElement( + const GURL& url, + const std::string& expected_events, + size_t expected_lcp_count, + const std::optional<size_t>& expected_lcp_index, + const base::Location& from_here = FROM_HERE) { + LcpTimingPredictedWaiter timing_predicted_waiter( + loading_predictor()->resource_prefetch_predictor()); + LcpUpdatedWaiter lcp_updated_waiter( + loading_predictor()->resource_prefetch_predictor(), expected_lcp_count); + EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); + const std::vector<std::optional<std::string>>& lcp_element_locators = + lcp_updated_waiter.Wait(); + const std::optional<std::string>& predicted_lcp_locator = + timing_predicted_waiter.Wait(); + EXPECT_EQ(predicted_lcp_locator.has_value(), + expected_lcp_index.has_value()); + if (expected_lcp_index) { + EXPECT_EQ(*lcp_element_locators[*expected_lcp_index], + *predicted_lcp_locator); + } + + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + EXPECT_EQ(expected_events, content::EvalJs(web_contents, R"( + globalThis.events.join(", ") + )")); + + LcpElementLearnWaiter lcp_element_waiter( + loading_predictor()->resource_prefetch_predictor()); + EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank"))) + << from_here.ToString(); + lcp_element_waiter.Wait(); + } + + LoadingPredictor* loading_predictor() { return loading_predictor_; } + + private: + std::unique_ptr<net::test_server::HttpResponse> RequestHandler( + const net::test_server::HttpRequest& request) { + if (request.GetURL().path() == "/slow-empty.js") { + std::unique_ptr<net::test_server::DelayedHttpResponse> resp = + std::make_unique<net::test_server::DelayedHttpResponse>( + base::Milliseconds(1000)); + resp->set_code(net::HTTP_OK); + resp->AddCustomHeader("Cache-Control", "no-store"); + resp->set_content_type("application/javascript"); + resp->set_content(std::string()); + return resp; + } + + return nullptr; + } + raw_ptr<LoadingPredictor> loading_predictor_ = nullptr; +}; + +class LCPPTimingPredictorBrowserTest : public LCPPTimingPredictorTestBase { + public: + LCPPTimingPredictorBrowserTest() { + scoped_feature_list_.InitWithFeatures( + /*enabled_features=*/ + {blink::features::kLCPTimingPredictorPrerender2}, + /*disabled_features=*/{}); + } + + void TestPrediction(const base::Location& from_here = FROM_HERE) { + const GURL kUrl = embedded_test_server()->GetURL( + "a.test", "/predictors/lcp_occur_twice.html"); + + NavigateAndWaitForLcpElement(kUrl, + /*expected_events=*/"LCP@IMG, LCP@DIV, Onload", + /*expected_lcp_count=*/2u, + /*expected_lcp_index=*/std::nullopt, + from_here); + NavigateAndWaitForLcpElement(kUrl, + /*expected_events=*/"LCP@IMG, LCP@DIV, Onload", + /*expected_lcp_count=*/2u, + /*expected_lcp_index=*/0u, // =IMG + from_here); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +// Confirm image element of the first LCP is predicted rather than div, or the +// actual LCP(current implementation) +IN_PROC_BROWSER_TEST_F(LCPPTimingPredictorBrowserTest, Base) { + TestPrediction(); +} + class SuppressesLoadingPredictorOnSlowNetworkBrowserTest : public LoadingPredictorBrowserTest { public:
diff --git a/chrome/browser/predictors/loading_predictor_tab_helper.cc b/chrome/browser/predictors/loading_predictor_tab_helper.cc index 909dd71b..3d59544 100644 --- a/chrome/browser/predictors/loading_predictor_tab_helper.cc +++ b/chrome/browser/predictors/loading_predictor_tab_helper.cc
@@ -8,6 +8,7 @@ #include <set> #include <string> +#include "base/check_is_test.h" #include "base/command_line.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" @@ -171,6 +172,28 @@ kMaxValue = kConversionFailure, }; +std::optional<blink::mojom::LCPCriticalPathPredictorNavigationTimeHint> +GetLCPPHint(content::NavigationHandle& navigation_handle, + LoadingPredictor& predictor) { + std::optional<LcppStat> lcpp_stat = + predictor.resource_prefetch_predictor()->GetLcppStat( + navigation_handle.GetInitiatorOrigin(), navigation_handle.GetURL()); + if (!lcpp_stat) { + base::UmaHistogramEnumeration( + "LoadingPredictor.SetLCPPNavigationHint.Status", + LcppHintStatus::kNoLcppData); + return std::nullopt; + } + if (!IsValidLcppStat(*lcpp_stat)) { + base::UmaHistogramEnumeration( + "LoadingPredictor.SetLCPPNavigationHint.Status", + LcppHintStatus::kInvalidLcppStat); + return std::nullopt; + } + + return ConvertLcppStatToLCPCriticalPathPredictorNavigationTimeHint( + *lcpp_stat); +} // Attach LCP Critical Path Predictor hint to NavigationHandle, so that it // would be sent to the renderer process upon navigation commit. void MaybeSetLCPPNavigationHint(content::NavigationHandle& navigation_handle, @@ -185,23 +208,17 @@ if (!navigation_url.is_valid() || !navigation_url.SchemeIsHTTPOrHTTPS()) { return; } - std::optional<LcppStat> lcpp_stat = - predictor.resource_prefetch_predictor()->GetLcppStat( - navigation_handle.GetInitiatorOrigin(), navigation_url); - if (!lcpp_stat) { - base::UmaHistogramEnumeration( - "LoadingPredictor.SetLCPPNavigationHint.Status", - LcppHintStatus::kNoLcppData); - return; - } - if (!IsValidLcppStat(*lcpp_stat)) { - base::UmaHistogramEnumeration( - "LoadingPredictor.SetLCPPNavigationHint.Status", - LcppHintStatus::kInvalidLcppStat); - return; - } + std::optional<blink::mojom::LCPCriticalPathPredictorNavigationTimeHint> hint = - ConvertLcppStatToLCPCriticalPathPredictorNavigationTimeHint(*lcpp_stat); + GetLCPPHint(navigation_handle, predictor); + if (predictor.IsLCPPTestingEnabled()) { + CHECK_IS_TEST(); + if (!hint) { + hint = blink::mojom::LCPCriticalPathPredictorNavigationTimeHint( + {}, {}, {}, {}, {}, /*for_testing=*/false); + } + hint->for_testing = true; + } if (hint) { navigation_handle.SetLCPPNavigationHint(*hint); base::UmaHistogramEnumeration(
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.cc b/chrome/browser/predictors/resource_prefetch_predictor.cc index 0fdcf2d..0ee699e 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor.cc +++ b/chrome/browser/predictors/resource_prefetch_predictor.cc
@@ -250,7 +250,6 @@ const LoadingPredictorConfig& config, Profile* profile) : profile_(profile), - observer_(nullptr), config_(config), initialization_state_(NOT_INITIALIZED), tables_(PredictorDatabaseFactory::GetForProfile(profile) @@ -297,8 +296,12 @@ return PredictPreconnectOrigins(main_frame_url, nullptr); } -void ResourcePrefetchPredictor::SetObserverForTesting(TestObserver* observer) { - observer_ = observer; +void ResourcePrefetchPredictor::AddObserverForTesting(TestObserver* observer) { + test_observer_set_.insert(observer); +} +void ResourcePrefetchPredictor::RemoveObserverForTesting( + TestObserver* observer) { + test_observer_set_.erase(observer); } void ResourcePrefetchPredictor::Shutdown() { @@ -336,8 +339,9 @@ summary.main_frame_url.DeprecatedGetOriginAsURL(), summary.origins); - if (observer_) - observer_->OnNavigationLearned(summary); + for (auto observer : test_observer_set_) { + observer->OnNavigationLearned(summary); + } } bool ResourcePrefetchPredictor::PredictPreconnectOrigins( @@ -424,8 +428,9 @@ DeleteAllUrls(); delete_all_data_requested_ = false; } - if (observer_) - observer_->OnPredictorInitialized(); + for (auto observer : test_observer_set_) { + observer->OnPredictorInitialized(); + } } void ResourcePrefetchPredictor::DeleteAllUrls() { @@ -635,8 +640,10 @@ } const bool data_updated = lcpp_data_->LearnLcpp(initiator_origin, url, inputs); - if (data_updated && observer_) { - observer_->OnLcppLearned(); + if (data_updated) { + for (auto observer : test_observer_set_) { + observer->OnLcppLearned(); + } } } @@ -653,6 +660,20 @@ return lcpp_data_->GetLcppStat(initiator_origin, url); } +void ResourcePrefetchPredictor::OnLcpUpdatedForTesting( + const std::optional<std::string>& element_locator) { + for (auto observer : test_observer_set_) { + observer->OnLcpUpdated(element_locator); + } +} + +void ResourcePrefetchPredictor::OnLcpTimingPredictedForTesting( + const std::optional<std::string>& element_locator) { + for (auto observer : test_observer_set_) { + observer->OnLcpTimingPredicted(element_locator); + } +} + void ResourcePrefetchPredictor::GetPreconnectAndPrefetchRequest( const std::optional<url::Origin>& initiator_origin, const GURL& url, @@ -710,12 +731,12 @@ // TestObserver. TestObserver::~TestObserver() { - predictor_->SetObserverForTesting(nullptr); + predictor_->RemoveObserverForTesting(this); } TestObserver::TestObserver(ResourcePrefetchPredictor* predictor) : predictor_(predictor) { - predictor_->SetObserverForTesting(this); + predictor_->AddObserverForTesting(this); } } // namespace predictors
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.h b/chrome/browser/predictors/resource_prefetch_predictor.h index e2c32e5..8a0c427 100644 --- a/chrome/browser/predictors/resource_prefetch_predictor.h +++ b/chrome/browser/predictors/resource_prefetch_predictor.h
@@ -181,11 +181,6 @@ // Returns true if preconnect data exists for the |main_frame_url|. virtual bool IsUrlPreconnectable(const GURL& main_frame_url) const; - // Sets the |observer| to be notified when the resource prefetch predictor - // data changes. Previously registered observer will be discarded. Call - // this with nullptr parameter to de-register observer. - void SetObserverForTesting(TestObserver* observer); - // Returns true iff there is OriginData that can be used for a |url| and fills // |prediction| with origins and hosts that need to be preconnected and // preresolved respectively. |prediction| pointer may be nullptr to get return @@ -197,6 +192,11 @@ // assembled a PageRequestSummary. virtual void RecordPageRequestSummary(const PageRequestSummary& summary); + // Add/remove the |observer| to be notified when the resource prefetch + // predictor + void AddObserverForTesting(TestObserver* observer); + void RemoveObserverForTesting(TestObserver* observer); + // Record LCP element locators after a page has finished loading and LCP has // been determined. void LearnLcpp(const std::optional<url::Origin>& initiator_origin, @@ -211,6 +211,11 @@ const std::optional<url::Origin>& initiator_origin, const GURL& url) const; + void OnLcpUpdatedForTesting( + const std::optional<std::string>& element_locator); + void OnLcpTimingPredictedForTesting( + const std::optional<std::string>& element_locator); + void GetPreconnectAndPrefetchRequest( const std::optional<url::Origin>& initiator_origin, const GURL& url, @@ -334,7 +339,7 @@ } const raw_ptr<Profile, DanglingUntriaged> profile_; - raw_ptr<TestObserver> observer_; + std::set<raw_ptr<TestObserver>> test_observer_set_; const LoadingPredictorConfig config_; InitializationState initialization_state_; scoped_refptr<ResourcePrefetchPredictorTables> tables_; @@ -372,6 +377,12 @@ virtual void OnLcppLearned() {} + virtual void OnLcpUpdated(const std::optional<std::string>& element_locator) { + } + + virtual void OnLcpTimingPredicted( + const std::optional<std::string>& element_locator) {} + protected: // |predictor| must be non-NULL and has to outlive the TestObserver. // Also the predictor must not have a TestObserver set.
diff --git a/chrome/browser/profiles/profile_keyed_service_browsertest.cc b/chrome/browser/profiles/profile_keyed_service_browsertest.cc index 962e780a..a6183d9 100644 --- a/chrome/browser/profiles/profile_keyed_service_browsertest.cc +++ b/chrome/browser/profiles/profile_keyed_service_browsertest.cc
@@ -774,6 +774,7 @@ #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) "ServerCertificateDatabaseService", #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + "ServiceWorkerTaskQueue", "SessionDataService", "SessionProtoDBFactory", "SessionsAPI",
diff --git a/chrome/browser/resources/glic/fre/fre_app_controller.ts b/chrome/browser/resources/glic/fre/fre_app_controller.ts index 51a67f78..2fd15a5 100644 --- a/chrome/browser/resources/glic/fre/fre_app_controller.ts +++ b/chrome/browser/resources/glic/fre/fre_app_controller.ts
@@ -267,11 +267,22 @@ }, MAX_WAIT_TIME_MS - MIN_HOLD_LOADING_TIME_MS); } + onSizeChanged(e: any): void { + window.resizeTo(e.newWidth, e.newHeight); + } + private createWebview(): chrome.webviewTag.WebView { const webview = document.createElement('webview') as chrome.webviewTag.WebView; webview.id = 'freGuestFrame'; + // TODO(crbug.com/408475473): Update the webviewTag definition to be able to + // define properties rather than using setAttribute. webview.setAttribute('partition', 'glicfrepart'); + webview.setAttribute('autosize', 'true'); + webview.setAttribute('minheight', window.outerHeight.toString()); + webview.setAttribute('minwidth', window.outerWidth.toString()); + webview.setAttribute('maxwidth', window.outerWidth.toString()); + webview.setAttribute('maxheight', window.screen.availHeight.toString()); $.webviewContainer.appendChild(webview); this.webviewEventTracker.add( @@ -280,6 +291,8 @@ webview, 'contentload', this.onContentLoad.bind(this)); this.webviewEventTracker.add( webview, 'newwindow', this.onNewWindow.bind(this)); + this.webviewEventTracker.add( + webview, 'sizechanged', this.onSizeChanged.bind(this)); return webview; }
diff --git a/chrome/browser/speech/extension_api/tts_engine_extension_api_chromeos.cc b/chrome/browser/speech/extension_api/tts_engine_extension_api_chromeos.cc index 9188e93..9f881d7 100644 --- a/chrome/browser/speech/extension_api/tts_engine_extension_api_chromeos.cc +++ b/chrome/browser/speech/extension_api/tts_engine_extension_api_chromeos.cc
@@ -7,14 +7,12 @@ #include "base/no_destructor.h" #include "base/system/sys_info.h" #include "chrome/browser/extensions/component_loader.h" -#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/speech/extension_api/tts_engine_extension_observer_chromeos.h" #include "chrome/browser/speech/extension_api/tts_engine_extension_observer_chromeos_factory.h" #include "chrome/browser/speech/extension_api/tts_extension_api_constants.h" #include "chrome/common/extensions/api/speech/tts_engine_manifest_handler.h" #include "chrome/common/extensions/extension_constants.h" -#include "extensions/browser/extension_system.h" #include "extensions/common/extension.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "ui/base/l10n/l10n_util.h" @@ -117,13 +115,9 @@ if (disable_built_in_tts_engine_for_testing_) return; - Profile* profile = Profile::FromBrowserContext(browser_context); - // Load the component extensions into this profile. - extensions::ExtensionService* extension_service = - extensions::ExtensionSystem::Get(profile)->extension_service(); - DCHECK(extension_service); - extension_service->component_loader()->AddChromeOsSpeechSynthesisExtensions(); + extensions::ComponentLoader::Get(browser_context) + ->AddChromeOsSpeechSynthesisExtensions(); } bool TtsExtensionEngineChromeOS::IsBuiltInTtsEngineInitialized(
diff --git a/chrome/browser/speech/extension_api/tts_extension_apitest.cc b/chrome/browser/speech/extension_api/tts_extension_apitest.cc index 45feaa7a..cf9f7d3b 100644 --- a/chrome/browser/speech/extension_api/tts_extension_apitest.cc +++ b/chrome/browser/speech/extension_api/tts_extension_apitest.cc
@@ -335,9 +335,8 @@ } void AddNetworkSpeechSynthesisExtension() { - ExtensionService* service = - extensions::ExtensionSystem::Get(profile())->extension_service(); - service->component_loader()->AddNetworkSpeechSynthesisExtension(); + auto* component_loader = extensions::ComponentLoader::Get(profile()); + component_loader->AddNetworkSpeechSynthesisExtension(); // Wait for any tts engine event listener to be added by the network tts // engine so that tests can be ready to validate state.
diff --git a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/LocalTabGroupMutationHelper.java b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/LocalTabGroupMutationHelper.java index 47ab5f88..4f1725c4 100644 --- a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/LocalTabGroupMutationHelper.java +++ b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/LocalTabGroupMutationHelper.java
@@ -106,6 +106,9 @@ mTabGroupSyncService.updateLocalTabId( localTabGroupId, syncTabId, tabIdMappings.get(syncTabId)); } + + // On any tab group open through the sync service, reset the archival timestamp and status. + mTabGroupSyncService.updateArchivalStatus(tabGroup.syncId, /* archivalStatus= */ false); } /**
diff --git a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/LocalTabGroupMutationHelperUnitTest.java b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/LocalTabGroupMutationHelperUnitTest.java index 3effc3b6b..4ed9e02 100644 --- a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/LocalTabGroupMutationHelperUnitTest.java +++ b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/LocalTabGroupMutationHelperUnitTest.java
@@ -157,6 +157,7 @@ verify(mTabGroupSyncService) .updateLocalTabGroupMapping(any(), any(), eq(OpeningSource.AUTO_OPENED_FROM_SYNC)); verify(mTabGroupSyncService, times(2)).updateLocalTabId(any(), any(), anyInt()); + verify(mTabGroupSyncService).updateArchivalStatus(savedTabGroup.syncId, false); } @Test @@ -171,6 +172,7 @@ verify(mTabGroupSyncService) .updateLocalTabGroupMapping(any(), any(), eq(OpeningSource.OPENED_FROM_REVISIT_UI)); verify(mTabGroupSyncService, times(1)).updateLocalTabId(any(), any(), anyInt()); + verify(mTabGroupSyncService).updateArchivalStatus(savedTabGroup.syncId, false); } @Test
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/InvalidationAwareThumbnailProvider.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/InvalidationAwareThumbnailProvider.java index b3b7799..7fb0608 100644 --- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/InvalidationAwareThumbnailProvider.java +++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/InvalidationAwareThumbnailProvider.java
@@ -6,15 +6,18 @@ import android.graphics.Canvas; +import org.chromium.build.annotations.NullMarked; + /** * A thumbnail provider that is aware of the visual state of the thumbnail and only requests * thumbnail capturing if that has changed since the last capture. */ +@NullMarked public interface InvalidationAwareThumbnailProvider { /** - * @return Whether a thumbnail should be captured for this provider. Should only happen if - * the contents have changed since the last thumbnail capture. + * Returns whether a thumbnail should be captured for this provider. Should only happen if the + * contents have changed since the last thumbnail capture. */ boolean shouldCaptureThumbnail();
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/OnTabSelectingListener.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/OnTabSelectingListener.java index 760bc11..925fe847 100644 --- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/OnTabSelectingListener.java +++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/OnTabSelectingListener.java
@@ -4,9 +4,11 @@ package org.chromium.chrome.browser.tab_ui; +import org.chromium.build.annotations.NullMarked; import org.chromium.chrome.browser.tab.Tab; /** Interface for selecting a tab. */ +@NullMarked public interface OnTabSelectingListener { /** * Called when a tab is getting selected. This will select the tab and exit the layout.
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/RecyclerViewPosition.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/RecyclerViewPosition.java index 30719ea..9091798e 100644 --- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/RecyclerViewPosition.java +++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/RecyclerViewPosition.java
@@ -4,7 +4,10 @@ package org.chromium.chrome.browser.tab_ui; +import org.chromium.build.annotations.NullMarked; + /** A structure for holding the a recycler view position and offset. */ +@NullMarked public class RecyclerViewPosition { private int mPosition; private int mOffset;
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabContentManager.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabContentManager.java index 542e104..3b3d885d 100644 --- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabContentManager.java +++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabContentManager.java
@@ -17,8 +17,6 @@ import android.view.ViewGroup.MarginLayoutParams; import androidx.annotation.IntDef; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import org.jni_zero.CalledByNative; @@ -32,6 +30,8 @@ import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.task.PostTask; import org.chromium.base.task.TaskTraits; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider; import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.tab.Tab; @@ -53,6 +53,7 @@ * could be live or static thumbnails. */ @JNINamespace("android") +@NullMarked public class TabContentManager { private static final int WAIT_FOR_NATIVE_BACKOFF_MS = 50; private static final int WAIT_FOR_NATIVE_MAX_BACKOFF_ATTEMPTS = 2; @@ -203,7 +204,7 @@ } @CalledByNative - private Tab getTabById(int tabId) { + private @Nullable Tab getTabById(int tabId) { if (mTabFinder == null) return null; return mTabFinder.getTabById(tabId); @@ -232,7 +233,7 @@ mListeners.remove(listener); } - private Bitmap readbackNativeBitmap(final Tab tab, float scale) { + private @Nullable Bitmap readbackNativeBitmap(final Tab tab, float scale) { NativePage nativePage = tab.getNativePage(); boolean isNativeViewShowing = isNativeViewShowing(tab); if (nativePage == null && !isNativeViewShowing) { @@ -258,7 +259,8 @@ return readbackNativeView(viewToDraw, scale, nativePage); } - private Bitmap readbackNativeView(View viewToDraw, float scale, NativePage nativePage) { + private @Nullable Bitmap readbackNativeView( + View viewToDraw, float scale, NativePage nativePage) { Bitmap bitmap; float overlayTranslateY = mBrowserControlsStateProvider.getTopVisibleContentOffset(); @@ -304,7 +306,7 @@ * @param tabId The ID of the tab to get the thumbnail for. * @param callback The callback to send the {@link Bitmap} with. */ - public void getEtc1TabThumbnailWithCallback(int tabId, @NonNull Callback<Bitmap> callback) { + public void getEtc1TabThumbnailWithCallback(int tabId, Callback<@Nullable Bitmap> callback) { if (!mSnapshotsEnabled || mNativeTabContentManager == 0) { callback.onResult(null); return; @@ -324,7 +326,7 @@ * @param callback The callback to send the {@link Bitmap} with. */ public void getTabThumbnailWithCallback( - int tabId, @NonNull Size thumbnailSize, @NonNull Callback<Bitmap> callback) { + int tabId, Size thumbnailSize, Callback<@Nullable Bitmap> callback) { if (!mSnapshotsEnabled) { callback.onResult(null); return; @@ -350,7 +352,7 @@ } @VisibleForTesting - public static Bitmap getJpegForTab(int tabId, @NonNull Size thumbnailSize) { + public static @Nullable Bitmap getJpegForTab(int tabId, Size thumbnailSize) { File file = getTabThumbnailFileJpeg(tabId); if (!file.isFile()) return null; if (thumbnailSize.getWidth() <= 0 || thumbnailSize.getHeight() <= 0) { @@ -394,7 +396,7 @@ } private void getTabThumbnailFromDisk( - int tabId, @NonNull Size thumbnailSize, @NonNull Callback<Bitmap> callback) { + int tabId, Size thumbnailSize, Callback<@Nullable Bitmap> callback) { // Get the JPEG once it is ready if a capture is ongoing. if (mNativeTabContentManager != 0) { TraceEvent.startAsync("GetTabThumbnailFromDiskJpegAwait", tabId); @@ -413,17 +415,15 @@ /** * Read the JPEG in java and report back with refetch. + * * @param tabId The Tab ID to wait for a JPEG of. * @param thumbnailSize The size of thumbnail that will be shown. * @param attempts The number of pre-native refetch attempts. * @param callback The callback to execute once native has finished any pending JPEG capture - * tasks for the tab. + * tasks for the tab. */ private void getJpegForTabWithRefetch( - int tabId, - @NonNull Size thumbnailSize, - int attempts, - @NonNull Callback<Bitmap> callback) { + int tabId, Size thumbnailSize, int attempts, Callback<@Nullable Bitmap> callback) { // Try JPEG thumbnail with backoff while pre-native. TraceEvent.startAsync("GetTabThumbnailFromDisk", tabId); PostTask.postDelayedTask( @@ -439,13 +439,14 @@ /** * Read the JPEG in java and report back without refetch. + * * @param tabId The Tab ID to wait for a JPEG of. * @param thumbnailSize The size of thumbnail that will be shown. * @param callback The callback to execute once native has finished any pending JPEG capture - * tasks for the tab. + * tasks for the tab. */ private void getJpegForTabNoRefetch( - int tabId, @NonNull Size thumbnailSize, @NonNull Callback<Bitmap> callback) { + int tabId, Size thumbnailSize, Callback<@Nullable Bitmap> callback) { PostTask.postTask( TaskTraits.USER_VISIBLE_MAY_BLOCK, () -> { @@ -467,13 +468,13 @@ /** * Wait for the JPEG in native by using the capture progress tracker. Once available execute the * callback. + * * @param tabId The Tab ID to wait for a JPEG of. * @param thumbnailSize The size of thumbnail that will be shown. * @param callback The callback to execute once native has finished any pending JPEG capture - * tasks for the tab. + * tasks for the tab. */ - private void fetchJpeg( - int tabId, @NonNull Size thumbnailSize, @NonNull Callback<Bitmap> callback) { + private void fetchJpeg(int tabId, Size thumbnailSize, Callback<@Nullable Bitmap> callback) { if (!mSnapshotsEnabled) { callback.onResult(null); return; @@ -500,10 +501,10 @@ private void onBitmapRead( int tabId, - @NonNull Size thumbnailSize, + Size thumbnailSize, int attempts, - Bitmap jpeg, - @NonNull Callback<Bitmap> callback) { + @Nullable Bitmap jpeg, + Callback<@Nullable Bitmap> callback) { TraceEvent.finishAsync("GetTabThumbnailFromDisk", tabId); if (jpeg != null) { recordThumbnailFetchingResult(ThumbnailFetchingResult.GOT_JPEG); @@ -531,28 +532,30 @@ /** * Cache the content of a tab as a thumbnail. + * * @param tab The tab whose content we will cache. */ - public void cacheTabThumbnail(@NonNull final Tab tab) { + public void cacheTabThumbnail(final Tab tab) { cacheTabThumbnailWithCallback(tab, /* returnBitmap= */ false, null); } /** * Cache the content of a tab as a thumbnail and call the {@code callback} when finished. + * * @param tab The tab whose content we will cache. * @param returnBitmap Whether to return a bitmap to the callback. Setting to false avoids an - * expensive bitmap copy if not required. + * expensive bitmap copy if not required. * @param callback Called when the caching is finished. The bitmap argument may be null if - * unsuccessful or {@code returnBitmap} is false. + * unsuccessful or {@code returnBitmap} is false. */ public void cacheTabThumbnailWithCallback( - @NonNull final Tab tab, boolean returnBitmap, Callback<Bitmap> callback) { + final Tab tab, boolean returnBitmap, @Nullable Callback<@Nullable Bitmap> callback) { if (mNativeTabContentManager == 0 || !mSnapshotsEnabled) return; captureThumbnail(tab, returnBitmap, callback); } - private Bitmap cacheNativeTabThumbnail(final Tab tab) { + private @Nullable Bitmap cacheNativeTabThumbnail(final Tab tab) { assert tab.getNativePage() != null || isNativeViewShowing(tab); Bitmap nativeBitmap = readbackNativeBitmap(tab, mThumbnailScale); @@ -570,7 +573,7 @@ * @param callback The callback to send the {@link Bitmap} with. */ private void captureThumbnail( - @NonNull final Tab tab, boolean returnBitmap, @Nullable Callback<Bitmap> callback) { + final Tab tab, boolean returnBitmap, @Nullable Callback<@Nullable Bitmap> callback) { assert mNativeTabContentManager != 0; assert mSnapshotsEnabled; @@ -609,7 +612,7 @@ Callback.runNullSafe(callback, null); return; } - Callback<Bitmap> wrappedCallback = + Callback<@Nullable Bitmap> wrappedCallback = (Bitmap bitmap) -> { if (bitmap != null) { long durationMs = SystemClock.elapsedRealtime() - startTime; @@ -715,7 +718,7 @@ Object tab, float thumbnailScale, boolean returnBitmap, - Callback<Bitmap> callback); + Callback<@Nullable Bitmap> callback); void cacheTabWithBitmap( long nativeTabContentManager, Object tab, Object bitmap, float thumbnailScale); @@ -730,7 +733,7 @@ long nativeTabContentManager, int tabId, Callback<Boolean> callback); void getEtc1TabThumbnail( - long nativeTabContentManager, int tabId, Callback<Bitmap> callback); + long nativeTabContentManager, int tabId, Callback<@Nullable Bitmap> callback); void setCaptureMinRequestTimeForTesting(long nativeTabContentManager, int timeMs);
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabContentManagerThumbnailProvider.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabContentManagerThumbnailProvider.java index c6b0cef..d609aa62 100644 --- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabContentManagerThumbnailProvider.java +++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabContentManagerThumbnailProvider.java
@@ -10,8 +10,11 @@ import android.util.Size; import org.chromium.base.Callback; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; /** {@link ThumbnailProvider} adapter for {@link TabContentManager}. */ +@NullMarked public class TabContentManagerThumbnailProvider implements ThumbnailProvider { private final TabContentManager mTabContentManager; @@ -24,14 +27,17 @@ @Override public void getTabThumbnailWithCallback( - int tabId, Size thumbnailSize, boolean isSelected, Callback<Drawable> callback) { + int tabId, + Size thumbnailSize, + boolean isSelected, + Callback<@Nullable Drawable> callback) { mTabContentManager.getTabThumbnailWithCallback( tabId, thumbnailSize, adaptCallback(callback)); } - private static Callback<Bitmap> adaptCallback(Callback<Drawable> callback) { - return (Bitmap bitmap) -> { + private static Callback<@Nullable Bitmap> adaptCallback(Callback<@Nullable Drawable> callback) { + return (@Nullable Bitmap bitmap) -> { Drawable drawable = null; if (bitmap != null) { drawable = new BitmapDrawable(bitmap);
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabGridIphDialogCoordinator.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabGridIphDialogCoordinator.java index aa60a8c..ae737e9 100644 --- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabGridIphDialogCoordinator.java +++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabGridIphDialogCoordinator.java
@@ -9,10 +9,11 @@ import android.view.ViewGroup; import android.view.ViewTreeObserver; -import androidx.annotation.Nullable; import androidx.appcompat.content.res.AppCompatResources; import org.chromium.base.TraceEvent; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.components.browser_ui.widget.IphDialogView; import org.chromium.ui.modaldialog.DialogDismissalCause; import org.chromium.ui.modaldialog.ModalDialogManager; @@ -20,6 +21,7 @@ import org.chromium.ui.modelutil.PropertyModel; /** Coordinator for the IPH dialog in grid tab switcher. */ +@NullMarked public class TabGridIphDialogCoordinator implements TabSwitcherIphController { private final IphDialogView mIphDialogView; private final PropertyModel mModel;
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabListFaviconProvider.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabListFaviconProvider.java index 64e53e7..646979a4 100644 --- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabListFaviconProvider.java +++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabListFaviconProvider.java
@@ -4,6 +4,8 @@ package org.chromium.chrome.browser.tab_ui; +import static org.chromium.build.NullUtil.assertNonNull; + import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -15,12 +17,14 @@ import androidx.annotation.ColorInt; import androidx.annotation.IntDef; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.appcompat.content.res.AppCompatResources; import org.chromium.base.Callback; +import org.chromium.build.annotations.Initializer; +import org.chromium.build.annotations.MonotonicNonNull; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tab.Tab; @@ -37,6 +41,7 @@ import java.util.Objects; /** Provider for processed favicons in Tab list. */ +@NullMarked public class TabListFaviconProvider { /** * Interface for lazily fetching favicons. Instances of this class should implement the fetch @@ -65,14 +70,12 @@ * comparisons of favicon objects. */ public abstract static class TabFavicon { - private final @NonNull Drawable mDefaultDrawable; - private final @NonNull Drawable mSelectedDrawable; + private final Drawable mDefaultDrawable; + private final Drawable mSelectedDrawable; private final boolean mIsRecolorAllowed; protected TabFavicon( - @NonNull Drawable defaultDrawable, - @NonNull Drawable selectedDrawable, - boolean allowRecolor) { + Drawable defaultDrawable, Drawable selectedDrawable, boolean allowRecolor) { mDefaultDrawable = defaultDrawable; mSelectedDrawable = selectedDrawable; mIsRecolorAllowed = allowRecolor; @@ -105,19 +108,19 @@ /** A favicon that is sourced from and equality checked on a single URL. */ @VisibleForTesting public static class UrlTabFavicon extends TabFavicon { - private final @NonNull GURL mGurl; + private final GURL mGurl; private UrlTabFavicon( - @NonNull Drawable defaultDrawable, - @NonNull Drawable selectedDrawable, + Drawable defaultDrawable, + Drawable selectedDrawable, boolean allowRecolor, - @NonNull GURL gurl) { + GURL gurl) { super(defaultDrawable, selectedDrawable, allowRecolor); mGurl = gurl; } @VisibleForTesting - public UrlTabFavicon(@NonNull Drawable drawable, @NonNull GURL gurl) { + public UrlTabFavicon(Drawable drawable, GURL gurl) { this(drawable, drawable, false, gurl); } @@ -135,10 +138,10 @@ /** Tracks the GURLS that were used for the composed favicon for the equality check. */ @VisibleForTesting public static class ComposedTabFavicon extends TabFavicon { - private final @NonNull GURL[] mGurls; + private final GURL[] mGurls; @VisibleForTesting - public ComposedTabFavicon(@NonNull Drawable drawable, @NonNull GURL[] gurls) { + public ComposedTabFavicon(Drawable drawable, GURL[] gurls) { super(drawable, drawable, false); mGurls = gurls; } @@ -185,8 +188,8 @@ private final @StaticTabFaviconType int mType; private ResourceTabFavicon( - @NonNull Drawable defaultDrawable, - @NonNull Drawable selectedDrawable, + Drawable defaultDrawable, + Drawable selectedDrawable, boolean allowRecolor, @StaticTabFaviconType int type) { super(defaultDrawable, selectedDrawable, allowRecolor); @@ -194,7 +197,7 @@ } @VisibleForTesting - public ResourceTabFavicon(@NonNull Drawable defaultDrawable, @StaticTabFaviconType int type) { + public ResourceTabFavicon(Drawable defaultDrawable, @StaticTabFaviconType int type) { this(defaultDrawable, defaultDrawable, false, type); } @@ -219,8 +222,7 @@ private static class LazyTabFaviconResolver { // Null after resolution succeeds. private @Nullable TabFaviconResolver mResolver; - // Null until resolution succeeds. - private @Nullable TabFavicon mTabFavicon; + private @MonotonicNonNull TabFavicon mTabFavicon; LazyTabFaviconResolver(TabFaviconResolver resolver) { assert resolver != null; @@ -229,10 +231,9 @@ TabFavicon get(Context context) { if (mTabFavicon == null) { - mTabFavicon = mResolver.resolve(context); - if (mTabFavicon != null) { - mResolver = null; - } + assert mResolver != null; + mTabFavicon = assertNonNull(mResolver.resolve(context)); + mResolver = null; } return mTabFavicon; } @@ -262,7 +263,7 @@ private final @Nullable TabWebContentsFaviconDelegate mTabWebContentsFaviconDelegate; private boolean mIsInitialized; - private Profile mProfile; + private @MonotonicNonNull Profile mProfile; private @Nullable FaviconHelper mFaviconHelper; /** @@ -397,8 +398,7 @@ * @param iconUrl The url the favicon came from. * @return a favicon fetcher that returns a processed version of the bitmap. */ - public TabFaviconFetcher getFaviconFromBitmapFetcher( - @NonNull Bitmap icon, @NonNull GURL iconUrl) { + public TabFaviconFetcher getFaviconFromBitmapFetcher(Bitmap icon, GURL iconUrl) { Drawable processedBitmap = processBitmap(icon, mIsTabStrip); return new TabFaviconFetcher() { @Override @@ -449,7 +449,7 @@ /** Returns the bitmap as a favicon. Visible for testing to override return value. */ @VisibleForTesting - public TabFavicon getFaviconFromBitmap(@NonNull Bitmap icon, @NonNull GURL iconUrl) { + public TabFavicon getFaviconFromBitmap(Bitmap icon, GURL iconUrl) { return new UrlTabFavicon(processBitmap(icon, mIsTabStrip), iconUrl); } @@ -682,6 +682,7 @@ } private Profile getProfile(boolean isIncognito) { + assert mProfile != null; if (!isIncognito) return mProfile; Profile otrProfile = mProfile.getPrimaryOtrProfile(/* createIfNeeded= */ false); @@ -726,6 +727,7 @@ return layerDrawable; } + @Initializer private static void maybeSetUpLazyTabFaviconResolvers( @ColorInt int defaultIconColor, @ColorInt int selectedIconColor, @@ -764,8 +766,6 @@ Bitmap chromeBitmap = BitmapFactory.decodeResource( context.getResources(), R.drawable.chromelogo16); - if (chromeBitmap == null) return null; - return createChromeOwnedResourceTabFavicon( context, chromeBitmap, @@ -824,8 +824,6 @@ Bitmap chromeBitmap = BitmapFactory.decodeResource( context.getResources(), R.drawable.chromelogo16); - if (chromeBitmap == null) return null; - return createChromeOwnedResourceTabFavicon( context, chromeBitmap,
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcher.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcher.java index bed98fbb..7d9ec7a 100644 --- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcher.java +++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcher.java
@@ -4,14 +4,15 @@ package org.chromium.chrome.browser.tab_ui; -import androidx.annotation.Nullable; - import org.chromium.base.supplier.Supplier; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.tab.Tab; import java.util.List; /** Interface for the Tab Switcher. */ +@NullMarked public interface TabSwitcher { /** Called when native initialization is completed. */
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcherCustomViewManager.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcherCustomViewManager.java index 58411bbd..fb363743 100644 --- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcherCustomViewManager.java +++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcherCustomViewManager.java
@@ -6,10 +6,11 @@ import android.view.View; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; /** A class that supplies custom view to TabSwitcher from other non tab switcher clients. */ +@NullMarked public class TabSwitcherCustomViewManager { /** * An interface for tab switcher, via which it can listen for signals concerning @@ -19,26 +20,23 @@ /** * This is fired when a client has requested a view to be shown. * - * @param customView The {@link View} that is requested to be added. + * @param customView The {@link View} that is requested to be added. * @param backPressRunnable A {@link Runnable} which can be supplied if clients also wish to - * handle back presses while the custom view is shown. A null - * value can be passed to - * not intercept back presses. - * @param clearTabList A boolean to indicate whether we should clear the tab list when - * showing the custom view. + * handle back presses while the custom view is shown. A null value can be passed to not + * intercept back presses. + * @param clearTabList A boolean to indicate whether we should clear the tab list when + * showing the custom view. */ void addCustomView( - @NonNull View customView, - @Nullable Runnable backPressRunnable, - boolean clearTabList); + View customView, @Nullable Runnable backPressRunnable, boolean clearTabList); /** - * This is fired when the same client has made the view unavailable for it to be shown - * any longer. + * This is fired when the same client has made the view unavailable for it to be shown any + * longer. * * @param customView The {@link View} that is requested to be removed. */ - void removeCustomView(@NonNull View customView); + void removeCustomView(View customView); } // The {@link Delegate} that relays the events concerning the availability of the @@ -56,7 +54,7 @@ * @param delegate The {@link Delegate} that is responsible for relaying signals from clients to * tab switcher, may be null if reset. */ - public void setDelegate(@NonNull Delegate delegate) { + public void setDelegate(Delegate delegate) { assert mDelegate == null || delegate == null; unbindDelegate(mDelegate); bindDelegate(delegate); @@ -66,18 +64,16 @@ /** * A method to request showing a custom view. * - * @param customView The {@link View} that is being requested by the client to be shown. + * @param customView The {@link View} that is being requested by the client to be shown. * @param backPressRunnable A {@link Runnable} which can be supplied if clients also wish to - * handle back presses while the custom view is shown. A null value - * can be passed to not - * intercept back presses. - * @param clearTabList A boolean to indicate whether we should clear the tab list when - * showing the custom view. - * + * handle back presses while the custom view is shown. A null value can be passed to not + * intercept back presses. + * @param clearTabList A boolean to indicate whether we should clear the tab list when showing + * the custom view. * @return true, if the request to show custom view was relayed successfully, false otherwise. */ public boolean requestView( - @NonNull View customView, @Nullable Runnable backPressRunnable, boolean clearTabList) { + View customView, @Nullable Runnable backPressRunnable, boolean clearTabList) { if (mIsCustomViewRequested) { assert false : "Previous request view is in-flight."; // assert statements are removed in release builds.
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcherIphController.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcherIphController.java index 4ca0cd6..f925d36 100644 --- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcherIphController.java +++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcherIphController.java
@@ -4,7 +4,10 @@ package org.chromium.chrome.browser.tab_ui; +import org.chromium.build.annotations.NullMarked; + /** Interface to control the IPH dialog. */ +@NullMarked public interface TabSwitcherIphController { /** Show the dialog with IPH. */ void showIph();
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcherUtils.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcherUtils.java index 9b60df9e..e0af4759 100644 --- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcherUtils.java +++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabSwitcherUtils.java
@@ -4,9 +4,11 @@ package org.chromium.chrome.browser.tab_ui; -import androidx.annotation.Nullable; +import static org.chromium.build.NullUtil.assertNonNull; import org.chromium.base.Callback; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.layouts.LayoutManager; import org.chromium.chrome.browser.layouts.LayoutStateProvider.LayoutStateObserver; import org.chromium.chrome.browser.layouts.LayoutType; @@ -21,6 +23,7 @@ import org.chromium.components.tab_group_sync.TabGroupUiActionHandler; /** Utility methods for TabSwitcher related actions. */ +@NullMarked public class TabSwitcherUtils { /** * A method to navigate to tab switcher. @@ -70,9 +73,11 @@ TabGroupModelFilter tabGroupModelFilter, Callback<Integer> requestOpenTabGroupDialog) { SavedTabGroup syncGroup = tabGroupSyncService.getGroup(syncId); + assert syncGroup != null; if (syncGroup.localId == null) { - tabGroupUiActionHandler.openTabGroup(syncGroup.syncId); + tabGroupUiActionHandler.openTabGroup(assertNonNull(syncGroup.syncId)); syncGroup = tabGroupSyncService.getGroup(syncId); + assert syncGroup != null; assert syncGroup.localId != null; }
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabThumbnailView.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabThumbnailView.java index 04e4dc0..7d58101 100644 --- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabThumbnailView.java +++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabThumbnailView.java
@@ -4,6 +4,8 @@ package org.chromium.chrome.browser.tab_ui; +import static org.chromium.build.NullUtil.assumeNonNull; + import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; @@ -21,19 +23,25 @@ import android.os.Build; import android.util.AttributeSet; import android.widget.ImageView; -import androidx.annotation.Nullable; + import androidx.appcompat.content.res.AppCompatResources; import androidx.core.view.ViewCompat; +import org.chromium.build.annotations.EnsuresNonNull; +import org.chromium.build.annotations.MonotonicNonNull; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; + /** - * A specialized {@link ImageView} that clips a thumbnail to a card shape with varied corner - * radii. Overlays a background drawable. The height is varied based on the width and the - * aspect ratio of the image. + * A specialized {@link ImageView} that clips a thumbnail to a card shape with varied corner radii. + * Overlays a background drawable. The height is varied based on the width and the aspect ratio of + * the image. * - * Alternatively, this could be implemented using - * * ShapeableImageView - however, this is inconsistent for hardware/software based draws. - * * RoundedCornerImageView - however, this doesn't handle non-Bitmap Drawables well. + * <p>Alternatively, this could be implemented using * ShapeableImageView - however, this is + * inconsistent for hardware/software based draws. * RoundedCornerImageView - however, this doesn't + * handle non-Bitmap Drawables well. */ +@NullMarked public class TabThumbnailView extends ImageView { private static final boolean SUPPORTS_ANTI_ALIAS_CLIP = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P; @@ -41,19 +49,19 @@ /** Placeholder drawable constants. */ private static final float SIZE_PERCENTAGE = 0.42f; - private static Integer sVerticalOffsetPx; + private static @MonotonicNonNull Integer sVerticalOffsetPx; /** To prevent {@link TabThumbnailView#updateImage()} from running during inflation. */ private boolean mInitialized; /** - * Placeholder icon drawable to use if there is no thumbnail. This is drawn on-top of the - * {@link mBackgroundDrawable} which defines the shape of the thumbnail. There are two - * separate layers because the background scales with the thumbnail size whereas the icon - * will be the SIZE_PERCENTAGE of the minimum side length of the thumbnail size centered - * and adjusted upwards. + * Placeholder icon drawable to use if there is no thumbnail. This is drawn on-top of the {@link + * mBackgroundDrawable} which defines the shape of the thumbnail. There are two separate layers + * because the background scales with the thumbnail size whereas the icon will be the + * SIZE_PERCENTAGE of the minimum side length of the thumbnail size centered and adjusted + * upwards. */ - private VectorDrawable mIconDrawable; + private @Nullable VectorDrawable mIconDrawable; private Matrix mIconMatrix; private int mIconColor; @@ -69,15 +77,14 @@ private final Path mPath; private final RectF mRectF; - // Realistically this will be set once and never again. - private float[] mRadii; + private @MonotonicNonNull float[] mRadii; public TabThumbnailView(Context context, AttributeSet attrs) { this(context, attrs, 0); } - public TabThumbnailView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); + public TabThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); if (sVerticalOffsetPx == null) { sVerticalOffsetPx = @@ -127,7 +134,7 @@ } @Override - public void setImageIcon(Icon icon) { + public void setImageIcon(@Nullable Icon icon) { super.setImageIcon(icon); updateImage(); } @@ -145,7 +152,7 @@ } @Override - public void setImageURI(Uri uri) { + public void setImageURI(@Nullable Uri uri) { super.setImageURI(uri); updateImage(); } @@ -157,6 +164,7 @@ return; } mPath.reset(); + assumeNonNull(mRadii); mPath.addRoundRect(mRectF, mRadii, Path.Direction.CW); canvas.save(); canvas.clipPath(mPath); @@ -245,11 +253,13 @@ /** * Sets the rounded corner radii. + * * @param cornerRadiusTopStart top start corner radius. * @param cornerRadiusTopEnd top end corner radius. * @param cornerRadiusBottomStart bottom start corner radius. * @param cornerRadiusBottomEnd bottom end corner radius. */ + @EnsuresNonNull("mRadii") void setRoundedCorners( int cornerRadiusTopStart, int cornerRadiusTopEnd, @@ -303,6 +313,7 @@ // Center and offset vertically by sVerticalOffsetPx to account for optical illusion of // centering. + assumeNonNull(sVerticalOffsetPx); mIconMatrix.postTranslate( (float) (width - edgeLength) / 2f, (float) (height - edgeLength) / 2f - sVerticalOffsetPx); @@ -310,7 +321,7 @@ } } - public VectorDrawable getIconDrawableForTesting() { + public @Nullable VectorDrawable getIconDrawableForTesting() { return mIconDrawable; } }
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabUiThemeUtils.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabUiThemeUtils.java index 6fdab9f..1fbff6e 100644 --- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabUiThemeUtils.java +++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabUiThemeUtils.java
@@ -16,6 +16,10 @@ import com.google.android.material.color.MaterialColors; import com.google.android.material.elevation.ElevationOverlayProvider; +import org.chromium.build.annotations.NullMarked; + +/** Utility methods for providing colors and styles for the tab UI. */ +@NullMarked public class TabUiThemeUtils { private static final String TAG = "TabUiThemeUtils";
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/ThumbnailProvider.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/ThumbnailProvider.java index 872a085..a6d1545 100644 --- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/ThumbnailProvider.java +++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/ThumbnailProvider.java
@@ -8,8 +8,11 @@ import android.util.Size; import org.chromium.base.Callback; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; /** An interface to get the thumbnails to be shown inside the tab grid cards. */ +@NullMarked public interface ThumbnailProvider { /** * Fetches a tab thumbnail in the form of a drawable. Usually from {@link TabContentManager}. @@ -21,5 +24,8 @@ * receive null if no bitmap is returned. */ void getTabThumbnailWithCallback( - int tabId, Size thumbnailSize, boolean isSelected, Callback<Drawable> callback); + int tabId, + Size thumbnailSize, + boolean isSelected, + Callback<@Nullable Drawable> callback); }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index cae64ac..4818d5c 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -4833,8 +4833,6 @@ "views/web_apps/web_app_icon_name_and_origin_view.h", "views/web_apps/web_app_identity_update_confirmation_view.cc", "views/web_apps/web_app_identity_update_confirmation_view.h", - "views/web_apps/web_app_info_image_source.cc", - "views/web_apps/web_app_info_image_source.h", "views/web_apps/web_app_install_dialog_delegate.cc", "views/web_apps/web_app_install_dialog_delegate.h", "views/web_apps/web_app_simple_install_dialog.cc", @@ -5429,6 +5427,8 @@ "web_applications/web_app_browser_controller.cc", "web_applications/web_app_browser_controller.h", "web_applications/web_app_dialogs.h", + "web_applications/web_app_info_image_source.cc", + "web_applications/web_app_info_image_source.h", "web_applications/web_app_launch_navigation_handle_user_data.cc", "web_applications/web_app_launch_navigation_handle_user_data.h", "web_applications/web_app_launch_process.cc",
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorPhone.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorPhone.java index 06e1a8f..7c64f1b3 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorPhone.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarCoordinatorPhone.java
@@ -156,17 +156,6 @@ } /** - * Returns true if this view has focus itself, or is the ancestor of the view that has focus. - * - * <p>TODO(crbug.com/40151029): Hide this View interaction if possible. - * - * @see View#hasFocus() - */ - public boolean hasFocus() { - return mLocationBarPhone.hasFocus(); - } - - /** * Invalidate the whole view. * * <p>TODO(crbug.com/40151029): Hide this View interaction if possible. @@ -189,17 +178,6 @@ } /** - * Sets the padding. - * - * <p>TODO(crbug.com/40151029): Hide this View interaction if possible. - * - * @see View#setPadding(int, int, int, int) - */ - public void setPadding(int left, int top, int right, int bottom) { - mLocationBarPhone.setPadding(left, top, right, bottom); - } - - /** * Sets the horizontal location of this view relative to its left position. * * <p>TODO(crbug.com/40151029): Hide this View interaction if possible.
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninAndHistorySyncActivityLauncher.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninAndHistorySyncActivityLauncher.java index 17dfe51..8b460ef6 100644 --- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninAndHistorySyncActivityLauncher.java +++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninAndHistorySyncActivityLauncher.java
@@ -36,6 +36,7 @@ SigninAccessPoint.CCT_ACCOUNT_MISMATCH_NOTIFICATION, SigninAccessPoint.COLLABORATION_JOIN_TAB_GROUP, SigninAccessPoint.COLLABORATION_SHARE_TAB_GROUP, + SigninAccessPoint.COLLABORATION_LEAVE_OR_DELETE_TAB_GROUP, }) @Retention(RetentionPolicy.SOURCE) @interface AccessPoint {}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherActionMenuCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherActionMenuCoordinator.java index 15bdbde..5297dba 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherActionMenuCoordinator.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherActionMenuCoordinator.java
@@ -269,7 +269,7 @@ R.drawable.ic_widgets); case MenuItemType.DIVIDER: default: - return buildMenuDivider(); + return buildMenuDivider(mProfile.isIncognitoBranded()); } }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java index 9f4d105..b9ee181 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -34,7 +34,6 @@ import android.view.ViewStub; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.TextView; import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; @@ -140,7 +139,6 @@ private ViewGroup mToolbarButtonsContainer; // Non-null after inflation occurs. private @NonNull ImageView mHomeButton; - private TextView mUrlBar; protected View mUrlActionContainer; private OptionalButtonCoordinator mOptionalButtonCoordinator; @@ -329,7 +327,6 @@ mToolbarButtonsContainer = findViewById(R.id.toolbar_buttons); mHomeButton = findViewById(R.id.home_button); - mUrlBar = findViewById(R.id.url_bar); mUrlActionContainer = findViewById(R.id.url_action_container); mToolbarBackground = new ColorDrawable(getToolbarColorForVisualState(VisualState.NORMAL)); @@ -470,7 +467,7 @@ * @param shouldUseFocusColor True if should return the color for focus state. */ private @ColorInt int getToolbarDefaultColor(boolean shouldUseFocusColor) { - if (mLocationBar.getPhoneCoordinator().hasFocus() || shouldUseFocusColor) { + if (urlHasFocus() || shouldUseFocusColor) { if (mDropdownListScrolled) { return isIncognitoBranded() ? getContext().getColor(R.color.omnibox_scrolled_bg_incognito) @@ -493,7 +490,7 @@ */ private @ColorInt int getLocationBarDefaultColorForToolbarColor( @ColorInt int toolbarColor, boolean shouldUseFocusColor) { - if (mLocationBar.getPhoneCoordinator().hasFocus() || shouldUseFocusColor) { + if (urlHasFocus() || shouldUseFocusColor) { // Omnibox has same background as the Omnibox suggestion. return OmniboxResourceProvider.getStandardSuggestionBackgroundColor( @@ -579,7 +576,7 @@ @Override public void onClick(View v) { // Don't allow clicks while the omnibox is being focused. - if (mLocationBar != null && mLocationBar.getPhoneCoordinator().hasFocus()) { + if (mLocationBar != null && urlHasFocus()) { return; } if (mHomeButton == v) { @@ -845,7 +842,7 @@ case VisualState.INCOGNITO: return ChromeColors.getDefaultThemeColor(getContext(), true); case VisualState.BRAND_COLOR: - if (mLocationBar.getPhoneCoordinator().hasFocus()) { + if (urlHasFocus()) { return getToolbarDefaultColor(/* shouldUseFocusColor= */ false); } return getToolbarDataProvider().getPrimaryColor(); @@ -1122,8 +1119,7 @@ // focusing on the NTP. In NTP, toolbar and locationbar need to transite color only when // the omnibox is focused. When the fake omnibox is scrolled, the color should not // change. - if ((mLocationBar.getPhoneCoordinator().hasFocus() || !isLocationBarShownInNtp) - && mTabSwitcherState == STATIC_TAB) { + if ((urlHasFocus() || !isLocationBarShownInNtp) && mTabSwitcherState == STATIC_TAB) { boolean isInGeneralNtp = isLocationBarShownInGeneralNtp() || mIsInLoadingPhaseFromNtpToWebpage; // Add a special case for general NTP to the defaultColor to ensure that the color @@ -1187,7 +1183,7 @@ - getResources() .getDimensionPixelSize(R.dimen.location_bar_url_action_offset); int toolbarSidePaddingChange = mToolbarSidePaddingForNtp - mToolbarSidePadding; - if (mLocationBar.getPhoneCoordinator().hasFocus()) { + if (urlHasFocus()) { urlActionsTranslationX = MathUtils.flipSignIf( (toolbarSidePaddingChange + urlActionContainerEndMarginChange) @@ -1247,7 +1243,7 @@ } if (!mUrlFocusChangeInProgress && getToolbarShadow() != null) { - getToolbarShadow().setAlpha(mUrlBar.hasFocus() ? 0.f : 1.f); + getToolbarShadow().setAlpha(urlHasFocus() ? 0.f : 1.f); } mLocationBar.getPhoneCoordinator().setAlpha(1); @@ -1279,7 +1275,7 @@ setClipToPadding(!isExpanded); if (!mUrlFocusChangeInProgress) { float alpha = 0.f; - if (!mUrlBar.hasFocus() && mNtpSearchBoxScrollFraction == 1.f) { + if (!urlHasFocus() && mNtpSearchBoxScrollFraction == 1.f) { alpha = 1.f; } getToolbarShadow().setAlpha(alpha);
diff --git a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc index 1a5e8b0c..7fb5a81 100644 --- a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc +++ b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc
@@ -557,11 +557,15 @@ ui::ImageModel::FromVectorIcon(vector_icons::kInfoOutlineIcon, ui::kColorMenuIcon, ui::SimpleMenuModel::kDefaultIconSize)); - menu_model->AddItemWithIcon( - COMMAND_SEND_FEEDBACK, l10n_util::GetStringUTF16(IDS_LENS_SEND_FEEDBACK), - ui::ImageModel::FromVectorIcon(vector_icons::kFeedbackIcon, - ui::kColorMenuIcon, - ui::SimpleMenuModel::kDefaultIconSize)); + + if (!lens::features::IsLensSearchSidePanelNewFeedbackEnabled()) { + menu_model->AddItemWithIcon( + COMMAND_SEND_FEEDBACK, + l10n_util::GetStringUTF16(IDS_LENS_SEND_FEEDBACK), + ui::ImageModel::FromVectorIcon(vector_icons::kFeedbackIcon, + ui::kColorMenuIcon, + ui::SimpleMenuModel::kDefaultIconSize)); + } return menu_model; }
diff --git a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java index 1aa650de..94648cd 100644 --- a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java +++ b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarView.java
@@ -140,6 +140,9 @@ mContainerView = (ViewGroup) LayoutInflater.from(activity).inflate(snackbarLayout, mParent, false); + // Make sure clicks are not consumed by content beneath the container view. + mContainerView.setClickable(true); + mSnackbarView = mContainerView.findViewById(R.id.snackbar); mAnimationDuration = mContainerView.getResources().getInteger(android.R.integer.config_mediumAnimTime);
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc index 3ed27d78..2c0363b 100644 --- a/chrome/browser/ui/toolbar/app_menu_model.cc +++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -1943,6 +1943,9 @@ IDS_GLIC_THREE_DOT_MENU_ITEM, glic::GlicVectorIconManager::GetVectorIcon( IDR_GLIC_BUTTON_VECTOR_ICON)); + SetIsNewFeatureAt(GetIndexOfCommandId(IDC_OPEN_GLIC).value(), + browser()->window()->MaybeShowNewBadgeFor( + features::kGlicAppMenuNewBadge)); } #endif
diff --git a/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc b/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc index 2a77468e..970d8e4 100644 --- a/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc +++ b/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.cc
@@ -237,6 +237,18 @@ controller->Show(*request_info); } +void CollaborationControllerDelegateDesktop::ShowLeaveDialog( + const tab_groups::EitherGroupID& either_id, + ResultCallback result) { + std::move(result).Run(CollaborationControllerDelegate::Outcome::kFailure); +} + +void CollaborationControllerDelegateDesktop::ShowDeleteDialog( + const tab_groups::EitherGroupID& either_id, + ResultCallback result) { + std::move(result).Run(CollaborationControllerDelegate::Outcome::kFailure); +} + void CollaborationControllerDelegateDesktop::PromoteTabGroup( const data_sharing::GroupId& group_id, ResultCallback result) { @@ -321,7 +333,7 @@ action == data_sharing::mojom::GroupAction::kDeleteGroup) && progress == data_sharing::mojom::GroupActionProgress::kSuccess) { std::move(result).Run( - CollaborationControllerDelegate::Outcome::kDeleteOrLeaveGroup); + CollaborationControllerDelegate::Outcome::kGroupLeftOrDeleted); } else { std::move(result).Run(CollaborationControllerDelegate::Outcome::kSuccess); }
diff --git a/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.h b/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.h index 8a2736a..de63b6c9 100644 --- a/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.h +++ b/chrome/browser/ui/views/data_sharing/collaboration_controller_delegate_desktop.h
@@ -50,6 +50,10 @@ ResultCallback result) override; void ShowManageDialog(const tab_groups::EitherGroupID& either_id, ResultCallback result) override; + void ShowLeaveDialog(const tab_groups::EitherGroupID& either_id, + ResultCallback result) override; + void ShowDeleteDialog(const tab_groups::EitherGroupID& either_id, + ResultCallback result) override; void PromoteTabGroup(const data_sharing::GroupId& group_id, ResultCallback result) override; void PromoteCurrentScreen() override;
diff --git a/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc b/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc index 640df4c7..1f501e4 100644 --- a/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc +++ b/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc
@@ -124,6 +124,7 @@ public: OmniboxSuggestionRowButton(PressedCallback callback, const gfx::VectorIcon& icon, + const gfx::Image& image, OmniboxPopupViewViews* popup_view, OmniboxPopupSelection selection) : MdTextButton(std::move(callback), @@ -131,6 +132,7 @@ CONTEXT_OMNIBOX_PRIMARY, /*use_text_color_for_icon=*/false), icon_(&icon), + image_(image), popup_view_(popup_view), selection_(selection) { SetTriggerableEventFlags(GetTriggerableEventFlags() | @@ -180,12 +182,17 @@ // different (for example, if the NTP colors are customized). const auto* const color_provider = GetColorProvider(); const bool selected = theme_state_ == OmniboxPartState::SELECTED; - SetImageModel(views::Button::STATE_NORMAL, - ui::ImageModel::FromVectorIcon( - *icon_, - selected ? kColorOmniboxResultsButtonIconSelected - : kColorOmniboxResultsButtonIcon, - GetLayoutConstant(LOCATION_BAR_ICON_SIZE))); + if (!image_.IsEmpty()) { + SetImageModel(views::Button::STATE_NORMAL, + ui::ImageModel::FromImage(image_)); + } else { + SetImageModel(views::Button::STATE_NORMAL, + ui::ImageModel::FromVectorIcon( + *icon_, + selected ? kColorOmniboxResultsButtonIconSelected + : kColorOmniboxResultsButtonIcon, + GetLayoutConstant(LOCATION_BAR_ICON_SIZE))); + } SetEnabledTextColors(color_provider->GetColor( selected ? kColorOmniboxResultsTextSelected : kColorOmniboxText)); ConfigureInkDropForRefresh2023( @@ -220,8 +227,16 @@ } } + void SetImage(const gfx::Image& image) { + if (image_ != image) { + image_ = image; + OnThemeChanged(); + } + } + private: raw_ptr<const gfx::VectorIcon> icon_; + gfx::Image image_; raw_ptr<OmniboxPopupViewViews> popup_view_; OmniboxPartState theme_state_ = OmniboxPartState::NORMAL; @@ -283,7 +298,8 @@ keyword_button_ = AddChildView(std::make_unique<OmniboxSuggestionRowButton>( base::BindRepeating(&OmniboxSuggestionButtonRowView::ButtonPressed, base::Unretained(this), selection), - vector_icons::kSearchChromeRefreshIcon, popup_view_, selection)); + vector_icons::kSearchChromeRefreshIcon, gfx::Image(), popup_view_, + selection)); } // Only create buttons for existent actions. @@ -295,8 +311,8 @@ auto* button = AddChildView(std::make_unique<OmniboxSuggestionRowButton>( base::BindRepeating(&OmniboxSuggestionButtonRowView::ButtonPressed, base::Unretained(this), selection), - match().actions[action_index]->GetVectorIcon(), popup_view_, - selection)); + match().actions[action_index]->GetVectorIcon(), + match().actions[action_index]->GetIconImage(), popup_view_, selection)); action_buttons_.push_back(button); } } @@ -362,7 +378,11 @@ action_button->SetTooltipText(label_strings.suggestion_contents); action_button->GetViewAccessibility().SetName( label_strings.accessibility_hint); - action_button->SetIcon(action->GetVectorIcon()); + if (!action->GetIconImage().IsEmpty()) { + action_button->SetImage(action->GetIconImage()); + } else { + action_button->SetIcon(action->GetVectorIcon()); + } } }
diff --git a/chrome/browser/ui/views/performance_controls/memory_saver_interactive_ui_test.cc b/chrome/browser/ui/views/performance_controls/memory_saver_interactive_ui_test.cc index 3dcbce9..8062943b 100644 --- a/chrome/browser/ui/views/performance_controls/memory_saver_interactive_ui_test.cc +++ b/chrome/browser/ui/views/performance_controls/memory_saver_interactive_ui_test.cc
@@ -82,9 +82,9 @@ constexpr char kDocumentWithVideo[] = "/media/bigbuck-player.html"; constexpr char kDocumentWithForm[] = "/form_interaction.html"; -// Any Chrome page that can be reliably discarded. This was previously the NTP, -// but NTP is sometimes ineligible for proactive tab discard. -constexpr std::string_view kChromePage = chrome::kChromeUIVersionURL; +// The URL to load in a secondary tab, opened to deactivate the discardable +// tab under test. +constexpr std::string_view kOtherPage = chrome::kChromeUINewTabURL; } // namespace @@ -146,15 +146,8 @@ base::test::ScopedFeatureList scoped_feature_list_; }; -// Check that a tab playing a video in the background won't be discarded -// TODO(crbug.com/408399396): Re-enable flaky test on Mac. -#if BUILDFLAG(IS_MAC) -#define MAYBE_TabWithVideoNotDiscarded DISABLED_TabWithVideoNotDiscarded -#else -#define MAYBE_TabWithVideoNotDiscarded TabWithVideoNotDiscarded -#endif IN_PROC_BROWSER_TEST_P(MemorySaverDiscardPolicyInteractiveTest, - MAYBE_TabWithVideoNotDiscarded) { + TabWithVideoNotDiscarded) { DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kVideoIsPlaying); const char kPlayVideo[] = "(el) => { el.play(); }"; const DeepQuery video = {"#video"}; @@ -173,7 +166,7 @@ embedded_test_server()->GetURL(kDocumentWithVideo)), ExecuteJsAt(kFirstTabContents, video, kPlayVideo), WaitForStateChange(kFirstTabContents, video_is_playing), - AddInstrumentedTab(kSecondTabContents, GURL(kChromePage)), + AddInstrumentedTab(kSecondTabContents, GURL(kOtherPage)), TryDiscardTab(0), CheckTabIsDiscarded(0, false)); } @@ -196,7 +189,7 @@ embedded_test_server()->GetURL(kDocumentWithAudio)), ExecuteJsAt(kFirstTabContents, audio, "(el) => { el.play(); }"), WaitForEvent(kFirstTabContents, kAudioIsAudible), - AddInstrumentedTab(kSecondTabContents, GURL(kChromePage)), + AddInstrumentedTab(kSecondTabContents, GURL(kOtherPage)), TryDiscardTab(0), CheckTabIsDiscarded(0, false)); } @@ -238,7 +231,7 @@ WaitForStateChange(kFirstTabContents, input_is_focused), PressKeyboard(), WaitForStateChange(kFirstTabContents, input_value_updated), - AddInstrumentedTab(kSecondTabContents, GURL(kChromePage), 1), + AddInstrumentedTab(kSecondTabContents, GURL(kOtherPage), 1), TryDiscardTab(0), CheckTabIsDiscarded(0, false)); } @@ -260,7 +253,7 @@ kFirstTabContents, https_server.GetURL( "a.test", "/notifications/notification_tester.html")), - AddInstrumentedTab(kSecondTabContents, GURL(kChromePage)), + AddInstrumentedTab(kSecondTabContents, GURL(kOtherPage)), TryDiscardTab(0), CheckTabIsDiscarded(0, false)); } @@ -426,7 +419,7 @@ IN_PROC_BROWSER_TEST_P(MemorySaverChipInteractiveTest, PageActionChipShows) { RunTestSequence(InstrumentTab(kFirstTabContents, 0), NavigateWebContents(kFirstTabContents, GetURL()), - AddInstrumentedTab(kSecondTabContents, GURL(kChromePage)), + AddInstrumentedTab(kSecondTabContents, GURL(kOtherPage)), SelectTab(kTabStripElementId, 0), EnsureNotPresent(kMemorySaverChipElementId), DiscardTabUntilChipStopsExpanding(0, 1, kFirstTabContents), @@ -476,9 +469,17 @@ // Page Action chip should only show on discarded non-chrome pages IN_PROC_BROWSER_TEST_P(MemorySaverChipInteractiveTest, ChipShowsOnNonChromeSites) { + // Any Chrome page that can be reliably discarded. This was + // previously the NTP, but NTP is sometimes ineligible for proactive tab + // discard. + constexpr std::string_view kDiscardableInternalPage = + chrome::kChromeUIVersionURL; + RunTestSequence(InstrumentTab(kFirstTabContents, 0), NavigateWebContents(kFirstTabContents, GetURL()), - AddInstrumentedTab(kSecondTabContents, GURL(kChromePage)), + AddInstrumentedTab(kSecondTabContents, + + GURL(kDiscardableInternalPage)), // Discards tab on non-chrome page DiscardAndReloadTab(0, kFirstTabContents), @@ -496,7 +497,7 @@ RunTestSequence( InstrumentTab(kFirstTabContents, 0), NavigateWebContents(kFirstTabContents, GetURL()), - AddInstrumentedTab(kSecondTabContents, GURL(kChromePage)), + AddInstrumentedTab(kSecondTabContents, GURL(kOtherPage)), DiscardAndReloadTab(0, kFirstTabContents), PressPageActionButton(), WaitForShow(MemorySaverBubbleView::kMemorySaverDialogBodyElementId), PressButton(MemorySaverBubbleView::kMemorySaverDialogOkButton), @@ -512,7 +513,7 @@ RunTestSequence( InstrumentTab(kFirstTabContents, 0), NavigateWebContents(kFirstTabContents, GetURL()), - AddInstrumentedTab(kSecondTabContents, GURL(kChromePage)), + AddInstrumentedTab(kSecondTabContents, GURL(kOtherPage)), DiscardAndReloadTab(0, kFirstTabContents), PressPageActionButton(), WaitForShow(MemorySaverBubbleView::kMemorySaverDialogBodyElementId), NameView( @@ -530,7 +531,7 @@ RunTestSequence( InstrumentTab(kFirstTabContents, 0), NavigateWebContents(kFirstTabContents, GetURL()), - AddInstrumentedTab(kSecondTabContents, GURL(kChromePage)), + AddInstrumentedTab(kSecondTabContents, GURL(kOtherPage)), DiscardAndReloadTab(0, kFirstTabContents), PressPageActionButton(), WaitForShow(MemorySaverBubbleView::kMemorySaverDialogBodyElementId), MousePressPageActionButton(), @@ -545,7 +546,7 @@ RunTestSequence( InstrumentTab(kFirstTabContents, 0), NavigateWebContents(kFirstTabContents, GetURL()), - AddInstrumentedTab(kSecondTabContents, GURL(kChromePage)), + AddInstrumentedTab(kSecondTabContents, GURL(kOtherPage)), DiscardAndReloadTab(0, kFirstTabContents), PressPageActionButton(), WaitForShow(MemorySaverBubbleView::kMemorySaverDialogBodyElementId), NameTab(1, kSecondTab), MoveMouseTo(kSecondTab), ClickMouse(), @@ -560,7 +561,7 @@ RunTestSequence( InstrumentTab(kFirstTabContents, 0), NavigateWebContents(kFirstTabContents, GetURL()), - AddInstrumentedTab(kSecondTabContents, GURL(kChromePage)), + AddInstrumentedTab(kSecondTabContents, GURL(kOtherPage)), DiscardAndReloadTab(0, kFirstTabContents), SetTabPreDiscardMemoryUsageKb(0, kMemoryUsageKb), PressPageActionButton(), WaitForShow(MemorySaverResourceView:: @@ -584,7 +585,7 @@ RunTestSequence( InstrumentTab(kFirstTabContents, 0), NavigateWebContents(kFirstTabContents, GetURL()), - AddInstrumentedTab(kSecondTabContents, GURL(kChromePage)), + AddInstrumentedTab(kSecondTabContents, GURL(kOtherPage)), DiscardAndReloadTab(0, kFirstTabContents), PressPageActionButton(), WaitForShow(MemorySaverBubbleView::kMemorySaverDialogBodyElementId), CheckViewProperty( @@ -682,7 +683,7 @@ kSkipPixelTestsReason), InstrumentTab(kFirstTabContents, 0), NavigateWebContents(kFirstTabContents, GetURL()), - AddInstrumentedTab(kSecondTabContents, GURL(kChromePage)), + AddInstrumentedTab(kSecondTabContents, GURL(kOtherPage)), ForceRefreshMemoryMetrics(), DiscardAndReloadTab(0, kFirstTabContents), SetTabPreDiscardMemoryUsageKb(0, 135 * 1024), PressPageActionButton(), WaitForShow( @@ -719,7 +720,7 @@ RunTestSequence( InstrumentTab(kFirstTabContents, 0), NavigateWebContents(kFirstTabContents, GetURL()), - AddInstrumentedTab(kSecondTabContents, GURL(kChromePage)), + AddInstrumentedTab(kSecondTabContents, GURL(kOtherPage)), TryDiscardTab(0), CheckTabIsDiscarded(0, true), WaitForPromo(feature_engagement::kIPHDiscardRingFeature), PressNonDefaultPromoButton(), InstrumentTab(kThirdTabContents, 2), @@ -770,7 +771,7 @@ kSkipPixelTestsReason), InstrumentTab(kFirstTabContents, 0), NavigateWebContents(kFirstTabContents, GetURL()), - AddInstrumentedTab(kSecondTabContents, GURL(kChromePage)), + AddInstrumentedTab(kSecondTabContents, GURL(kOtherPage)), Do(base::BindLambdaForTesting( [=, this]() { GetTabStrip()->StopAnimating(true); })), TryDiscardTab(0), CheckTabIsDiscarded(0, true),
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_service.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_service.cc index dc51194c..8465c84 100644 --- a/chrome/browser/ui/views/side_panel/read_anything/read_anything_service.cc +++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_service.cc
@@ -98,14 +98,12 @@ extension_misc::kReadingModeGDocsHelperManifestFilename, /*should_localize=*/false); #else - extensions::ExtensionService* service = - extensions::ExtensionSystem::Get(profile_)->extension_service(); - if (!service) { - // In tests, the service might not be created. + auto* component_loader = extensions::ComponentLoader::Get(profile_); + if (!component_loader) { + // In tests, the loader might not be created. CHECK_IS_TEST(); return; } - extensions::ComponentLoader* component_loader = service->component_loader(); if (!component_loader->Exists( extension_misc::kReadingModeGDocsHelperExtensionId)) { component_loader->Add( @@ -120,15 +118,13 @@ EmbeddedA11yExtensionLoader::GetInstance()->RemoveExtensionWithId( extension_misc::kReadingModeGDocsHelperExtensionId); #else - extensions::ExtensionService* service = - extensions::ExtensionSystem::Get(profile_)->extension_service(); - if (!service) { - // In tests, the service might not be created. + auto* component_loader = extensions::ComponentLoader::Get(profile_); + if (!component_loader) { + // In tests, the loader might not be created. CHECK_IS_TEST(); return; } - service->component_loader()->Remove( - extension_misc::kReadingModeGDocsHelperExtensionId); + component_loader->Remove(extension_misc::kReadingModeGDocsHelperExtensionId); #endif // BUILDFLAG(IS_CHROMEOS) } @@ -160,14 +156,12 @@ void ReadAnythingService::InstallTtsDownloadExtension() { #if !BUILDFLAG(IS_CHROMEOS) - extensions::ExtensionService* service = - extensions::ExtensionSystem::Get(profile_)->extension_service(); - if (!service) { - // In tests, the service might not be created. + auto* component_loader = extensions::ComponentLoader::Get(profile_); + if (!component_loader) { + // In tests, the loader might not be created. CHECK_IS_TEST(); return; } - extensions::ComponentLoader* component_loader = service->component_loader(); if (!component_loader->Exists(extension_misc::kTTSEngineExtensionId)) { component_loader->Add(IDR_TTS_ENGINE_MANIFEST, base::FilePath(FILE_PATH_LITERAL("tts_engine"))); @@ -177,13 +171,12 @@ void ReadAnythingService::RemoveTtsDownloadExtension() { #if !BUILDFLAG(IS_CHROMEOS) - extensions::ExtensionService* service = - extensions::ExtensionSystem::Get(profile_)->extension_service(); - if (!service) { + auto* component_loader = extensions::ComponentLoader::Get(profile_); + if (!component_loader) { // In tests, the service might not be created. CHECK_IS_TEST(); return; } - service->component_loader()->Remove(extension_misc::kTTSEngineExtensionId); + component_loader->Remove(extension_misc::kTTSEngineExtensionId); #endif // !BUILDFLAG(IS_CHROMEOS) }
diff --git a/chrome/browser/ui/views/tabs/dragging/dragging_tabs_session.cc b/chrome/browser/ui/views/tabs/dragging/dragging_tabs_session.cc index 7396ed27..7613b911 100644 --- a/chrome/browser/ui/views/tabs/dragging/dragging_tabs_session.cc +++ b/chrome/browser/ui/views/tabs/dragging/dragging_tabs_session.cc
@@ -19,16 +19,31 @@ namespace { constexpr char kDragAmongTabsPresentationTimeHistogram[] = "Browser.TabDragging.DragAmongTabsPresentationTime"; + +int CalculateMouseOffset(const DragSessionData& drag_data_, + float offset_to_width_ratio_) { + std::vector<TabSlotView*> tabs_to_source(drag_data_.attached_views()); + TabSlotView* source_view = drag_data_.source_view_drag_data()->attached_view; + tabs_to_source.erase( + tabs_to_source.begin() + drag_data_.source_view_index_ + 1, + tabs_to_source.end()); + const int new_x = + TabStrip::GetSizeNeededForViews(tabs_to_source) - source_view->width() + + base::ClampRound(offset_to_width_ratio_ * source_view->width()); + + return new_x; } +} // namespace + DraggingTabsSession::DraggingTabsSession(DragSessionData drag_data, TabDragContext* attached_context, - int mouse_offset, + float offset_to_width_ratio_, bool initial_move, gfx::Point start_point_in_screen) : drag_data_(drag_data), attached_context_(attached_context), - mouse_offset_(mouse_offset), + mouse_offset_(CalculateMouseOffset(drag_data_, offset_to_width_ratio_)), initial_move_(initial_move), last_move_attached_context_loc_( views::View::ConvertPointFromScreen(attached_context,
diff --git a/chrome/browser/ui/views/tabs/dragging/dragging_tabs_session.h b/chrome/browser/ui/views/tabs/dragging/dragging_tabs_session.h index 6de2674..1ea1fe3d 100644 --- a/chrome/browser/ui/views/tabs/dragging/dragging_tabs_session.h +++ b/chrome/browser/ui/views/tabs/dragging/dragging_tabs_session.h
@@ -18,13 +18,14 @@ public: // `drag_data` is a copy of the drag configuration for the full session. // `attached_context` is the context in which the tabs are being dragged. - // `mouse_offset` is the desired x offset from the leading edge of the first - // tab to the cursor. `initial_move` should be true if the drag session is - // beginning, and the tabs have not been moved from their initial positions. + // `offset_to_width_ratio_` is the desired x offset into the source + // TabSlotView (e.g. 0.5 if the drag started in the center of the tab). + // `initial_move` should be true if the drag session is beginning, and + // the tabs have not been moved from their initial positions. // `point_in_screen` is the initial cursor screen position. explicit DraggingTabsSession(DragSessionData drag_data, TabDragContext* attached_context, - int mouse_offset, + float offset_to_width_ratio_, bool initial_move, gfx::Point point_in_screen); ~DraggingTabsSession() final;
diff --git a/chrome/browser/ui/views/tabs/dragging/dragging_tabs_session_browsertest.cc b/chrome/browser/ui/views/tabs/dragging/dragging_tabs_session_browsertest.cc new file mode 100644 index 0000000..99ae36e --- /dev/null +++ b/chrome/browser/ui/views/tabs/dragging/dragging_tabs_session_browsertest.cc
@@ -0,0 +1,97 @@ +// 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/views/tabs/dragging/dragging_tabs_session.h" + +#include "chrome/browser/ui/browser_tabstrip.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "chrome/browser/ui/views/tabs/dragging/drag_session_data.h" +#include "chrome/browser/ui/views/tabs/tab_strip.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "components/tabs/public/tab_interface.h" +#include "content/public/test/browser_test.h" +#include "ui/base/models/list_selection_model.h" +#include "ui/gfx/geometry/vector2d.h" +#include "ui/views/view.h" + +class DraggingTabsSessionBrowserTest : public InProcessBrowserTest { + public: + DraggingTabsSessionBrowserTest() = default; + ~DraggingTabsSessionBrowserTest() override = default; + + void SetUpOnMainThread() override { + model_ = browser()->GetTabStripModel(); + view_ = browser()->GetBrowserView().tabstrip(); + } + + void TearDownOnMainThread() override { + view_->GetDragContext()->StoppedDragging(); + model_ = nullptr; + view_ = nullptr; + } + + protected: + std::tuple<tabs::TabInterface*, Tab*> AddTab(int index, bool foreground) { + chrome::AddTabAt(browser(), GURL("about:blank"), index, foreground); + view_->StopAnimating(true); + return std::make_tuple(model_->GetTabAtIndex(index), view_->tab_at(index)); + } + + // Sets up model and view state, and populates a DragSessionData, to drag the + // tabs at `tab_indices`. The tab at `tab_indices[source_index]` is the + // source view for the drag session. Mirrors TabDragController::AttachImpl(). + // TODO(382754501): Extend this to work with header drags. + DragSessionData StartDragging(std::vector<int> tab_indices, + int source_index) { + ui::ListSelectionModel selection; + selection.SetSelectedIndex(tab_indices[source_index]); + for (int tab_index : tab_indices) { + selection.AddIndexToSelection(tab_index); + } + model_->SetSelectionFromModel(selection); + + DragSessionData drag_data; + for (int tab_index : tab_indices) { + Tab* const tab_view = view_->tab_at(tab_index); + drag_data.tab_drag_data_.emplace_back(view_->GetDragContext(), tab_view); + drag_data.tab_drag_data_.back().attached_view = tab_view; + } + drag_data.source_view_index_ = source_index; + + view_->GetDragContext()->StartedDragging(drag_data.attached_views()); + + return drag_data; + } + + raw_ptr<TabStripModel> model_; + raw_ptr<TabStrip> view_; +}; + +IN_PROC_BROWSER_TEST_F(DraggingTabsSessionBrowserTest, BasicTest) { + // Open two tabs. + auto [tab_0, tab_0_view] = AddTab(0, true); + auto [tab_1, tab_1_view] = AddTab(1, false); + + // Set up drag session. + DragSessionData drag_data = StartDragging({0}, 0); + const gfx::Point start_point = tab_0_view->GetBoundsInScreen().CenterPoint(); + DraggingTabsSession session(drag_data, view_->GetDragContext(), 0.5, true, + start_point); + + // Swap them. + const gfx::Point target_point = + tab_1_view->GetBoundsInScreen().CenterPoint() + gfx::Vector2d(10, 0); + session.MoveAttached(target_point); + + // They should have been swapped in the model. + EXPECT_EQ(tab_0, model_->GetTabAtIndex(1)); + EXPECT_EQ(tab_1, model_->GetTabAtIndex(0)); + + // The dragged tab should be positioned with its center point under the + // cursor. + EXPECT_EQ(tab_0_view->GetBoundsInScreen().CenterPoint(), target_point); +} + +// TODO(crbug.com/382754501): Add more tests, and delete tests that are made +// redundant in tab_drag_controller_interactive_uitest.cc.
diff --git a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc index 8fa2fb0..4d85c82a 100644 --- a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc +++ b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc
@@ -2534,19 +2534,8 @@ current_state_ == DragState::kWaitingToExitRunLoop); CHECK_EQ(dragging_tabs_session_, nullptr); - // The size of the dragged tab may have changed. Adjust the x offset so that - // ratio of mouse_offset_ to original width is maintained. - std::vector<TabSlotView*> tabs_to_source(drag_data_.attached_views()); - TabSlotView* source_view = drag_data_.source_view_drag_data()->attached_view; - tabs_to_source.erase( - tabs_to_source.begin() + drag_data_.source_view_index_ + 1, - tabs_to_source.end()); - const int new_x = - TabStrip::GetSizeNeededForViews(tabs_to_source) - source_view->width() + - base::ClampRound(offset_to_width_ratio_ * source_view->width()); - dragging_tabs_session_ = std::make_unique<DraggingTabsSession>( - drag_data_, attached_context_, new_x, initial_move, + drag_data_, attached_context_, offset_to_width_ratio_, initial_move, start_point_in_screen); }
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service.cc b/chrome/browser/ui/views/user_education/browser_user_education_service.cc index 49927f4..196d3bf 100644 --- a/chrome/browser/ui/views/user_education/browser_user_education_service.cc +++ b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
@@ -1660,6 +1660,11 @@ user_education::Metadata(136, "agale@chromium.org", "Shown in the glic settings page when the user " "wants to change the keyboard shortcut."))); + + registry.RegisterFeature(user_education::NewBadgeSpecification( + features::kGlicAppMenuNewBadge, + user_education::Metadata(136, "sophey@chromium.org", + "Shown in the three dot menu."))); #endif // BUILDFLAG(ENABLE_GLIC) }
diff --git a/chrome/browser/ui/views/web_apps/create_shortcut_confirmation_view.cc b/chrome/browser/ui/views/web_apps/create_shortcut_confirmation_view.cc index c2e289e..c7dbdac2 100644 --- a/chrome/browser/ui/views/web_apps/create_shortcut_confirmation_view.cc +++ b/chrome/browser/ui/views/web_apps/create_shortcut_confirmation_view.cc
@@ -13,9 +13,9 @@ #include "base/strings/string_util.h" #include "build/build_config.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" -#include "chrome/browser/ui/views/web_apps/web_app_info_image_source.h" #include "chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h" #include "chrome/browser/ui/web_applications/web_app_dialogs.h" +#include "chrome/browser/ui/web_applications/web_app_info_image_source.h" #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h" #include "chrome/browser/web_applications/web_app_constants.h" #include "chrome/common/chrome_features.h"
diff --git a/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_impl.cc b/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_impl.cc index 8b5ef0c..1153953 100644 --- a/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_impl.cc +++ b/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_impl.cc
@@ -19,7 +19,7 @@ #include "chrome/browser/ui/views/accessibility/non_accessible_image_view.h" #include "chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_model.h" #include "chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view.h" -#include "chrome/browser/ui/views/web_apps/web_app_info_image_source.h" +#include "chrome/browser/ui/web_applications/web_app_info_image_source.h" #include "chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_metadata.h" #include "chrome/grit/generated_resources.h" #include "chrome/grit/theme_resources.h"
diff --git a/chrome/browser/ui/views/web_apps/launch_app_user_choice_dialog_view.cc b/chrome/browser/ui/views/web_apps/launch_app_user_choice_dialog_view.cc index 5538b8b..8c22cee 100644 --- a/chrome/browser/ui/views/web_apps/launch_app_user_choice_dialog_view.cc +++ b/chrome/browser/ui/views/web_apps/launch_app_user_choice_dialog_view.cc
@@ -15,8 +15,8 @@ #include "build/build_config.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" -#include "chrome/browser/ui/views/web_apps/web_app_info_image_source.h" #include "chrome/browser/ui/views/web_apps/web_app_views_utils.h" +#include "chrome/browser/ui/web_applications/web_app_info_image_source.h" #include "chrome/browser/web_applications/web_app_icon_manager.h" #include "chrome/browser/web_applications/web_app_provider.h" #include "chrome/browser/web_applications/web_app_registrar.h"
diff --git a/chrome/browser/ui/views/web_apps/sub_apps_install_dialog.cc b/chrome/browser/ui/views/web_apps/sub_apps_install_dialog.cc index 5af072b..0de8c02 100644 --- a/chrome/browser/ui/views/web_apps/sub_apps_install_dialog.cc +++ b/chrome/browser/ui/views/web_apps/sub_apps_install_dialog.cc
@@ -6,9 +6,9 @@ #include "base/i18n/message_formatter.h" #include "base/types/cxx23_to_underlying.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" -#include "chrome/browser/ui/views/web_apps/web_app_info_image_source.h" #include "chrome/browser/ui/web_applications/sub_apps_install_dialog_controller.h" #include "chrome/browser/ui/web_applications/web_app_dialogs.h" +#include "chrome/browser/ui/web_applications/web_app_info_image_source.h" #include "chrome/browser/web_applications/web_app_install_info.h" #include "chrome/grit/generated_resources.h" #include "components/constrained_window/constrained_window_views.h"
diff --git a/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc b/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc index 5fbea47..5d3f027 100644 --- a/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc +++ b/chrome/browser/ui/views/web_apps/web_app_detailed_install_dialog.cc
@@ -21,9 +21,9 @@ #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/page_action/page_action_icon_view.h" #include "chrome/browser/ui/views/web_apps/web_app_icon_name_and_origin_view.h" -#include "chrome/browser/ui/views/web_apps/web_app_info_image_source.h" #include "chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h" #include "chrome/browser/ui/web_applications/web_app_dialogs.h" +#include "chrome/browser/ui/web_applications/web_app_info_image_source.h" #include "chrome/browser/web_applications/web_app_constants.h" #include "chrome/browser/web_applications/web_app_helpers.h" #include "chrome/browser/web_applications/web_app_screenshot_fetcher.h"
diff --git a/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc b/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc index a4a9e00..2503b389 100644 --- a/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc +++ b/chrome/browser/ui/views/web_apps/web_app_diy_install_dialog.cc
@@ -16,10 +16,10 @@ #include "chrome/browser/ui/url_identity.h" #include "chrome/browser/ui/views/controls/site_icon_text_and_origin_view.h" #include "chrome/browser/ui/views/web_apps/web_app_icon_name_and_origin_view.h" -#include "chrome/browser/ui/views/web_apps/web_app_info_image_source.h" #include "chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h" #include "chrome/browser/ui/views/web_apps/web_app_views_utils.h" #include "chrome/browser/ui/web_applications/web_app_dialogs.h" +#include "chrome/browser/ui/web_applications/web_app_info_image_source.h" #include "chrome/browser/web_applications/web_app_helpers.h" #include "chrome/browser/web_applications/web_app_install_info.h" #include "chrome/common/chrome_features.h"
diff --git a/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc b/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc index 62ac8de..46743ab9 100644 --- a/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc +++ b/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc
@@ -14,9 +14,9 @@ #include "chrome/browser/ui/views/frame/toolbar_button_provider.h" #include "chrome/browser/ui/views/page_action/page_action_icon_view.h" #include "chrome/browser/ui/views/web_apps/web_app_icon_name_and_origin_view.h" -#include "chrome/browser/ui/views/web_apps/web_app_info_image_source.h" #include "chrome/browser/ui/views/web_apps/web_app_install_dialog_delegate.h" #include "chrome/browser/ui/web_applications/web_app_dialogs.h" +#include "chrome/browser/ui/web_applications/web_app_info_image_source.h" #include "chrome/browser/web_applications/web_app_constants.h" #include "chrome/browser/web_applications/web_app_install_info.h" #include "chrome/common/chrome_features.h"
diff --git a/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc b/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc index 346f26e..e7b4550 100644 --- a/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc +++ b/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc
@@ -12,8 +12,8 @@ #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/web_apps/web_app_icon_name_and_origin_view.h" -#include "chrome/browser/ui/views/web_apps/web_app_info_image_source.h" #include "chrome/browser/ui/web_applications/web_app_dialogs.h" +#include "chrome/browser/ui/web_applications/web_app_info_image_source.h" #include "chrome/browser/web_applications/web_app_command_scheduler.h" #include "chrome/browser/web_applications/web_app_icon_manager.h" #include "chrome/browser/web_applications/web_app_provider.h"
diff --git a/chrome/browser/ui/views/web_apps/web_app_info_image_source.cc b/chrome/browser/ui/web_applications/web_app_info_image_source.cc similarity index 90% rename from chrome/browser/ui/views/web_apps/web_app_info_image_source.cc rename to chrome/browser/ui/web_applications/web_app_info_image_source.cc index 5f063f90..d127d8c 100644 --- a/chrome/browser/ui/views/web_apps/web_app_info_image_source.cc +++ b/chrome/browser/ui/web_applications/web_app_info_image_source.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/ui/views/web_apps/web_app_info_image_source.h" +#include "chrome/browser/ui/web_applications/web_app_info_image_source.h" #include "ui/gfx/image/image_skia_rep.h"
diff --git a/chrome/browser/ui/views/web_apps/web_app_info_image_source.h b/chrome/browser/ui/web_applications/web_app_info_image_source.h similarity index 80% rename from chrome/browser/ui/views/web_apps/web_app_info_image_source.h rename to chrome/browser/ui/web_applications/web_app_info_image_source.h index cbf9c41..f941590 100644 --- a/chrome/browser/ui/views/web_apps/web_app_info_image_source.h +++ b/chrome/browser/ui/web_applications/web_app_info_image_source.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_UI_VIEWS_WEB_APPS_WEB_APP_INFO_IMAGE_SOURCE_H_ -#define CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_INFO_IMAGE_SOURCE_H_ +#ifndef CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_INFO_IMAGE_SOURCE_H_ +#define CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_INFO_IMAGE_SOURCE_H_ #include <map> @@ -30,4 +30,4 @@ std::map<web_app::SquareSizePx, SkBitmap> icons_; }; -#endif // CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_INFO_IMAGE_SOURCE_H_ +#endif // CHROME_BROWSER_UI_WEB_APPLICATIONS_WEB_APP_INFO_IMAGE_SOURCE_H_
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt index e80b0bd..c40a0ca 100644 --- a/chrome/build/android-arm32.pgo.txt +++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@ -chrome-android32-main-1744048751-cdd6f0152f5419d88b8abc9a19346fc8ec2d6970-4c01c407f87e60a9c0d41f6372df70364a780128.profdata +chrome-android32-main-1744070359-d61eb98387bb823904715ba9d499cb07dafad4c1-b20a7e9f55ac394455e41a818f9912c54fe4ae63.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt index 5c5083e..3058bcf 100644 --- a/chrome/build/android-arm64.pgo.txt +++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@ -chrome-android64-main-1744052807-caabcc0ff5bfedfc3cff1d3f5c895df2f2764e5b-d8fa5193bebed721a11659ea3d585daffb055115.profdata +chrome-android64-main-1744063392-751694ce8f2e794563e119030df1ac7dc5dc0ab3-a8784ddce1e0407aacaf37cad4c204243a9b85b2.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index a90439f..4f97b1dc 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1744055965-91ffa3508ce63c9e79b0b8ee0e9dccfc1b63d6e9-f4e9a3c7f4b43b0229b0f4fd2607d581467f22b1.profdata +chrome-mac-arm-main-1744070359-85b0de046d5a337edc3691044abdabec8e229e44-b20a7e9f55ac394455e41a818f9912c54fe4ae63.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt index 34b2dbd0..dfe43265 100644 --- a/chrome/build/win-arm64.pgo.txt +++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@ -chrome-win-arm64-main-1744048751-891667e5bebfe740306e51207ebb4d374f026ff1-4c01c407f87e60a9c0d41f6372df70364a780128.profdata +chrome-win-arm64-main-1744070359-01f1d962b8f10272345aa899f907f19d0cad815e-b20a7e9f55ac394455e41a818f9912c54fe4ae63.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index a8d3e0f..049b051 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1744016320-86018eff961a3c6f1c867f24e858368cf2218270-6c693ba714627222c6361ef2cf94a43bd92d5f1a.profdata +chrome-win32-main-1744037890-0b88e3f4059a0f88ac12b512b3651e48aa3041d4-2f82cfff06d4e917915d3840a892876b2ed1d161.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 9a855ef..9d4aa17 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1744016320-6311cc51409eb3a81fd434d24da46f2e290d5edd-6c693ba714627222c6361ef2cf94a43bd92d5f1a.profdata +chrome-win64-main-1744037890-836cb9f51431689a0e2a741f2d96dbb4e9c0319f-2f82cfff06d4e917915d3840a892876b2ed1d161.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index ca76b5d..87f1130 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -407,6 +407,10 @@ "GlicKeyboardShortcutNewBadge", base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kGlicAppMenuNewBadge, + "GlicAppMenuNewBadge", + base::FEATURE_DISABLED_BY_DEFAULT); + BASE_FEATURE(kGlicDebugWebview, "GlicDebugWebview", base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h index 1ad2fdb..6607931a 100644 --- a/chrome/common/chrome_features.h +++ b/chrome/common/chrome_features.h
@@ -235,6 +235,9 @@ COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kGlicKeyboardShortcutNewBadge); +COMPONENT_EXPORT(CHROME_FEATURES) +BASE_DECLARE_FEATURE(kGlicAppMenuNewBadge); + COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kGlicDetached); COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kGlicZOrderChanges);
diff --git a/chrome/common/extensions/api/omnibox.json b/chrome/common/extensions/api/omnibox.json index 7715b8e..8df003b 100644 --- a/chrome/common/extensions/api/omnibox.json +++ b/chrome/common/extensions/api/omnibox.json
@@ -50,6 +50,12 @@ "type": "string", "minLength": 1, "description": "The action button hover tooltip text." + }, + "icon": { + "optional": true, + "type": "object", + "additionalProperties": { "type": "any" }, + "description": "The icon shown in the action button on the leading side of the action label. The icon must be specified as an imageData object. The size should not be more than 160 px wide and tall." } } },
diff --git a/chrome/release_scripts b/chrome/release_scripts index b0e8d98..a8b04e9 160000 --- a/chrome/release_scripts +++ b/chrome/release_scripts
@@ -1 +1 @@ -Subproject commit b0e8d98f5141c3b50870406a7cf1e090806b2f8e +Subproject commit a8b04e95dc407615a7d2bdc70903376809093548
diff --git a/chrome/renderer/resources/extensions/omnibox_custom_bindings.js b/chrome/renderer/resources/extensions/omnibox_custom_bindings.js index 8cbe5d0..7409f3e8 100644 --- a/chrome/renderer/resources/extensions/omnibox_custom_bindings.js +++ b/chrome/renderer/resources/extensions/omnibox_custom_bindings.js
@@ -4,8 +4,10 @@ // Custom binding for the omnibox API. Only injected into the v8 contexts // for extensions which have permission for the omnibox API. +const inServiceWorker = requireNative('utils').isInServiceWorker(); +const SetIconCommon = requireNative('setIcon').SetIconCommon; -var inServiceWorker = requireNative('utils').isInServiceWorker(); +const kMaxActionIconSize = 160; // Remove invalid characters from |text| so that it is suitable to use // for |AutocompleteMatch::contents|. @@ -78,6 +80,71 @@ return result; } +function smellsLikeImageData(imageData) { + // See if this object at least looks like an ImageData element. + // Unfortunately, we cannot use instanceof because the ImageData + // constructor is not public. + // + // We do this manually instead of using JSONSchema to avoid having these + // properties show up in the doc. + return (typeof imageData == 'object') && ('width' in imageData) && + ('height' in imageData) && ('data' in imageData); +} + +// TODO(crbug.com/408069174): Move this to a common file shared with +// set_icon.js. +function verifyImageData(imageData) { + if (!smellsLikeImageData(imageData)) { + throw new Error('The imageData property must contain an ImageData object.'); + } + if (imageData.width > kMaxActionIconSize || + imageData.height > kMaxActionIconSize) { + throw new Error('Icons must be smaller 160 px wide and tall.'); + } +} + +// Register custom hook to update action icons to a format that can be parsed by +// the browser. +apiBridge.registerCustomHook(function(bindingsAPI) { + var apiFunctions = bindingsAPI.apiFunctions; + apiFunctions.setUpdateArgumentsPreValidate( + 'sendSuggestions', function(requestId, userSuggestions) { + for (let i = 0; i < userSuggestions.length; i++) { + let suggestion = userSuggestions[i]; + + if (!suggestion.actions) { + continue; + } + let actions = []; + for (let j = 0; j < suggestion.actions.length; j++) { + if (!suggestion.actions[j].icon) { + $Array.push(actions, suggestion.actions[j]); + continue; + } + // Deep copy is needed in case many actions point to the same icon. + let icon = new ImageData( + new Uint8ClampedArray(suggestion.actions[j].icon.data), + suggestion.actions[j].icon.width, + suggestion.actions[j].icon.height); + let action = { + name: suggestion.actions[j].name, + label: suggestion.actions[j].label, + tooltipText: suggestion.actions[j].tooltipText, + icon: {} + }; + verifyImageData(icon); + let details = {imageData: {}}; + details.imageData = {__proto__: null}; + details.imageData[icon.width.toString()] = icon; + action.icon = SetIconCommon(details); + $Array.push(actions, action); + } + suggestion.actions = actions; + } + return [requestId, userSuggestions]; + }); +}); + // The following custom hooks are only registered in non-service worker // contexts. In service worker contexts, we instead parse the description and // styles from the browser process.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index d3394cb6..6cca268 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -3428,6 +3428,7 @@ "../browser/ui/views/side_panel/side_panel_web_ui_view_browsertest.cc", "../browser/ui/views/tab_search_bubble_host_browsertest.cc", "../browser/ui/views/tabs/alert_indicator_button_browsertest.cc", + "../browser/ui/views/tabs/dragging/dragging_tabs_session_browsertest.cc", "../browser/ui/views/tabs/recent_activity_bubble_dialog_view_browsertest.cc", "../browser/ui/views/tabs/tab_search_button_browsertest.cc", "../browser/ui/views/tabs/tab_search_container_browsertest.cc", @@ -4299,6 +4300,7 @@ "../browser/extensions/api/usb/chrome_usb_apitest.cc", "../browser/extensions/api/user_scripts/user_scripts_apitest.cc", "../browser/extensions/api/user_scripts/user_scripts_apitest.h", + "../browser/extensions/api/user_scripts/user_scripts_uitest.cc", "../browser/extensions/api/web_navigation/web_navigation_apitest.cc", "../browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc", "../browser/extensions/api/webrtc_from_web_accessible_resource_browsertest.cc",
diff --git a/chrome/test/data/extensions/api_test/user_scripts/allowed_tests/background.js b/chrome/test/data/extensions/api_test/user_scripts/allowed_tests/background.js index 237a0bd..bff04da 100644 --- a/chrome/test/data/extensions/api_test/user_scripts/allowed_tests/background.js +++ b/chrome/test/data/extensions/api_test/user_scripts/allowed_tests/background.js
@@ -9,6 +9,22 @@ div.id = 'user-script-code'; document.body.appendChild(div);` +async function verifyApiIsNotAvailable() { + let message; + try { + await chrome.userScripts.getScripts(); + message = 'failure (chrome.userScripts API is available)'; + } catch (e) { + const expectedError = + 'TypeError: Cannot read properties of undefined (reading ' + + '\'getScripts\')'; + message = e.toString() == expectedError ? + 'success' : + 'Unexpected error: ' + e.toString(); + } + chrome.test.sendScriptResult(message); +} + async function registerUserScripts() { const userScripts = [ {
diff --git a/chrome/test/data/predictors/lcp_occur_twice.html b/chrome/test/data/predictors/lcp_occur_twice.html new file mode 100644 index 0000000..65b0f9b --- /dev/null +++ b/chrome/test/data/predictors/lcp_occur_twice.html
@@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> + <head> + <script> +globalThis.events = []; + +let lcp_count = 0; +new PerformanceObserver(entryList => { + lcp_count++; + if( lcp_count === 1) { + const div = document.createElement("div"); + div.innerHTML = "<div style=\"width:100px;height:100px;font-size:50pt;\">This is LCP.</div>"; + document.body.appendChild(div); + } + entryList.getEntries().forEach(entry => { + globalThis.events.push(`LCP@${entry.element.tagName}`); + }); +}).observe({ type: "largest-contentful-paint", buffered: true }); + +window.onload = () => { + globalThis.events.push("Onload"); +} + </script> + </head> + <body> + <img style="width:50px;height:50px;" src="red_rectangle.png"/> + <script defer src="/slow-empty.js"></script> + </body> +</html>
diff --git a/chrome/test/data/webui/cr_elements/cr_toast_test.ts b/chrome/test/data/webui/cr_elements/cr_toast_test.ts index ce313e1..1ca6b9a 100644 --- a/chrome/test/data/webui/cr_elements/cr_toast_test.ts +++ b/chrome/test/data/webui/cr_elements/cr_toast_test.ts
@@ -126,4 +126,37 @@ await toast.hide(); assertFalse(isVisible(slottedElement)); }); + + test( + 'stop and restart auto-hide depending when toast focus changes', + async function() { + const duration = 100; + toast.duration = duration; + + const button1 = document.createElement('button'); + const button2 = document.createElement('button'); + button1.textContent = 'Test 1'; + button2.textContent = 'Test 2'; + toast.appendChild(button1); + toast.appendChild(button2); + + await toast.show(); + button1.focus(); + assertTrue(toast.open); + + mockTimer.tick(duration); + assertTrue(toast.open); + + button2.focus(); + assertTrue(toast.open); + + mockTimer.tick(duration); + assertTrue(toast.open); + + button2.blur(); + assertTrue(toast.open); + + mockTimer.tick(duration); + assertFalse(toast.open); + }); });
diff --git a/chrome/test/data/webui/side_panel/BUILD.gn b/chrome/test/data/webui/side_panel/BUILD.gn index 6ae96b9..0212b43 100644 --- a/chrome/test/data/webui/side_panel/BUILD.gn +++ b/chrome/test/data/webui/side_panel/BUILD.gn
@@ -13,6 +13,9 @@ "bookmarks/power_bookmarks_edit_dialog_test.ts", "bookmarks/power_bookmarks_labels_test.ts", "bookmarks/power_bookmarks_list_test.ts", + "bookmarks/power_bookmarks_list_test_util.ts", + "bookmarks/power_bookmarks_list_transport_mode_test.ts", + "bookmarks/power_bookmarks_list_tree_view_test.ts", "bookmarks/power_bookmarks_service_test.ts", "bookmarks/keyboard_arrow_navigation_service_test.ts", "bookmarks/test_bookmarks_api_proxy.ts",
diff --git a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_test.ts b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_test.ts index f592f0c..eb0cb493 100644 --- a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_test.ts +++ b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_test.ts
@@ -4,11 +4,9 @@ import 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_list.js'; -import {ActionSource, SortOrder, ViewType} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks.mojom-webui.js'; -import type {BookmarksTreeNode} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks.mojom-webui.js'; +import {SortOrder, ViewType} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks.mojom-webui.js'; import {BookmarksApiProxyImpl} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks_api_proxy.js'; import type {PowerBookmarkRowElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmark_row.js'; -import {NESTED_BOOKMARKS_BASE_MARGIN, NESTED_BOOKMARKS_MARGIN_PER_DEPTH} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmark_row.js'; import type {PowerBookmarksListElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_list.js'; import {PageCallbackRouter} from 'chrome://resources/cr_components/commerce/price_tracking.mojom-webui.js'; import type {PageRemote} from 'chrome://resources/cr_components/commerce/price_tracking.mojom-webui.js'; @@ -19,7 +17,6 @@ import type {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.js'; import type {CrUrlListItemElement} from 'chrome://resources/cr_elements/cr_url_list_item/cr_url_list_item.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; -import type {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js'; import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js'; import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js'; @@ -28,74 +25,11 @@ import {TestMock} from 'chrome://webui-test/test_mock.js'; import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js'; +import {createTestBookmarks, getBookmarks, getBookmarksInList, getBookmarkWithId, getPowerBookmarksRowElement, initializeUi} from './power_bookmarks_list_test_util.js'; import {TestBookmarksApiProxy} from './test_bookmarks_api_proxy.js'; -const FOLDERS: BookmarksTreeNode[] = [ - { - id: '1', - parentId: '0', - index: 0, - title: 'Bookmarks Bar', - url: null, - dateAdded: null, - dateLastUsed: null, - children: [], - }, - { - id: '2', - parentId: '0', - title: 'Other Bookmarks', - index: 1, - url: null, - dateAdded: null, - dateLastUsed: null, - children: [ - { - id: '3', - parentId: '2', - index: 0, - title: 'First child bookmark', - url: 'http://child/bookmark/1/', - dateAdded: 1, - dateLastUsed: null, - children: null, - }, - { - id: '4', - parentId: '2', - index: 1, - title: 'Second child bookmark', - url: 'http://child/bookmark/2/', - dateAdded: 3, - dateLastUsed: null, - children: null, - }, - { - id: '5', - parentId: '2', - index: 2, - title: 'Child folder', - url: null, - dateAdded: 2, - dateLastUsed: null, - children: [ - { - id: '6', - parentId: '5', - index: 0, - title: 'Nested bookmark', - url: 'http://nested/bookmark/', - dateAdded: 4, - dateLastUsed: null, - children: null, - }, - ], - }, - ], - }, -]; - suite('General', () => { + const FOLDERS = createTestBookmarks(); let powerBookmarksList: PowerBookmarksListElement; let bookmarksApi: TestBookmarksApiProxy; const priceTrackingProxy = TestMock.fromClass(PriceTrackingBrowserProxyImpl); @@ -104,21 +38,6 @@ PageImageServiceHandlerRemote; let metrics: MetricsTracker; - function getBookmarks() { - return getBookmarksInList(0).concat(getBookmarksInList(1)); - } - - function getBookmarksInList(listIndex: number): - chrome.bookmarks.BookmarkTreeNode[] { - const ironList = - powerBookmarksList.shadowRoot!.querySelector<IronListElement>( - `#shownBookmarksIronList${listIndex}`); - if (!ironList || !ironList.items) { - return []; - } - return ironList.items; - } - function getAddTabButton(): CrButtonElement { return powerBookmarksList.shadowRoot!.querySelector<CrButtonElement>( '#addCurrentTabButton')!; @@ -129,21 +48,10 @@ '.new-folder-row')!; } - function getBookmarkWithId(id: string): chrome.bookmarks.BookmarkTreeNode| - undefined { - return getBookmarks().find((bookmark) => bookmark.id === id); - } - - function getPowerBookmarksRowElement(id: string): PowerBookmarkRowElement| - undefined { - return powerBookmarksList.shadowRoot! - .querySelector<PowerBookmarkRowElement>(`#bookmark-${id}`) || - undefined; - } - function getCrUrlListItemElementWithId(id: string): CrUrlListItemElement| undefined { - const powerBookmarkRowElement = getPowerBookmarksRowElement(id); + const powerBookmarkRowElement = + getPowerBookmarksRowElement(powerBookmarksList, id); if (!powerBookmarkRowElement) { return undefined; } @@ -168,7 +76,7 @@ } async function openBookmark(id: string) { - const bookmark = getBookmarkWithId(id); + const bookmark = getBookmarkWithId(powerBookmarksList, id); assertTrue(!!bookmark); powerBookmarksList.clickBookmarkRowForTests(bookmark); @@ -177,8 +85,10 @@ } async function selectBookmark(id: string) { - const checkboxClicked = - eventToPromise('checkbox-change', getPowerBookmarksRowElement(id)!); + const checkboxClicked = eventToPromise( + 'checkbox-change', + getPowerBookmarksRowElement(powerBookmarksList, id)!, + ); const bookmarkListItem = getCrUrlListItemElementWithId(id); assertTrue(!!bookmarkListItem); await bookmarkListItem.updateComplete; @@ -186,25 +96,6 @@ await checkboxClicked; } - async function initializeUI() { - // Remove all children from document.body - while (document.body.firstChild) { - document.body.removeChild(document.body.firstChild); - } - powerBookmarksList = document.createElement('power-bookmarks-list'); - - // Ensure the PowerBookmarksListElement is given a fixed height to expand - // to. - const parentElement = document.createElement('div'); - parentElement.style.height = '500px'; - parentElement.appendChild(powerBookmarksList); - document.body.appendChild(parentElement); - - await bookmarksApi.whenCalled('getAllBookmarks'); - await waitAfterNextRender(powerBookmarksList); - flush(); - } - setup(async () => { document.body.innerHTML = window.trustedTypes!.emptyHTML; @@ -249,1040 +140,743 @@ isBookmarksInTransportModeEnabled: false, }); - await initializeUI(); + powerBookmarksList = await initializeUi(bookmarksApi); }); - test('GetsAndShowsTopLevelBookmarks', () => { - assertEquals(1, bookmarksApi.getCallCount('getAllBookmarks')); - assertEquals(FOLDERS[1]!.children!.length + 1, getBookmarks().length); - }); - - test('RebuildsKeyboardNavigationOnBoomkmarkNodeAdded', async () => { - await flushTasks(); - - assertEquals( - JSON.stringify( - powerBookmarksList.getKeyboardNavigationServiceforTesting() - .getElementsForTesting() - .map((el: HTMLElement) => el.id)), - JSON.stringify( - ['bookmark-1', 'bookmark-5', 'bookmark-4', 'bookmark-3'])); - - bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ - id: '999', - title: 'New bookmark of current url', - index: 0, - parentId: FOLDERS[1]!.id, - url: powerBookmarksList.getCurrentUrlForTesting()!, - children: null, - dateAdded: null, - dateLastUsed: null, - }); - await microtasksFinished(); - await flushTasks(); - - assertEquals( - JSON.stringify( - powerBookmarksList.getKeyboardNavigationServiceforTesting() - .getElementsForTesting() - .map((el: HTMLElement) => el.id)), - JSON.stringify([ - 'bookmark-1', - 'bookmark-5', - 'bookmark-999', - 'bookmark-4', - 'bookmark-3', - ])); - }); - - test('RebuildsKeyboardNavigationOnRemoved', async () => { - await flushTasks(); - - assertEquals( - JSON.stringify( - powerBookmarksList.getKeyboardNavigationServiceforTesting() - .getElementsForTesting() - .map((el: HTMLElement) => el.id)), - JSON.stringify( - ['bookmark-1', 'bookmark-5', 'bookmark-4', 'bookmark-3'])); - - bookmarksApi.callbackRouterRemote.onBookmarkNodesRemoved(['4']); - await flushTasks(); - await waitAfterNextRender(powerBookmarksList); - - assertEquals( - JSON.stringify( - powerBookmarksList.getKeyboardNavigationServiceforTesting() - .getElementsForTesting() - .map((el: HTMLElement) => el.id)), - JSON.stringify(['bookmark-1', 'bookmark-5', 'bookmark-3'])); - }); - - test('RebuildsKeyboardNavigationMoved', async () => { - await flushTasks(); - - assertEquals( - JSON.stringify( - powerBookmarksList.getKeyboardNavigationServiceforTesting() - .getElementsForTesting() - .map((el: HTMLElement) => el.id)), - JSON.stringify( - ['bookmark-1', 'bookmark-5', 'bookmark-4', 'bookmark-3'])); - - const movedBookmark = FOLDERS[1]!.children![2]!.children![0]!; - assertTrue(!!movedBookmark); - bookmarksApi.callbackRouterRemote.onBookmarkNodeMoved( - /*oldParentId=*/ FOLDERS[1]!.children![2]! - .id, // Moving from child folder. - /*oldIndex=*/ 0, - /*parentId=*/ FOLDERS[1]!.id, // Moving to other bookmarks. - /*index=*/ 0, - ); - await microtasksFinished(); - await flushTasks(); - - assertEquals( - JSON.stringify( - powerBookmarksList.getKeyboardNavigationServiceforTesting() - .getElementsForTesting() - .map((el: HTMLElement) => el.id)), - JSON.stringify([ - 'bookmark-1', - 'bookmark-5', - 'bookmark-6', - 'bookmark-4', - 'bookmark-3', - ])); - }); - - test('DefaultsToSortByNewest', () => { - const bookmarks = getBookmarks(); - assertEquals(4, bookmarks.length); - // All folders should come first - assertEquals('1', bookmarks[0]!.id); - assertEquals('5', bookmarks[1]!.id); - // Newest URL should come next - assertEquals('4', bookmarks[2]!.id); - // Older URL should be last - assertEquals('3', bookmarks[3]!.id); - }); - - test('FiltersBookmarks', async () => { - await openBookmark('5'); - await performSearch('bookmark'); - - // One bookmark matches the query and is in the active folder. - assertEquals(1, getBookmarksInList(0).length); - // Three bookmarks match the query but are not in the active folder. - assertEquals(3, getBookmarksInList(1).length); - assertEquals( - 1, - metrics.count( - 'PowerBookmarks.SidePanel.SearchOrFilter.BookmarksShown', 4)); - - await performSearch('nested'); - - assertEquals(1, getBookmarksInList(0).length); - assertEquals(0, getBookmarksInList(1).length); - assertEquals( - 1, - metrics.count( - 'PowerBookmarks.SidePanel.SearchOrFilter.BookmarksShown', 1)); - - await performSearch('child'); - - assertEquals(0, getBookmarksInList(0).length); - assertEquals(2, getBookmarksInList(1).length); - assertEquals( - 1, - metrics.count( - 'PowerBookmarks.SidePanel.SearchOrFilter.BookmarksShown', 2)); - }); - - test('UpdatesChangedBookmarks', async () => { - const changedBookmark = FOLDERS[1]!.children![0]!; - bookmarksApi.callbackRouterRemote.onBookmarkNodeChanged( - changedBookmark.id, 'New title', 'http://new/url'); - await flushTasks(); - - const bookmark = getBookmarkWithId('3'); - assertTrue(!!bookmark); - - assertEquals('New title', bookmark.title); - assertEquals('http://new/url', bookmark.url); - - const crUrlListItemElement = getCrUrlListItemElementWithId('3'); - assertTrue(!!crUrlListItemElement); - await crUrlListItemElement.updateComplete; - - assertEquals('New title', crUrlListItemElement.title); - }); - - test('UpdatesChangedBookmarksWithFilter', async () => { - await performSearch('abc'); - - assertEquals(0, getBookmarks().length); - - const changedBookmark = FOLDERS[1]!.children![0]!; - bookmarksApi.callbackRouterRemote.onBookmarkNodeChanged( - changedBookmark.id, 'abcdef', 'http://new/url'); - await flushTasks(); - - // Bookmark matches search term and should display. - assertEquals(1, getBookmarks().length); - - bookmarksApi.callbackRouterRemote.onBookmarkNodeChanged( - changedBookmark.id, 'New title', 'http://new/url'); - await flushTasks(); - - // Bookmark no longer matches search term and should not display. - assertEquals(0, getBookmarks().length); - }); - - test('DefaultsAddTabButtonEnabled', () => { - const btn = getAddTabButton(); - // The AddTabButton is enabled because the current url is not bookmarked. - assertFalse(btn.disabled); - }); - - test('UpdatesAddTabButton', async () => { - bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ - id: '999', - title: 'New bookmark of current url', - index: 0, - parentId: FOLDERS[1]!.id, - url: powerBookmarksList.getCurrentUrlForTesting()!, - children: null, - dateAdded: null, - dateLastUsed: null, - }); - await flushTasks(); - - let btn = getAddTabButton(); - assertTrue(btn.disabled); - - bookmarksApi.callbackRouterRemote.onBookmarkNodesRemoved(['999']); - await flushTasks(); - - btn = getAddTabButton(); - assertFalse(btn.disabled); - }); - - test('AddNewFolderClicked', async () => { - const addNewFolderButton = getAddNewFolderButton(); - assertTrue(!!addNewFolderButton); - assertFalse(addNewFolderButton.disabled); - - addNewFolderButton.click(); - await bookmarksApi.whenCalled('createFolder'); - }); - - test('AddsCreatedBookmark', async () => { - bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ - id: '999', - title: 'New bookmark', - index: 0, - parentId: FOLDERS[1]!.id, - url: 'http://new/bookmark', - children: null, - dateAdded: null, - dateLastUsed: null, - }); - await flushTasks(); - - const bookmarks = getBookmarks(); - assertEquals(5, bookmarks.length); - }); - - test('AddsCreatedBookmarkForNewFolder', async () => { - // Create a new folder without a children array. - bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ - id: '1000', - title: 'New folder', - index: 0, - parentId: FOLDERS[1]!.id, - url: null, - children: null, - dateAdded: null, - dateLastUsed: null, - }); - await flushTasks(); - - // Create a new bookmark within that folder. - bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ - id: '1001', - title: 'New bookmark in new folder', - index: 0, - parentId: '1000', - url: 'http://google.com', - children: null, - dateAdded: null, - dateLastUsed: null, - }); - await flushTasks(); - - const bookmarks = getBookmarks(); - assertEquals(5, bookmarks.length); - - const newFolder = getBookmarkWithId('1000'); - assertTrue(!!newFolder); - - assertEquals(1, newFolder.children!.length); - }); - - test('AddsCreatedBookmarkWithFilter', async () => { - await openBookmark('5'); - await performSearch('bookmark'); - - assertEquals(1, getBookmarksInList(0).length); - assertEquals(3, getBookmarksInList(1).length); - - bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ - id: '123', - title: 'New bookmark', - index: 0, - parentId: '5', - url: 'http://new/bookmark', - children: null, - dateAdded: null, - dateLastUsed: null, - }); - await flushTasks(); - - // New bookmark matches search term and is under active folder, gets - // displayed in primary list - assertTrue(!!getBookmarkWithId('123')); - assertEquals(2, getBookmarksInList(0).length); - assertEquals(3, getBookmarksInList(1).length); - - bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ - id: '456', - title: 'foo', - index: 0, - parentId: FOLDERS[1]!.id, - url: 'http://foo', - children: null, - dateAdded: null, - dateLastUsed: null, - }); - await flushTasks(); - - // New bookmark does not match search term, doesn't get displayed - assertFalse(!!getBookmarkWithId('456')); - assertEquals(2, getBookmarksInList(0).length); - assertEquals(3, getBookmarksInList(1).length); - - bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ - id: '789', - title: 'Bookmark', - index: 0, - parentId: FOLDERS[1]!.id, - url: 'http://bookmark', - children: null, - dateAdded: null, - dateLastUsed: null, - }); - await flushTasks(); - - // New bookmark matches search term and is not under active folder, gets - // displayed in secondary list - assertTrue(!!getBookmarkWithId('789')); - assertEquals(2, getBookmarksInList(0).length); - assertEquals(4, getBookmarksInList(1).length); - }); - - test('MovesBookmarks', async () => { - const movedBookmark = FOLDERS[1]!.children![2]!.children![0]!; - assertTrue(!!movedBookmark); - bookmarksApi.callbackRouterRemote.onBookmarkNodeMoved( - /*oldParentId=*/ FOLDERS[1]!.children![2]! - .id, // Moving from child folder. - /*oldIndex=*/ 0, - /*parentId=*/ FOLDERS[1]!.id, // Moving to other bookmarks. - /*index=*/ 0, - ); - await flushTasks(); - - const bookmarks = getBookmarks(); - assertEquals(5, bookmarks.length); - - const childFolder = getBookmarkWithId('5'); - assertTrue(!!childFolder); - assertEquals(0, childFolder.children!.length); - }); - - test('MovesBookmarksIntoNewFolder', async () => { - // Create a new folder without a children array. - bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ - id: '1000', - title: 'New folder', - index: 0, - parentId: FOLDERS[1]!.id, - url: null, - children: null, - dateAdded: null, - dateLastUsed: null, - }); - await flushTasks(); - - const movedBookmark = FOLDERS[1]!.children![2]!.children![0]!; - assertTrue(!!movedBookmark); - bookmarksApi.callbackRouterRemote.onBookmarkNodeMoved( - /*oldParentId=*/ FOLDERS[1]!.children![2]!.id, - /*oldIndex=*/ 0, - /*parentId=*/ '1000', - /*index=*/ 0, - ); - await flushTasks(); - - const newFolder = getBookmarkWithId('1000'); - assertTrue(!!newFolder); - - assertEquals(1, newFolder.children!.length); - }); - - test('MovesBookmarkWithFilter', async () => { - await openBookmark('5'); - await performSearch('bookmark'); - - assertEquals(1, getBookmarksInList(0).length); - assertEquals(3, getBookmarksInList(1).length); - - const movedBookmark = getBookmarkWithId('6'); - assertTrue(!!movedBookmark); - bookmarksApi.callbackRouterRemote.onBookmarkNodeMoved( - /*oldParentId=*/ FOLDERS[1]!.children![2]! - .id, // Moving from child folder. - /*oldIndex=*/ 0, - /*parentId=*/ FOLDERS[1]!.id, // Moving to other bookmarks. - /*index=*/ 0, - ); - await flushTasks(); - - // Moved bookmark is no longer in active folder, should move from primary - // to secondary list. - assertEquals(0, getBookmarksInList(0).length); - assertEquals(4, getBookmarksInList(1).length); - - bookmarksApi.callbackRouterRemote.onBookmarkNodeMoved( - /*oldParentId=*/ FOLDERS[1]!.id, // Moving from other bookmarks. - /*oldIndex=*/ 0, - /*parentId=*/ FOLDERS[1]!.children![2]!.id, // Moving to child folder. - /*index=*/ 0, - ); - await flushTasks(); - - // Moved bookmark is now in active folder, should move from secondary - // to primary list. - assertEquals(1, getBookmarksInList(0).length); - assertEquals(3, getBookmarksInList(1).length); - }); - - test('RemovesBookmark', async () => { - const originalShownBookmarkCount = getBookmarks().length; - - bookmarksApi.callbackRouterRemote.onBookmarkNodesRemoved(['3']); - await flushTasks(); - - const removedBookmark = getBookmarkWithId('3'); - assertTrue(!removedBookmark); - - assertEquals(originalShownBookmarkCount - 1, getBookmarks().length); - }); - - test('SetsCompactDescription', () => { - const folder = getBookmarkWithId('5'); - assertTrue(!!folder); - - assertEquals( - '(1)', - getPowerBookmarksRowElement('5')?.getBookmarkDescriptionForTests( - folder)); - }); - - test('SetsExpandedDescription', () => { - const viewButton: HTMLElement = - powerBookmarksList.shadowRoot!.querySelector('#viewButton')!; - viewButton.click(); - - const folder = getBookmarkWithId('4'); - assertTrue(!!folder); - - assertEquals( - 'child', - getPowerBookmarksRowElement('4')?.getBookmarkDescriptionForTests( - folder)); - }); - - test('SetsExpandedSearchResultDescription', async () => { - const viewButton = - powerBookmarksList.shadowRoot!.querySelector<HTMLElement>( - '#viewButton')!; - viewButton.click(); - - await performSearch('child bookmark'); - - const folder = getBookmarkWithId('4'); - assertTrue(!!folder); - - assertEquals( - 'child - All Bookmarks', - getPowerBookmarksRowElement('4')?.getBookmarkDescriptionForTests( - folder)); - }); - - test('RenamesBookmark', async () => { - const renamedBookmarkId = '4'; - powerBookmarksList.setRenamingIdForTests(renamedBookmarkId); - - await flushTasks(); - - const rowElement = getPowerBookmarksRowElement(renamedBookmarkId); - assertTrue(!!rowElement); - let input = rowElement.shadowRoot.querySelector<CrInputElement>('cr-input'); - assertTrue(!!input); - - const inputChange = eventToPromise('input-change', rowElement); - - const newName = 'foo'; - input.value = newName; - input.inputElement.dispatchEvent(new Event('change')); - - await inputChange; - await flushTasks(); - - // Committing a new input value should rename the bookmark and remove the - // input. - assertEquals(1, bookmarksApi.getCallCount('renameBookmark')); - assertEquals( - renamedBookmarkId, bookmarksApi.getArgs('renameBookmark')[0][0]); - assertEquals(newName, bookmarksApi.getArgs('renameBookmark')[0][1]); - input = rowElement.shadowRoot.querySelector<CrInputElement>('cr-input'); - assertFalse(!!input); - }); - - test('BlursRenameInput', async () => { - const renamedBookmarkId = '4'; - powerBookmarksList.setRenamingIdForTests(renamedBookmarkId); - - await flushTasks(); - - const rowElement = - powerBookmarksList.shadowRoot!.querySelector<PowerBookmarkRowElement>( - `#bookmark-${renamedBookmarkId}`); - assertTrue(!!rowElement); - let input = rowElement.shadowRoot.querySelector<CrInputElement>('cr-input'); - const inputBlurred = eventToPromise( - 'input-change', getPowerBookmarksRowElement(renamedBookmarkId)!); - assertTrue(!!input); - input.inputElement.blur(); - await inputBlurred; - - await flushTasks(); - - // Blurring the input should remove it. - input = rowElement.shadowRoot.querySelector<CrInputElement>('cr-input'); - assertFalse(!!input); - }); - - test('ShowsFolderImages', () => { - const viewButton: HTMLElement = - powerBookmarksList.shadowRoot!.querySelector('#viewButton')!; - viewButton.click(); - - flush(); - - const bookmarksBarFolderElement = getCrUrlListItemElementWithId('1'); - assertTrue(!!bookmarksBarFolderElement); - assertEquals(0, bookmarksBarFolderElement.imageUrls.length); - - const childFolderElement = getCrUrlListItemElementWithId('5'); - assertTrue(!!childFolderElement); - assertNotEquals(0, childFolderElement.imageUrls.length); - }); - - test('DeletesSelectedBookmarks', async () => { - const editButton: HTMLElement = - powerBookmarksList.shadowRoot!.querySelector('#editButton')!; - editButton.click(); - - flush(); - - await selectBookmark('3'); - await selectBookmark('5'); - - flush(); - - const deleteButton: HTMLButtonElement = - powerBookmarksList.shadowRoot!.querySelector('#deleteButton')!; - assertFalse(deleteButton.disabled); - deleteButton.click(); - - flush(); - - assertEquals(1, bookmarksApi.getCallCount('deleteBookmarks')); - assertEquals('3', bookmarksApi.getArgs('deleteBookmarks')[0][0]); - assertEquals('5', bookmarksApi.getArgs('deleteBookmarks')[0][1]); - }); - - test('EditBookmarkWithBookmarksInTransportModeDisabled', async () => { - const bookmark = getBookmarkWithId('3')!; - const contextMenu = powerBookmarksList.$.contextMenu; - const editClicked = eventToPromise('edit-clicked', contextMenu); - - // Open the context menu. - contextMenu.showAtPosition( - new MouseEvent('click'), [bookmark], false, false); - await waitAfterNextRender(contextMenu); - - // Get the edit option in the menu. - const menuItems = - contextMenu.shadowRoot!.querySelectorAll('.dropdown-item'); - assertEquals( - menuItems[3]!.textContent!.includes(loadTimeData.getString('menuEdit')), - true); - const editItem = contextMenu.shadowRoot!.querySelectorAll<HTMLElement>( - '.dropdown-item')[3]!; - - // Click on edit and wait for the call to propagate. - editItem.click(); - await editClicked; - await flushTasks(); - - // The edit dialog is opened. - const editDialog = powerBookmarksList.$.editDialog; - assertTrue(editDialog.$.dialog.open); - assertEquals(bookmark.title, editDialog.$.nameInput.inputElement.value); - assertEquals(bookmark.url, editDialog.$.urlInput.inputElement.value); - }); - - test('MoveBookmarksWithBookmarksInTransportModeDisabled', async () => { - const bookmarks = [getBookmarkWithId('3')!, getBookmarkWithId('5')!]; - const contextMenu = powerBookmarksList.$.contextMenu; - const editClicked = eventToPromise('edit-clicked', contextMenu); - - // Open the context menu. - contextMenu.showAtPosition( - new MouseEvent('click'), bookmarks, false, false); - await waitAfterNextRender(contextMenu); - - // Get the move option in the menu. - const menuItems = - contextMenu.shadowRoot!.querySelectorAll('.dropdown-item'); - assertEquals( - menuItems[4]!.textContent!.includes( - loadTimeData.getString('tooltipMove')), - true); - const moveItem = contextMenu.shadowRoot!.querySelectorAll<HTMLElement>( - '.dropdown-item')[4]!; - - // Click on move and wait for the call to propagate. - moveItem.click(); - await editClicked; - await flushTasks(); - - // The edit dialog is opened. - const editDialog = powerBookmarksList.$.editDialog; - assertTrue(editDialog.$.dialog.open); - }); - - test('LogsBookmarkCountMetric', async () => { - // Initially should have 4 bookmarks shown. - assertEquals( - 1, metrics.count('PowerBookmarks.SidePanel.BookmarksShown', 4)); - - await openBookmark('5'); - - // Folder with id 5 only has 1 bookmark shown. - assertEquals( - 1, metrics.count('PowerBookmarks.SidePanel.BookmarksShown', 1)); - }); - - test('TogglesSectionVisibilityAndEmptyStates', async () => { - const search = powerBookmarksList.$.searchField; - const labels = powerBookmarksList.$.labels; - const heading = powerBookmarksList.$.heading; - const folderEmptyState = powerBookmarksList.$.folderEmptyState; - const bookmarksList = powerBookmarksList.$.bookmarks; - const topLevelEmptyState = powerBookmarksList.$.topLevelEmptyState; - const footer = powerBookmarksList.$.footer; - assertEquals( - loadTimeData.getString('emptyTitle'), topLevelEmptyState.heading); - assertEquals(loadTimeData.getString('emptyBody'), topLevelEmptyState.body); - - // Has bookmarks. - assertFalse(isHidden(search)); - assertTrue(isHidden(labels)); - assertFalse(isHidden(heading)); - assertTrue(isHidden(folderEmptyState)); - assertFalse(isHidden(bookmarksList)); - assertTrue(isHidden(topLevelEmptyState)); - assertFalse(isHidden(footer)); - - // Opening an empty folder. - await openBookmark('1'); - - assertFalse(isHidden(search)); - assertTrue(isHidden(labels)); - assertFalse(isHidden(heading)); - assertFalse(isHidden(folderEmptyState)); - assertTrue(isHidden(bookmarksList)); - assertTrue(isHidden(topLevelEmptyState)); - assertFalse(isHidden(footer)); - - // A search with no results. - const searchField = - powerBookmarksList.shadowRoot!.querySelector('cr-toolbar-search-field'); - assertTrue(!!searchField); - searchField.$.searchInput.value = 'abcdef'; - searchField.onSearchTermSearch(); - await flushTasks(); - assertEquals( - loadTimeData.getString('emptyTitleSearch'), topLevelEmptyState.heading); - assertFalse(isHidden(search)); - assertTrue(isHidden(labels)); - assertTrue(isHidden(heading)); - assertTrue(isHidden(folderEmptyState)); - assertTrue(isHidden(bookmarksList)); - assertFalse(isHidden(topLevelEmptyState)); - assertTrue(isHidden(footer)); - - // Adding a tracked product shows filter chips. - const newProduct = { - bookmarkId: BigInt(3), - info: { - title: 'Product Baz', - clusterTitle: 'Product Cluster Baz', - domain: 'baz.com', - imageUrl: {url: 'https://baz.com/image'}, - productUrl: {url: 'https://baz.com/product'}, - currentPrice: '$56', - previousPrice: '$78', - clusterId: BigInt(12345), - categoryLabels: [], - priceSummary: '', - }, - }; - - callbackRouterRemote.priceTrackedForBookmark(newProduct); - await flushTasks(); - assertFalse(isHidden(labels)); - }); -}); - -suite('TransportMode', () => { - let powerBookmarksList: PowerBookmarksListElement; - let bookmarksApi: TestBookmarksApiProxy; - const priceTrackingProxy = TestMock.fromClass(PriceTrackingBrowserProxyImpl); - let imageServiceHandler: TestMock<PageImageServiceHandlerRemote>& - PageImageServiceHandlerRemote; - - function getBookmarks() { - return getBookmarksInList(0).concat(getBookmarksInList(1)); - } - - function getBookmarksInList(listIndex: number): - chrome.bookmarks.BookmarkTreeNode[] { - const ironList = - powerBookmarksList.shadowRoot!.querySelector<IronListElement>( - `#shownBookmarksIronList${listIndex}`); - if (!ironList || !ironList.items) { - return []; - } - return ironList.items; - } - - function getBookmarkWithId(id: string): chrome.bookmarks.BookmarkTreeNode| - undefined { - return getBookmarks().find((bookmark) => bookmark.id === id); - } - - async function initializeUI() { - // Remove all children from document.body - while (document.body.firstChild) { - document.body.removeChild(document.body.firstChild); - } - powerBookmarksList = document.createElement('power-bookmarks-list'); - - // Ensure the PowerBookmarksListElement is given a fixed height to expand - // to. - const parentElement = document.createElement('div'); - parentElement.style.height = '500px'; - parentElement.appendChild(powerBookmarksList); - document.body.appendChild(parentElement); - - await bookmarksApi.whenCalled('getAllBookmarks'); - await waitAfterNextRender(powerBookmarksList); - flush(); - } - - setup(async () => { - document.body.innerHTML = window.trustedTypes!.emptyHTML; - - bookmarksApi = new TestBookmarksApiProxy(); - bookmarksApi.setAllBookmarks(structuredClone(FOLDERS)); - BookmarksApiProxyImpl.setInstance(bookmarksApi); - - priceTrackingProxy.reset(); - const callbackRouter = new PageCallbackRouter(); - priceTrackingProxy.setResultFor('getCallbackRouter', callbackRouter); - priceTrackingProxy.setResultFor( - 'getAllPriceTrackedBookmarkProductInfo', - Promise.resolve({productInfos: []})); - priceTrackingProxy.setResultFor( - 'getAllShoppingBookmarkProductInfo', - Promise.resolve({productInfos: []})); - priceTrackingProxy.setResultFor( - 'getShoppingCollectionBookmarkFolderId', - Promise.resolve({collectionId: BigInt(-1)})); - PriceTrackingBrowserProxyImpl.setInstance(priceTrackingProxy); - - imageServiceHandler = TestMock.fromClass(PageImageServiceHandlerRemote); - PageImageServiceBrowserProxy.setInstance( - new PageImageServiceBrowserProxy(imageServiceHandler)); - imageServiceHandler.setResultFor('getPageImageUrl', Promise.resolve({ - result: {imageUrl: {url: 'https://example.com/image.png'}}, - })); - - loadTimeData.overrideValues({ - sortOrder: SortOrder.kNewest, - viewType: ViewType.kCompact, - emptyTitle: 'empty title base', - emptyTitleSearch: 'empty title search', - emptyTitleFolder: 'folder is empty', - emptyBodyFolder: 'folder body', - emptyTitleGuest: 'guest title', - emptyBodyGuest: 'guest body', - bookmarksTreeViewEnabled: false, - isBookmarksInTransportModeEnabled: true, + suite('Part1', function() { + test('GetsAndShowsTopLevelBookmarks', () => { + assertEquals(1, bookmarksApi.getCallCount('getAllBookmarks')); + assertEquals( + FOLDERS[1]!.children!.length + 1, + getBookmarks(powerBookmarksList).length); }); - await initializeUI(); - }); + test('RebuildsKeyboardNavigationOnBoomkmarkNodeAdded', async () => { + await flushTasks(); - test('EditBookmarkWithBookmarksInTransportModeEnabled', async () => { - const bookmarkId = '3'; - const contextMenu = powerBookmarksList.$.contextMenu; - const editClicked = eventToPromise('edit-clicked', contextMenu); + assertEquals( + JSON.stringify( + powerBookmarksList.getKeyboardNavigationServiceforTesting() + .getElementsForTesting() + .map((el: HTMLElement) => el.id)), + JSON.stringify( + ['bookmark-1', 'bookmark-5', 'bookmark-4', 'bookmark-3'])); - // Open the context menu. - contextMenu.showAtPosition( - new MouseEvent('click'), [getBookmarkWithId(bookmarkId)!], false, - false); - await waitAfterNextRender(contextMenu); + bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ + id: '999', + title: 'New bookmark of current url', + index: 0, + parentId: FOLDERS[1]!.id, + url: powerBookmarksList.getCurrentUrlForTesting()!, + children: null, + dateAdded: null, + dateLastUsed: null, + }); + await microtasksFinished(); + await flushTasks(); - // Get the edit option in the menu. - const menuItems = - contextMenu.shadowRoot!.querySelectorAll('.dropdown-item'); - assertEquals( - menuItems[3]!.textContent!.includes(loadTimeData.getString('menuEdit')), - true); - const editItem = contextMenu.shadowRoot!.querySelectorAll<HTMLElement>( - '.dropdown-item')[3]!; - - // Click on edit and wait for the call to propagate. - editItem.click(); - await editClicked; - await flushTasks(); - - // The native edit dialog is opened. - assertEquals(1, bookmarksApi.getCallCount('contextMenuEdit')); - assertEquals(bookmarkId, bookmarksApi.getArgs('contextMenuEdit')[0][0][0]); - assertEquals( - ActionSource.kBookmark, bookmarksApi.getArgs('contextMenuEdit')[0][1]); - }); - - - test('MoveBookmarksWithBookmarksInTransportModeEnabled', async () => { - const bookmarkId = '3'; - const bookmarks = [getBookmarkWithId(bookmarkId)!, getBookmarkWithId('5')!]; - const contextMenu = powerBookmarksList.$.contextMenu; - const editClicked = eventToPromise('edit-clicked', contextMenu); - - // Open the context menu. - contextMenu.showAtPosition( - new MouseEvent('click'), bookmarks, false, false); - await waitAfterNextRender(contextMenu); - - // Get the move option in the menu. - const menuItems = - contextMenu.shadowRoot!.querySelectorAll('.dropdown-item'); - assertEquals( - menuItems[4]!.textContent!.includes( - loadTimeData.getString('tooltipMove')), - true); - const moveItem = contextMenu.shadowRoot!.querySelectorAll<HTMLElement>( - '.dropdown-item')[4]!; - - // Click on move and wait for the call to propagate. - moveItem.click(); - await editClicked; - await flushTasks(); - - // The native move dialog is opened. - assertEquals(1, bookmarksApi.getCallCount('contextMenuMove')); - assertEquals(bookmarkId, bookmarksApi.getArgs('contextMenuMove')[0][0][0]); - assertEquals( - ActionSource.kBookmark, bookmarksApi.getArgs('contextMenuMove')[0][1]); - }); -}); - -suite('TreeView', () => { - let powerBookmarksList: PowerBookmarksListElement; - let bookmarksApi: TestBookmarksApiProxy; - const priceTrackingProxy = TestMock.fromClass(PriceTrackingBrowserProxyImpl); - let imageServiceHandler: TestMock<PageImageServiceHandlerRemote>& - PageImageServiceHandlerRemote; - - function getBookmarks() { - return getBookmarksInList(0).concat(getBookmarksInList(1)); - } - - function getBookmarksInList(listIndex: number): - chrome.bookmarks.BookmarkTreeNode[] { - const ironList = - powerBookmarksList.shadowRoot!.querySelector<IronListElement>( - `#shownBookmarksIronList${listIndex}`); - if (!ironList || !ironList.items) { - return []; - } - return ironList.items; - } - - function getPowerBookmarksRowElement(id: string): PowerBookmarkRowElement| - undefined { - return powerBookmarksList.shadowRoot! - .querySelector<PowerBookmarkRowElement>(`#bookmark-${id}`) || - undefined; - } - - async function initializeUI() { - // Remove all children from document.body - while (document.body.firstChild) { - document.body.removeChild(document.body.firstChild); - } - powerBookmarksList = document.createElement('power-bookmarks-list'); - - // Ensure the PowerBookmarksListElement is given a fixed height to expand - // to. - const parentElement = document.createElement('div'); - parentElement.style.height = '500px'; - parentElement.appendChild(powerBookmarksList); - document.body.appendChild(parentElement); - - await bookmarksApi.whenCalled('getAllBookmarks'); - await waitAfterNextRender(powerBookmarksList); - flush(); - } - - setup(async () => { - document.body.innerHTML = window.trustedTypes!.emptyHTML; - - bookmarksApi = new TestBookmarksApiProxy(); - bookmarksApi.setAllBookmarks(structuredClone(FOLDERS)); - BookmarksApiProxyImpl.setInstance(bookmarksApi); - - priceTrackingProxy.reset(); - const callbackRouter = new PageCallbackRouter(); - priceTrackingProxy.setResultFor('getCallbackRouter', callbackRouter); - priceTrackingProxy.setResultFor( - 'getAllPriceTrackedBookmarkProductInfo', - Promise.resolve({productInfos: []})); - priceTrackingProxy.setResultFor( - 'getAllShoppingBookmarkProductInfo', - Promise.resolve({productInfos: []})); - priceTrackingProxy.setResultFor( - 'getShoppingCollectionBookmarkFolderId', - Promise.resolve({collectionId: BigInt(-1)})); - PriceTrackingBrowserProxyImpl.setInstance(priceTrackingProxy); - - imageServiceHandler = TestMock.fromClass(PageImageServiceHandlerRemote); - PageImageServiceBrowserProxy.setInstance( - new PageImageServiceBrowserProxy(imageServiceHandler)); - imageServiceHandler.setResultFor('getPageImageUrl', Promise.resolve({ - result: {imageUrl: {url: 'https://example.com/image.png'}}, - })); - - loadTimeData.overrideValues({ - sortOrder: SortOrder.kNewest, - viewType: ViewType.kCompact, - emptyTitle: 'empty title base', - emptyTitleSearch: 'empty title search', - emptyTitleFolder: 'folder is empty', - emptyBodyFolder: 'folder body', - emptyTitleGuest: 'guest title', - emptyBodyGuest: 'guest body', - bookmarksTreeViewEnabled: true, + assertEquals( + JSON.stringify( + powerBookmarksList.getKeyboardNavigationServiceforTesting() + .getElementsForTesting() + .map((el: HTMLElement) => el.id)), + JSON.stringify([ + 'bookmark-1', + 'bookmark-5', + 'bookmark-999', + 'bookmark-4', + 'bookmark-3', + ])); }); - await initializeUI(); + test('RebuildsKeyboardNavigationOnRemoved', async () => { + await flushTasks(); + + assertEquals( + JSON.stringify( + powerBookmarksList.getKeyboardNavigationServiceforTesting() + .getElementsForTesting() + .map((el: HTMLElement) => el.id)), + JSON.stringify( + ['bookmark-1', 'bookmark-5', 'bookmark-4', 'bookmark-3'])); + + bookmarksApi.callbackRouterRemote.onBookmarkNodesRemoved(['4']); + await flushTasks(); + await waitAfterNextRender(powerBookmarksList); + + assertEquals( + JSON.stringify( + powerBookmarksList.getKeyboardNavigationServiceforTesting() + .getElementsForTesting() + .map((el: HTMLElement) => el.id)), + JSON.stringify(['bookmark-1', 'bookmark-5', 'bookmark-3'])); + }); + + test('RebuildsKeyboardNavigationMoved', async () => { + await flushTasks(); + + assertEquals( + JSON.stringify( + powerBookmarksList.getKeyboardNavigationServiceforTesting() + .getElementsForTesting() + .map((el: HTMLElement) => el.id)), + JSON.stringify( + ['bookmark-1', 'bookmark-5', 'bookmark-4', 'bookmark-3'])); + + const movedBookmark = FOLDERS[1]!.children![2]!.children![0]!; + assertTrue(!!movedBookmark); + bookmarksApi.callbackRouterRemote.onBookmarkNodeMoved( + /*oldParentId=*/ FOLDERS[1]!.children![2]! + .id, // Moving from child folder. + /*oldIndex=*/ 0, + /*parentId=*/ FOLDERS[1]!.id, // Moving to other bookmarks. + /*index=*/ 0, + ); + await microtasksFinished(); + await flushTasks(); + + assertEquals( + JSON.stringify( + powerBookmarksList.getKeyboardNavigationServiceforTesting() + .getElementsForTesting() + .map((el: HTMLElement) => el.id)), + JSON.stringify([ + 'bookmark-1', + 'bookmark-5', + 'bookmark-6', + 'bookmark-4', + 'bookmark-3', + ])); + }); + + test('DefaultsToSortByNewest', () => { + const bookmarks = getBookmarks(powerBookmarksList); + assertEquals(4, bookmarks.length); + // All folders should come first + assertEquals('1', bookmarks[0]!.id); + assertEquals('5', bookmarks[1]!.id); + // Newest URL should come next + assertEquals('4', bookmarks[2]!.id); + // Older URL should be last + assertEquals('3', bookmarks[3]!.id); + }); + + test('FiltersBookmarks', async () => { + await openBookmark('5'); + await performSearch('bookmark'); + + // One bookmark matches the query and is in the active folder. + assertEquals(1, getBookmarksInList(powerBookmarksList, 0).length); + // Three bookmarks match the query but are not in the active folder. + assertEquals(3, getBookmarksInList(powerBookmarksList, 1).length); + assertEquals( + 1, + metrics.count( + 'PowerBookmarks.SidePanel.SearchOrFilter.BookmarksShown', 4)); + + await performSearch('nested'); + + assertEquals(1, getBookmarksInList(powerBookmarksList, 0).length); + assertEquals(0, getBookmarksInList(powerBookmarksList, 1).length); + assertEquals( + 1, + metrics.count( + 'PowerBookmarks.SidePanel.SearchOrFilter.BookmarksShown', 1)); + + await performSearch('child'); + + assertEquals(0, getBookmarksInList(powerBookmarksList, 0).length); + assertEquals(2, getBookmarksInList(powerBookmarksList, 1).length); + assertEquals( + 1, + metrics.count( + 'PowerBookmarks.SidePanel.SearchOrFilter.BookmarksShown', 2)); + }); + + test('UpdatesChangedBookmarks', async () => { + const changedBookmark = FOLDERS[1]!.children![0]!; + bookmarksApi.callbackRouterRemote.onBookmarkNodeChanged( + changedBookmark.id, 'New title', 'http://new/url'); + await flushTasks(); + + const bookmark = getBookmarkWithId(powerBookmarksList, '3'); + assertTrue(!!bookmark); + + assertEquals('New title', bookmark.title); + assertEquals('http://new/url', bookmark.url); + + const crUrlListItemElement = getCrUrlListItemElementWithId('3'); + assertTrue(!!crUrlListItemElement); + await crUrlListItemElement.updateComplete; + + assertEquals('New title', crUrlListItemElement.title); + }); + + test('UpdatesChangedBookmarksWithFilter', async () => { + await performSearch('abc'); + + assertEquals(0, getBookmarks(powerBookmarksList).length); + + const changedBookmark = FOLDERS[1]!.children![0]!; + bookmarksApi.callbackRouterRemote.onBookmarkNodeChanged( + changedBookmark.id, 'abcdef', 'http://new/url'); + await flushTasks(); + + // Bookmark matches search term and should display. + assertEquals(1, getBookmarks(powerBookmarksList).length); + + bookmarksApi.callbackRouterRemote.onBookmarkNodeChanged( + changedBookmark.id, 'New title', 'http://new/url'); + await flushTasks(); + + // Bookmark no longer matches search term and should not display. + assertEquals(0, getBookmarks(powerBookmarksList).length); + }); + + test('DefaultsAddTabButtonEnabled', () => { + const btn = getAddTabButton(); + // The AddTabButton is enabled because the current url is not bookmarked. + assertFalse(btn.disabled); + }); + + test('UpdatesAddTabButton', async () => { + bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ + id: '999', + title: 'New bookmark of current url', + index: 0, + parentId: FOLDERS[1]!.id, + url: powerBookmarksList.getCurrentUrlForTesting()!, + children: null, + dateAdded: null, + dateLastUsed: null, + }); + await flushTasks(); + + let btn = getAddTabButton(); + assertTrue(btn.disabled); + + bookmarksApi.callbackRouterRemote.onBookmarkNodesRemoved(['999']); + await flushTasks(); + + btn = getAddTabButton(); + assertFalse(btn.disabled); + }); + + test('AddNewFolderClicked', async () => { + const addNewFolderButton = getAddNewFolderButton(); + assertTrue(!!addNewFolderButton); + assertFalse(addNewFolderButton.disabled); + + addNewFolderButton.click(); + await bookmarksApi.whenCalled('createFolder'); + }); + + test('AddsCreatedBookmark', async () => { + bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ + id: '999', + title: 'New bookmark', + index: 0, + parentId: FOLDERS[1]!.id, + url: 'http://new/bookmark', + children: null, + dateAdded: null, + dateLastUsed: null, + }); + await flushTasks(); + + const bookmarks = getBookmarks(powerBookmarksList); + assertEquals(5, bookmarks.length); + }); + + test('AddsCreatedBookmarkForNewFolder', async () => { + // Create a new folder without a children array. + bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ + id: '1000', + title: 'New folder', + index: 0, + parentId: FOLDERS[1]!.id, + url: null, + children: null, + dateAdded: null, + dateLastUsed: null, + }); + await flushTasks(); + + // Create a new bookmark within that folder. + bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ + id: '1001', + title: 'New bookmark in new folder', + index: 0, + parentId: '1000', + url: 'http://google.com', + children: null, + dateAdded: null, + dateLastUsed: null, + }); + await flushTasks(); + + const bookmarks = getBookmarks(powerBookmarksList); + assertEquals(5, bookmarks.length); + + const newFolder = getBookmarkWithId(powerBookmarksList, '1000'); + assertTrue(!!newFolder); + + assertEquals(1, newFolder.children!.length); + }); + + test('AddsCreatedBookmarkWithFilter', async () => { + await openBookmark('5'); + await performSearch('bookmark'); + + assertEquals(1, getBookmarksInList(powerBookmarksList, 0).length); + assertEquals(3, getBookmarksInList(powerBookmarksList, 1).length); + + bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ + id: '123', + title: 'New bookmark', + index: 0, + parentId: '5', + url: 'http://new/bookmark', + children: null, + dateAdded: null, + dateLastUsed: null, + }); + await flushTasks(); + + // New bookmark matches search term and is under active folder, gets + // displayed in primary list + assertTrue(!!getBookmarkWithId(powerBookmarksList, '123')); + assertEquals(2, getBookmarksInList(powerBookmarksList, 0).length); + assertEquals(3, getBookmarksInList(powerBookmarksList, 1).length); + + bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ + id: '456', + title: 'foo', + index: 0, + parentId: FOLDERS[1]!.id, + url: 'http://foo', + children: null, + dateAdded: null, + dateLastUsed: null, + }); + await flushTasks(); + + // New bookmark does not match search term, doesn't get displayed + assertFalse(!!getBookmarkWithId(powerBookmarksList, '456')); + assertEquals(2, getBookmarksInList(powerBookmarksList, 0).length); + assertEquals(3, getBookmarksInList(powerBookmarksList, 1).length); + + bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ + id: '789', + title: 'Bookmark', + index: 0, + parentId: FOLDERS[1]!.id, + url: 'http://bookmark', + children: null, + dateAdded: null, + dateLastUsed: null, + }); + await flushTasks(); + + // New bookmark matches search term and is not under active folder, gets + // displayed in secondary list + assertTrue(!!getBookmarkWithId(powerBookmarksList, '789')); + assertEquals(2, getBookmarksInList(powerBookmarksList, 0).length); + assertEquals(4, getBookmarksInList(powerBookmarksList, 1).length); + }); }); - test('ShowsExpandButtonForFolders', () => { - const folderElement = getPowerBookmarksRowElement('5'); - assertTrue(!!folderElement); + suite('Part2', function() { + test('MovesBookmarks', async () => { + const movedBookmark = FOLDERS[1]!.children![2]!.children![0]!; + assertTrue(!!movedBookmark); + bookmarksApi.callbackRouterRemote.onBookmarkNodeMoved( + /*oldParentId=*/ FOLDERS[1]!.children![2]! + .id, // Moving from child folder. + /*oldIndex=*/ 0, + /*parentId=*/ FOLDERS[1]!.id, // Moving to other bookmarks. + /*index=*/ 0, + ); + await flushTasks(); - let expandButton = - folderElement.shadowRoot.querySelector<PowerBookmarkRowElement>( - '#expandButton'); - // Assert that the expand button is present for folders - assertTrue(!!expandButton); + const bookmarks = getBookmarks(powerBookmarksList); + assertEquals(5, bookmarks.length); - const singleBookmarkElement = getPowerBookmarksRowElement('3'); - assertTrue(!!singleBookmarkElement); + const childFolder = getBookmarkWithId(powerBookmarksList, '5'); + assertTrue(!!childFolder); + assertEquals(0, childFolder.children!.length); + }); - expandButton = - singleBookmarkElement.shadowRoot.querySelector<PowerBookmarkRowElement>( - '#expandButton'); - // Assert that the expand button is not present for single bookmarks - assertFalse(!!expandButton); - }); + test('MovesBookmarksIntoNewFolder', async () => { + // Create a new folder without a children array. + bookmarksApi.callbackRouterRemote.onBookmarkNodeAdded({ + id: '1000', + title: 'New folder', + index: 0, + parentId: FOLDERS[1]!.id, + url: null, + children: null, + dateAdded: null, + dateLastUsed: null, + }); + await flushTasks(); - test('ShowsCorrectFoldersOnTreeView', () => { - assertEquals(FOLDERS[1]!.children!.length + 1, getBookmarks().length); - }); + const movedBookmark = FOLDERS[1]!.children![2]!.children![0]!; + assertTrue(!!movedBookmark); + bookmarksApi.callbackRouterRemote.onBookmarkNodeMoved( + /*oldParentId=*/ FOLDERS[1]!.children![2]!.id, + /*oldIndex=*/ 0, + /*parentId=*/ '1000', + /*index=*/ 0, + ); + await flushTasks(); - test('ExpandAndCollapseNestedBookmarks', async () => { - const folderElement = getPowerBookmarksRowElement('5'); - assertTrue(!!folderElement); + const newFolder = getBookmarkWithId(powerBookmarksList, '1000'); + assertTrue(!!newFolder); - const expandButton = - folderElement.shadowRoot.querySelector<PowerBookmarkRowElement>( - '#expandButton'); - assertTrue(!!expandButton); + assertEquals(1, newFolder.children!.length); + }); - expandButton.click(); - await expandButton.updateComplete; - await folderElement.updateComplete; + test('MovesBookmarkWithFilter', async () => { + await openBookmark('5'); + await performSearch('bookmark'); - // Verify nested bookmarks are now visible - const nestedBookmarkElement = - folderElement.shadowRoot.querySelector<PowerBookmarkRowElement>( - '#bookmark-6'); - assertTrue(!!nestedBookmarkElement); - // Verify that the nested bookmark has the correct depth - assertEquals(1, nestedBookmarkElement.depth); + assertEquals(1, getBookmarksInList(powerBookmarksList, 0).length); + assertEquals(3, getBookmarksInList(powerBookmarksList, 1).length); - const bookmarkDiv = - nestedBookmarkElement.shadowRoot.querySelector<HTMLElement>( - '#bookmark'); - assertTrue(!!bookmarkDiv); + const movedBookmark = getBookmarkWithId(powerBookmarksList, '6'); + assertTrue(!!movedBookmark); + bookmarksApi.callbackRouterRemote.onBookmarkNodeMoved( + /*oldParentId=*/ FOLDERS[1]!.children![2]! + .id, // Moving from child folder. + /*oldIndex=*/ 0, + /*parentId=*/ FOLDERS[1]!.id, // Moving to other bookmarks. + /*index=*/ 0, + ); + await flushTasks(); - // Check if the depth is correctly applied to the style - const computedStyle = getComputedStyle(bookmarkDiv); - const expectedMargin = - nestedBookmarkElement.depth * NESTED_BOOKMARKS_MARGIN_PER_DEPTH + - NESTED_BOOKMARKS_BASE_MARGIN; - assertEquals(`${expectedMargin}px`, computedStyle.marginLeft); + // Moved bookmark is no longer in active folder, should move from primary + // to secondary list. + assertEquals(0, getBookmarksInList(powerBookmarksList, 0).length); + assertEquals(4, getBookmarksInList(powerBookmarksList, 1).length); - expandButton.click(); - await expandButton.updateComplete; - await folderElement.updateComplete; + bookmarksApi.callbackRouterRemote.onBookmarkNodeMoved( + /*oldParentId=*/ FOLDERS[1]!.id, // Moving from other bookmarks. + /*oldIndex=*/ 0, + /*parentId=*/ FOLDERS[1]!.children![2]!.id, // Moving to child + // folder. + /*index=*/ 0, + ); + await flushTasks(); - // Verify nested bookmarks are no longer visible - const collapsedNestedBookmarkElement = - folderElement.shadowRoot.querySelector<PowerBookmarkRowElement>( - '#bookmark-6'); - assertFalse(!!collapsedNestedBookmarkElement); + // Moved bookmark is now in active folder, should move from secondary + // to primary list. + assertEquals(1, getBookmarksInList(powerBookmarksList, 0).length); + assertEquals(3, getBookmarksInList(powerBookmarksList, 1).length); + }); + + test('RemovesBookmark', async () => { + const originalShownBookmarkCount = + getBookmarks(powerBookmarksList).length; + + bookmarksApi.callbackRouterRemote.onBookmarkNodesRemoved(['3']); + await flushTasks(); + + const removedBookmark = getBookmarkWithId(powerBookmarksList, '3'); + assertTrue(!removedBookmark); + + assertEquals( + originalShownBookmarkCount - 1, + getBookmarks(powerBookmarksList).length); + }); + + test('SetsCompactDescription', () => { + const folder = getBookmarkWithId(powerBookmarksList, '5'); + assertTrue(!!folder); + + assertEquals( + '(1)', + getPowerBookmarksRowElement(powerBookmarksList, '5') + ?.getBookmarkDescriptionForTests(folder)); + }); + + test('SetsExpandedDescription', () => { + const viewButton: HTMLElement = + powerBookmarksList.shadowRoot!.querySelector('#viewButton')!; + viewButton.click(); + + const folder = getBookmarkWithId(powerBookmarksList, '4'); + assertTrue(!!folder); + + assertEquals( + 'child', + getPowerBookmarksRowElement(powerBookmarksList, '4') + ?.getBookmarkDescriptionForTests(folder)); + }); + + test('SetsExpandedSearchResultDescription', async () => { + const viewButton = + powerBookmarksList.shadowRoot!.querySelector<HTMLElement>( + '#viewButton')!; + viewButton.click(); + + await performSearch('child bookmark'); + + const folder = getBookmarkWithId(powerBookmarksList, '4'); + assertTrue(!!folder); + + assertEquals( + 'child - All Bookmarks', + getPowerBookmarksRowElement(powerBookmarksList, '4') + ?.getBookmarkDescriptionForTests(folder)); + }); + + test('RenamesBookmark', async () => { + const renamedBookmarkId = '4'; + powerBookmarksList.setRenamingIdForTests(renamedBookmarkId); + + await flushTasks(); + + const rowElement = + getPowerBookmarksRowElement(powerBookmarksList, renamedBookmarkId); + assertTrue(!!rowElement); + let input = + rowElement.shadowRoot.querySelector<CrInputElement>('cr-input'); + assertTrue(!!input); + + const inputChange = eventToPromise('input-change', rowElement); + + const newName = 'foo'; + input.value = newName; + input.inputElement.dispatchEvent(new Event('change')); + + await inputChange; + await flushTasks(); + + // Committing a new input value should rename the bookmark and remove the + // input. + assertEquals(1, bookmarksApi.getCallCount('renameBookmark')); + assertEquals( + renamedBookmarkId, bookmarksApi.getArgs('renameBookmark')[0][0]); + assertEquals(newName, bookmarksApi.getArgs('renameBookmark')[0][1]); + input = rowElement.shadowRoot.querySelector<CrInputElement>('cr-input'); + assertFalse(!!input); + }); + + test('BlursRenameInput', async () => { + const renamedBookmarkId = '4'; + powerBookmarksList.setRenamingIdForTests(renamedBookmarkId); + + await flushTasks(); + + const rowElement = + powerBookmarksList.shadowRoot!.querySelector<PowerBookmarkRowElement>( + `#bookmark-${renamedBookmarkId}`); + assertTrue(!!rowElement); + let input = + rowElement.shadowRoot.querySelector<CrInputElement>('cr-input'); + const inputBlurred = eventToPromise( + 'input-change', + getPowerBookmarksRowElement(powerBookmarksList, renamedBookmarkId)!); + assertTrue(!!input); + input.inputElement.blur(); + await inputBlurred; + + await flushTasks(); + + // Blurring the input should remove it. + input = rowElement.shadowRoot.querySelector<CrInputElement>('cr-input'); + assertFalse(!!input); + }); + + test('ShowsFolderImages', () => { + const viewButton: HTMLElement = + powerBookmarksList.shadowRoot!.querySelector('#viewButton')!; + viewButton.click(); + + flush(); + + const bookmarksBarFolderElement = getCrUrlListItemElementWithId('1'); + assertTrue(!!bookmarksBarFolderElement); + assertEquals(0, bookmarksBarFolderElement.imageUrls.length); + + const childFolderElement = getCrUrlListItemElementWithId('5'); + assertTrue(!!childFolderElement); + assertNotEquals(0, childFolderElement.imageUrls.length); + }); + + test('DeletesSelectedBookmarks', async () => { + const editButton: HTMLElement = + powerBookmarksList.shadowRoot!.querySelector('#editButton')!; + editButton.click(); + + flush(); + + await selectBookmark('3'); + await selectBookmark('5'); + + flush(); + + const deleteButton: HTMLButtonElement = + powerBookmarksList.shadowRoot!.querySelector('#deleteButton')!; + assertFalse(deleteButton.disabled); + deleteButton.click(); + + flush(); + + assertEquals(1, bookmarksApi.getCallCount('deleteBookmarks')); + assertEquals('3', bookmarksApi.getArgs('deleteBookmarks')[0][0]); + assertEquals('5', bookmarksApi.getArgs('deleteBookmarks')[0][1]); + }); + + test('EditBookmarkWithBookmarksInTransportModeDisabled', async () => { + const bookmark = getBookmarkWithId(powerBookmarksList, '3')!; + const contextMenu = powerBookmarksList.$.contextMenu; + const editClicked = eventToPromise('edit-clicked', contextMenu); + + // Open the context menu. + contextMenu.showAtPosition( + new MouseEvent('click'), [bookmark], false, false); + await waitAfterNextRender(contextMenu); + + // Get the edit option in the menu. + const menuItems = + contextMenu.shadowRoot!.querySelectorAll('.dropdown-item'); + assertEquals( + menuItems[3]!.textContent!.includes( + loadTimeData.getString('menuEdit')), + true); + const editItem = contextMenu.shadowRoot!.querySelectorAll<HTMLElement>( + '.dropdown-item')[3]!; + + // Click on edit and wait for the call to propagate. + editItem.click(); + await editClicked; + await flushTasks(); + + // The edit dialog is opened. + const editDialog = powerBookmarksList.$.editDialog; + assertTrue(editDialog.$.dialog.open); + assertEquals(bookmark.title, editDialog.$.nameInput.inputElement.value); + assertEquals(bookmark.url, editDialog.$.urlInput.inputElement.value); + }); + + test('MoveBookmarksWithBookmarksInTransportModeDisabled', async () => { + const bookmarks = [ + getBookmarkWithId(powerBookmarksList, '3')!, + getBookmarkWithId(powerBookmarksList, '5')!, + ]; + const contextMenu = powerBookmarksList.$.contextMenu; + const editClicked = eventToPromise('edit-clicked', contextMenu); + + // Open the context menu. + contextMenu.showAtPosition( + new MouseEvent('click'), bookmarks, false, false); + await waitAfterNextRender(contextMenu); + + // Get the move option in the menu. + const menuItems = + contextMenu.shadowRoot!.querySelectorAll('.dropdown-item'); + assertEquals( + menuItems[4]!.textContent!.includes( + loadTimeData.getString('tooltipMove')), + true); + const moveItem = contextMenu.shadowRoot!.querySelectorAll<HTMLElement>( + '.dropdown-item')[4]!; + + // Click on move and wait for the call to propagate. + moveItem.click(); + await editClicked; + await flushTasks(); + + // The edit dialog is opened. + const editDialog = powerBookmarksList.$.editDialog; + assertTrue(editDialog.$.dialog.open); + }); + + test('LogsBookmarkCountMetric', async () => { + // Initially should have 4 bookmarks shown. + assertEquals( + 1, metrics.count('PowerBookmarks.SidePanel.BookmarksShown', 4)); + + await openBookmark('5'); + + // Folder with id 5 only has 1 bookmark shown. + assertEquals( + 1, metrics.count('PowerBookmarks.SidePanel.BookmarksShown', 1)); + }); + + test('TogglesSectionVisibilityAndEmptyStates', async () => { + const search = powerBookmarksList.$.searchField; + const labels = powerBookmarksList.$.labels; + const heading = powerBookmarksList.$.heading; + const folderEmptyState = powerBookmarksList.$.folderEmptyState; + const bookmarksList = powerBookmarksList.$.bookmarks; + const topLevelEmptyState = powerBookmarksList.$.topLevelEmptyState; + const footer = powerBookmarksList.$.footer; + assertEquals( + loadTimeData.getString('emptyTitle'), topLevelEmptyState.heading); + assertEquals( + loadTimeData.getString('emptyBody'), topLevelEmptyState.body); + + // Has bookmarks. + assertFalse(isHidden(search)); + assertTrue(isHidden(labels)); + assertFalse(isHidden(heading)); + assertTrue(isHidden(folderEmptyState)); + assertFalse(isHidden(bookmarksList)); + assertTrue(isHidden(topLevelEmptyState)); + assertFalse(isHidden(footer)); + + // Opening an empty folder. + await openBookmark('1'); + + assertFalse(isHidden(search)); + assertTrue(isHidden(labels)); + assertFalse(isHidden(heading)); + assertFalse(isHidden(folderEmptyState)); + assertTrue(isHidden(bookmarksList)); + assertTrue(isHidden(topLevelEmptyState)); + assertFalse(isHidden(footer)); + + // A search with no results. + const searchField = powerBookmarksList.shadowRoot!.querySelector( + 'cr-toolbar-search-field'); + assertTrue(!!searchField); + searchField.$.searchInput.value = 'abcdef'; + searchField.onSearchTermSearch(); + await flushTasks(); + assertEquals( + loadTimeData.getString('emptyTitleSearch'), + topLevelEmptyState.heading); + assertFalse(isHidden(search)); + assertTrue(isHidden(labels)); + assertTrue(isHidden(heading)); + assertTrue(isHidden(folderEmptyState)); + assertTrue(isHidden(bookmarksList)); + assertFalse(isHidden(topLevelEmptyState)); + assertTrue(isHidden(footer)); + + // Adding a tracked product shows filter chips. + const newProduct = { + bookmarkId: BigInt(3), + info: { + title: 'Product Baz', + clusterTitle: 'Product Cluster Baz', + domain: 'baz.com', + imageUrl: {url: 'https://baz.com/image'}, + productUrl: {url: 'https://baz.com/product'}, + currentPrice: '$56', + previousPrice: '$78', + clusterId: BigInt(12345), + categoryLabels: [], + priceSummary: '', + }, + }; + + callbackRouterRemote.priceTrackedForBookmark(newProduct); + await flushTasks(); + assertFalse(isHidden(labels)); + }); }); });
diff --git a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_test_util.ts b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_test_util.ts new file mode 100644 index 0000000..b1dbffe --- /dev/null +++ b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_test_util.ts
@@ -0,0 +1,127 @@ +// 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 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_list.js'; + +import type {BookmarksTreeNode} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks.mojom-webui.js'; +import type {PowerBookmarkRowElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmark_row.js'; +import type {PowerBookmarksListElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_list.js'; +import type {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js'; +import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js'; + +import type {TestBookmarksApiProxy} from './test_bookmarks_api_proxy.js'; + +export function createTestBookmarks(): BookmarksTreeNode[] { + return [ + { + id: '1', + parentId: '0', + index: 0, + title: 'Bookmarks Bar', + url: null, + dateAdded: null, + dateLastUsed: null, + children: [], + }, + { + id: '2', + parentId: '0', + title: 'Other Bookmarks', + index: 1, + url: null, + dateAdded: null, + dateLastUsed: null, + children: [ + { + id: '3', + parentId: '2', + index: 0, + title: 'First child bookmark', + url: 'http://child/bookmark/1/', + dateAdded: 1, + dateLastUsed: null, + children: null, + }, + { + id: '4', + parentId: '2', + index: 1, + title: 'Second child bookmark', + url: 'http://child/bookmark/2/', + dateAdded: 3, + dateLastUsed: null, + children: null, + }, + { + id: '5', + parentId: '2', + index: 2, + title: 'Child folder', + url: null, + dateAdded: 2, + dateLastUsed: null, + children: [ + { + id: '6', + parentId: '5', + index: 0, + title: 'Nested bookmark', + url: 'http://nested/bookmark/', + dateAdded: 4, + dateLastUsed: null, + children: null, + }, + ], + }, + ], + }, + ]; +} + +export function getBookmarks(element: PowerBookmarksListElement) { + return getBookmarksInList(element, 0).concat(getBookmarksInList(element, 1)); +} + +export function getBookmarksInList( + element: PowerBookmarksListElement, + listIndex: number): chrome.bookmarks.BookmarkTreeNode[] { + const ironList = element.shadowRoot!.querySelector<IronListElement>( + `#shownBookmarksIronList${listIndex}`); + if (!ironList || !ironList.items) { + return []; + } + return ironList.items; +} + +export function getBookmarkWithId( + element: PowerBookmarksListElement, + id: string): chrome.bookmarks.BookmarkTreeNode|undefined { + return getBookmarks(element).find(bookmark => bookmark.id === id); +} + +export function getPowerBookmarksRowElement( + element: PowerBookmarksListElement, id: string): PowerBookmarkRowElement| + undefined { + return element.shadowRoot!.querySelector<PowerBookmarkRowElement>( + `#bookmark-${id}`) || + undefined; +} + +export async function initializeUi(bookmarksApi: TestBookmarksApiProxy): + Promise<PowerBookmarksListElement> { + const element = document.createElement('power-bookmarks-list'); + + // Ensure the PowerBookmarksListElement is given a fixed height to expand + // to. + const parentElement = document.createElement('div'); + parentElement.style.height = '500px'; + parentElement.appendChild(element); + document.body.appendChild(parentElement); + + await bookmarksApi.whenCalled('getAllBookmarks'); + await waitAfterNextRender(element); + flush(); + return element; +}
diff --git a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_transport_mode_test.ts b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_transport_mode_test.ts new file mode 100644 index 0000000..3561ac7 --- /dev/null +++ b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_transport_mode_test.ts
@@ -0,0 +1,142 @@ +// 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 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_list.js'; + +import {ActionSource, SortOrder, ViewType} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks.mojom-webui.js'; +import {BookmarksApiProxyImpl} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks_api_proxy.js'; +import type {PowerBookmarksListElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_list.js'; +import {PageCallbackRouter} from 'chrome://resources/cr_components/commerce/price_tracking.mojom-webui.js'; +import {PriceTrackingBrowserProxyImpl} from 'chrome://resources/cr_components/commerce/price_tracking_browser_proxy.js'; +import {PageImageServiceBrowserProxy} from 'chrome://resources/cr_components/page_image_service/browser_proxy.js'; +import {PageImageServiceHandlerRemote} from 'chrome://resources/cr_components/page_image_service/page_image_service.mojom-webui.js'; +import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; +import {assertEquals} from 'chrome://webui-test/chai_assert.js'; +import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js'; +import {TestMock} from 'chrome://webui-test/test_mock.js'; +import {eventToPromise} from 'chrome://webui-test/test_util.js'; + +import {createTestBookmarks, getBookmarkWithId, initializeUi} from './power_bookmarks_list_test_util.js'; +import {TestBookmarksApiProxy} from './test_bookmarks_api_proxy.js'; + +suite('TransportMode', () => { + let powerBookmarksList: PowerBookmarksListElement; + let bookmarksApi: TestBookmarksApiProxy; + const priceTrackingProxy = TestMock.fromClass(PriceTrackingBrowserProxyImpl); + let imageServiceHandler: TestMock<PageImageServiceHandlerRemote>& + PageImageServiceHandlerRemote; + + setup(async () => { + document.body.innerHTML = window.trustedTypes!.emptyHTML; + + bookmarksApi = new TestBookmarksApiProxy(); + bookmarksApi.setAllBookmarks(structuredClone(createTestBookmarks())); + BookmarksApiProxyImpl.setInstance(bookmarksApi); + + priceTrackingProxy.reset(); + const callbackRouter = new PageCallbackRouter(); + priceTrackingProxy.setResultFor('getCallbackRouter', callbackRouter); + priceTrackingProxy.setResultFor( + 'getAllPriceTrackedBookmarkProductInfo', + Promise.resolve({productInfos: []})); + priceTrackingProxy.setResultFor( + 'getAllShoppingBookmarkProductInfo', + Promise.resolve({productInfos: []})); + priceTrackingProxy.setResultFor( + 'getShoppingCollectionBookmarkFolderId', + Promise.resolve({collectionId: BigInt(-1)})); + PriceTrackingBrowserProxyImpl.setInstance(priceTrackingProxy); + + imageServiceHandler = TestMock.fromClass(PageImageServiceHandlerRemote); + PageImageServiceBrowserProxy.setInstance( + new PageImageServiceBrowserProxy(imageServiceHandler)); + imageServiceHandler.setResultFor('getPageImageUrl', Promise.resolve({ + result: {imageUrl: {url: 'https://example.com/image.png'}}, + })); + + loadTimeData.overrideValues({ + sortOrder: SortOrder.kNewest, + viewType: ViewType.kCompact, + emptyTitle: 'empty title base', + emptyTitleSearch: 'empty title search', + emptyTitleFolder: 'folder is empty', + emptyBodyFolder: 'folder body', + emptyTitleGuest: 'guest title', + emptyBodyGuest: 'guest body', + bookmarksTreeViewEnabled: false, + isBookmarksInTransportModeEnabled: true, + }); + + powerBookmarksList = await initializeUi(bookmarksApi); + }); + + test('EditBookmarkWithBookmarksInTransportModeEnabled', async () => { + const bookmarkId = '3'; + const contextMenu = powerBookmarksList.$.contextMenu; + const editClicked = eventToPromise('edit-clicked', contextMenu); + + // Open the context menu. + contextMenu.showAtPosition( + new MouseEvent('click'), + [getBookmarkWithId(powerBookmarksList, bookmarkId)!], false, false); + await waitAfterNextRender(contextMenu); + + // Get the edit option in the menu. + const menuItems = + contextMenu.shadowRoot!.querySelectorAll('.dropdown-item'); + assertEquals( + menuItems[3]!.textContent!.includes(loadTimeData.getString('menuEdit')), + true); + const editItem = contextMenu.shadowRoot!.querySelectorAll<HTMLElement>( + '.dropdown-item')[3]!; + + // Click on edit and wait for the call to propagate. + editItem.click(); + await editClicked; + await flushTasks(); + + // The native edit dialog is opened. + assertEquals(1, bookmarksApi.getCallCount('contextMenuEdit')); + assertEquals(bookmarkId, bookmarksApi.getArgs('contextMenuEdit')[0][0][0]); + assertEquals( + ActionSource.kBookmark, bookmarksApi.getArgs('contextMenuEdit')[0][1]); + }); + + + test('MoveBookmarksWithBookmarksInTransportModeEnabled', async () => { + const bookmarkId = '3'; + const bookmarks = [ + getBookmarkWithId(powerBookmarksList, bookmarkId)!, + getBookmarkWithId(powerBookmarksList, '5')!, + ]; + const contextMenu = powerBookmarksList.$.contextMenu; + const editClicked = eventToPromise('edit-clicked', contextMenu); + + // Open the context menu. + contextMenu.showAtPosition( + new MouseEvent('click'), bookmarks, false, false); + await waitAfterNextRender(contextMenu); + + // Get the move option in the menu. + const menuItems = + contextMenu.shadowRoot!.querySelectorAll('.dropdown-item'); + assertEquals( + menuItems[4]!.textContent!.includes( + loadTimeData.getString('tooltipMove')), + true); + const moveItem = contextMenu.shadowRoot!.querySelectorAll<HTMLElement>( + '.dropdown-item')[4]!; + + // Click on move and wait for the call to propagate. + moveItem.click(); + await editClicked; + await flushTasks(); + + // The native move dialog is opened. + assertEquals(1, bookmarksApi.getCallCount('contextMenuMove')); + assertEquals(bookmarkId, bookmarksApi.getArgs('contextMenuMove')[0][0][0]); + assertEquals( + ActionSource.kBookmark, bookmarksApi.getArgs('contextMenuMove')[0][1]); + }); +});
diff --git a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_tree_view_test.ts b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_tree_view_test.ts new file mode 100644 index 0000000..b1cd726 --- /dev/null +++ b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_list_tree_view_test.ts
@@ -0,0 +1,144 @@ +// 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 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_list.js'; + +import {SortOrder, ViewType} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks.mojom-webui.js'; +import {BookmarksApiProxyImpl} from 'chrome://bookmarks-side-panel.top-chrome/bookmarks_api_proxy.js'; +import type {PowerBookmarkRowElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmark_row.js'; +import {NESTED_BOOKMARKS_BASE_MARGIN, NESTED_BOOKMARKS_MARGIN_PER_DEPTH} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmark_row.js'; +import type {PowerBookmarksListElement} from 'chrome://bookmarks-side-panel.top-chrome/power_bookmarks_list.js'; +import {PageCallbackRouter} from 'chrome://resources/cr_components/commerce/price_tracking.mojom-webui.js'; +import {PriceTrackingBrowserProxyImpl} from 'chrome://resources/cr_components/commerce/price_tracking_browser_proxy.js'; +import {PageImageServiceBrowserProxy} from 'chrome://resources/cr_components/page_image_service/browser_proxy.js'; +import {PageImageServiceHandlerRemote} from 'chrome://resources/cr_components/page_image_service/page_image_service.mojom-webui.js'; +import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; +import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; +import {TestMock} from 'chrome://webui-test/test_mock.js'; + +import {createTestBookmarks, getBookmarks, getPowerBookmarksRowElement, initializeUi} from './power_bookmarks_list_test_util.js'; +import {TestBookmarksApiProxy} from './test_bookmarks_api_proxy.js'; + +suite('TreeView', () => { + const FOLDERS = createTestBookmarks(); + let powerBookmarksList: PowerBookmarksListElement; + let bookmarksApi: TestBookmarksApiProxy; + const priceTrackingProxy = TestMock.fromClass(PriceTrackingBrowserProxyImpl); + let imageServiceHandler: TestMock<PageImageServiceHandlerRemote>& + PageImageServiceHandlerRemote; + + setup(async () => { + document.body.innerHTML = window.trustedTypes!.emptyHTML; + + bookmarksApi = new TestBookmarksApiProxy(); + bookmarksApi.setAllBookmarks(structuredClone(FOLDERS)); + BookmarksApiProxyImpl.setInstance(bookmarksApi); + + priceTrackingProxy.reset(); + const callbackRouter = new PageCallbackRouter(); + priceTrackingProxy.setResultFor('getCallbackRouter', callbackRouter); + priceTrackingProxy.setResultFor( + 'getAllPriceTrackedBookmarkProductInfo', + Promise.resolve({productInfos: []})); + priceTrackingProxy.setResultFor( + 'getAllShoppingBookmarkProductInfo', + Promise.resolve({productInfos: []})); + priceTrackingProxy.setResultFor( + 'getShoppingCollectionBookmarkFolderId', + Promise.resolve({collectionId: BigInt(-1)})); + PriceTrackingBrowserProxyImpl.setInstance(priceTrackingProxy); + + imageServiceHandler = TestMock.fromClass(PageImageServiceHandlerRemote); + PageImageServiceBrowserProxy.setInstance( + new PageImageServiceBrowserProxy(imageServiceHandler)); + imageServiceHandler.setResultFor('getPageImageUrl', Promise.resolve({ + result: {imageUrl: {url: 'https://example.com/image.png'}}, + })); + + loadTimeData.overrideValues({ + sortOrder: SortOrder.kNewest, + viewType: ViewType.kCompact, + emptyTitle: 'empty title base', + emptyTitleSearch: 'empty title search', + emptyTitleFolder: 'folder is empty', + emptyBodyFolder: 'folder body', + emptyTitleGuest: 'guest title', + emptyBodyGuest: 'guest body', + bookmarksTreeViewEnabled: true, + }); + + powerBookmarksList = await initializeUi(bookmarksApi); + }); + + test('ShowsExpandButtonForFolders', () => { + const folderElement = getPowerBookmarksRowElement(powerBookmarksList, '5'); + assertTrue(!!folderElement); + + let expandButton = + folderElement.shadowRoot.querySelector<PowerBookmarkRowElement>( + '#expandButton'); + // Assert that the expand button is present for folders + assertTrue(!!expandButton); + + const singleBookmarkElement = + getPowerBookmarksRowElement(powerBookmarksList, '3'); + assertTrue(!!singleBookmarkElement); + + expandButton = + singleBookmarkElement.shadowRoot.querySelector<PowerBookmarkRowElement>( + '#expandButton'); + // Assert that the expand button is not present for single bookmarks + assertFalse(!!expandButton); + }); + + test('ShowsCorrectFoldersOnTreeView', () => { + assertEquals( + FOLDERS[1]!.children!.length + 1, + getBookmarks(powerBookmarksList).length); + }); + + test('ExpandAndCollapseNestedBookmarks', async () => { + const folderElement = getPowerBookmarksRowElement(powerBookmarksList, '5'); + assertTrue(!!folderElement); + + const expandButton = + folderElement.shadowRoot.querySelector<PowerBookmarkRowElement>( + '#expandButton'); + assertTrue(!!expandButton); + + expandButton.click(); + await expandButton.updateComplete; + await folderElement.updateComplete; + + // Verify nested bookmarks are now visible + const nestedBookmarkElement = + folderElement.shadowRoot.querySelector<PowerBookmarkRowElement>( + '#bookmark-6'); + assertTrue(!!nestedBookmarkElement); + // Verify that the nested bookmark has the correct depth + assertEquals(1, nestedBookmarkElement.depth); + + const bookmarkDiv = + nestedBookmarkElement.shadowRoot.querySelector<HTMLElement>( + '#bookmark'); + assertTrue(!!bookmarkDiv); + + // Check if the depth is correctly applied to the style + const computedStyle = getComputedStyle(bookmarkDiv); + const expectedMargin = + nestedBookmarkElement.depth * NESTED_BOOKMARKS_MARGIN_PER_DEPTH + + NESTED_BOOKMARKS_BASE_MARGIN; + assertEquals(`${expectedMargin}px`, computedStyle.marginLeft); + + expandButton.click(); + await expandButton.updateComplete; + await folderElement.updateComplete; + + // Verify nested bookmarks are no longer visible + const collapsedNestedBookmarkElement = + folderElement.shadowRoot.querySelector<PowerBookmarkRowElement>( + '#bookmark-6'); + assertFalse(!!collapsedNestedBookmarkElement); + }); +});
diff --git a/chrome/test/data/webui/side_panel/bookmarks/sp_bookmarks_interactive_test.cc b/chrome/test/data/webui/side_panel/bookmarks/sp_bookmarks_interactive_test.cc index ce613c4..745077e 100644 --- a/chrome/test/data/webui/side_panel/bookmarks/sp_bookmarks_interactive_test.cc +++ b/chrome/test/data/webui/side_panel/bookmarks/sp_bookmarks_interactive_test.cc
@@ -15,15 +15,6 @@ } }; -class SidePanelBookmarksListTest : public SidePanelBookmarksTest { - protected: - void RunTestSuite(const std::string& suiteName) { - SidePanelBookmarksTest::RunTest( - "side_panel/bookmarks/power_bookmarks_list_test.js", - base::StringPrintf("runMochaSuite('%s');", suiteName.c_str())); - } -}; - // TODO(crbug.com/40882667): Flaky on Mac and Linux dbg. Re-enable this test. #if BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_LINUX) && !defined(NDEBUG)) #define MAYBE_ShoppingList DISABLED_ShoppingList @@ -45,18 +36,6 @@ "mocha.run()"); } -IN_PROC_BROWSER_TEST_F(SidePanelBookmarksListTest, General) { - RunTestSuite("General"); -} - -IN_PROC_BROWSER_TEST_F(SidePanelBookmarksListTest, TransportMode) { - RunTestSuite("TransportMode"); -} - -IN_PROC_BROWSER_TEST_F(SidePanelBookmarksListTest, TreeView) { - RunTestSuite("TreeView"); -} - IN_PROC_BROWSER_TEST_F(SidePanelPowerBookmarksTest, Service) { RunTest("side_panel/bookmarks/power_bookmarks_service_test.js", "mocha.run()"); @@ -76,3 +55,26 @@ IN_PROC_BROWSER_TEST_F(SidePanelPowerBookmarksTest, Labels) { RunTest("side_panel/bookmarks/power_bookmarks_labels_test.js", "mocha.run()"); } + +using SidePanelBookmarksListTest = SidePanelBookmarksTest; +IN_PROC_BROWSER_TEST_F(SidePanelBookmarksListTest, General1) { + SidePanelBookmarksTest::RunTest( + "side_panel/bookmarks/power_bookmarks_list_test.js", + "runMochaSuite('General Part1');"); +} + +IN_PROC_BROWSER_TEST_F(SidePanelBookmarksListTest, General2) { + SidePanelBookmarksTest::RunTest( + "side_panel/bookmarks/power_bookmarks_list_test.js", + "runMochaSuite('General Part2');"); +} + +IN_PROC_BROWSER_TEST_F(SidePanelBookmarksListTest, TransportMode) { + RunTest("side_panel/bookmarks/power_bookmarks_list_transport_mode_test.js", + "mocha.run()"); +} + +IN_PROC_BROWSER_TEST_F(SidePanelBookmarksListTest, TreeView) { + RunTest("side_panel/bookmarks/power_bookmarks_list_tree_view_test.js", + "mocha.run()"); +}
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni index 17ee8a3..58ab98f7 100644 --- a/chromeos/tast_control.gni +++ b/chromeos/tast_control.gni
@@ -103,6 +103,11 @@ "terminal.SSH", "arc.Print.vm_t", + # b/409064294 + "apps.SystemWebAppsInstallGuest", + "peripherals.LaunchAppFromGuestSession.diagnostics", + "health.MonitorKeyboardDiagnosticEvent.non_tablet_mode_form_factors", + # READ COMMENT AT TOP BEFORE ADDING NEW TESTS HERE. ]
diff --git a/clank b/clank index 618a8eb..599d780 160000 --- a/clank +++ b/clank
@@ -1 +1 @@ -Subproject commit 618a8ebb6ca63dee8c6b5066c777e88ec88596d2 +Subproject commit 599d7806ce1185408289232b25620f00947b7c6a
diff --git a/components/autofill/core/browser/payments/bnpl_manager.cc b/components/autofill/core/browser/payments/bnpl_manager.cc index 6825f6ce..8cbcfbe 100644 --- a/components/autofill/core/browser/payments/bnpl_manager.cc +++ b/components/autofill/core/browser/payments/bnpl_manager.cc
@@ -44,6 +44,12 @@ base::FeatureList::IsEnabled(features::kAutofillEnableBuyNowPayLater); } +bool ShouldShowPermanentErrorDialog( + PaymentsAutofillClient::PaymentsRpcResult result) { + return result == PaymentsAutofillClient::PaymentsRpcResult:: + kVcnRetrievalPermanentFailure; +} + } // namespace BnplManager::OngoingFlowState::OngoingFlowState() = default; @@ -196,9 +202,7 @@ } else { payments_autofill_client().ShowAutofillErrorDialog( AutofillErrorDialogContext::WithBnplPermanentOrTemporaryError( - /*is_permanent_error=*/result == - PaymentsAutofillClient::PaymentsRpcResult:: - kVcnRetrievalPermanentFailure)); + /*is_permanent_error=*/ShouldShowPermanentErrorDialog(result))); } Reset(); } @@ -266,8 +270,7 @@ payments_autofill_client().ShowAutofillErrorDialog( AutofillErrorDialogContext::WithBnplPermanentOrTemporaryError( - /*is_permanent_error=*/result == - PaymentsAutofillClient::PaymentsRpcResult::kPermanentFailure)); + /*is_permanent_error=*/ShouldShowPermanentErrorDialog(result))); Reset(); } @@ -345,8 +348,7 @@ } else { payments_autofill_client().ShowAutofillErrorDialog( AutofillErrorDialogContext::WithBnplPermanentOrTemporaryError( - /*is_permanent_error=*/result == - PaymentsAutofillClient::PaymentsRpcResult::kPermanentFailure)); + /*is_permanent_error=*/ShouldShowPermanentErrorDialog(result))); Reset(); } } @@ -497,9 +499,7 @@ } else { payments_autofill_client().ShowAutofillErrorDialog( AutofillErrorDialogContext::WithBnplPermanentOrTemporaryError( - /*is_permanent_error=*/result == - PaymentsAutofillClient::PaymentsRpcResult::kPermanentFailure)); - + /*is_permanent_error=*/ShouldShowPermanentErrorDialog(result))); Reset(); } }
diff --git a/components/autofill/core/browser/payments/bnpl_manager_unittest.cc b/components/autofill/core/browser/payments/bnpl_manager_unittest.cc index 9ef95d9e..dc933e78 100644 --- a/components/autofill/core/browser/payments/bnpl_manager_unittest.cc +++ b/components/autofill/core/browser/payments/bnpl_manager_unittest.cc
@@ -605,7 +605,8 @@ EXPECT_CALL(*payments_network_interface_, GetBnplPaymentInstrumentForFetchingUrl) .WillOnce(base::test::RunOnceCallback<1>( - PaymentsAutofillClient::PaymentsRpcResult::kPermanentFailure, + PaymentsAutofillClient::PaymentsRpcResult:: + kVcnRetrievalPermanentFailure, response)); OnIssuerSelected(linked_issuer);
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/BrowserUiListMenuUtils.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/BrowserUiListMenuUtils.java index a5a947e..ae84165 100644 --- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/BrowserUiListMenuUtils.java +++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/BrowserUiListMenuUtils.java
@@ -188,6 +188,54 @@ * Helper function to build a list menu item. Pass 0 for attributes that aren't applicable to * the menu item (e.g. if there is no icon or text). * + * @param title The text on the menu item. + * @param menuId Id of the menu item. + * @param isIncognito Whether the current menu item will be displayed in incognito mode. + * @param enabled Whether or not this menu item should be enabled. + * @return ListItem Representing an item with text and maybe an icon. + */ + public static ListItem buildMenuListItemWithIncognitoBranding( + String title, @IdRes int menuId, boolean isIncognito, boolean enabled) { + PropertyModel.Builder builder = + getListItemPropertyBuilder() + .with(ListMenuItemProperties.TITLE, title) + .with(ListMenuItemProperties.MENU_ITEM_ID, menuId) + .with(ListMenuItemProperties.ENABLED, enabled); + + if (isIncognito) { + builder.with( + ListMenuItemProperties.TEXT_APPEARANCE_ID, + R.style.TextAppearance_TextLarge_Primary_Baseline_Light); + } + return new MVCListAdapter.ListItem(ListMenuItemType.MENU_ITEM, builder.build()); + } + + /** + * Helper function to build a list menu item. Pass 0 for attributes that aren't applicable to + * the menu item (e.g. if there is no icon or text). + * + * @param titleId The text on the menu item. + * @param menuId Id of the menu item. + * @param isIncognito Whether the current menu item will be displayed in incognito mode. + * @param enabled Whether or not this menu item should be enabled. + * @return ListItem Representing an item with text and maybe an icon. + */ + public static ListItem buildMenuListItemWithIncognitoBranding( + @StringRes int titleId, @IdRes int menuId, boolean isIncognito, boolean enabled) { + return buildMenuListItemWithIncognitoBranding( + titleId, + menuId, + /* startIconId= */ 0, + /* iconTintColorStateList= */ 0, + R.style.TextAppearance_TextLarge_Primary_Baseline_Light, + isIncognito, + enabled); + } + + /** + * Helper function to build a list menu item. Pass 0 for attributes that aren't applicable to + * the menu item (e.g. if there is no icon or text). + * * @param titleId The text on the menu item. * @param menuId Id of the menu item. * @param startIconId The icon on the start of the menu item. Pass 0 for no icon.
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/IphDialogView.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/IphDialogView.java index 3a9ea05..f594ea0 100644 --- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/IphDialogView.java +++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/IphDialogView.java
@@ -4,7 +4,7 @@ package org.chromium.components.browser_ui.widget; -import static org.chromium.build.NullUtil.assumeNonNull; +import static org.chromium.build.NullUtil.assertNonNull; import android.content.Context; import android.content.res.Configuration; @@ -118,7 +118,7 @@ * * @param rootView The root view of the IPH dialog. Will be used to update the IPH view layout. */ - public void setRootView(View rootView) { + public void setRootView(@Nullable View rootView) { mRootView = rootView; } @@ -140,7 +140,7 @@ /** Update the IPH view layout based on the current size of the root view. */ public void updateLayout() { - assumeNonNull(mRootView); + assertNonNull(mRootView); int rootViewHeight = mRootView.getHeight(); if (mParentViewHeight == rootViewHeight) return; mParentViewHeight = rootViewHeight;
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/listmenu/BrowserUiListMenuRenderTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/listmenu/BrowserUiListMenuRenderTest.java index d8a38b244..f567f2b 100644 --- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/listmenu/BrowserUiListMenuRenderTest.java +++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/listmenu/BrowserUiListMenuRenderTest.java
@@ -14,18 +14,18 @@ import androidx.test.filters.MediumTest; import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.MethodRule; import org.junit.runner.RunWith; import org.chromium.base.ThreadUtils; import org.chromium.base.test.BaseActivityTestRule; import org.chromium.base.test.params.BaseJUnit4RunnerDelegate; -import org.chromium.base.test.params.ParameterAnnotations; +import org.chromium.base.test.params.MethodParamAnnotationRule; +import org.chromium.base.test.params.ParameterAnnotations.UseMethodParameter; import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate; +import org.chromium.base.test.params.ParameterProvider; import org.chromium.base.test.params.ParameterSet; import org.chromium.base.test.params.ParameterizedRunner; import org.chromium.base.test.util.Batch; @@ -37,9 +37,11 @@ import org.chromium.ui.modelutil.MVCListAdapter.ModelList; import org.chromium.ui.test.util.BlankUiTestActivity; import org.chromium.ui.test.util.NightModeTestUtils; +import org.chromium.ui.test.util.NightModeTestUtils.NightModeParams; import org.chromium.ui.test.util.RenderTestRule; import java.io.IOException; +import java.util.Collections; import java.util.List; /** Render tests for {@link BasicListMenu}. */ @@ -47,39 +49,38 @@ @UseRunnerDelegate(BaseJUnit4RunnerDelegate.class) @Batch(Batch.UNIT_TESTS) public class BrowserUiListMenuRenderTest { - @ParameterAnnotations.ClassParameter - private static List<ParameterSet> sClassParams = - new NightModeTestUtils.NightModeParams().getParameters(); + /** Used to run a test only with night mode. */ + public static class NightModeOnlyParameterProvider implements ParameterProvider { - @ClassRule - public static final BaseActivityTestRule<BlankUiTestActivity> sActivityTestRule = + private static List<ParameterSet> sNightModeOnly = + Collections.singletonList(new ParameterSet().value(true).name("NightModeEnabled")); + + @Override + public Iterable<ParameterSet> getParameters() { + return sNightModeOnly; + } + } + + @Rule + public final BaseActivityTestRule<BlankUiTestActivity> mActivityTestRule = new BaseActivityTestRule<>(BlankUiTestActivity.class); - private static Activity sActivity; - @Rule public RenderTestRule mRenderTestRule = RenderTestRule.Builder.withPublicCorpus() .setBugComponent(RenderTestRule.Component.UI_BROWSER_MOBILE) .build(); + @Rule public MethodRule mMethodParamAnnotationProcessor = new MethodParamAnnotationRule(); + private View mView; - public BrowserUiListMenuRenderTest(boolean nightModeEnabled) { - NightModeTestUtils.setUpNightModeForBlankUiTestActivity(nightModeEnabled); - mRenderTestRule.setNightModeEnabled(nightModeEnabled); - } - - @BeforeClass - public static void setupSuite() { - sActivity = sActivityTestRule.launchActivity(null); - } - - @Before - public void setUp() throws Exception { + private void setup(boolean nightMode, boolean incognito) { + Activity activity = mActivityTestRule.launchActivity(null); + NightModeTestUtils.setUpNightModeForBlankUiTestActivity(nightMode); + mRenderTestRule.setNightModeEnabled(nightMode); ThreadUtils.runOnUiThreadBlocking( () -> { - Activity activity = sActivity; ModelList data = new ModelList(); data.add( BrowserUiListMenuUtils.buildMenuListItem( @@ -108,7 +109,7 @@ data.add( BrowserUiListMenuUtils.buildMenuListItem( R.string.test_primary_1, 0, 0, false)); - data.add(BasicListMenu.buildMenuDivider()); + data.add(BasicListMenu.buildMenuDivider(incognito)); ListMenu.Delegate delegate = item -> {}; BasicListMenu listMenu = @@ -130,7 +131,18 @@ @Test @MediumTest @Feature({"RenderTest"}) - public void testRender_BasicListMenu() throws IOException { + @UseMethodParameter(NightModeParams.class) + public void testRender_BasicListMenu(boolean nightMode) throws IOException { + setup(nightMode, /* incognito= */ false); mRenderTestRule.render(mView, "basic_list_menu"); } + + @Test + @MediumTest + @UseMethodParameter(NightModeOnlyParameterProvider.class) + @Feature({"RenderTest"}) + public void testRender_BasicListMenu_Incognito(boolean nightMode) throws IOException { + setup(nightMode, /* incognito= */ true); + mRenderTestRule.render(mView, "basic_list_menu_incognito"); + } }
diff --git a/components/collaboration/internal/collaboration_controller.cc b/components/collaboration/internal/collaboration_controller.cc index fcee3fc..58e8541 100644 --- a/components/collaboration/internal/collaboration_controller.cc +++ b/components/collaboration/internal/collaboration_controller.cc
@@ -18,6 +18,7 @@ #include "components/collaboration/public/collaboration_service.h" #include "components/collaboration/public/collaboration_utils.h" #include "components/data_sharing/public/data_sharing_service.h" +#include "components/data_sharing/public/group_data.h" #include "components/data_sharing/public/logger.h" #include "components/data_sharing/public/logger_common.mojom.h" #include "components/data_sharing/public/logger_utils.h" @@ -77,6 +78,10 @@ return "SharingTabGroupUrl"; case StateId::kShowingManageScreen: return "ShowingManageScreen"; + case CollaborationController::StateId::kLeavingGroup: + return "LeavingGroup"; + case CollaborationController::StateId::kDeletingGroup: + return "DeletingGroup"; case StateId::kCleaningUpSharedTabGroup: return "CleaningUpSharedTabGroup"; case StateId::kCancel: @@ -126,7 +131,7 @@ return; case Outcome::kFailure: // The following outcomes should only be used by specific state. - case Outcome::kDeleteOrLeaveGroup: + case Outcome::kGroupLeftOrDeleted: HandleError(); return; } @@ -335,6 +340,8 @@ RecordShareOrManageEvent( GetLogger(), CollaborationServiceShareOrManageEvent::kNotSignedIn); break; + case FlowType::kLeaveOrDelete: + break; } controller->delegate()->ShowAuthenticationUi( @@ -533,60 +540,14 @@ void OnEnter(const ErrorInfo& error) override { switch (controller->flow().type) { - case FlowType::kJoin: { - RecordJoinEvent(GetLogger(), - CollaborationServiceJoinEvent::kFlowRequirementsMet); - - const data_sharing::GroupId group_id = - controller->flow().join_token().group_id; - // Check if user is already part of the group. - if (IsPeopleGroupInDataSharing(group_id)) { - if (IsTabGroupInSync(group_id)) { - RecordJoinEvent( - GetLogger(), - CollaborationServiceJoinEvent::kOpenedExistingGroup); - controller->TransitionTo(StateId::kOpeningLocalTabGroup); - return; - } - - RecordJoinEvent(GetLogger(), CollaborationServiceJoinEvent:: - kFoundCollaborationWithoutTabGroup); - controller->TransitionTo(StateId::kWaitingForSyncAndDataSharingGroup); - return; - } - // If user is not part of the group, do a readgroup to ensure version - // match. - // TODO(haileywang): Do the version check in the preview data and do the - // network requests in parallel instead of one by one. - controller->data_sharing_service()->ReadNewGroup( - controller->flow().join_token(), - base::BindOnce(&CheckingFlowRequirementsState:: - ProcessGroupDataOrFailureOutcome, - local_weak_ptr_factory_.GetWeakPtr())); + case FlowType::kJoin: + CheckJoinFlowRequirements(); break; - } case FlowType::kShareOrManage: - RecordShareOrManageEvent( - GetLogger(), - CollaborationServiceShareOrManageEvent::kFlowRequirementsMet); - - std::optional<tab_groups::SavedTabGroup> sync_group = - controller->tab_group_sync_service()->GetGroup( - controller->flow().either_id()); - if (!sync_group.has_value()) { - RecordShareOrManageEvent( - GetLogger(), - CollaborationServiceShareOrManageEvent::kSyncedTabGroupNotFound); - HandleError(); - return; - } - - if (sync_group.value().is_shared_tab_group()) { - controller->TransitionTo(StateId::kShowingManageScreen); - return; - } - - controller->TransitionTo(StateId::kShowingShareScreen); + CheckShareFlowRequirements(); + break; + case FlowType::kLeaveOrDelete: + CheckLeaveOrDeleteFlowRequirements(); break; } } @@ -620,6 +581,80 @@ controller->TransitionTo(StateId::kAddingUserToGroup); } + void CheckJoinFlowRequirements() { + RecordJoinEvent(GetLogger(), + CollaborationServiceJoinEvent::kFlowRequirementsMet); + + const data_sharing::GroupId group_id = + controller->flow().join_token().group_id; + // Check if user is already part of the group. + if (IsPeopleGroupInDataSharing(group_id)) { + if (IsTabGroupInSync(group_id)) { + RecordJoinEvent(GetLogger(), + CollaborationServiceJoinEvent::kOpenedExistingGroup); + controller->TransitionTo(StateId::kOpeningLocalTabGroup); + return; + } + + RecordJoinEvent( + GetLogger(), + CollaborationServiceJoinEvent::kFoundCollaborationWithoutTabGroup); + controller->TransitionTo(StateId::kWaitingForSyncAndDataSharingGroup); + return; + } + // If user is not part of the group, do a readgroup to ensure version + // match. + // TODO(haileywang): Do the version check in the preview data and do the + // network requests in parallel instead of one by one. + controller->data_sharing_service()->ReadNewGroup( + controller->flow().join_token(), + base::BindOnce( + &CheckingFlowRequirementsState::ProcessGroupDataOrFailureOutcome, + local_weak_ptr_factory_.GetWeakPtr())); + } + + void CheckShareFlowRequirements() { + RecordShareOrManageEvent( + GetLogger(), + CollaborationServiceShareOrManageEvent::kFlowRequirementsMet); + + std::optional<tab_groups::SavedTabGroup> sync_group = + controller->tab_group_sync_service()->GetGroup( + controller->flow().either_id()); + if (!sync_group.has_value()) { + RecordShareOrManageEvent( + GetLogger(), + CollaborationServiceShareOrManageEvent::kSyncedTabGroupNotFound); + HandleError(); + return; + } + + if (sync_group.value().is_shared_tab_group()) { + controller->TransitionTo(StateId::kShowingManageScreen); + return; + } + + controller->TransitionTo(StateId::kShowingShareScreen); + } + + void CheckLeaveOrDeleteFlowRequirements() { + auto group_id_opt = GetGroupIdFromEitherId(controller->flow().either_id()); + if (!group_id_opt.has_value()) { + HandleError(); + return; + } + data_sharing::MemberRole role = + controller->collaboration_service()->GetCurrentUserRoleForGroup( + group_id_opt.value()); + if (role == data_sharing::MemberRole::kMember) { + controller->TransitionTo(StateId::kLeavingGroup); + } else if (role == data_sharing::MemberRole::kOwner) { + controller->TransitionTo(StateId::kDeletingGroup); + } else { + HandleError(); + } + } + base::WeakPtrFactory<CheckingFlowRequirementsState> local_weak_ptr_factory_{ this}; }; @@ -976,10 +1011,27 @@ void OnProcessingFinishedWithSuccess() override { controller->Exit(); } }; -class ShowingManageScreen : public ControllerState { +class ManageGroupControllerState : public ControllerState { + public: + ManageGroupControllerState(StateId id, CollaborationController* controller) + : ControllerState(id, controller) {} + + void ProcessOutcome(Outcome outcome) override { + switch (outcome) { + case Outcome::kGroupLeftOrDeleted: + controller->TransitionTo(StateId::kCleaningUpSharedTabGroup); + return; + default: + ControllerState::ProcessOutcome(outcome); + return; + } + } +}; + +class ShowingManageScreen : public ManageGroupControllerState { public: ShowingManageScreen(StateId id, CollaborationController* controller) - : ControllerState(id, controller) {} + : ManageGroupControllerState(id, controller) {} void OnEnter(const ErrorInfo& error) override { CHECK_EQ(controller->flow().type, FlowType::kShareOrManage); @@ -993,23 +1045,90 @@ local_weak_ptr_factory_.GetWeakPtr())); } - void ProcessOutcome(Outcome outcome) override { - switch (outcome) { - case Outcome::kDeleteOrLeaveGroup: - controller->TransitionTo(StateId::kCleaningUpSharedTabGroup); - return; - default: - ControllerState::ProcessOutcome(outcome); - return; - } - } - void OnProcessingFinishedWithSuccess() override { controller->Exit(); } private: base::WeakPtrFactory<ShowingManageScreen> local_weak_ptr_factory_{this}; }; +class LeavingGroupState : public ManageGroupControllerState { + public: + LeavingGroupState(StateId id, CollaborationController* controller) + : ManageGroupControllerState(id, controller) {} + + void OnEnter(const ErrorInfo& error) override { + controller->delegate()->ShowLeaveDialog( + controller->flow().either_id(), + base::BindOnce(&LeavingGroupState::ProcessOutcome, + local_weak_ptr_factory_.GetWeakPtr())); + } + + void OnProcessingFinishedWithSuccess() override { + auto group_id_opt = GetGroupIdFromEitherId(controller->flow().either_id()); + if (!group_id_opt.has_value()) { + HandleError(); + return; + } + controller->data_sharing_service()->LeaveGroup( + group_id_opt.value(), + base::BindOnce(&LeavingGroupState::ProcessPeopleGroupActionOutcome, + local_weak_ptr_factory_.GetWeakPtr())); + } + + private: + void ProcessPeopleGroupActionOutcome( + data_sharing::DataSharingService::PeopleGroupActionOutcome outcome) { + if (outcome == + data_sharing::DataSharingService::PeopleGroupActionOutcome::kSuccess) { + controller->TransitionTo(StateId::kCleaningUpSharedTabGroup); + return; + } + + HandleError(); + } + + base::WeakPtrFactory<LeavingGroupState> local_weak_ptr_factory_{this}; +}; + +class DeletingGroupState : public ManageGroupControllerState { + public: + DeletingGroupState(StateId id, CollaborationController* controller) + : ManageGroupControllerState(id, controller) {} + + void OnEnter(const ErrorInfo& error) override { + controller->delegate()->ShowDeleteDialog( + controller->flow().either_id(), + base::BindOnce(&DeletingGroupState::ProcessOutcome, + local_weak_ptr_factory_.GetWeakPtr())); + } + + void OnProcessingFinishedWithSuccess() override { + auto group_id_opt = GetGroupIdFromEitherId(controller->flow().either_id()); + if (!group_id_opt.has_value()) { + HandleError(); + return; + } + controller->data_sharing_service()->DeleteGroup( + group_id_opt.value(), + base::BindOnce(&DeletingGroupState::ProcessPeopleGroupActionOutcome, + local_weak_ptr_factory_.GetWeakPtr())); + } + + private: + void ProcessPeopleGroupActionOutcome( + data_sharing::DataSharingService::PeopleGroupActionOutcome outcome) { + if (outcome == + data_sharing::DataSharingService::PeopleGroupActionOutcome::kSuccess) { + controller->TransitionTo(StateId::kCleaningUpSharedTabGroup); + return; + } + + HandleError(); + } + + base::WeakPtrFactory<DeletingGroupState> local_weak_ptr_factory_{this}; +}; + class CleaningUpSharedTabGroupState : public ControllerState { public: CleaningUpSharedTabGroupState(StateId id, CollaborationController* controller) @@ -1021,8 +1140,8 @@ HandleError(); return; } - data_sharing::GroupId group_id = group_id_opt.value(); + controller->tab_group_sync_service()->OnCollaborationRemoved( syncer::CollaborationId(group_id.value())); controller->data_sharing_service()->OnCollaborationGroupRemoved(group_id); @@ -1059,9 +1178,7 @@ CollaborationController::Flow::Flow(FlowType type, const tab_groups::EitherGroupID& either_id) - : type(type), either_id_(either_id) { - DCHECK(type == FlowType::kShareOrManage); -} + : type(type), either_id_(either_id) {} CollaborationController::Flow::Flow(const Flow&) = default; @@ -1172,6 +1289,10 @@ return std::make_unique<SharingTabGroupUrl>(state, this); case StateId::kShowingManageScreen: return std::make_unique<ShowingManageScreen>(state, this); + case StateId::kLeavingGroup: + return std::make_unique<LeavingGroupState>(state, this); + case StateId::kDeletingGroup: + return std::make_unique<DeletingGroupState>(state, this); case StateId::kCleaningUpSharedTabGroup: return std::make_unique<CleaningUpSharedTabGroupState>(state, this); case StateId::kCancel:
diff --git a/components/collaboration/internal/collaboration_controller.h b/components/collaboration/internal/collaboration_controller.h index 6f0b893..a62d695 100644 --- a/components/collaboration/internal/collaboration_controller.h +++ b/components/collaboration/internal/collaboration_controller.h
@@ -76,6 +76,12 @@ // Delegate is showing the manage people screen. kShowingManageScreen, + // Delegate is showing the leave group screen. + kLeavingGroup, + + // Delegate is showing the delete group screen. + kDeletingGroup, + // A shared tab group has been deleted, cleaning up. kCleaningUpSharedTabGroup, @@ -91,7 +97,7 @@ // Join flow constructor. Flow(FlowType type, const data_sharing::GroupToken& token); - // Share flow constructor. + // Share/manage/leave/delete flow constructor. Flow(FlowType type, const tab_groups::EitherGroupID& either_id); ~Flow(); @@ -106,7 +112,6 @@ } const tab_groups::EitherGroupID& either_id() const { - DCHECK_EQ(type, FlowType::kShareOrManage); return either_id_; } @@ -124,7 +129,7 @@ // ID for join flow. const data_sharing::GroupToken join_token_; - // ID for share flow. + // ID for share/manage/leave/delete flow. const tab_groups::EitherGroupID either_id_; data_sharing::GroupToken share_token_; }; @@ -185,7 +190,7 @@ StateId GetStateForTesting(); private: - static constexpr std::array<std::pair<StateId, StateId>, 35> + static constexpr std::array<std::pair<StateId, StateId>, 41> kValidTransitions = {{ // kPending transitions to: // @@ -253,6 +258,8 @@ {StateId::kCheckingFlowRequirements, StateId::kOpeningLocalTabGroup}, {StateId::kCheckingFlowRequirements, StateId::kShowingShareScreen}, {StateId::kCheckingFlowRequirements, StateId::kShowingManageScreen}, + {StateId::kCheckingFlowRequirements, StateId::kLeavingGroup}, + {StateId::kCheckingFlowRequirements, StateId::kDeletingGroup}, {StateId::kCheckingFlowRequirements, StateId::kError}, // kAddingUserToGroup transition to: @@ -316,6 +323,20 @@ // kError: An error occurred while showing the manage people screen. {StateId::kShowingManageScreen, StateId::kCleaningUpSharedTabGroup}, {StateId::kShowingManageScreen, StateId::kError}, + + // kShowingManageScreen transition to: + // + // kCleaningUpSharedTabGroup: After leaving group successfully. + // kError: An error occurred while leaving group. + {StateId::kLeavingGroup, StateId::kCleaningUpSharedTabGroup}, + {StateId::kLeavingGroup, StateId::kError}, + + // kDeletingGroup transition to: + // + // kCleaningUpSharedTabGroup: When deletion has been completed. + // kError: An error occurred while deleting group. + {StateId::kDeletingGroup, StateId::kCleaningUpSharedTabGroup}, + {StateId::kDeletingGroup, StateId::kError}, }}; bool IsValidStateTransition(StateId from, StateId to);
diff --git a/components/collaboration/internal/collaboration_controller_unittest.cc b/components/collaboration/internal/collaboration_controller_unittest.cc index dc44f80..2c95e55 100644 --- a/components/collaboration/internal/collaboration_controller_unittest.cc +++ b/components/collaboration/internal/collaboration_controller_unittest.cc
@@ -16,6 +16,7 @@ #include "components/collaboration/public/collaboration_flow_type.h" #include "components/collaboration/test_support/mock_collaboration_controller_delegate.h" #include "components/collaboration/test_support/mock_collaboration_service.h" +#include "components/data_sharing/public/data_sharing_service.h" #include "components/data_sharing/public/group_data.h" #include "components/data_sharing/test_support/mock_data_sharing_service.h" #include "components/saved_tab_groups/public/saved_tab_group.h" @@ -802,7 +803,7 @@ OnCollaborationRemoved(syncer::CollaborationId(kGroupId.value()))); EXPECT_CALL(*data_sharing_service_, OnCollaborationGroupRemoved(kGroupId)); EXPECT_CALL(*delegate_, OnFlowFinished()); - std::move(manage_dialog_callback).Run(Outcome::kDeleteOrLeaveGroup); + std::move(manage_dialog_callback).Run(Outcome::kGroupLeftOrDeleted); } TEST_F(CollaborationControllerTest, ShareFlowCanceledBeforeSignin) { @@ -857,4 +858,48 @@ metrics::CollaborationServiceShareOrManageEvent::kFlowRequirementsMet, 0); } +TEST_F(CollaborationControllerTest, LeaveFlow) { + // Start leave flow. + tab_groups::LocalTabGroupID local_id = + tab_groups::test::GenerateRandomTabGroupID(); + tab_groups::EitherGroupID either_id = local_id; + SavedTabGroup tab_group(std::u16string(u"title"), + tab_groups::TabGroupColorId::kGrey, {}); + tab_group.SetLocalGroupId(local_id); + tab_group.SetCollaborationId(tab_groups::CollaborationId(kGroupId.value())); + EXPECT_CALL(*tab_group_sync_service_, GetGroup(either_id)) + .WillRepeatedly(Return(tab_group)); + InitializeController(base::DoNothing(), + Flow(FlowType::kLeaveOrDelete, either_id)); + EXPECT_EQ(controller_->GetStateForTesting(), StateId::kPending); + + // Simulate that the user is part of the group as member. + EXPECT_CALL(*collaboration_service_, GetCurrentUserRoleForGroup(kGroupId)) + .WillRepeatedly(Return(data_sharing::MemberRole::kMember)); + base::OnceCallback<void(Outcome)> leave_dialog_callback; + EXPECT_CALL(*delegate_, ShowLeaveDialog(either_id, IsNotNullCallback())) + .WillOnce(MoveArg<1>(&leave_dialog_callback)); + + controller_->SetStateForTesting(StateId::kCheckingFlowRequirements); + EXPECT_EQ(controller_->GetStateForTesting(), StateId::kLeavingGroup); + + // Leave the collaboration group. + base::OnceCallback<void( + data_sharing::DataSharingService::PeopleGroupActionOutcome)> + people_group_action_callback; + EXPECT_CALL(*data_sharing_service_, LeaveGroup(kGroupId, IsNotNullCallback())) + .WillOnce(MoveArg<1>(&people_group_action_callback)); + std::move(leave_dialog_callback).Run(Outcome::kSuccess); + + // Clean up the tab group. + EXPECT_CALL( + *tab_group_sync_service_, + OnCollaborationRemoved(syncer::CollaborationId(kGroupId.value()))); + EXPECT_CALL(*data_sharing_service_, OnCollaborationGroupRemoved(kGroupId)); + EXPECT_CALL(*delegate_, OnFlowFinished()); + std::move(people_group_action_callback) + .Run( + data_sharing::DataSharingService::PeopleGroupActionOutcome::kSuccess); +} + } // namespace collaboration
diff --git a/components/collaboration/internal/collaboration_service_impl.cc b/components/collaboration/internal/collaboration_service_impl.cc index 1134927..211ded5 100644 --- a/components/collaboration/internal/collaboration_service_impl.cc +++ b/components/collaboration/internal/collaboration_service_impl.cc
@@ -104,23 +104,40 @@ std::unique_ptr<CollaborationControllerDelegate> delegate, const tab_groups::EitherGroupID& either_id, CollaborationServiceShareOrManageEntryPoint entry) { - auto it = share_controllers_.find(either_id); - if (it != share_controllers_.end()) { + auto it = collaboration_controllers_.find(either_id); + if (it != collaboration_controllers_.end()) { it->second->delegate()->PromoteCurrentScreen(); return; } - CancelAllFlows(base::BindOnce( - &CollaborationServiceImpl::StartShareOrManageFlowInternal, - weak_ptr_factory_.GetWeakPtr(), std::move(delegate), either_id)); + CancelAllFlows( + base::BindOnce(&CollaborationServiceImpl::StartCollaborationFlowInternal, + weak_ptr_factory_.GetWeakPtr(), std::move(delegate), + either_id, FlowType::kShareOrManage)); RecordShareOrManageEvent(data_sharing_service_->GetLogger(), CollaborationServiceShareOrManageEvent::kStarted); } +void CollaborationServiceImpl::StartLeaveOrDeleteFlow( + std::unique_ptr<CollaborationControllerDelegate> delegate, + const tab_groups::EitherGroupID& either_id, + CollaborationServiceLeaveOrDeleteEntryPoint entry) { + auto it = collaboration_controllers_.find(either_id); + if (it != collaboration_controllers_.end()) { + it->second->delegate()->PromoteCurrentScreen(); + return; + } + + CancelAllFlows( + base::BindOnce(&CollaborationServiceImpl::StartCollaborationFlowInternal, + weak_ptr_factory_.GetWeakPtr(), std::move(delegate), + either_id, FlowType::kLeaveOrDelete)); +} + void CollaborationServiceImpl::CancelAllFlows( base::OnceCallback<void()> finish_callback) { - if (join_controllers_.empty() && share_controllers_.empty()) { + if (join_controllers_.empty() && collaboration_controllers_.empty()) { // Don't post task if we can already start the flow. std::move(finish_callback).Run(); return; @@ -129,7 +146,7 @@ for (const auto& [token, controller] : join_controllers_) { controller->Cancel(); } - for (const auto& [id, controller] : share_controllers_) { + for (const auto& [id, controller] : collaboration_controllers_) { controller->Cancel(); } @@ -224,11 +241,11 @@ } } -void CollaborationServiceImpl::FinishShareFlow( +void CollaborationServiceImpl::FinishCollaborationFlow( const tab_groups::EitherGroupID& group_id) { - auto it = share_controllers_.find(group_id); - if (it != share_controllers_.end()) { - share_controllers_.erase(it); + auto it = collaboration_controllers_.find(group_id); + if (it != collaboration_controllers_.end()) { + collaboration_controllers_.erase(it); } } @@ -378,17 +395,18 @@ weak_ptr_factory_.GetWeakPtr(), token))}); } -void CollaborationServiceImpl::StartShareOrManageFlowInternal( +void CollaborationServiceImpl::StartCollaborationFlowInternal( std::unique_ptr<CollaborationControllerDelegate> delegate, - const tab_groups::EitherGroupID& group_id) { - share_controllers_.insert( - {group_id, + const tab_groups::EitherGroupID& either_id, + FlowType type) { + collaboration_controllers_.insert( + {either_id, std::make_unique<CollaborationController>( - Flow(FlowType::kShareOrManage, group_id), this, - data_sharing_service_.get(), tab_group_sync_service_.get(), - sync_service_.get(), identity_manager_.get(), std::move(delegate), - base::BindOnce(&CollaborationServiceImpl::FinishShareFlow, - weak_ptr_factory_.GetWeakPtr(), group_id))}); + Flow(type, either_id), this, data_sharing_service_.get(), + tab_group_sync_service_.get(), sync_service_.get(), + identity_manager_.get(), std::move(delegate), + base::BindOnce(&CollaborationServiceImpl::FinishCollaborationFlow, + weak_ptr_factory_.GetWeakPtr(), either_id))}); } void CollaborationServiceImpl::OnCollaborationGroupRemoved(
diff --git a/components/collaboration/internal/collaboration_service_impl.h b/components/collaboration/internal/collaboration_service_impl.h index 8886b35..f1b370d 100644 --- a/components/collaboration/internal/collaboration_service_impl.h +++ b/components/collaboration/internal/collaboration_service_impl.h
@@ -57,6 +57,10 @@ std::unique_ptr<CollaborationControllerDelegate> delegate, const tab_groups::EitherGroupID& either_id, CollaborationServiceShareOrManageEntryPoint entry) override; + void StartLeaveOrDeleteFlow( + std::unique_ptr<CollaborationControllerDelegate> delegate, + const tab_groups::EitherGroupID& either_id, + CollaborationServiceLeaveOrDeleteEntryPoint entry) override; void CancelAllFlows(base::OnceCallback<void()> finish_callback) override; ServiceStatus GetServiceStatus() override; data_sharing::MemberRole GetCurrentUserRoleForGroup( @@ -89,7 +93,7 @@ // Called to clean up a flow given a GroupToken. void FinishJoinFlow(const data_sharing::GroupToken& token); - void FinishShareFlow(const tab_groups::EitherGroupID& group_id); + void FinishCollaborationFlow(const tab_groups::EitherGroupID& group_id); private: SyncStatus GetSyncStatus(); @@ -99,9 +103,10 @@ void StartJoinFlowInternal( std::unique_ptr<CollaborationControllerDelegate> delegate, const data_sharing::GroupToken& token); - void StartShareOrManageFlowInternal( + void StartCollaborationFlowInternal( std::unique_ptr<CollaborationControllerDelegate> delegate, - const tab_groups::EitherGroupID& group_id); + const tab_groups::EitherGroupID& either_id, + FlowType type); void OnCollaborationGroupRemoved( const data_sharing::GroupId& group_id, base::OnceCallback<void(bool)> callback, @@ -136,7 +141,7 @@ std::map<data_sharing::GroupToken, std::unique_ptr<CollaborationController>> join_controllers_; std::map<tab_groups::EitherGroupID, std::unique_ptr<CollaborationController>> - share_controllers_; + collaboration_controllers_; base::WeakPtrFactory<CollaborationServiceImpl> weak_ptr_factory_{this}; };
diff --git a/components/collaboration/internal/empty_collaboration_service.cc b/components/collaboration/internal/empty_collaboration_service.cc index 02da120..b3bb8fc4 100644 --- a/components/collaboration/internal/empty_collaboration_service.cc +++ b/components/collaboration/internal/empty_collaboration_service.cc
@@ -28,6 +28,11 @@ const tab_groups::EitherGroupID& either_id, CollaborationServiceShareOrManageEntryPoint entry) {} +void EmptyCollaborationService::StartLeaveOrDeleteFlow( + std::unique_ptr<CollaborationControllerDelegate> delegate, + const tab_groups::EitherGroupID& either_id, + CollaborationServiceLeaveOrDeleteEntryPoint entry) {} + ServiceStatus EmptyCollaborationService::GetServiceStatus() { return ServiceStatus(); }
diff --git a/components/collaboration/internal/empty_collaboration_service.h b/components/collaboration/internal/empty_collaboration_service.h index 372e33e..fa2333f 100644 --- a/components/collaboration/internal/empty_collaboration_service.h +++ b/components/collaboration/internal/empty_collaboration_service.h
@@ -27,6 +27,10 @@ std::unique_ptr<CollaborationControllerDelegate> delegate, const tab_groups::EitherGroupID& either_id, CollaborationServiceShareOrManageEntryPoint entry) override; + void StartLeaveOrDeleteFlow( + std::unique_ptr<CollaborationControllerDelegate> delegate, + const tab_groups::EitherGroupID& either_id, + CollaborationServiceLeaveOrDeleteEntryPoint entry) override; void CancelAllFlows(base::OnceCallback<void()> finish_callback) override; ServiceStatus GetServiceStatus() override; data_sharing::MemberRole GetCurrentUserRoleForGroup(
diff --git a/components/collaboration/public/collaboration_controller_delegate.h b/components/collaboration/public/collaboration_controller_delegate.h index b26da476..f301515 100644 --- a/components/collaboration/public/collaboration_controller_delegate.h +++ b/components/collaboration/public/collaboration_controller_delegate.h
@@ -109,7 +109,7 @@ kSuccess = 0, kFailure = 1, kCancel = 2, - kDeleteOrLeaveGroup = 3, + kGroupLeftOrDeleted = 3, }; CollaborationControllerDelegate() = default; @@ -166,6 +166,14 @@ virtual void ShowManageDialog(const tab_groups::EitherGroupID& either_id, ResultCallback result) = 0; + // Request to show the leave dialog. + virtual void ShowLeaveDialog(const tab_groups::EitherGroupID& either_id, + ResultCallback result) = 0; + + // Request to show the delete dialog. + virtual void ShowDeleteDialog(const tab_groups::EitherGroupID& either_id, + ResultCallback result) = 0; + // Open the local tab group associated with `group_id` in UI. virtual void PromoteTabGroup(const data_sharing::GroupId& group_id, ResultCallback result) = 0;
diff --git a/components/collaboration/public/collaboration_flow_entry_point.h b/components/collaboration/public/collaboration_flow_entry_point.h index fcd98454..0be5cde 100644 --- a/components/collaboration/public/collaboration_flow_entry_point.h +++ b/components/collaboration/public/collaboration_flow_entry_point.h
@@ -48,6 +48,18 @@ }; // LINT.ThenChange(//tools/metrics/histograms/metadata/collaboration_service/enums.xml:CollaborationServiceShareOrManageEntryPoint) +// Types of entry point to start a leave or delete collaboration flow. +// These values are persisted to logs. Entries should not be renumbered and +// number values should never be reused. +// LINT.IfChange(CollaborationServiceLeaveOrDeleteEntryPoint) +// GENERATED_JAVA_ENUM_PACKAGE: ( +// org.chromium.components.collaboration) +enum class CollaborationServiceLeaveOrDeleteEntryPoint { + kUnknown = 0, + kMaxValue = kUnknown, +}; +// LINT.ThenChange(//tools/metrics/histograms/metadata/collaboration_service/enums.xml:CollaborationServiceLeaveOrDeleteEntryPoint) + } // namespace collaboration #endif // COMPONENTS_COLLABORATION_PUBLIC_COLLABORATION_FLOW_ENTRY_POINT_H_
diff --git a/components/collaboration/public/collaboration_flow_type.h b/components/collaboration/public/collaboration_flow_type.h index 25f5898..e214137 100644 --- a/components/collaboration/public/collaboration_flow_type.h +++ b/components/collaboration/public/collaboration_flow_type.h
@@ -12,6 +12,7 @@ enum class FlowType { kJoin = 0, kShareOrManage = 1, + kLeaveOrDelete = 2, }; } // namespace collaboration
diff --git a/components/collaboration/public/collaboration_service.h b/components/collaboration/public/collaboration_service.h index 2c89cdcd..ea9d56c1 100644 --- a/components/collaboration/public/collaboration_service.h +++ b/components/collaboration/public/collaboration_service.h
@@ -81,6 +81,13 @@ const tab_groups::EitherGroupID& either_id, CollaborationServiceShareOrManageEntryPoint entry) = 0; + // Starts a new leave or delete flow. This will cancel all existing ongoing + // flows in the same browser instance. + virtual void StartLeaveOrDeleteFlow( + std::unique_ptr<CollaborationControllerDelegate> delegate, + const tab_groups::EitherGroupID& either_id, + CollaborationServiceLeaveOrDeleteEntryPoint entry) = 0; + // Cancels all the flows currently displayed. virtual void CancelAllFlows(base::OnceCallback<void()> finish_callback) = 0;
diff --git a/components/collaboration/test_support/mock_collaboration_controller_delegate.h b/components/collaboration/test_support/mock_collaboration_controller_delegate.h index 424aef4..2b2b9f2d 100644 --- a/components/collaboration/test_support/mock_collaboration_controller_delegate.h +++ b/components/collaboration/test_support/mock_collaboration_controller_delegate.h
@@ -55,6 +55,16 @@ ResultCallback result), (override)); MOCK_METHOD(void, + ShowLeaveDialog, + (const tab_groups::EitherGroupID& either_id, + ResultCallback result), + (override)); + MOCK_METHOD(void, + ShowDeleteDialog, + (const tab_groups::EitherGroupID& either_id, + ResultCallback result), + (override)); + MOCK_METHOD(void, PromoteTabGroup, (const data_sharing::GroupId& group_id, ResultCallback result), (override));
diff --git a/components/collaboration/test_support/mock_collaboration_service.h b/components/collaboration/test_support/mock_collaboration_service.h index 721a7c5d..e23c5265 100644 --- a/components/collaboration/test_support/mock_collaboration_service.h +++ b/components/collaboration/test_support/mock_collaboration_service.h
@@ -30,6 +30,12 @@ const tab_groups::EitherGroupID& either_id, CollaborationServiceShareOrManageEntryPoint entry), (override)); + MOCK_METHOD(void, + StartLeaveOrDeleteFlow, + (std::unique_ptr<CollaborationControllerDelegate> delegate, + const tab_groups::EitherGroupID& either_id, + CollaborationServiceLeaveOrDeleteEntryPoint entry), + (override)); MOCK_METHOD(void, CancelAllFlows, (base::OnceCallback<void()>), (override)); MOCK_METHOD(ServiceStatus, GetServiceStatus, (), (override)); MOCK_METHOD(data_sharing::MemberRole,
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn index 82519f0..910d8fe4 100644 --- a/components/omnibox/browser/BUILD.gn +++ b/components/omnibox/browser/BUILD.gn
@@ -203,6 +203,8 @@ "enterprise_search_aggregator_provider.h", "enterprise_search_aggregator_suggestions_service.cc", "enterprise_search_aggregator_suggestions_service.h", + "extension_suggestion.cc", + "extension_suggestion.h", "favicon_cache.cc", "favicon_cache.h", "featured_search_provider.cc",
diff --git a/components/omnibox/browser/actions/omnibox_action.cc b/components/omnibox/browser/actions/omnibox_action.cc index 1bdd53e..1b6361ed 100644 --- a/components/omnibox/browser/actions/omnibox_action.cc +++ b/components/omnibox/browser/actions/omnibox_action.cc
@@ -111,6 +111,10 @@ } #endif +gfx::Image OmniboxAction::GetIconImage() const { + return gfx::Image(); +} + size_t OmniboxAction::EstimateMemoryUsage() const { size_t total = 0; total += base::trace_event::EstimateMemoryUsage(url_);
diff --git a/components/omnibox/browser/actions/omnibox_action.h b/components/omnibox/browser/actions/omnibox_action.h index 6b4480b..ce1b53d 100644 --- a/components/omnibox/browser/actions/omnibox_action.h +++ b/components/omnibox/browser/actions/omnibox_action.h
@@ -19,6 +19,7 @@ #include "ui/base/page_transition_types.h" #include "ui/base/window_open_disposition.h" #include "ui/gfx/color_utils.h" +#include "ui/gfx/image/image.h" #include "url/gurl.h" #if (!BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !BUILDFLAG(IS_IOS) @@ -173,6 +174,9 @@ virtual const gfx::VectorIcon& GetVectorIcon() const; #endif + // Returns a custom (non vector icon) image for the action. + virtual gfx::Image GetIconImage() const; + // Estimates RAM usage in bytes for this Action. virtual size_t EstimateMemoryUsage() const;
diff --git a/components/omnibox/browser/actions/omnibox_extension_action.cc b/components/omnibox/browser/actions/omnibox_extension_action.cc index bd7ee89..fb8273e 100644 --- a/components/omnibox/browser/actions/omnibox_extension_action.cc +++ b/components/omnibox/browser/actions/omnibox_extension_action.cc
@@ -6,13 +6,16 @@ #include <utility> +#include "base/base64.h" #include "components/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/gfx/image/image.h" OmniboxExtensionAction::OmniboxExtensionAction( const std::u16string& label, const std::u16string& tooltip, - base::RepeatingClosure on_action_executed) + base::RepeatingClosure on_action_executed, + gfx::Image icon) : OmniboxAction(OmniboxAction::LabelStrings( label, tooltip, @@ -20,7 +23,8 @@ IDS_ACC_OMNIBOX_ACTION_IN_EXTENSION_SUGGEST_SUFFIX), tooltip), GURL()), - on_action_executed_(std::move(on_action_executed)) { + on_action_executed_(std::move(on_action_executed)), + icon_image_(std::move(icon)) { CHECK(on_action_executed_); } @@ -33,3 +37,7 @@ OmniboxActionId OmniboxExtensionAction::ActionId() const { return OmniboxActionId::EXTENSION_ACTION; } + +gfx::Image OmniboxExtensionAction::GetIconImage() const { + return icon_image_; +}
diff --git a/components/omnibox/browser/actions/omnibox_extension_action.h b/components/omnibox/browser/actions/omnibox_extension_action.h index 0d21b01..b7bda65 100644 --- a/components/omnibox/browser/actions/omnibox_extension_action.h +++ b/components/omnibox/browser/actions/omnibox_extension_action.h
@@ -16,17 +16,20 @@ public: OmniboxExtensionAction(const std::u16string& label, const std::u16string& tooltip, - base::RepeatingClosure on_action_executed); + base::RepeatingClosure on_action_executed, + gfx::Image icon); // OmniboxAction: void Execute(ExecutionContext& context) const override; OmniboxActionId ActionId() const override; + gfx::Image GetIconImage() const override; protected: ~OmniboxExtensionAction() override; private: base::RepeatingClosure on_action_executed_; + gfx::Image icon_image_; }; #endif // COMPONENTS_OMNIBOX_BROWSER_ACTIONS_OMNIBOX_EXTENSION_ACTION_H_
diff --git a/components/omnibox/browser/actions/omnibox_extension_action_unittest.cc b/components/omnibox/browser/actions/omnibox_extension_action_unittest.cc index 6e39812..98ee337 100644 --- a/components/omnibox/browser/actions/omnibox_extension_action_unittest.cc +++ b/components/omnibox/browser/actions/omnibox_extension_action_unittest.cc
@@ -18,8 +18,11 @@ #include "components/strings/grit/components_strings.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/window_open_disposition.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/image/image_unittest_util.h" namespace { @@ -29,9 +32,18 @@ scoped_refptr<OmniboxExtensionAction> CreateSimpleAction( base::RepeatingClosure on_action_executed) { return base::MakeRefCounted<OmniboxExtensionAction>( - kLabel, kTooltipText, std::move(on_action_executed)); + kLabel, kTooltipText, std::move(on_action_executed), gfx::Image()); } +scoped_refptr<OmniboxExtensionAction> CreateSimpleActionWithIcon( + base::RepeatingClosure on_action_executed) { + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 16); + bitmap.eraseColor(SK_ColorRED); + gfx::Image image = gfx::Image(gfx::ImageSkia::CreateFrom1xBitmap(bitmap)); + return base::MakeRefCounted<OmniboxExtensionAction>( + kLabel, kTooltipText, std::move(on_action_executed), image); +} } // namespace TEST(OmniboxExtensionActionTest, BasicInfo) { @@ -63,3 +75,16 @@ WindowOpenDisposition::CURRENT_TAB); action->Execute(context); } + +TEST(OmniboxExtensionActionTest, ImageIsSet) { + auto on_action_executed = base::MockRepeatingClosure(); + scoped_refptr<OmniboxExtensionAction> action = + CreateSimpleActionWithIcon(on_action_executed.Get()); + + gfx::Image image = action->GetIconImage(); + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 16); + bitmap.eraseColor(SK_ColorRED); + gfx::test::CheckColors(bitmap.getColor(0, 0), + image.ToSkBitmap()->getColor(0, 0)); +}
diff --git a/components/omnibox/browser/extension_suggestion.cc b/components/omnibox/browser/extension_suggestion.cc new file mode 100644 index 0000000..fde8a71 --- /dev/null +++ b/components/omnibox/browser/extension_suggestion.cc
@@ -0,0 +1,37 @@ +// 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/omnibox/browser/extension_suggestion.h" + +ExtensionSuggestion::Action::Action(std::string name, + std::string label, + std::string tooltip_text, + gfx::Image icon) + : name(std::move(name)), + label(std::move(label)), + tooltip_text(std::move(tooltip_text)), + icon(std::move(icon)) {} +ExtensionSuggestion::Action::~Action() = default; +ExtensionSuggestion::Action::Action(ExtensionSuggestion::Action&& rhs) = + default; +ExtensionSuggestion::Action& ExtensionSuggestion::Action::operator=( + ExtensionSuggestion::Action&& rhs) = default; + +ExtensionSuggestion::ExtensionSuggestion( + std::string content, + std::string description, + bool deletable, + ACMatchClassifications match_classifications, + std::optional<std::vector<Action>> actions, + std::optional<std::string> icon_url) + : content(std::move(content)), + description(std::move(description)), + deletable(deletable), + match_classifications(std::move(match_classifications)), + actions(std::move(actions)), + icon_url(std::move(icon_url)) {} +ExtensionSuggestion::~ExtensionSuggestion() = default; +ExtensionSuggestion::ExtensionSuggestion(ExtensionSuggestion&& rhs) = default; +ExtensionSuggestion& ExtensionSuggestion::operator=(ExtensionSuggestion&& rhs) = + default;
diff --git a/components/omnibox/browser/extension_suggestion.h b/components/omnibox/browser/extension_suggestion.h new file mode 100644 index 0000000..6ba18ce --- /dev/null +++ b/components/omnibox/browser/extension_suggestion.h
@@ -0,0 +1,86 @@ +// 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_OMNIBOX_BROWSER_EXTENSION_SUGGESTION_H_ +#define COMPONENTS_OMNIBOX_BROWSER_EXTENSION_SUGGESTION_H_ + +#include <stdint.h> + +#include <map> +#include <memory> +#include <optional> +#include <string> +#include <vector> + +#include "base/values.h" +#include "components/omnibox/browser/autocomplete_match.h" +#include "components/omnibox/browser/extension_suggestion.h" +#include "ui/gfx/image/image.h" + +// A suggest result parsed from omnibox_api::SuggestResult. +struct ExtensionSuggestion { + // An action button attached to a suggest result. + struct Action { + Action(std::string name, + std::string label, + std::string tooltip_text, + gfx::Image icon); + + ~Action(); + Action(const Action&) = delete; + Action& operator=(const Action&) = delete; + Action(Action&& rhs); + Action& operator=(Action&& rhs); + + // The string sent to the extension in the event corresponding to the user + // clicking on the action. + std::string name; + + // The action button label. + std::string label; + + // The action button hover tooltip text. + std::string tooltip_text; + + // The deserialized image data of an action icon. + gfx::Image icon; + }; + + ExtensionSuggestion(std::string content, + std::string description, + bool deletable, + ACMatchClassifications match_classifications, + std::optional<std::vector<Action>> actions, + std::optional<std::string> icon_url); + + ~ExtensionSuggestion(); + ExtensionSuggestion(const ExtensionSuggestion&) = delete; + ExtensionSuggestion& operator=(const ExtensionSuggestion&) = delete; + ExtensionSuggestion(ExtensionSuggestion&& rhs); + ExtensionSuggestion& operator=(ExtensionSuggestion&& rhs); + + // The text that is put into the URL bar, and that is sent to the extension + // when the user chooses this entry. + std::string content; + + // The text that is displayed in the URL dropdown. Can contain XML-style + // markup for styling. + std::string description; + + // Whether the suggest result can be deleted by the user. + bool deletable; + + // The formatting for the suggestion text that matches the search query text. + ACMatchClassifications match_classifications; + + // An array of actions attached to the suggestion. Only supported for + // suggestions added in unscoped mode. + std::optional<std::vector<Action>> actions; + + // An icon shown on the leading edge of the suggestion in the omnibox + // dropdown. Only supported for suggestions added in unscoped mode. + std::optional<std::string> icon_url; +}; + +#endif // COMPONENTS_OMNIBOX_BROWSER_EXTENSION_SUGGESTION_H_
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc index 8bbf636..4712acbd 100644 --- a/components/omnibox/browser/omnibox_field_trial.cc +++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -583,8 +583,8 @@ // Do not launch for iOS since the feature is not supported in iOS yet. #if !BUILDFLAG(IS_IOS) if (IsEnglishLocale(locale)) { - return !base::FeatureList::IsEnabled( - omnibox::kDisableOnDeviceTailEnglishModel); + return base::FeatureList::IsEnabled( + omnibox::kOnDeviceTailEnableEnglishModel); } #endif // !BUILDFLAG(IS_IOS)
diff --git a/components/omnibox/browser/omnibox_suggestions_watcher.cc b/components/omnibox/browser/omnibox_suggestions_watcher.cc index 8fe2a04..2491fc2 100644 --- a/components/omnibox/browser/omnibox_suggestions_watcher.cc +++ b/components/omnibox/browser/omnibox_suggestions_watcher.cc
@@ -18,10 +18,11 @@ } void OmniboxSuggestionsWatcher::NotifySuggestionsReady( - extensions::api::omnibox::SendSuggestions::Params* suggestions, + const std::vector<ExtensionSuggestion>& suggestions, + const int request_id, const std::string& extension_id) { for (auto& observer : observers_) - observer.OnOmniboxSuggestionsReady(suggestions, extension_id); + observer.OnOmniboxSuggestionsReady(suggestions, request_id, extension_id); } void OmniboxSuggestionsWatcher::NotifyDefaultSuggestionChanged() {
diff --git a/components/omnibox/browser/omnibox_suggestions_watcher.h b/components/omnibox/browser/omnibox_suggestions_watcher.h index a57d646..4fcf8cb 100644 --- a/components/omnibox/browser/omnibox_suggestions_watcher.h +++ b/components/omnibox/browser/omnibox_suggestions_watcher.h
@@ -9,16 +9,7 @@ #include "base/observer_list_types.h" #include "build/build_config.h" #include "components/keyed_service/core/keyed_service.h" - -namespace extensions { -namespace api { -namespace omnibox { -namespace SendSuggestions { -struct Params; -} // namespace SendSuggestions -} // namespace omnibox -} // namespace api -} // namespace extensions +#include "components/omnibox/browser/extension_suggestion.h" // This KeyedService is meant to observe omnibox suggestions and provide // notifications to observers on suggestion changes. @@ -29,7 +20,8 @@ class Observer : public base::CheckedObserver { public: virtual void OnOmniboxSuggestionsReady( - extensions::api::omnibox::SendSuggestions::Params* suggestions, + const std::vector<ExtensionSuggestion>& suggestions, + const int request_id, const std::string& extension_id) {} virtual void OnOmniboxDefaultSuggestionChanged() {} @@ -42,7 +34,8 @@ delete; void NotifySuggestionsReady( - extensions::api::omnibox::SendSuggestions::Params* suggestions, + const std::vector<ExtensionSuggestion>& suggestions, + const int request_id, const std::string& extension_id); void NotifyDefaultSuggestionChanged();
diff --git a/components/omnibox/browser/on_device_head_provider_unittest.cc b/components/omnibox/browser/on_device_head_provider_unittest.cc index 416ced0..3bd4100 100644 --- a/components/omnibox/browser/on_device_head_provider_unittest.cc +++ b/components/omnibox/browser/on_device_head_provider_unittest.cc
@@ -290,8 +290,8 @@ { SCOPED_TRACE("disable tail model for English locales"); base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature( - omnibox::kDisableOnDeviceTailEnglishModel); + scoped_feature_list.InitAndDisableFeature( + omnibox::kOnDeviceTailEnableEnglishModel); provider_->Start(input, false); task_environment_.RunUntilIdle();
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc index 1f6cc068..a07037a 100644 --- a/components/omnibox/common/omnibox_features.cc +++ b/components/omnibox/common/omnibox_features.cc
@@ -155,9 +155,9 @@ "OmniboxOnDeviceHeadProviderKorean", DISABLED); BASE_FEATURE(kOnDeviceTailModel, "OmniboxOnDeviceTailModel", DISABLED); -BASE_FEATURE(kDisableOnDeviceTailEnglishModel, - "OmniboxDisableOnDeviceTailEnglishModel", - DISABLED); +BASE_FEATURE(kOnDeviceTailEnableEnglishModel, + "OmniboxOnDeviceTailEnableEnglishModel", + ENABLED); // If enabled, the relevant AutocompleteProviders will store "title" data in // AutocompleteMatch::contents and "URL" data in AutocompleteMatch::description
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h index a937b4a7..8b8a9eda 100644 --- a/components/omnibox/common/omnibox_features.h +++ b/components/omnibox/common/omnibox_features.h
@@ -59,7 +59,7 @@ BASE_DECLARE_FEATURE(kOnDeviceHeadProviderNonIncognito); BASE_DECLARE_FEATURE(kOnDeviceHeadProviderKorean); BASE_DECLARE_FEATURE(kOnDeviceTailModel); -BASE_DECLARE_FEATURE(kDisableOnDeviceTailEnglishModel); +BASE_DECLARE_FEATURE(kOnDeviceTailEnableEnglishModel); // Provider-specific - These features change the behavior of specific providers. // TODO(crbug.com/40179316): Clean up feature flag used in staged roll-out of
diff --git a/components/optimization_guide/core/model_execution/on_device_model_adaptation_controller.cc b/components/optimization_guide/core/model_execution/on_device_model_adaptation_controller.cc index ff63e04..8df94a5c 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_adaptation_controller.cc +++ b/components/optimization_guide/core/model_execution/on_device_model_adaptation_controller.cc
@@ -22,6 +22,12 @@ namespace { +void CloseAssetsInBackground(on_device_model::AdaptationAssets assets) { + // Close the files on a background thread. + base::ThreadPool::PostTask(FROM_HERE, {base::MayBlock()}, + base::DoNothingWithBoundArgs(std::move(assets))); +} + // Invoked when adaptation assets are loaded. Calls the controller to continue // loading the model if its still alive. Otherwise the loaded assets are closed // in background task. @@ -30,9 +36,7 @@ mojo::PendingReceiver<on_device_model::mojom::OnDeviceModel> model, on_device_model::AdaptationAssets assets) { if (!adaptation_controller) { - // Close the files on a background thread. - base::ThreadPool::PostTask(FROM_HERE, {base::MayBlock()}, - base::DoNothingWithBoundArgs(std::move(assets))); + CloseAssetsInBackground(std::move(assets)); return; } adaptation_controller->LoadAdaptationModelFromAssets(std::move(model), @@ -43,8 +47,9 @@ OnDeviceModelAdaptationController::OnDeviceModelAdaptationController( ModelBasedCapabilityKey feature, - base::WeakPtr<OnDeviceModelServiceController> controller) - : feature_(feature), controller_(controller) { + base::WeakPtr<ModelController> controller, + const on_device_model::AdaptationAssetPaths& asset_paths) + : feature_(feature), controller_(controller), asset_paths_(asset_paths) { CHECK(features::internal::GetOptimizationTargetForCapability(feature_)); } @@ -52,15 +57,13 @@ default; mojo::Remote<on_device_model::mojom::OnDeviceModel>& -OnDeviceModelAdaptationController::GetOrCreateModelRemote( - const on_device_model::AdaptationAssetPaths& adaptation_assets) { +OnDeviceModelAdaptationController::GetOrCreateRemote() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); CHECK(features::internal::GetOptimizationTargetForCapability(feature_)); if (!model_remote_) { base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, {base::MayBlock()}, - base::BindOnce(&on_device_model::LoadAdaptationAssets, - adaptation_assets), + base::BindOnce(&on_device_model::LoadAdaptationAssets, asset_paths_), base::BindOnce(OnAdaptationAssetsLoaded, weak_ptr_factory_.GetWeakPtr(), model_remote_.BindNewPipeAndPassReceiver())); // Disconnects should only happen on a service crash, and we track those @@ -75,12 +78,10 @@ void OnDeviceModelAdaptationController::LoadAdaptationModelFromAssets( mojo::PendingReceiver<on_device_model::mojom::OnDeviceModel> model, on_device_model::AdaptationAssets assets) { - auto& base_model_remote = controller_->base_model_remote(); + auto& base_model_remote = controller_->GetOrCreateRemote(); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!base_model_remote) { - // Close the files on a background thread. - base::ThreadPool::PostTask(FROM_HERE, {base::MayBlock()}, - base::DoNothingWithBoundArgs(std::move(assets))); + CloseAssetsInBackground(std::move(assets)); return; } auto params = on_device_model::mojom::LoadAdaptationParams::New();
diff --git a/components/optimization_guide/core/model_execution/on_device_model_adaptation_controller.h b/components/optimization_guide/core/model_execution/on_device_model_adaptation_controller.h index f83e530..c8f97d8 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_adaptation_controller.h +++ b/components/optimization_guide/core/model_execution/on_device_model_adaptation_controller.h
@@ -13,12 +13,13 @@ namespace optimization_guide { // Controls the on-device model adaptations per feature. -class OnDeviceModelAdaptationController { +class OnDeviceModelAdaptationController final : public ModelController { public: OnDeviceModelAdaptationController( ModelBasedCapabilityKey feature, - base::WeakPtr<OnDeviceModelServiceController> controller); - ~OnDeviceModelAdaptationController(); + base::WeakPtr<ModelController> controller, + const on_device_model::AdaptationAssetPaths& asset_paths); + ~OnDeviceModelAdaptationController() override; OnDeviceModelAdaptationController(const OnDeviceModelAdaptationController&) = delete; @@ -30,13 +31,19 @@ mojo::PendingReceiver<on_device_model::mojom::OnDeviceModel> model, on_device_model::AdaptationAssets assets); - mojo::Remote<on_device_model::mojom::OnDeviceModel>& GetOrCreateModelRemote( - const on_device_model::AdaptationAssetPaths& adaptation_assets); + mojo::Remote<on_device_model::mojom::OnDeviceModel>& GetOrCreateRemote() + override; + + base::WeakPtr<OnDeviceModelAdaptationController> GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); + } private: ModelBasedCapabilityKey feature_; - base::WeakPtr<OnDeviceModelServiceController> controller_; + base::WeakPtr<ModelController> controller_; + + on_device_model::AdaptationAssetPaths asset_paths_; mojo::Remote<on_device_model::mojom::OnDeviceModel> model_remote_;
diff --git a/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.cc b/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.cc index 2e69731..af66270f 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.cc +++ b/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.cc
@@ -100,6 +100,11 @@ const OnDeviceModelAdaptationMetadata&) = default; OnDeviceModelAdaptationMetadata::~OnDeviceModelAdaptationMetadata() = default; +bool OnDeviceModelAdaptationMetadata::operator==( + const OnDeviceModelAdaptationMetadata& other) const { + return version_ == other.version_ && asset_paths_ == other.asset_paths_; +} + OnDeviceModelAdaptationLoader::OnDeviceModelAdaptationLoader( ModelBasedCapabilityKey feature, OptimizationGuideModelProvider* model_provider,
diff --git a/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.h b/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.h index 7bfd341..8786a7b 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.h +++ b/components/optimization_guide/core/model_execution/on_device_model_adaptation_loader.h
@@ -36,6 +36,8 @@ OnDeviceModelAdaptationMetadata(const OnDeviceModelAdaptationMetadata&); ~OnDeviceModelAdaptationMetadata(); + bool operator==(const OnDeviceModelAdaptationMetadata& other) const; + const on_device_model::AdaptationAssetPaths* asset_paths() const { return base::OptionalToPtr(asset_paths_); }
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc b/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc index f6f7735..5dca81d 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc +++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc
@@ -184,16 +184,15 @@ } CHECK(base_model_controller_->model_metadata()); - on_device_model::ModelAssetPaths model_paths = - base_model_controller_->PopulateModelPaths(); CHECK(features::internal::GetOptimizationTargetForCapability(feature)); auto* adaptation_metadata = GetFeatureMetadata(feature); CHECK(adaptation_metadata); OnDeviceOptions opts; opts.model_client = std::make_unique<OnDeviceModelClient>( - feature, weak_ptr_factory_.GetWeakPtr(), model_paths, - base::OptionalFromPtr(adaptation_metadata->asset_paths())); + feature, weak_ptr_factory_.GetWeakPtr(), + base_model_controller_->GetOrCreateFeatureController( + feature, base::OptionalFromPtr(adaptation_metadata->asset_paths()))); opts.model_versions = GetModelVersions(*base_model_controller_->model_metadata(), safety_client_, adaptation_metadata->version()); @@ -232,27 +231,6 @@ base::DoNothingWithBoundArgs(std::move(controller))))); } -mojo::Remote<on_device_model::mojom::OnDeviceModel>& -OnDeviceModelServiceController::GetOrCreateModelRemote( - ModelBasedCapabilityKey feature, - const on_device_model::ModelAssetPaths& model_paths, - base::optional_ref<const on_device_model::AdaptationAssetPaths> - adaptation_assets) { - base_model_controller_->GetOrCreateRemote(model_paths); - if (!adaptation_assets.has_value()) { - return base_model_controller_->DirectUse(); - } - auto it = model_adaptation_controllers_.find(feature); - if (it == model_adaptation_controllers_.end()) { - it = model_adaptation_controllers_ - .emplace( - std::piecewise_construct, std::forward_as_tuple(feature), - std::forward_as_tuple(feature, weak_ptr_factory_.GetWeakPtr())) - .first; - } - return it->second.GetOrCreateModelRemote(*adaptation_assets); -} - void OnDeviceModelServiceController::SetLanguageDetectionModel( base::optional_ref<const ModelInfo> model_info) { safety_client_.SetLanguageDetectionModel(model_info); @@ -269,7 +247,6 @@ std::unique_ptr<OnDeviceModelMetadata> model_metadata) { bool did_model_change = !model_metadata.get() != !base_model_controller_->model_metadata(); - model_adaptation_controllers_.clear(); base_model_controller_.emplace(weak_ptr_factory_.GetSafeRef(), std::move(model_metadata)); @@ -283,13 +260,19 @@ std::unique_ptr<OnDeviceModelAdaptationMetadata> adaptation_metadata) { if (!adaptation_metadata) { model_adaptation_metadata_.erase(feature); - } else { - model_adaptation_metadata_.emplace(feature, *adaptation_metadata); + base_model_controller_->EraseController(feature); + NotifyModelAvailabilityChange(feature); + return; } - auto it = model_adaptation_controllers_.find(feature); - if (it != model_adaptation_controllers_.end()) { - model_adaptation_controllers_.erase(it); + auto it = model_adaptation_metadata_.find(feature); + if (it != model_adaptation_metadata_.end() && + it->second == *adaptation_metadata) { + // Duplicate update (can be caused by multiple profiles). + // Don't invalidate the existing controller. + return; } + model_adaptation_metadata_.emplace(feature, *adaptation_metadata); + base_model_controller_->EraseController(feature); NotifyModelAvailabilityChange(feature); } @@ -310,13 +293,10 @@ OnDeviceModelServiceController::OnDeviceModelClient::OnDeviceModelClient( ModelBasedCapabilityKey feature, base::WeakPtr<OnDeviceModelServiceController> controller, - const on_device_model::ModelAssetPaths& model_paths, - base::optional_ref<const on_device_model::AdaptationAssetPaths> - adaptation_assets) + base::WeakPtr<ModelController> model_controller) : feature_(feature), - controller_(controller), - model_paths_(model_paths), - adaptation_assets_(adaptation_assets.CopyAsOptional()) {} + controller_(std::move(controller)), + model_controller_(std::move(model_controller)) {} OnDeviceModelServiceController::OnDeviceModelClient::~OnDeviceModelClient() = default; @@ -324,11 +304,11 @@ std::unique_ptr<OnDeviceOptions::Client> OnDeviceModelServiceController::OnDeviceModelClient::Clone() const { return std::make_unique<OnDeviceModelServiceController::OnDeviceModelClient>( - feature_, controller_, model_paths_, adaptation_assets_); + feature_, controller_, model_controller_); } bool OnDeviceModelServiceController::OnDeviceModelClient::ShouldUse() { - return controller_ && + return controller_ && model_controller_ && controller_->access_controller_->ShouldStartNewSession() == OnDeviceModelEligibilityReason::kSuccess; } @@ -336,9 +316,8 @@ void OnDeviceModelServiceController::OnDeviceModelClient::StartSession( mojo::PendingReceiver<on_device_model::mojom::Session> pending, on_device_model::mojom::SessionParamsPtr params) { - controller_ - ->GetOrCreateModelRemote(feature_, model_paths_, adaptation_assets_) - ->StartSession(std::move(pending), std::move(params)); + model_controller_->GetOrCreateRemote()->StartSession(std::move(pending), + std::move(params)); } void OnDeviceModelServiceController::OnDeviceModelClient:: @@ -426,16 +405,44 @@ OnDeviceModelServiceController::BaseModelController::~BaseModelController() = default; +base::WeakPtr<ModelController> OnDeviceModelServiceController:: + BaseModelController::GetOrCreateFeatureController( + ModelBasedCapabilityKey feature, + base::optional_ref<const on_device_model::AdaptationAssetPaths> + adaptation_assets) { + if (!adaptation_assets.has_value()) { + has_direct_use_ = true; + return weak_ptr_factory_.GetWeakPtr(); + } + auto it = model_adaptation_controllers_.find(feature); + if (it == model_adaptation_controllers_.end()) { + it = model_adaptation_controllers_ + .emplace(std::piecewise_construct, std::forward_as_tuple(feature), + std::forward_as_tuple(feature, GetWeakPtr(), + *adaptation_assets)) + .first; + } + // Path should be equal. + return it->second.GetWeakPtr(); +} + +void OnDeviceModelServiceController::BaseModelController::EraseController( + ModelBasedCapabilityKey feature) { + auto it = model_adaptation_controllers_.find(feature); + if (it != model_adaptation_controllers_.end()) { + model_adaptation_controllers_.erase(it); + } +} + mojo::Remote<on_device_model::mojom::OnDeviceModel>& -OnDeviceModelServiceController::BaseModelController::GetOrCreateRemote( - const on_device_model::ModelAssetPaths& model_paths) { +OnDeviceModelServiceController::BaseModelController::GetOrCreateRemote() { if (remote_) { return remote_; } controller_->service_client_.AddPendingUsage(); // Warm up the service. base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, {base::MayBlock()}, - base::BindOnce(&on_device_model::LoadModelAssets, model_paths), + base::BindOnce(&on_device_model::LoadModelAssets, PopulateModelPaths()), base::BindOnce( [](base::WeakPtr<BaseModelController> self, mojo::PendingReceiver<on_device_model::mojom::OnDeviceModel> @@ -453,14 +460,9 @@ &BaseModelController::OnDisconnect, base::Unretained(this))); // By default the model will be reset immediately when idle. If a feature is // going using the base model, the idle handler will be set explicitly there. - remote_.reset_on_idle_timeout(base::TimeDelta()); - return remote_; -} - -mojo::Remote<on_device_model::mojom::OnDeviceModel>& -OnDeviceModelServiceController::BaseModelController::DirectUse() { - // The base model is being used by a feature directly, so extend idle timeout. - remote_.reset_on_idle_timeout(features::GetOnDeviceModelIdleTimeout()); + remote_.reset_on_idle_timeout(has_direct_use_ + ? features::GetOnDeviceModelIdleTimeout() + : base::TimeDelta()); return remote_; } @@ -509,8 +511,8 @@ } mojo::Remote<on_device_model::mojom::Session> session; - GetOrCreateRemote(PopulateModelPaths()) - ->StartSession(session.BindNewPipeAndPassReceiver(), nullptr); + GetOrCreateRemote()->StartSession(session.BindNewPipeAndPassReceiver(), + nullptr); model_validator_ = std::make_unique<OnDeviceModelValidator>( model_metadata_->validation_config(), base::BindOnce(&BaseModelController::FinishValidation, @@ -527,4 +529,7 @@ access_controller().OnValidationFinished(result); } +ModelController::ModelController() = default; +ModelController::~ModelController() = default; + } // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller.h b/components/optimization_guide/core/model_execution/on_device_model_service_controller.h index 2bfb56f9..4e216ae 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_service_controller.h +++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller.h
@@ -53,6 +53,18 @@ class OnDeviceModelMetadata; class OnDeviceModelAdaptationController; +class ModelController { + public: + ModelController(); + virtual ~ModelController() = 0; + + ModelController(ModelController&) = delete; + ModelController& operator=(ModelController&) = delete; + + virtual mojo::Remote<on_device_model::mojom::OnDeviceModel>& + GetOrCreateRemote() = 0; +}; + // Controls the lifetime of the on-device model service, loading and unloading // of the models, and executing them via the service. // @@ -141,17 +153,17 @@ } private: - class BaseModelController final { + class BaseModelController final : public ModelController { public: explicit BaseModelController( base::SafeRef<OnDeviceModelServiceController> controller, std::unique_ptr<OnDeviceModelMetadata> model_metadata); - ~BaseModelController(); + ~BaseModelController() override; // Ensures the service is running and base model remote is created. // TODO(holte): Don't take paths as an argument. - mojo::Remote<on_device_model::mojom::OnDeviceModel>& GetOrCreateRemote( - const on_device_model::ModelAssetPaths& model_paths); + mojo::Remote<on_device_model::mojom::OnDeviceModel>& GetOrCreateRemote() + override; // Return the remote for direct use by the feature, adjusting idle timeout. mojo::Remote<on_device_model::mojom::OnDeviceModel>& DirectUse(); @@ -165,6 +177,17 @@ OnDeviceModelMetadata* model_metadata() { return model_metadata_.get(); } + base::WeakPtr<BaseModelController> GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); + } + + base::WeakPtr<ModelController> GetOrCreateFeatureController( + ModelBasedCapabilityKey key, + base::optional_ref<const on_device_model::AdaptationAssetPaths> + adaptation_assets); + + void EraseController(ModelBasedCapabilityKey key); + private: OnDeviceModelAccessController& access_controller() { return *controller_->access_controller_; @@ -185,8 +208,18 @@ // Called when validation has finished or failed. void FinishValidation(OnDeviceModelValidationResult result); + // The service controller that owns this. base::SafeRef<OnDeviceModelServiceController> controller_; + // The metadata of the model this can load. std::unique_ptr<OnDeviceModelMetadata> model_metadata_; + + // Whether any feature uses this without an adaptation. + bool has_direct_use_ = false; + + // Controllers for adaptations that depend on this model. + std::map<ModelBasedCapabilityKey, OnDeviceModelAdaptationController> + model_adaptation_controllers_; + std::unique_ptr<OnDeviceModelValidator> model_validator_; mojo::Remote<on_device_model::mojom::OnDeviceModel> remote_; base::WeakPtrFactory<BaseModelController> weak_ptr_factory_{this}; @@ -197,9 +230,7 @@ OnDeviceModelClient( ModelBasedCapabilityKey feature, base::WeakPtr<OnDeviceModelServiceController> controller, - const on_device_model::ModelAssetPaths& model_paths, - base::optional_ref<const on_device_model::AdaptationAssetPaths> - adaptation_assets); + base::WeakPtr<ModelController> model_controller); ~OnDeviceModelClient() override; std::unique_ptr<OnDeviceOptions::Client> Clone() const override; bool ShouldUse() override; @@ -211,22 +242,12 @@ private: ModelBasedCapabilityKey feature_; base::WeakPtr<OnDeviceModelServiceController> controller_; - on_device_model::ModelAssetPaths model_paths_; - - // Model adaptation assets are populated when it was required. - std::optional<on_device_model::AdaptationAssetPaths> adaptation_assets_; + base::WeakPtr<ModelController> model_controller_; }; friend class OnDeviceModelAdaptationController; friend class OnDeviceModelClient; friend class base::RefCounted<OnDeviceModelServiceController>; - // Ensures the service is running and provides a remote for the model. - mojo::Remote<on_device_model::mojom::OnDeviceModel>& GetOrCreateModelRemote( - ModelBasedCapabilityKey feature, - const on_device_model::ModelAssetPaths& model_paths, - base::optional_ref<const on_device_model::AdaptationAssetPaths> - adaptation_assets); - // Called when the service disconnects unexpectedly. void OnServiceDisconnected(on_device_model::ServiceDisconnectReason reason); @@ -245,12 +266,6 @@ on_device_model::ServiceClient service_client_; SafetyClient safety_client_; - // Maintains the live model adaptation controllers per feature. Created when - // model adaptation is needed for a feature, and removed when adaptation - // remote gets disconnected. - std::map<ModelBasedCapabilityKey, OnDeviceModelAdaptationController> - model_adaptation_controllers_; - // Map from feature to its adaptation assets. Present only for features that // have valid model adaptation. It could be missing for features that require // model adaptation, but they have not been loaded yet.
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc b/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc index e6643e1..a28c8d1 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc +++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc
@@ -734,7 +734,9 @@ TEST_F(OnDeviceModelServiceControllerTest, MidSessionModelUpdate) { Initialize(standard_assets_); - auto session = CreateSession(); + auto session = test_controller_->CreateSession( + kFeature, base::BindRepeating(BadRequestRemote), logger_.GetWeakPtr(), + /*config_params=*/std::nullopt); // Simulate a model update. FakeBaseModelAsset next_model({ @@ -743,12 +745,10 @@ on_device_component_state_manager_.SetReady(next_model); task_environment_.RunUntilIdle(); - // Verify the existing session still works. + // Existing session will fail / fallback to remote. session->ExecuteModel(PageUrlRequest("foo"), response_.GetStreamingCallback()); - ASSERT_TRUE(response_.GetFinalStatus()); - // Note that the session does not execute with the new model. - EXPECT_EQ(*response_.value(), "Context: execute:foo off:0 max:1024\n"); + ASSERT_FALSE(response_.GetFinalStatus()); } TEST_F(OnDeviceModelServiceControllerTest, SessionBeforeAndAfterModelUpdate) {
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal index 6200c82..b9de30f 160000 --- a/components/optimization_guide/internal +++ b/components/optimization_guide/internal
@@ -1 +1 @@ -Subproject commit 6200c82b11623748653bcac9e5293f347bf072fb +Subproject commit b9de30fc7bc555c84f54acac23b132a3fc37aad0
diff --git a/components/page_load_metrics/google/browser/gws_page_load_metrics_observer.cc b/components/page_load_metrics/google/browser/gws_page_load_metrics_observer.cc index f8c24e4..880e0bb 100644 --- a/components/page_load_metrics/google/browser/gws_page_load_metrics_observer.cc +++ b/components/page_load_metrics/google/browser/gws_page_load_metrics_observer.cc
@@ -55,6 +55,8 @@ HISTOGRAM_PREFIX "NavigationTiming.NavigationStartToFirstLoaderCallback"; const char kHistogramGWSNavigationStartToOnComplete[] = HISTOGRAM_PREFIX "NavigationTiming.NavigationStartToOnComplete"; +const char kHistogramGWSCreateStreamDelay[] = + HISTOGRAM_PREFIX "NavigationTiming.CreateStreamDelay"; const char kHistogramGWSInitializeStreamDelay[] = HISTOGRAM_PREFIX "NavigationTiming.InitializeStreamDelay"; @@ -410,6 +412,8 @@ PAGE_LOAD_SHORT_HISTOGRAM( internal::kHistogramGWSConnectTimingFinalRequestSslDelay, timing.final_request_ssl_delay); + PAGE_LOAD_SHORT_HISTOGRAM(internal::kHistogramGWSCreateStreamDelay, + timing.create_stream_delay); PAGE_LOAD_SHORT_HISTOGRAM(internal::kHistogramGWSInitializeStreamDelay, timing.initialize_stream_delay);
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentDetailsUpdateConnection.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentDetailsUpdateConnection.java index cef7ce40..236ca927 100644 --- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentDetailsUpdateConnection.java +++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentDetailsUpdateConnection.java
@@ -10,6 +10,7 @@ import android.content.ServiceConnection; import android.os.IBinder; +import org.chromium.base.Log; import org.chromium.build.annotations.NullMarked; /** @@ -19,9 +20,11 @@ */ @NullMarked public class PaymentDetailsUpdateConnection implements ServiceConnection { + private static final String TAG = "PaymentDetailUpdate"; private final Context mContext; private final Intent mPaymentAppServiceIntent; private final IPaymentDetailsUpdateService.Stub mChromiumService; + private final String mServiceName; private boolean mIsBindingInitiated; /** @@ -44,11 +47,16 @@ mContext = context; mPaymentAppServiceIntent = paymentAppServiceIntent; mChromiumService = chromiumService; + mServiceName = + mPaymentAppServiceIntent != null && mPaymentAppServiceIntent.getComponent() != null + ? mPaymentAppServiceIntent.getComponent().getClassName() + : ""; } /** Connect to the service. */ public void connectToService() { mIsBindingInitiated = true; + Log.i(TAG, "Connecting to \"%s\".", mServiceName); try { // "Regardless of the return value, you should later call // unbindService(ServiceConnection) to release the connection." @@ -62,6 +70,11 @@ // be found. Call unbindService(ServiceConnection) to release the connection when this // exception is thrown." // https://developer.android.com/reference/android/content/Context#bindService(android.content.Intent,%20android.content.ServiceConnection,%20int) + Log.e( + TAG, + "No permission to connect to \"%s\" or it cannot be found: %s", + mServiceName, + e.getMessage()); terminateConnection(); } } @@ -70,6 +83,7 @@ @Override public void onServiceConnected(ComponentName name, IBinder service) { if (service == null) { + Log.e(TAG, "Null service \"%s\".", mServiceName); terminateConnection(); return; } @@ -77,15 +91,18 @@ IPaymentDetailsUpdateServiceCallback paymentAppService = IPaymentDetailsUpdateServiceCallback.Stub.asInterface(service); if (paymentAppService == null) { + Log.e(TAG, "Mismatched service interface \"%s\".", mServiceName); terminateConnection(); return; } + Log.i(TAG, "Sending payment details upate service to \"%s\".", mServiceName); try { paymentAppService.setPaymentDetailsUpdateService(mChromiumService); } catch (Throwable e) { // Many undocumented exceptions are not caught in the remote Service but passed on // to the Service caller, see writeException in Parcel.java. + Log.e(TAG, "Exception in remote service \"%s\": %s", mServiceName, e.getMessage()); terminateConnection(); } } @@ -99,6 +116,7 @@ // receive a call to onServiceConnected(ComponentName, IBinder) when the Service is next // running." // https://developer.android.com/reference/android/content/ServiceConnection#onServiceDisconnected(android.content.ComponentName) + Log.i(TAG, "Service \"%s\" disconnected.", mServiceName); terminateConnection(); } @@ -110,6 +128,7 @@ // with this ServiceConnection even if this callback was invoked following // Context.bindService() bindService()." // https://developer.android.com/reference/android/content/ServiceConnection#onNullBinding(android.content.ComponentName) + Log.e(TAG, "Null binding for service \"%s\".", mServiceName); terminateConnection(); } @@ -121,12 +140,14 @@ // with this ServiceConnection even if this callback was invoked following // Context.bindService() bindService()." // https://developer.android.com/reference/android/content/ServiceConnection#onBindingDied(android.content.ComponentName) + Log.e(TAG, "Service \"%s\" binding died.", mServiceName); terminateConnection(); } /** Disconnect from the service, if still connected, and release the tracking resources. */ public void terminateConnection() { if (mIsBindingInitiated) { + Log.i(TAG, "Terminating connection to service \"%s\".", mServiceName); mContext.unbindService(/* serviceConnection= */ this); mIsBindingInitiated = false; }
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/intent/IsReadyToPayServiceHelper.java b/components/payments/content/android/java/src/org/chromium/components/payments/intent/IsReadyToPayServiceHelper.java index 841250e..d25914b 100644 --- a/components/payments/content/android/java/src/org/chromium/components/payments/intent/IsReadyToPayServiceHelper.java +++ b/components/payments/content/android/java/src/org/chromium/components/payments/intent/IsReadyToPayServiceHelper.java
@@ -12,10 +12,9 @@ import android.os.IBinder; import android.os.RemoteException; -import androidx.annotation.VisibleForTesting; - import org.chromium.IsReadyToPayService; import org.chromium.IsReadyToPayServiceCallback; +import org.chromium.base.Log; import org.chromium.base.metrics.RecordHistogram; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; @@ -25,6 +24,8 @@ @NullMarked public class IsReadyToPayServiceHelper extends IsReadyToPayServiceCallback.Stub implements ServiceConnection { + private static final String TAG = "IsReadyToPayService"; + /** The maximum number of milliseconds to wait for a response from a READY_TO_PAY service. */ private long mReadyToPayTimeoutMs = 2000; @@ -40,6 +41,7 @@ private boolean mIsServiceConnected; private Handler mHandler; private Intent mIsReadyToPayIntent; + private String mServiceName; /** The callback that returns the result (success or error) to the helper's caller. */ public interface ResultHandler { @@ -69,6 +71,10 @@ mResultHandler = resultHandler; mHandler = new Handler(); mIsReadyToPayIntent = isReadyToPayIntent; + mServiceName = + mIsReadyToPayIntent != null && mIsReadyToPayIntent.getComponent() != null + ? mIsReadyToPayIntent.getComponent().getClassName() + : ""; } /** @@ -76,6 +82,7 @@ * asynchronously. Note that resultHandler would be invoked only once. */ public void query() { + Log.i(TAG, "Connecting to \"%s\".", mServiceName); try { // This method returns "true if the system is in the process of bringing up a // service that your client has permission to bind to; false if the system couldn't @@ -88,7 +95,11 @@ mIsReadyToPayIntent, /* serviceConnection= */ this, Context.BIND_AUTO_CREATE); + if (!mIsServiceBindingInitiated) { + Log.e(TAG, "Could not find \"%s\" or no permission to connect.", mServiceName); + } } catch (SecurityException e) { + Log.e(TAG, "Error connecting to \"%s\": %s", mServiceName, e.getMessage()); // Intentionally blank, so mIsServiceBindingInitiated is false. } @@ -100,6 +111,7 @@ mHandler.postDelayed( () -> { if (!mIsServiceConnected) { + Log.e(TAG, "Timeout connecting to \"%s\".", mServiceName); reportError(); } }, @@ -116,6 +128,7 @@ IsReadyToPayService isReadyToPayService = IsReadyToPayService.Stub.asInterface(service); if (isReadyToPayService == null) { + Log.e(TAG, "Interface mismatch in \"%s\".", mServiceName); reportError(); return; } @@ -124,11 +137,14 @@ "PaymentRequest.PrePurchaseQuery", PrePurchaseQuery.ANDROID_INTENT, PrePurchaseQuery.MAX_VALUE); + + Log.i(TAG, "Querying \"%s\".", mServiceName); try { isReadyToPayService.isReadyToPay(/* callback= */ this); } catch (Throwable e) { // Many undocumented exceptions are not caught in the remote Service but passed on // to the Service caller, see writeException in Parcel.java. + Log.e(TAG, "Error in remote service \"%s\": %s.", mServiceName, e.getMessage()); reportError(); return; } @@ -144,6 +160,7 @@ @Override public void onServiceDisconnected(ComponentName name) { // Do not wait for the service to restart. + Log.i(TAG, "\"%s\" disconnected.", mServiceName); reportError(); } @@ -151,6 +168,11 @@ @Override public void handleIsReadyToPay(boolean isReadyToPay) throws RemoteException { if (mResultHandler == null) return; + if (isReadyToPay) { + Log.i(TAG, "\"%s\": Ready to pay.", mServiceName); + } else { + Log.e(TAG, "\"%s\": Not ready to pay.", mServiceName); + } mResultHandler.onIsReadyToPayServiceResponse(isReadyToPay); mResultHandler = null; destroy(); @@ -168,17 +190,16 @@ if (mIsServiceBindingInitiated) { // ServiceConnection "parameter must not be null." // https://developer.android.com/reference/android/content/Context.html#unbindService(android.content.ServiceConnection) + Log.i(TAG, "Terminating connection to \"%s\".", mServiceName); mContext.unbindService(/* serviceConnection= */ this); mIsServiceBindingInitiated = false; } mHandler.removeCallbacksAndMessages(null); } - /** * @param timeoutForTest The number of milliseconds to use for timeouts in tests. */ - @VisibleForTesting public void setTimeoutsMsForTesting(long timeoutForTesting) { mServiceConnectionTimeoutMs = timeoutForTesting; mReadyToPayTimeoutMs = timeoutForTesting;
diff --git a/components/performance_manager/scenarios/browser_performance_scenarios.cc b/components/performance_manager/scenarios/browser_performance_scenarios.cc index fdcb059..5fc7bb9 100644 --- a/components/performance_manager/scenarios/browser_performance_scenarios.cc +++ b/components/performance_manager/scenarios/browser_performance_scenarios.cc
@@ -9,6 +9,7 @@ #include <utility> #include "base/check_op.h" +#include "base/containers/span.h" #include "base/memory/raw_ptr.h" #include "base/memory/read_only_shared_memory_region.h" #include "base/memory/structured_shared_memory.h" @@ -26,6 +27,7 @@ #include "components/performance_manager/scenarios/performance_scenario_data.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" +#include "third_party/perfetto/include/perfetto/tracing/string_helpers.h" #include "third_party/perfetto/include/perfetto/tracing/track.h" namespace performance_manager { @@ -34,6 +36,21 @@ namespace { +void MaybeEmitNestingChangeEvent( + const perfetto::NamedTrack* track, + size_t old_nesting_level, + size_t new_nesting_level, + base::span<const perfetto::StaticString> event_names) { + // Close trace events for each removed nesting level. + for (size_t i = old_nesting_level; track && i > new_nesting_level; --i) { + TRACE_EVENT_END("performance_scenarios", *track); + } + // Open trace events for each added nesting level. + for (size_t i = old_nesting_level; track && i < new_nesting_level; ++i) { + TRACE_EVENT_BEGIN("performance_scenarios", event_names.at(i), *track); + } +} + // Generic methods that change according to the Scenario type. template <typename Scenario> struct ScenarioTraits { @@ -42,11 +59,14 @@ // Returns a reference to the Scenario slot in shared memory. std::atomic<Scenario>& ScenarioRef(); - // Opens a trace event for `scenario` if a tracing track is registered. - void MaybeBeginTraceEvent(Scenario scenario) const; + // Returns the trace event nesting level for `scenario`. Implement this if + // this Scenario type nests cleanly in traces. + size_t NestingLevel(Scenario scenario) const; - // Closes the trace event for `scenario` if a tracing track is registered. - void MaybeEndTraceEvent(Scenario scenario) const; + // Records trace events for a switch from `old_scenario` to `new_scenario` if + // a tracing track is registered. + void MaybeRecordTraceEvent(Scenario old_scenario, + Scenario new_scenario) const; }; template <> @@ -58,46 +78,26 @@ return state_ptr->shared_state().WritableRef().loading; } - void MaybeBeginTraceEvent(LoadingScenario scenario) const { - if (!state_ptr->loading_tracing_track()) { - return; - } + size_t NestingLevel(LoadingScenario scenario) const { switch (scenario) { case LoadingScenario::kNoPageLoading: - // No trace event. - return; + return 0; case LoadingScenario::kBackgroundPageLoading: - TRACE_EVENT_BEGIN("performance_scenarios", "BackgroundPageLoading", - *state_ptr->loading_tracing_track()); - return; + return 1; case LoadingScenario::kVisiblePageLoading: - TRACE_EVENT_BEGIN("performance_scenarios", "VisiblePageLoading", - *state_ptr->loading_tracing_track()); - return; + return 2; case LoadingScenario::kFocusedPageLoading: - TRACE_EVENT_BEGIN("performance_scenarios", "FocusedPageLoading", - *state_ptr->loading_tracing_track()); - return; + return 3; } NOTREACHED(); } - void MaybeEndTraceEvent(LoadingScenario scenario) const { - if (!state_ptr->loading_tracing_track()) { - return; - } - switch (scenario) { - case LoadingScenario::kNoPageLoading: - // No trace event. - return; - case LoadingScenario::kBackgroundPageLoading: - case LoadingScenario::kVisiblePageLoading: - case LoadingScenario::kFocusedPageLoading: - TRACE_EVENT_END("performance_scenarios", - *state_ptr->loading_tracing_track()); - return; - } - NOTREACHED(); + void MaybeRecordTraceEvent(LoadingScenario old_scenario, + LoadingScenario new_scenario) const { + MaybeEmitNestingChangeEvent( + state_ptr->loading_tracing_track(), NestingLevel(old_scenario), + NestingLevel(new_scenario), + {"AnyPageLoading", "VisiblePageLoading", "FocusedPageLoading"}); } raw_ptr<PerformanceScenarioData> state_ptr; @@ -112,36 +112,21 @@ return state_ptr->shared_state().WritableRef().input; } - void MaybeBeginTraceEvent(InputScenario scenario) const { - if (!state_ptr->input_tracing_track()) { - return; - } + size_t NestingLevel(InputScenario scenario) const { switch (scenario) { case InputScenario::kNoInput: - // No trace event. - return; + return 0; case InputScenario::kTyping: - TRACE_EVENT_BEGIN("performance_scenarios", "Typing", - *state_ptr->input_tracing_track()); - return; + return 1; } NOTREACHED(); } - void MaybeEndTraceEvent(InputScenario scenario) const { - if (!state_ptr->input_tracing_track()) { - return; - } - switch (scenario) { - case InputScenario::kNoInput: - // No trace event. - return; - case InputScenario::kTyping: - TRACE_EVENT_END("performance_scenarios", - *state_ptr->input_tracing_track()); - return; - } - NOTREACHED(); + void MaybeRecordTraceEvent(InputScenario old_scenario, + InputScenario new_scenario) const { + MaybeEmitNestingChangeEvent(state_ptr->input_tracing_track(), + NestingLevel(old_scenario), + NestingLevel(new_scenario), {"Typing"}); } raw_ptr<PerformanceScenarioData> state_ptr; @@ -185,8 +170,7 @@ Scenario old_scenario = traits.ScenarioRef().exchange(new_scenario, std::memory_order_relaxed); if (old_scenario != new_scenario) { - traits.MaybeEndTraceEvent(old_scenario); - traits.MaybeBeginTraceEvent(new_scenario); + traits.MaybeRecordTraceEvent(old_scenario, new_scenario); } } }
diff --git a/components/performance_manager/scenarios/loading_scenario_observer.cc b/components/performance_manager/scenarios/loading_scenario_observer.cc index afaa3104..16f57b9c 100644 --- a/components/performance_manager/scenarios/loading_scenario_observer.cc +++ b/components/performance_manager/scenarios/loading_scenario_observer.cc
@@ -12,12 +12,16 @@ #include "base/notreached.h" #include "base/numerics/checked_math.h" #include "base/sequence_checker.h" +#include "base/trace_event/typed_macros.h" +#include "base/unguessable_token.h" #include "components/performance_manager/graph/graph_impl.h" #include "components/performance_manager/graph/page_node_impl.h" #include "components/performance_manager/graph/process_node_impl.h" #include "components/performance_manager/scenario_api/performance_scenarios.h" #include "components/performance_manager/scenarios/browser_performance_scenarios.h" #include "components/performance_manager/scenarios/loading_scenario_data.h" +#include "third_party/perfetto/include/perfetto/tracing/tracing.h" +#include "third_party/perfetto/include/perfetto/tracing/track.h" namespace performance_manager { @@ -49,6 +53,32 @@ return LoadingScenario::kNoPageLoading; } +void RecordLoadingTraceEvent(const PageNode* page_node, + bool was_loading, + bool is_loading) { + static perfetto::NamedTrack loading_state_track = [] { + // Each PageNode gets its own track, which is grouped under this track. + perfetto::NamedTrack track{"PageNodeLoadingState", 0, + perfetto::Track::Global(0)}; + if (perfetto::Tracing::IsInitialized()) { + // Because the track doesn't get any events of its own it must manually + // emit the track descriptor. SetTrackDescriptor may crash in unit tests + // where tracing isn't initialized. + base::TrackEvent::SetTrackDescriptor(track, track.Serialize()); + } + return track; + }(); + const base::UnguessableToken page_token = + PageNodeImpl::FromNode(page_node)->page_token().value(); + perfetto::Track page_track(base::UnguessableTokenHash()(page_token), + loading_state_track); + if (is_loading && !was_loading) { + TRACE_EVENT_BEGIN("performance_scenarios", "PageNode Loading", page_track); + } else if (was_loading && !is_loading) { + TRACE_EVENT_END("performance_scenarios", page_track); + } +} + } // namespace LoadingScenarioObserver::LoadingScenarioObserver() = default; @@ -116,6 +146,8 @@ DecrementLoadingCounts(process_nodes, page_node->IsVisible(), page_node->IsFocused()); UpdateLoadingScenarios(process_nodes); + RecordLoadingTraceEvent(page_node, /*was_loading=*/true, + /*is_loading=*/false); } } @@ -165,6 +197,7 @@ page_node->IsFocused()); } UpdateLoadingScenarios(process_nodes); + RecordLoadingTraceEvent(page_node, was_loading, is_loading); } }
diff --git a/components/signin/core/browser/signin_metrics_service.cc b/components/signin/core/browser/signin_metrics_service.cc index 9aabff0..f9dd79c 100644 --- a/components/signin/core/browser/signin_metrics_service.cc +++ b/components/signin/core/browser/signin_metrics_service.cc
@@ -185,6 +185,7 @@ case signin_metrics::AccessPoint::kCollaborationJoinTabGroup: case signin_metrics::AccessPoint::kHistorySyncOptinExpansionPill: case signin_metrics::AccessPoint::kWidget: + case signin_metrics::AccessPoint::kCollaborationLeaveOrDeleteTabGroup: return; }
diff --git a/components/signin/public/base/signin_metrics.cc b/components/signin/public/base/signin_metrics.cc index 3307f3c..83674b6 100644 --- a/components/signin/public/base/signin_metrics.cc +++ b/components/signin/public/base/signin_metrics.cc
@@ -475,6 +475,10 @@ base::RecordAction(base::UserMetricsAction( "Signin_Signin_FromCollaborationJoinTabGroup")); break; + case AccessPoint::kCollaborationLeaveOrDeleteTabGroup: + base::RecordAction(base::UserMetricsAction( + "Signin_Signin_FromCollaborationLeaveOrDeleteTabGroup")); + break; case AccessPoint::kSafetyCheck: VLOG(1) << "Signin_Signin_From* user action is not recorded " << "for access point " << static_cast<int>(access_point); @@ -750,6 +754,7 @@ case AccessPoint::kCollaborationJoinTabGroup: case AccessPoint::kHistorySyncOptinExpansionPill: case AccessPoint::kWidget: + case AccessPoint::kCollaborationLeaveOrDeleteTabGroup: NOTREACHED() << "Signin_Impression_From* user actions are not recorded " "for access point " << static_cast<int>(access_point);
diff --git a/components/signin/public/base/signin_metrics.h b/components/signin/public/base/signin_metrics.h index 0286609..33f92dd 100644 --- a/components/signin/public/base/signin_metrics.h +++ b/components/signin/public/base/signin_metrics.h
@@ -272,10 +272,13 @@ // Access point triggered when the account used in widget is different from // the one used in the app. iOS only. kWidget = 78, + // Access point triggered when a user attempts to leave or delete a tab group + // without being signed in or synced. + kCollaborationLeaveOrDeleteTabGroup = 79, // Add values above this line with a corresponding label to the // "SigninAccessPoint" enum in // tools/metrics/histograms/metadata/signin/enums.xml. - kMaxValue = kWidget, // This must be last. + kMaxValue = kCollaborationLeaveOrDeleteTabGroup, // This must be last. }; // LINT.ThenChange(/tools/metrics/histograms/metadata/signin/enums.xml)
diff --git a/components/signin/public/base/signin_metrics_unittest.cc b/components/signin/public/base/signin_metrics_unittest.cc index 4d66ee2..833626a 100644 --- a/components/signin/public/base/signin_metrics_unittest.cc +++ b/components/signin/public/base/signin_metrics_unittest.cc
@@ -227,6 +227,8 @@ return "HistorySyncOptinExpansionPill"; case AccessPoint::kWidget: return "Widget"; + case AccessPoint::kCollaborationLeaveOrDeleteTabGroup: + return "CollaborationLeaveOrDeleteTabGroup"; } } };
diff --git a/components/update_client/pipeline.cc b/components/update_client/pipeline.cc index 499857d5..b4833cd 100644 --- a/components/update_client/pipeline.cc +++ b/components/update_client/pipeline.cc
@@ -254,9 +254,10 @@ download_progress_callback))); } else if (operation.type == "puff") { ops.push(SkipIfCached( - cache_check, base::BindOnce(&PuffOperation, crx_cache, - config->GetPatcherFactory()->Create(), - event_adder, id, operation.sha256_from))); + cache_check, + base::BindOnce(&PuffOperation, crx_cache, + config->GetPatcherFactory()->Create(), event_adder, id, + operation.sha256_previous))); } else if (operation.type == "xz") { ops.push(SkipIfCached( cache_check, @@ -264,9 +265,10 @@ event_adder))); } else if (operation.type == "zucc") { ops.push(SkipIfCached( - cache_check, base::BindOnce(&ZucchiniOperation, crx_cache, - config->GetPatcherFactory()->Create(), - event_adder, id, operation.sha256_from))); + cache_check, + base::BindOnce(&ZucchiniOperation, crx_cache, + config->GetPatcherFactory()->Create(), event_adder, id, + operation.sha256_previous))); } else if (operation.type == "crx3") { ops.push(base::BindOnce( &InstallOperation, crx_cache, config->GetUnzipperFactory()->Create(),
diff --git a/components/update_client/protocol_parser.h b/components/update_client/protocol_parser.h index e7d0b6eb..ac3dcc118c 100644 --- a/components/update_client/protocol_parser.h +++ b/components/update_client/protocol_parser.h
@@ -41,7 +41,7 @@ std::string type; std::string sha256_in; std::string sha256_out; - std::string sha256_from; + std::string sha256_previous; std::string path; std::string arguments; int64_t size = 0;
diff --git a/components/update_client/protocol_parser_json.cc b/components/update_client/protocol_parser_json.cc index 37fe5af3..7294c1c 100644 --- a/components/update_client/protocol_parser_json.cc +++ b/components/update_client/protocol_parser_json.cc
@@ -106,7 +106,7 @@ op.type = type.value(); op.sha256_out = ParseWithDefault(node, "out", "sha256", {}); op.sha256_in = ParseWithDefault(node, "in", "sha256", {}); - op.sha256_from = ParseWithDefault(node, "from", "sha256", {}); + op.sha256_previous = ParseWithDefault(node, "previous", "sha256", {}); op.path = ParseWithDefault(node, "path", {}); op.arguments = ParseWithDefault(node, "arguments", {}); op.size = ParseNumberWithDefault(node, "size", 0);
diff --git a/components/update_client/update_client_unittest.cc b/components/update_client/update_client_unittest.cc index 778e90e..f22874a 100644 --- a/components/update_client/update_client_unittest.cc +++ b/components/update_client/update_client_unittest.cc
@@ -1483,7 +1483,7 @@ "urls": [{"url": "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.puff"}], "out": {"sha256": "f2254da51fa2478a8ba90e58e1c28e24033ec7841015eebf1c82e31b957c44b2"}}, { "type": "puff", - "from": {"sha256": "8f5aa190311237cae00675af87ff457f278cd1a05895470ac5d46647d4a3c2ea"}}, + "previous": {"sha256": "8f5aa190311237cae00675af87ff457f278cd1a05895470ac5d46647d4a3c2ea"}}, { "type": "crx3", "in": {"sha256": "c87d8742c3ff3d7a0cb6f3c91aa2fcf3dea63618086a7db1c5be5300e1d4d6b6"} }]}]}}]}})"); @@ -2048,7 +2048,7 @@ "urls": [{"url": "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.puff"}], "out": {"sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}}, { "type": "puff", - "from": {"sha256": "8f5aa190311237cae00675af87ff457f278cd1a05895470ac5d46647d4a3c2ea"}}, + "previous": {"sha256": "8f5aa190311237cae00675af87ff457f278cd1a05895470ac5d46647d4a3c2ea"}}, { "type": "crx3", "in": {"sha256": "c87d8742c3ff3d7a0cb6f3c91aa2fcf3dea63618086a7db1c5be5300e1d4d6b6"} }]}, @@ -3135,7 +3135,7 @@ "urls": [{"url": "http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1to2.puff"}], "out": {"sha256": "f2254da51fa2478a8ba90e58e1c28e24033ec7841015eebf1c82e31b957c44b2"}}, { "type": "puff", - "from": {"sha256": "8f5aa190311237cae00675af87ff457f278cd1a05895470ac5d46647d4a3c2ea"}}, + "previous": {"sha256": "8f5aa190311237cae00675af87ff457f278cd1a05895470ac5d46647d4a3c2ea"}}, { "type": "crx3", "in": {"sha256": "c87d8742c3ff3d7a0cb6f3c91aa2fcf3dea63618086a7db1c5be5300e1d4d6b6"} }]},
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc index 875ee5d..fbbd83a2f 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc
@@ -6938,6 +6938,8 @@ navigation_handle_timing_.final_request_ssl_delay = ssl_delay; if (response_head_->load_timing_internal_info) { + navigation_handle_timing_.create_stream_delay = + response_head_->load_timing_internal_info->create_stream_delay; navigation_handle_timing_.initialize_stream_delay = response_head_->load_timing_internal_info->initialize_stream_delay; // Reset `load_timing_internal_info` to make sure that isn't exposed.
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc index 7a43bdd2e..39363ee8 100644 --- a/content/browser/service_worker/service_worker_context_core.cc +++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -1128,6 +1128,14 @@ return it->second.count; } +void ServiceWorkerContextCore::NotifyWillCreateURLLoaderFactory( + const GURL& scope) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + for (auto& observer : sync_observer_list_->observers) { + observer.OnWillCreateURLLoaderFactory(scope); + } +} + void ServiceWorkerContextCore::NotifyRegistrationStored( int64_t registration_id, const GURL& scope, @@ -1221,6 +1229,16 @@ version->version_id(), client_uuid, render_frame_host_id); } +void ServiceWorkerContextCore::OnStartWorkerMessageSent( + ServiceWorkerVersion* version) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + DCHECK_EQ(this, version->context().get()); + + for (auto& observer : sync_observer_list_->observers) { + observer.OnStartWorkerMessageSent(version->version_id(), version->scope()); + } +} + void ServiceWorkerContextCore::OnRunningStateChanged( ServiceWorkerVersion* version) { DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h index b19329f..561c71f2 100644 --- a/content/browser/service_worker/service_worker_context_core.h +++ b/content/browser/service_worker/service_worker_context_core.h
@@ -367,6 +367,7 @@ void OnNoControllees(ServiceWorkerVersion* version); // ServiceWorkerVersion::Observer overrides. + void OnStartWorkerMessageSent(ServiceWorkerVersion* version) override; void OnRunningStateChanged(ServiceWorkerVersion* version) override; void OnVersionStateChanged(ServiceWorkerVersion* version) override; void OnDevToolsRoutingIdChanged(ServiceWorkerVersion* version) override; @@ -493,6 +494,10 @@ // version. The count resets to zero when the worker successfully starts. int GetVersionFailureCount(int64_t version_id); + // Called by ServiceWorkerRegisterJob before the URLLoaderFactory used + // to fetch the worker script is constructed. + void NotifyWillCreateURLLoaderFactory(const GURL& scope); + // Called by ServiceWorkerStorage when StoreRegistration() succeeds. void NotifyRegistrationStored(int64_t registration_id, const GURL& scope,
diff --git a/content/browser/service_worker/service_worker_context_core_observer.h b/content/browser/service_worker/service_worker_context_core_observer.h index 41fee60..69356c4 100644 --- a/content/browser/service_worker/service_worker_context_core_observer.h +++ b/content/browser/service_worker/service_worker_context_core_observer.h
@@ -82,6 +82,11 @@ int64_t version_id, const std::string& uuid, GlobalRenderFrameHostId render_frame_host_id) {} + + // Called before the URLLoaderFactory used to fetch the worker script is + // constructed. + virtual void OnWillCreateURLLoaderFactory(const GURL& scope) {} + // Called when the ServiceWorkerContainer.register() promise is resolved. // // This is called before the service worker registration is persisted to
diff --git a/content/browser/service_worker/service_worker_register_job.cc b/content/browser/service_worker/service_worker_register_job.cc index e628f00..97701ad 100644 --- a/content/browser/service_worker/service_worker_register_job.cc +++ b/content/browser/service_worker/service_worker_register_job.cc
@@ -613,6 +613,7 @@ void ServiceWorkerRegisterJob::UpdateAndContinue() { SetPhase(UPDATE); + context_->NotifyWillCreateURLLoaderFactory(scope_); scoped_refptr<network::SharedURLLoaderFactory> loader_factory = context_->wrapper()->GetLoaderFactoryForUpdateCheck( scope_,
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc index 1c96682..b99b9e4 100644 --- a/content/browser/service_worker/service_worker_version.cc +++ b/content/browser/service_worker/service_worker_version.cc
@@ -1463,6 +1463,12 @@ } } +void ServiceWorkerVersion::OnStartWorkerMessageSent() { + for (auto& observer : observers_) { + observer.OnStartWorkerMessageSent(this); + } +} + void ServiceWorkerVersion::OnStarted( blink::mojom::ServiceWorkerStartStatus start_status, FetchHandlerType new_fetch_handler_type,
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h index 3c35a54..befe521 100644 --- a/content/browser/service_worker/service_worker_version.h +++ b/content/browser/service_worker/service_worker_version.h
@@ -180,6 +180,7 @@ class Observer { public: + virtual void OnStartWorkerMessageSent(ServiceWorkerVersion* version) {} virtual void OnRunningStateChanged(ServiceWorkerVersion* version) {} virtual void OnVersionStateChanged(ServiceWorkerVersion* version) {} virtual void OnDevToolsRoutingIdChanged(ServiceWorkerVersion* version) {} @@ -892,6 +893,7 @@ void OnScriptLoaded() override; void OnProcessAllocated() override; void OnStarting() override; + void OnStartWorkerMessageSent() override; void OnStarted(blink::mojom::ServiceWorkerStartStatus status, FetchHandlerType new_fetch_handler_type, bool new_has_hid_event_handlers,
diff --git a/content/public/browser/navigation_handle_timing.h b/content/public/browser/navigation_handle_timing.h index 46d35c9b..58d13c9d7 100644 --- a/content/public/browser/navigation_handle_timing.h +++ b/content/public/browser/navigation_handle_timing.h
@@ -142,6 +142,9 @@ base::TimeDelta final_request_connect_delay; base::TimeDelta final_request_ssl_delay; + // CreateStream related delay information. + base::TimeDelta create_stream_delay; + // InitializeStream related delay information. base::TimeDelta initialize_stream_delay; };
diff --git a/content/public/browser/service_worker_context.h b/content/public/browser/service_worker_context.h index 1639cbb..b605c45e 100644 --- a/content/public/browser/service_worker_context.h +++ b/content/public/browser/service_worker_context.h
@@ -12,6 +12,7 @@ #include "base/functional/callback_forward.h" #include "base/observer_list_types.h" +#include "base/scoped_observation_traits.h" #include "base/task/sequenced_task_runner.h" #include "content/common/content_export.h" #include "content/public/browser/browser_thread.h" @@ -92,12 +93,18 @@ // synchronously with changes in //content. class ServiceWorkerContextObserverSynchronous : public base::CheckedObserver { public: + // Called after the message to start the service worker has been sent. + virtual void OnStartWorkerMessageSent(int64_t version_id, const GURL& scope) { + } // Called when the service worker with id `version_id` will be stopped. virtual void OnStopping(int64_t version_id, const ServiceWorkerRunningInfo& worker_info) {} // Called when the service worker with id `version_id` has stopped running. virtual void OnStopped(int64_t version_id, const ServiceWorkerRunningInfo& worker_info) {} + // Called before the URLLoaderFactory used to fetch the worker script is + // constructed. + virtual void OnWillCreateURLLoaderFactory(const GURL& scope) {} // TODO(crbug.com/334940006): Add the rest of the extensions methods // (OnRegistrationStored(), OnReportConsoleMessage(), OnDestruct()) and adapt @@ -344,4 +351,37 @@ } // namespace content +namespace base { + +template <> +struct ScopedObservationTraits<content::ServiceWorkerContext, + content::ServiceWorkerContextObserver> { + static void AddObserver(content::ServiceWorkerContext* source, + content::ServiceWorkerContextObserver* observer) { + source->AddObserver(observer); + } + static void RemoveObserver(content::ServiceWorkerContext* source, + content::ServiceWorkerContextObserver* observer) { + source->RemoveObserver(observer); + } +}; + +template <> +struct ScopedObservationTraits< + content::ServiceWorkerContext, + content::ServiceWorkerContextObserverSynchronous> { + static void AddObserver( + content::ServiceWorkerContext* source, + content::ServiceWorkerContextObserverSynchronous* observer) { + source->AddSyncObserver(observer); + } + static void RemoveObserver( + content::ServiceWorkerContext* source, + content::ServiceWorkerContextObserverSynchronous* observer) { + source->RemoveSyncObserver(observer); + } +}; + +} // namespace base + #endif // CONTENT_PUBLIC_BROWSER_SERVICE_WORKER_CONTEXT_H_
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc index 4a9b3ae..3a99635c 100644 --- a/extensions/browser/api/web_request/web_request_api.cc +++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -30,6 +30,7 @@ #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" +#include "content/public/browser/service_worker_context.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" #include "content/public/common/url_constants.h" @@ -52,6 +53,7 @@ #include "extensions/browser/install_prefs_helper.h" #include "extensions/browser/process_manager.h" #include "extensions/browser/process_map.h" +#include "extensions/browser/service_worker/service_worker_task_queue_factory.h" #include "extensions/browser/warning_service.h" #include "extensions/browser/warning_set.h" #include "extensions/common/api/web_request.h" @@ -91,6 +93,8 @@ namespace { +WebRequestAPI::TestObserver* g_test_observer = nullptr; + // These values are persisted to logs. Entries should not be renumbered and // numeric values should never be reused. // @@ -334,12 +338,22 @@ event_router->RegisterObserver(this, event_name); } extensions::ExtensionRegistry::Get(browser_context_)->AddObserver(this); + + // We only have to observe ServiceWorkerTaskQueue and react to + // `OnAllRegistrationsStored` if we need to defer calls to + // `ResetURLLoaderFactories` to after registration storage. + if (base::FeatureList::IsEnabled( + extensions_features::kDeferResetURLLoaderFactories)) { + service_worker_task_queue_observation_.Observe( + ServiceWorkerTaskQueue::Get(browser_context_)); + } } WebRequestAPI::~WebRequestAPI() = default; void WebRequestAPI::Shutdown() { proxies_.reset(); + service_worker_context_observation_.RemoveAllObservations(); EventRouter::Get(browser_context_)->UnregisterObserver(this); extensions::ExtensionRegistry::Get(browser_context_)->RemoveObserver(this); // TODO(crbug.com/40264286): Remove this once WebRequestEventRouter @@ -358,6 +372,15 @@ return g_factory.Pointer(); } +// static +void WebRequestAPI::SetObserverForTest(TestObserver* observer) { + g_test_observer = observer; +} + +WebRequestAPI::TestObserver::TestObserver() = default; + +WebRequestAPI::TestObserver::~TestObserver() = default; + void WebRequestAPI::OnListenerRemoved(const EventListenerInfo& details) { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -700,14 +723,65 @@ ->HasAnyExtraHeadersListener(browser_context_); } +void WebRequestAPI::ResetURLLoaderFactories() { + browser_context_->GetDefaultStoragePartition()->ResetURLLoaderFactories(); + if (g_test_observer) { + g_test_observer->OnDidResetURLLoaderFactories(); + } +} + void WebRequestAPI::UpdateMayHaveProxies() { bool may_have_proxies = MayHaveProxies(); if (!may_have_proxies_ && may_have_proxies) { - browser_context_->GetDefaultStoragePartition()->ResetURLLoaderFactories(); + if (base::FeatureList::IsEnabled( + extensions_features::kDeferResetURLLoaderFactories) && + has_pending_worker_registrations_) { + // If any service worker registration is still in flight (started, but not + // stored), it's not safe to call `ResetURLLoaderFactories`, since it + // can cause network service failures in other extensions' registrations. + // See https://crbug.com/394523691. + deferred_reset_url_loader_factories_ = true; + // TODO(crbug.com/408312299): avoid deferring all factory resets here. + // Strictly speaking, we only need to defer factories for the loading + // extension. Other extensions's factories and web content factories + // can be reset safely. + } else { + // Otherwise, we can safely call it now. + ResetURLLoaderFactories(); + } } may_have_proxies_ = may_have_proxies; } +void WebRequestAPI::OnWillRegisterServiceWorker( + content::ServiceWorkerContext* context) { + // The registration process for an extension's service worker is starting. + // We begin observing the service worker's context, so that we can listen + // for `OnWillCreateURLLoaderFactory`, after which it's not safe to call + // `ResetURLLoaderFactories()` anymore. + if (!service_worker_context_observation_.IsObservingSource(context)) { + service_worker_context_observation_.AddObservation(context); + } +} + +void WebRequestAPI::OnWillCreateURLLoaderFactory(const GURL& scope) { + // The URLLoaderFactory that will be used to fetch the worker's script + // is about to be constructed. From here on until there is no more + // registrations in flight, it's not safe to call `ResetURLLoaderFactories()`. + has_pending_worker_registrations_ = true; +} + +void WebRequestAPI::OnAllRegistrationsStored() { + // No more registrations are in flight. Stop observing the service workers... + has_pending_worker_registrations_ = false; + service_worker_context_observation_.RemoveAllObservations(); + // ...and reset the URLLoaderFactories if we haven't done it already. + if (deferred_reset_url_loader_factories_) { + ResetURLLoaderFactories(); + deferred_reset_url_loader_factories_ = false; + } +} + void WebRequestAPI::OnExtensionLoaded(content::BrowserContext* browser_context, const Extension* extension) { CHECK(extension); @@ -785,6 +859,12 @@ ->RemoveLazyListener(browser_context, extension_id, sub_event_name); } +template <> +void BrowserContextKeyedAPIFactory< + WebRequestAPI>::DeclareFactoryDependencies() { + DependsOn(ServiceWorkerTaskQueueFactory::GetInstance()); +} + // Special QuotaLimitHeuristic for WebRequestHandlerBehaviorChangedFunction. // // Each call of webRequest.handlerBehaviorChanged() clears the in-memory cache
diff --git a/extensions/browser/api/web_request/web_request_api.h b/extensions/browser/api/web_request/web_request_api.h index 526f53bf..a8fc099e 100644 --- a/extensions/browser/api/web_request/web_request_api.h +++ b/extensions/browser/api/web_request/web_request_api.h
@@ -22,12 +22,16 @@ #include "base/memory/raw_ptr.h" #include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" +#include "base/scoped_multi_source_observation.h" +#include "base/scoped_observation.h" #include "base/strings/string_util.h" #include "base/task/sequenced_task_runner.h" #include "base/time/time.h" #include "base/values.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/global_request_id.h" +#include "content/public/browser/service_worker_context.h" +#include "content/public/browser/service_worker_context_observer.h" #include "extensions/browser/api/declarative_webrequest/request_stage.h" #include "extensions/browser/api/web_request/extension_web_request_event_router.h" #include "extensions/browser/api/web_request/web_request_permissions.h" @@ -36,6 +40,7 @@ #include "extensions/browser/extension_api_frame_id_map.h" #include "extensions/browser/extension_function.h" #include "extensions/browser/extension_registry_observer.h" +#include "extensions/browser/service_worker/service_worker_task_queue.h" #include "extensions/common/extension_id.h" #include "ipc/ipc_sender.h" #include "net/base/auth.h" @@ -50,6 +55,7 @@ namespace content { class BrowserContext; class RenderFrameHost; +class ServiceWorkerContext; } // namespace content namespace net { @@ -71,7 +77,9 @@ // per BrowserContext which is shared with incognito. class WebRequestAPI : public BrowserContextKeyedAPI, public EventRouter::Observer, - public ExtensionRegistryObserver { + public ExtensionRegistryObserver, + public ServiceWorkerTaskQueue::RegistrationObserver, + public content::ServiceWorkerContextObserverSynchronous { public: // A callback used to asynchronously respond to an intercepted authentication // request. If |should_cancel| is true the request will be cancelled. @@ -191,6 +199,19 @@ static BrowserContextKeyedAPIFactory<WebRequestAPI>* GetFactoryInstance(); void Shutdown() override; + class TestObserver { + public: + TestObserver(); + TestObserver(const TestObserver&) = delete; + TestObserver& operator=(const TestObserver&) = delete; + virtual ~TestObserver(); + + // Called when `ResetURLLoaderFactories()` has been performed. + virtual void OnDidResetURLLoaderFactories() {} + }; + + static void SetObserverForTest(TestObserver* observer); + // EventRouter::Observer overrides: void OnListenerRemoved(const EventListenerInfo& details) override; @@ -300,6 +321,8 @@ // URLLoaderFactories if so. void UpdateMayHaveProxies(); + void ResetURLLoaderFactories(); + // ExtensionRegistryObserver implementation. void OnExtensionLoaded(content::BrowserContext* browser_context, const Extension* extension) override; @@ -307,6 +330,16 @@ const Extension* extension, UnloadedExtensionReason reason) override; + // content::ServiceWorkerContextObserverSynchronous: + // Listens for the moment right before the URLLoaderFactory that will + // be used to fetch the worker's script is constructed. + void OnWillCreateURLLoaderFactory(const GURL& scope) override; + + // ServiceWorkerTaskQueue::RegistrationObserver: + void OnWillRegisterServiceWorker( + content::ServiceWorkerContext* context) override; + void OnAllRegistrationsStored() override; + // This a proxy API for the tasks that are posted. It is either called // when the task is run and forwards to the corresponding member function // in ExtensionWebRequestEventRouter, or not, if the owning BrowserContext @@ -357,9 +390,32 @@ // |UpdateMayHaveProxies()|. bool may_have_proxies_; + // Stores whether there's any active worker registration for extensions, + // which means their URLLoaderFactory have already been constructed. + // Used to decide whether |ResetURLLoaderFactories()| can be called + // immediately, or if it needs to be deferred to when all registrations + // are stored. + bool has_pending_worker_registrations_ = false; + + // Stores whether the execution of |ResetURLLoaderFactories()| has been + // deferred to when service workers registrations are stored. + bool deferred_reset_url_loader_factories_ = false; + + base::ScopedMultiSourceObservation< + content::ServiceWorkerContext, + content::ServiceWorkerContextObserverSynchronous> + service_worker_context_observation_{this}; + + base::ScopedObservation<ServiceWorkerTaskQueue, + ServiceWorkerTaskQueue::RegistrationObserver> + service_worker_task_queue_observation_{this}; + base::WeakPtrFactory<WebRequestAPI> weak_factory_{this}; }; +template <> +void BrowserContextKeyedAPIFactory<WebRequestAPI>::DeclareFactoryDependencies(); + class WebRequestInternalFunction : public ExtensionFunction { public: WebRequestInternalFunction() = default;
diff --git a/extensions/browser/service_worker/service_worker_task_queue.cc b/extensions/browser/service_worker/service_worker_task_queue.cc index 0c70b94..721c2af 100644 --- a/extensions/browser/service_worker/service_worker_task_queue.cc +++ b/extensions/browser/service_worker/service_worker_task_queue.cc
@@ -183,6 +183,10 @@ // This can happen is when the registration got unregistered right before we // tried to start it. See crbug.com/999027 for details. DCHECK(!GetWorkerState(context_id)); + // In that case, we expect `DeactivateExtension` to have been called + // already, and for the registration records to have already been cleared. + DCHECK(!incomplete_registrations_.contains(context_id)); + DCHECK(!pending_storage_registrations_.contains(context_id.extension_id)); return; } @@ -214,7 +218,7 @@ // this happens. // If there was a pending registration for this extension, erase it. - pending_registrations_.erase(context_id.extension_id); + EraseInFlightRegistration(context_id); } bool ServiceWorkerTaskQueue::IsStartWorkerFailureUnexpected( @@ -339,6 +343,16 @@ } } +void ServiceWorkerTaskQueue::AddRegistrationObserver( + RegistrationObserver* observer) { + registration_observers_.AddObserver(observer); +} + +void ServiceWorkerTaskQueue::RemoveRegistrationObserver( + RegistrationObserver* observer) { + registration_observers_.RemoveObserver(observer); +} + void ServiceWorkerTaskQueue::StopObservingContextForTest( content::ServiceWorkerContext* service_worker_context) { StopObserving(service_worker_context); @@ -562,9 +576,13 @@ } else { worker_reregistration_attempts_[context_id.token] = 0; } + incomplete_registrations_.insert(context_id); content::ServiceWorkerContext* service_worker_context = GetServiceWorkerContext(extension.id()); + for (auto& observer : registration_observers_) { + observer.OnWillRegisterServiceWorker(service_worker_context); + } service_worker_context->RegisterServiceWorker( script_url, blink::StorageKey::CreateFirstParty(url::Origin::Create(option.scope)), @@ -600,7 +618,7 @@ // Erase any registrations that might still have been pending being fully // stored. - pending_registrations_.erase(extension_id); + EraseInFlightRegistration(context_id); content::ServiceWorkerContext* service_worker_context = GetServiceWorkerContext(extension->id()); @@ -686,6 +704,26 @@ return iter->second < 3; } +void ServiceWorkerTaskQueue::EraseInFlightRegistration( + const SequencedContextId& context_id) { + auto incomplete_removed = incomplete_registrations_.erase(context_id); + auto pending_storage_removed = + pending_storage_registrations_.erase(context_id.extension_id); + + // NOTE: `removed` may be false when called from `DeactivateExtension`, + // and if the extension registration is not in flight / pending storage + // while deactivation is being requested. + bool removed = incomplete_removed || pending_storage_removed; + bool has_in_flight_registrations = !incomplete_registrations_.empty() || + !pending_storage_registrations_.empty(); + + if (removed && !has_in_flight_registrations) { + for (auto& observer : registration_observers_) { + observer.OnAllRegistrationsStored(); + } + } +} + void ServiceWorkerTaskQueue::DidRegisterServiceWorker( const SequencedContextId& context_id, RegistrationReason reason, @@ -694,6 +732,10 @@ const bool success = IsWorkerRegistrationSuccess(status_code); base::UmaHistogramBoolean( "Extensions.ServiceWorkerBackground.WorkerRegistrationState2", success); + if (!success && g_test_observer) { + g_test_observer->OnWorkerRegistrationFailed(context_id.extension_id, + status_code); + } ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_); const ExtensionId& extension_id = context_id.extension_id; @@ -710,6 +752,7 @@ if (g_test_observer) { g_test_observer->OnWorkerRegistered(context_id.extension_id); } + EraseInFlightRegistration(context_id); return; } if (!IsCurrentActivation(extension_id, context_id.token)) { @@ -721,6 +764,7 @@ if (g_test_observer) { g_test_observer->OnWorkerRegistered(context_id.extension_id); } + EraseInFlightRegistration(context_id); return; } @@ -791,15 +835,16 @@ if (g_test_observer) { g_test_observer->OnWorkerRegistered(context_id.extension_id); } + EraseInFlightRegistration(context_id); return; } base::UmaHistogramTimes("Extensions.ServiceWorkerBackground.RegistrationTime", base::Time::Now() - start_time); worker_registered_.insert(context_id); - - pending_registrations_.emplace(extension->id(), - *GetCurrentActivationToken(extension->id())); + incomplete_registrations_.erase(context_id); + pending_storage_registrations_.emplace( + extension->id(), *GetCurrentActivationToken(extension->id())); if (HasPendingTasks(context_id)) { // TODO(lazyboy): If worker for |context_id| is already running, consider @@ -967,8 +1012,8 @@ void ServiceWorkerTaskQueue::OnRegistrationStored(int64_t registration_id, const GURL& scope) { const ExtensionId extension_id = scope.host(); - auto iter = pending_registrations_.find(extension_id); - if (iter == pending_registrations_.end()) { + auto iter = pending_storage_registrations_.find(extension_id); + if (iter == pending_storage_registrations_.end()) { return; } @@ -978,7 +1023,9 @@ DCHECK_EQ("/", scope.path()); base::UnguessableToken activation_token = iter->second; - pending_registrations_.erase(iter); + SequencedContextId context_id = {extension_id, browser_context_->UniqueId(), + activation_token}; + EraseInFlightRegistration(context_id); ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_); const Extension* extension =
diff --git a/extensions/browser/service_worker/service_worker_task_queue.h b/extensions/browser/service_worker/service_worker_task_queue.h index fcec355..4271b2eb 100644 --- a/extensions/browser/service_worker/service_worker_task_queue.h +++ b/extensions/browser/service_worker/service_worker_task_queue.h
@@ -14,6 +14,7 @@ #include "base/containers/flat_map.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" +#include "base/scoped_observation_traits.h" #include "base/strings/string_util.h" #include "base/unguessable_token.h" #include "base/version.h" @@ -214,6 +215,24 @@ // |context|. static ServiceWorkerTaskQueue* Get(content::BrowserContext* context); + class RegistrationObserver : public base::CheckedObserver { + public: + // Called when a service worker registration is about to start. + // From this point onwards, and until it's either stored or fails, + // the registration is considered to be "in flight". + virtual void OnWillRegisterServiceWorker( + content::ServiceWorkerContext* context) = 0; + + // Called when no service worker registrations for activated + // extensions are in flight anymore, i.e. they have been stored, + // or failed before being stored. + virtual void OnAllRegistrationsStored() = 0; + }; + + // Adds or removes observers. + void AddRegistrationObserver(RegistrationObserver* observer); + void RemoveRegistrationObserver(RegistrationObserver* observer); + // Always returns true since we currently request a worker to start for every // task sent to it. bool ShouldEnqueueTask(content::BrowserContext* context, @@ -329,6 +348,10 @@ // it ultimately succeeds or fails). virtual void RequestedWorkerStart(const ExtensionId& extension_id) {} + virtual void OnWorkerRegistrationFailed( + const ExtensionId& extension_id, + blink::ServiceWorkerStatusCode status_code) {} + virtual void DidStartWorkerFail( const ExtensionId& extension_id, size_t num_pending_tasks, @@ -504,6 +527,9 @@ // Whether there are any pending tasks to run for the activated extension. bool HasPendingTasks(const SequencedContextId& context_id); + // Erases in-flight registration records for the given `context_id`. + void EraseInFlightRegistration(const SequencedContextId& context_id); + // Whether the task queue (as a keyed service) has been informed that the // browser context is shutting down. Used for metrics purposes. bool browser_context_shutting_down_ = false; @@ -535,13 +561,18 @@ // for an activation token. std::map<base::UnguessableToken, int> worker_reregistration_attempts_; - // A set of pending service worker registrations. These are registrations that - // succeeded in the first step (triggering `DidRegisterServiceWorker`), but - // have not yet been stored. They are cleared out (and the registration state - // is stored) in response to `OnRegistrationStored`. - // The key is the extension's ID and the value is the activation token - // expected for that registration. - std::map<ExtensionId, base::UnguessableToken> pending_registrations_; + // Tracks service worker registrations that have started but haven't completed + // yet. This differs from `pending_storage_registrations_`, which tracks + // registrations that have successfully completed but are pending storage. + std::set<SequencedContextId> incomplete_registrations_; + + // A set of service worker registrations that are pending storage. + // These are registrations that succeeded in the first step (triggering + // `DidRegisterServiceWorker`), but have not yet been stored. + // They are cleared out (and the registration state is stored) in response to + // `OnRegistrationStored`. The key is the extension's ID and the value is the + // activation token expected for that registration. + std::map<ExtensionId, base::UnguessableToken> pending_storage_registrations_; // TODO(crbug.com/40276609): Do we need to track this by `SequencedContextId` // or could we used `ExtensionId` instead? @@ -549,9 +580,31 @@ // //content layer. std::set<SequencedContextId> worker_registered_; + base::ObserverList<RegistrationObserver> registration_observers_; + base::WeakPtrFactory<ServiceWorkerTaskQueue> weak_factory_{this}; }; } // namespace extensions +namespace base { + +template <> +struct ScopedObservationTraits< + extensions::ServiceWorkerTaskQueue, + extensions::ServiceWorkerTaskQueue::RegistrationObserver> { + static void AddObserver( + extensions::ServiceWorkerTaskQueue* source, + extensions::ServiceWorkerTaskQueue::RegistrationObserver* observer) { + source->AddRegistrationObserver(observer); + } + static void RemoveObserver( + extensions::ServiceWorkerTaskQueue* source, + extensions::ServiceWorkerTaskQueue::RegistrationObserver* observer) { + source->RemoveRegistrationObserver(observer); + } +}; + +} // namespace base + #endif // EXTENSIONS_BROWSER_SERVICE_WORKER_SERVICE_WORKER_TASK_QUEUE_H_
diff --git a/extensions/browser/service_worker/service_worker_test_utils.cc b/extensions/browser/service_worker/service_worker_test_utils.cc index 9a5522c..20e3010 100644 --- a/extensions/browser/service_worker/service_worker_test_utils.cc +++ b/extensions/browser/service_worker/service_worker_test_utils.cc
@@ -12,6 +12,7 @@ #include "content/public/browser/storage_partition.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" +#include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/mojom/service_worker/service_worker_database.mojom-forward.h" namespace extensions { @@ -45,6 +46,7 @@ : extension_scope_(GetScopeForExtensionID(std::move(extension_id))), context_(context) { scoped_observation_.Observe(context_); + scoped_sync_observation_.Observe(context_); } TestServiceWorkerContextObserver::TestServiceWorkerContextObserver( @@ -53,6 +55,7 @@ : extension_scope_(GetScopeForExtensionID(std::move(extension_id))), context_(GetServiceWorkerContext(browser_context)) { scoped_observation_.Observe(context_); + scoped_sync_observation_.Observe(context_); } TestServiceWorkerContextObserver::~TestServiceWorkerContextObserver() = default; @@ -62,13 +65,26 @@ return; } + SCOPED_TRACE("Waiting for worker registration to be stored"); base::RunLoop run_loop; stored_quit_closure_ = run_loop.QuitClosure(); run_loop.Run(); } +int64_t TestServiceWorkerContextObserver::WaitForStartWorkerMessageSent() { + if (!start_message_sent_version_id_) { + SCOPED_TRACE("Waiting for StartWorker message to be sent"); + base::RunLoop run_loop; + start_message_sent_quit_closure_ = run_loop.QuitClosure(); + run_loop.Run(); + } + + return *start_message_sent_version_id_; +} + int64_t TestServiceWorkerContextObserver::WaitForWorkerStarted() { if (!running_version_id_) { + SCOPED_TRACE("Waiting for worker to be started"); base::RunLoop run_loop; started_quit_closure_ = run_loop.QuitClosure(); run_loop.Run(); @@ -84,6 +100,7 @@ return *stopped_version_id_; } + SCOPED_TRACE("Waiting for worker to be stopped"); base::RunLoop run_loop; stopped_quit_closure_ = run_loop.QuitClosure(); run_loop.Run(); @@ -93,6 +110,7 @@ int64_t TestServiceWorkerContextObserver::WaitForWorkerActivated() { if (!activated_version_id_) { + SCOPED_TRACE("Waiting for worker to be activated"); base::RunLoop run_loop; activated_quit_closure_ = run_loop.QuitClosure(); run_loop.Run(); @@ -123,6 +141,19 @@ } } +void TestServiceWorkerContextObserver::OnStartWorkerMessageSent( + int64_t version_id, + const GURL& scope) { + if (extension_scope_ && extension_scope_ != scope) { + return; + } + + start_message_sent_version_id_ = version_id; + if (start_message_sent_quit_closure_) { + std::move(start_message_sent_quit_closure_).Run(); + } +} + void TestServiceWorkerContextObserver::OnVersionStartedRunning( int64_t version_id, const content::ServiceWorkerRunningInfo& running_info) { @@ -161,6 +192,7 @@ void TestServiceWorkerContextObserver::OnDestruct( content::ServiceWorkerContext* context) { scoped_observation_.Reset(); + scoped_sync_observation_.Reset(); context_ = nullptr; }
diff --git a/extensions/browser/service_worker/service_worker_test_utils.h b/extensions/browser/service_worker/service_worker_test_utils.h index 2a99600..e272ccd0 100644 --- a/extensions/browser/service_worker/service_worker_test_utils.h +++ b/extensions/browser/service_worker/service_worker_test_utils.h
@@ -39,7 +39,8 @@ // Note: This class only works well when there is a *single* service worker // being registered. We could extend this to track multiple workers. class TestServiceWorkerContextObserver - : public content::ServiceWorkerContextObserver { + : public content::ServiceWorkerContextObserver, + public content::ServiceWorkerContextObserverSynchronous { public: explicit TestServiceWorkerContextObserver( content::ServiceWorkerContext* context, @@ -58,6 +59,11 @@ // scope to be stored. void WaitForRegistrationStored(); + // Wait for OnStartWorkerMessageSent event is triggered, so that the observer + // captures the version ID of the service worker that is about to be started. + // Returns the version ID. + int64_t WaitForStartWorkerMessageSent(); + // Wait for OnVersionStartedRunning event is triggered, so that the observer // captures the running service worker version ID. Returns the version ID. int64_t WaitForWorkerStarted(); @@ -89,6 +95,9 @@ void OnVersionActivated(int64_t version_id, const GURL& scope) override; void OnDestruct(content::ServiceWorkerContext* context) override; + // ServiceWorkerContextObserverSynchronous: + void OnStartWorkerMessageSent(int64_t version_id, const GURL& scope) override; + using RegistrationsMap = std::map<GURL, int>; RegistrationsMap registrations_completed_map_; @@ -96,6 +105,7 @@ // Multiple events may come in so we must wait for the specific event // to be triggered. base::OnceClosure activated_quit_closure_; + base::OnceClosure start_message_sent_quit_closure_; base::OnceClosure started_quit_closure_; base::OnceClosure stored_quit_closure_; base::OnceClosure stopped_quit_closure_; @@ -104,6 +114,7 @@ std::optional<bool> registration_stored_; std::optional<int64_t> activated_version_id_; + std::optional<int64_t> start_message_sent_version_id_; std::optional<int64_t> running_version_id_; std::optional<int64_t> stopped_version_id_; @@ -112,6 +123,10 @@ base::ScopedObservation<content::ServiceWorkerContext, content::ServiceWorkerContextObserver> scoped_observation_{this}; + + base::ScopedObservation<content::ServiceWorkerContext, + content::ServiceWorkerContextObserverSynchronous> + scoped_sync_observation_{this}; }; // Observes ProcessManager::UnregisterServiceWorker.
diff --git a/extensions/common/extension_features.cc b/extensions/common/extension_features.cc index 3316395e..d1b5903 100644 --- a/extensions/common/extension_features.cc +++ b/extensions/common/extension_features.cc
@@ -64,6 +64,10 @@ "EMF_NO_EXTENSION_ID_FOR_EXTENSION_SOURCE", base::FEATURE_ENABLED_BY_DEFAULT); +BASE_FEATURE(kDeferResetURLLoaderFactories, + "DeferResetURLLoaderFactories", + base::FEATURE_ENABLED_BY_DEFAULT); + BASE_FEATURE(kEnableWebHidInWebView, "EnableWebHidInWebView", base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/extensions/common/extension_features.h b/extensions/common/extension_features.h index 30db8ea..a57edbd 100644 --- a/extensions/common/extension_features.h +++ b/extensions/common/extension_features.h
@@ -85,6 +85,11 @@ // extension). BASE_DECLARE_FEATURE(kCheckingNoExtensionIdInExtensionIpcs); +// If enabled, defers the execution of WebRequestAPI call of +// `ResetURLLoaderFactories()` to when there's no extension service worker +// registrations in flight, to avoid disrupting the worker(s) registration(s). +BASE_DECLARE_FEATURE(kDeferResetURLLoaderFactories); + // If enabled, <webview>s will be allowed to request permission from an // embedding Chrome App to request access to Human Interface Devices. BASE_DECLARE_FEATURE(kEnableWebHidInWebView);
diff --git a/extensions/test/extension_test_notification_observer.h b/extensions/test/extension_test_notification_observer.h index c377929..95c6c868 100644 --- a/extensions/test/extension_test_notification_observer.h +++ b/extensions/test/extension_test_notification_observer.h
@@ -35,7 +35,7 @@ ExtensionTestNotificationObserver& operator=( const ExtensionTestNotificationObserver&) = delete; - ~ExtensionTestNotificationObserver(); + virtual ~ExtensionTestNotificationObserver(); // Waits for all extension views to load. bool WaitForExtensionViewsToLoad();
diff --git a/ios/chrome/app/resources/BUILD.gn b/ios/chrome/app/resources/BUILD.gn index ee354ecc..06c05b1f 100644 --- a/ios/chrome/app/resources/BUILD.gn +++ b/ios/chrome/app/resources/BUILD.gn
@@ -71,6 +71,7 @@ "$root_gen_dir/components/internal_debug_pages_disabled_resources.pak", "$root_gen_dir/components/management_resources.pak", "$root_gen_dir/components/metrics/metrics_server_urls.pak", + "$root_gen_dir/components/net_export_resources.pak", "$root_gen_dir/components/ntp_tiles_internals_resources.pak", "$root_gen_dir/components/optimization_guide_internals_resources.pak", "$root_gen_dir/components/policy_resources.pak",
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_default_account/consistency_default_account_mediator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_default_account/consistency_default_account_mediator.mm index a4cd0e2..f6a8d53 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_default_account/consistency_default_account_mediator.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin/consistency_promo_signin/consistency_default_account/consistency_default_account_mediator.mm
@@ -151,6 +151,7 @@ case signin_metrics::AccessPoint::kCollaborationJoinTabGroup: case signin_metrics::AccessPoint::kHistorySyncOptinExpansionPill: case signin_metrics::AccessPoint::kWidget: + case signin_metrics::AccessPoint::kCollaborationLeaveOrDeleteTabGroup: // Nothing prevents instantiating ConsistencyDefaultAccountViewController // with an arbitrary entry point, API-wise. In doubt, no label is a good, // generic default that fits all entry points.
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin_promo_view_mediator.mm b/ios/chrome/browser/authentication/ui_bundled/signin_promo_view_mediator.mm index 9d039a3..ddb88246 100644 --- a/ios/chrome/browser/authentication/ui_bundled/signin_promo_view_mediator.mm +++ b/ios/chrome/browser/authentication/ui_bundled/signin_promo_view_mediator.mm
@@ -133,6 +133,7 @@ case signin_metrics::AccessPoint::kCollaborationJoinTabGroup: case signin_metrics::AccessPoint::kHistorySyncOptinExpansionPill: case signin_metrics::AccessPoint::kWidget: + case signin_metrics::AccessPoint::kCollaborationLeaveOrDeleteTabGroup: return false; } } @@ -227,6 +228,7 @@ case signin_metrics::AccessPoint::kCollaborationJoinTabGroup: case signin_metrics::AccessPoint::kHistorySyncOptinExpansionPill: case signin_metrics::AccessPoint::kWidget: + case signin_metrics::AccessPoint::kCollaborationLeaveOrDeleteTabGroup: NOTREACHED() << "Unexpected value for access point " << static_cast<int>(access_point); } @@ -322,6 +324,7 @@ case signin_metrics::AccessPoint::kCollaborationJoinTabGroup: case signin_metrics::AccessPoint::kHistorySyncOptinExpansionPill: case signin_metrics::AccessPoint::kWidget: + case signin_metrics::AccessPoint::kCollaborationLeaveOrDeleteTabGroup: NOTREACHED() << "Unexpected value for access point " << static_cast<int>(access_point); } @@ -406,6 +409,7 @@ case signin_metrics::AccessPoint::kCollaborationJoinTabGroup: case signin_metrics::AccessPoint::kHistorySyncOptinExpansionPill: case signin_metrics::AccessPoint::kWidget: + case signin_metrics::AccessPoint::kCollaborationLeaveOrDeleteTabGroup: return nullptr; } } @@ -489,6 +493,7 @@ case signin_metrics::AccessPoint::kCollaborationJoinTabGroup: case signin_metrics::AccessPoint::kHistorySyncOptinExpansionPill: case signin_metrics::AccessPoint::kWidget: + case signin_metrics::AccessPoint::kCollaborationLeaveOrDeleteTabGroup: return nullptr; } }
diff --git a/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.h b/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.h index 34d18150..f523f5ae 100644 --- a/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.h +++ b/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.h
@@ -55,6 +55,10 @@ ResultCallback result) override; void ShowManageDialog(const tab_groups::EitherGroupID& either_id, ResultCallback result) override; + void ShowLeaveDialog(const tab_groups::EitherGroupID& either_id, + ResultCallback result) override; + void ShowDeleteDialog(const tab_groups::EitherGroupID& either_id, + ResultCallback result) override; void PromoteTabGroup(const data_sharing::GroupId& group_id, ResultCallback result) override; void PromoteCurrentScreen() override;
diff --git a/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm b/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm index 1537ea7e..c0c0a3a 100644 --- a/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm +++ b/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm
@@ -203,6 +203,9 @@ case FlowType::kShareOrManage: access_point = AccessPoint::kCollaborationShareTabGroup; break; + case FlowType::kLeaveOrDelete: + access_point = AccessPoint::kCollaborationLeaveOrDeleteTabGroup; + break; } ShowSigninCommand* command = [[ShowSigninCommand alloc] @@ -295,6 +298,18 @@ std::move(callback)); } +void IOSCollaborationControllerDelegate::ShowLeaveDialog( + const tab_groups::EitherGroupID& either_id, + ResultCallback result) { + std::move(result).Run(CollaborationControllerDelegate::Outcome::kFailure); +} + +void IOSCollaborationControllerDelegate::ShowDeleteDialog( + const tab_groups::EitherGroupID& either_id, + ResultCallback result) { + std::move(result).Run(CollaborationControllerDelegate::Outcome::kFailure); +} + void IOSCollaborationControllerDelegate::PromoteTabGroup( const data_sharing::GroupId& group_id, ResultCallback result) {
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/BUILD.gn b/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/BUILD.gn index e7e539f..04bbc14 100644 --- a/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/BUILD.gn +++ b/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/BUILD.gn
@@ -30,6 +30,7 @@ "//components/prefs:prefs", "//components/prefs/ios:ios", "//components/url_formatter", + "//ios/chrome/app/strings", "//ios/chrome/browser/content_suggestions/ui_bundled:constants", "//ios/chrome/browser/content_suggestions/ui_bundled/magic_stack:public", "//ios/chrome/browser/favicon/model:model", @@ -44,6 +45,7 @@ "//ios/chrome/common/ui/favicon", "//ios/chrome/common/ui/favicon:favicon_constants", "//ios/chrome/common/ui/util:util", + "//ui/base", "//url:url", ] }
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_mediator.mm b/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_mediator.mm index 3d31c56..a9afca3e 100644 --- a/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_mediator.mm +++ b/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_mediator.mm
@@ -8,6 +8,7 @@ #import "base/memory/raw_ptr.h" #import "base/strings/sys_string_conversions.h" +#import "base/strings/utf_string_conversions.h" #import "components/bookmarks/browser/bookmark_model.h" #import "components/bookmarks/browser/bookmark_node.h" #import "components/commerce/core/commerce_constants.h" @@ -21,6 +22,7 @@ #import "components/prefs/ios/pref_observer_bridge.h" #import "components/prefs/pref_change_registrar.h" #import "components/prefs/pref_service.h" +#import "components/url_formatter/elide_url.h" #import "ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_action_delegate.h" #import "ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_data.h" #import "ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_favicon_consumer.h" @@ -32,6 +34,8 @@ #import "ios/chrome/common/ui/favicon/favicon_attributes.h" #import "ios/chrome/common/ui/favicon/favicon_constants.h" #import "ios/chrome/common/ui/favicon/favicon_view.h" +#import "ios/chrome/grit/ios_strings.h" +#import "ui/base/l10n/l10n_util_mac.h" @interface ShopCardMediator () <PrefObserverDelegate, ShopCardFaviconConsumerSource> @@ -206,6 +210,20 @@ _shopCardItem.shopCardData.productURL = bookmark->url(); _shopCardItem.shopCardData.productTitle = [NSString stringWithUTF8String:specifics.title().c_str()]; + + _shopCardItem.shopCardData.accessibilityString = l10n_util::GetNSStringF( + IDS_IOS_CONTENT_SUGGESTIONS_SHOPCARD_PRICE_TRACKING_ACCESSIBILITY_LABEL, + base::SysNSStringToUTF16( + _shopCardItem.shopCardData.priceDrop->previous_price), + base::SysNSStringToUTF16( + _shopCardItem.shopCardData.priceDrop->current_price), + base::SysNSStringToUTF16(_shopCardItem.shopCardData.productTitle), + GetHostnameFromGURL(bookmark->url())); +} + +std::u16string GetHostnameFromGURL(const GURL& url) { + return url_formatter:: + FormatUrlForDisplayOmitSchemePathTrivialSubdomainsAndMobilePrefix(url); } - (NSString*)GetFormattedPrice:(payments::CurrencyFormatter*)formatter
diff --git a/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_view.mm b/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_view.mm index 7be3ffc2..bd672de 100644 --- a/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_view.mm +++ b/ios/chrome/browser/content_suggestions/ui_bundled/shop_card/shop_card_view.mm
@@ -22,6 +22,7 @@ #import "url/gurl.h" namespace { +NSString* const kShopCardViewIdentifier = @"kShopCardViewIdentifier"; const CGFloat kHorizontalStackSpacing = 16.0f; const CGFloat kVerticalStackSpacing = 6.0f; const CGFloat kCenterSymbolSize = 20.0; @@ -109,6 +110,7 @@ // Styling [self addProductImageAndOverlay]; [self addFaviconImageAndContainer:_item.shopCardData.faviconImage]; + _faviconImageContainer.backgroundColor = UIColor.whiteColor; _faviconImageContainer.layer.mask = [self faviconMaskWithRadius:kFaviconImageContainerTrailingCornerRadius imageHeightWidth:kFaviconImageWidthHeight]; @@ -152,6 +154,7 @@ // Styling [self addProductImageEmptyGray]; [self addFaviconImageAndContainer:_item.shopCardData.faviconImage]; + _faviconImageContainer.backgroundColor = UIColor.whiteColor; [self addShadowForFaviconContainer]; // Hierarchy @@ -204,6 +207,26 @@ _contentStack.alignment = UIStackViewAlignmentTop; [self addSubview:_contentStack]; AddSameConstraints(_contentStack, self); + + // Accessibility + self.isAccessibilityElement = YES; + self.accessibilityIdentifier = kShopCardViewIdentifier; + self.accessibilityTraits = UIAccessibilityTraitButton; + self.accessibilityLabel = _item.shopCardData.accessibilityString; + _priceNotificationsChip.isAccessibilityElement = YES; + _titleLabel.accessibilityTraits |= UIAccessibilityTraitHeader; + // For larger font size, hide price chip. + if (@available(iOS 17, *)) { + NSArray<UITrait>* traits = TraitCollectionSetForTraits( + @[ UITraitPreferredContentSizeCategory.class ]); + [self registerForTraitChanges:traits + withAction:@selector(hideDomainOnTraitChange)]; + } +} + +- (void)hideDomainOnTraitChange { + _urlLabel.hidden = self.traitCollection.preferredContentSizeCategory > + UIContentSizeCategoryExtraExtraLarge; } // Returns the tab hostname from the given `URL`.
diff --git a/media/base/picture_in_picture_events_info.cc b/media/base/picture_in_picture_events_info.cc index a0bcc79..5cd4c0c 100644 --- a/media/base/picture_in_picture_events_info.cc +++ b/media/base/picture_in_picture_events_info.cc
@@ -34,17 +34,18 @@ // static std::string PictureInPictureEventsInfo::AutoPipInfoToString( AutoPipInfo auto_pip_info) { + constexpr std::array<std::string, 2> bool_to_string{"false", "true"}; return base::StringPrintf( "{Reason: %s, has audio focus: %s, is_playing: %s, was recently audible: " "%s, has safe url: %s, meets media engagement conditions: %s, blocked " "due to content setting: %s}", AutoPipReasonToString(auto_pip_info.auto_pip_reason), - auto_pip_info.has_audio_focus ? "true" : "false", - auto_pip_info.is_playing ? "true" : "false", - auto_pip_info.was_recently_audible ? "true" : "false", - auto_pip_info.has_safe_url ? "true" : "false", - auto_pip_info.meets_media_engagement_conditions ? "true" : "false", - auto_pip_info.blocked_due_to_content_setting ? "true" : "false"); + bool_to_string[auto_pip_info.has_audio_focus], + bool_to_string[auto_pip_info.is_playing], + bool_to_string[auto_pip_info.was_recently_audible], + bool_to_string[auto_pip_info.has_safe_url], + bool_to_string[auto_pip_info.meets_media_engagement_conditions], + bool_to_string[auto_pip_info.blocked_due_to_content_setting]); } } // namespace media
diff --git a/mojo/core/ipcz_driver/transport.cc b/mojo/core/ipcz_driver/transport.cc index 69db6750..c1516bae 100644 --- a/mojo/core/ipcz_driver/transport.cc +++ b/mojo/core/ipcz_driver/transport.cc
@@ -520,7 +520,7 @@ } #if BUILDFLAG(IS_WIN) - DCHECK(handles.empty()); + CHECK(handles.empty()); size_t num_handles = header.num_handles; const HandleOwner handle_owner = header.handle_owner;
diff --git a/net/base/load_timing_internal_info.h b/net/base/load_timing_internal_info.h index 6680872e..88366d9 100644 --- a/net/base/load_timing_internal_info.h +++ b/net/base/load_timing_internal_info.h
@@ -21,6 +21,9 @@ bool operator==(const LoadTimingInternalInfo& other) const; ~LoadTimingInternalInfo(); + // The time taken for HTTP stream creating to finish. + base::TimeDelta create_stream_delay; + // The time taken for HTTP stream initialization to finish if the // initialization was blocked. base::TimeDelta initialize_stream_delay;
diff --git a/net/cert/internal/system_trust_store_unittest.cc b/net/cert/internal/system_trust_store_unittest.cc index 6a3299fd..e7309d8c 100644 --- a/net/cert/internal/system_trust_store_unittest.cc +++ b/net/cert/internal/system_trust_store_unittest.cc
@@ -69,6 +69,7 @@ std::unique_ptr<TrustStoreChrome> test_trust_store_chrome = TrustStoreChrome::CreateTrustStoreForTesting( base::span<const ChromeRootCertInfo>(kChromeRootCertList), + base::span(kEutlRootCertList), /*version=*/1); std::unique_ptr<net::PlatformTrustStore> test_platform_trust_store = @@ -112,6 +113,7 @@ std::unique_ptr<TrustStoreChrome> test_trust_store_chrome = TrustStoreChrome::CreateTrustStoreForTesting( base::span<const ChromeRootCertInfo>(kChromeRootCertList), + base::span(kEutlRootCertList), /*version=*/1); std::unique_ptr<net::PlatformTrustStore> test_platform_trust_store =
diff --git a/net/cert/internal/trust_store_chrome.cc b/net/cert/internal/trust_store_chrome.cc index ec704405..5612671 100644 --- a/net/cert/internal/trust_store_chrome.cc +++ b/net/cert/internal/trust_store_chrome.cc
@@ -71,8 +71,17 @@ ChromeRootStoreData::Anchor::Anchor( std::shared_ptr<const bssl::ParsedCertificate> certificate, std::vector<ChromeRootCertConstraints> constraints) + : ChromeRootStoreData::Anchor::Anchor(certificate, + constraints, + /*eutl=*/false) {} + +ChromeRootStoreData::Anchor::Anchor( + std::shared_ptr<const bssl::ParsedCertificate> certificate, + std::vector<ChromeRootCertConstraints> constraints, + bool eutl) : certificate(std::move(certificate)), - constraints(std::move(constraints)) {} + constraints(std::move(constraints)), + eutl(eutl) {} ChromeRootStoreData::Anchor::~Anchor() = default; ChromeRootStoreData::Anchor::Anchor(const Anchor& other) = default; @@ -93,12 +102,13 @@ ChromeRootStoreData& ChromeRootStoreData::operator=( ChromeRootStoreData&& other) = default; -std::optional<ChromeRootStoreData> -ChromeRootStoreData::CreateFromRootStoreProto( - const chrome_root_store::RootStore& proto) { - ChromeRootStoreData root_store_data; +namespace { - for (auto& anchor : proto.trust_anchors()) { +std::optional<std::vector<ChromeRootStoreData::Anchor>> CreateAnchors( + const google::protobuf::RepeatedPtrField<chrome_root_store::TrustAnchor>& + anchors) { + std::vector<ChromeRootStoreData::Anchor> data_anchors; + for (auto& anchor : anchors) { if (anchor.der().empty()) { LOG(ERROR) << "Error anchor with empty DER in update"; return std::nullopt; @@ -145,9 +155,29 @@ min_version, max_version_exclusive, base::ToVector(constraint.permitted_dns_names())); } - root_store_data.anchors_.emplace_back(std::move(parsed), - std::move(constraints)); + data_anchors.emplace_back(std::move(parsed), std::move(constraints), + anchor.eutl()); } + return data_anchors; +} + +} // namespace + +std::optional<ChromeRootStoreData> +ChromeRootStoreData::CreateFromRootStoreProto( + const chrome_root_store::RootStore& proto) { + ChromeRootStoreData root_store_data; + + auto trust_anchors = CreateAnchors(proto.trust_anchors()); + if (!trust_anchors) { + return std::nullopt; + } + root_store_data.trust_anchors_ = std::move(*trust_anchors); + auto additional_certs = CreateAnchors(proto.additional_certs()); + if (!additional_certs) { + return std::nullopt; + } + root_store_data.additional_certs_ = std::move(*additional_certs); root_store_data.version_ = proto.version_major(); @@ -155,20 +185,22 @@ } ChromeRootStoreData ChromeRootStoreData::CreateFromCompiledRootStore() { - return ChromeRootStoreData(kChromeRootCertList, + return ChromeRootStoreData(kChromeRootCertList, kEutlRootCertList, /*certs_are_static=*/true, /*version=*/CompiledChromeRootStoreVersion()); } ChromeRootStoreData ChromeRootStoreData::CreateForTesting( base::span<const ChromeRootCertInfo> certs, + base::span<const base::span<const uint8_t>> eutl_certs, int64_t version) { - return ChromeRootStoreData(certs, + return ChromeRootStoreData(certs, eutl_certs, /*certs_are_static=*/false, version); } ChromeRootStoreData::ChromeRootStoreData( base::span<const ChromeRootCertInfo> certs, + base::span<const base::span<const uint8_t>> eutl_certs, bool certs_are_static, int64_t version) : version_(version) { @@ -200,7 +232,23 @@ for (const auto& constraint : cert_info.constraints) { cert_constraints.emplace_back(constraint); } - anchors_.emplace_back(std::move(parsed), std::move(cert_constraints)); + trust_anchors_.emplace_back(std::move(parsed), std::move(cert_constraints)); + } + + for (const auto& cert_bytes : eutl_certs) { + bssl::UniquePtr<CRYPTO_BUFFER> cert; + if (certs_are_static) { + cert = x509_util::CreateCryptoBufferFromStaticDataUnsafe(cert_bytes); + } else { + cert = x509_util::CreateCryptoBuffer(cert_bytes); + } + bssl::CertErrors errors; + auto parsed = bssl::ParsedCertificate::Create( + std::move(cert), x509_util::DefaultParseCertificateOptions(), &errors); + CHECK(parsed); + additional_certs_.emplace_back(std::move(parsed), + std::vector<ChromeRootCertConstraints>(), + /*eutl=*/true); } } @@ -217,12 +265,20 @@ std::pair<std::string_view, std::vector<ChromeRootCertConstraints>>> constraints; - for (const auto& anchor : root_store_data.anchors()) { + for (const auto& anchor : root_store_data.trust_anchors()) { if (!anchor.constraints.empty()) { constraints.emplace_back(anchor.certificate->der_cert().AsStringView(), anchor.constraints); } trust_store_.AddTrustAnchor(anchor.certificate); + if (anchor.eutl) { + eutl_trust_store_.AddTrustAnchor(anchor.certificate); + } + } + for (const auto& anchor : root_store_data.additional_certs()) { + if (anchor.eutl) { + eutl_trust_store_.AddTrustAnchor(anchor.certificate); + } } constraints_ = base::flat_map(std::move(constraints)); @@ -361,11 +417,12 @@ // static std::unique_ptr<TrustStoreChrome> TrustStoreChrome::CreateTrustStoreForTesting( base::span<const ChromeRootCertInfo> certs, + base::span<const base::span<const uint8_t>> eutl_certs, int64_t version, ConstraintOverrideMap override_constraints) { // Note: wrap_unique is used because the constructor is private. return base::WrapUnique(new TrustStoreChrome( - ChromeRootStoreData::CreateForTesting(certs, version), + ChromeRootStoreData::CreateForTesting(certs, eutl_certs, version), std::move(override_constraints))); }
diff --git a/net/cert/internal/trust_store_chrome.h b/net/cert/internal/trust_store_chrome.h index b25a6b2..6121814 100644 --- a/net/cert/internal/trust_store_chrome.h +++ b/net/cert/internal/trust_store_chrome.h
@@ -71,6 +71,9 @@ struct NET_EXPORT Anchor { Anchor(std::shared_ptr<const bssl::ParsedCertificate> certificate, std::vector<ChromeRootCertConstraints> constraints); + Anchor(std::shared_ptr<const bssl::ParsedCertificate> certificate, + std::vector<ChromeRootCertConstraints> constraints, + bool eutl); ~Anchor(); Anchor(const Anchor& other); @@ -80,9 +83,10 @@ std::shared_ptr<const bssl::ParsedCertificate> certificate; std::vector<ChromeRootCertConstraints> constraints; + bool eutl; }; - // CreateChromeRootStoreData converts |proto| into a usable + // CreateFromRootStoreProto converts |proto| into a usable // ChromeRootStoreData object. Returns std::nullopt if the passed in // proto has errors in it (e.g. an unparsable DER-encoded certificate). static std::optional<ChromeRootStoreData> CreateFromRootStoreProto( @@ -95,6 +99,7 @@ // Creates a ChromeRootStoreData using the provided test data. static ChromeRootStoreData CreateForTesting( base::span<const ChromeRootCertInfo> certs, + base::span<const base::span<const uint8_t>> eutl_certs, int64_t version); ~ChromeRootStoreData(); @@ -104,16 +109,21 @@ ChromeRootStoreData& operator=(const ChromeRootStoreData& other); ChromeRootStoreData& operator=(ChromeRootStoreData&& other); - const std::vector<Anchor>& anchors() const { return anchors_; } + const std::vector<Anchor>& trust_anchors() const { return trust_anchors_; } + const std::vector<Anchor>& additional_certs() const { + return additional_certs_; + } int64_t version() const { return version_; } private: ChromeRootStoreData(); ChromeRootStoreData(base::span<const ChromeRootCertInfo> certs, + base::span<const base::span<const uint8_t>> eutl_certs, bool certs_are_static, int64_t version); - std::vector<Anchor> anchors_; + std::vector<Anchor> trust_anchors_; + std::vector<Anchor> additional_certs_; int64_t version_; }; @@ -150,6 +160,7 @@ // default Chrome Root Store. static std::unique_ptr<TrustStoreChrome> CreateTrustStoreForTesting( base::span<const ChromeRootCertInfo> certs, + base::span<const base::span<const uint8_t>> eutl_certs, int64_t version, ConstraintOverrideMap override_constraints = {}); @@ -206,7 +217,6 @@ // entry in this map, it will override the entry in `constraints_` (if any). const ConstraintOverrideMap override_constraints_; - // TODO(crbug.com/392931067): populate the EU Trust List. bssl::TrustStoreInMemory eutl_trust_store_; int64_t version_;
diff --git a/net/cert/internal/trust_store_chrome_unittest.cc b/net/cert/internal/trust_store_chrome_unittest.cc index cee4b14..f694e6b6 100644 --- a/net/cert/internal/trust_store_chrome_unittest.cc +++ b/net/cert/internal/trust_store_chrome_unittest.cc
@@ -48,14 +48,16 @@ std::unique_ptr<TrustStoreChrome> trust_store_chrome = TrustStoreChrome::CreateTrustStoreForTesting( base::span<const ChromeRootCertInfo>(kChromeRootCertList), + base::span(kEutlRootCertList), /*version=*/1); // Check every certificate in test_store.certs is included. CertificateList certs = CreateCertificateListFromFile( GetTestNetDataDirectory().AppendASCII("ssl/chrome_root_store"), "test_store.certs", X509Certificate::FORMAT_PEM_CERT_SEQUENCE); - ASSERT_EQ(certs.size(), 2u); + ASSERT_EQ(certs.size(), 3u); + size_t eutl_certs = 0; for (const auto& cert : certs) { std::shared_ptr<const bssl::ParsedCertificate> parsed = ToParsedCertificate(*cert); @@ -63,7 +65,15 @@ bssl::CertificateTrust trust = trust_store_chrome->GetTrust(parsed.get()); EXPECT_EQ(bssl::CertificateTrust::ForTrustAnchor().ToDebugString(), trust.ToDebugString()); + // Count how many certs are on the EUTL. + bssl::CertificateTrust eutl_trust = + trust_store_chrome->eutl_trust_store()->GetTrust(parsed.get()); + if (eutl_trust.type == bssl::CertificateTrustType::TRUSTED_ANCHOR) { + eutl_certs++; + } } + // There should be one cert from test_store.certs on the EUTL. + EXPECT_EQ(eutl_certs, 1); // Other certificates should not be included. Which test cert used here isn't // important as long as it isn't one of the certificates in the @@ -80,10 +90,51 @@ trust.ToDebugString()); } +TEST(TrustStoreChromeTestNoFixture, ContainsEutlCert) { + std::unique_ptr<TrustStoreChrome> trust_store_chrome = + TrustStoreChrome::CreateTrustStoreForTesting( + base::span<const ChromeRootCertInfo>(kChromeRootCertList), + base::span(kEutlRootCertList), + /*version=*/1); + + // Check that the single certificate in test_additional.certs is included in + // the EUTL trust store, but not trusted for TLS connection establishment. + CertificateList certs = CreateCertificateListFromFile( + GetTestNetDataDirectory().AppendASCII("ssl/chrome_root_store"), + "test_additional.certs", X509Certificate::FORMAT_PEM_CERT_SEQUENCE); + ASSERT_EQ(certs.size(), 1u); + std::shared_ptr<const bssl::ParsedCertificate> parsed = + ToParsedCertificate(*certs[0]); + + bssl::CertificateTrust eutl_trust = + trust_store_chrome->eutl_trust_store()->GetTrust(parsed.get()); + EXPECT_EQ(bssl::CertificateTrust::ForTrustAnchor().ToDebugString(), + eutl_trust.ToDebugString()); + + EXPECT_FALSE(trust_store_chrome->Contains(parsed.get())); + bssl::CertificateTrust trust = trust_store_chrome->GetTrust(parsed.get()); + EXPECT_EQ(bssl::CertificateTrust::ForUnspecified().ToDebugString(), + trust.ToDebugString()); + + // Other certificates should not be included. Which test cert used here isn't + // important as long as it isn't one of the certificates in the + // chrome_root_store/test_store.certs. + scoped_refptr<X509Certificate> other_cert = + ImportCertFromFile(GetTestCertsDirectory(), "root_ca_cert.pem"); + ASSERT_TRUE(other_cert); + std::shared_ptr<const bssl::ParsedCertificate> other_parsed = + ToParsedCertificate(*other_cert); + eutl_trust = + trust_store_chrome->eutl_trust_store()->GetTrust(other_parsed.get()); + EXPECT_EQ(bssl::CertificateTrust::ForUnspecified().ToDebugString(), + eutl_trust.ToDebugString()); +} + TEST(TrustStoreChromeTestNoFixture, Constraints) { std::unique_ptr<TrustStoreChrome> trust_store_chrome = TrustStoreChrome::CreateTrustStoreForTesting( base::span<const ChromeRootCertInfo>(kChromeRootCertList), + base::span(kEutlRootCertList), /*version=*/1); const std::string kUnconstrainedCertHash = @@ -97,7 +148,7 @@ CertificateList certs = CreateCertificateListFromFile( GetTestNetDataDirectory().AppendASCII("ssl/chrome_root_store"), "test_store.certs", X509Certificate::FORMAT_PEM_CERT_SEQUENCE); - ASSERT_EQ(certs.size(), 2u); + ASSERT_EQ(certs.size(), 3u); for (const auto& cert : certs) { std::shared_ptr<const bssl::ParsedCertificate> parsed = ToParsedCertificate(*cert); @@ -213,6 +264,7 @@ std::unique_ptr<TrustStoreChrome> trust_store_chrome = TrustStoreChrome::CreateTrustStoreForTesting( std::move(root_cert_info), + /*eutl_certs=*/{}, /*version=*/1, std::move(override_constraints)); {
diff --git a/net/data/ssl/chrome_root_store/BUILD.gn b/net/data/ssl/chrome_root_store/BUILD.gn index d782e4f..75da29e1 100644 --- a/net/data/ssl/chrome_root_store/BUILD.gn +++ b/net/data/ssl/chrome_root_store/BUILD.gn
@@ -8,6 +8,7 @@ tool = "//net/tools/root_store_tool:root_store_tool" inputs = [ + "additional.certs", "root_store.certs", "root_store.textproto", ] @@ -31,6 +32,7 @@ tool = "//net/tools/root_store_tool:root_store_tool" inputs = [ + "test_additional.certs", "test_store.certs", "test_store.textproto", ] @@ -38,6 +40,8 @@ args = [ "--root-store=" + rebase_path("test_store.textproto", root_build_dir), "--certs=" + rebase_path("test_store.certs", root_build_dir), + "--additional-certs=" + + rebase_path("test_additional.certs", root_build_dir), "--write-cpp-root-store=" + rebase_path("${target_gen_dir}/chrome-root-store-test-data-inc.cc", root_build_dir),
diff --git a/net/data/ssl/chrome_root_store/test_additional.certs b/net/data/ssl/chrome_root_store/test_additional.certs new file mode 100644 index 0000000..a448747 --- /dev/null +++ b/net/data/ssl/chrome_root_store/test_additional.certs
@@ -0,0 +1,41 @@ +List: "http://www.nmhh.hu/tl/pub/HU_TL.xml" +SHA-256 hash: f7c7e28fb5e79f314aaac6bbba932f15e1a72069f435d4c9e707f93ca1482ee3 +TSP service name: "Qualified e-Szigno TLS CA 2018" +Subject: "CN=Qualified e-Szigno TLS CA 2018,O=Microsec Ltd.,L=Budapest,C=HU,2.5.4.97=#130e56415448552d3233353834343937" +Issuer: "CN=Microsec e-Szigno Root CA 2009,O=Microsec Ltd.,L=Budapest,C=HU,1.2.840.113549.1.9.1=#0c10696e666f40652d737a69676e6f2e6875" +-----BEGIN CERTIFICATE----- +MIIGRDCCBSygAwIBAgINALhu3yfY9pZ8ZHBjCjANBgkqhkiG9w0BAQsFADCBgjEL +MAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRYwFAYDVQQKDA1NaWNyb3Nl +YyBMdGQuMScwJQYDVQQDDB5NaWNyb3NlYyBlLVN6aWdubyBSb290IENBIDIwMDkx +HzAdBgkqhkiG9w0BCQEWEGluZm9AZS1zemlnbm8uaHUwHhcNMTgwNzMxMDkwMDAw +WhcNMjkxMjI5MDkwMDAwWjB6MQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBl +c3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xFzAVBgNVBGEMDlZBVEhVLTIzNTg0 +NDk3MScwJQYDVQQDDB5RdWFsaWZpZWQgZS1Temlnbm8gVExTIENBIDIwMTgwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCUqWys07LtoLYGPI2CaftcqV04 +RnOKO/0VvI3xTidTpedvmwhk827c/Gks68y6ncC9chZGmvgN6G9lnD663kovauXC +Y69+xHfTskdt5xUQDyKKBjIztZNwdWKM9sJZE6UDgrIjHrLJ11FQs1dK9XhrtHIQ +O+XSOat8DYcXwAgpTqkpTe3RUDVQT7z+PleqJRe8tr6YCYuOiq4ZN2PTpd3LxB/B +25LZkYfB1aK7aHv/aXbo6mxR6+wr052a608IRnfHfY5lPV0GEDfCHtoY7Nk7nkb7 +d8LKyBcFqAJ18EKjYDJHYhn9lVa/huF1ZwoQomwi+3XeHySmUjzbO1HVW9VPAgMB +AAGjggK+MIICujAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA7BgNV +HSAENDAyMDAGBFUdIAAwKDAmBggrBgEFBQcCARYaaHR0cDovL2NwLmUtc3ppZ25v +Lmh1L2FjcHMwHQYDVR0OBBYEFH2ETsLUa+rB1yKMaMPpoPTsmIocMB8GA1UdIwQY +MBaAFMsPxt9CQ8w9y7VII6EaeqYquzRoMIG2BgNVHR8Ega4wgaswN6A1oDOGMWh0 +dHA6Ly9yb290Y2EyMDA5LWNybDEuZS1zemlnbm8uaHUvcm9vdGNhMjAwOS5jcmww +N6A1oDOGMWh0dHA6Ly9yb290Y2EyMDA5LWNybDIuZS1zemlnbm8uaHUvcm9vdGNh +MjAwOS5jcmwwN6A1oDOGMWh0dHA6Ly9yb290Y2EyMDA5LWNybDMuZS1zemlnbm8u +aHUvcm9vdGNhMjAwOS5jcmwwggFfBggrBgEFBQcBAQSCAVEwggFNMC8GCCsGAQUF +BzABhiNodHRwOi8vcm9vdGNhMjAwOS1vY3NwMS5lLXN6aWduby5odTAvBggrBgEF +BQcwAYYjaHR0cDovL3Jvb3RjYTIwMDktb2NzcDIuZS1zemlnbm8uaHUwLwYIKwYB +BQUHMAGGI2h0dHA6Ly9yb290Y2EyMDA5LW9jc3AzLmUtc3ppZ25vLmh1MDwGCCsG +AQUFBzAChjBodHRwOi8vcm9vdGNhMjAwOS1jYTEuZS1zemlnbm8uaHUvcm9vdGNh +MjAwOS5jcnQwPAYIKwYBBQUHMAKGMGh0dHA6Ly9yb290Y2EyMDA5LWNhMi5lLXN6 +aWduby5odS9yb290Y2EyMDA5LmNydDA8BggrBgEFBQcwAoYwaHR0cDovL3Jvb3Rj +YTIwMDktY2EzLmUtc3ppZ25vLmh1L3Jvb3RjYTIwMDkuY3J0MA0GCSqGSIb3DQEB +CwUAA4IBAQB/UW04hmlTDGlMwETB/pdfSi6eFSvWtDxBd5LsS+5cR17yT9u5Iqxg +37Ov5SWS2gaJP+JQ2Llgpz3YbYnJYexiFTsWhPEmUQR3YoQfIJ6tYmmMK1EE2bJE +fWmXMJDps3h4o2IAKsIsV5NjjTYdVtg6tAoxdKUEMJj7y1K4Z/DoMONz/Xd1Lmf1 +clzWuce4vzA+5VxdpWJsupDUfOS0uuvUjoZxrYXMIiSarYzn51XU0kM+kf/+cNu1 +E0NKnqshwv10EWG5IKFRsq0LYWjogihvzFlAmXftt3G12gXFAwndw2Hed8HXBfUj +8WjjEBCliiD/N9P40C9JEZovvAkkL07h +-----END CERTIFICATE-----
diff --git a/net/data/ssl/chrome_root_store/test_store.certs b/net/data/ssl/chrome_root_store/test_store.certs index 8be1b0de..cec3184 100644 --- a/net/data/ssl/chrome_root_store/test_store.certs +++ b/net/data/ssl/chrome_root_store/test_store.certs
@@ -1,3 +1,128 @@ +# 55926084ec963a64b96e2abe01ce0ba86a64fbfebcc7aab5afc155b37fd76066 +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 6271844772424770508 (0x570a119742c4e3cc) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C = IT, L = Milan, O = Actalis S.p.A./03358520967, CN = Actalis Authentication Root CA + Validity + Not Before: Sep 22 11:22:02 2011 GMT + Not After : Sep 22 11:22:02 2030 GMT + Subject: C = IT, L = Milan, O = Actalis S.p.A./03358520967, CN = Actalis Authentication Root CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (4096 bit) + Modulus: + 00:a7:c6:c4:a5:29:a4:2c:ef:e5:18:c5:b0:50:a3: + 6f:51:3b:9f:0a:5a:c9:c2:48:38:0a:c2:1c:a0:18: + 7f:91:b5:87:b9:40:3f:dd:1d:68:1f:08:83:d5:2d: + 1e:88:a0:f8:8f:56:8f:6d:99:02:92:90:16:d5:5f: + 08:6c:89:d7:e1:ac:bc:20:c2:b1:e0:83:51:8a:69: + 4d:00:96:5a:6f:2f:c0:44:7e:a3:0e:e4:91:cd:58: + ee:dc:fb:c7:1e:45:47:dd:27:b9:08:01:9f:a6:21: + 1d:f5:41:2d:2f:4c:fd:28:ad:e0:8a:ad:22:b4:56: + 65:8e:86:54:8f:93:43:29:de:39:46:78:a3:30:23: + ba:cd:f0:7d:13:57:c0:5d:d2:83:6b:48:4c:c4:ab: + 9f:80:5a:5b:3a:bd:c9:a7:22:3f:80:27:33:5b:0e: + b7:8a:0c:5d:07:37:08:cb:6c:d2:7a:47:22:44:35: + c5:cc:cc:2e:8e:dd:2a:ed:b7:7d:66:0d:5f:61:51: + 22:55:1b:e3:46:e3:e3:3d:d0:35:62:9a:db:af:14: + c8:5b:a1:cc:89:1b:e1:30:26:fc:a0:9b:1f:81:a7: + 47:1f:04:eb:a3:39:92:06:9f:99:d3:bf:d3:ea:4f: + 50:9c:19:fe:96:87:1e:3c:65:f6:a3:18:24:83:86: + 10:e7:54:3e:a8:3a:76:24:4f:81:21:c5:e3:0f:02: + f8:93:94:47:20:bb:fe:d4:0e:d3:68:b9:dd:c4:7a: + 84:82:e3:53:54:79:dd:db:9c:d2:f2:07:9b:2e:b6: + bc:3e:ed:85:6d:ef:25:11:f2:97:1a:42:61:f7:4a: + 97:e8:8b:b1:10:07:fa:65:81:b2:a2:39:cf:f7:3c: + ff:18:fb:c6:f1:5a:8b:59:e2:02:ac:7b:92:d0:4e: + 14:4f:59:45:f6:0c:5e:28:5f:b0:e8:3f:45:cf:cf: + af:9b:6f:fb:84:d3:77:5a:95:6f:ac:94:84:9e:ee: + bc:c0:4a:8f:4a:93:f8:44:21:e2:31:45:61:50:4e: + 10:d8:e3:35:7c:4c:19:b4:de:05:bf:a3:06:9f:c8: + b5:cd:e4:1f:d7:17:06:0d:7a:95:74:55:0d:68:1a: + fc:10:1b:62:64:9d:6d:e0:95:a0:c3:94:07:57:0d: + 14:e6:bd:05:fb:b8:9f:e6:df:8b:e2:c6:e7:7e:96: + f6:53:c5:80:34:50:28:58:f0:12:50:71:17:30:ba: + e6:78:63:bc:f4:b2:ad:9b:2b:b2:fe:e1:39:8c:5e: + ba:0b:20:94:de:7b:83:b8:ff:e3:56:8d:b7:11:e9: + 3b:8c:f2:b1:c1:5d:9d:a4:0b:4c:2b:d9:b2:18:f5: + b5:9f:4b + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 52:D8:88:3A:C8:9F:78:66:ED:89:F3:7B:38:70:94:C9:02:02:36:D0 + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Authority Key Identifier: + keyid:52:D8:88:3A:C8:9F:78:66:ED:89:F3:7B:38:70:94:C9:02:02:36:D0 + + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + Signature Algorithm: sha256WithRSAEncryption + 0b:7b:72:87:c0:60:a6:49:4c:88:58:e6:1d:88:f7:14:64:48: + a6:d8:58:0a:0e:4f:13:35:df:35:1d:d4:ed:06:31:c8:81:3e: + 6a:d5:dd:3b:1a:32:ee:90:3d:11:d2:2e:f4:8e:c3:63:2e:23: + 66:b0:67:be:6f:b6:c0:13:39:60:aa:a2:34:25:93:75:52:de: + a7:9d:ad:0e:87:89:52:71:6a:16:3c:19:1d:83:f8:9a:29:65: + be:f4:3f:9a:d9:f0:f3:5a:87:21:71:80:4d:cb:e0:38:9b:3f: + bb:fa:e0:30:4d:cf:86:d3:65:10:19:18:d1:97:02:b1:2b:72: + 42:68:ac:a0:bd:4e:5a:da:18:bf:6b:98:81:d0:fd:9a:be:5e: + 15:48:cd:11:15:b9:c0:29:5c:b4:e8:88:f7:3e:36:ae:b7:62: + fd:1e:62:de:70:78:10:1c:48:5b:da:bc:a4:38:ba:67:ed:55: + 3e:5e:57:df:d4:03:40:4c:81:a4:d2:4f:63:a7:09:42:09:14: + fc:00:a9:c2:80:73:4f:2e:c0:40:d9:11:7b:48:ea:7a:02:c0: + d3:eb:28:01:26:58:74:c1:c0:73:22:6d:93:95:fd:39:7d:bb: + 2a:e3:f6:82:e3:2c:97:5f:4e:1f:91:94:fa:fe:2c:a3:d8:76: + 1a:b8:4d:b2:38:4f:9b:fa:1d:48:60:79:26:e2:f3:fd:a9:d0: + 9a:e8:70:8f:49:7a:d6:e5:bd:0a:0e:db:2d:f3:8d:bf:eb:e3: + a4:7d:cb:c7:95:71:e8:da:a3:7c:c5:c2:f8:74:92:04:1b:86: + ac:a4:22:53:40:b6:ac:fe:4c:76:cf:fb:94:32:c0:35:9f:76: + 3f:6e:e5:90:6e:a0:a6:26:a2:b8:2c:be:d1:2b:85:fd:a7:68: + c8:ba:01:2b:b1:6c:74:1d:b8:73:95:e7:ee:b7:c7:25:f0:00: + 4c:00:b2:7e:b6:0b:8b:1c:f3:c0:50:9e:25:b9:e0:08:de:36: + 66:ff:37:a5:d1:bb:54:64:2c:c9:27:b5:4b:92:7e:65:ff:d3: + 2d:e1:b9:4e:bc:7f:a4:41:21:90:41:77:a6:39:1f:ea:9e:e3: + 9f:d0:66:6f:05:ec:aa:76:7e:bf:6b:16:a0:eb:b5:c7:fc:92: + 54:2f:2b:11:27:25:37:78:4c:51:6a:b0:f3:cc:58:5d:14:f1: + 6a:48:15:ff:c2:07:b6:b1:8d:0f:8e:5c:50:46:b3:3d:bf:01: + 98:4f:b2:59:54:47:3e:34:7b:78:6d:56:93:2e:73:ea:66:28: + 78:cd:1d:14:bf:a0:8f:2f:2e:b8:2e:8e:f2:14:8a:cc:e9:b5: + 7c:fb:6c:9d:0c:a5:e1:96 +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + # 568d6905a2c88708a4b3025190edcfedb1974a606a13c6e5290fcb2ae63edab5 Certificate: Data:
diff --git a/net/data/ssl/chrome_root_store/test_store.textproto b/net/data/ssl/chrome_root_store/test_store.textproto index 02ed960..7ea2021 100644 --- a/net/data/ssl/chrome_root_store/test_store.textproto +++ b/net/data/ssl/chrome_root_store/test_store.textproto
@@ -29,3 +29,17 @@ permitted_dns_names: "baz.example.com" } } + +# CN=Actalis Authentication Root CA, O=Actalis S.p.A./03358520967, L=Milan, C=IT +# https://ssltest-a.actalis.it:8443 +trust_anchors { + sha256_hex: "55926084ec963a64b96e2abe01ce0ba86a64fbfebcc7aab5afc155b37fd76066" + ev_policy_oids: "2.23.140.1.1" + eutl: true +} + +# "CN=Qualified e-Szigno TLS CA 2018,O=Microsec Ltd.,L=Budapest,C=HU,2.5.4.97=#130e56415448552d3233353834343937" +additional_certs { + sha256_hex: "f7c7e28fb5e79f314aaac6bbba932f15e1a72069f435d4c9e707f93ca1482ee3" + eutl: true +}
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index e9821ba..e570a07e 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc
@@ -578,6 +578,12 @@ void HttpNetworkTransaction::PopulateLoadTimingInternalInfo( LoadTimingInternalInfo* load_timing_internal_info) const { + if (!create_stream_start_time_.is_null() && + !create_stream_end_time_.is_null()) { + CHECK_LE(create_stream_start_time_, create_stream_end_time_); + load_timing_internal_info->create_stream_delay = + create_stream_end_time_ - create_stream_start_time_; + } if (!initialize_stream_start_time_.is_null() && !initialize_stream_end_time_.is_null()) { CHECK_LE(initialize_stream_start_time_, initialize_stream_end_time_); @@ -982,6 +988,10 @@ } create_stream_start_time_ = base::TimeTicks::Now(); + // Reset `create_stream_end_time__` to prevent an inconsistent state in + // case that `DoCreateStream` is called multiple times. + create_stream_end_time_ = base::TimeTicks(); + if (ForWebSocketHandshake()) { stream_request_ = session_->http_stream_factory()->RequestWebSocketHandshakeStream( @@ -1003,9 +1013,11 @@ RecordStreamRequestResult(result); CopyConnectionAttemptsFromStreamRequest(); if (result == OK) { + create_stream_end_time_ = base::TimeTicks::Now(); next_state_ = STATE_CONNECTED_CALLBACK; DCHECK(stream_.get()); CHECK(!create_stream_start_time_.is_null()); + CHECK_LE(create_stream_start_time_, create_stream_end_time_); base::UmaHistogramTimes( base::StrCat( {"Net.NetworkTransaction.Create", @@ -1013,7 +1025,7 @@ : "HttpStreamTime."), (IsGoogleHostWithAlpnH3(url_.host()) ? "GoogleHost." : ""), NegotiatedProtocolToHistogramSuffix(negotiated_protocol_)}), - base::TimeTicks::Now() - create_stream_start_time_); + create_stream_end_time_ - create_stream_start_time_); if (!reset_connection_and_request_for_resend_start_time_.is_null()) { base::UmaHistogramTimes( "Net.NetworkTransaction.ResetConnectionAndResendRequestTime",
diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index 1e2c86c..7e6f4ee 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h
@@ -451,8 +451,9 @@ // transaction. int64_t total_sent_bytes_ = 0; - // When the transaction started creating a stream. + // When the transaction started / finished creating a stream. base::TimeTicks create_stream_start_time_; + base::TimeTicks create_stream_end_time_; // When the transaction started / finished sending the request, including // the body, if present. |send_start_time_| is set to |base::TimeTicks()|
diff --git a/net/test/embedded_test_server/embedded_test_server.cc b/net/test/embedded_test_server/embedded_test_server.cc index fb9d8d1..758557ec 100644 --- a/net/test/embedded_test_server/embedded_test_server.cc +++ b/net/test/embedded_test_server/embedded_test_server.cc
@@ -473,6 +473,10 @@ intermediate->SetCertificatePolicies(cert_config_.policy_oids); } + if (!cert_config_.qwac_qc_types.empty()) { + leaf->SetQwacQcStatements(cert_config_.qwac_qc_types); + } + if (!cert_config_.dns_names.empty() || !cert_config_.ip_addresses.empty()) { leaf->SetSubjectAltNames(cert_config_.dns_names, cert_config_.ip_addresses); } else {
diff --git a/net/test/embedded_test_server/embedded_test_server.h b/net/test/embedded_test_server/embedded_test_server.h index 8f49f5b..7f917b65 100644 --- a/net/test/embedded_test_server/embedded_test_server.h +++ b/net/test/embedded_test_server/embedded_test_server.h
@@ -333,6 +333,11 @@ // intermediate cert (if an intermediate is configured). std::vector<std::string> policy_oids; + // QWAC QC types for the QcStatements extension. If non-empty, the + // QcStatements extension will be set on the leaf cert containing values + // appropriate for a QWAC with the given QC types. + std::vector<bssl::der::Input> qwac_qc_types; + // Value to use for leaf's basicConstraints isCA field bool leaf_is_ca = false;
diff --git a/services/cert_verifier/cert_verifier_service_factory.cc b/services/cert_verifier/cert_verifier_service_factory.cc index a2d1c3b5..3f9556ee 100644 --- a/services/cert_verifier/cert_verifier_service_factory.cc +++ b/services/cert_verifier/cert_verifier_service_factory.cc
@@ -387,7 +387,7 @@ return; } - if (root_store_data->anchors().empty()) { + if (root_store_data->trust_anchors().empty()) { LOG(ERROR) << "parsed root store contained no anchors"; return; } @@ -406,7 +406,7 @@ InitializeRootStoreDataIfNecessary(); info_ptr->version = proc_params_.root_store_data->version(); - for (const auto& anchor : proc_params_.root_store_data->anchors()) { + for (const auto& anchor : proc_params_.root_store_data->trust_anchors()) { if (!IsAnchorTrustedOnThisChromeVersion(anchor)) { continue; }
diff --git a/services/cert_verifier/cert_verifier_service_factory_unittest.cc b/services/cert_verifier/cert_verifier_service_factory_unittest.cc index 59de9972..65f99a2 100644 --- a/services/cert_verifier/cert_verifier_service_factory_unittest.cc +++ b/services/cert_verifier/cert_verifier_service_factory_unittest.cc
@@ -617,7 +617,8 @@ // In cases where the compiled Chrome Root Store has roots with version // constraints, there might be less trusted roots depending on what version # // the test is running at. - EXPECT_LE(info_ptr->root_cert_info.size(), root_store_data.anchors().size()); + EXPECT_LE(info_ptr->root_cert_info.size(), + root_store_data.trust_anchors().size()); EXPECT_GT(info_ptr->root_cert_info.size(), static_cast<std::size_t>(0)); }
diff --git a/services/network/BUILD.gn b/services/network/BUILD.gn index a75687f..bbf14e0 100644 --- a/services/network/BUILD.gn +++ b/services/network/BUILD.gn
@@ -46,6 +46,8 @@ "disk_cache/mojo_backend_file_operations_factory.h", "dns_config_change_manager.cc", "dns_config_change_manager.h", + "file_opener_for_upload.cc", + "file_opener_for_upload.h", "host_resolver.cc", "host_resolver.h", "host_resolver_mdns_listener.cc",
diff --git a/services/network/file_opener_for_upload.cc b/services/network/file_opener_for_upload.cc new file mode 100644 index 0000000..30c97f5d --- /dev/null +++ b/services/network/file_opener_for_upload.cc
@@ -0,0 +1,104 @@ +// 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 "services/network/file_opener_for_upload.h" + +#include "base/task/thread_pool.h" +#include "net/base/net_errors.h" +#include "services/network/public/mojom/network_context_client.mojom.h" + +namespace network { + +namespace { + +// `opened_files` need to be closed on a blocking task runner, so move the +// `opened_files` vector onto a sequence that can block so it gets destroyed +// there. +void PostCloseFiles(std::vector<base::File> opened_files) { + base::ThreadPool::PostTask( + FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE}, + base::DoNothingWithBoundArgs(std::move(opened_files))); +} + +} // namespace + +FileOpenerForUpload::FileOpenerForUpload( + std::vector<base::FilePath> paths, + const GURL& url, + int32_t process_id, + mojom::NetworkContextClient* const network_context_client, + SetUpUploadCallback set_up_upload_callback) + : paths_(std::move(paths)), + url_(url), + process_id_(process_id), + network_context_client_(network_context_client), + set_up_upload_callback_(std::move(set_up_upload_callback)) {} + +FileOpenerForUpload::~FileOpenerForUpload() { + if (!opened_files_.empty()) { + PostCloseFiles(std::move(opened_files_)); + } +} + +void FileOpenerForUpload::Start() { + opened_files_.reserve(paths_.size()); + StartOpeningNextBatch(); +} + +// static +void FileOpenerForUpload::OnFilesForUploadOpened( + base::WeakPtr<FileOpenerForUpload> file_opener, + size_t num_files_requested, + int error_code, + std::vector<base::File> opened_files) { + if (!file_opener) { + PostCloseFiles(std::move(opened_files)); + return; + } + + if (error_code == net::OK && num_files_requested != opened_files.size()) { + error_code = net::ERR_FAILED; + } + + if (error_code != net::OK) { + PostCloseFiles(std::move(opened_files)); + file_opener->FilesForUploadOpenedDone(error_code); + return; + } + file_opener->opened_files_.insert( + file_opener->opened_files_.end(), + std::make_move_iterator(opened_files.begin()), + std::make_move_iterator(opened_files.end())); + + if (file_opener->opened_files_.size() < file_opener->paths_.size()) { + file_opener->StartOpeningNextBatch(); + return; + } + + file_opener->FilesForUploadOpenedDone(net::OK); +} + +void FileOpenerForUpload::StartOpeningNextBatch() { + size_t num_files_to_request = std::min(paths_.size() - opened_files_.size(), + kMaxFileUploadRequestsPerBatch); + std::vector<base::FilePath> batch_paths( + paths_.begin() + opened_files_.size(), + paths_.begin() + opened_files_.size() + num_files_to_request); + + network_context_client_->OnFileUploadRequested( + process_id_, /*async=*/true, batch_paths, url_.get(), + base::BindOnce(&FileOpenerForUpload::OnFilesForUploadOpened, + weak_ptr_factory_.GetWeakPtr(), num_files_to_request)); +} + +void FileOpenerForUpload::FilesForUploadOpenedDone(int error_code) { + if (error_code == net::OK) { + std::move(set_up_upload_callback_).Run(std::move(opened_files_)); + } else { + std::move(set_up_upload_callback_) + .Run(base::unexpected(static_cast<net::Error>(error_code))); + } +} + +} // namespace network
diff --git a/services/network/file_opener_for_upload.h b/services/network/file_opener_for_upload.h new file mode 100644 index 0000000..0669522 --- /dev/null +++ b/services/network/file_opener_for_upload.h
@@ -0,0 +1,87 @@ +// 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 SERVICES_NETWORK_FILE_OPENER_FOR_UPLOAD_H_ +#define SERVICES_NETWORK_FILE_OPENER_FOR_UPLOAD_H_ + +#include <memory> +#include <optional> +#include <vector> + +#include "base/files/file.h" +#include "base/functional/callback.h" +#include "base/memory/raw_ref.h" +#include "base/memory/weak_ptr.h" +#include "base/types/expected.h" +#include "net/base/net_errors.h" + +class GURL; + +namespace network { +namespace mojom { +class NetworkContextClient; +} // namespace mojom + +// Manages opening a list of files specified by `paths_`. This is used by +// URLLoader to prepare files listed in a ResourceRequestBody for upload. It +// interacts with the NetworkContextClient to request file access and opens +// files in batches to avoid overwhelming the system. +class FileOpenerForUpload { + public: + // Callback type invoked when all files have been processed (successfully or + // with an error). + using SetUpUploadCallback = base::OnceCallback<void( + base::expected<std::vector<base::File>, net::Error>)>; + + // Maximum number of file open requests sent to the NetworkContextClient + // in a single batch via OnFileUploadRequested. + static constexpr size_t kMaxFileUploadRequestsPerBatch = 64; + + // Constructor initiates the file opening process. + FileOpenerForUpload(std::vector<base::FilePath> paths, + const GURL& url, + int32_t process_id, + mojom::NetworkContextClient* const network_context_client, + SetUpUploadCallback set_up_upload_callback); + + FileOpenerForUpload(const FileOpenerForUpload&) = delete; + FileOpenerForUpload& operator=(const FileOpenerForUpload&) = delete; + + ~FileOpenerForUpload(); + + // Starts the file opening operations. + void Start(); + + private: + static void OnFilesForUploadOpened( + base::WeakPtr<FileOpenerForUpload> file_opener, + size_t num_files_requested, + int error_code, + std::vector<base::File> opened_files); + + // Initiates the process of opening the next batch of files from `paths_`. + void StartOpeningNextBatch(); + + // Called when all batches have been processed or an error occurred. + // Invokes the final `set_up_upload_callback_`. + void FilesForUploadOpenedDone(int error_code); + + // The list of file paths requested to be opened. + const std::vector<base::FilePath> paths_; + // The URL associated with the upload request (used in OnFileUploadRequested). + // Stored as a raw_ref as the GURL's lifetime is expected to exceed this + // object. + const raw_ref<const GURL> url_; + const int32_t process_id_; + const raw_ptr<mojom::NetworkContextClient> network_context_client_; + SetUpUploadCallback set_up_upload_callback_; + // The files opened so far. + std::vector<base::File> opened_files_; + + base::WeakPtrFactory<FileOpenerForUpload> weak_ptr_factory_{this}; +}; + +} // namespace network + +#endif // SERVICES_NETWORK_FILE_OPENER_FOR_UPLOAD_H_
diff --git a/services/network/public/cpp/load_timing_internal_info_mojom_traits.cc b/services/network/public/cpp/load_timing_internal_info_mojom_traits.cc index 7f0202dc..f89ba8ce 100644 --- a/services/network/public/cpp/load_timing_internal_info_mojom_traits.cc +++ b/services/network/public/cpp/load_timing_internal_info_mojom_traits.cc
@@ -12,6 +12,14 @@ const base::TimeDelta& StructTraits<network::mojom::LoadTimingInternalInfoDataView, net::LoadTimingInternalInfo>:: + create_stream_delay(const net::LoadTimingInternalInfo& info) { + return info.create_stream_delay; +} + +// static +const base::TimeDelta& +StructTraits<network::mojom::LoadTimingInternalInfoDataView, + net::LoadTimingInternalInfo>:: initialize_stream_delay(const net::LoadTimingInternalInfo& info) { return info.initialize_stream_delay; }
diff --git a/services/network/public/cpp/load_timing_internal_info_mojom_traits.h b/services/network/public/cpp/load_timing_internal_info_mojom_traits.h index a41c3c93..91a3d73 100644 --- a/services/network/public/cpp/load_timing_internal_info_mojom_traits.h +++ b/services/network/public/cpp/load_timing_internal_info_mojom_traits.h
@@ -16,6 +16,8 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) StructTraits<network::mojom::LoadTimingInternalInfoDataView, net::LoadTimingInternalInfo> { + static const base::TimeDelta& create_stream_delay( + const net::LoadTimingInternalInfo& info); static const base::TimeDelta& initialize_stream_delay( const net::LoadTimingInternalInfo& info); static bool Read(network::mojom::LoadTimingInternalInfoDataView data,
diff --git a/services/network/public/mojom/load_timing_internal_info.mojom b/services/network/public/mojom/load_timing_internal_info.mojom index 10ced8f9..df8503e 100644 --- a/services/network/public/mojom/load_timing_internal_info.mojom +++ b/services/network/public/mojom/load_timing_internal_info.mojom
@@ -8,5 +8,6 @@ // Mirror of net::LoadTimingInternalInfo. struct LoadTimingInternalInfo { + mojo_base.mojom.TimeDelta create_stream_delay; mojo_base.mojom.TimeDelta initialize_stream_delay; };
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc index 10e2914..f846972 100644 --- a/services/network/url_loader.cc +++ b/services/network/url_loader.cc
@@ -81,6 +81,7 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" #include "services/network/ad_heuristic_cookie_overrides.h" +#include "services/network/file_opener_for_upload.h" #include "services/network/orb/orb_impl.h" #include "services/network/public/cpp/client_hints.h" #include "services/network/public/cpp/constants.h" @@ -618,109 +619,6 @@ } } -// This class is used to manage the queue of pending file upload operations -// initiated by the URLLoader::OpenFilesForUpload(). -class URLLoader::FileOpenerForUpload { - public: - typedef base::OnceCallback<void(int, std::vector<base::File>)> - SetUpUploadCallback; - - FileOpenerForUpload(std::vector<base::FilePath> paths, - URLLoader* url_loader, - int32_t process_id, - mojom::NetworkContextClient* const network_context_client, - SetUpUploadCallback set_up_upload_callback) - : paths_(std::move(paths)), - url_loader_(url_loader), - process_id_(process_id), - network_context_client_(network_context_client), - set_up_upload_callback_(std::move(set_up_upload_callback)) { - StartOpeningNextBatch(); - } - - FileOpenerForUpload(const FileOpenerForUpload&) = delete; - FileOpenerForUpload& operator=(const FileOpenerForUpload&) = delete; - - ~FileOpenerForUpload() { - if (!opened_files_.empty()) - PostCloseFiles(std::move(opened_files_)); - } - - private: - static void OnFilesForUploadOpened( - base::WeakPtr<FileOpenerForUpload> file_opener, - size_t num_files_requested, - int error_code, - std::vector<base::File> opened_files) { - if (!file_opener) { - PostCloseFiles(std::move(opened_files)); - return; - } - - if (error_code == net::OK && num_files_requested != opened_files.size()) - error_code = net::ERR_FAILED; - - if (error_code != net::OK) { - PostCloseFiles(std::move(opened_files)); - file_opener->FilesForUploadOpenedDone(error_code); - return; - } - - for (base::File& file : opened_files) - file_opener->opened_files_.push_back(std::move(file)); - - if (file_opener->opened_files_.size() < file_opener->paths_.size()) { - file_opener->StartOpeningNextBatch(); - return; - } - - file_opener->FilesForUploadOpenedDone(net::OK); - } - - // |opened_files| need to be closed on a blocking task runner, so move the - // |opened_files| vector onto a sequence that can block so it gets destroyed - // there. - static void PostCloseFiles(std::vector<base::File> opened_files) { - base::ThreadPool::PostTask( - FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING}, - base::DoNothingWithBoundArgs(std::move(opened_files))); - } - - void StartOpeningNextBatch() { - size_t num_files_to_request = std::min(paths_.size() - opened_files_.size(), - kMaxFileUploadRequestsPerBatch); - std::vector<base::FilePath> batch_paths( - paths_.begin() + opened_files_.size(), - paths_.begin() + opened_files_.size() + num_files_to_request); - - network_context_client_->OnFileUploadRequested( - process_id_, /*async=*/true, batch_paths, - url_loader_->url_request_->url(), - base::BindOnce(&FileOpenerForUpload::OnFilesForUploadOpened, - weak_ptr_factory_.GetWeakPtr(), num_files_to_request)); - } - - void FilesForUploadOpenedDone(int error_code) { - url_loader_->url_request_->LogUnblocked(); - - if (error_code == net::OK) - std::move(set_up_upload_callback_).Run(net::OK, std::move(opened_files_)); - else - std::move(set_up_upload_callback_).Run(error_code, {}); - } - - // The paths of files for upload - const std::vector<base::FilePath> paths_; - const raw_ptr<URLLoader> url_loader_; - const int32_t process_id_; - const raw_ptr<mojom::NetworkContextClient> network_context_client_; - SetUpUploadCallback set_up_upload_callback_; - // The files opened so far. - std::vector<base::File> opened_files_; - - base::WeakPtrFactory<FileOpenerForUpload> weak_ptr_factory_{this}; -}; - void URLLoader::OpenFilesForUpload(const ResourceRequest& request) { std::vector<base::FilePath> paths; for (const auto& element : *request.request_body.get()->elements()) { @@ -729,7 +627,7 @@ } } if (paths.empty()) { - SetUpUpload(request, net::OK, std::vector<base::File>()); + SetUpUpload(request, std::vector<base::File>()); return; } if (!network_context_client_) { @@ -745,28 +643,35 @@ } url_request_->LogBlockedBy("Opening Files"); file_opener_for_upload_ = std::make_unique<FileOpenerForUpload>( - std::move(paths), this, factory_params_->process_id, + std::move(paths), url_request_->url(), factory_params_->process_id, network_context_client_, base::BindOnce(&URLLoader::SetUpUpload, base::Unretained(this), request)); + file_opener_for_upload_->Start(); } -void URLLoader::SetUpUpload(const ResourceRequest& request, - int error_code, - std::vector<base::File> opened_files) { - if (error_code != net::OK) { - DCHECK(opened_files.empty()); +void URLLoader::SetUpUpload( + const ResourceRequest& request, + base::expected<std::vector<base::File>, net::Error> file_open_result) { + if (file_opener_for_upload_) { + file_opener_for_upload_.reset(); + // This corresponds to the LogBlockedBy call made just before creating + // FileOpenerForUpload. + url_request_->LogUnblocked(); + } + if (!file_open_result.has_value()) { // Defer calling NotifyCompleted to make sure the URLLoader finishes // initializing before getting deleted. base::SequencedTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, base::BindOnce(&URLLoader::NotifyCompleted, - weak_ptr_factory_.GetWeakPtr(), error_code)); + weak_ptr_factory_.GetWeakPtr(), + file_open_result.error())); return; } scoped_refptr<base::SequencedTaskRunner> task_runner = base::ThreadPool::CreateSequencedTaskRunner( {base::MayBlock(), base::TaskPriority::USER_VISIBLE}); url_request_->set_upload(url_loader_util::CreateUploadDataStream( - request.request_body.get(), opened_files, task_runner.get())); + request.request_body.get(), file_open_result.value(), task_runner.get())); if (request.enable_upload_progress) { upload_progress_tracker_ = std::make_unique<UploadProgressTracker>(
diff --git a/services/network/url_loader.h b/services/network/url_loader.h index 1619d6f..9c62cf1 100644 --- a/services/network/url_loader.h +++ b/services/network/url_loader.h
@@ -99,8 +99,7 @@ } // namespace internal -constexpr size_t kMaxFileUploadRequestsPerBatch = 64; - +class FileOpenerForUpload; class KeepaliveStatisticsRecorder; class NetToMojoPendingBuffer; class ScopedThrottlingToken; @@ -327,9 +326,6 @@ const raw_ptr<URLLoader> pointer_; }; - class FileOpenerForUpload; - friend class FileOpenerForUpload; - // An enum class representing the result of keepalive requests. This is used // for UMA so do NOT re-assign values. enum class KeepaliveRequestResult { @@ -347,8 +343,7 @@ void OpenFilesForUpload(const ResourceRequest& request); void SetUpUpload(const ResourceRequest& request, - int error_code, - const std::vector<base::File> opened_files); + base::expected<std::vector<base::File>, net::Error>); // A continuation of `OnConnected` to process the result of the asynchronous // Local Network Access permission check. @@ -708,6 +703,8 @@ mojo::Remote<mojom::TrustedHeaderClient> header_client_; + // Handles asynchronously opening files for upload. Holds a reference to the + // request's URL (from `url_request_`), so `url_request_` must outlive this. std::unique_ptr<FileOpenerForUpload> file_opener_for_upload_; // If the request is configured for Trust Tokens
diff --git a/services/network/url_loader_unittest.cc b/services/network/url_loader_unittest.cc index aa039ba..6d90331 100644 --- a/services/network/url_loader_unittest.cc +++ b/services/network/url_loader_unittest.cc
@@ -99,6 +99,7 @@ #include "net/url_request/url_request_job.h" #include "net/url_request/url_request_test_job.h" #include "net/url_request/url_request_test_util.h" +#include "services/network/file_opener_for_upload.h" #include "services/network/public/cpp/cors/origin_access_list.h" #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/ip_address_space_util.h" @@ -3155,7 +3156,7 @@ base::FilePath file_path = GetTestFilePath("simple_page.html"); std::string expected_body; - size_t num_files = 2 * kMaxFileUploadRequestsPerBatch; + size_t num_files = 2 * FileOpenerForUpload::kMaxFileUploadRequestsPerBatch; for (size_t i = 0; i < num_files; ++i) { std::string tmp_expected_body; ASSERT_TRUE(base::ReadFileToString(file_path, &tmp_expected_body)) @@ -3182,7 +3183,7 @@ base::FilePath file_path = GetTestFilePath("simple_page.html"); scoped_refptr<ResourceRequestBody> request_body(new ResourceRequestBody()); - size_t num_files = 2 * kMaxFileUploadRequestsPerBatch; + size_t num_files = 2 * FileOpenerForUpload::kMaxFileUploadRequestsPerBatch; for (size_t i = 0; i < num_files; ++i) { request_body->AppendFileRange( file_path, 0, std::numeric_limits<uint64_t>::max(), base::Time()); @@ -3199,7 +3200,7 @@ base::FilePath file_path = GetTestFilePath("simple_page.html"); scoped_refptr<ResourceRequestBody> request_body(new ResourceRequestBody()); - size_t num_files = 2 * kMaxFileUploadRequestsPerBatch; + size_t num_files = 2 * FileOpenerForUpload::kMaxFileUploadRequestsPerBatch; for (size_t i = 0; i < num_files; ++i) { request_body->AppendFileRange( file_path, 0, std::numeric_limits<uint64_t>::max(), base::Time());
diff --git a/services/on_device_model/public/cpp/model_assets.h b/services/on_device_model/public/cpp/model_assets.h index bf6689c..4ef85fb4 100644 --- a/services/on_device_model/public/cpp/model_assets.h +++ b/services/on_device_model/public/cpp/model_assets.h
@@ -46,6 +46,10 @@ AdaptationAssetPaths(const AdaptationAssetPaths&); ~AdaptationAssetPaths(); + bool operator==(const AdaptationAssetPaths& other) const { + return weights == other.weights; + } + base::FilePath weights; };
diff --git a/testing/buildbot/filters/android.emulator_15_tablet.chrome_public_test_apk.filter b/testing/buildbot/filters/android.emulator_15_tablet.chrome_public_test_apk.filter index 8bb7337..0c95edf 100644 --- a/testing/buildbot/filters/android.emulator_15_tablet.chrome_public_test_apk.filter +++ b/testing/buildbot/filters/android.emulator_15_tablet.chrome_public_test_apk.filter
@@ -49,4 +49,7 @@ # crbug.com/401287512 -org.chromium.chrome.browser.contextualsearch.ContextualSearchInstrumentationTest.testNonResolveGesture --org.chromium.chrome.browser.contextualsearch.ContextualSearchInstrumentationTest.testResolveGesture \ No newline at end of file +-org.chromium.chrome.browser.contextualsearch.ContextualSearchInstrumentationTest.testResolveGesture + +# crbug.com/409145689 +-org.chromium.chrome.browser.settings.MainSettingsFragmentTest.testPlusAddressesEnabled \ No newline at end of file
diff --git a/testing/buildbot/filters/android.emulator_15_tablet_landscape.chrome_public_test_apk.filter b/testing/buildbot/filters/android.emulator_15_tablet_landscape.chrome_public_test_apk.filter index 7a69227..94e1ec8 100644 --- a/testing/buildbot/filters/android.emulator_15_tablet_landscape.chrome_public_test_apk.filter +++ b/testing/buildbot/filters/android.emulator_15_tablet_landscape.chrome_public_test_apk.filter
@@ -52,4 +52,7 @@ # crbug.com/408282366 -org.chromium.chrome.browser.download.DownloadTest.testUrlEscaping -org.chromium.chrome.browser.download.DownloadTest.testHttpPostDownload --org.chromium.chrome.browser.download.DownloadTest.testHttpGetDownload \ No newline at end of file +-org.chromium.chrome.browser.download.DownloadTest.testHttpGetDownload + +# crbug.com/409145689 +-org.chromium.chrome.browser.settings.MainSettingsFragmentTest.testPlusAddressesEnabled \ No newline at end of file
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py index c98f8c8..4c388b3 100755 --- a/testing/scripts/run_performance_tests.py +++ b/testing/scripts/run_performance_tests.py
@@ -732,7 +732,9 @@ 'speedometer_3.1': 'third_party/speedometer/v3.1', 'speedometer_3.0': 'third_party/speedometer/v3.0', 'speedometer_2.1': 'third_party/speedometer/v2.1', - 'speedometer_2.0': 'third_party/speedometer/v2.0' + 'speedometer_2.0': 'third_party/speedometer/v2.0', + 'jetstream_2.2': 'third_party/jetstream/v2.2', + 'motionmark_1.3': 'third_party/blink/perf_tests/MotionMark' } def __init__(self, options, isolated_out_dir):
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 58ae1d3b..c05f3d7 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -8128,6 +8128,7 @@ { "platforms": [ "android", + "android_webview", "chromeos", "fuchsia", "linux", @@ -14189,6 +14190,21 @@ ] } ], + "Lobster": [ + { + "platforms": [ + "chromeos" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "Lobster" + ] + } + ] + } + ], "LobsterDogfood": [ { "platforms": [ @@ -14198,9 +14214,22 @@ { "name": "Enabled_Dogfood", "enable_features": [ - "LobsterDogfood", - "LobsterQuickInsertZeroState", - "LobsterRightClickMenu" + "LobsterUseRewrittenQuery" + ] + } + ] + } + ], + "LobsterQueryRewrittenDogfood": [ + { + "platforms": [ + "chromeos" + ], + "experiments": [ + { + "name": "Enabled_Dogfood", + "enable_features": [ + "LobsterDogfood" ] } ]
diff --git a/third_party/androidx/build.gradle b/third_party/androidx/build.gradle index 30be3d01..a15cb44e 100644 --- a/third_party/androidx/build.gradle +++ b/third_party/androidx/build.gradle
@@ -296,7 +296,7 @@ google() maven { // This URL is generated by the fetch_all_androidx.py script. - url 'https://androidx.dev/snapshots/builds/13321477/artifacts/repository' + url 'https://androidx.dev/snapshots/builds/13323247/artifacts/repository' } mavenCentral() }
diff --git a/third_party/angle b/third_party/angle index 0e28c030..6e992fa 160000 --- a/third_party/angle +++ b/third_party/angle
@@ -1 +1 @@ -Subproject commit 0e28c030a2625e3022a9adeec9746519af4217a2 +Subproject commit 6e992fa20b4bbcc5c7892ce085d95b0549c9cfa8
diff --git a/third_party/blink/public/mojom/lcp_critical_path_predictor/lcp_critical_path_predictor.mojom b/third_party/blink/public/mojom/lcp_critical_path_predictor/lcp_critical_path_predictor.mojom index ad83981..07a2a64 100644 --- a/third_party/blink/public/mojom/lcp_critical_path_predictor/lcp_critical_path_predictor.mojom +++ b/third_party/blink/public/mojom/lcp_critical_path_predictor/lcp_critical_path_predictor.mojom
@@ -9,16 +9,30 @@ import "services/network/public/mojom/fetch_api.mojom"; import "url/mojom/url.mojom"; +struct LcpElement { + // LCP ElementLocator information serialized in proto3 binary format. + mojo_base.mojom.ByteString? locator; + // Indicates if the element is an image element. + bool is_image; + // Predicted index of `locator` in + // LCPCriticalPathPredictorNavigationTimeHint. + uint32? predicted_index; +}; + // Interface for LCP Critical Path Predictor from the renderer process. +// Design doc(Google internal) +// https://docs.google.com/document/d/1V5PFwVTDonJ1RE52ZI5WGRihn07atHLApnIWsZ3fgfM/ interface LCPCriticalPathPredictorHost { - // `lcp_element_locator` is a LCP ElementLocator information - // serialized in proto3 binary format. - // `is_image_element` indicates if the element is an image element. - // `predicted_lcp_index` is predicted index of `lcp_element_locators` in - // LCPCriticalPathPredictorNavigationTimeHint. - OnLcpUpdated(mojo_base.mojom.ByteString? lcp_element_locator, - bool is_image_element, - uint32? predicted_lcp_index); + // Called when an LCP-element candidate is updated. (For each page load, + // there can be multiple LCP-element candidates, and the LCP element will be + // determined after the page load finishes. + OnLcpUpdated(LcpElement lcp_element); + + // Only for testing. Called when LCP timing is predicted or fallbacked when + // prediction is failed. + // `element_locator` is the LCP element's locator or null when this is + // fallbacked. + OnLcpTimingPredictedForTesting(mojo_base.mojom.ByteString? element_locator); // `lcp_influencer_scripts` contain the list of script URLs that affected // the LCP element. @@ -68,4 +82,7 @@ // Unused preloaded URLs in the past loads. array<url.mojom.Url> unused_preloads; + + // True to enable some debugging operations. + bool for_testing; };
diff --git a/third_party/blink/renderer/core/css/css_math_function_value.cc b/third_party/blink/renderer/core/css/css_math_function_value.cc index b308b048..d7122edc 100644 --- a/third_party/blink/renderer/core/css/css_math_function_value.cc +++ b/third_party/blink/renderer/core/css/css_math_function_value.cc
@@ -70,11 +70,6 @@ return ClampToPermittedRange(expression_->DoubleValue()); } -double CSSMathFunctionValue::ComputeSeconds() const { - DCHECK_EQ(kCalcTime, expression_->Category()); - return ClampToPermittedRange(*expression_->ComputeValueInCanonicalUnit()); -} - double CSSMathFunctionValue::ComputeDegrees() const { DCHECK_EQ(kCalcAngle, expression_->Category()); return ClampToPermittedRange(*expression_->ComputeValueInCanonicalUnit());
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 56dbbd4..49935553 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
@@ -76,7 +76,6 @@ // between px and em). Otherwise, it hits a DCHECK. double DoubleValue() const; - double ComputeSeconds() const; double ComputeSeconds(const CSSLengthResolver&) const; double ComputeDegrees() const; double ComputeDegrees(const CSSLengthResolver&) const;
diff --git a/third_party/blink/renderer/core/css/css_primitive_value.cc b/third_party/blink/renderer/core/css/css_primitive_value.cc index bb6f2d2..b50f5a7 100644 --- a/third_party/blink/renderer/core/css/css_primitive_value.cc +++ b/third_party/blink/renderer/core/css/css_primitive_value.cc
@@ -279,13 +279,6 @@ // TODO(crbug.com/1133390): When we support <frequency>, we must clamp like // <time>. -double CSSPrimitiveValue::ComputeSeconds() const { - double result = IsCalculated() - ? To<CSSMathFunctionValue>(this)->ComputeSeconds() - : To<CSSNumericLiteralValue>(this)->ComputeSeconds(); - return CSSValueClampingUtils::ClampTime(result); -} - double CSSPrimitiveValue::ComputeDegrees() const { double result = IsCalculated() ? To<CSSMathFunctionValue>(this)->ComputeDegrees()
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 90daf53..c9d5a5f3 100644 --- a/third_party/blink/renderer/core/css/css_primitive_value.h +++ b/third_party/blink/renderer/core/css/css_primitive_value.h
@@ -378,7 +378,6 @@ static CSSPrimitiveValue* CreateFromLength(const Length& value, float zoom); double ComputeDegrees() const; - double ComputeSeconds() const; double ComputeDotsPerPixel() const; double ComputeDegrees(const CSSLengthResolver&) const;
diff --git a/third_party/blink/renderer/core/css/css_primitive_value_test.cc b/third_party/blink/renderer/core/css/css_primitive_value_test.cc index 794a6178..8d8718a 100644 --- a/third_party/blink/renderer/core/css/css_primitive_value_test.cc +++ b/third_party/blink/renderer/core/css/css_primitive_value_test.cc
@@ -103,7 +103,8 @@ TEST_F(CSSPrimitiveValueTest, ClampTimeToNonNegative) { UnitValue a = {4926, UnitType::kMilliseconds}; UnitValue b = {5, UnitType::kSeconds}; - EXPECT_EQ(0.0, CreateNonNegativeSubtraction(a, b)->ComputeSeconds()); + EXPECT_EQ(0.0, CreateNonNegativeSubtraction(a, b)->ComputeSeconds( + CSSToLengthConversionData(/*element=*/nullptr))); } TEST_F(CSSPrimitiveValueTest, ClampAngleToNonNegative) {
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc index f9570c8..f0ba20d 100644 --- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc +++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
@@ -3540,7 +3540,8 @@ double StyleBuilderConverter::ConvertTimeValue(const StyleResolverState& state, const CSSValue& value) { - return To<CSSPrimitiveValue>(value).ComputeSeconds(); + return To<CSSPrimitiveValue>(value).ComputeSeconds( + state.CssToLengthConversionData()); } std::optional<StyleOverflowClipMargin>
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc index 84ce7a81..d59385c 100644 --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -3136,8 +3136,6 @@ } void LocalFrame::SetEvictCachedSessionStorageOnFreezeOrUnload() { - DCHECK(RuntimeEnabledFeatures::Prerender2Enabled( - GetDocument()->GetExecutionContext())); evict_cached_session_storage_on_freeze_or_unload_ = true; }
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc index 6377ce1f..1123ea5 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -94,6 +94,7 @@ #include <utility> #include <vector> +#include "base/check_is_test.h" #include "base/compiler_specific.h" #include "base/notreached.h" #include "base/numerics/safe_conversions.h" @@ -3394,6 +3395,11 @@ unused_preloads.emplace_back(url); } lcpp->set_unused_preloads(std::move(unused_preloads)); + + if (hint->for_testing) { + CHECK_IS_TEST(); + lcpp->enable_testing(); + } } bool WebLocalFrameImpl::IsFeatureEnabled(
diff --git a/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.cc b/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.cc index f7de40c7..e533cc8 100644 --- a/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.cc +++ b/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.cc
@@ -100,6 +100,10 @@ unused_preloads_ = std::move(preloads); } +void LCPCriticalPathPredictor::enable_testing() { + report_timing_predictor_for_testing_ = true; +} + void LCPCriticalPathPredictor::Reset() { lcp_element_locators_.clear(); lcp_element_locator_strings_.clear(); @@ -113,6 +117,8 @@ has_lcp_occurred_ = false; is_outermost_main_frame_document_loaded_ = false; has_sent_unused_preloads_ = false; + + report_timing_predictor_for_testing_ = false; } void LCPCriticalPathPredictor::AddLCPPredictedCallback(LCPCallback callback) { @@ -145,6 +151,15 @@ for (auto& callback : callbacks) { std::move(callback).Run(lcp_element); } + + if (report_timing_predictor_for_testing_) { + const std::optional<std::string> lcp_element_locator_string = + lcp_element + ? std::optional<std::string>( + element_locator::OfElement(*lcp_element).SerializeAsString()) + : std::nullopt; + GetHost().OnLcpTimingPredictedForTesting(lcp_element_locator_string); + } } bool LCPCriticalPathPredictor::IsElementMatchingLocator( @@ -195,13 +210,13 @@ is_recordable_type && (lcp_element_locator_string.size() <= features::kLCPCriticalPathPredictorMaxElementLocatorLength.Get()); - GetHost().OnLcpUpdated( + GetHost().OnLcpUpdated(mojom::blink::LcpElement::New( is_recordable ? std::optional<std::string>(lcp_element_locator_string) : std::nullopt, is_image_element, predicted_lcp_index == kNotFound ? std::nullopt - : std::optional<wtf_size_t>(predicted_lcp_index)); + : std::optional<wtf_size_t>(predicted_lcp_index))); } if (base::FeatureList::IsEnabled(features::kLCPPAutoPreconnectLcpOrigin)) {
diff --git a/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.h b/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.h index 734d1347..759ac49 100644 --- a/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.h +++ b/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.h
@@ -67,6 +67,8 @@ const Vector<KURL>& unused_preloads() { return unused_preloads_; } + void enable_testing(); + void Reset(); bool IsLcpInfluencerScript(const KURL& url); @@ -112,6 +114,8 @@ bool has_lcp_occurred_ = false; bool is_outermost_main_frame_document_loaded_ = false; bool has_sent_unused_preloads_ = false; + + bool report_timing_predictor_for_testing_ = false; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc index 64fbe4f..d854764 100644 --- a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc +++ b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
@@ -701,19 +701,16 @@ rule_set->prefetch_with_subresources_rules()); } - // If kPrerender2 is enabled, collect all prerender speculation rules. - if (RuntimeEnabledFeatures::Prerender2Enabled(execution_context)) { - push_candidates(mojom::blink::SpeculationAction::kPrerender, rule_set, - rule_set->prerender_rules()); + push_candidates(mojom::blink::SpeculationAction::kPrerender, rule_set, + rule_set->prerender_rules()); - // Set the flag to evict the cached data of Session Storage when the - // document is frozen or unload to avoid reusing old data in the cache - // after the session storage has been modified by another renderer - // process. See crbug.com/1215680 for more details. - LocalFrame* frame = document.GetFrame(); - if (frame && frame->IsMainFrame()) { - frame->SetEvictCachedSessionStorageOnFreezeOrUnload(); - } + // Set the flag to evict the cached data of Session Storage when the + // document is frozen or unload to avoid reusing old data in the cache + // after the session storage has been modified by another renderer process. + // See crbug.com/1215680 for more details. + LocalFrame* frame = document.GetFrame(); + if (frame && frame->IsMainFrame()) { + frame->SetEvictCachedSessionStorageOnFreezeOrUnload(); } } @@ -873,10 +870,8 @@ rule_set, rule_set->prefetch_with_subresources_rules()); } - if (RuntimeEnabledFeatures::Prerender2Enabled(execution_context)) { - push_link_candidates(mojom::blink::SpeculationAction::kPrerender, - rule_set, rule_set->prerender_rules()); - } + push_link_candidates(mojom::blink::SpeculationAction::kPrerender, + rule_set, rule_set->prerender_rules()); } if (!link_candidates->empty()) {
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc index 567e24da..c109a46e 100644 --- a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc +++ b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set_test.cc
@@ -236,7 +236,6 @@ } private: - ScopedPrerender2ForTest enable_prerender2_{true}; test::TaskEnvironment task_environment_; Persistent<ExecutionContext> execution_context_; }; @@ -1024,33 +1023,6 @@ })); } -// Tests that prerender rules are ignored unless Prerender2 is enabled. -TEST_F(SpeculationRuleSetTest, PrefetchIgnorePrerenderRules) { - // Overwrite the kPrerender2 flag. - ScopedPrerender2ForTest enable_prerender{false}; - - DummyPageHolder page_holder; - StubSpeculationHost speculation_host; - const String speculation_script = - R"({"prefetch": [ - {"source": "list", - "urls": ["https://example.com/foo", "https://example.com/bar"], - "requires": ["anonymous-client-ip-when-cross-origin"]} - ], - "prerender": [ - {"source": "list", "urls": ["https://example.com/prerender"]} - ] - })"; - PropagateRulesToStubSpeculationHost(page_holder, speculation_host, - speculation_script); - - const auto& candidates = speculation_host.candidates(); - EXPECT_EQ(candidates.size(), 2u); - EXPECT_FALSE(std::ranges::any_of(candidates, [](const auto& candidate) { - return candidate->action == mojom::blink::SpeculationAction::kPrerender; - })); -} - // Tests that the presence of a speculationrules script is recorded. TEST_F(SpeculationRuleSetTest, UseCounter) { DummyPageHolder page_holder;
diff --git a/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent_unittest.cc b/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent_unittest.cc index 48f0fa7..ef0799fa 100644 --- a/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent_unittest.cc +++ b/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent_unittest.cc
@@ -2437,10 +2437,15 @@ TEST_F(AIPageContentAgentTest, PaidContent) { frame_test_helpers::LoadHTMLString( helper_.LocalMainFrame(), R"HTML( + <head> + <script></script> + <script type='unrelated'></script> + <script type="application/ld+json">{this: "will fail parsing",}</script> + <script type="application/ld+json">"not": "an object"</script> <script type="application/ld+json">{ "@context": "http://schema.org", "@type": "NewsArticle", - "mainEntityOfPage": "https://www.evergreengazette.com/dailyplanet.com/world/world-news/", + "mainEntityOfPage": "https://www.evergreengazette.com/world/world-news/", "headline": "City Council Debates Future of Automated Transit System", "alternativeHeadline": "City Council Debates Future of Automated Transit System", "dateModified": "2025-03-25T19:17:05.541Z", @@ -2567,7 +2572,10 @@ <script type="application/ld+json">{ "@context": "http://schema.org", "@type": "NewsArticle", - "isAccessibleForFree": "False" + "isAccessibleForFree": "False", + "hasPart": { + "@type": "unrelated" + } }</script> <body> Content @@ -2598,7 +2606,7 @@ mojom::blink::AIPageContentAnnotatedRole::kPaidContent)); } -TEST_F(AIPageContentAgentTest, DISABLED_PaidContentMicrodata) { +TEST_F(AIPageContentAgentTest, PaidContentMicrodata) { frame_test_helpers::LoadHTMLString( helper_.LocalMainFrame(), R"HTML( <script type="application/ld+json">{ @@ -2609,8 +2617,12 @@ <body> Content <div class="paidContent"> - <meta itemprop="isAccessibleForFree" content="false"> - Paid Content + <meta itemprop="isAccessibleForFree" content="false"> + Paid Content + </div> + <div class="paidContent"> + <meta itemprop="unrelated"> + Content </div> </body> )HTML", @@ -2796,6 +2808,119 @@ mojom::blink::AIPageContentAnnotatedRole::kPaidContent)); } +TEST_F(AIPageContentAgentTest, PaidContentSubframeMicrodata) { + frame_test_helpers::LoadHTMLString( + helper_.LocalMainFrame(), R"HTML( + <script type="application/ld+json">{ + "@context": "https://schema.org", + "@type": "NewsArticle", + "isAccessibleForFree": true + }</script> + <body> + Free Content + <div class="paidContent"> + <meta itemprop="isAccessibleForFree" content="false"> + Microdata not checked + </div> + <iframe srcdoc=' + <script type="application/ld+json">{ + "@context": "http://schema.org", + "@type": "NewsArticle", + "isAccessibleForFree": false + }</script> + <body> + Content + <div class="paidContent"> + <meta itemprop="isAccessibleForFree" content="false"> + Paid Content + </div> + </body> + '></iframe> + <iframe srcdoc=' + <body> + Content + <div class="paidContent"> + <meta itemprop="isAccessibleForFree" content="false"> + Microdata not checked + </div> + </body> + '></iframe> + <iframe srcdoc=' + <script type="application/ld+json">{ + "@context": "http://schema.org", + "@type": "NewsArticle", + "isAccessibleForFree": false + }</script> + <body> + Content + <div class="paidContent"> + <meta itemprop="isAccessibleForFree" content="false"> + Paid Content + </div> + </body> + '></iframe> + </body> + )HTML", + url_test_helpers::ToKURL("http://foobar.com")); + + auto content = GetAIPageContent(); + ASSERT_TRUE(content); + ASSERT_TRUE(content->root_node); + + // The root node does not contain paid content. + EXPECT_FALSE(content->frame_data->contains_paid_content); + + const auto& root = *content->root_node; + auto& nodes = root.children_nodes; + + EXPECT_FALSE(ContainsRole(nodes[0]->content_attributes->annotated_roles, + mojom::blink::AIPageContentAnnotatedRole::kPaidContent)); + EXPECT_FALSE(ContainsRole(nodes[1]->content_attributes->annotated_roles, + mojom::blink::AIPageContentAnnotatedRole::kPaidContent)); + + const auto& iframe1 = nodes[2]; + EXPECT_EQ(iframe1->content_attributes->attribute_type, + mojom::blink::AIPageContentAttributeType::kIframe); + EXPECT_TRUE(iframe1->content_attributes->iframe_data->local_frame_data + ->contains_paid_content); + + const auto& children1 = iframe1->children_nodes[0]->children_nodes; + EXPECT_FALSE( + ContainsRole(children1[0]->content_attributes->annotated_roles, + mojom::blink::AIPageContentAnnotatedRole::kPaidContent)); + EXPECT_TRUE( + ContainsRole(children1[1]->content_attributes->annotated_roles, + mojom::blink::AIPageContentAnnotatedRole::kPaidContent)); + + const auto& iframe2 = nodes[3]; + EXPECT_EQ(iframe2->content_attributes->attribute_type, + mojom::blink::AIPageContentAttributeType::kIframe); + EXPECT_FALSE(iframe2->content_attributes->iframe_data->local_frame_data + ->contains_paid_content); + + const auto& children2 = iframe2->children_nodes[0]->children_nodes; + EXPECT_FALSE( + ContainsRole(children2[0]->content_attributes->annotated_roles, + mojom::blink::AIPageContentAnnotatedRole::kPaidContent)); + EXPECT_FALSE( + ContainsRole(children2[1]->content_attributes->annotated_roles, + mojom::blink::AIPageContentAnnotatedRole::kPaidContent)); + + const auto& iframe3 = nodes[4]; + EXPECT_EQ(iframe3->content_attributes->attribute_type, + mojom::blink::AIPageContentAttributeType::kIframe); + EXPECT_TRUE(iframe3->content_attributes->iframe_data->local_frame_data + ->contains_paid_content); + + const auto& children3 = iframe3->children_nodes[0]->children_nodes; + EXPECT_FALSE( + ContainsRole(children3[0]->content_attributes->annotated_roles, + mojom::blink::AIPageContentAnnotatedRole::kPaidContent)); + EXPECT_TRUE( + ContainsRole(children3[1]->content_attributes->annotated_roles, + mojom::blink::AIPageContentAnnotatedRole::kPaidContent)); +} + void CheckMatchesNode( const mojom::blink::AIPageContentHitTestNode& hit_test_node, const mojom::blink::AIPageContentNode& node) {
diff --git a/third_party/blink/renderer/modules/content_extraction/paid_content.cc b/third_party/blink/renderer/modules/content_extraction/paid_content.cc index e78b0b3f..175ab03 100644 --- a/third_party/blink/renderer/modules/content_extraction/paid_content.cc +++ b/third_party/blink/renderer/modules/content_extraction/paid_content.cc
@@ -8,6 +8,7 @@ #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/static_node_list.h" +#include "third_party/blink/renderer/core/html/html_meta_element.h" #include "third_party/blink/renderer/core/html/html_head_element.h" #include "third_party/blink/renderer/core/html/html_script_element.h" #include "third_party/blink/renderer/platform/json/json_parser.h" @@ -16,6 +17,9 @@ namespace blink { namespace { + +const char kIsAccessibleForFree[] = "isAccessibleForFree"; + bool ObjectValuePresentAndEquals(const JSONObject* object, const String& key, const String& value) { @@ -70,6 +74,17 @@ } // namespace bool PaidContent::IsPaidElement(const Element* element) const { + auto* document = &element->GetDocument(); + if (check_microdata_.Contains(document) && check_microdata_.at(document)) { + for (HTMLMetaElement& meta_element : + Traversal<HTMLMetaElement>::ChildrenOf(*element)) { + auto itemprop = meta_element.FastGetAttribute(html_names::kItempropAttr); + if (itemprop.GetString() != kIsAccessibleForFree) { + continue; + } + return meta_element.Content() == "false"; + } + } for (const auto& paid_element : paid_elements_) { if (element == paid_element) { return true; @@ -115,11 +130,13 @@ // and WebPage. Multiple types are supported. // check for isAccessibleForFree=false - if (!ObjectValuePresentAndFalse(script_obj, "isAccessibleForFree")) { + if (!ObjectValuePresentAndFalse(script_obj, kIsAccessibleForFree)) { continue; } paid_content_present = true; + bool has_part_found = false; + // Check for hasPart with isAccessibleForFree=false and a cssSelector JSONValue* hasPart_val = script_obj->Get("hasPart"); if (hasPart_val) { @@ -130,23 +147,30 @@ JSONValue* hasPart_obj_val = hasPart_array->at(j); if (hasPart_obj_val->GetType() == JSONValue::kTypeObject) { JSONObject* hasPart_obj = JSONObject::Cast(hasPart_obj_val); - AppendHasPartElements(document, *hasPart_obj); + has_part_found |= AppendHasPartElements(document, *hasPart_obj); } } } else if (hasPart_type == JSONValue::kTypeObject) { JSONObject* hasPart_obj = JSONObject::Cast(hasPart_val); - AppendHasPartElements(document, *hasPart_obj); + has_part_found |= AppendHasPartElements(document, *hasPart_obj); } } + + // Assume that pages will only use either ld+json or microdata. + // If ld+json hasPart exists, don't check for microdata to save + // the cost of checking each element. + if (!has_part_found) { + check_microdata_.Set(&document, true); + } return paid_content_present; } return paid_content_present; } -void PaidContent::AppendHasPartElements(Document& document, +bool PaidContent::AppendHasPartElements(Document& document, JSONObject& hasPart_obj) { if (ObjectValuePresentAndEquals(&hasPart_obj, "@type", "WebPageElement") && - ObjectValuePresentAndFalse(&hasPart_obj, "isAccessibleForFree")) { + ObjectValuePresentAndFalse(&hasPart_obj, kIsAccessibleForFree)) { JSONValue* selector_val = hasPart_obj.Get("cssSelector"); if (selector_val && selector_val->GetType() == JSONValue::kTypeString) { String selector; @@ -158,8 +182,10 @@ paid_elements_.push_back(elements->item(j)); } } + return true; } } + return false; } } // namespace blink
diff --git a/third_party/blink/renderer/modules/content_extraction/paid_content.h b/third_party/blink/renderer/modules/content_extraction/paid_content.h index aef1a49..960685d 100644 --- a/third_party/blink/renderer/modules/content_extraction/paid_content.h +++ b/third_party/blink/renderer/modules/content_extraction/paid_content.h
@@ -25,8 +25,11 @@ bool IsPaidElement(const Element* element) const; private: + // Whether to check for microdata annotations while walking. + HeapHashMap<WeakMember<Document>, bool> check_microdata_; + // Appends elements found by the cssSelector in the hasPart object. - void AppendHasPartElements(Document& document, JSONObject& hasPart_obj); + bool AppendHasPartElements(Document& document, JSONObject& hasPart_obj); // List of nodes marked as isAccessibleForFree=false. HeapVector<Member<Element>> paid_elements_;
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc index b253080..94e73459 100644 --- a/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc +++ b/third_party/blink/renderer/platform/graphics/video_frame_resource_provider.cc
@@ -114,6 +114,12 @@ if (media_transform.mirrored) { transform.RotateAboutYAxis(180.0); transform.Translate(-quad_rect.width(), 0); + + if (media_transform.rotation == media::VIDEO_ROTATION_90 || + media_transform.rotation == media::VIDEO_ROTATION_270) { + transform.RotateAboutZAxis(180.0); + transform.Translate(-quad_rect.width(), -quad_rect.height()); + } } gfx::Rect visible_quad_rect = quad_rect;
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 9e4b2133a..cb6559a 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -9072,6 +9072,9 @@ # Gardener 2025-04-02 crbug.com/407865639 external/wpt/css/css-grid/grid-extrinsically-sized-mutations.html [ Failure Pass ] +# Gardener 2025-04-07 +crbug.com/409121557 [ Release Win11-arm64 ] http/tests/inspector-protocol/fedcm/fedcm-open-url.js [ Failure Pass ] + # Recent WebNN test failures on Mac14 which need to be investigated: crbug.com/407486638 [ Mac14 ] virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru.https.any.html?npu [ Failure Pass ] crbug.com/407486638 [ Mac14-arm64 ] virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/gru.https.any.html?npu [ Failure Pass ] @@ -9093,3 +9096,6 @@ crbug.com/407477500 [ Mac14-arm64 ] virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/reduce_product.https.any.html?gpu [ Failure Pass ] crbug.com/407478938 [ Mac14 ] virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/transpose.https.any.html?gpu [ Failure Pass ] crbug.com/407478938 [ Mac14-arm64 ] virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/transpose.https.any.html?gpu [ Failure Pass ] + +# Gardener 2025-04-08 +crbug.com/409149439 [ Win11-arm64 ] external/wpt/partitioned-popins/partitioned-popins.partitions.tentative.https.window.html?include=variant-5-test [ Failure Pass ]
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 63e9570..0e87e0ba 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
@@ -174859,6 +174859,45 @@ {} ] ], + "scroll-marker-contain-009.tentative.html": [ + "8037adf8441168ec87f118c1da3a6a7bede7ef77", + [ + null, + [ + [ + "/css/css-overflow/scroll-marker-contain-009-ref.tentative.html", + "==" + ] + ], + {} + ] + ], + "scroll-marker-contain-010.tentative.html": [ + "fbdad62f84665ea55b8c7d647b631b0f215f1e19", + [ + null, + [ + [ + "/css/css-overflow/scroll-marker-contain-009-ref.tentative.html", + "==" + ] + ], + {} + ] + ], + "scroll-marker-contain-011.tentative.html": [ + "8cfc78130e0f93ddf3a768b0b582f292d9c4291c", + [ + null, + [ + [ + "/css/css-overflow/scroll-marker-contain-009-ref.tentative.html", + "==" + ] + ], + {} + ] + ], "scroll-marker-counters.html": [ "25e4fdfd24f849128c711cdc5a8610abba5e1d8f", [ @@ -355146,6 +355185,10 @@ "d74777d3bbf0b230048a17320afa6040a6b8eb62", [] ], + "scroll-marker-contain-009-ref.tentative.html": [ + "07b797ff789a382d91ee3207ba0172009bef0c77", + [] + ], "scroll-marker-counters-ref.html": [ "7113a5c5d23a97cf83f173a72ef96b57ecf09263", [] @@ -382009,7 +382052,7 @@ [] ], "trust-token-redemption-supported-by-feature-policy.tentative-expected.txt": [ - "352385b00454210b80dcfb1878693f591196aad0", + "3afa9fcfc2e7a663d654060cdb63bebfd1eea074", [] ], "vertical-scroll-main-frame-manual.tentative.html.headers": [ @@ -393239,7 +393282,7 @@ [] ], "text.yaml": [ - "900431860e27fa1c41276cc43bc961ea3da45270", + "0a8e54135775874c4f390101dce100bfab6716f1", [] ], "the-canvas-state.yaml": [ @@ -519608,6 +519651,13 @@ {} ] ], + "3d-point-mapping-overlapping.html": [ + "ffcfc1ae042697fc083ceb108182ba09086b4593", + [ + null, + {} + ] + ], "3d-rendering-context-behavior.html": [ "94e476c3426b53c96b6b1a7ebb70d7f0586e8313", [ @@ -625873,7 +625923,7 @@ ] ], "2d.text.measure.selection-rects-baselines.tentative.worker.js": [ - "bdd015eef827a23bafccb9b95d9c595d2654d5ef", + "d270e1dcfd81b6ca55a919a3e2fea21ebc53d981", [ "html/canvas/offscreen/text/2d.text.measure.selection-rects-baselines.tentative.worker.html", {}
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/text/2d.text.measure.selection-rects-baselines.tentative.worker.js b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/text/2d.text.measure.selection-rects-baselines.tentative.worker.js index bdd015e..d270e1dc 100644 --- a/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/text/2d.text.measure.selection-rects-baselines.tentative.worker.js +++ b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/text/2d.text.measure.selection-rects-baselines.tentative.worker.js
@@ -32,8 +32,8 @@ '🏁🎶🏁', ')(あ)(', '-abcd_', - 'اين المكتبة؟', - 'bidiالرياضيات' + 'איפה הספרייה?', + 'bidiמתמטיקה' ] for (const text of kTexts) {
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/text.yaml b/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/text.yaml index 9004318..0a8e541 100644 --- a/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/text.yaml +++ b/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/text.yaml
@@ -1408,8 +1408,8 @@ '🏁🎶🏁', ')(あ)(', '-abcd_', - 'اين المكتبة؟', - 'bidiالرياضيات' + 'איפה הספרייה?', + 'bidiמתמטיקה' ] for (text of kTexts) { @@ -1489,8 +1489,8 @@ '🏁🎶🏁', ')(あ)(', '-abcd_', - 'اين المكتبة؟', - 'bidiالرياضيات' + 'איפה הספרייה?', + 'bidiמתמטיקה' ] for (const text of kTexts) {
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-css-properties.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-css-properties.tentative.html index 8e918b0..0aa45a4 100644 --- a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-css-properties.tentative.html +++ b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-css-properties.tentative.html
@@ -25,6 +25,7 @@ test_valid_value(prop, '32s'); test_valid_value(prop, '123ms'); test_valid_value(prop, 'inherit'); + test_valid_value(prop, 'calc(2s * sibling-index())'); // Invalid values: test_invalid_value(prop, '0', '0s');
diff --git a/third_party/boringssl/src b/third_party/boringssl/src index d5440dd..ef839bf 160000 --- a/third_party/boringssl/src +++ b/third_party/boringssl/src
@@ -1 +1 @@ -Subproject commit d5440dd2c2c500ac2d3bba4afec47a054b4d99ae +Subproject commit ef839bf397fb4ecdb66ef2679a08ac7b3563c50b
diff --git a/third_party/catapult b/third_party/catapult index d451fed..afb0c8b 160000 --- a/third_party/catapult +++ b/third_party/catapult
@@ -1 +1 @@ -Subproject commit d451fed9573b5eb485dc364514d1ec95f20eadb0 +Subproject commit afb0c8b9082937de851ec953e07b0410db640b50
diff --git a/third_party/compiler-rt/src b/third_party/compiler-rt/src index 4727d6a..a0beb0c 160000 --- a/third_party/compiler-rt/src +++ b/third_party/compiler-rt/src
@@ -1 +1 @@ -Subproject commit 4727d6a1939e3f2af6affe74234f46930ef5321c +Subproject commit a0beb0c8bd142da77c77eca551a968f9ff0036db
diff --git a/third_party/crossbench b/third_party/crossbench index f5a24fd..7848090f 160000 --- a/third_party/crossbench +++ b/third_party/crossbench
@@ -1 +1 @@ -Subproject commit f5a24fd88bd5137b412f59477878d0782e19ff15 +Subproject commit 7848090f51d703417a9488f01ef7c10d2da8da2d
diff --git a/third_party/dawn b/third_party/dawn index 36be462..4cfedf0 160000 --- a/third_party/dawn +++ b/third_party/dawn
@@ -1 +1 @@ -Subproject commit 36be4620f07d464268815b4933e91db34c99fee6 +Subproject commit 4cfedf0f36940477134b3eff352cf396b74e8e3b
diff --git a/third_party/openscreen/src b/third_party/openscreen/src index ddc89a0..9c99d9f 160000 --- a/third_party/openscreen/src +++ b/third_party/openscreen/src
@@ -1 +1 @@ -Subproject commit ddc89a049295f3fa6ff01646c0a02eb747eba6af +Subproject commit 9c99d9f3c0b7029b25b07bed43fbc73901839dc6
diff --git a/third_party/pdfium b/third_party/pdfium index eeaea7e..a4b11d6 160000 --- a/third_party/pdfium +++ b/third_party/pdfium
@@ -1 +1 @@ -Subproject commit eeaea7e7fae274a49c3f4686eebc1abbadc4861a +Subproject commit a4b11d6beb5b3293c6e69e36afc928e738272dd6
diff --git a/third_party/protobuf/proto_library.gni b/third_party/protobuf/proto_library.gni index 3839fe4fd..57e6d228 100644 --- a/third_party/protobuf/proto_library.gni +++ b/third_party/protobuf/proto_library.gni
@@ -641,8 +641,7 @@ } } - public_deps += [ ":$action_name" ] - deps = [] + deps = [ ":$action_name" ] # This will link any libraries in the deps (the use of invoker.deps in the # action won't link it).
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps index 8ede18e..ddb9ba0 160000 --- a/third_party/vulkan-deps +++ b/third_party/vulkan-deps
@@ -1 +1 @@ -Subproject commit 8ede18e2d91dfd40c5e71c25f42761519142dd1a +Subproject commit ddb9ba0fdcc3193af3c60f811fa4524d54943890
diff --git a/third_party/vulkan-loader/src b/third_party/vulkan-loader/src index b8eb2b9..7b6539f 160000 --- a/third_party/vulkan-loader/src +++ b/third_party/vulkan-loader/src
@@ -1 +1 @@ -Subproject commit b8eb2b901835497b91db7bd7005f4d6ddba2bf1e +Subproject commit 7b6539f24633096c25631bab9fd572bd1ad9b27b
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src index 2d41063e..5cf5ba4 160000 --- a/third_party/vulkan-validation-layers/src +++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@ -Subproject commit 2d41063e90346d2fceca5f40b90e43a7a3368f7b +Subproject commit 5cf5ba49fd12c048e4192b150eb7528253a887ba
diff --git a/third_party/webgpu-cts/src b/third_party/webgpu-cts/src index de07a42..75af759 160000 --- a/third_party/webgpu-cts/src +++ b/third_party/webgpu-cts/src
@@ -1 +1 @@ -Subproject commit de07a4258079a3a7065a0c2aa8d39fe20f8ab5c2 +Subproject commit 75af759c3c6bc4cb77a0df7c735a8a2061acaa37
diff --git a/tools/json_schema_compiler/test/web_idl/basics.idl b/tools/json_schema_compiler/test/web_idl/basics.idl index 68dc487f..8ef32b4 100644 --- a/tools/json_schema_compiler/test/web_idl/basics.idl +++ b/tools/json_schema_compiler/test/web_idl/basics.idl
@@ -57,8 +57,13 @@ // |arg2|: This second argument uses a custom type. static undefined parameterComments(boolean arg1, ExampleType arg2); + static attribute OnTestOneEvent onTestOne; + static attribute OnTestTwoEvent onTestTwo; }; +interface OnTestOneEvent {}; +interface OnTestTwoEvent {}; + dictionary ExampleType { // Attribute comment attached to ExampleType.someString. DOMString someString;
diff --git a/tools/json_schema_compiler/web_idl_schema.py b/tools/json_schema_compiler/web_idl_schema.py index d6c8c44..ea53ed5a 100755 --- a/tools/json_schema_compiler/web_idl_schema.py +++ b/tools/json_schema_compiler/web_idl_schema.py
@@ -514,6 +514,33 @@ return result +class Event: + """Represents an API event and processes the details of it. + + Given an IDLNode of class Attribute for an event, extracts out the details of + the associated event callback and converts it to a Python dictionary + representing it. + TODO(crbug.com/340297705): Add in processing for the description and + parameters for events. + + Attributes: + node: The IDLNode for the Attribute definition for this event. + """ + + def __init__(self, node: IDLNode) -> None: + self.node = node + + def process(self) -> dict: + properties = OrderedDict() + properties['name'] = self.node.GetName() + + # Events just store the details of the event callback function, hence the + # type is considered 'function'. + properties['type'] = 'function' + + return properties + + class Namespace: """Represents an API namespace and processes individual details of it. @@ -541,19 +568,25 @@ def process(self) -> dict: functions = [] types = [] + events = [] description = ProcessNodeDescription(self.namespace).description nodoc = False platforms = None + # Functions are defined as Operations on the API Interface definition. for node in self.namespace.GetListOf('Operation'): functions.append(Operation(node).process()) - # Types are defined as dictionaries at the top level of the IDL file, which - # are found on the parent node of the Interface being processed for this - # namespace. + # Types are defined as Dictionaries at the top level of the IDL file, which + # are found on the parent node of the API Interface definition. for node in self.namespace.GetParent().GetListOf('Dictionary'): types.append(Dictionary(node).process()) + # Events are defined as Attributes on the API Interface definition, which + # use types that are defined as Interfaces on the top level of the IDL file. + for node in self.namespace.GetListOf('Attribute'): + events.append(Event(node).process()) + for extended_attribute in GetExtendedAttributes(self.namespace): attribute_name = extended_attribute.GetName() if attribute_name == 'nodoc': @@ -569,6 +602,7 @@ 'namespace': self.name, 'functions': functions, 'types': types, + 'events': events, 'nodoc': nodoc, 'description': description, 'platforms': platforms
diff --git a/tools/json_schema_compiler/web_idl_schema_test.py b/tools/json_schema_compiler/web_idl_schema_test.py index 467af4c..2fc699e 100755 --- a/tools/json_schema_compiler/web_idl_schema_test.py +++ b/tools/json_schema_compiler/web_idl_schema_test.py
@@ -50,6 +50,25 @@ raise KeyError('Could not find "type" with id "%s" in schema' % name) +def getEvent(schema: dict, name: str) -> dict: + """Gets the event dictionary with the specified name from the schema. + + Args: + schema: The processed API schema dictionary to look for the event in. + name: The name of the event to look for. + + Returns: + The dictionary for the event with the specified name. + + Raises: + KeyError: If the given event name was not found in the list of events. + """ + for item in schema['events']: + if item['name'] == name: + return item + raise KeyError('Could not find "event" with name "%s" in schema' % name) + + def getFunctionReturn(schema: dict, name: str) -> dict: """Gets the return dictionary for the function with the specified name. @@ -266,6 +285,22 @@ '$ref': 'ExampleType' }, function_parameters[1]) + # Tests that API events are processed as expected. + # TODO(crbug.com/379052294): Add description and parameter testing when they + # are added to the processor. + def testEvents(self): + schema = self.idl_basics + + event_one = getEvent(schema, 'onTestOne') + # This is a bit of a tautology for now, as getEvent() uses name to retrieve + # the object and raises a KeyError if it is not found. + self.assertEqual('onTestOne', event_one.get('name')) + self.assertEqual('function', event_one.get('type')) + + event_two = getEvent(schema, 'onTestTwo') + self.assertEqual('onTestTwo', event_two.get('name')) + self.assertEqual('function', event_two.get('type')) + # Tests that Dictionaries defined on the top level of the IDL file are # processed into types on the resulting namespace. def testApiTypesOnNamespace(self): @@ -333,8 +368,8 @@ # support for shared types to the new parser. def testMissingBrowserInterfaceError(self): expected_error_regex = ( - '.* File\(test\/web_idl\/missing_browser_interface.idl\): Required' - ' partial Browser interface not found in schema\.') + r'.* File\(test\/web_idl\/missing_browser_interface.idl\): Required' + r' partial Browser interface not found in schema\.') self.assertRaisesRegex( SchemaCompilerError, expected_error_regex, @@ -346,8 +381,8 @@ # throws an error. def testMissingAttributeOnBrowserError(self): expected_error_regex = ( - '.* Interface\(Browser\): The partial Browser interface should have' - ' exactly one attribute for the name the API will be exposed under\.') + r'.* Interface\(Browser\): The partial Browser interface should have' + r' exactly one attribute for the name the API will be exposed under\.') self.assertRaisesRegex( Exception, expected_error_regex, @@ -359,8 +394,8 @@ # doesn't support yet throws an error. def testUnsupportedBasicTypeError(self): expected_error_regex = ( - '.* PrimitiveType\(float\): Unsupported basic type found when' - ' processing type\.') + r'.* PrimitiveType\(float\): Unsupported basic type found when' + r' processing type\.') self.assertRaisesRegex( SchemaCompilerError, expected_error_regex, @@ -372,7 +407,7 @@ # doesn't support yet throws an error. def testUnsupportedTypeClassError(self): expected_error_regex = ( - '.* Any\(\): Unsupported type class when processing type\.') + r'.* Any\(\): Unsupported type class when processing type\.') self.assertRaisesRegex( SchemaCompilerError, expected_error_regex, @@ -385,8 +420,8 @@ # always be copyright lines and not part of the description). def testDocumentationCommentReachedTopOfFileError(self): expected_error_regex = ( - '.* Reached top of file when trying to parse description from file' - ' comment. Make sure there is a blank line before the comment.') + r'.* Reached top of file when trying to parse description from file' + r' comment. Make sure there is a blank line before the comment.') self.assertRaisesRegex( SchemaCompilerError, expected_error_regex, @@ -398,8 +433,8 @@ # 'void' has been deprecated and 'undefined' should be used instead. def testVoidUsageTriggersError(self): expected_error_regex = ( - 'Error processing node PrimitiveType\(void\): Usage of "void" in IDL is' - ' deprecated, use "Undefined" instead.') + r'Error processing node PrimitiveType\(void\): Usage of "void" in IDL' + r' is deprecated, use "Undefined" instead.') self.assertRaisesRegex( SchemaCompilerError, expected_error_regex, @@ -411,8 +446,8 @@ # processing for results in a schema compiler error. def testUnknownNamespaceExtendedAttributeNameError(self): expected_error_regex = ( - '.* Interface\(TestWebIdl\): Unknown extended attribute with name' - ' "UnknownExtendedAttribute" when processing namespace.') + r'.* Interface\(TestWebIdl\): Unknown extended attribute with name' + r' "UnknownExtendedAttribute" when processing namespace.') self.assertRaisesRegex( SchemaCompilerError, expected_error_regex,
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index 4a17f75a..a8db9ca 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -39664,6 +39664,15 @@ </description> </action> +<action name="Signin_Signin_FromCollaborationLeaveOrDeleteTabGroup"> + <owner>gambard@chromium.org</owner> + <owner>chrome-signin-mobile-team@google.com</owner> + <description> + Recorded on sign in start from access point + signin_metrics::AccessPoint::kCollaborationLeaveOrDeleteTabGroup. + </description> +</action> + <action name="Signin_Signin_FromCollaborationShareTabGroup"> <owner>gambard@chromium.org</owner> <owner>chrome-signin-mobile-team@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/collaboration_service/enums.xml b/tools/metrics/histograms/metadata/collaboration_service/enums.xml index 1beefc9..e3436ff4 100644 --- a/tools/metrics/histograms/metadata/collaboration_service/enums.xml +++ b/tools/metrics/histograms/metadata/collaboration_service/enums.xml
@@ -80,6 +80,14 @@ <!-- LINT.ThenChange(//components/collaboration/internal/metrics.h:CollaborationServiceJoinEvent) --> +<!-- LINT.IfChange(CollaborationServiceLeaveOrDeleteEntryPoint) --> + +<enum name="CollaborationServiceLeaveOrDeleteEntryPoint"> + <int value="0" label="Unknown"/> +</enum> + +<!-- LINT.ThenChange(//components/collaboration/public/collaboration_flow_entry_point.h:CollaborationServiceLeaveOrDeleteEntryPoint) --> + <!-- LINT.IfChange(CollaborationServiceShareOrManageEntryPoint) --> <enum name="CollaborationServiceShareOrManageEntryPoint">
diff --git a/tools/metrics/histograms/metadata/page/histograms.xml b/tools/metrics/histograms/metadata/page/histograms.xml index ef524151..50300e3 100644 --- a/tools/metrics/histograms/metadata/page/histograms.xml +++ b/tools/metrics/histograms/metadata/page/histograms.xml
@@ -1315,6 +1315,19 @@ </histogram> <histogram + name="PageLoad.Clients.GoogleSearch.NavigationTiming.CreateStreamDelay" + units="ms" expires_after="2025-10-01"> + <owner>hayato@chromium.org</owner> + <owner>chrome-loading@google.com</owner> + <summary> + The time taken for HTTP stream creation to finish, for Google Search page + loads. Recorded every time HttpStream::CreateStream() is called, for Google + Search page loads. Emitted when the navigation is completed or the app is + backgrounded on Android. + </summary> +</histogram> + +<histogram name="PageLoad.Clients.GoogleSearch.NavigationTiming.InitializeStreamDelay" units="ms" expires_after="2025-09-01"> <owner>hayato@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/signin/enums.xml b/tools/metrics/histograms/metadata/signin/enums.xml index 7916141..a29e3fd 100644 --- a/tools/metrics/histograms/metadata/signin/enums.xml +++ b/tools/metrics/histograms/metadata/signin/enums.xml
@@ -692,6 +692,7 @@ <int value="76" label="Join collaboration tab group authentication"/> <int value="77" label="History Sync Opt-in Expanded Pill"/> <int value="78" label="Widget (iOS only)"/> + <int value="79" label="Leave or delete collaboration tab group"/> </enum> <enum name="SigninAccountReconcilorState">
diff --git a/tools/metrics/histograms/metadata/user_education/histograms.xml b/tools/metrics/histograms/metadata/user_education/histograms.xml index 449eae9..3f21e96f 100644 --- a/tools/metrics/histograms/metadata/user_education/histograms.xml +++ b/tools/metrics/histograms/metadata/user_education/histograms.xml
@@ -34,6 +34,8 @@ <variant name="ComposeProactiveNudge" summary="For Compose feature; shown on autofill popup."/> <variant name="Glic" summary="Glic toggle on settings page."/> + <variant name="GlicAppMenuNewBadge" + summary="Glic menu item in the three dot menu."/> <variant name="GlicKeyboardShortcutNewBadge" summary="Glic keyboard shortcut input on settings page."/> <variant name="LensOverlay"
diff --git a/tools/perf/BUILD.gn b/tools/perf/BUILD.gn index 59af759..c83d62ee 100644 --- a/tools/perf/BUILD.gn +++ b/tools/perf/BUILD.gn
@@ -82,6 +82,9 @@ # For Speedometer benchmarks. "//third_party/speedometer", + # For Jetstream benchmarks. + "//third_party/jetstream", + # For smoothness.tough_canvas_cases "//chrome/test/data/perf/",
diff --git a/tools/perf/core/bot_platforms.py b/tools/perf/core/bot_platforms.py index 9222e799..f6e3b16d 100644 --- a/tools/perf/core/bot_platforms.py +++ b/tools/perf/core/bot_platforms.py
@@ -434,11 +434,13 @@ 'motionmark_1.3.1', estimated_runtime=estimated_runtime) -def _crossbench_motionmark1_3(estimated_runtime=360): + +def _crossbench_motionmark1_3(estimated_runtime=360, arguments=None): """Alias for the latest MotionMark 1.3.X version.""" return CrossbenchConfig('motionmark1.3.crossbench', 'motionmark_1.3', - estimated_runtime=estimated_runtime) + estimated_runtime=estimated_runtime, + arguments=arguments) def _crossbench_motionmark_main(estimated_runtime=360): @@ -466,11 +468,12 @@ estimated_runtime=estimated_runtime) -def _crossbench_jetstream2(estimated_runtime=180): +def _crossbench_jetstream2(estimated_runtime=180, arguments=None): """Alias of the latest JetStream 2.X version.""" return CrossbenchConfig('jetstream2.crossbench', 'jetstream_2.2', - estimated_runtime=estimated_runtime) + estimated_runtime=estimated_runtime, + arguments=arguments) def _crossbench_jetstream_main(estimated_runtime=180): @@ -525,12 +528,13 @@ ]) _CROSSBENCH_PIXEL9 = frozenset([ - _crossbench_jetstream2(), - _crossbench_speedometer3_1(arguments=['--fileserver']), - _crossbench_motionmark1_3(), + _crossbench_jetstream2(arguments=['--fileserver', '--debug']), + _crossbench_speedometer3_1(arguments=['--fileserver', '--debug']), + _crossbench_motionmark1_3(arguments=['--fileserver', '--debug']), _crossbench_loadline_phone(arguments=[ '--cool-down-threshold=moderate', '--no-splash', + '--debug', ]), ])
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index 5c09ddd..7f694fb4fb 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,7 +6,7 @@ }, "win": { "hash": "0cbfb22f24bde5b8c8cb729386931ca0f8839710", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/745c6d3fead9008f05ee73172e46d059863b5ab3/trace_processor_shell.exe" + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/181c298de2ebe848dad23788d76ae1f517aae6ec/trace_processor_shell.exe" }, "linux_arm": { "hash": "28bd9c986197285caeb7e5f7e8434e8f61bd7822",
diff --git a/tools/perf/core/shard_maps/android-pixel9-perf_map.json b/tools/perf/core/shard_maps/android-pixel9-perf_map.json index 9658aba..ec90a8f 100644 --- a/tools/perf/core/shard_maps/android-pixel9-perf_map.json +++ b/tools/perf/core/shard_maps/android-pixel9-perf_map.json
@@ -11,7 +11,10 @@ "crossbench": { "jetstream_2.2": { "display_name": "jetstream2.crossbench", - "arguments": [] + "arguments": [ + "--fileserver", + "--debug" + ] } }, "benchmarks": {} @@ -22,7 +25,8 @@ "display_name": "loadline_phone.crossbench", "arguments": [ "--cool-down-threshold=moderate", - "--no-splash" + "--no-splash", + "--debug" ] } }, @@ -32,7 +36,10 @@ "crossbench": { "motionmark_1.3": { "display_name": "motionmark1.3.crossbench", - "arguments": [] + "arguments": [ + "--fileserver", + "--debug" + ] } }, "benchmarks": {} @@ -42,7 +49,8 @@ "speedometer_3.1": { "display_name": "speedometer3.1.crossbench", "arguments": [ - "--fileserver" + "--fileserver", + "--debug" ] } },
diff --git a/tools/perf/core/shard_maps/android-pixel9-pro-perf_map.json b/tools/perf/core/shard_maps/android-pixel9-pro-perf_map.json index 9658aba..ec90a8f 100644 --- a/tools/perf/core/shard_maps/android-pixel9-pro-perf_map.json +++ b/tools/perf/core/shard_maps/android-pixel9-pro-perf_map.json
@@ -11,7 +11,10 @@ "crossbench": { "jetstream_2.2": { "display_name": "jetstream2.crossbench", - "arguments": [] + "arguments": [ + "--fileserver", + "--debug" + ] } }, "benchmarks": {} @@ -22,7 +25,8 @@ "display_name": "loadline_phone.crossbench", "arguments": [ "--cool-down-threshold=moderate", - "--no-splash" + "--no-splash", + "--debug" ] } }, @@ -32,7 +36,10 @@ "crossbench": { "motionmark_1.3": { "display_name": "motionmark1.3.crossbench", - "arguments": [] + "arguments": [ + "--fileserver", + "--debug" + ] } }, "benchmarks": {} @@ -42,7 +49,8 @@ "speedometer_3.1": { "display_name": "speedometer3.1.crossbench", "arguments": [ - "--fileserver" + "--fileserver", + "--debug" ] } },
diff --git a/tools/perf/core/shard_maps/android-pixel9-pro-xl-perf_map.json b/tools/perf/core/shard_maps/android-pixel9-pro-xl-perf_map.json index 9658aba..ec90a8f 100644 --- a/tools/perf/core/shard_maps/android-pixel9-pro-xl-perf_map.json +++ b/tools/perf/core/shard_maps/android-pixel9-pro-xl-perf_map.json
@@ -11,7 +11,10 @@ "crossbench": { "jetstream_2.2": { "display_name": "jetstream2.crossbench", - "arguments": [] + "arguments": [ + "--fileserver", + "--debug" + ] } }, "benchmarks": {} @@ -22,7 +25,8 @@ "display_name": "loadline_phone.crossbench", "arguments": [ "--cool-down-threshold=moderate", - "--no-splash" + "--no-splash", + "--debug" ] } }, @@ -32,7 +36,10 @@ "crossbench": { "motionmark_1.3": { "display_name": "motionmark1.3.crossbench", - "arguments": [] + "arguments": [ + "--fileserver", + "--debug" + ] } }, "benchmarks": {} @@ -42,7 +49,8 @@ "speedometer_3.1": { "display_name": "speedometer3.1.crossbench", "arguments": [ - "--fileserver" + "--fileserver", + "--debug" ] } },
diff --git a/tools/pgo/generate_profile.py b/tools/pgo/generate_profile.py index d844f35..e71fabd 100755 --- a/tools/pgo/generate_profile.py +++ b/tools/pgo/generate_profile.py
@@ -308,7 +308,10 @@ _android_browser_installed = True if args.android_hostname: - cmd += [f"--android={args.android_hostname}"] + cmd += [ + "--connect-to-device-over-network", + f"--device={args.android_hostname}", + ] _LOGGER.debug( f"Running benchmark on Android with command: {' '.join(cmd)}")
diff --git a/tools/rust/update_rust.py b/tools/rust/update_rust.py index c7262fa..c9a4f2f 100755 --- a/tools/rust/update_rust.py +++ b/tools/rust/update_rust.py
@@ -31,7 +31,7 @@ # These fields are written by //tools/clang/scripts/upload_revision.py, and # should not be changed manually. -RUST_REVISION = 'f7b43542838f0a4a6cfdb17fbeadf45002042a77' +RUST_REVISION = '3f690c2257b7080cd3a8cce64e082fc972148990' RUST_SUB_REVISION = 1 # The revision of Crubit to use from https://github.com/google/crubit
diff --git a/ui/android/java/src/org/chromium/ui/listmenu/BasicListMenu.java b/ui/android/java/src/org/chromium/ui/listmenu/BasicListMenu.java index 6386ef9..704efdd2 100644 --- a/ui/android/java/src/org/chromium/ui/listmenu/BasicListMenu.java +++ b/ui/android/java/src/org/chromium/ui/listmenu/BasicListMenu.java
@@ -49,10 +49,17 @@ /** * Helper function to build a ListItem of a divider. * + * @param isIncognito Whether we're creating an incognito-themed menu. * @return ListItem Representing a divider. */ - public static ListItem buildMenuDivider() { - return new ListItem(ListMenuItemType.DIVIDER, new PropertyModel()); + public static ListItem buildMenuDivider(boolean isIncognito) { + PropertyModel.Builder builder = + new PropertyModel.Builder(ListSectionDividerProperties.ALL_KEYS); + if (isIncognito) { + builder.with( + ListSectionDividerProperties.COLOR_ID, R.color.divider_line_bg_color_light); + } + return new ListItem(ListMenuItemType.DIVIDER, builder.build()); } /**
diff --git a/ui/android/java/src/org/chromium/ui/listmenu/ListSectionDividerProperties.java b/ui/android/java/src/org/chromium/ui/listmenu/ListSectionDividerProperties.java index 43b650c..883964b 100644 --- a/ui/android/java/src/org/chromium/ui/listmenu/ListSectionDividerProperties.java +++ b/ui/android/java/src/org/chromium/ui/listmenu/ListSectionDividerProperties.java
@@ -14,6 +14,9 @@ public static final WritableIntPropertyKey LEFT_PADDING_DIMEN_ID = new WritableIntPropertyKey(); public static final WritableIntPropertyKey RIGHT_PADDING_DIMEN_ID = new WritableIntPropertyKey(); + public static final WritableIntPropertyKey COLOR_ID = new WritableIntPropertyKey(); - public static final PropertyKey[] ALL_KEYS = {LEFT_PADDING_DIMEN_ID, RIGHT_PADDING_DIMEN_ID}; + public static final PropertyKey[] ALL_KEYS = { + LEFT_PADDING_DIMEN_ID, RIGHT_PADDING_DIMEN_ID, COLOR_ID + }; }
diff --git a/ui/android/java/src/org/chromium/ui/listmenu/ListSectionDividerViewBinder.java b/ui/android/java/src/org/chromium/ui/listmenu/ListSectionDividerViewBinder.java index 2ab04cf..abc292d 100644 --- a/ui/android/java/src/org/chromium/ui/listmenu/ListSectionDividerViewBinder.java +++ b/ui/android/java/src/org/chromium/ui/listmenu/ListSectionDividerViewBinder.java
@@ -10,9 +10,11 @@ import androidx.annotation.DimenRes; import androidx.annotation.Px; +import androidx.core.content.ContextCompat; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; +import org.chromium.ui.R; import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyModel; @@ -49,6 +51,12 @@ rightPaddingPx, view.getPaddingBottom()); } + } else if (propertyKey == ListSectionDividerProperties.COLOR_ID) { + view.findViewById(R.id.divider_view) + .setBackgroundColor( + ContextCompat.getColor( + view.getContext(), + model.get(ListSectionDividerProperties.COLOR_ID))); } } }
diff --git a/ui/views/controls/menu/menu_config_chromeos.cc b/ui/views/controls/menu/menu_config_chromeos.cc index aec943d..1425b3f7 100644 --- a/ui/views/controls/menu/menu_config_chromeos.cc +++ b/ui/views/controls/menu/menu_config_chromeos.cc
@@ -6,6 +6,8 @@ namespace views { -void MenuConfig::InitPlatform() {} +void MenuConfig::InitPlatform() { + use_bubble_border = true; +} } // namespace views
diff --git a/ui/views/controls/menu/menu_scroll_view_container.cc b/ui/views/controls/menu/menu_scroll_view_container.cc index e997f21..cd829fe0 100644 --- a/ui/views/controls/menu/menu_scroll_view_container.cc +++ b/ui/views/controls/menu/menu_scroll_view_container.cc
@@ -249,8 +249,9 @@ // Enable background blur for ChromeOS system context menu. background_view_->SetPaintToLayer(); auto* background_layer = background_view_->layer(); - background_layer->SetFillsBoundsOpaquely(false); + background_layer->SetName("MenuScrollViewContainer/background"); if (ShouldApplyBackgroundBlur()) { + background_layer->SetFillsBoundsOpaquely(false); background_layer->SetBackgroundBlur(kBackgroundBlurSigma); background_layer->SetBackdropFilterQuality(kBackgroundBlurQuality); }
diff --git a/ui/views/test/mock_native_widget.h b/ui/views/test/mock_native_widget.h index dd9beb8..271d78f 100644 --- a/ui/views/test/mock_native_widget.h +++ b/ui/views/test/mock_native_widget.h
@@ -75,7 +75,7 @@ (ui::mojom::ModalType modal_type), (override)); MOCK_METHOD(void, - SetColorMode, + OnWidgetThemeChanged, (ui::ColorProviderKey::ColorMode), (override)); MOCK_METHOD(gfx::Rect, GetWindowBoundsInScreen, (), (const override));
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc index 006ed28..c359937 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -852,11 +852,9 @@ desktop_window_tree_host_->InitModalType(modal_type); } -void DesktopNativeWidgetAura::SetColorMode( +void DesktopNativeWidgetAura::OnWidgetThemeChanged( ui::ColorProviderKey::ColorMode color_mode) { - // Intentional no-op. - // The window frame is drawn by views. The OS does not need to know about - // which color mode the window is using. + desktop_window_tree_host_->OnWidgetThemeChanged(color_mode); } gfx::Rect DesktopNativeWidgetAura::GetWindowBoundsInScreen() const {
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h index 76fbcaae1..c298ad3 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
@@ -148,7 +148,7 @@ void SetWindowIcons(const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) override; void InitModalType(ui::mojom::ModalType modal_type) override; - void SetColorMode(ui::ColorProviderKey::ColorMode color_mode) override; + void OnWidgetThemeChanged(ui::ColorProviderKey::ColorMode color_mode) override; gfx::Rect GetWindowBoundsInScreen() const override; gfx::Rect GetClientAreaBoundsInScreen() const override; gfx::Rect GetRestoredBounds() const override;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host.h b/ui/views/widget/desktop_aura/desktop_window_tree_host.h index 4a2279af..4803c129 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host.h +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host.h
@@ -64,6 +64,9 @@ // Called from DesktopNativeWidgetAura::OnWidgetInitDone(). virtual void OnWidgetInitDone() = 0; + virtual void OnWidgetThemeChanged( + ui::ColorProviderKey::ColorMode color_mode) = 0; + // Called from DesktopNativeWidgetAura::OnWindowActivated(). // `active`: if `DesktopNativeWidgetAura::content_window()` contains the // `aura::Window` that gains active.
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc index 649cff57..2150c34 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -328,6 +328,9 @@ GetWindowMaskForClipping().isEmpty()); } +void DesktopWindowTreeHostPlatform::OnWidgetThemeChanged( + ui::ColorProviderKey::ColorMode color_mode) {} + void DesktopWindowTreeHostPlatform::OnActiveWindowChanged(bool active) { #if BUILDFLAG(IS_OZONE) // When bubbles are accelerated widgets, `window_children_` can contain a
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h index 859548249..0040738 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h
@@ -70,6 +70,8 @@ void Init(const Widget::InitParams& params) override; void OnNativeWidgetCreated(const Widget::InitParams& params) override; void OnWidgetInitDone() override; + void OnWidgetThemeChanged( + ui::ColorProviderKey::ColorMode color_mode) override; void OnActiveWindowChanged(bool active) override; std::unique_ptr<corewm::Tooltip> CreateTooltip() override; std::unique_ptr<aura::client::DragDropClient> CreateDragDropClient() override;
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc index 3af1fe1..6ebd309 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -182,24 +182,6 @@ } } -//////////////////////////////////////////////////////////////////////////////// -// DesktopWindowTreeHostWin, WidgetObserver implementation: -void DesktopWindowTreeHostWin::OnWidgetThemeChanged(Widget* widget) { - if (ShouldApplySystemBackdrop()) { - // Ensure that DWM knows to apply the correct color scheme to the window - // backdrop whenever it changes. - BOOL use_dark_mode = - widget->GetColorMode() == ui::ColorProviderKey::ColorMode::kDark; - HRESULT hr = DwmSetWindowAttribute(GetHWND(), DWMWA_USE_IMMERSIVE_DARK_MODE, - &use_dark_mode, sizeof(use_dark_mode)); - CHECK_EQ(hr, S_OK); - return; - } - wuc_backdrop_->UpdateBackdropColor( - GetWidget()->GetColorProvider()->GetColor(ui::kColorFrameActive)); -} - -//////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHostWin, DesktopWindowTreeHost implementation: void DesktopWindowTreeHostWin::Init(const Widget::InitParams& params) { @@ -240,8 +222,6 @@ if (((message_handler_->window_ex_style() & WS_EX_NOREDIRECTIONBITMAP) == WS_EX_NOREDIRECTIONBITMAP) && !message_handler_->is_translucent()) { - // Observe the widget to update the backdrop when the color mode changes. - widget_observation_.Observe(GetWidget()); // Ensure that the hwnd has been created. CHECK(GetHWND()); @@ -302,6 +282,25 @@ void DesktopWindowTreeHostWin::OnWidgetInitDone() {} +void DesktopWindowTreeHostWin::OnWidgetThemeChanged( + ui::ColorProviderKey::ColorMode color_mode) { + if (ShouldApplySystemBackdrop()) { + // Ensure that DWM knows to apply the correct color scheme to the window + // backdrop whenever it changes. + BOOL use_dark_mode = + color_mode == ui::ColorProviderKey::ColorMode::kDark; + HRESULT hr = DwmSetWindowAttribute(GetHWND(), DWMWA_USE_IMMERSIVE_DARK_MODE, + &use_dark_mode, sizeof(use_dark_mode)); + CHECK_EQ(hr, S_OK); + return; + } + + if (GetWidget() && wuc_backdrop_) { + wuc_backdrop_->UpdateBackdropColor( + GetWidget()->GetColorProvider()->GetColor(ui::kColorFrameActive)); + } +} + std::unique_ptr<corewm::Tooltip> DesktopWindowTreeHostWin::CreateTooltip() { return std::make_unique<corewm::TooltipAura>(); }
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h index f268b15..b441b87 100644 --- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h +++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h
@@ -16,7 +16,6 @@ #include "ui/gfx/win/wuc_backdrop.h" #include "ui/views/views_export.h" #include "ui/views/widget/desktop_aura/desktop_window_tree_host.h" -#include "ui/views/widget/widget_observer.h" #include "ui/views/win/hwnd_message_handler_delegate.h" #include "ui/wm/public/animation_host.h" @@ -46,11 +45,11 @@ class DesktopWindowTreeHostWinTestApi; } -class VIEWS_EXPORT DesktopWindowTreeHostWin : public DesktopWindowTreeHost, - public wm::AnimationHost, - public aura::WindowTreeHost, - public HWNDMessageHandlerDelegate, - public WidgetObserver { +class VIEWS_EXPORT DesktopWindowTreeHostWin + : public DesktopWindowTreeHost, + public wm::AnimationHost, + public aura::WindowTreeHost, + public HWNDMessageHandlerDelegate { public: DesktopWindowTreeHostWin( internal::NativeWidgetDelegate* native_widget_delegate, @@ -100,6 +99,8 @@ void OnNativeWidgetCreated(const Widget::InitParams& params) override; void OnActiveWindowChanged(bool active) override; void OnWidgetInitDone() override; + void OnWidgetThemeChanged( + ui::ColorProviderKey::ColorMode color_mode) override; std::unique_ptr<corewm::Tooltip> CreateTooltip() override; std::unique_ptr<aura::client::DragDropClient> CreateDragDropClient() override; void Close() override; @@ -270,9 +271,6 @@ void HandleWindowScaleFactorChanged(float window_scale_factor) override; void HandleHeadlessWindowBoundsChanged(const gfx::Rect& bounds) override; - // Overridden from WidgetObserver. - void OnWidgetThemeChanged(Widget* widget) override; - Widget* GetWidget(); const Widget* GetWidget() const; HWND GetHWND() const; @@ -347,8 +345,6 @@ // A Windows.Ui.Composition visual tree that represents the window backdrop. std::unique_ptr<gfx::WUCBackdrop> wuc_backdrop_; - base::ScopedObservation<Widget, WidgetObserver> widget_observation_{this}; - // Visibility of the cursor. On Windows we can have multiple root windows and // the implementation of ::ShowCursor() is based on a counter, so making this // member static ensures that ::ShowCursor() is always called exactly once
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc index fd63dec..e596242d 100644 --- a/ui/views/widget/native_widget_aura.cc +++ b/ui/views/widget/native_widget_aura.cc
@@ -564,7 +564,7 @@ } } -void NativeWidgetAura::SetColorMode( +void NativeWidgetAura::OnWidgetThemeChanged( ui::ColorProviderKey::ColorMode color_mode) { // Intentional no-op. // The window frame is drawn by views. The OS does not need to know about
diff --git a/ui/views/widget/native_widget_aura.h b/ui/views/widget/native_widget_aura.h index 6f9c037..a71f1eb 100644 --- a/ui/views/widget/native_widget_aura.h +++ b/ui/views/widget/native_widget_aura.h
@@ -107,7 +107,7 @@ void SetWindowIcons(const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) override; void InitModalType(ui::mojom::ModalType modal_type) override; - void SetColorMode(ui::ColorProviderKey::ColorMode color_mode) override; + void OnWidgetThemeChanged(ui::ColorProviderKey::ColorMode color_mode) override; gfx::Rect GetWindowBoundsInScreen() const override; gfx::Rect GetClientAreaBoundsInScreen() const override; gfx::Rect GetRestoredBounds() const override;
diff --git a/ui/views/widget/native_widget_mac.h b/ui/views/widget/native_widget_mac.h index ea2d365..c1a6c62 100644 --- a/ui/views/widget/native_widget_mac.h +++ b/ui/views/widget/native_widget_mac.h
@@ -155,7 +155,11 @@ void SetWindowIcons(const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) override; void InitModalType(ui::mojom::ModalType modal_type) override; - void SetColorMode(ui::ColorProviderKey::ColorMode color_mode) override; + // Suppress warning about hiding virtual WidgetObserver::OnWidgetThemeChanged. + // TODO(kerenzhu): Do not observe Widget in this class. + using WidgetObserver::OnWidgetThemeChanged; + void OnWidgetThemeChanged( + ui::ColorProviderKey::ColorMode color_mode) override; gfx::Rect GetWindowBoundsInScreen() const override; gfx::Rect GetClientAreaBoundsInScreen() const override; gfx::Rect GetRestoredBounds() const override;
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm index 897646ac..9595049 100644 --- a/ui/views/widget/native_widget_mac.mm +++ b/ui/views/widget/native_widget_mac.mm
@@ -549,7 +549,7 @@ // Everything happens upon show. } -void NativeWidgetMac::SetColorMode(ui::ColorProviderKey::ColorMode color_mode) { +void NativeWidgetMac::OnWidgetThemeChanged(ui::ColorProviderKey::ColorMode color_mode) { if (ns_window_host_) { ns_window_host_->SetColorMode(color_mode); }
diff --git a/ui/views/widget/native_widget_private.h b/ui/views/widget/native_widget_private.h index 924395d..bf237efa 100644 --- a/ui/views/widget/native_widget_private.h +++ b/ui/views/widget/native_widget_private.h
@@ -173,8 +173,10 @@ // initially parented. virtual void InitModalType(ui::mojom::ModalType modal_type) = 0; - // Sets the color mode used for window styling. - virtual void SetColorMode(ui::ColorProviderKey::ColorMode color_mode) = 0; + // Notifies the NativeWidget that the widget theme has changed. + // At the moment, the platform window only cares about the color mode. + virtual void OnWidgetThemeChanged( + ui::ColorProviderKey::ColorMode color_mode) = 0; // See method documentation in Widget. virtual gfx::Rect GetWindowBoundsInScreen() const = 0;
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc index 6a034e7..527f7fa 100644 --- a/ui/views/widget/widget.cc +++ b/ui/views/widget/widget.cc
@@ -579,7 +579,7 @@ parent_->OnChildAdded(this); } - native_widget_->SetColorMode(GetColorMode()); + native_widget_->OnWidgetThemeChanged(GetColorMode()); UpdateAccessibleNameForRootView(); native_theme_observation_.Observe(GetNativeTheme()); @@ -1442,7 +1442,7 @@ NotifyColorProviderChanged(); if (native_widget_) { - native_widget_->SetColorMode(GetColorMode()); + native_widget_->OnWidgetThemeChanged(GetColorMode()); } }
diff --git a/ui/webui/resources/cr_elements/cr_toast/cr_toast.ts b/ui/webui/resources/cr_elements/cr_toast/cr_toast.ts index ec63071..4be8d4e 100644 --- a/ui/webui/resources/cr_elements/cr_toast/cr_toast.ts +++ b/ui/webui/resources/cr_elements/cr_toast/cr_toast.ts
@@ -40,6 +40,12 @@ accessor open: boolean = false; private hideTimeoutId_: number|null = null; + constructor() { + super(); + this.addEventListener('focusin', this.clearTimeout_); + this.addEventListener('focusout', this.resetAutoHide_); + } + override willUpdate(changedProperties: PropertyValues<this>) { super.willUpdate(changedProperties); @@ -48,14 +54,18 @@ } } - /** - * Cancels existing auto-hide, and sets up new auto-hide. - */ - private resetAutoHide_() { + private clearTimeout_() { if (this.hideTimeoutId_ !== null) { window.clearTimeout(this.hideTimeoutId_); this.hideTimeoutId_ = null; } + } + + /** + * Cancels existing auto-hide, and sets up new auto-hide. + */ + private resetAutoHide_() { + this.clearTimeout_(); if (this.open && this.duration !== 0) { this.hideTimeoutId_ = window.setTimeout(() => {