diff --git a/.gitmodules b/.gitmodules index e3e720e9..d2fb67b 100644 --- a/.gitmodules +++ b/.gitmodules
@@ -194,6 +194,9 @@ [submodule "third_party/crossbench"] path = third_party/crossbench url = https://chromium.googlesource.com/crossbench +[submodule "third_party/crossbench-web-tests"] + path = third_party/crossbench-web-tests + url = https://chromium.googlesource.com/chromium/web-tests [submodule "third_party/depot_tools"] path = third_party/depot_tools url = https://chromium.googlesource.com/chromium/tools/depot_tools
diff --git a/DEPS b/DEPS index 3585025..cb76c48 100644 --- a/DEPS +++ b/DEPS
@@ -240,7 +240,7 @@ # luci-go CIPD package version. # Make sure the revision is uploaded by infra-packagers builder. # https://ci.chromium.org/p/infra-internal/g/infra-packagers/console - 'luci_go': 'git_revision:efbba64186435ac28ad419751e67e377c7459c38', + 'luci_go': 'git_revision:ef84eaf5160dadce3d98b8a2e4aa658dd2c0a7f1', # This can be overridden, e.g. with custom_vars, to build clang from HEAD # instead of downloading the prebuilt pinned revision. @@ -295,7 +295,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': 'f2624278cd4bc2a5d4cc211124a6b60bdb4621b0', + 'skia_revision': 'cdd82125aabea8e9970f42b0fb55d104bc371520', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -303,7 +303,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': '2b12571c828100adca9b721ed5df96a0c94cd083', + 'angle_revision': '214a48c41a84799cdf7cf821fcb0e3a0fccd2b11', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -311,11 +311,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': '7f80ad7406e307600914d4ba6ac429cb556df9a3', + 'pdfium_revision': '864375abbcd387f3d3153ce810cc8fd9c395a15e', # 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': '1fecca988bd10687c9f2b6b9e6328f03f526e8d7', + 'boringssl_revision': '5903cfafaf9cd4f1ef436b6e5717ba511f096e83', # 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. @@ -365,6 +365,10 @@ # and whatever else without interference from each other. 'crossbench_revision': '0d9d615c19d18a07514e2b9563bcc939a27a9766', # 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_web_tests_revision': '3c76c8201f0732fe9781742229ab8ac43bf90cbf', + # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. 'libfuzzer_revision': 'bea408a6e01f0f7e6c82a43121fe3af4506c932e', @@ -515,10 +519,10 @@ # If you change this, also update the libc++ revision in # //buildtools/deps_revisions.gni. - 'libcxx_revision': 'be9dd89ffbfdf2b6cf6b9c5ec076d6adbc5d25a6', + 'libcxx_revision': 'c105b13e377d3fa5fdf033957f31abc1894a9968', # GN CIPD package version. - 'gn_version': 'git_revision:c9ed4b171ce3f2a1fe4bdd7cd0eb95607cbdb708', + 'gn_version': 'git_revision:acfe5fde8c5475a9a83bd55f3f222a6dbbcbd0ae', # ninja CIPD package. 'ninja_package': 'infra/3pp/tools/ninja/', @@ -838,157 +842,157 @@ 'objects': [ { # The Android libclang_rt.builtins libraries are currently only included in the Linux clang package. - 'object_name': 'Linux_x64/clang-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': '3c98ad24de10a71de8fedbcb9485e676891bddd0ed2c9d2a884adb2c1a7331d7', - 'size_bytes': 54825068, - 'generation': 1752145985596342, + 'object_name': 'Linux_x64/clang-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '73e573aa1824bf1cd8ac8815304e41c174938ab05362e316d9c3d8d3ae8f4d0b', + 'size_bytes': 54836700, + 'generation': 1753711033480152, 'condition': '(host_os == "linux" or checkout_android) and non_git_source', }, { - 'object_name': 'Linux_x64/clang-tidy-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': '105bc0e246fb4a0f5e3b2f8843a6f3167348cd0e70bea06fbbdfbc537d5d18f8', - 'size_bytes': 13661896, - 'generation': 1752145985913124, + 'object_name': 'Linux_x64/clang-tidy-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '7cd6261a5ffe1989bcaed190b6911d8bb1f296b1dfbbc92b5eda2262d2d2d604', + 'size_bytes': 13658364, + 'generation': 1753711033512134, 'condition': 'host_os == "linux" and checkout_clang_tidy and non_git_source', }, { - 'object_name': 'Linux_x64/clangd-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': '67012005e55161594fee12cb319e9d9b02d5913398e77dc75b84c9652388c896', - 'size_bytes': 13879792, - 'generation': 1752145986092003, + 'object_name': 'Linux_x64/clangd-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '19c844ed67e3cc7054fd2af4793f5828fa8bd4a712fb797e90cb814fc81d406e', + 'size_bytes': 13870348, + 'generation': 1753711033515636, 'condition': 'host_os == "linux" and checkout_clangd and non_git_source', }, { - 'object_name': 'Linux_x64/llvm-code-coverage-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': '45ca07ede1286a65d8079a7a2a8a4820376fa119993b589a2a145dbc7c0be108', - 'size_bytes': 2313916, - 'generation': 1752145986583128, + 'object_name': 'Linux_x64/llvm-code-coverage-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '78dbed2a5e4e9747c18f5e4876070c22e126bf6990acc66effcb5c981a30c346', + 'size_bytes': 2313988, + 'generation': 1753711033567913, 'condition': 'host_os == "linux" and checkout_clang_coverage_tools and non_git_source', }, { - 'object_name': 'Linux_x64/llvmobjdump-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': 'ad0f0e732bbf3a98f9439166211fbc5963e2b28d9f1b18aae032732e46f1508d', - 'size_bytes': 5678112, - 'generation': 1752145986537219, + 'object_name': 'Linux_x64/llvmobjdump-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '4e34eac7c8fc81440df3524f28b7140276710996241acdf8515592ff73586f87', + 'size_bytes': 5675412, + 'generation': 1753711033518775, 'condition': '((checkout_linux or checkout_mac or checkout_android) and host_os == "linux") and non_git_source', }, { - 'object_name': 'Mac/clang-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': '2d8f77b22ec41fddee69295552f5701e984d1f6ff4137c6df80732d9000993f2', - 'size_bytes': 52436332, - 'generation': 1752145988392806, + 'object_name': 'Mac/clang-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': 'f585d02104e485eb443a26555a779a0444bf30ccc3600d34ab1f35d09a500598', + 'size_bytes': 52411164, + 'generation': 1753711035252100, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac/clang-mac-runtime-library-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': '1e42e2f82ba212e0fe85ca966563b575d56a024783a4f565530915a07201e915', - 'size_bytes': 996428, - 'generation': 1752146008780584, + 'object_name': 'Mac/clang-mac-runtime-library-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '9816d228b6d30ba0fe469be5df19199fcf1851b39872add9ba3be5a8e350830a', + 'size_bytes': 996000, + 'generation': 1753711043142947, 'condition': 'checkout_mac and not host_os == "mac"', }, { - 'object_name': 'Mac/clang-tidy-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': 'e8b1b1ebeecf943ae0aaa77e1f8b7f8346262944af6e986d25573710920b15d5', - 'size_bytes': 13740128, - 'generation': 1752145988728636, + 'object_name': 'Mac/clang-tidy-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': 'e78bba5afc0c824d17c096b0e4b4c52145433bbb43a7f3b8c3264251b9f866d1', + 'size_bytes': 13746260, + 'generation': 1753711035308001, 'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clang_tidy', }, { - 'object_name': 'Mac/clangd-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': 'd64c710fb90c6db7b4fc8e637a65367a6c298bdc3e31a06dd359f75958d8214f', - 'size_bytes': 15151012, - 'generation': 1752145988938363, + 'object_name': 'Mac/clangd-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '26dba5d090c218342c9ae92dc771ae5ac46953f427752c40995c1c5174802da7', + 'size_bytes': 15150168, + 'generation': 1753711035309045, 'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clangd', }, { - 'object_name': 'Mac/llvm-code-coverage-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': 'c8fd6f5bde2d3f4d5e317624754837d78a18dd1c693a75c952c6fbba4f260fe5', - 'size_bytes': 2282292, - 'generation': 1752145989568677, + 'object_name': 'Mac/llvm-code-coverage-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '217fccb5c021908d3972f32f40a9631fbd5cac9d2bee3fd973aef88b2f6a251e', + 'size_bytes': 2282228, + 'generation': 1753711035430925, 'condition': 'host_os == "mac" and host_cpu == "x64" and checkout_clang_coverage_tools', }, { - 'object_name': 'Mac/llvmobjdump-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': 'fff3642ab8fbfffa4f5e8feda7b7f8ab7838099b2d0d1f5ce323a1d8aebf05ca', - 'size_bytes': 5490072, - 'generation': 1752145989091474, + 'object_name': 'Mac/llvmobjdump-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '4eae5f9ad4fba33dea45815fd965258f4649cf8e0794b6c431ce166a966cb29d', + 'size_bytes': 5483720, + 'generation': 1753711035319601, 'condition': 'host_os == "mac" and host_cpu == "x64"', }, { - 'object_name': 'Mac_arm64/clang-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': '68460f5290547d101c4c9c2e4ea74b783159e553e90dad5b422f03f58d16d05c', - 'size_bytes': 44344416, - 'generation': 1752146010574027, + 'object_name': 'Mac_arm64/clang-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '17275ca988de014adc269f8fa694f501cecd6c25c0494fc45582fc4e3d258bed', + 'size_bytes': 44345732, + 'generation': 1753711044909340, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Mac_arm64/clang-tidy-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': 'ecca250ba2245bbbb9a643cf298cc573f42e47490b06d15d3df84bb83e151af1', - 'size_bytes': 11873856, - 'generation': 1752146010875446, + 'object_name': 'Mac_arm64/clang-tidy-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '387eb3412e41a17c4e40e8fe3eeda65806a78bba6bb5799ff3ccce6ee499bb2d', + 'size_bytes': 11877428, + 'generation': 1753711044929125, 'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clang_tidy', }, { - 'object_name': 'Mac_arm64/clangd-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': '67f961074a8ad9df577b6dc1dc1de8527e0ba514f1d9f71480f7ebc6c3cc9281', - 'size_bytes': 12133348, - 'generation': 1752146011066509, + 'object_name': 'Mac_arm64/clangd-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '21893beaab322a2f04a3f07fdab1fe6219ca04d6151d621cbcf445214f213c23', + 'size_bytes': 12132752, + 'generation': 1753711044937334, 'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clangd', }, { - 'object_name': 'Mac_arm64/llvm-code-coverage-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': '16d4aba308a7f74dd16326f0d5d9be8391424664fee4fd4d756dc2abd0e54066', - 'size_bytes': 1989020, - 'generation': 1752146011858185, + 'object_name': 'Mac_arm64/llvm-code-coverage-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '33603b2ac2577042d0cb8ed050dc5c7c5894984cf85933003e87fd3eb854bb7b', + 'size_bytes': 1988508, + 'generation': 1753711045041769, 'condition': 'host_os == "mac" and host_cpu == "arm64" and checkout_clang_coverage_tools', }, { - 'object_name': 'Mac_arm64/llvmobjdump-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': 'a8fddab5bd008eeac135e832fe327d15459bc7b1d2ca87a12fa4a9ba3c4be009', - 'size_bytes': 5317004, - 'generation': 1752146011233744, + 'object_name': 'Mac_arm64/llvmobjdump-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '26c961fa85d76f5f2fcdfd80980b4a0ca4c394307cf78e65888d061e29ae9b0b', + 'size_bytes': 5315616, + 'generation': 1753711044929262, 'condition': 'host_os == "mac" and host_cpu == "arm64"', }, { - 'object_name': 'Win/clang-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': '1021cc3231615a3556f691820014eb40dc93b4eb714bf4037dced002a454b763', - 'size_bytes': 47391392, - 'generation': 1752146036687604, + 'object_name': 'Win/clang-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': 'ce28d6045da4a2d3e909a3b272e973ecab3360452780e501b2110b6fd51be899', + 'size_bytes': 47389532, + 'generation': 1753711055091539, 'condition': 'host_os == "win"', }, { - 'object_name': 'Win/clang-tidy-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': 'f4e64b58f73ce41007646c70b437fa540c5eb9513478aa329a334f22efd5c1e8', - 'size_bytes': 13484108, - 'generation': 1752146036657887, + 'object_name': 'Win/clang-tidy-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '1380577341962e45d86ace7791bb327a8fb0f7854b31cb22271a79742df19921', + 'size_bytes': 13484452, + 'generation': 1753711055103864, 'condition': 'host_os == "win" and checkout_clang_tidy', }, { - 'object_name': 'Win/clang-win-runtime-library-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': '296019d05e69b95534e85e23f203cb606398124686b33839ffddb43b09000913', - 'size_bytes': 2503600, - 'generation': 1752146058762703, + 'object_name': 'Win/clang-win-runtime-library-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': 'ea7a3e86fbfa94eef0fa2560206cf596876cf96145be2d03bdf2af7f1a23198e', + 'size_bytes': 2506736, + 'generation': 1753711063058491, 'condition': 'checkout_win and not host_os == "win"', }, { - 'object_name': 'Win/clangd-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': '9761d932731e92673d65def879cdc1c14b8083a4d8a5d8da81a0bf3adf982192', - 'size_bytes': 13905628, - 'generation': 1752146036811142, + 'object_name': 'Win/clangd-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': 'fa919c060a1369d069a4f7b656a89f89393c7e08cfa96fd5bc85cb3208258ddf', + 'size_bytes': 13911060, + 'generation': 1753711055091772, 'condition': 'host_os == "win" and checkout_clangd', }, { - 'object_name': 'Win/llvm-code-coverage-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': '8e193cee359ca9ed8fa2a6502e15f6153fa31ac2c2b787876e42ac1dcd24d22e', - 'size_bytes': 2382660, - 'generation': 1752146037275180, + 'object_name': 'Win/llvm-code-coverage-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': '393704c1adb6b3b1202b46144ad7d081eb79f330700a4d109269722d680d52de', + 'size_bytes': 2383152, + 'generation': 1753711055387102, 'condition': 'host_os == "win" and checkout_clang_coverage_tools', }, { - 'object_name': 'Win/llvmobjdump-llvmorg-21-init-16348-gbd809ffb-13.tar.xz', - 'sha256sum': 'a723426c200b3155b89b7f86146fb112e8f25b1c91c193d34f6a33d5aa55478a', - 'size_bytes': 5653036, - 'generation': 1752146036966159, + 'object_name': 'Win/llvmobjdump-llvmorg-21-init-16348-gbd809ffb-14.tar.xz', + 'sha256sum': 'd1a654f0342bad3658ed9fab7a91a7726c6aa0af44a6e6c33da96ddf3602ece4', + 'size_bytes': 5649720, + 'generation': 1753711055166454, 'condition': '(checkout_linux or checkout_mac or checkout_android) and host_os == "win"', }, ] @@ -1178,7 +1182,7 @@ 'packages': [ { 'package': 'chromium/chrome/android/orderfiles/arm', - 'version': '-UfqtrMDupVAMUpEOK7jjF9yrmyu3TQwf-Yk_WEb5HUC', + 'version': 'LqEg53v50eg9OuVMCBD3ax3vYIa3PBs3c2Psrj6vsdYC', }, ], 'condition': 'checkout_android', @@ -1189,7 +1193,7 @@ 'packages': [ { 'package': 'chromium/chrome/android/orderfiles/arm64', - 'version': 'Z8Me43-75raz2apop1YPIalkpQi4Ns6D-bPxlUITRd8C', + 'version': '0WGQl_tlTi3EqkZ6H410yKCWWDiJ5IpykczR1x2beDwC', }, ], 'condition': 'checkout_android', @@ -1501,7 +1505,7 @@ 'packages': [ { 'package': 'chromium/third_party/enterprise_companion/chromium_linux64', - 'version': 'ZCtOlx4ypJ7QiZKG8NUwyBB97mjPft2Npr17QZHnM74C', + 'version': '5YXxv_zYLqsbROlMUQEaZxItwyBw1F5flG06wg_wsG4C', }, ], }, @@ -1512,7 +1516,7 @@ 'packages': [ { 'package': 'chromium/third_party/enterprise_companion/chromium_mac_amd64', - 'version': '5njOfQQ1_TAXswK6m8z0wp_uGveKpKAIdHwEDCel6rsC', + 'version': 'rIO14mTPU8UEHQm2UUz-aqqfbzu-BVbRC6b-GmMWq9EC', }, ], }, @@ -1523,7 +1527,7 @@ 'packages': [ { 'package': 'chromium/third_party/enterprise_companion/chromium_mac_arm64', - 'version': 'h5PLlHpe9Z2gNQJRR8cZ7PG1z4pLcHQ8pwtxQq92w9MC', + 'version': 'haQjbJqXSLZWs3HgL_t913AoIXFJLU62nAAaGrXmn40C', }, ], }, @@ -1592,12 +1596,12 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - '90e74033250d2ab075da14a221c397ee4c0e2599', + '0e2e94e036bdbd47b362c54b645e6da30ad2c932', 'condition': 'checkout_android and checkout_src_internal', }, 'src/docs/website': { - 'url': Var('chromium_git') + '/website.git' + '@' + '1906b0bbaa142dc87acd171137d5a234aeba74c8', + 'url': Var('chromium_git') + '/website.git' + '@' + 'c5d4064bbf9dc71893bfa60db578e8f92995bd57', }, 'src/ios/third_party/earl_grey2/src': { @@ -1606,7 +1610,7 @@ }, 'src/ios/third_party/edo/src': { - 'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git' + '@' + '4d0798b9c79a7c0d7d553603fde0453342c70878', + 'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git' + '@' + '5030b6a79c86f3b0787061f9c933b7476631efe3', 'condition': 'checkout_ios', }, @@ -2071,9 +2075,11 @@ 'src/third_party/crossbench': Var('chromium_git') + '/crossbench.git' + '@' + Var('crossbench_revision'), + 'src/third_party/crossbench-web-tests': + Var('chromium_git') + '/chromium/web-tests.git' + '@' + Var('crossbench_web_tests_revision'), 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '435e3b303e996904365d5e96579bfc793e113482', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1b44e27e7d4c63dfe83d10ed01d23824536e9811', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), @@ -3029,7 +3035,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/linux-amd64', - 'version': 'Sg25-ZztJqPtMF0rPXgRDM3-fKRveWcIbIvbwwKPAPUC', + 'version': '9cBfu6rdomrQL916sGYsOiSQUrWOuIR_jBjvm7ccFeQC', }, ], 'dep_type': 'cipd', @@ -3039,7 +3045,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/windows-amd64', - 'version': '0sP0Mad75mVvpE0TL1hNJaOPR1oZCkpx5K_zELfKe2YC', + 'version': 'vQKW52sc1DbdemqngJE-HRBoOAXzfbPJiltAV2gCcT8C', }, ], 'dep_type': 'cipd', @@ -3050,7 +3056,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/mac-amd64', - 'version': 'hZpj0hRnYNFplpUOcSd95nLfQDnWLU1-a_aTEzwQCjwC', + 'version': 'e7Uq2XB1IRBbp3a32o4m4yyU-eHdxmQ-2Kcp464UUoQC', }, ], 'dep_type': 'cipd', @@ -3061,7 +3067,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/mac-arm64', - 'version': 'ulgsu9nJXlCe-TeAX6PIPIIVr557_qKnjOjyPO9ib9UC', + 'version': 'mfzexptpGHu1w9xTtsm7-y3fgnZeDzkcRg99pgZm6XYC', }, ], 'dep_type': 'cipd', @@ -3187,7 +3193,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_deps/autorolled', - 'version': 'vlG4G37DLs353rVRwLE7CniAGEKiPqu0_EAeblkBZ4gC', + 'version': 'eVHebCnHTK7NZxoNjIIvqsRQO2Ifa7ANmnM3dJhFoBkC', }, ], 'condition': 'checkout_android and non_git_source', @@ -3680,7 +3686,7 @@ 'src/components/optimization_guide/internal': { 'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' + - 'c66de765dcd2a1b6a180828665edf21f1403a832', + 'dd2a4751e727af7bf8fb5baa1fb52e070f354c17', 'condition': 'checkout_src_internal', }, @@ -3746,7 +3752,7 @@ 'src/ios_internal': { 'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' + - '3039cfbc4bd2cbf1cf1f0425581e86e7394cc794', + '66f62a3e6eaa6a83cdb803293328c40ab5db35aa', 'condition': 'checkout_ios and checkout_src_internal', },
diff --git a/WATCHLISTS b/WATCHLISTS index 07d612d..8d83d60 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -3250,7 +3250,7 @@ 'vakh+safe_browsing_watch@chromium.org', 'xinghuilu+watch@chromium.org', 'zackhan+watch@chromium.org'], - 'sampling_profiler': ['thiabaud@google.com', 'spvw@chromium.org'], + 'sampling_profiler': ['thiabaud@google.com', 'spvm@chromium.org'], 'scanning': ['gavinwill+scanning-watch@chromium.org'], 'screen_ai': ['rhalavati+watch@chromium.org'], 'search_engine_choice_screen': ['chrome-waffle-eng+watch@google.com'],
diff --git a/ash/ash_prefs.cc b/ash/ash_prefs.cc index e54f993..5250336d 100644 --- a/ash/ash_prefs.cc +++ b/ash/ash_prefs.cc
@@ -155,7 +155,7 @@ DockedMagnifierController::RegisterProfilePrefs(registry); FeatureDiscoveryDurationReporterImpl::RegisterProfilePrefs(registry); FocusModeController::RegisterProfilePrefs(registry); - RegisterProfilePrefsFullRestore(registry, for_test); + RegisterProfilePrefsFullRestore(registry); FullscreenController::RegisterProfilePrefs(registry); GameDashboardController::RegisterProfilePrefs(registry); GeolocationController::RegisterProfilePrefs(registry);
diff --git a/ash/wm/test/wm_pixel_diff_test.cc b/ash/wm/test/wm_pixel_diff_test.cc index 5884f72..24644a8 100644 --- a/ash/wm/test/wm_pixel_diff_test.cc +++ b/ash/wm/test/wm_pixel_diff_test.cc
@@ -6,7 +6,9 @@ #include <utility> #include "ash/constants/ash_features.h" +#include "ash/constants/ash_pref_names.h" #include "ash/public/cpp/test/shell_test_api.h" +#include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "ash/test/ash_test_base.h" #include "ash/test/ash_test_util.h" @@ -166,6 +168,9 @@ } TEST_F(WmPixelDiffTest, InformedRestoreNoScreenshotDialog) { + ash::Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean( + prefs::kShowInformedRestoreOnboarding, false); + UpdateDisplay("1600x1000"); // Chrome apps are unique as they show tab info additionally. Create one
diff --git a/ash/wm/window_restore/informed_restore_test_base.cc b/ash/wm/window_restore/informed_restore_test_base.cc index 474bf4d1..8766079b 100644 --- a/ash/wm/window_restore/informed_restore_test_base.cc +++ b/ash/wm/window_restore/informed_restore_test_base.cc
@@ -4,6 +4,7 @@ #include "ash/wm/window_restore/informed_restore_test_base.h" +#include "ash/constants/ash_pref_names.h" #include "ash/session/test_session_controller_client.h" #include "components/account_id/account_id.h" @@ -29,7 +30,12 @@ ClearLogin(); - SimulateUserLogin({kTestUserEmail}); + auto account_id = SimulateUserLogin({kTestUserEmail}); + + // Disable the onboarding dialog for testing. + PrefService* prefs = + ash_test_helper()->prefs_provider()->GetUserPrefs(account_id); + prefs->SetBoolean(prefs::kShowInformedRestoreOnboarding, false); } } // namespace ash
diff --git a/ash/wm/window_restore/window_restore_util.cc b/ash/wm/window_restore/window_restore_util.cc index 927a382c..99543bb 100644 --- a/ash/wm/window_restore/window_restore_util.cc +++ b/ash/wm/window_restore/window_restore_util.cc
@@ -58,16 +58,13 @@ } // namespace -void RegisterProfilePrefsFullRestore(PrefRegistrySimple* registry, - bool for_test) { +void RegisterProfilePrefsFullRestore(PrefRegistrySimple* registry) { registry->RegisterIntegerPref( prefs::kRestoreAppsAndPagesPrefName, static_cast<int>(full_restore::RestoreOption::kAskEveryTime), user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); - // TODO(crbug.com/432562252): Fix tests that fail when this value is true. - registry->RegisterBooleanPref(prefs::kShowInformedRestoreOnboarding, - !for_test); + registry->RegisterBooleanPref(prefs::kShowInformedRestoreOnboarding, true); registry->RegisterIntegerPref(prefs::kInformedRestoreNudgeShownCount, 0); registry->RegisterTimePref(prefs::kInformedRestoreNudgeLastShown, base::Time());
diff --git a/ash/wm/window_restore/window_restore_util.h b/ash/wm/window_restore/window_restore_util.h index 166a828c..8a81d9e 100644 --- a/ash/wm/window_restore/window_restore_util.h +++ b/ash/wm/window_restore/window_restore_util.h
@@ -38,9 +38,7 @@ } // namespace full_restore // Registers the restore pref. -// TODO(crbug.com/432562252): Remove `for_test`. -ASH_EXPORT void RegisterProfilePrefsFullRestore(PrefRegistrySimple* registry, - bool for_test = false); +ASH_EXPORT void RegisterProfilePrefsFullRestore(PrefRegistrySimple* registry); // Returns true if the pref `kRestoreAppsAndPagesPrefName` exists. Otherwise, // returns false.
diff --git a/base/android/java/src/org/chromium/base/ApplicationStatus.java b/base/android/java/src/org/chromium/base/ApplicationStatus.java index 171498b..7ab3567 100644 --- a/base/android/java/src/org/chromium/base/ApplicationStatus.java +++ b/base/android/java/src/org/chromium/base/ApplicationStatus.java
@@ -41,8 +41,8 @@ /** * Provides information about the current activity's status, and a way to register / unregister * listeners for state changes. TODO(crbug.com/40411113): ApplicationStatus will not work on - * WebView/WebLayer, and should be moved out of base and into //chrome. It should not be relied upon - * for //components. + * WebView, and should be moved out of base and into //chrome. It should not be relied upon for + * //components. */ @NullMarked @JNINamespace("base::android")
diff --git a/base/android/java/src/org/chromium/base/BundleUtils.java b/base/android/java/src/org/chromium/base/BundleUtils.java index ccbdfce..3ed2dec 100644 --- a/base/android/java/src/org/chromium/base/BundleUtils.java +++ b/base/android/java/src/org/chromium/base/BundleUtils.java
@@ -215,7 +215,6 @@ // SplitCompat is installed on the application context, so check there for library paths // which were added to that ClassLoader. ClassLoader classLoader = ContextUtils.getApplicationContext().getClassLoader(); - // In WebLayer, the class loader will be a WrappedClassLoader. if (classLoader instanceof BaseDexClassLoader) { path = ((BaseDexClassLoader) classLoader).findLibrary(libraryName); } else if (classLoader instanceof WrappedClassLoader) {
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java index f5f6eac..e0e62648d 100644 --- a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java +++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java
@@ -65,7 +65,7 @@ * responsible for loading native libraries and running the main entry point of the service. * * <p>This class does not directly inherit from Service because the logic may be used by a Service - * implementation which cannot directly inherit from this class (e.g. for WebLayer child services). + * implementation which cannot directly inherit from this class. */ @JNINamespace("base::android") @SuppressWarnings("SynchronizeOnNonFinalField") // mMainThread assigned in onCreate().
diff --git a/base/metrics/histogram_base.h b/base/metrics/histogram_base.h index 4a214a0e..90738d12 100644 --- a/base/metrics/histogram_base.h +++ b/base/metrics/histogram_base.h
@@ -22,7 +22,6 @@ namespace base { -class Value; class HistogramBase; class HistogramSamples; class Pickle;
diff --git a/base/test/trace_event_analyzer.h b/base/test/trace_event_analyzer.h index 9789243..b112e6f1 100644 --- a/base/test/trace_event_analyzer.h +++ b/base/test/trace_event_analyzer.h
@@ -103,10 +103,6 @@ #include "base/trace_event/trace_event.h" #include "base/values.h" -namespace base { -class Value; -} - namespace trace_analyzer { class QueryNode;
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h index bcc3d8f..19ec986c 100644 --- a/base/threading/thread_restrictions.h +++ b/base/threading/thread_restrictions.h
@@ -447,12 +447,6 @@ namespace web { class WebMainLoop; } // namespace web -namespace weblayer { -class BrowserContextImpl; -class ContentBrowserClientImpl; -class ProfileImpl; -class WebLayerPathProvider; -} // namespace weblayer // NOTE: Please do not append entries here. Put them in the list above and keep // the list sorted. @@ -647,10 +641,6 @@ friend class ui::DrmDisplayHostManager; friend class ui::ScopedAllowBlockingForGbmSurface; friend class ui::SelectFileDialogLinux; - friend class weblayer::BrowserContextImpl; - friend class weblayer::ContentBrowserClientImpl; - friend class weblayer::ProfileImpl; - friend class weblayer::WebLayerPathProvider; #if BUILDFLAG(IS_MAC) friend class printing::PrintBackendServiceImpl; #endif
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h index 92500570..6c811af 100644 --- a/base/trace_event/builtin_categories.h +++ b/base/trace_event/builtin_categories.h
@@ -253,7 +253,6 @@ perfetto::Category("wayland"), perfetto::Category("webaudio").SetTags("audio"), perfetto::Category("webengine.fidl"), - perfetto::Category("weblayer"), perfetto::Category("WebCore"), perfetto::Category("webnn"), perfetto::Category("webrtc").SetTags("audio", "video"),
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index d61b895b..e1aa253 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -3892,6 +3892,7 @@ "*/AsyncTabParamsManagerImpl.kt", "//third_party/androidx/*", "*/webengine_shell_apk/*", + "local_modifications/pdf/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt", ] _found_kt = filter_exclude(_abs_kt_files, _kt_allowlist) assert(
diff --git a/buildtools/deps_revisions.gni b/buildtools/deps_revisions.gni index 2be6871..914a6e115 100644 --- a/buildtools/deps_revisions.gni +++ b/buildtools/deps_revisions.gni
@@ -5,5 +5,5 @@ declare_args() { # Used to cause full rebuilds on libc++ rolls. This should be kept in sync # with the libcxx_revision var in //DEPS. - libcxx_revision = "be9dd89ffbfdf2b6cf6b9c5ec076d6adbc5d25a6" + libcxx_revision = "c105b13e377d3fa5fdf033957f31abc1894a9968" }
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni index f4edd39..ea0efd4 100644 --- a/chrome/android/chrome_java_resources.gni +++ b/chrome/android/chrome_java_resources.gni
@@ -411,6 +411,7 @@ "java/res/drawable/tablet_tab_strip_close_all_tabs_context_menu.xml", "java/res/drawable/text_cursor_lowend.xml", "java/res/drawable/virtual_card_enrollment_illustration.xml", + "java/res/drawable/volume_up_24dp.xml", "java/res/drawable/xr_toolbar_rounded_rect.xml", "java/res/layout-sw600dp/find_toolbar.xml", "java/res/layout/account_chooser_dialog_item.xml",
diff --git a/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item_layout.xml b/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item_layout.xml index 3c7fdaa..34f6f30f 100644 --- a/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item_layout.xml +++ b/chrome/android/features/tab_ui/java/res/layout/tab_grid_card_item_layout.xml
@@ -88,6 +88,17 @@ android:ellipsize="none" android:singleLine="true" android:textAppearance="@style/TextAppearance.TextMediumThick.Primary"/> + <ImageView + android:id="@+id/media_indicator_icon" + android:src="@drawable/volume_up_24dp" + android:layout_height="@dimen/tab_grid_card_header_height" + android:layout_width="@dimen/tab_grid_card_header_height" + android:layout_gravity="center_vertical" + android:padding="@dimen/tab_grid_card_favicon_padding_start" + android:importantForAccessibility="no" + app:layout_constraintTop_toTopOf="@id/card_view" + app:layout_constraintEnd_toStartOf="@id/action_button" + android:visibility="gone"/> <org.chromium.chrome.browser.tab_ui.TabThumbnailView android:id="@+id/tab_thumbnail" android:layout_width="0dp"
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MoveTabUtils.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MoveTabUtils.java index 13f69a6..a545641 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MoveTabUtils.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MoveTabUtils.java
@@ -96,14 +96,30 @@ * Moves the specified group to the requested index. If the index is not a valid move operation, * it will calculate a valid index. * - * <p>If the requested index is inside the tab group, it will result in a no-op since the tab + * <p>{@param requestedIndex} is not the true destination index; it doesn't take into account + * the size of the moving tab group, and it will be modified to be a valid index (see below). + * + * <p>If the requested index is inside the source tab group, it will be a no-op since the tab * group is already in that index range. * * <p>If the requested index is a single tab, it will insert the tab group in that position. * - * <p>If the requestedIndex is inside a tab group, it will be placed adjacent to the destination - * group. This may result in a no-op if the source tab group is already adjacent to the - * destination group. + * <p>If the requestedIndex is inside a tab group, it will insert the tab group adjacent to the + * destination group. This may result in a no-op if the source tab group is already adjacent to + * the destination group. + * + * <p>"Insert" rounds away from the current location of the tab group. That is, if the tab group + * is being "inserted" to location i, if the tab group is moving left, the tab group will be + * inserted before the current contents of location i. If the tab group is moving right, the tab + * group will be inserted after the current contents of location i. + * + * <p>Taking a concrete example, if you have tabs D E [A B C], a requested index of 1 gives + * D [A B C] E and a requested index of 0 gives [A B C] D E. With a start state of [A B C] D E, + * a requested index of 3 (the current index of D) will result in the state D [A B C] E. [A B C] + * actually ends up at index 1, but that calling this method with a requested index of 1 would + * be a no-op because [A B C] already occupies index 1. If, because we wanted to move the tab + * group to the right of index D, we requested index 4 (current index of D plus 1), the tab + * group would actually move to after E (D E [A B C]). * * @param tabModel The current {@link TabModel}. * @param filter The current {@link TabGroupModelFilter}.
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridView.java index dfd1e00..3146682 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridView.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridView.java
@@ -24,8 +24,10 @@ import android.view.ViewGroup; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.ImageView; +import android.widget.TextView; import androidx.annotation.IntDef; +import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.content.res.ResourcesCompat; import androidx.core.widget.ImageViewCompat; import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat; @@ -33,6 +35,7 @@ import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.quick_delete.QuickDeleteAnimationGradientDrawable; +import org.chromium.chrome.browser.tab.Tab.MediaState; import org.chromium.chrome.browser.tasks.tab_management.TabListModel.AnimationStatus; import org.chromium.chrome.browser.tasks.tab_management.TabProperties.TabActionState; import org.chromium.chrome.tab_ui.R; @@ -222,6 +225,35 @@ cardWrapper.setBackground(gridCardHighlightDrawable); } + void setMediaIndicator(@MediaState int mediaState) { + // TODO(crbug.com/430072416): Update state for muted tabs once the setting controls are + // implemented. + TextView tabTitle = findViewById(R.id.tab_title); + ImageView tabMediaIndicator = findViewById(R.id.media_indicator_icon); + ConstraintLayout.LayoutParams titleParams = + (ConstraintLayout.LayoutParams) tabTitle.getLayoutParams(); + + int mediaIndicatorVisibility = View.GONE; + switch (mediaState) { + case MediaState.AUDIBLE: + titleParams.endToEnd = R.id.media_indicator_icon; + mediaIndicatorVisibility = View.VISIBLE; + break; + case MediaState.MUTED: + case MediaState.RECORDING: + case MediaState.SHARING: + case MediaState.NONE: + titleParams.endToEnd = R.id.card_view; + break; + default: + assert false : "Invalid media state"; + break; + } + + tabTitle.setLayoutParams(titleParams); + tabMediaIndicator.setVisibility(mediaIndicatorVisibility); + } + private void setTabActionButtonCloseDrawable() { assert mTabActionState != TabActionState.UNSET;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java index 72c58460..25fbb72 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinder.java
@@ -218,6 +218,8 @@ } else if (TabProperties.TAB_LONG_CLICK_LISTENER == propertyKey) { setNullableLongClickListener( model.get(TabProperties.TAB_LONG_CLICK_LISTENER), view, model); + } else if (TabProperties.MEDIA_INDICATOR == propertyKey) { + ((TabGridView) view).setMediaIndicator(model.get(TabProperties.MEDIA_INDICATOR)); } } @@ -535,6 +537,7 @@ TextView titleView = rootView.fastFindViewById(R.id.tab_title); TabThumbnailView thumbnail = rootView.fastFindViewById(R.id.tab_thumbnail); ChromeImageView backgroundView = rootView.fastFindViewById(R.id.background_view); + ImageView mediaIndicator = rootView.fastFindViewById(R.id.media_indicator_icon); cardView.getBackground().mutate(); final @ColorInt int backgroundColor = @@ -555,6 +558,10 @@ backgroundView, TabUiThemeProvider.getHoveredCardBackgroundTintList( backgroundView.getContext(), isIncognito, isSelected)); + + mediaIndicator.setImageTintList( + TabCardThemeUtil.getMediaIndicatorColorStateList( + mediaIndicator.getContext(), isIncognito, isSelected)); } private static void updateColorForSelectionToggleButton(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java index 4055f1ec..975419c9 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediator.java
@@ -329,7 +329,7 @@ } @Override - public void didMergeTabToGroup(Tab movedTab) { + public void didMergeTabToGroup(Tab movedTab, boolean isDestinationTab) { resetTabStrip(); } };
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java index e2153e1..f7ae3f3e5 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -57,6 +57,7 @@ import org.chromium.chrome.browser.collaboration.CollaborationServiceFactory; import org.chromium.chrome.browser.data_sharing.DataSharingServiceFactory; import org.chromium.chrome.browser.data_sharing.DataSharingTabManager; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; import org.chromium.chrome.browser.preferences.ChromeSharedPreferences; import org.chromium.chrome.browser.price_tracking.PriceTrackingFeatures; @@ -65,6 +66,7 @@ import org.chromium.chrome.browser.quick_delete.QuickDeleteAnimationGradientDrawable; import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.Tab.MediaState; import org.chromium.chrome.browser.tab.TabCreationState; import org.chromium.chrome.browser.tab.TabId; import org.chromium.chrome.browser.tab.TabLaunchType; @@ -644,6 +646,28 @@ updateFaviconForTab(model, tab, null, null); } } + + @Override + public void onMediaStateChanged(Tab updatedTab, @MediaState int mediaState) { + assert mShowingTabs; + + @Nullable PropertyModel model; + Tab representativeTab = updatedTab; + if (mActionsOnAllRelatedTabs && isTabInTabGroup(updatedTab)) { + @Nullable Pair<Integer, Tab> indexAndTab = + getIndexAndTabForTabGroupId(updatedTab.getTabGroupId()); + if (indexAndTab == null) return; + model = mModelList.get(indexAndTab.first).model; + representativeTab = indexAndTab.second; + } else { + model = mModelList.getModelFromTabId(updatedTab.getId()); + } + + if (model == null) return; + model.set( + TabProperties.MEDIA_INDICATOR, + getTabGridMediaIndicator(representativeTab)); + } }; private final TabGroupModelFilterObserver mTabGroupObserver = @@ -792,31 +816,24 @@ } @Override - public void didMergeTabToGroup(Tab movedTab) { + public void didMergeTabToGroup(Tab movedTab, boolean isDestinationTab) { assert mShowingTabs; TabGroupModelFilter filter = mCurrentTabGroupModelFilterSupplier.get(); assumeNonNull(filter); TabModel tabModel = filter.getTabModel(); if (mActionsOnAllRelatedTabs) { - // When merging Tab 1 to Tab 2 as a new group, or merging Tab 1 to an - // existing group 1, we can always find the current indexes of 1) Tab 1 - // and 2) Tab 2 or group 1 in the model. The method - // getIndexesForMergeToGroup() returns these two ids by using Tab 1's - // related Tabs, which have been updated in - // TabModel. List<Tab> relatedTabs = getRelatedTabsForId(movedTab.getId()); Pair<Integer, Integer> positions = - mModelList.getIndexesForMergeToGroup(tabModel, relatedTabs); + mModelList.getIndexesForMergeToGroup( + tabModel, movedTab, isDestinationTab, relatedTabs); int srcIndex = positions.second; int desIndex = positions.first; - // If only the desIndex is valid then the movedTab was already part of - // another group and is not present in the model. This happens only during - // an undo. - // Refresh just the desIndex tab card in the model. The removal of the - // movedTab from its previous group was already handled by - // didMoveTabOutOfGroup. + // If only the desIndex is valid then just update the destination index as + // this is either a group of size 1 being created or a tab is being moved + // between groups. In the latter case the group moved from will be updated + // by TabGroupModelFilterObserver#didMoveTabOutOfGroup(Tab, int). if (desIndex != TabModel.INVALID_TAB_INDEX && srcIndex == TabModel.INVALID_TAB_INDEX) { Tab tab = @@ -833,13 +850,9 @@ return; } + // We merged the source group to the destination group. Remove the source + // group and update the destination group. mModelList.removeAt(srcIndex); - if (getRelatedTabsForId(movedTab.getId()).size() == 2) { - // When users use drop-to-merge to create a group. - RecordUserAction.record("TabGroup.Created.DropToMerge"); - } else { - RecordUserAction.record("TabGrid.Drag.DropToMerge"); - } desIndex = srcIndex > desIndex ? desIndex @@ -848,6 +861,15 @@ filter.getRepresentativeTabAt( mModelList.getTabCardCountsBefore(desIndex)); updateTab(desIndex, newSelectedTabInMergedGroup, true, false); + + // TODO(crbug.com/434246302): These metrics are probably wrong as it looks + // like they get emitted per-tab merged, rather than per-group merged. + if (getRelatedTabsForId(movedTab.getId()).size() == 2) { + // When users use drop-to-merge to create a group. + RecordUserAction.record("TabGroup.Created.DropToMerge"); + } else { + RecordUserAction.record("TabGrid.Drag.DropToMerge"); + } } else { // If no tab is present we can't check if the added tab is part of the // current group. Assume it isn't since a group state with 0 tab should be @@ -1716,6 +1738,7 @@ model.set(TabProperties.IS_SELECTED, isTabSelected); model.set(TabProperties.SHOULD_SHOW_PRICE_DROP_TOOLTIP, false); model.set(TabProperties.TITLE, getLatestTitleForTab(tab, /* useDefault= */ true)); + model.set(TabProperties.MEDIA_INDICATOR, getTabGridMediaIndicator(tab)); bindTabActionStateProperties(model.get(TabProperties.TAB_ACTION_STATE), tab, model); @@ -1748,6 +1771,26 @@ return filter.isTabInTabGroup(tab); } + private @MediaState int getTabGridMediaIndicator(Tab representativeTab) { + if (!DeviceFormFactor.isNonMultiDisplayContextOnTablet(mActivity) + || !ChromeFeatureList.sMediaIndicatorsAndroid.isEnabled()) { + return MediaState.NONE; + } + + if (!mActionsOnAllRelatedTabs || !isTabInTabGroup(representativeTab)) { + return representativeTab.getMediaState(); + } + List<Tab> relatedTabs = getRelatedTabsForId(representativeTab.getId()); + @MediaState int state; + for (Tab tab : relatedTabs) { + state = tab.getMediaState(); + if (state != MediaState.NONE) { + return state; + } + } + return MediaState.NONE; + } + /** * @return The callback that hosts the logic for swipe and drag related actions. */ @@ -2090,6 +2133,7 @@ QuickDeleteAnimationStatus.TAB_RESTORE) .with(TabProperties.VISIBILITY, View.VISIBLE) .with(TabProperties.USE_SHRINK_CLOSE_ANIMATION, false) + .with(TabProperties.MEDIA_INDICATOR, getTabGridMediaIndicator(tab)) .build(); if (!mActionsOnAllRelatedTabs || isInTabGroup) { @@ -2664,8 +2708,7 @@ Tab tab = getTabForIndex(index); // If the found tab has a different group ID from the tabGroupId set in the args then the - // update - // is likely for a group that no longer exists so we should drop the update. + // update is likely for a group that no longer exists so we should drop the update. if (tab == null || !tabGroupId.equals(tab.getTabGroupId()) || !filter.isTabInTabGroup(tab)) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java index 9eecb4e8..ae0859e 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java
@@ -337,28 +337,32 @@ } /** - * This method gets indexes in the {@link TabListModel} of the two tabs that are merged into one - * group. When moving a Tab to a group, we always put it at the end of the group. For example: - * move tab1 to tab2 to form a group, tab1 is after tab2 in the TabModel (tab2, tab1); Then - * move another Tab tab3 to (tab2, tab1) group, tab3 is after tab1, (tab2, tab1, tab3). Thus, - * the last Tab in the related Tabs is the movedTab. When merging groups merge group1 to group2 - * then the tab will exist in (group2, group1) order. However it is not guaranteed that the - * tab representing group1 in this model will be the last tab in the group. To account for this - * start at the front of the group in TabModel index order to find the desIndex of the group or - * tab to merge to. Then search the rest of the tabs that were merged for srcIndex that was - * merged from. For undoing multi-group merges the srcIndex may be invalid while the desIndex is - * always valid as the tab may be moving between existing groups and so has no index in this - * model of its own. + * This method gets indexes in the {@link TabListModel} of the tab cards that are merged into a + * group. This should always produce a valid destination index which is the index in the {@link + * TabListModel} that the moved tab should exist in. The source index may be invalid if a group + * of size 1 is created or the tab was moved between groups. In the case of moving between + * groups as the other group will be updated by {@link + * TabGroupModelFilterObserver#didMoveTabOutOfGroup(Tab, int)}. * - * @param tabModel The tabModel that owns the tabs. - * @param tabs The list that contains tabs of the newly merged group. - * @return A Pair with its first member as the index of the tab that is selected to merge to and - * the second member as the index of the tab that is being merged from. + * @param tabModel The tabModel that owns the tabs. + * @param movedTab The tab that is being merged. + * @param isDestinationTab Whether the moved tab is being merged to the group or is the + * destination. + * @param tabs The list that contains tabs of the newly merged group. + * @return A Pair with its first member as the index that is merged to and the the second member + * as the index that is being merged from. */ - Pair<Integer, Integer> getIndexesForMergeToGroup(TabModel tabModel, List<Tab> tabs) { - int srcIndex = TabModel.INVALID_TAB_INDEX; - int desIndex = TabModel.INVALID_TAB_INDEX; + Pair<Integer, Integer> getIndexesForMergeToGroup( + TabModel tabModel, Tab movedTab, boolean isDestinationTab, List<Tab> tabs) { + // The moved tab is always involved in the merge, but it may not have an index if it was + // moved between groups. + int movedTabListModelIndex = indexFromTabId(movedTab.getId()); + // TODO(crbug.com/433947821): The use of TabModel here is probably overkill. Consider + // iterating through just tabs. + + // Find the other index that is involved in the merge it should be in the list of tabs. + int otherTabListModelIndex = TabModel.INVALID_TAB_INDEX; int startIndex = tabModel.indexOf(tabs.get(0)); int endIndex = tabModel.indexOf(tabs.get(tabs.size() - 1)); // Ensure the last tab is last in the model and the first tab is the first. @@ -367,14 +371,34 @@ Tab curTab = tabModel.getTabAtChecked(i); // Group should be contiguous. assert tabs.contains(curTab); - int index = indexFromTabId(curTab.getId()); - if (index != TabModel.INVALID_TAB_INDEX && desIndex == TabModel.INVALID_TAB_INDEX) { - desIndex = index; - } else if (index != TabModel.INVALID_TAB_INDEX - && srcIndex == TabModel.INVALID_TAB_INDEX) { - srcIndex = index; - break; - } + if (curTab == movedTab) continue; + + otherTabListModelIndex = indexFromTabId(curTab.getId()); + if (otherTabListModelIndex != TabModel.INVALID_TAB_INDEX) break; + } + + // If nothing is found in the model early return, this might be a case of tab group undo. + if (movedTabListModelIndex == TabModel.INVALID_TAB_INDEX + && otherTabListModelIndex == TabModel.INVALID_TAB_INDEX) { + return new Pair<>(TabModel.INVALID_TAB_INDEX, TabModel.INVALID_TAB_INDEX); + } + + final int desIndex; + final int srcIndex; + if (isDestinationTab || otherTabListModelIndex == TabModel.INVALID_TAB_INDEX) { + // We allow failing to find the other index as it might be a case of tab group undo + // which has a intermediate sequencing and model updates that can result in failing to + // find the tab among the related tabs. + + // The moved tab is the destination tab and should always be in the model. + assert movedTabListModelIndex != TabModel.INVALID_TAB_INDEX; + + desIndex = movedTabListModelIndex; + srcIndex = otherTabListModelIndex; + } else { + // The other tab is the destination tab and should always be in the model. + desIndex = otherTabListModelIndex; + srcIndex = movedTabListModelIndex; } return new Pair<>(desIndex, srcIndex); }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManager.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManager.java index 620d8184..5cea0e1 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManager.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManager.java
@@ -81,7 +81,7 @@ private final TabGroupModelFilterObserver mTabGroupModelFilterObserver = new TabGroupModelFilterObserver() { @Override - public void didMergeTabToGroup(Tab movedTab) { + public void didMergeTabToGroup(Tab movedTab, boolean isDestinationTab) { maybeUpdateForTab(movedTab, /* mayAddDot= */ true); } };
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManagerUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManagerUnitTest.java index 82a61cd..cc7c770 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManagerUnitTest.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManagerUnitTest.java
@@ -230,7 +230,9 @@ createDirtyTabMessageForIds(List.of(EXISTING_TAB_ID)); when(mTab.getTabGroupId()).thenReturn(null); - mTabGroupModelFilterObserverCaptor.getValue().didMergeTabToGroup(mTab); + mTabGroupModelFilterObserverCaptor + .getValue() + .didMergeTabToGroup(mTab, /* isDestinationTab= */ false); verifyHidden(); }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java index 9bfe4f41..b27098d 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java
@@ -170,6 +170,9 @@ public static final WritableObjectPropertyKey<String> TAB_GROUP_SYNC_ID = new WritableObjectPropertyKey<>(); + /** The {@link org.chromium.chrome.browser.tab.TabImpl.MediaState} indicator of the tab. */ + public static final WritableIntPropertyKey MEDIA_INDICATOR = new WritableIntPropertyKey(); + private static final PropertyKey[] COMMON_KEYS_TAB_AND_GROUP_GRID = new PropertyKey[] { IS_INCOGNITO, @@ -208,7 +211,8 @@ SHOULD_SHOW_PRICE_DROP_TOOLTIP, HAS_NOTIFICATION_BUBBLE, TAB_CARD_LABEL_DATA, - IS_HIGHLIGHTED + IS_HIGHLIGHTED, + MEDIA_INDICATOR }, COMMON_KEYS_TAB_AND_GROUP_GRID);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/archived_tabs_auto_delete_promo/ArchivedTabsAutoDeletePromoManager.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/archived_tabs_auto_delete_promo/ArchivedTabsAutoDeletePromoManager.java index 93d5e8b..01da1bec 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/archived_tabs_auto_delete_promo/ArchivedTabsAutoDeletePromoManager.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/archived_tabs_auto_delete_promo/ArchivedTabsAutoDeletePromoManager.java
@@ -113,6 +113,7 @@ * 6. There is at least one tab in the archive. */ private boolean checkConditions() { + if (mTabArchiveSettings.isInTestingMode()) return true; return ChromeFeatureList.sAndroidTabDeclutterAutoDelete.isEnabled() && ChromeFeatureList.sAndroidTabDeclutterAutoDeleteKillSwitch.isEnabled() && !mTabArchiveSettings.getAutoDeleteDecisionMade()
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinderUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinderUnitTest.java index cc98b08..c0332c8e 100644 --- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinderUnitTest.java +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinderUnitTest.java
@@ -57,6 +57,7 @@ import org.chromium.base.test.util.Features.DisableFeatures; import org.chromium.base.test.util.Features.EnableFeatures; import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.tab.Tab.MediaState; import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData; import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData.PriceDrop; import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider.TabFavicon; @@ -84,6 +85,7 @@ @Mock private TabThumbnailView mThumbnailView; @Mock private FrameLayout mTabGroupColorViewContainer; @Mock private ImageView mFaviconView; + @Mock private ImageView mMediaIndicatorView; @Mock private ViewStub mTabCardLabelStub; @Mock private TabCardLabelView mTabCardLabelView; @Mock private ImageView mActionButton; @@ -122,6 +124,8 @@ when(mViewGroup.fastFindViewById(R.id.tab_group_color_view_container)) .thenReturn(mTabGroupColorViewContainer); when(mViewGroup.fastFindViewById(R.id.tab_favicon)).thenReturn(mFaviconView); + when(mViewGroup.fastFindViewById(R.id.media_indicator_icon)) + .thenReturn(mMediaIndicatorView); when(mViewGroup.fastFindViewById(R.id.price_info_box_outer)).thenReturn(mPriceCardView); when(mViewGroup.fastFindViewById(R.id.tab_card_label_stub)).thenReturn(mTabCardLabelStub); when(mViewGroup.fastFindViewById(R.id.action_button)).thenReturn(mActionButton); @@ -136,6 +140,7 @@ .when(mTabCardLabelStub) .inflate(); when(mFaviconView.getContext()).thenReturn(mContext); + when(mMediaIndicatorView.getContext()).thenReturn(mContext); when(mViewGroup.getContext()).thenReturn(mContext); when(mViewGroup.getResources()).thenReturn(mContext.getResources()); @@ -504,6 +509,15 @@ verify(mActionButton).setOnTouchListener(isA(OnPeripheralClickListener.class)); } + @Test + @EnableFeatures(ChromeFeatureList.MEDIA_INDICATORS_ANDROID) + public void testMediaIndicator() { + mModel.set(TabProperties.MEDIA_INDICATOR, MediaState.AUDIBLE); + TabGridViewBinder.bindTab(mModel, mViewGroup, TabProperties.MEDIA_INDICATOR); + + verify(mViewGroup).setMediaIndicator(eq(MediaState.AUDIBLE)); + } + private void assertImageMatrix( ArgumentCaptor<Matrix> matrixCaptor, float expectedScale, float expectedTrans) { float[] matValues = new float[9];
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java index ee92812..9ba8f868 100644 --- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java
@@ -964,7 +964,9 @@ doReturn(tabs).when(mTabGroupModelFilter).getRelatedTabList(TAB1_ID); doReturn(true).when(mTabGroupModelFilter).isTabInTabGroup(mTab1); doReturn(new Token(1L, TAB2_ROOT_ID)).when(mTab1).getTabGroupId(); - mTabGroupModelFilterObserverArgumentCaptor.getValue().didMergeTabToGroup(mTab1); + mTabGroupModelFilterObserverArgumentCaptor + .getValue() + .didMergeTabToGroup(mTab1, /* isDestinationTab= */ true); verifyResetStrip(true, tabs); } @@ -973,7 +975,9 @@ public void uiNotVisibleAfterMergeNonCurrentTabToGroup() { initAndAssertProperties(mTab1); - mTabGroupModelFilterObserverArgumentCaptor.getValue().didMergeTabToGroup(mTab3); + mTabGroupModelFilterObserverArgumentCaptor + .getValue() + .didMergeTabToGroup(mTab3, /* isDestinationTab= */ false); verify(mResetHandler, never()).resetGridWithListOfTabs(any()); }
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java index 1ea8422..d488988 100644 --- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java +++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -122,6 +122,7 @@ import org.chromium.chrome.browser.signin.services.IdentityServicesProvider; import org.chromium.chrome.browser.tab.MockTab; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.Tab.MediaState; import org.chromium.chrome.browser.tab.TabCreationState; import org.chromium.chrome.browser.tab.TabLaunchType; import org.chromium.chrome.browser.tab.TabObserver; @@ -1129,7 +1130,9 @@ .thenReturn(Arrays.asList(mTab1, mTab2)); when(mTabModel.indexOf(mTab1)).thenReturn(POSITION1); when(mTabModel.indexOf(mTab2)).thenReturn(POSITION2); - mTabGroupModelFilterObserverCaptor.getValue().didMergeTabToGroup(mTab2); + mTabGroupModelFilterObserverCaptor + .getValue() + .didMergeTabToGroup(mTab2, /* isDestinationTab= */ false); assertThat(mModelList.size(), equalTo(3)); mFakeViewHolder1 = prepareFakeViewHolder(mItemView1, 0); @@ -1154,7 +1157,9 @@ when(mTabGroupModelFilter.getRelatedTabList(TAB3_ID)).thenReturn(Arrays.asList(tab3, tab4)); when(mTabModel.indexOf(tab3)).thenReturn(2); when(mTabModel.indexOf(tab4)).thenReturn(3); - mTabGroupModelFilterObserverCaptor.getValue().didMergeTabToGroup(tab4); + mTabGroupModelFilterObserverCaptor + .getValue() + .didMergeTabToGroup(tab4, /* isDestinationTab= */ false); assertThat(mModelList.size(), equalTo(2)); mFakeViewHolder1 = prepareFakeViewHolder(mItemView1, 0); @@ -1176,7 +1181,9 @@ when(mTabGroupModelFilter.getRelatedTabList(TAB3_ID)) .thenReturn(Arrays.asList(mTab1, mTab2, tab3, tab4)); - mTabGroupModelFilterObserverCaptor.getValue().didMergeTabToGroup(tab3); + mTabGroupModelFilterObserverCaptor + .getValue() + .didMergeTabToGroup(tab3, /* isDestinationTab= */ false); assertThat(mModelList.size(), equalTo(1)); } @@ -1772,7 +1779,9 @@ assertNotNull(mModelList.get(0).model.get(TabProperties.FAVICON_FETCHER)); assertNotNull(mModelList.get(1).model.get(TabProperties.FAVICON_FETCHER)); - mTabGroupModelFilterObserverCaptor.getValue().didMergeTabToGroup(mTab1); + mTabGroupModelFilterObserverCaptor + .getValue() + .didMergeTabToGroup(mTab1, /* isDestinationTab= */ true); assertThat(mModelList.size(), equalTo(1)); assertThat(mModelList.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB1_ID)); @@ -1800,7 +1809,9 @@ assertNotNull(mModelList.get(1).model.get(TabProperties.FAVICON_FETCHER)); mTabGroupModelFilter.setTabGroupTitle(TAB_GROUP_ID, CUSTOMIZED_DIALOG_TITLE1); - mTabGroupModelFilterObserverCaptor.getValue().didMergeTabToGroup(mTab1); + mTabGroupModelFilterObserverCaptor + .getValue() + .didMergeTabToGroup(mTab1, /* isDestinationTab= */ true); assertThat(mModelList.size(), equalTo(1)); assertThat(mModelList.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB1_ID)); @@ -1827,7 +1838,9 @@ createTabGroup(List.of(mTab1, mTab2), TAB1_ID, TAB_GROUP_ID); when(mTabGroupModelFilter.getGroupLastShownTabId(TAB_GROUP_ID)).thenReturn(TAB1_ID); - mTabGroupModelFilterObserverCaptor.getValue().didMergeTabToGroup(mTab2); + mTabGroupModelFilterObserverCaptor + .getValue() + .didMergeTabToGroup(mTab2, /* isDestinationTab= */ false); assertThat(mModelList.size(), equalTo(2)); assertThat(mModelList.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB1_ID)); @@ -1851,7 +1864,9 @@ createTabGroup(List.of(mTab2), TAB2_ID, new Token(7, 9)); - mTabGroupModelFilterObserverCaptor.getValue().didMergeTabToGroup(mTab2); + mTabGroupModelFilterObserverCaptor + .getValue() + .didMergeTabToGroup(mTab2, /* isDestinationTab= */ false); assertThat(mModelList.size(), equalTo(1)); assertThat(mModelList.get(0).model.get(TabProperties.TAB_ID), equalTo(TAB1_ID)); @@ -2657,7 +2672,9 @@ mTabGroupModelFilterObserverCaptor.getValue().didMoveTabOutOfGroup(tab4, POSITION1); assertThat(mModelList.size(), equalTo(2)); - mTabGroupModelFilterObserverCaptor.getValue().didMergeTabToGroup(tab4); + mTabGroupModelFilterObserverCaptor + .getValue() + .didMergeTabToGroup(tab4, /* isDestinationTab= */ false); assertThat(mModelList.size(), equalTo(2)); assertThat(mModelList.indexFromTabId(TAB1_ID), equalTo(0)); @@ -3027,7 +3044,9 @@ doReturn(POSITION1).when(mTabGroupModelFilter).representativeIndexOf(mTab1); doReturn(POSITION1).when(mTabGroupModelFilter).representativeIndexOf(mTab2); - mTabGroupModelFilterObserverCaptor.getValue().didMergeTabToGroup(mTab2); + mTabGroupModelFilterObserverCaptor + .getValue() + .didMergeTabToGroup(mTab2, /* isDestinationTab= */ false); assertEquals( mTab1Domain + ", " + mTab2Domain, mModelList.get(POSITION1).model.get(TabProperties.URL_DOMAIN)); @@ -3068,7 +3087,9 @@ setUpTabListMediator(TabListMediatorType.TAB_GRID_DIALOG, TabListMode.GRID); verify(mTab2, times(1)).addObserver(mTabObserverCaptor.getValue()); - mTabGroupModelFilterObserverCaptor.getValue().didMergeTabToGroup(mTab2); + mTabGroupModelFilterObserverCaptor + .getValue() + .didMergeTabToGroup(mTab2, /* isDestinationTab= */ false); assertEquals(mTab1Domain, mModelList.get(POSITION1).model.get(TabProperties.URL_DOMAIN)); assertEquals(mTab2Domain, mModelList.get(POSITION2).model.get(TabProperties.URL_DOMAIN)); verify(mTab2, times(2)).addObserver(mTabObserverCaptor.getValue()); @@ -3105,7 +3126,9 @@ List<Tab> tabs = new ArrayList<>(Arrays.asList(mTab1, mTab2)); createTabGroup(tabs, TAB1_ID, TAB_GROUP_ID); - mTabGroupModelFilterObserverCaptor.getValue().didMergeTabToGroup(mTab2); + mTabGroupModelFilterObserverCaptor + .getValue() + .didMergeTabToGroup(mTab2, /* isDestinationTab= */ false); assertEquals( mTab1Domain + ", " + mTab2Domain, mModelList.get(POSITION1).model.get(TabProperties.URL_DOMAIN)); @@ -5107,6 +5130,36 @@ assertEquals(0, mModelList.size()); } + @Test + @EnableFeatures(ChromeFeatureList.MEDIA_INDICATORS_ANDROID) + public void testMediaState_TabAudible() { + // Simulate tablet + when(mResources.getInteger(R.integer.min_screen_width_bucket)).thenReturn(2); + assertEquals(MediaState.NONE, mModelList.get(0).model.get(TabProperties.MEDIA_INDICATOR)); + + when(mTab1.getMediaState()).thenReturn(MediaState.AUDIBLE); + mTabObserverCaptor.getValue().onMediaStateChanged(mTab1, MediaState.AUDIBLE); + + assertEquals( + MediaState.AUDIBLE, mModelList.get(0).model.get(TabProperties.MEDIA_INDICATOR)); + } + + @Test + @EnableFeatures(ChromeFeatureList.MEDIA_INDICATORS_ANDROID) + public void testMediaState_TabNone() { + // Simulate tablet + when(mResources.getInteger(R.integer.min_screen_width_bucket)).thenReturn(2); + when(mTab1.getMediaState()).thenReturn(MediaState.AUDIBLE); + mTabObserverCaptor.getValue().onMediaStateChanged(mTab1, MediaState.AUDIBLE); + assertEquals( + MediaState.AUDIBLE, mModelList.get(0).model.get(TabProperties.MEDIA_INDICATOR)); + + when(mTab1.getMediaState()).thenReturn(MediaState.NONE); + mTabObserverCaptor.getValue().onMediaStateChanged(mTab1, MediaState.NONE); + + assertEquals(MediaState.NONE, mModelList.get(0).model.get(TabProperties.MEDIA_INDICATOR)); + } + private void setUpTabGroupCardDescriptionString() { doAnswer( invocation -> {
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java index a64b2b2a..0621762 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
@@ -1373,6 +1373,11 @@ return mMediator.getRestoringStateSupplier(); } + @Override + public List<String> getFeedUrls() { + return mMediator.getFeedUrls(); + } + private int getLateralPaddingsPx() { return mActivity .getResources()
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java index 15dde83..efd3b45a 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
@@ -83,6 +83,7 @@ import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.mojom.WindowOpenDisposition; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -892,6 +893,10 @@ return mIsLoadingFeed; } + public List<String> getFeedUrls() { + return (mCurrentStream != null) ? mCurrentStream.getFeedUrls() : new ArrayList<String>(); + } + /** Unbinds the stream and clear all the stream's contents. */ private void unbindStream() { unbindStream(false, false);
diff --git a/chrome/android/java/res/drawable/volume_up_24dp.xml b/chrome/android/java/res/drawable/volume_up_24dp.xml new file mode 100644 index 0000000..a045624 --- /dev/null +++ b/chrome/android/java/res/drawable/volume_up_24dp.xml
@@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="960" + android:viewportHeight="960" + android:tint="?attr/colorControlNormal" + android:autoMirrored="true"> + <path + android:fillColor="@android:color/white" + android:pathData="M560,829L560,747Q650,721 705,647Q760,573 760,479Q760,385 705,311Q650,237 560,211L560,129Q684,157 762,254.5Q840,352 840,479Q840,606 762,703.5Q684,801 560,829ZM120,600L120,360L280,360L480,160L480,800L280,600L120,600ZM560,640L560,318Q607,340 633.5,384Q660,428 660,480Q660,531 633.5,574.5Q607,618 560,640ZM400,354L314,440L200,440L200,520L314,520L400,606L400,354ZM300,480L300,480L300,480L300,480L300,480L300,480Z"/> +</vector>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java index e28781f..6964a22 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java
@@ -519,7 +519,7 @@ } if (StripLayoutUtils.shouldApplyMoreDensity()) { - applySingleThemeOverlay(R.style.ThemeOverlay_BrowserUI_ToolbarButtonSizeDesktop); + applySingleThemeOverlay(R.style.ThemeOverlay_BrowserUI_DesktopDensity); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/OmahaServiceStartDelayer.java b/chrome/android/java/src/org/chromium/chrome/browser/OmahaServiceStartDelayer.java index 6b07359..ea91602 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/OmahaServiceStartDelayer.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/OmahaServiceStartDelayer.java
@@ -13,26 +13,31 @@ import org.chromium.base.ContextUtils; import org.chromium.base.ThreadUtils; +import org.chromium.build.annotations.EnsuresNonNullIf; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.omaha.OmahaBase; /** - * Class to delay interactions with other classes based on whether the app is in the foreground - * or not and whether the device is considered interactive. Useful for running actions that should - * not occur while the phone's screen is off, i.e. when the user expects the device to be sleeping. + * Class to delay interactions with other classes based on whether the app is in the foreground or + * not and whether the device is considered interactive. Useful for running actions that should not + * occur while the phone's screen is off, i.e. when the user expects the device to be sleeping. * - * This class does not do any system monitoring on its own, and requires the owner to invoke + * <p>This class does not do any system monitoring on its own, and requires the owner to invoke * {@link #onForegroundSessionStart()} and {@link #onForegroundSessionEnd()} when the foreground * session starts and ends respectively. * - * The class also verifies that the screen is on at the time of trying to schedule the delayed task - * as well as right before trying to execute the task, in the case where the screen is turned off, - * but the app is still considered to be in the foreground. + * <p>The class also verifies that the screen is on at the time of trying to schedule the delayed + * task as well as right before trying to execute the task, in the case where the screen is turned + * off, but the app is still considered to be in the foreground. * - * If the app is first in the foreground, leading to a scheduled task, then goes to the background, - * before coming to the foreground again in quick succession, the timer should be reset. + * <p>If the app is first in the foreground, leading to a scheduled task, then goes to the + * background, before coming to the foreground again in quick succession, the timer should be reset. * - * When conditions are right, executes code in {@link OmahaServiceStartDelayer.OmahaRunnable#run()}. + * <p>When conditions are right, executes code in {@link + * OmahaServiceStartDelayer.OmahaRunnable#run()}. */ +@NullMarked public class OmahaServiceStartDelayer { /** * @return true iff the device is currently considered to be in an interactive state. @@ -53,7 +58,7 @@ private final Handler mHandler = new Handler(Looper.getMainLooper()); private Runnable mOmahaRunnable = OmahaBase::onForegroundSessionStart; - private Runnable mRunnableTask; + private @Nullable Runnable mRunnableTask; /** See {@link ChromeActivitySessionTracker#onForegroundSessionStart()}. */ public void onForegroundSessionStart() { @@ -97,6 +102,7 @@ * @return True if there is currently a Runnable that is waiting for execution. */ @VisibleForTesting + @EnsuresNonNullIf("mRunnableTask") boolean hasRunnableController() { return mRunnableTask != null; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java index 24e25df..e1dc1f6d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -9,6 +9,7 @@ import static org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutUtils.MAX_TAB_WIDTH_DP; import static org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutUtils.MIN_TAB_WIDTH_DP; import static org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutUtils.TAB_OVERLAP_WIDTH_DP; +import static org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutUtils.findGroupTitle; import static org.chromium.chrome.browser.tasks.tab_management.TabUiThemeUtil.FOLIO_FOOT_LENGTH_DP; import android.animation.Animator; @@ -174,19 +175,30 @@ // Visibility Constants private static final float NEW_TAB_BUTTON_BACKGROUND_Y_OFFSET_DP = 3.f; - private static final float NEW_TAB_BUTTON_CLICK_SLOP_DP = 8.f; private static final float NEW_TAB_BUTTON_BACKGROUND_WIDTH_DP = 32.f; private static final float NEW_TAB_BUTTON_BACKGROUND_HEIGHT_DP = 32.f; - private static final float BUTTON_DESIRED_TOUCH_TARGET_SIZE = 48.f; // Desired spacing between new tab button and tabs when tab strip is not full. - private static final float NEW_TAB_BUTTON_X_OFFSET_TOWARDS_TABS = 4.f; private static final float DESIRED_PADDING_BETWEEN_NEW_TAB_BUTTON_AND_TABS = 2.f; private static final float NEW_TAB_BUTTON_DEFAULT_PRESSED_OPACITY = 0.2f; private static final float NEW_TAB_BUTTON_HOVER_BACKGROUND_PRESSED_OPACITY = 0.12f; private static final float NEW_TAB_BUTTON_HOVER_BACKGROUND_DEFAULT_OPACITY = 0.08f; static final float FADE_FULL_OPACITY_THRESHOLD_DP = 24.f; - private static final float NEW_TAB_BUTTON_WITH_MODEL_SELECTOR_BUTTON_PADDING = 8.f; + + // Values adapt based on whether the device is desktop or tablet. + private static final boolean IS_DESKTOP_DENSITY = StripLayoutUtils.shouldApplyMoreDensity(); + private static final float BUTTON_DESIRED_TOUCH_TARGET_SIZE = + IS_DESKTOP_DENSITY ? NEW_TAB_BUTTON_BACKGROUND_WIDTH_DP : 48.f; + private static final float NEW_TAB_BUTTON_CLICK_SLOP_DP = + (BUTTON_DESIRED_TOUCH_TARGET_SIZE - NEW_TAB_BUTTON_BACKGROUND_WIDTH_DP) / 2; + // On tablets (48dp button touch target), we can't fully shift the NTB left without affecting + // touch target, so we apply a small actual offset(4dp) and also rely on a visual shift(6dp) in + // the CC layer instead. On desktop (32dp touch target), we have more room to apply a real + // offset(10dp) directly. No more visual offset needed for desktop. + private static final float NEW_TAB_BUTTON_X_OFFSET_TOWARDS_TABS = + IS_DESKTOP_DENSITY ? 10.f : 4.f; + private static final float NEW_TAB_BUTTON_WITH_MODEL_SELECTOR_BUTTON_PADDING = + IS_DESKTOP_DENSITY ? 24.f : 8.f; private static final int MESSAGE_UPDATE_SPINNER = 1; private static final int MESSAGE_HOVER_CARD = 2; @@ -236,7 +248,7 @@ } @Override - public void didMergeTabToGroup(Tab movedTab) { + public void didMergeTabToGroup(Tab movedTab, boolean isDestinationTab) { // TODO(crbug.com/375047646): Investigate kicking off animations here. Token tabGroupId = movedTab.getTabGroupId(); assumeNonNull(tabGroupId); @@ -894,8 +906,10 @@ * @return Visual offset of new tab button icon. */ protected float getNtbVisualOffsetHorizontal() { - return (BUTTON_DESIRED_TOUCH_TARGET_SIZE - mNewTabButtonWidth) / 2 - - DESIRED_PADDING_BETWEEN_NEW_TAB_BUTTON_AND_TABS; + return Math.max( + (BUTTON_DESIRED_TOUCH_TARGET_SIZE - mNewTabButtonWidth) / 2 + - DESIRED_PADDING_BETWEEN_NEW_TAB_BUTTON_AND_TABS, + 0); } /** @@ -4511,9 +4525,10 @@ populateVisibleViews(mStripGroupTitles, mStripGroupTitlesToRender); } - private float adjustNewTabButtonOffsetIfFull(float offset) { + private float adjustNewTabButtonOffsetIfNotFull(float offset) { if (!isTabStripFull()) { - // Move NTB close to tabs by 4 dp when tab strip is not full. + // Move NTB close to tabs by 4 dp(for tablet) or 10dp(for desktop) when tab strip is not + // full. boolean isLtr = !LocalizationUtils.isLayoutRtl(); offset += MathUtils.flipSignIf(NEW_TAB_BUTTON_X_OFFSET_TOWARDS_TABS, isLtr); } @@ -4531,7 +4546,7 @@ boolean rtl = LocalizationUtils.isLayoutRtl(); float offset = getStartPositionForStripViews() + MathUtils.flipSignIf(viewsWidth, rtl); if (rtl) offset += getCachedTabWidth() - mNewTabButtonWidth; - offset = adjustNewTabButtonOffsetIfFull(offset); + offset = adjustNewTabButtonOffsetIfNotFull(offset); CompositorAnimator animator = CompositorAnimator.ofFloatProperty( @@ -4563,7 +4578,7 @@ mRightMargin, mWidth, mNewTabButtonWidth); - offset = adjustNewTabButtonOffsetIfFull(offset); + offset = adjustNewTabButtonOffsetIfNotFull(offset); // 3. Hide the new tab button if it's not visible on the screen. boolean isRtl = LocalizationUtils.isLayoutRtl();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java index c98e0b41..a624883 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
@@ -179,8 +179,12 @@ private static final float MODEL_SELECTOR_BUTTON_BACKGROUND_HEIGHT_DP = 32.f; private static final float MODEL_SELECTOR_BUTTON_HOVER_BACKGROUND_PRESSED_OPACITY = 0.12f; private static final float MODEL_SELECTOR_BUTTON_HOVER_BACKGROUND_DEFAULT_OPACITY = 0.08f; - private static final float MODEL_SELECTOR_BUTTON_CLICK_SLOP_DP = 12.f; - private static final float BUTTON_DESIRED_TOUCH_TARGET_SIZE = 48.f; + private static final float BUTTON_DESIRED_TOUCH_TARGET_SIZE = + StripLayoutUtils.shouldApplyMoreDensity() + ? MODEL_SELECTOR_BUTTON_BACKGROUND_WIDTH_DP + : 48.f; + private static final float MODEL_SELECTOR_BUTTON_CLICK_SLOP_DP = + (BUTTON_DESIRED_TOUCH_TARGET_SIZE - MODEL_SELECTOR_BUTTON_BACKGROUND_WIDTH_DP) / 2; // Tab strip transition constants. @VisibleForTesting @@ -639,7 +643,6 @@ startupInfo.createdIncognitoTabOnStartup); } - // Incognito button for Tab Strip Redesign. private void createModelSelectorButton( Context context, StripLayoutViewOnClickHandler selectorClickHandler, @@ -659,7 +662,7 @@ R.drawable.ic_incognito, MODEL_SELECTOR_BUTTON_CLICK_SLOP_DP); - // Tab strip redesign button bg size is 32 * 32. + // Button bg size is 32 * 32. mModelSelectorButton.setBackgroundResourceId(R.drawable.bg_circle_tab_strip_button); mModelSelectorWidth = MODEL_SELECTOR_BUTTON_BACKGROUND_WIDTH_DP;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java index 4cdbfba..1d0f4858 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutTab.java
@@ -857,7 +857,7 @@ float dpToPx = getDpToPx(); TypedArray closeAttributes = mContext.obtainStyledAttributes( - new int[] {R.attr.toolbarButtonWidth, R.attr.toolbarButtonHeight}); + new int[] {R.attr.closeButtonWidth, R.attr.closeButtonHeight}); int widthPx = closeAttributes.getDimensionPixelSize(0, 0); int heightPx = closeAttributes.getDimensionPixelSize(1, 0); closeAttributes.recycle();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/display_cutout/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/display_cutout/OWNERS new file mode 100644 index 0000000..4bf0e5b --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/display_cutout/OWNERS
@@ -0,0 +1,9 @@ +# Primary +wenyufu@chromium.org +clhager@google.com + +# Back up +twellington@chromium.org + +# Original owners before 2025 +file://media/OWNERS
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java index adc3d9b..2d4a99a1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaBase.java
@@ -4,6 +4,8 @@ package org.chromium.chrome.browser.omaha; +import static org.chromium.build.NullUtil.assumeNonNull; + import android.content.SharedPreferences; import android.text.format.DateUtils; @@ -16,6 +18,9 @@ import org.chromium.base.StreamUtil; import org.chromium.base.ThreadUtils; import org.chromium.base.version_info.VersionInfo; +import org.chromium.build.annotations.EnsuresNonNullIf; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.net.ChromiumNetworkAdapter; import org.chromium.net.NetworkTrafficAnnotationTag; @@ -35,38 +40,42 @@ * Keeps tabs on the current state of Chrome, tracking if and when a request should be sent to the * Omaha Server. * - * When Chrome is brought to the foreground, it will trigger a call to - * {@link OmahaBase#onForegroundSessionStart}, which kicks off a series of scheduled events - * that allow the class to run. A single alarm is used to trigger the whole pipeline when needed. - * - If Chrome isn't running when the alarm is fired, no pings or update checks will be performed. - * - If Chrome doesn't have a pending request to POST, no POST will be performed. + * <p>When Chrome is brought to the foreground, it will trigger a call to {@link + * OmahaBase#onForegroundSessionStart}, which kicks off a series of scheduled events that allow the + * class to run. A single alarm is used to trigger the whole pipeline when needed. - If Chrome isn't + * running when the alarm is fired, no pings or update checks will be performed. - If Chrome doesn't + * have a pending request to POST, no POST will be performed. * - * When a fresh install is detected (or the user clears their data), OmahaBase will send an XML + * <p>When a fresh install is detected (or the user clears their data), OmahaBase will send an XML * request saying that a new install was detected, then follow up with an XML request saying that * the user was active and that we need to check for Chrome updates. * - * mevissen suggested being conservative with our timers for sending requests. - * POST attempts that fail to be acknowledged by the server are re-attempted, with at least - * one hour between each attempt. + * <p>mevissen suggested being conservative with our timers for sending requests. POST attempts that + * fail to be acknowledged by the server are re-attempted, with at least one hour between each + * attempt. * - * Status is saved directly to the the disk after every run of the pipeline. + * <p>Status is saved directly to the the disk after every run of the pipeline. * - * Implementation notes: + * <p>Implementation notes: * http://docs.google.com/a/google.com/document/d/1scTCovqASf5ktkOeVj8wFRkWTCeDYw2LrOBNn05CDB0/edit */ +@NullMarked public class OmahaBase { // Used in various org.chromium.chrome.browser.omaha files. static final String TAG = "omaha"; /** Version config data structure. */ public static class VersionConfig { - public final String latestVersion; - public final String downloadUrl; + public final @Nullable String latestVersion; + public final @Nullable String downloadUrl; public final int serverDate; - public final String updateStatus; + public final @Nullable String updateStatus; protected VersionConfig( - String latestVersion, String downloadUrl, int serverDate, String updateStatus) { + @Nullable String latestVersion, + @Nullable String downloadUrl, + int serverDate, + @Nullable String updateStatus) { this.latestVersion = latestVersion; this.downloadUrl = downloadUrl; this.serverDate = serverDate; @@ -123,12 +132,12 @@ private boolean mStateHasBeenRestored; // State saved written to and read from disk. - private RequestData mCurrentRequest; + private @Nullable RequestData mCurrentRequest; private long mTimestampOfInstall; private long mTimestampForNextPostAttempt; private long mTimestampForNewRequest; - private String mInstallSource; - protected VersionConfig mVersionConfig; + private @Nullable String mInstallSource; + protected @Nullable VersionConfig mVersionConfig; protected boolean mSendInstallEvent; // Request failure error code. @@ -172,7 +181,7 @@ String installSource = mDelegate.isInSystemImage() ? INSTALL_SOURCE_SYSTEM : INSTALL_SOURCE_ORGANIC; RequestData currentRequest = - createRequestData(false, currentTimestamp, null, installSource); + createRequestData(false, currentTimestamp, /* persistedID= */ null, installSource); String sessionID = mDelegate.generateUUID(); long timestampOfInstall = OmahaPrefUtils.getSharedPreferences() @@ -298,17 +307,21 @@ } protected boolean generateAndPostRequest(long currentTimestamp, String sessionID) { + assert mCurrentRequest != null; mVersionConfig = generateAndPostRequest( currentTimestamp, sessionID, mCurrentRequest, mTimestampOfInstall); return mVersionConfig != null; } - protected VersionConfig generateAndPostRequest( + protected @Nullable VersionConfig generateAndPostRequest( long currentTimestamp, String sessionID, RequestData currentRequest, long timestampOfInstall) { + RequestGenerator requestGenerator = getRequestGenerator(); + assumeNonNull(requestGenerator); + try { // Generate the XML for the current request. long installAgeInDays = @@ -317,21 +330,18 @@ timestampOfInstall, currentRequest.isSendInstallEvent()); String xml = - getRequestGenerator() - .generateXML( - sessionID, - getInstalledVersion(), - installAgeInDays, - mVersionConfig == null - ? UNKNOWN_DATE - : mVersionConfig.serverDate, - currentRequest); + requestGenerator.generateXML( + sessionID, + getInstalledVersion(), + installAgeInDays, + mVersionConfig == null ? UNKNOWN_DATE : mVersionConfig.serverDate, + currentRequest); // Send the request to the server & wait for a response. String response = postRequest(currentTimestamp, xml); // Parse out the response. - String appId = getRequestGenerator().getAppId(); + String appId = requestGenerator.getAppId(); ResponseParser parser = new ResponseParser(appId, currentRequest.isSendInstallEvent()); return parser.parseResponse(response); } catch (RequestFailureException e) { @@ -382,15 +392,15 @@ mDelegate.onRegisterNewRequestDone(mTimestampForNewRequest, mTimestampForNextPostAttempt); } - private RequestData createRequestData(long currentTimestamp, String persistedID) { + private RequestData createRequestData(long currentTimestamp, @Nullable String persistedID) { return createRequestData(mSendInstallEvent, currentTimestamp, persistedID, mInstallSource); } private RequestData createRequestData( boolean sendInstallEvent, long currentTimestamp, - String persistedID, - String installSource) { + @Nullable String persistedID, + @Nullable String installSource) { // If we're sending a persisted event, keep trying to send the same request ID. String requestID; if (persistedID == null || INVALID_REQUEST_ID.equals(persistedID)) { @@ -401,6 +411,7 @@ return new RequestData(sendInstallEvent, currentTimestamp, requestID, installSource); } + @EnsuresNonNullIf("mCurrentRequest") private boolean hasRequest() { return mCurrentRequest != null; } @@ -418,6 +429,7 @@ urlConnection.setFixedLengthStreamingMode( ApiCompatibilityUtils.getBytesUtf8(xml).length); if (mSendInstallEvent && getBackoffScheduler().getNumFailedAttempts() > 0) { + assumeNonNull(mCurrentRequest); String age = Long.toString(mCurrentRequest.getAgeInSeconds(timestamp)); urlConnection.addRequestProperty("X-RequestAge", age); } @@ -455,7 +467,7 @@ setting: 'This feature cannot be disabled.' }"""); try { - URL url = new URL(getRequestGenerator().getServerUrl()); + URL url = new URL(assumeNonNull(getRequestGenerator()).getServerUrl()); HttpURLConnection connection = (HttpURLConnection) ChromiumNetworkAdapter.openConnection(url, annotation); connection.setConnectTimeout(MS_CONNECTION_TIMEOUT); @@ -557,7 +569,7 @@ mDelegate.onSaveStateDone(mTimestampForNewRequest, mTimestampForNextPostAttempt); } - private RequestGenerator getRequestGenerator() { + private @Nullable RequestGenerator getRequestGenerator() { return mDelegate.getRequestGenerator(); } @@ -635,7 +647,8 @@ } } - static void setVersionConfig(SharedPreferences.Editor editor, VersionConfig versionConfig) { + static void setVersionConfig( + SharedPreferences.Editor editor, @Nullable VersionConfig versionConfig) { editor.putString( OmahaPrefUtils.PREF_LATEST_VERSION, versionConfig == null ? "" : versionConfig.latestVersion);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaDelegate.java index 336d4d3..ac67002 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaDelegate.java
@@ -15,14 +15,20 @@ OmahaDelegate() {} - /** @return Whether Chrome is installed as part of the system image. */ + /** + * @return Whether Chrome is installed as part of the system image. + */ abstract boolean isInSystemImage(); - /** @return The scheduler used to trigger jobs. */ + /** + * @return The scheduler used to trigger jobs. + */ abstract ExponentialBackoffScheduler getScheduler(); - /** @return The {@link RequestGenerator} used to create Omaha XML. */ - final RequestGenerator getRequestGenerator() { + /** + * @return The {@link RequestGenerator} used to create Omaha XML. + */ + final @Nullable RequestGenerator getRequestGenerator() { if (mRequestGenerator == null) mRequestGenerator = createRequestGenerator(); return mRequestGenerator; } @@ -41,12 +47,13 @@ abstract void scheduleService(long currentTimestampMs, long nextTimestampMs); /** Creates a {@link RequestGenerator}. */ - abstract RequestGenerator createRequestGenerator(); + abstract @Nullable RequestGenerator createRequestGenerator(); /** * Called when {@link OmahaBase#registerNewRequest} finishes. + * * @param timestampRequestMs When the next active user request should be generated. - * @param timestampPostMs Earliest time the next POST should be allowed. + * @param timestampPostMs Earliest time the next POST should be allowed. */ void onRegisterNewRequestDone(long timestampRequestMs, long timestampPostMs) {}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaDelegateBase.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaDelegateBase.java index 6d2ed76..39f71870 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaDelegateBase.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaDelegateBase.java
@@ -8,15 +8,16 @@ import android.content.pm.ApplicationInfo; import android.os.PowerManager; -import androidx.annotation.Nullable; - import org.chromium.base.ApplicationStatus; import org.chromium.base.ContextUtils; import org.chromium.base.ServiceLoaderUtil; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import java.util.UUID; /** Delegates calls out from the OmahaClient. */ +@NullMarked public abstract class OmahaDelegateBase extends OmahaDelegate { private final ExponentialBackoffScheduler mScheduler;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaService.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaService.java index f9572cd2..491d7f62 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaService.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/OmahaService.java
@@ -8,13 +8,13 @@ import android.app.job.JobService; import android.content.Context; -import androidx.annotation.Nullable; - import org.chromium.base.ContextUtils; import org.chromium.base.Log; import org.chromium.base.task.AsyncTask; 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.components.background_task_scheduler.BackgroundTask; import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory; import org.chromium.components.background_task_scheduler.TaskIds; @@ -22,9 +22,10 @@ import org.chromium.components.background_task_scheduler.TaskParameters; /** - * Manages scheduling and running of the Omaha client code. - * Delegates out to either an {@link IntentService} or {@link JobService}, as necessary. + * Manages scheduling and running of the Omaha client code. Delegates out to either an {@link + * IntentService} or {@link JobService}, as necessary. */ +@NullMarked public class OmahaService extends OmahaBase implements BackgroundTask { private static class OmahaClientDelegate extends OmahaDelegateBase { @Override @@ -43,7 +44,7 @@ } private static final Object DELEGATE_LOCK = new Object(); - private static OmahaService sInstance; + private static @Nullable OmahaService sInstance; private static boolean sHasPendingJob; public static @Nullable OmahaService getInstance() { @@ -53,7 +54,7 @@ } } - private AsyncTask<Void> mJobServiceTask; + private @Nullable AsyncTask<Void> mJobServiceTask; /** Used only by {@link BackgroundTaskScheduler}. */ public OmahaService() { @@ -85,7 +86,7 @@ } @Override - public void onPostExecute(Void result) { + public void onPostExecute(@Nullable Void result) { callback.taskFinished(false); } }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestData.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestData.java index 72cde983..ae403a4b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestData.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestData.java
@@ -5,21 +5,24 @@ package org.chromium.chrome.browser.omaha; import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; /** - * Represents parameters for a single XML request to send to the server. - * Persisted requests (those that must be resent in case of failure) should use the same ID from - * the first failed attempt. + * Represents parameters for a single XML request to send to the server. Persisted requests (those + * that must be resent in case of failure) should use the same ID from the first failed attempt. */ @NullMarked public class RequestData { private final long mCreationTimestamp; private final boolean mSendInstallEvent; private final String mRequestID; - private final String mInstallSource; + private final @Nullable String mInstallSource; public RequestData( - boolean sendInstallEvent, long timeStamp, String requestID, String installSource) { + boolean sendInstallEvent, + long timeStamp, + String requestID, + @Nullable String installSource) { assert requestID != null; mSendInstallEvent = sendInstallEvent; mCreationTimestamp = timeStamp; @@ -56,10 +59,10 @@ } /** - * Get the install source for the APK. Values can include - * {@link OmahaClient#INSTALL_SOURCE_SYSTEM} or {@link OmahaClient#INSTALL_SOURCE_ORGANIC}. + * Get the install source for the APK. Values can include {@link + * OmahaClient#INSTALL_SOURCE_SYSTEM} or {@link OmahaClient#INSTALL_SOURCE_ORGANIC}. */ - public String getInstallSource() { + public @Nullable String getInstallSource() { return mInstallSource; } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/ResponseParser.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/ResponseParser.java index ecec93e..29258f6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/ResponseParser.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/ResponseParser.java
@@ -7,13 +7,18 @@ import android.text.TextUtils; import org.chromium.base.Log; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.omaha.OmahaBase.VersionConfig; import org.chromium.chrome.browser.omaha.XMLParser.Node; /** * Parses XML responses from the Omaha Update Server. * + * <pre> + * * Expects XML formatted like: + * * <?xml version="1.0" encoding="UTF-8"?> * <daystart elapsed_days="4804" elapsed_seconds="65524"/> * <app appid="{appid}" status="ok"> @@ -35,8 +40,11 @@ * </app> * </response> * + * </pre> + * * The appid is dependent on the variant of Chrome that is running. */ +@NullMarked public class ResponseParser { private static final String TAG = "ResponseParser"; @@ -57,13 +65,13 @@ private final boolean mExpectUpdatecheck; private final boolean mStrictParsingMode; - private Integer mDaystartSeconds; - private Integer mDaystartDays; - private String mAppStatus; + private @Nullable Integer mDaystartSeconds; + private @Nullable Integer mDaystartDays; + private @Nullable String mAppStatus; - private String mUpdateStatus; - private String mNewVersion; - private String mUrl; + private @Nullable String mUpdateStatus; + private @Nullable String mNewVersion; + private @Nullable String mUrl; private boolean mParsedInstallEvent; private boolean mParsedPing; @@ -111,19 +119,19 @@ return mDaystartDays; } - public String getNewVersion() { + public @Nullable String getNewVersion() { return mNewVersion; } - public String getURL() { + public @Nullable String getURL() { return mUrl; } - public String getAppStatus() { + public @Nullable String getAppStatus() { return mAppStatus; } - public String getUpdateStatus() { + public @Nullable String getUpdateStatus() { return mUpdateStatus; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java index 76253db5..7c1b9c8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java
@@ -7,38 +7,49 @@ import android.app.Activity; import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import org.jni_zero.CalledByNative; import org.jni_zero.JniType; import org.jni_zero.NativeMethods; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherImpl; +import org.chromium.chrome.browser.fullscreen.BrowserControlsManager; import org.chromium.chrome.browser.fullscreen.BrowserControlsManagerSupplier; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabmodel.TabModelSelectorSupplier; import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.modaldialog.DialogDismissalCause; +import org.chromium.ui.modaldialog.ModalDialogManager; /** JNI call glue between the native password manager CredentialLeak class and Java objects. */ +@NullMarked public class CredentialLeakDialogBridge { private long mNativeCredentialLeakDialogViewAndroid; private final PasswordManagerDialogCoordinator mCredentialLeakDialog; private final WindowAndroid mWindowAndroid; private CredentialLeakDialogBridge( - @NonNull WindowAndroid windowAndroid, long nativeCredentialLeakDialogViewAndroid) { + WindowAndroid windowAndroid, long nativeCredentialLeakDialogViewAndroid) { mNativeCredentialLeakDialogViewAndroid = nativeCredentialLeakDialogViewAndroid; mWindowAndroid = windowAndroid; + ModalDialogManager modalDialogManager = windowAndroid.getModalDialogManager(); + assert modalDialogManager != null; + Activity activity = windowAndroid.getActivity().get(); + assert activity != null; + + BrowserControlsManager browserControlsManager = + BrowserControlsManagerSupplier.getValueOrNullFrom(windowAndroid); + assert browserControlsManager != null; mCredentialLeakDialog = new PasswordManagerDialogCoordinator( - windowAndroid.getModalDialogManager(), - windowAndroid.getActivity().get().findViewById(android.R.id.content), - BrowserControlsManagerSupplier.getValueOrNullFrom(windowAndroid)); + modalDialogManager, + activity.findViewById(android.R.id.content), + browserControlsManager); } @CalledByNative @@ -78,7 +89,7 @@ String credentialLeakDetails, int illustrationId, String positiveButton, - String negativeButton) { + @Nullable String negativeButton) { return new PasswordManagerDialogContents( credentialLeakTitle, credentialLeakDetails,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncher.java index ec37139..aa0377a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncher.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordCheckupLauncher.java
@@ -7,14 +7,14 @@ import android.content.Intent; import android.net.Uri; -import androidx.annotation.Nullable; - import org.jni_zero.CalledByNative; import org.jni_zero.JniType; import org.chromium.base.ServiceLoaderUtil; import org.chromium.base.supplier.ObservableSupplier; import org.chromium.base.supplier.ObservableSupplierImpl; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.safety_check.SafetyCheckSettingsFragment; import org.chromium.chrome.browser.settings.SettingsCustomTabLauncherImpl; @@ -24,12 +24,15 @@ import org.chromium.ui.modaldialog.ModalDialogManager; /** A utitily class for launching the password leak check. */ +@NullMarked public class PasswordCheckupLauncher { @CalledByNative private static void launchCheckupOnlineWithWindowAndroid( @JniType("std::string") String checkupUrl, WindowAndroid windowAndroid) { if (windowAndroid.getContext().get() == null) return; // Window not available yet/anymore. - launchCheckupOnlineWithActivity(checkupUrl, windowAndroid.getActivity().get()); + Activity activity = windowAndroid.getActivity().get(); + assert activity != null; + launchCheckupOnlineWithActivity(checkupUrl, activity); } @CalledByNative @@ -92,7 +95,9 @@ WindowAndroid windowAndroid) { ObservableSupplierImpl<ModalDialogManager> modalDialogManagerSupplier = new ObservableSupplierImpl<>(); - modalDialogManagerSupplier.set(windowAndroid.getModalDialogManager()); + ModalDialogManager modalDialogManager = windowAndroid.getModalDialogManager(); + assert modalDialogManager != null; + modalDialogManagerSupplier.set(modalDialogManager); return modalDialogManagerSupplier; } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogCoordinator.java index f91929c..785d4593 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogCoordinator.java
@@ -13,6 +13,8 @@ import android.view.LayoutInflater; import android.view.View; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider; import org.chromium.ui.modaldialog.DialogDismissalCause; @@ -25,9 +27,10 @@ * The coordinator for the password manager illustration modal dialog. Manages the sub-component * objects. */ +@NullMarked public class PasswordManagerDialogCoordinator { private final PasswordManagerDialogMediator mMediator; - private PropertyModel mModel; + private @Nullable PropertyModel mModel; public PasswordManagerDialogCoordinator( ModalDialogManager modalDialogManager,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogMediator.java index 1f9f42b20..040d25187 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogMediator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogMediator.java
@@ -12,6 +12,9 @@ import org.chromium.base.Callback; import org.chromium.base.task.PostTask; import org.chromium.base.task.TaskTraits; +import org.chromium.build.annotations.MonotonicNonNull; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider; import org.chromium.chrome.browser.modaldialog.ChromeTabModalPresenter; @@ -21,15 +24,16 @@ import org.chromium.ui.modelutil.PropertyModel; /** Mediator class responsible for the logic of showing the password manager dialog. */ +@NullMarked class PasswordManagerDialogMediator implements View.OnLayoutChangeListener { private final ModalDialogManager mDialogManager; private final View mAndroidContentView; private final BrowserControlsStateProvider mBrowserControlsStateProvider; private final PropertyModel.Builder mHostDialogModelBuilder; - private PropertyModel mHostDialogModel; - private PropertyModel mModel; - private Resources mResources; + private @MonotonicNonNull PropertyModel mHostDialogModel; + private @MonotonicNonNull PropertyModel mModel; + private @MonotonicNonNull Resources mResources; private @ModalDialogManager.ModalDialogType int mDialogType; private static class DialogClickHandler implements ModalDialogProperties.Controller { @@ -134,6 +138,7 @@ } void showDialog() { + assert mModel != null; mModel.set( ILLUSTRATION_VISIBLE, hasSufficientSpaceForIllustration(mAndroidContentView.getHeight())); @@ -146,7 +151,7 @@ mAndroidContentView.removeOnLayoutChangeListener(this); } - public PropertyModel getModelForTesting() { + public @Nullable PropertyModel getModelForTesting() { return mModel; } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerErrorMessageHelperBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerErrorMessageHelperBridge.java index ac2be93b..126241b8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerErrorMessageHelperBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerErrorMessageHelperBridge.java
@@ -13,6 +13,7 @@ import org.chromium.base.IntentUtils; import org.chromium.base.TimeUtils; import org.chromium.base.metrics.RecordHistogram; +import org.chromium.build.annotations.NullMarked; import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; import org.chromium.chrome.browser.preferences.ChromeSharedPreferences; import org.chromium.chrome.browser.preferences.Pref; @@ -26,6 +27,7 @@ import org.chromium.components.signin.base.CoreAccountInfo; import org.chromium.components.signin.identitymanager.ConsentLevel; import org.chromium.components.signin.identitymanager.IdentityManager; +import org.chromium.components.sync.SyncService; import org.chromium.components.sync.TrustedVaultUserActionTriggerForUMA; import org.chromium.components.user_prefs.UserPrefs; import org.chromium.ui.base.WindowAndroid; @@ -33,6 +35,7 @@ import java.util.concurrent.TimeUnit; /** The bridge provides a way to interact with the Android sign in flow. */ +@NullMarked public class PasswordManagerErrorMessageHelperBridge { @VisibleForTesting static final long MINIMAL_INTERVAL_BETWEEN_PROMPTS_MS = @@ -106,14 +109,16 @@ */ @CalledByNative static void startUpdateAccountCredentialsFlow(WindowAndroid windowAndroid, Profile profile) { + IdentityManager identityManager = + IdentityServicesProvider.get().getIdentityManager(profile); + assert identityManager != null : "Regular profile should have an IdentityManager"; final CoreAccountInfo primaryAccountInfo = - IdentityServicesProvider.get() - .getIdentityManager(profile) - .getPrimaryAccountInfo(ConsentLevel.SIGNIN); + identityManager.getPrimaryAccountInfo(ConsentLevel.SIGNIN); // If the account has been removed before calling this method, there are no credentials to // update. if (primaryAccountInfo == null) return; final Activity activity = windowAndroid.getActivity().get(); + assert activity != null : "Activity should not be null"; AccountManagerFacadeProvider.getInstance() .updateCredentials( CoreAccountInfo.getAndroidAccountFrom(primaryAccountInfo), @@ -133,11 +138,13 @@ WindowAndroid windowAndroid, Profile profile, @TrustedVaultUserActionTriggerForUMA int trustedVaultUserActionTriggerForUMA) { - final CoreAccountInfo primaryAccountInfo = - SyncServiceFactory.getForProfile(profile).getAccountInfo(); + SyncService syncService = SyncServiceFactory.getForProfile(profile); + assert syncService != null; + final CoreAccountInfo primaryAccountInfo = syncService.getAccountInfo(); // If the account has been removed before calling this method, there is nothing to do. if (primaryAccountInfo == null) return; final Activity activity = windowAndroid.getActivity().get(); + assert activity != null; TrustedVaultClient.get() .createKeyRetrievalIntent(primaryAccountInfo)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerLauncher.java index d05603b..b2435941 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerLauncher.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerLauncher.java
@@ -4,11 +4,16 @@ package org.chromium.chrome.browser.password_manager; +import static org.chromium.build.NullUtil.assertNonNull; +import static org.chromium.build.NullUtil.assumeNonNull; + +import android.app.Activity; import android.content.Context; import org.jni_zero.CalledByNative; import org.chromium.base.supplier.Supplier; +import org.chromium.build.annotations.NullMarked; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.settings.SettingsCustomTabLauncherImpl; import org.chromium.chrome.browser.sync.SyncServiceFactory; @@ -19,6 +24,7 @@ import org.chromium.ui.modaldialog.ModalDialogManager; /** Bridge between Java and native PasswordManager code. */ +@NullMarked public class PasswordManagerLauncher { private PasswordManagerLauncher() {} @@ -42,7 +48,7 @@ SyncService syncService = SyncServiceFactory.getForProfile(profile); String account = PasswordManagerHelper.hasChosenToSyncPasswords(syncService) - ? CoreAccountInfo.getEmailFrom(syncService.getAccountInfo()) + ? CoreAccountInfo.getEmailFrom(assumeNonNull(syncService).getAccountInfo()) : null; PasswordManagerHelper.getForProfile(originalProfile) .showPasswordSettings( @@ -61,11 +67,13 @@ boolean managePasskeys) { WindowAndroid window = webContents.getTopLevelNativeWindow(); if (window == null) return; + Activity context = window.getActivity().get(); + assert context != null; showPasswordSettings( - window.getActivity().get(), + context, Profile.fromWebContents(webContents), referrer, - () -> window.getModalDialogManager(), + () -> assertNonNull(window.getModalDialogManager()), managePasskeys); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java index ad5001c..5de34e2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
@@ -4,6 +4,7 @@ package org.chromium.chrome.browser.password_manager.settings; +import static org.chromium.build.NullUtil.assumeNonNull; import static org.chromium.chrome.browser.password_manager.PasswordMetricsUtil.PASSWORD_SETTINGS_EXPORT_METRICS_ID; import android.app.Activity; @@ -30,6 +31,7 @@ import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.supplier.ObservableSupplier; import org.chromium.base.supplier.ObservableSupplierImpl; +import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; import org.chromium.chrome.browser.password_check.PasswordCheck; @@ -65,6 +67,7 @@ * * <p>TODO: crbug.com/372657804 - Make sure that the PasswordSettings is not created in UPM M4.1 */ +@NullMarked public class PasswordSettings extends ChromeBaseSettingsFragment implements PasswordListObserver, Preference.OnPreferenceClickListener, @@ -123,12 +126,12 @@ private @TrustedVaultBannerState int mTrustedVaultBannerState = TrustedVaultBannerState.NOT_SHOWN; - private MenuItem mHelpItem; - private MenuItem mSearchItem; + private @Nullable MenuItem mHelpItem; + private @Nullable MenuItem mSearchItem; - private String mSearchQuery; - private Preference mLinkPref; - private Menu mMenu; + private @Nullable String mSearchQuery; + private @Nullable Preference mLinkPref; + private @Nullable Menu mMenu; private @Nullable PasswordCheck mPasswordCheck; private @ManagePasswordsReferrer int mManagePasswordsReferrer; @@ -153,12 +156,17 @@ @Override public FragmentManager getFragmentManager() { - return PasswordSettings.this.getFragmentManager(); + FragmentManager fragmentManager = + PasswordSettings.this.getFragmentManager(); + assert fragmentManager != null; + return fragmentManager; } @Override public int getViewId() { - return getView().getId(); + View view = getView(); + assert view != null; + return view.getId(); } @Override @@ -176,8 +184,9 @@ setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getStyledContext())); PasswordManagerHandlerProvider.getForProfile(getProfile()).addObserver(this); - if (SyncServiceFactory.getForProfile(getProfile()) != null) { - SyncServiceFactory.getForProfile(getProfile()).addSyncStateChangedListener(this); + SyncService syncService = SyncServiceFactory.getForProfile(getProfile()); + if (syncService != null) { + syncService.addSyncStateChangedListener(this); } setHasOptionsMenu(true); // Password Export might be optional but Search is always present. @@ -197,7 +206,7 @@ } private @ManagePasswordsReferrer int getReferrerFromInstanceStateOrLaunchBundle( - Bundle savedInstanceState) { + @Nullable Bundle savedInstanceState) { if (savedInstanceState != null && savedInstanceState.containsKey( PasswordManagerHelper.MANAGE_PASSWORDS_REFERRER)) { @@ -256,8 +265,10 @@ mExportFlow.startExporting(); return true; } + + assumeNonNull(mSearchItem); if (SearchUtils.handleSearchNavigation(item, mSearchItem, mSearchQuery, getActivity())) { - filterPasswords(null); + filterPasswords(/* query= */ null); return true; } if (id == R.id.menu_id_targeted_help) { @@ -268,7 +279,9 @@ return super.onOptionsItemSelected(item); } - private void filterPasswords(String query) { + private void filterPasswords(@Nullable String query) { + assert mHelpItem != null; + mSearchQuery = query; mHelpItem.setShowAsAction( mSearchQuery == null @@ -306,11 +319,15 @@ mNoPasswords = false; mNoPasswordExceptions = false; getPreferenceScreen().removeAll(); + PasswordManagerHandlerProvider passwordManagerHandlerProvider = + PasswordManagerHandlerProvider.getForProfile(getProfile()); + assert passwordManagerHandlerProvider != null; + PasswordManagerHandler passwordManagerHandler = + passwordManagerHandlerProvider.getPasswordManagerHandler(); + assert passwordManagerHandler != null; if (mSearchQuery != null) { // Only the filtered passwords and exceptions should be shown. - PasswordManagerHandlerProvider.getForProfile(getProfile()) - .getPasswordManagerHandler() - .updatePasswordLists(); + passwordManagerHandler.updatePasswordLists(); return; } @@ -331,9 +348,7 @@ R.string.android_trusted_vault_banner_sub_label_offer_opt_in, this::openTrustedVaultOptInDialog); } - PasswordManagerHandlerProvider.getForProfile(getProfile()) - .getPasswordManagerHandler() - .updatePasswordLists(); + passwordManagerHandler.updatePasswordLists(); } private boolean shouldShowAutoSigninOption() { @@ -386,10 +401,11 @@ passwordParent = getPreferenceScreen(); } for (int i = 0; i < count; i++) { - SavedPasswordEntry saved = + PasswordManagerHandler passwordManagerHandler = PasswordManagerHandlerProvider.getForProfile(getProfile()) - .getPasswordManagerHandler() - .getSavedPasswordEntry(i); + .getPasswordManagerHandler(); + assert passwordManagerHandler != null; + SavedPasswordEntry saved = passwordManagerHandler.getSavedPasswordEntry(i); String url = saved.getUrl(); String name = saved.getUserName(); String password = saved.getPassword(); @@ -461,10 +477,11 @@ profileCategory.setOrder(ORDER_EXCEPTIONS); getPreferenceScreen().addPreference(profileCategory); for (int i = 0; i < count; i++) { - String exception = + PasswordManagerHandler passwordManagerHandler = PasswordManagerHandlerProvider.getForProfile(getProfile()) - .getPasswordManagerHandler() - .getSavedPasswordException(i); + .getPasswordManagerHandler(); + assert passwordManagerHandler != null; + String exception = passwordManagerHandler.getSavedPasswordException(i); Preference preference = new Preference(getStyledContext()); preference.setTitle(exception); preference.setOnPreferenceClickListener(this); @@ -488,7 +505,7 @@ } @Override - public void onActivityResult(int requestCode, int resultCode, Intent intent) { + public void onActivityResult(int requestCode, int resultCode, @Nullable Intent intent) { super.onActivityResult(requestCode, resultCode, intent); if (requestCode != PASSWORD_EXPORT_INTENT_REQUEST_CODE) return; if (resultCode != Activity.RESULT_OK) return; @@ -511,8 +528,9 @@ public void onDestroy() { super.onDestroy(); - if (SyncServiceFactory.getForProfile(getProfile()) != null) { - SyncServiceFactory.getForProfile(getProfile()).removeSyncStateChangedListener(this); + SyncService syncService = SyncServiceFactory.getForProfile(getProfile()); + if (syncService != null) { + syncService.removeSyncStateChangedListener(this); } // The component should only be destroyed when the activity has been closed by the user // (e.g. by pressing on the back button) and not when the activity is temporarily destroyed @@ -541,12 +559,14 @@ } else { boolean isBlockedCredential = !preference.getExtras().containsKey(PasswordSettings.PASSWORD_LIST_NAME); - PasswordManagerHandlerProvider.getForProfile(getProfile()) - .getPasswordManagerHandler() - .showPasswordEntryEditingView( - getActivity(), - preference.getExtras().getInt(PasswordSettings.PASSWORD_LIST_ID), - isBlockedCredential); + PasswordManagerHandler passwordManagerHandler = + PasswordManagerHandlerProvider.getForProfile(getProfile()) + .getPasswordManagerHandler(); + assert passwordManagerHandler != null; + passwordManagerHandler.showPasswordEntryEditingView( + getActivity(), + preference.getExtras().getInt(PasswordSettings.PASSWORD_LIST_ID), + isBlockedCredential); } return true; } @@ -712,9 +732,9 @@ } private boolean openTrustedVaultOptInDialog(Preference unused) { - assert SyncServiceFactory.getForProfile(getProfile()) != null; - CoreAccountInfo accountInfo = - SyncServiceFactory.getForProfile(getProfile()).getAccountInfo(); + SyncService syncService = SyncServiceFactory.getForProfile(getProfile()); + assert syncService != null; + CoreAccountInfo accountInfo = syncService.getAccountInfo(); assert accountInfo != null; SyncSettingsUtils.openTrustedVaultOptInDialog( this, accountInfo, REQUEST_CODE_TRUSTED_VAULT_OPT_IN); @@ -733,7 +753,7 @@ return true; } - Menu getMenuForTesting() { + @Nullable Menu getMenuForTesting() { return mMenu; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiveSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiveSettings.java index e1f5936c..a173133 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiveSettings.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabArchiveSettings.java
@@ -248,4 +248,8 @@ mPrefsManager.removeKey(ChromePreferenceKeys.TAB_DECLUTTER_DIALOG_IPH_DISMISS_COUNT); mPrefsManager.removeKey(ChromePreferenceKeys.TAB_DECLUTTER_AUTO_DELETE_DECISION_MADE); } + + public boolean isInTestingMode() { + return ChromeFeatureList.sAndroidTabDeclutterAutoDeletePromoTest.getValue(); + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java index a15e072..727da6b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabImpl.java
@@ -103,6 +103,7 @@ import org.chromium.content_public.browser.back_forward_transition.AnimationStage; import org.chromium.content_public.browser.navigation_controller.UserAgentOverrideOption; import org.chromium.content_public.common.Referrer; +import org.chromium.ui.base.DeviceFormFactor; import org.chromium.ui.base.PageTransition; import org.chromium.ui.base.ViewAndroidDelegate; import org.chromium.ui.base.WindowAndroid; @@ -275,6 +276,7 @@ private @Nullable Token mTabGroupId; private boolean mTabHasSensitiveContent; private boolean mIsPinned; + private @MediaState int mMediaState; private @TabUserAgent int mUserAgent = TabUserAgent.DEFAULT; /** @@ -2678,6 +2680,22 @@ } @Override + public @MediaState int getMediaState() { + return mMediaState; + } + + @Override + public void setMediaState(@MediaState int mediaState) { + mMediaState = mediaState; + if (ChromeFeatureList.sMediaIndicatorsAndroid.isEnabled() + && DeviceFormFactor.isNonMultiDisplayContextOnTablet(getContext())) { + for (TabObserver observer : mObservers) { + observer.onMediaStateChanged(this, mediaState); + } + } + } + + @Override public void onTabRestoredFromArchivedTabModel() { for (TabObserver observer : mObservers) { observer.onTabUnarchived(this);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java index b652155..ea1d9021 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
@@ -37,6 +37,7 @@ import org.chromium.chrome.browser.policy.PolicyAuditor; import org.chromium.chrome.browser.policy.PolicyAuditor.AuditEvent; import org.chromium.chrome.browser.serial.SerialNotificationManager; +import org.chromium.chrome.browser.tab.Tab.MediaState; import org.chromium.chrome.browser.ui.native_page.NativePage; import org.chromium.chrome.browser.usb.UsbNotificationManager; import org.chromium.content_public.browser.GlobalRenderFrameHostId; @@ -414,5 +415,16 @@ mLastUrl, mTab.isIncognito()); } + + @Override + public void mediaStartedPlaying() { + // TODO(crbug.com/430072416): Identify when audio is muted. + mTab.setMediaState(MediaState.AUDIBLE); + } + + @Override + public void mediaStoppedPlaying() { + mTab.setMediaState(MediaState.NONE); + } } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/KeyboardFocusRowManager.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/KeyboardFocusRowManager.java index aec48418..beb41d7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/KeyboardFocusRowManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/KeyboardFocusRowManager.java
@@ -150,7 +150,8 @@ } // The next item in the focus cycle order is BOOKMARKS_BAR, if it is present. - if (mBookmarkBarCoordinatorSupplier.hasValue()) { + if (mBookmarkBarCoordinatorSupplier.hasValue() + && mBookmarkBarCoordinatorSupplier.get().isVisible()) { keyboardFocusRows.add(KeyboardFocusRow.BOOKMARKS_BAR); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java index fa1f6f83..59dc806 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -4,9 +4,6 @@ package org.chromium.chrome.browser.tabbed_mode; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; - import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; @@ -17,9 +14,7 @@ import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.text.TextUtils; -import android.view.View; import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; import android.view.ViewStub; import androidx.annotation.ColorInt; @@ -687,12 +682,23 @@ } if (mBookmarkBarVisibilityProvider != null) { - destroyBookmarkBarIfNecessary(); + if (mBookmarkBarCoordinator != null) { + mBookmarkBarVisibilityProvider.removeObserver(mBookmarkBarCoordinator); + } mBookmarkBarVisibilityProvider.removeObserver(mBookmarkBarVisibilityObserver); mBookmarkBarVisibilityProvider.destroy(); mBookmarkBarVisibilityProvider = null; } + if (mBookmarkBarCoordinator != null) { + mBookmarkBarCoordinator.destroy(); + mBookmarkBarCoordinator = null; + mBookmarkOpener = null; + if (mToolbarManager != null) { + mToolbarManager.setBookmarkBarHeightSupplier(null); + } + } + if (mLoadingFullscreenCoordinator != null) { mLoadingFullscreenCoordinator.destroy(); mLoadingFullscreenCoordinator = null; @@ -1792,6 +1798,11 @@ mBookmarkBarVisibilityProvider.addObserver(mBookmarkBarCoordinator); } mBookmarkBarHeightSupplier = mBookmarkBarCoordinator::getTopControlHeight; + } else { + mBookmarkBarCoordinator.setVisibility(true); + // When toggling the visibility of the existing view, the LayoutChangeListener will not + // be triggered as it is on instantiation, so we update the top controls height here. + updateTopControlsHeight(); } if (mToolbarManager != null) { @@ -1799,42 +1810,6 @@ } } - private void destroyBookmarkBarIfNecessary() { - View view = null; - - if (mBookmarkBarCoordinator != null) { - view = mBookmarkBarCoordinator.getView(); - if (mBookmarkBarVisibilityProvider != null) { - mBookmarkBarVisibilityProvider.removeObserver(mBookmarkBarCoordinator); - } - mBookmarkBarCoordinator.destroy(); - mBookmarkBarCoordinator = null; - } - - if (mBookmarkOpener != null) { - mBookmarkOpener = null; - } - - if (mToolbarManager != null) { - mToolbarManager.setBookmarkBarHeightSupplier(null); - } - - if (view != null) { - // Remove view for bookmark bar. - final var parent = (ViewGroup) view.getParent(); - final int index = parent.indexOfChild(view); - parent.removeViewInLayout(view); - - // Add stub for bookmark bar. - final var viewStub = new ViewStub(mActivity, R.layout.bookmark_bar); - viewStub.setId(R.id.bookmark_bar_stub); - viewStub.setInflatedId(R.id.bookmark_bar); - parent.addView(viewStub, index, new LayoutParams(MATCH_PARENT, WRAP_CONTENT)); - - updateTopControlsHeight(); - } - } - private void updateBookmarkBarIfNecessary(boolean visible) { if (visible) { // We create the BookmarkBar, but we do not update the Top Controls height here since @@ -1844,7 +1819,10 @@ // onLayoutChange we will have the correct height and update the top controls then. createBookmarkBarIfNecessary(); } else { - destroyBookmarkBarIfNecessary(); + if (mBookmarkBarCoordinator != null) { + mBookmarkBarCoordinator.setVisibility(false); + updateTopControlsHeight(); + } } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java index a0db507..094c6f1f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java
@@ -371,7 +371,13 @@ // cast in C++ otherwise results in the tab going to the end of the list which is not // intended. newIndex = Math.max(0, newIndex); - moveTabInternal(tab, currentIndex, newIndex, tab.getTabGroupId(), tab.getIsPinned()); + moveTabInternal( + tab, + currentIndex, + newIndex, + tab.getTabGroupId(), + tab.getIsPinned(), + /* isDestinationTab= */ false); } @Override @@ -471,7 +477,7 @@ // the observer interface entirely. for (TabGroupModelFilterObserver observer : mTabGroupObservers) { observer.willMergeTabToGroup(tab, Tab.INVALID_TAB_ID, tabGroupId); - observer.didMergeTabToGroup(tab); + observer.didMergeTabToGroup(tab, /* isDestinationTab= */ false); } } @@ -860,7 +866,8 @@ curIndex, newIndex, /* newTabGroupId= */ null, - /* isPinned= */ tab.getIsPinned()); + /* isPinned= */ tab.getIsPinned(), + /* isDestinationTab= */ false); if (finalIndex != curIndex) { for (TabGroupModelFilterObserver observer : mTabGroupObservers) { observer.didMoveTabGroup(tab, curIndex, finalIndex); @@ -1159,7 +1166,8 @@ indexOf(sourceTab), approximateIndex, /* newTabGroupId= */ null, - /* isPinned= */ false); + /* isPinned= */ false, + /* isDestinationTab= */ false); } // Internal methods. @@ -1302,7 +1310,12 @@ // The C++ side will adjust to a valid index. moveTabInternal( - tab, currentIndex, currentIndex, /* newTabGroupId= */ null, isPinned); + tab, + currentIndex, + currentIndex, + /* newTabGroupId= */ null, + isPinned, + /* isDestinationTab= */ false); } public void mergeListOfTabsToGroupInternal( @@ -1353,7 +1366,12 @@ if (!wasDestinationTabInGroup) { int index = indexOf(destinationTab); moveTabInternal( - destinationTab, index, index, destinationTabGroupId, /* isPinned= */ false); + destinationTab, + index, + index, + destinationTabGroupId, + /* isPinned= */ false, + /* isDestinationTab= */ true); } // Adopt the title of the first candidate group with a title that was merged into the @@ -1377,7 +1395,12 @@ // Move all the tabs to the end of the tab group. The native code will find the right // index to insert the tab. moveTabInternal( - tab, indexOf(tab), endIndex, destinationTabGroupId, /* isPinned= */ false); + tab, + indexOf(tab), + endIndex, + destinationTabGroupId, + /* isPinned= */ false, + /* isDestinationTab= */ false); } for (TabGroupModelFilterObserver observer : mTabGroupObservers) { @@ -1408,10 +1431,16 @@ * @param newIndex The new index of the tab. This might be adjusted in C++ to a valid index. * @param newTabGroupId The new tab group id of the tab. * @param isPinned Whether the tab is pinned. + * @param isDestinationTab Whether the tab is the destination tab in a merge operation. * @return The final index of the tab. */ private int moveTabInternal( - Tab tab, int index, int newIndex, @Nullable Token newTabGroupId, boolean isPinned) { + Tab tab, + int index, + int newIndex, + @Nullable Token newTabGroupId, + boolean isPinned, + boolean isDestinationTab) { assert newTabGroupId == null || !isPinned : "Pinned and grouped tabs are mutually exclusive."; @@ -1506,7 +1535,7 @@ if (isMergingIntoGroup) { for (TabGroupModelFilterObserver observer : mTabGroupObservers) { - observer.didMergeTabToGroup(tab); + observer.didMergeTabToGroup(tab, isDestinationTab); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java index 85198d41..4d844ea 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
@@ -57,6 +57,7 @@ import org.chromium.chrome.browser.tabpersistence.TabStateDirectory; import org.chromium.chrome.browser.tabpersistence.TabStateFileManager; import org.chromium.chrome.browser.tabwindow.TabWindowManager; +import org.chromium.components.browser_ui.util.ConversionUtils; import org.chromium.components.embedder_support.util.UrlUtilities; import org.chromium.content_public.browser.LoadUrlParams; @@ -1945,7 +1946,11 @@ byte[] data; try { stream = new FileInputStream(stateFile); - data = new byte[(int) stateFile.length()]; + int size = (int) stateFile.length(); + int sizeInKb = size / ConversionUtils.BYTES_PER_KILOBYTE; + RecordHistogram.recordMemoryKBHistogram( + "Tabs.Metadata.FileSizeOnRead." + mClientTag, sizeInKb); + data = new byte[size]; stream.read(data); } catch (IOException exception) { Log.e(TAG, "Could not read state file.", exception);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/display_cutout/OWNERS b/chrome/android/javatests/src/org/chromium/chrome/browser/display_cutout/OWNERS new file mode 100644 index 0000000..825f537 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/display_cutout/OWNERS
@@ -0,0 +1 @@ +file://chrome/android/java/src/org/chromium/chrome/browser/display_cutout/OWNERS \ No newline at end of file
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 9f82b2a..4760c34 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
@@ -75,6 +75,7 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import org.chromium.base.BaseSwitches; import org.chromium.base.ContextUtils; import org.chromium.base.ThreadUtils; import org.chromium.base.metrics.RecordHistogram; @@ -3587,6 +3588,49 @@ }); } + @Test + @SmallTest + @Feature({"Preferences"}) + @CommandLineFlags.Add(BaseSwitches.ENABLE_LOW_END_DEVICE_MODE) + @EnableFeatures(ChromeFeatureList.PERMISSION_SITE_SETTING_RADIO_BUTTON) + public void testAddingJavascriptOptimizerExceptionsBlockedIfNotEnoughRam() { + final SettingsActivity settingsActivity = + SiteSettingsTestUtils.startSiteSettingsCategory( + SiteSettingsCategory.Type.JAVASCRIPT_OPTIMIZER); + + ThreadUtils.runOnUiThreadBlocking( + () -> { + SingleCategorySettings singleCategorySettings = + (SingleCategorySettings) settingsActivity.getMainFragment(); + + checkPreferencesForSettingsActivity( + settingsActivity, + new String[] { + SingleCategorySettings.INFO_TEXT_KEY, + SingleCategorySettings.BINARY_RADIO_BUTTON_KEY, + SingleCategorySettings.ADD_EXCEPTION_KEY, + SingleCategorySettings.ADD_EXCEPTION_DISABLED_REASON_KEY, + }); + + Preference addExceptionButton = + singleCategorySettings.findPreference( + SingleCategorySettings.ADD_EXCEPTION_KEY); + Assert.assertFalse(addExceptionButton.isEnabled()); + + Preference addExceptionButtonDisabledReason = + singleCategorySettings.findPreference( + SingleCategorySettings.ADD_EXCEPTION_DISABLED_REASON_KEY); + Context context = ApplicationProvider.getApplicationContext(); + int expectedReasonId = + R.string.website_settings_js_opt_add_exceptions_disabled_reason; + Assert.assertEquals( + context.getString(expectedReasonId), + addExceptionButtonDisabledReason.getTitle()); + + settingsActivity.finish(); + }); + } + /** * Test that if the Javascript-optimizer is enabled by enterprise policy but disabled by the OS * advanced-portection-mode setting that the enterprise policy is given precedence.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImplTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImplTest.java index b4648598..eecfb469 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImplTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImplTest.java
@@ -681,8 +681,9 @@ } @Override - public void didMergeTabToGroup(Tab movedTab) { + public void didMergeTabToGroup(Tab movedTab, boolean isDestinationTab) { assertEquals(tab0, movedTab); + assertTrue(isDestinationTab); didMergeTabToGroupHelper.notifyCalled(); }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java index 81f2c67..1af64e84a 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManagerTest.java
@@ -1128,16 +1128,16 @@ "Ensure top padding increase the entire height", motionEventHandled(SCREEN_WIDTH / 2, topPadding + TAB_STRIP_HEIGHT_PX - 1)); - // topBound(1) = msbOffsetY(3) + topPadding(10) - touchSlop(12) - // bottomBound(57) = msbOffsetY(3) + topPadding(10) + msbHeight(32) + touchSlop(12) + // topBound(5) = msbOffsetY(3) + topPadding(10) - touchSlop(8) + // bottomBound(53) = msbOffsetY(3) + topPadding(10) + msbHeight(32) + touchSlop(8) assertEquals( "Touch target top bound for MSB is incorrect.", - 1, + 5, mStripLayoutHelperManager.getModelSelectorButton().getTouchTargetBounds().top, 0f); assertEquals( "Touch target bottom bound for MSB is incorrect.", - 57, + 53, mStripLayoutHelperManager.getModelSelectorButton().getTouchTargetBounds().bottom, 0f); } @@ -1203,16 +1203,16 @@ rect.bottom); Rect rect2 = mSystemExclusionRectCaptor.getValue().get(1); - // Left: 728 = width(800) - rightPadding(20) - modelSelectorWidth(32) - endPadding(8) - - // clickSlop(12) + // Left: 732 = width(800) - rightPadding(20) - modelSelectorWidth(32) - endPadding(8) - + // clickSlop(8) // Top: 5 = max(topPadding(5) , topPadding(5) + modelSelectorYOffset(3) - - // clickSlop(12))) - // Right: 784 = width(800) - rightPadding(20) - endPadding(8) + clickSlop(12) + // clickSlop(8))) + // Right: 780 = width(800) - rightPadding(20) - endPadding(8) + clickSlop(8) // Bottom: 45 = min(height(45), topPadding(5) + modelSelectorHeight(32) + - // clickSlop(12)) + // clickSlop(8)) assertEquals( "2nd rect should represent model selector button.", - new Rect(728, 5, 784, 45), + new Rect(732, 5, 780, 45), rect2); } else { assertEquals(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/display_cutout/OWNERS b/chrome/android/junit/src/org/chromium/chrome/browser/display_cutout/OWNERS new file mode 100644 index 0000000..825f537 --- /dev/null +++ b/chrome/android/junit/src/org/chromium/chrome/browser/display_cutout/OWNERS
@@ -0,0 +1 @@ +file://chrome/android/java/src/org/chromium/chrome/browser/display_cutout/OWNERS \ No newline at end of file
diff --git a/chrome/app/glic_strings.grdp b/chrome/app/glic_strings.grdp index 511fae3..34df28e 100644 --- a/chrome/app/glic_strings.grdp +++ b/chrome/app/glic_strings.grdp
@@ -1,75 +1,75 @@ <?xml version="1.0" encoding="utf-8"?> <!-- Glic specific strings (included from generated_resources.grd). --> <grit-part> - <message name="IDS_TOOLTIP_TAB_ALERT_STATE_GLIC_ACCESSING" desc="Extra tool tip text, when the tab's context is being shared with Gemini" translateable="false"> + <message name="IDS_TOOLTIP_TAB_ALERT_STATE_GLIC_ACCESSING" desc="Extra tool tip text, when the tab's context is being shared with Gemini"> You're sharing this page with Gemini </message> - <message name="IDS_TOOLTIP_TAB_ALERT_STATE_GLIC_SHARING" desc="Extra tool tip text, when the tab's context is being shared with Gemini" translateable="false"> + <message name="IDS_TOOLTIP_TAB_ALERT_STATE_GLIC_SHARING" desc="Extra tool tip text, when the tab's context is being shared with Gemini"> You're sharing this page with Gemini </message> - <message name="IDS_TAB_AX_LABEL_GLIC_ACCESSING" is_accessibility_with_no_ui="true" desc="Accessibility label text, when the tab is sharing data with Gemini. Example: 'Google Photos - Tab context shared with Gemini'" translateable="false"> + <message name="IDS_TAB_AX_LABEL_GLIC_ACCESSING" is_accessibility_with_no_ui="true" desc="Accessibility label text, when the tab is sharing data with Gemini. Example: 'Wikipedia - Tab context shared with Gemini'"> <ph name="WINDOW_TITLE"> - $1<ex>Google Photos</ex> + $1<ex>Wikipedia</ex> </ph> - You're sharing this page with Gemini </message> - <message name="IDS_TAB_AX_LABEL_GLIC_SHARING" is_accessibility_with_no_ui="true" desc="Accessibility label text, when the tab is sharing data with Gemini. Example: 'Google Photos - Tab context shared with Gemini'" translateable="false"> + <message name="IDS_TAB_AX_LABEL_GLIC_SHARING" is_accessibility_with_no_ui="true" desc="Accessibility label text, when the tab is sharing data with Gemini. Example: 'Wikipedia - Tab context shared with Gemini'"> <ph name="WINDOW_TITLE"> - $1<ex>Google Photos</ex> + $1<ex>Wikipedia</ex> </ph> - You're sharing this tab with Gemini for the duration of this conversation </message> - <message name="IDS_GLIC_PROMO_BODY" desc="Gemini promo body text." translateable="false"> + <message name="IDS_GLIC_PROMO_BODY" desc="Text for a promo pointing to the Gemini icon in Chrome."> Chat with voice or text to get answers, key takeaways, and more as you browse </message> <if expr="is_win or is_linux"> - <message name="IDS_GLIC_STATUS_ICON_MENU_CUSTOMIZE_KEYBOARD_SHORTCUT" translateable="false" desc="This is the menu item for customizing the keyboard shortcut."> + <message name="IDS_GLIC_STATUS_ICON_MENU_CUSTOMIZE_KEYBOARD_SHORTCUT" desc="Menu text for the Gemini icon on the system try to open Chrome settings and change the keyboard shortcut for opening Gemini in Chrome."> Customize keyboard shortcut </message> </if> <if expr="is_macosx"> - <message name="IDS_GLIC_STATUS_ICON_MENU_CUSTOMIZE_KEYBOARD_SHORTCUT" translateable="false" desc="This is the menu item for customizing the keyboard shortcut."> + <message name="IDS_GLIC_STATUS_ICON_MENU_CUSTOMIZE_KEYBOARD_SHORTCUT" desc="Menu text for the Gemini icon on the system try to open Chrome settings and change the keyboard shortcut for opening Gemini in Chrome. [With ellipsis]"> Customize Keyboard Shortcut... </message> </if> <if expr="is_win or is_linux"> - <message name="IDS_GLIC_STATUS_ICON_MENU_EXIT" translateable="false" desc="This is the menu item for exiting Chrome."> + <message name="IDS_GLIC_STATUS_ICON_MENU_EXIT" desc="This is the menu item for exiting Chrome."> Exit </message> </if> <if expr="is_win or is_linux"> - <message name="IDS_GLIC_STATUS_ICON_MENU_SETTINGS" translateable="false" desc="This is the menu item for opening the Gemini status icon settings. (on Windows and Linux)."> + <message name="IDS_GLIC_STATUS_ICON_MENU_SETTINGS" desc="This is the menu item for opening the Gemini status icon settings."> Settings </message> </if> <if expr="is_macosx"> - <message name="IDS_GLIC_STATUS_ICON_MENU_SETTINGS" translateable="false" desc="This is the menu item for opening the Gemini status icon settings. (on Mac only)."> + <message name="IDS_GLIC_STATUS_ICON_MENU_SETTINGS" desc="This is the menu item for opening the Gemini status icon settings. [With ellipsis]"> Settings... </message> </if> <message name="IDS_GLIC_OS_WIDGET_TOGGLE_HELP_BUBBLE" translateable="false" desc="Help bubble message above the toggle for turning off the Gemini icon in the status tray/launcher area"> Don't show Gemini in menu bar </message> - <message name="IDS_GLIC_OS_WIDGET_KEYBOARD_SHORTCUT_HELP_BUBBLE" translateable="false" desc="Help bubble message above the keyboard shortcut editor that allows customizing it"> + <message name="IDS_GLIC_OS_WIDGET_KEYBOARD_SHORTCUT_HELP_BUBBLE" desc="Help bubble message above the keyboard shortcut editor that allows customizing it"> Customize the keyboard shortcut here </message> - <message name="IDS_GLIC_OFFLINE_NOTICE_HEADER" translateable="false" desc="This is the headline on the offline notice informing the user that there is no network connection."> + <message name="IDS_GLIC_OFFLINE_NOTICE_HEADER" desc="Headline on the offline notice informing the user that there is no network connection."> You're offline </message> - <message name="IDS_GLIC_OFFLINE_NOTICE_ACTION" translateable="false" desc="This is the body text for the offline notice."> + <message name="IDS_GLIC_OFFLINE_NOTICE_ACTION" desc="Body text for the offline notice."> Check your internet connection and try to use Gemini again </message> - <message name="IDS_GLIC_OFFLINE_NOTICE_ACTION_BUTTON" translateable="false" desc="This is the button label text for the offline notice."> + <message name="IDS_GLIC_OFFLINE_NOTICE_ACTION_BUTTON" desc="This is the button label text for the offline notice."> Try again </message> <message name="IDS_GLIC_UNRESPONSIVE_MESSAGE" translateable="false" desc="This is the message that appears overlaying the floaty window for a few seconds if the Gemini backend becomes unresponsive."> Gemini is refreshing... </message> - <message name="IDS_GLIC_ERROR_NOTICE_HEADER" translateable="false" desc="Headline on the error notice informing the user that Gemini failed to load."> + <message name="IDS_GLIC_ERROR_NOTICE_HEADER" desc="Headline on the error notice informing the user that Gemini failed to load."> Something went wrong </message> - <message name="IDS_GLIC_ERROR_NOTICE" translateable="false" desc="This is the text of the error notice."> + <message name="IDS_GLIC_ERROR_NOTICE" desc="This is the text of the error notice."> Gemini isn't available right now. Try again later. </message> - <message name="IDS_GLIC_ERROR_NOTICE_ACTION_BUTTON" translateable="false" desc="This is the button label text for the error notice."> + <message name="IDS_GLIC_ERROR_NOTICE_ACTION_BUTTON" desc="This is the button label text for the error notice."> Try again </message> <message name="IDS_GLIC_NOTICE_CLOSE_BUTTON_LABEL" translateable="false" desc="Accessible label for the dialog notice and FRE close buttons."> @@ -101,89 +101,89 @@ </message> <!-- Glic settings page strings --> - <message name="IDS_SETTINGS_GLIC_ROW_SUBLABEL" translateable="false" desc="Sublabel of the Gemini row button leading to the Gemini subpage."> + <message name="IDS_SETTINGS_GLIC_ROW_SUBLABEL" desc="Sublabel of the Gemini row button leading to the Gemini subpage."> AI assistant that gives key takeaways, clarifies concepts, finds answers and more </message> <message name="IDS_SETTINGS_GLIC_POLICY_DISABLED_MESSAGE" translateable="false" desc="Message shown when Gemini is disabled by enterprise policy."> This feature is disabled by your organization </message> - <message name="IDS_SETTINGS_GLIC_PREFERENCES_SECTION" translateable="false" desc="Heading on settings page for basic preferences."> + <message name="IDS_SETTINGS_GLIC_PREFERENCES_SECTION" desc="Heading on settings page for basic preferences."> Preferences </message> <message name="IDS_SETTINGS_GLIC_PERMISSIONS_SECTION" translateable="false" desc="Heading on settings page for permissions-related preferences."> Permissions </message> - <message name="IDS_SETTINGS_GLIC_BUTTON_TOGGLE" translateable="false" desc="Name of the toggle that controls whether the Gemini icon is shown in the tabstrip."> + <message name="IDS_SETTINGS_GLIC_BUTTON_TOGGLE" desc="Name of the toggle that controls whether the Gemini icon is shown in the tabstrip."> Show Gemini at the top of the browser </message> - <message name="IDS_SETTINGS_GLIC_BUTTON_TOGGLE_SUBLABEL" translateable="false" desc="Sublabel of the toggle that controls whether the Gemini icon is shown in the tabstrip."> + <message name="IDS_SETTINGS_GLIC_BUTTON_TOGGLE_SUBLABEL" desc="Sublabel of the toggle that controls whether the Gemini icon is shown in the tabstrip."> Quickly open Gemini whenever you need it </message> <if expr="is_macosx"> - <message name="IDS_SETTINGS_GLIC_OS_WIDGET_TOGGLE" translateable="false" desc="Name of the toggle that controls whether the Gemini icon is shown in the menu bar (on Mac only)."> + <message name="IDS_SETTINGS_GLIC_OS_WIDGET_TOGGLE" desc="Name of the toggle that controls whether the Gemini icon is shown in the MacOS menu bar (on Mac only)."> Show Gemini in Mac menu bar and turn on keyboard shortcut </message> </if> <if expr="not is_macosx"> - <message name="IDS_SETTINGS_GLIC_OS_WIDGET_TOGGLE" translateable="false" desc="Name of the toggle that controls whether the Gemini icon is shown in the system tray (on Windows and Linux)."> + <message name="IDS_SETTINGS_GLIC_OS_WIDGET_TOGGLE" desc="Name of the toggle that controls whether the Gemini icon is shown in the system tray (on Windows and Linux)."> Show Gemini in system tray and turn on keyboard shortcut </message> </if> - <message name="IDS_SETTINGS_GLIC_OS_WIDGET_TOGGLE_SUBLABEL" translateable="false" desc="Sublabel of the toggle that controls whether the Gemini icon is shown in the menu bar (on Mac) / system tray (on Windows and Linux)."> + <message name="IDS_SETTINGS_GLIC_OS_WIDGET_TOGGLE_SUBLABEL" desc="Sublabel of the toggle that controls whether the Gemini icon is shown in the menu bar (on Mac) / system tray (on Windows and Linux)."> Easy access to use Gemini across your device, even if Chrome is minimized. </message> - <message name="IDS_SETTINGS_GLIC_KEYBOARD_SHORTCUT" translateable="false" desc="Name of the keyboard shortcut setting."> + <message name="IDS_SETTINGS_GLIC_KEYBOARD_SHORTCUT" desc="Name of the keyboard shortcut setting."> Keyboard shortcut </message> - <message name="IDS_SETTINGS_GLIC_KEYBOARD_SHORTCUT_SUBLABEL" translateable="false" desc="Sublabel describing the keyboard shortcut that's used to globally invoke Gemini."> + <message name="IDS_SETTINGS_GLIC_KEYBOARD_SHORTCUT_SUBLABEL" desc="Sublabel describing the keyboard shortcut that's used to globally invoke Gemini."> Open Gemini with a keyboard shortcut. </message> - <message name="IDS_SETTINGS_GLIC_KEYBOARD_SHORTCUT_LEARN_MORE_LABEL" translateable="false" desc="Label for the 'Learn more' link in the sublabel describing the keyboard shortcut that's used to globally invoke Gemini."> + <message name="IDS_SETTINGS_GLIC_KEYBOARD_SHORTCUT_LEARN_MORE_LABEL" desc="Label for the 'Learn more' link in the sublabel for Gemini in Chrome settings."> Learn more </message> - <message name="IDS_SETTINGS_GLIC_NAVIGATION_SHORTCUT" translateable="false" desc="Name of the navigation shortcut setting."> + <message name="IDS_SETTINGS_GLIC_NAVIGATION_SHORTCUT" desc="Title for the navigation shortcut setting to leave and return to Gemini in Chrome."> Navigation shortcut </message> - <message name="IDS_SETTINGS_GLIC_NAVIGATION_SHORTCUT_SUBLABEL" translateable="false" desc="Sublabel describing the keyboard shortcut navigate between Chrome and Gemini."> + <message name="IDS_SETTINGS_GLIC_NAVIGATION_SHORTCUT_SUBLABEL" desc="Sublabel describing the keyboard shortcut navigate between Chrome and Gemini."> To leave or return to Gemini, use keyboard shortcut. </message> - <message name="IDS_SETTINGS_GLIC_CLOSED_CAPTIONING" translateable="false" desc="Name of the closed captioning setting."> + <message name="IDS_SETTINGS_GLIC_CLOSED_CAPTIONING" desc="Name of the closed captioning setting."> Closed Captioning </message> - <message name="IDS_SETTINGS_GLIC_CLOSED_CAPTIONING_SUBLABEL" translateable="false" desc="Sublabel describing that this setting will enable closed captions for Gemini Live."> + <message name="IDS_SETTINGS_GLIC_CLOSED_CAPTIONING_SUBLABEL" desc="Sublabel describing that this setting will enable closed captions for Gemini Live."> Show closed captions when using Gemini Live </message> - <message name="IDS_SETTINGS_GLIC_PERMISSIONS_LOCATION_TOGGLE" translateable="false" desc="Name of the location sharing setting."> + <message name="IDS_SETTINGS_GLIC_PERMISSIONS_LOCATION_TOGGLE" desc="Name of the location sharing setting."> Precise Location </message> - <message name="IDS_SETTINGS_GLIC_PERMISSIONS_LOCATION_TOGGLE_SUBLABEL" translateable="false" desc="Sublabel describing that this setting will share location data with Gemini for location-aware answers"> + <message name="IDS_SETTINGS_GLIC_PERMISSIONS_LOCATION_TOGGLE_SUBLABEL" desc="Sublabel describing that this setting will share location data with Gemini for location-aware answers"> Use your precise location for the most accurate Gemini results, instead of an estimated location based on your IP address. </message> <message name="IDS_SETTINGS_GLIC_PERMISSIONS_LOCATION_TOGGLE_SUBLABEL_DATA_PROTECTED" translateable="false" desc="Sublabel describing that this setting will share location data with Gemini for location-aware answers, but the user's data is not used to improve the model"> Use your precise location for the most accurate Gemini results, instead of an estimated location based on your IP address. Your data is not used to improve generative AI models </message> - <message name="IDS_SETTINGS_GLIC_PERMISSIONS_MICROPHONE_TOGGLE" translateable="false" desc="Name of the microphone setting."> + <message name="IDS_SETTINGS_GLIC_PERMISSIONS_MICROPHONE_TOGGLE" desc="Name of the microphone setting."> Microphone </message> - <message name="IDS_SETTINGS_GLIC_PERMISSIONS_MICROPHONE_TOGGLE_SUBLABEL" translateable="false" desc="Sublabel describing that this setting will enable using the microphone to have a voice chat with Gemini"> + <message name="IDS_SETTINGS_GLIC_PERMISSIONS_MICROPHONE_TOGGLE_SUBLABEL" desc="Sublabel describing that this setting will enable using the microphone to have a voice chat with Gemini"> Use your microphone to chat with Gemini. When you speak to Gemini, audio is stored in Gemini Apps Activity (if it's on) </message> <message name="IDS_SETTINGS_GLIC_PERMISSIONS_MICROPHONE_TOGGLE_SUBLABEL_DATA_PROTECTED" translateable="false" desc="Sublabel describing that this setting will enable using the microphone to have a voice chat with Gemini, but the user's data is not used to improve the model"> Use your microphone to chat with Gemini. When you speak to Gemini, audio is stored in Gemini Apps Activity (if it's on). Your data is not used to improve generative AI models </message> - <message name="IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_TOGGLE" translateable="false" desc="Name of the enable current tab access setting."> + <message name="IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_TOGGLE" desc="Name of the enable current tab access setting."> Page content sharing </message> - <message name="IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_TOGGLE_SUBLABEL" translateable="false" desc="Sublabel describing that this setting will enable sharing the current tab's content with Gemini"> + <message name="IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_TOGGLE_SUBLABEL" desc="Sublabel describing that this setting will enable sharing the current tab's content with Gemini"> Share page content with Gemini to get more relevant answers. </message> <message name="IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_TOGGLE_SUBLABEL_DATA_PROTECTED" translateable="false" desc="Sublabel describing that this setting will enable sharing the current tab's content with Gemini, but the user's data is not used to improve the model"> Share page content with Gemini to get more relevant answers. Your data is not used to improve generative AI models. </message> - <message name="IDS_SETTINGS_GLIC_PERMISSIONS_ACTIVITY_BUTTON" translateable="false" desc="Name of the Gemini Apps Activity link out."> + <message name="IDS_SETTINGS_GLIC_PERMISSIONS_ACTIVITY_BUTTON" desc="Name of the Gemini Apps Activity link out."> Gemini Apps Activity </message> - <message name="IDS_SETTINGS_GLIC_PERMISSIONS_ACTIVITY_BUTTON_SUBLABEL" translateable="false" desc="Sublabel describing that this link allows the user to view and manage their activity relating to Gemini Apps."> + <message name="IDS_SETTINGS_GLIC_PERMISSIONS_ACTIVITY_BUTTON_SUBLABEL" desc="Sublabel describing that this link allows the user to view and manage their activity relating to Gemini Apps."> View and manage your Gemini Apps activity </message> <message name="IDS_SETTINGS_GLIC_PERMISSIONS_ACTIVITY_BUTTON_URL" translateable="false" desc="URL to navigate when clicking the 'manage your activity' button."> @@ -195,16 +195,16 @@ <message name="IDS_SETTINGS_GLIC_EXTENSIONS_BUTTON_SUBLABEL" translateable="false" desc="Sublabel describing that this link allows the user to manage Gemini connected apps."> Manage which apps Gemini connects to </message> - <message name="IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_WHEN_ON_1" translateable="false" desc="First point in the 'when on' column of the 'tab access' expando"> + <message name="IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_WHEN_ON_1" desc="First point in the 'when on' column of the 'tab access' expando"> You’ll get more relevant answers related to the page content you’re sharing </message> - <message name="IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_WHEN_ON_2" translateable="false" desc="Second point in the 'when on' column of the 'tab access' expando"> + <message name="IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_WHEN_ON_2" desc="Second point in the 'when on' column of the 'tab access' expando"> You can use voice or text chat to ask Gemini a question about the page content you’re sharing </message> - <message name="IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_CONSIDER_1" translateable="false" desc="First point in 'things to consider' column of the 'tab access' expando"> + <message name="IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_CONSIDER_1" desc="First point in 'things to consider' column of the 'tab access' expando"> When you share a page with Gemini, the page’s full content and URL are sent to Google. </message> - <message name="IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_CONSIDER_1_LEARN_MORE_LABEL" translateable="false" desc="Label for the 'Learn more' link in the 'things to consider' section."> + <message name="IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_CONSIDER_1_LEARN_MORE_LABEL" desc="Label for the 'Learn more' link in the 'things to consider' section."> Learn more </message>
diff --git a/chrome/app/glic_strings_grdp/IDS_GLIC_ERROR_NOTICE.png.sha1 b/chrome/app/glic_strings_grdp/IDS_GLIC_ERROR_NOTICE.png.sha1 new file mode 100644 index 0000000..ad8c54d --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_GLIC_ERROR_NOTICE.png.sha1
@@ -0,0 +1 @@ +7d73e980b037d1ddc67d09e695399d4ad11e7c3b \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_GLIC_ERROR_NOTICE_ACTION_BUTTON.png.sha1 b/chrome/app/glic_strings_grdp/IDS_GLIC_ERROR_NOTICE_ACTION_BUTTON.png.sha1 new file mode 100644 index 0000000..ad8c54d --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_GLIC_ERROR_NOTICE_ACTION_BUTTON.png.sha1
@@ -0,0 +1 @@ +7d73e980b037d1ddc67d09e695399d4ad11e7c3b \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_GLIC_ERROR_NOTICE_HEADER.png.sha1 b/chrome/app/glic_strings_grdp/IDS_GLIC_ERROR_NOTICE_HEADER.png.sha1 new file mode 100644 index 0000000..ad8c54d --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_GLIC_ERROR_NOTICE_HEADER.png.sha1
@@ -0,0 +1 @@ +7d73e980b037d1ddc67d09e695399d4ad11e7c3b \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_GLIC_OFFLINE_NOTICE_ACTION.png.sha1 b/chrome/app/glic_strings_grdp/IDS_GLIC_OFFLINE_NOTICE_ACTION.png.sha1 new file mode 100644 index 0000000..e5b0a72 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_GLIC_OFFLINE_NOTICE_ACTION.png.sha1
@@ -0,0 +1 @@ +2c7ab6689ac2c751f1f12a22e473711c05fe88f0 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_GLIC_OFFLINE_NOTICE_ACTION_BUTTON.png.sha1 b/chrome/app/glic_strings_grdp/IDS_GLIC_OFFLINE_NOTICE_ACTION_BUTTON.png.sha1 new file mode 100644 index 0000000..e5b0a72 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_GLIC_OFFLINE_NOTICE_ACTION_BUTTON.png.sha1
@@ -0,0 +1 @@ +2c7ab6689ac2c751f1f12a22e473711c05fe88f0 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_GLIC_OFFLINE_NOTICE_HEADER.png.sha1 b/chrome/app/glic_strings_grdp/IDS_GLIC_OFFLINE_NOTICE_HEADER.png.sha1 new file mode 100644 index 0000000..e5b0a72 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_GLIC_OFFLINE_NOTICE_HEADER.png.sha1
@@ -0,0 +1 @@ +2c7ab6689ac2c751f1f12a22e473711c05fe88f0 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_GLIC_OS_WIDGET_KEYBOARD_SHORTCUT_HELP_BUBBLE.png.sha1 b/chrome/app/glic_strings_grdp/IDS_GLIC_OS_WIDGET_KEYBOARD_SHORTCUT_HELP_BUBBLE.png.sha1 new file mode 100644 index 0000000..762410d --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_GLIC_OS_WIDGET_KEYBOARD_SHORTCUT_HELP_BUBBLE.png.sha1
@@ -0,0 +1 @@ +f07890116a056982606119954046ca92a7bd1e29 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_GLIC_PROMO_BODY.png.sha1 b/chrome/app/glic_strings_grdp/IDS_GLIC_PROMO_BODY.png.sha1 new file mode 100644 index 0000000..4aa1815 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_GLIC_PROMO_BODY.png.sha1
@@ -0,0 +1 @@ +68d8117b4ce8014b24713046ac148ae30bad2d84 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_GLIC_STATUS_ICON_MENU_CUSTOMIZE_KEYBOARD_SHORTCUT.png.sha1 b/chrome/app/glic_strings_grdp/IDS_GLIC_STATUS_ICON_MENU_CUSTOMIZE_KEYBOARD_SHORTCUT.png.sha1 new file mode 100644 index 0000000..4870984 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_GLIC_STATUS_ICON_MENU_CUSTOMIZE_KEYBOARD_SHORTCUT.png.sha1
@@ -0,0 +1 @@ +43fefbd647b6d1efd926eed99b6d5357d70a8ed9 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_GLIC_STATUS_ICON_MENU_EXIT.png.sha1 b/chrome/app/glic_strings_grdp/IDS_GLIC_STATUS_ICON_MENU_EXIT.png.sha1 new file mode 100644 index 0000000..dd8ef640 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_GLIC_STATUS_ICON_MENU_EXIT.png.sha1
@@ -0,0 +1 @@ +809958c336171f11ae81c0743b506782bf0e61a3 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_GLIC_STATUS_ICON_MENU_SETTINGS.png.sha1 b/chrome/app/glic_strings_grdp/IDS_GLIC_STATUS_ICON_MENU_SETTINGS.png.sha1 new file mode 100644 index 0000000..b4039ae --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_GLIC_STATUS_ICON_MENU_SETTINGS.png.sha1
@@ -0,0 +1 @@ +aa25fa8045471f5b9361a03c23e078d984f89dbc \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_BUTTON_TOGGLE.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_BUTTON_TOGGLE.png.sha1 new file mode 100644 index 0000000..fb1f24d --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_BUTTON_TOGGLE.png.sha1
@@ -0,0 +1 @@ +a2118133219f5608884402199f495649109ec2cb \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_BUTTON_TOGGLE_SUBLABEL.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_BUTTON_TOGGLE_SUBLABEL.png.sha1 new file mode 100644 index 0000000..fff8dbb --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_BUTTON_TOGGLE_SUBLABEL.png.sha1
@@ -0,0 +1 @@ +6e1f16f3341eee61d7a11b471da38f15a9e436e2 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_CLOSED_CAPTIONING.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_CLOSED_CAPTIONING.png.sha1 new file mode 100644 index 0000000..2a1c559 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_CLOSED_CAPTIONING.png.sha1
@@ -0,0 +1 @@ +ee71208cb1f7d968ab63febd1e8855c05da9fed8 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_CLOSED_CAPTIONING_SUBLABEL.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_CLOSED_CAPTIONING_SUBLABEL.png.sha1 new file mode 100644 index 0000000..b2c5d909 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_CLOSED_CAPTIONING_SUBLABEL.png.sha1
@@ -0,0 +1 @@ +96d99c5eead75feee59b699e216bad7267ea9098 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_KEYBOARD_SHORTCUT.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_KEYBOARD_SHORTCUT.png.sha1 new file mode 100644 index 0000000..e7959f73 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_KEYBOARD_SHORTCUT.png.sha1
@@ -0,0 +1 @@ +3b126197c40747f2c89014ddf358785bd3b5ba3e \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_KEYBOARD_SHORTCUT_LEARN_MORE_LABEL.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_KEYBOARD_SHORTCUT_LEARN_MORE_LABEL.png.sha1 new file mode 100644 index 0000000..e396dff --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_KEYBOARD_SHORTCUT_LEARN_MORE_LABEL.png.sha1
@@ -0,0 +1 @@ +c0ef29020e7d782bd47bd2e08ae32a9affb607bc \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_KEYBOARD_SHORTCUT_SUBLABEL.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_KEYBOARD_SHORTCUT_SUBLABEL.png.sha1 new file mode 100644 index 0000000..0d5941e --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_KEYBOARD_SHORTCUT_SUBLABEL.png.sha1
@@ -0,0 +1 @@ +62234e9b560a009611c94ac103400c007c90d3a7 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_NAVIGATION_SHORTCUT.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_NAVIGATION_SHORTCUT.png.sha1 new file mode 100644 index 0000000..23679efe --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_NAVIGATION_SHORTCUT.png.sha1
@@ -0,0 +1 @@ +235e56175ccbfda8b42a050145e517fc636f365d \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_NAVIGATION_SHORTCUT_SUBLABEL.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_NAVIGATION_SHORTCUT_SUBLABEL.png.sha1 new file mode 100644 index 0000000..7c8997f2 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_NAVIGATION_SHORTCUT_SUBLABEL.png.sha1
@@ -0,0 +1 @@ +d57495d35944871d3a12df54c1cf57299468eb0e \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_OS_WIDGET_TOGGLE.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_OS_WIDGET_TOGGLE.png.sha1 new file mode 100644 index 0000000..060b4f7 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_OS_WIDGET_TOGGLE.png.sha1
@@ -0,0 +1 @@ +2d5786b86fc3589d999ffb0a62aa22b6050e9f6f \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_OS_WIDGET_TOGGLE_SUBLABEL.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_OS_WIDGET_TOGGLE_SUBLABEL.png.sha1 new file mode 100644 index 0000000..9526d8e --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_OS_WIDGET_TOGGLE_SUBLABEL.png.sha1
@@ -0,0 +1 @@ +671fae89d146ac6628d60d8633ea06b36d6e2e5a \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_ACTIVITY_BUTTON.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_ACTIVITY_BUTTON.png.sha1 new file mode 100644 index 0000000..ae1f4c01 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_ACTIVITY_BUTTON.png.sha1
@@ -0,0 +1 @@ +c26db834c50f9485f832842ff1f5587382aa7612 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_ACTIVITY_BUTTON_SUBLABEL.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_ACTIVITY_BUTTON_SUBLABEL.png.sha1 new file mode 100644 index 0000000..01df253 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_ACTIVITY_BUTTON_SUBLABEL.png.sha1
@@ -0,0 +1 @@ +d414ced43a9579cc8ffd4ed399af78fca7172ae8 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_LOCATION_TOGGLE.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_LOCATION_TOGGLE.png.sha1 new file mode 100644 index 0000000..a5a2a7c --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_LOCATION_TOGGLE.png.sha1
@@ -0,0 +1 @@ +68d70f3c75fd04403a402d3cfeace24cd0863ebf \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_LOCATION_TOGGLE_SUBLABEL.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_LOCATION_TOGGLE_SUBLABEL.png.sha1 new file mode 100644 index 0000000..c6fcfc1 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_LOCATION_TOGGLE_SUBLABEL.png.sha1
@@ -0,0 +1 @@ +5c92083eb59b4cf9d9f196c7068161f8f42b439f \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_MICROPHONE_TOGGLE.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_MICROPHONE_TOGGLE.png.sha1 new file mode 100644 index 0000000..76e13c14 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_MICROPHONE_TOGGLE.png.sha1
@@ -0,0 +1 @@ +b5e7b0bbed6e95b8dfee01e746af36b4d3c22ace \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_MICROPHONE_TOGGLE_SUBLABEL.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_MICROPHONE_TOGGLE_SUBLABEL.png.sha1 new file mode 100644 index 0000000..1753e06 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_MICROPHONE_TOGGLE_SUBLABEL.png.sha1
@@ -0,0 +1 @@ +e04174381629da4dfb34057bc302c79ac1c80a1e \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_CONSIDER_1.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_CONSIDER_1.png.sha1 new file mode 100644 index 0000000..91c81ef --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_CONSIDER_1.png.sha1
@@ -0,0 +1 @@ +3e176d6973bbd18627a32fe251c75b90e8898302 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_CONSIDER_1_LEARN_MORE_LABEL.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_CONSIDER_1_LEARN_MORE_LABEL.png.sha1 new file mode 100644 index 0000000..72bc5c30b --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_CONSIDER_1_LEARN_MORE_LABEL.png.sha1
@@ -0,0 +1 @@ +879611abcc09295863794f99adc05a0b486d0b05 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_TOGGLE.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_TOGGLE.png.sha1 new file mode 100644 index 0000000..6bc62ba --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_TOGGLE.png.sha1
@@ -0,0 +1 @@ +872faa3a31c25ba6d0077a253d29de7f410f4151 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_TOGGLE_SUBLABEL.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_TOGGLE_SUBLABEL.png.sha1 new file mode 100644 index 0000000..ee06129 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_TOGGLE_SUBLABEL.png.sha1
@@ -0,0 +1 @@ +49534b4ee77eb63c85d3d79727aedc1f273208d3 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_WHEN_ON_1.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_WHEN_ON_1.png.sha1 new file mode 100644 index 0000000..9f26f2b --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_WHEN_ON_1.png.sha1
@@ -0,0 +1 @@ +d6c87789c5e8f635ebe4db11a0093018b5f43ca5 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_WHEN_ON_2.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_WHEN_ON_2.png.sha1 new file mode 100644 index 0000000..87bc9f06 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PERMISSIONS_TAB_ACCESS_WHEN_ON_2.png.sha1
@@ -0,0 +1 @@ +819971246eb77e37f1b8022d1547c7eaa9a84e1a \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PREFERENCES_SECTION.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PREFERENCES_SECTION.png.sha1 new file mode 100644 index 0000000..b59e269 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_PREFERENCES_SECTION.png.sha1
@@ -0,0 +1 @@ +6ada2815888611415b88f540af732bca62990613 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_ROW_SUBLABEL.png.sha1 b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_ROW_SUBLABEL.png.sha1 new file mode 100644 index 0000000..31b845d --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_SETTINGS_GLIC_ROW_SUBLABEL.png.sha1
@@ -0,0 +1 @@ +81eb57ce56bbc3947862403b3f9cde713733bcc4 \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_TOOLTIP_TAB_ALERT_STATE_GLIC_ACCESSING.png.sha1 b/chrome/app/glic_strings_grdp/IDS_TOOLTIP_TAB_ALERT_STATE_GLIC_ACCESSING.png.sha1 new file mode 100644 index 0000000..ae52807 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_TOOLTIP_TAB_ALERT_STATE_GLIC_ACCESSING.png.sha1
@@ -0,0 +1 @@ +bdee8d92cdc76a8ede7f0c872e074e0699853e9c \ No newline at end of file
diff --git a/chrome/app/glic_strings_grdp/IDS_TOOLTIP_TAB_ALERT_STATE_GLIC_SHARING.png.sha1 b/chrome/app/glic_strings_grdp/IDS_TOOLTIP_TAB_ALERT_STATE_GLIC_SHARING.png.sha1 new file mode 100644 index 0000000..ae52807 --- /dev/null +++ b/chrome/app/glic_strings_grdp/IDS_TOOLTIP_TAB_ALERT_STATE_GLIC_SHARING.png.sha1
@@ -0,0 +1 @@ +bdee8d92cdc76a8ede7f0c872e074e0699853e9c \ No newline at end of file
diff --git a/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/2.svg b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/2.svg new file mode 100644 index 0000000..8b6d5c50 --- /dev/null +++ b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/2.svg
@@ -0,0 +1,3 @@ +<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M716.976 511.949C717.015 547.938 707.532 583.296 689.49 614.436L689.512 614.448L511.977 921.948C583.942 921.933 654.635 902.98 716.956 866.993C779.277 831.006 831.031 779.253 867.019 716.932C903.006 654.611 921.96 583.918 921.976 511.953C921.992 439.988 903.071 369.285 867.111 306.948H511.977C538.898 306.948 565.555 312.251 590.427 322.553C615.298 332.856 637.897 347.956 656.933 366.992C675.969 386.028 691.069 408.627 701.372 433.499C711.674 458.371 716.976 485.028 716.976 511.949V511.949Z" fill="#AECBFA" style="fill:#AECBFA;fill:color(display-p3 0.6824 0.7961 0.9804);fill-opacity:1;"/> +</svg>
diff --git a/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/3.svg b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/3.svg new file mode 100644 index 0000000..bccf84d --- /dev/null +++ b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/3.svg
@@ -0,0 +1,3 @@ +<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M409.531 689.485C378.343 671.524 352.463 645.632 334.517 614.436L334.495 614.448L156.96 306.948C120.991 369.28 102.058 439.979 102.063 511.944C102.067 583.91 121.01 654.606 156.988 716.933C192.965 779.259 244.711 831.021 307.027 867.018C369.342 903.014 440.033 921.978 511.999 922.005L689.567 614.45C676.106 637.764 658.185 658.199 636.828 674.587C615.47 690.976 591.093 702.997 565.089 709.965C539.085 716.933 511.964 718.71 485.273 715.196C458.582 711.682 432.845 702.946 409.531 689.485V689.485Z" fill="#669DF6" style="fill:#669DF6;fill:color(display-p3 0.4000 0.6157 0.9647);fill-opacity:1;"/> +</svg>
diff --git a/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/5.svg b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/5.svg new file mode 100644 index 0000000..f24e7ec8 --- /dev/null +++ b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/5.svg
@@ -0,0 +1,3 @@ +<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M409.468 334.515C440.616 316.487 475.979 307.02 511.968 307.076V307.051H867.038C831.042 244.735 779.281 192.99 716.955 157.012C654.63 121.034 583.933 102.091 511.969 102.085C440.004 102.08 369.305 121.012 306.974 156.98C244.642 192.948 192.873 244.685 156.867 306.995L334.434 614.551C320.974 591.237 312.237 565.5 308.723 538.809C305.209 512.118 306.986 484.997 313.954 458.993C320.921 432.989 332.942 408.612 349.331 387.254C365.719 365.896 386.154 347.975 409.468 334.515Z" fill="#1967D2" style="fill:#1967D2;fill:color(display-p3 0.0980 0.4039 0.8235);fill-opacity:1;"/> +</svg>
diff --git a/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/blue.svg b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/blue.svg new file mode 100644 index 0000000..3816d635 --- /dev/null +++ b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/blue.svg
@@ -0,0 +1,3 @@ +<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M512 678.562C603.99 678.562 678.562 603.99 678.562 512C678.562 420.01 603.99 345.438 512 345.438C420.01 345.438 345.438 420.01 345.438 512C345.438 603.99 420.01 678.562 512 678.562Z" fill="#1A73E8" style="fill:#1A73E8;fill:color(display-p3 0.1020 0.4510 0.9098);fill-opacity:1;"/> +</svg>
diff --git a/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/cr.svg b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/cr.svg new file mode 100644 index 0000000..81c4acde --- /dev/null +++ b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/cr.svg
@@ -0,0 +1,3 @@ +<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M186.855 235.968C180.869 235.968 175.375 234.902 170.373 232.77C165.453 230.556 161.148 227.44 157.458 223.422C153.85 219.322 151.062 214.484 149.094 208.908C147.126 203.25 146.142 196.936 146.142 189.966C146.142 182.996 147.126 176.682 149.094 171.024C151.144 165.366 153.973 160.528 157.581 156.51C161.271 152.492 165.617 149.417 170.619 147.285C175.703 145.071 181.238 143.964 187.224 143.964C193.538 143.964 199.401 145.071 204.813 147.285C210.225 149.499 214.694 152.779 218.22 157.125C221.828 161.471 224.001 166.924 224.739 173.484L207.765 176.313C207.027 172.787 205.715 169.794 203.829 167.334C201.943 164.874 199.606 162.988 196.818 161.676C194.03 160.282 190.832 159.585 187.224 159.585C182.714 159.585 178.573 160.774 174.801 163.152C171.111 165.448 168.159 168.851 165.945 173.361C163.813 177.871 162.747 183.406 162.747 189.966C162.747 196.444 163.813 201.938 165.945 206.448C168.077 210.958 170.988 214.402 174.678 216.78C178.368 219.076 182.55 220.224 187.224 220.224C192.472 220.224 196.941 218.789 200.631 215.919C204.321 213.049 206.699 209.031 207.765 203.865L225.108 205.71C223.878 211.614 221.664 216.862 218.466 221.454C215.35 225.964 211.168 229.531 205.92 232.155C200.754 234.697 194.399 235.968 186.855 235.968ZM238.535 234V169.302H253.664V177.666H254.648C255.55 175.698 256.739 173.935 258.215 172.377C259.773 170.819 261.577 169.589 263.627 168.687C265.759 167.785 268.137 167.334 270.761 167.334C272.319 167.334 273.59 167.416 274.574 167.58C275.558 167.744 276.419 167.908 277.157 168.072V184.062C276.009 183.57 274.697 183.16 273.221 182.832C271.745 182.504 270.064 182.34 268.178 182.34C265.554 182.34 263.217 182.996 261.167 184.308C259.117 185.62 257.518 187.506 256.37 189.966C255.222 192.426 254.648 195.337 254.648 198.699V234H238.535Z" fill="#1967D2" style="fill:#1967D2;fill:color(display-p3 0.0980 0.4039 0.8235);fill-opacity:1;"/> +</svg>
diff --git a/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/donut.svg b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/donut.svg new file mode 100644 index 0000000..26fbe51 --- /dev/null +++ b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/donut.svg
@@ -0,0 +1,4 @@ +<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M511.984 306.938C625.203 306.938 716.984 398.719 716.984 511.938C716.984 625.156 625.203 716.938 511.984 716.938C398.766 716.938 306.984 625.156 306.984 511.938C306.985 398.719 398.766 306.938 511.984 306.938ZM511.984 345.393C419.994 345.393 345.422 419.965 345.422 511.955C345.422 603.945 419.995 678.518 511.984 678.518C603.974 678.518 678.547 603.945 678.547 511.955C678.547 419.965 603.974 345.393 511.984 345.393Z" fill="white" style="fill:white;fill-opacity:1;"/> +<path d="M716.984 511.938C716.984 398.719 625.203 306.938 511.984 306.938C398.766 306.938 306.985 398.719 306.984 511.938C306.984 625.156 398.766 716.938 511.984 716.938C625.203 716.938 716.984 625.156 716.984 511.938ZM674.547 511.955C674.547 422.174 601.765 349.393 511.984 349.393C422.204 349.393 349.422 422.174 349.422 511.955C349.422 601.736 422.204 674.518 511.984 674.518V678.518C419.995 678.518 345.422 603.945 345.422 511.955C345.422 419.965 419.994 345.393 511.984 345.393C603.974 345.393 678.547 419.965 678.547 511.955C678.547 603.945 603.974 678.518 511.984 678.518V674.518C601.064 674.518 673.408 602.869 674.533 514.057L674.547 511.955ZM720.984 511.938C720.984 627.365 627.412 720.938 511.984 720.938C396.557 720.938 302.984 627.365 302.984 511.938C302.985 396.51 396.557 302.938 511.984 302.938C627.412 302.938 720.984 396.51 720.984 511.938Z" fill="white" style="fill:white;fill-opacity:1;"/> +</svg>
diff --git a/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/pill.svg b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/pill.svg new file mode 100644 index 0000000..3a3a5e8 --- /dev/null +++ b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/pill.svg
@@ -0,0 +1,3 @@ +<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect x="75.5" y="95" width="269" height="194" rx="97" fill="#AECBFA" style="fill:#AECBFA;fill:color(display-p3 0.6824 0.7961 0.9804);fill-opacity:1;"/> +</svg>
diff --git a/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/ring.png b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/ring.png new file mode 100644 index 0000000..e36f8dda --- /dev/null +++ b/chrome/app/theme/chromium/mac/AppIcon.icon/Assets/ring.png Binary files differ
diff --git a/chrome/app/theme/chromium/mac/AppIcon.icon/icon.json b/chrome/app/theme/chromium/mac/AppIcon.icon/icon.json new file mode 100644 index 0000000..f8e12d5 --- /dev/null +++ b/chrome/app/theme/chromium/mac/AppIcon.icon/icon.json
@@ -0,0 +1,332 @@ +{ + "fill" : { + "solid" : "extended-gray:1.00000,1.00000" + }, + "groups" : [ + { + "layers" : [ + { + "fill-specializations" : [ + { + "appearance" : "tinted", + "value" : { + "solid" : "extended-gray:0.00000,1.00000" + } + } + ], + "glass" : false, + "hidden" : false, + "image-name" : "cr.svg", + "name" : "cr", + "opacity-specializations" : [ + { + "value" : 0 + }, + { + "appearance" : "tinted", + "value" : 1 + } + ] + }, + { + "fill-specializations" : [ + { + "appearance" : "tinted", + "value" : { + "solid" : "extended-gray:0.80182,1.00000" + } + } + ], + "glass-specializations" : [ + { + "value" : false + }, + { + "appearance" : "tinted", + "value" : true + } + ], + "image-name" : "pill.svg", + "name" : "pill", + "opacity-specializations" : [ + { + "value" : 0 + }, + { + "appearance" : "tinted", + "value" : 1 + } + ] + } + ], + "opacity-specializations" : [ + { + "value" : 0 + }, + { + "appearance" : "tinted", + "value" : 1 + } + ], + "shadow-specializations" : [ + { + "value" : { + "kind" : "layer-color", + "opacity" : 0.5 + } + }, + { + "appearance" : "tinted", + "value" : { + "kind" : "layer-color", + "opacity" : 1 + } + } + ], + "specular-specializations" : [ + { + "value" : false + }, + { + "appearance" : "tinted", + "value" : true + } + ], + "translucency" : { + "enabled" : false, + "value" : 0.1 + } + }, + { + "layers" : [ + { + "image-name" : "ring.png", + "name" : "ring", + "opacity-specializations" : [ + { + "appearance" : "tinted", + "value" : 0 + } + ] + }, + { + "fill-specializations" : [ + { + "appearance" : "tinted", + "value" : { + "linear-gradient" : [ + "extended-gray:0.90202,1.00000", + "extended-gray:0.30292,1.00000" + ] + } + } + ], + "image-name" : "5.svg", + "name" : "red", + "opacity-specializations" : [ + { + "value" : 0 + }, + { + "appearance" : "tinted", + "value" : 1 + } + ] + }, + { + "fill-specializations" : [ + { + "appearance" : "tinted", + "value" : { + "linear-gradient" : [ + "extended-gray:0.30292,1.00000", + "extended-gray:0.90202,1.00000" + ] + } + } + ], + "image-name" : "2.svg", + "name" : "yellow", + "opacity-specializations" : [ + { + "value" : 0 + }, + { + "appearance" : "tinted", + "value" : 1 + } + ] + }, + { + "fill-specializations" : [ + { + "appearance" : "tinted", + "value" : { + "linear-gradient" : [ + "extended-gray:0.90202,1.00000", + "extended-gray:0.30292,1.00000" + ] + } + } + ], + "image-name" : "3.svg", + "name" : "green", + "opacity-specializations" : [ + { + "value" : 0 + }, + { + "appearance" : "tinted", + "value" : 1 + } + ] + } + ], + "lighting-specializations" : [ + { + "value" : "combined" + }, + { + "appearance" : "tinted", + "value" : "individual" + } + ], + "shadow" : { + "kind" : "layer-color", + "opacity" : 0.5 + }, + "translucency-specializations" : [ + { + "value" : { + "enabled" : true, + "value" : 0.1 + } + }, + { + "appearance" : "dark", + "value" : { + "enabled" : false, + "value" : 0.1 + } + }, + { + "appearance" : "tinted", + "value" : { + "enabled" : true, + "value" : 0.1 + } + } + ] + }, + { + "hidden-specializations" : [ + { + "idiom" : "square", + "value" : false + } + ], + "layers" : [ + { + "fill-specializations" : [ + { + "appearance" : "tinted", + "value" : { + "solid" : "extended-gray:0.70180,1.00000" + } + } + ], + "glass" : true, + "hidden" : false, + "image-name-specializations" : [ + { + "value" : "2 – Layer.svg" + }, + { + "idiom" : "square", + "value" : "blue.svg" + } + ], + "name" : "blue" + } + ], + "lighting" : "combined", + "shadow-specializations" : [ + { + "value" : { + "kind" : "neutral", + "opacity" : 0.5 + } + }, + { + "appearance" : "dark", + "value" : { + "kind" : "layer-color", + "opacity" : 0.5 + } + }, + { + "appearance" : "tinted", + "value" : { + "kind" : "layer-color", + "opacity" : 0.5 + } + } + ], + "translucency-specializations" : [ + { + "value" : { + "enabled" : true, + "value" : 0.1 + } + }, + { + "appearance" : "dark", + "value" : { + "enabled" : false, + "value" : 0.1 + } + }, + { + "appearance" : "tinted", + "value" : { + "enabled" : true, + "value" : 0.1 + } + } + ] + }, + { + "hidden-specializations" : [ + { + "idiom" : "square", + "value" : false + } + ], + "layers" : [ + { + "glass" : false, + "image-name" : "donut.svg", + "name" : "donut" + } + ], + "opacity-specializations" : [ + { + "appearance" : "tinted", + "value" : 0 + } + ], + "shadow" : { + "kind" : "none", + "opacity" : 0.5 + }, + "specular" : false, + "translucency" : { + "enabled" : false, + "value" : 0.5 + } + } + ], + "supported-platforms" : { + "squares" : "shared" + } +} \ No newline at end of file
diff --git a/chrome/app/theme/chromium/mac/Assets.car b/chrome/app/theme/chromium/mac/Assets.car index 302ae118d0..cc692a8 100644 --- a/chrome/app/theme/chromium/mac/Assets.car +++ b/chrome/app/theme/chromium/mac/Assets.car Binary files differ
diff --git a/chrome/app/theme/chromium/mac/app.icns b/chrome/app/theme/chromium/mac/app.icns index d2d7296..97072c8c 100644 --- a/chrome/app/theme/chromium/mac/app.icns +++ b/chrome/app/theme/chromium/mac/app.icns Binary files differ
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index e023fec..7445a84f 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -416,6 +416,8 @@ "data_sharing/data_sharing_navigation_utils.h", "data_sharing/data_sharing_service_factory.cc", "data_sharing/data_sharing_service_factory.h", + "data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory.cc", + "data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory.h", "defaults.cc", "defaults.h", "digital_credentials/digital_credentials_keyed_service.cc", @@ -2197,6 +2199,7 @@ "//components/crx_file", "//components/custom_handlers", "//components/data_sharing/internal", + "//components/data_sharing/internal:personal_collaboration_data_internal", "//components/data_sharing/public", "//components/device_event_log", "//components/device_reauth",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index b47dab3..1dde07ad 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -1219,6 +1219,7 @@ {"find", "true"}, {"lens_overlay", "true"}, {"translate", "true"}, + {"mandatory_reauth", "true"}, {"memory_saver", "true"}, {"price_insights", "true"}, {"offer_notification", "true"}, @@ -4786,6 +4787,15 @@ }; #endif // BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_ANDROID) +const FeatureEntry::FeatureParam kAndroidTabDeclutterAutoDeleteTesting[] = { + {"android_tab_declutter_auto_delete_promo_test", "true"}}; +const FeatureEntry::FeatureVariation + kAndroidTabDeclutterAutoDeleteVariations[] = { + {"Testing", kAndroidTabDeclutterAutoDeleteTesting, + std::size(kAndroidTabDeclutterAutoDeleteTesting), nullptr}}; +#endif // BUILDFLAG(IS_ANDROID) + // RECORDING USER METRICS FOR FLAGS: // ----------------------------------------------------------------------------- // The first line of the entry is the internal name. @@ -5986,6 +5996,10 @@ {"draw-cutout-edge-to-edge", flag_descriptions::kDrawCutoutEdgeToEdgeName, flag_descriptions::kDrawCutoutEdgeToEdgeDescription, kOsAndroid, FEATURE_VALUE_TYPE(features::kDrawCutoutEdgeToEdge)}, + {"draw-chrome-pages-edge-to-edge", + flag_descriptions::kDrawChromePagesEdgeToEdgeName, + flag_descriptions::kDrawChromePagesEdgeToEdgeDescription, kOsAndroid, + FEATURE_VALUE_TYPE(chrome::android::kDrawChromePagesEdgeToEdge)}, {"edge-to-edge-bottom-chin", flag_descriptions::kEdgeToEdgeBottomChinName, flag_descriptions::kEdgeToEdgeBottomChinDescription, kOsAndroid, FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kEdgeToEdgeBottomChin, @@ -10964,7 +10978,10 @@ {"android-tab-declutter-auto-delete", flag_descriptions::kAndroidTabDeclutterAutoDeleteName, flag_descriptions::kAndroidTabDeclutterAutoDeleteDescription, kOsAndroid, - FEATURE_VALUE_TYPE(chrome::android::kAndroidTabDeclutterAutoDelete)}, + FEATURE_WITH_PARAMS_VALUE_TYPE( + chrome::android::kAndroidTabDeclutterAutoDelete, + kAndroidTabDeclutterAutoDeleteVariations, + "AndroidTabDeclutterAutoDeleteVariations")}, {"android-tab-declutter-auto-delete-kill-switch", flag_descriptions::kAndroidTabDeclutterAutoDeleteKillSwitchName,
diff --git a/chrome/browser/actor/DEPS b/chrome/browser/actor/DEPS index 3e5331c..a51d963 100644 --- a/chrome/browser/actor/DEPS +++ b/chrome/browser/actor/DEPS
@@ -32,6 +32,7 @@ # Test dependencies on UiEventDispatcher. 'actor_keyed_service_unittest\.cc': [ "+chrome/browser/actor/ui/event_dispatcher.h", + "+chrome/browser/actor/ui/mock_actor_ui_state_manager.h", ], 'actor_keyed_service_fake*': [ # This is required for unit tests.
diff --git a/chrome/browser/actor/actor_keyed_service.cc b/chrome/browser/actor/actor_keyed_service.cc index 7f8ce25..4c4328c 100644 --- a/chrome/browser/actor/actor_keyed_service.cc +++ b/chrome/browser/actor/actor_keyed_service.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "base/containers/span.h" #include "base/task/single_thread_task_runner.h" #include "base/types/pass_key.h" #include "chrome/browser/actor/actor_keyed_service_factory.h" @@ -106,7 +107,7 @@ task->Act(std::move(actions), base::BindOnce(&ActorKeyedService::OnActionFinished, weak_ptr_factory_.GetWeakPtr(), std::move(callback), - task_id.value())); + task_id)); } TaskId ActorKeyedService::CreateTask() { @@ -172,8 +173,9 @@ void ActorKeyedService::ConvertToBrowserActionResult( base::OnceCallback<void(optimization_guide::proto::BrowserActionResult)> callback, - int task_id, + TaskId task_id, int32_t tab_id, + const GURL& url, actor::mojom::ActionResultPtr action_result, base::expected<std::unique_ptr<optimization_guide::proto::TabObservation>, std::string> context_result) { @@ -187,16 +189,25 @@ } auto& tab_observation = **context_result; if (tab_observation.has_annotated_page_content()) { + size_t size = tab_observation.annotated_page_content().ByteSizeLong(); + std::vector<uint8_t> buffer(size); + tab_observation.annotated_page_content().SerializeToArray(buffer.data(), + size); + journal_.LogAnnotatedPageContent(url, task_id, buffer); + browser_action_result.mutable_annotated_page_content()->Swap( tab_observation.mutable_annotated_page_content()); } if (tab_observation.has_screenshot()) { + journal_.LogScreenshot(url, task_id, tab_observation.screenshot_mime_type(), + base::as_byte_span(tab_observation.screenshot())); + browser_action_result.set_screenshot(tab_observation.screenshot().data(), tab_observation.screenshot().size()); browser_action_result.set_screenshot_mime_type( tab_observation.screenshot_mime_type()); } - browser_action_result.set_task_id(task_id); + browser_action_result.set_task_id(task_id.value()); browser_action_result.set_tab_id(tab_id); browser_action_result.set_action_result(actor::IsOk(*action_result) ? 1 : 0); RunLater( @@ -206,7 +217,7 @@ void ActorKeyedService::OnActionFinished( base::OnceCallback<void(optimization_guide::proto::BrowserActionResult)> callback, - int task_id, + TaskId task_id, actor::mojom::ActionResultPtr action_result, std::optional<size_t> index_of_failed_action) { auto* task = GetTask(actor::TaskId(task_id)); @@ -221,9 +232,11 @@ } int32_t tab_id = tab->GetHandle().raw_value(); RequestTabObservation( - *tab, base::BindOnce(&ActorKeyedService::ConvertToBrowserActionResult, - weak_ptr_factory_.GetWeakPtr(), std::move(callback), - task_id, tab_id, std::move(action_result))); + *tab, + base::BindOnce(&ActorKeyedService::ConvertToBrowserActionResult, + weak_ptr_factory_.GetWeakPtr(), std::move(callback), + task_id, tab_id, tab->GetContents()->GetLastCommittedURL(), + std::move(action_result))); } void ActorKeyedService::PerformActions(
diff --git a/chrome/browser/actor/actor_keyed_service.h b/chrome/browser/actor/actor_keyed_service.h index 62ee5102..9101efb 100644 --- a/chrome/browser/actor/actor_keyed_service.h +++ b/chrome/browser/actor/actor_keyed_service.h
@@ -22,6 +22,7 @@ #include "components/optimization_guide/proto/features/actions_data.pb.h" #include "components/optimization_guide/proto/features/model_prototyping.pb.h" #include "components/tabs/public/tab_interface.h" +#include "url/gurl.h" class Profile; @@ -129,7 +130,7 @@ void OnActionFinished( base::OnceCallback<void(optimization_guide::proto::BrowserActionResult)> callback, - int task_id, + TaskId task_id, actor::mojom::ActionResultPtr action_result, std::optional<size_t> index_of_failed_action); @@ -141,8 +142,9 @@ void ConvertToBrowserActionResult( base::OnceCallback<void(optimization_guide::proto::BrowserActionResult)> callback, - int task_id, + TaskId task_id, int32_t tab_id, + const GURL& url, actor::mojom::ActionResultPtr action_result, TabObservationResult context_result); void OnTabOservationResult(
diff --git a/chrome/browser/actor/actor_keyed_service_unittest.cc b/chrome/browser/actor/actor_keyed_service_unittest.cc index e95f536..8a10113 100644 --- a/chrome/browser/actor/actor_keyed_service_unittest.cc +++ b/chrome/browser/actor/actor_keyed_service_unittest.cc
@@ -12,6 +12,7 @@ #include "chrome/browser/actor/actor_test_util.h" #include "chrome/browser/actor/execution_engine.h" #include "chrome/browser/actor/ui/event_dispatcher.h" +#include "chrome/browser/actor/ui/mock_actor_ui_state_manager.h" #include "chrome/common/actor/action_result.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" @@ -23,6 +24,18 @@ namespace { +using ::testing::_; + +std::unique_ptr<ui::ActorUiStateManagerInterface> BuildUiStateManagerMock() { + std::unique_ptr<ui::MockActorUiStateManager> ui_state_manager = + std::make_unique<ui::MockActorUiStateManager>(); + ON_CALL(*ui_state_manager, OnUiEvent(_, _)) + .WillByDefault([](ui::AsyncUiEvent, ui::UiCompleteCallback callback) { + std::move(callback).Run(MakeOkResult()); + }); + return ui_state_manager; +} + class ActorKeyedServiceTest : public testing::Test { public: ActorKeyedServiceTest() @@ -51,6 +64,7 @@ // Adds a task to ActorKeyedService TEST_F(ActorKeyedServiceTest, AddActiveTask) { auto* actor_service = ActorKeyedService::Get(profile()); + actor_service->SetActorUiStateManagerForTesting(BuildUiStateManagerMock()); std::unique_ptr<ExecutionEngine> execution_engine = std::make_unique<ExecutionEngine>(profile()); actor_service->AddActiveTask(std::make_unique<ActorTask>( @@ -64,6 +78,7 @@ // Stops a task. TEST_F(ActorKeyedServiceTest, StopActiveTask) { auto* actor_service = ActorKeyedService::Get(profile()); + actor_service->SetActorUiStateManagerForTesting(BuildUiStateManagerMock()); std::unique_ptr<ExecutionEngine> execution_engine = std::make_unique<ExecutionEngine>(profile()); TaskId id = actor_service->AddActiveTask(std::make_unique<ActorTask>(
diff --git a/chrome/browser/actor/aggregated_journal.cc b/chrome/browser/actor/aggregated_journal.cc index 6a2a4e6b..49652c8 100644 --- a/chrome/browser/actor/aggregated_journal.cc +++ b/chrome/browser/actor/aggregated_journal.cc
@@ -188,14 +188,14 @@ void AggregatedJournal::LogScreenshot(const GURL& url, TaskId task_id, std::string_view mime_type, - const std::vector<uint8_t>& data) { + base::span<const uint8_t> data) { CHECK_EQ(mime_type, "image/jpeg"); auto entry = std::make_unique<Entry>( url.possibly_invalid_spec(), mojom::JournalEntry::New(mojom::JournalEntryType::kInstant, task_id.value(), /*id=*/0, base::Time::Now(), "Screenshot", /*details=*/std::string())); - entry->jpg_screenshot.emplace(data); + entry->jpg_screenshot.emplace(data.begin(), data.end()); AddEntry(std::move(entry)); }
diff --git a/chrome/browser/actor/aggregated_journal.h b/chrome/browser/actor/aggregated_journal.h index 4a501c0e..0552266e 100644 --- a/chrome/browser/actor/aggregated_journal.h +++ b/chrome/browser/actor/aggregated_journal.h
@@ -10,6 +10,7 @@ #include <vector> #include "base/containers/ring_buffer.h" +#include "base/containers/span.h" #include "base/memory/safe_ref.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" @@ -99,7 +100,7 @@ void LogScreenshot(const GURL& url, TaskId task_id, std::string_view mime_type, - const std::vector<uint8_t>& data); + base::span<const uint8_t> data); // Log Annotated Page Content. void LogAnnotatedPageContent(const GURL& url,
diff --git a/chrome/browser/actor/ui/actor_overlay_browsertest.cc b/chrome/browser/actor/ui/actor_overlay_browsertest.cc index 71fd4616..783bcc5 100644 --- a/chrome/browser/actor/ui/actor_overlay_browsertest.cc +++ b/chrome/browser/actor/ui/actor_overlay_browsertest.cc
@@ -110,9 +110,18 @@ // The main actor_overlay_view container should initially be hidden. It should // also have no children. - EXPECT_FALSE(browser()->GetBrowserView().GetActorOverlayView()->GetVisible()); - EXPECT_EQ( - browser()->GetBrowserView().GetActorOverlayView()->children().size(), 0u); + EXPECT_FALSE(browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->GetVisible()); + EXPECT_EQ(browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->children() + .size(), + 0u); content::BrowserContext* browser_context = browser()->tab_strip_model()->GetActiveWebContents()->GetBrowserContext(); @@ -125,16 +134,29 @@ // Verify container size and that it remains hidden because the child is // hidden. - EXPECT_EQ( - browser()->GetBrowserView().GetActorOverlayView()->children().size(), 1u); - EXPECT_FALSE(browser()->GetBrowserView().GetActorOverlayView()->GetVisible()); + EXPECT_EQ(browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->children() + .size(), + 1u); + EXPECT_FALSE(browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->GetVisible()); // Make the added WebView visible, and update the container's visibility. overlay_web_view->SetVisible(true); window_controller->MaybeUpdateContainerVisibility(); // Container view should now be visible. - EXPECT_TRUE(browser()->GetBrowserView().GetActorOverlayView()->GetVisible()); + EXPECT_TRUE(browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->GetVisible()); std::unique_ptr<views::WebView> managed_overlay_web_view = window_controller->RemoveChildWebView(overlay_web_view); // The raw_ptr to the removed view is now invalid, so set it to nullptr. @@ -143,9 +165,18 @@ // Confirm managed WebView is not null and the container should become hidden // again ASSERT_NE(managed_overlay_web_view, nullptr); - EXPECT_FALSE(browser()->GetBrowserView().GetActorOverlayView()->GetVisible()); - EXPECT_EQ( - browser()->GetBrowserView().GetActorOverlayView()->children().size(), 0u); + EXPECT_FALSE(browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->GetVisible()); + EXPECT_EQ(browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->children() + .size(), + 0u); } IN_PROC_BROWSER_TEST_F(ActorOverlayTest, SendStartEventAndStopEvent) { @@ -160,24 +191,44 @@ actor::ui::StartingToActOnTab(tab_handle, actor::TaskId(1)), base::DoNothing()); ASSERT_TRUE(base::test::RunUntil([&]() { - return browser()->GetBrowserView().GetActorOverlayView()->GetVisible(); + return browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->GetVisible(); })); - EXPECT_EQ( - browser()->GetBrowserView().GetActorOverlayView()->children().size(), 1u); + EXPECT_EQ(browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->children() + .size(), + 1u); EXPECT_TRUE(browser() ->GetBrowserView() - .GetActorOverlayView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() ->children()[0] ->GetVisible()); state_manager->OnUiEvent(actor::ui::StoppedActingOnTab(tab_handle)); ASSERT_TRUE(base::test::RunUntil([&]() { - return !browser()->GetBrowserView().GetActorOverlayView()->GetVisible(); + return !browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->GetVisible(); })); - EXPECT_EQ( - browser()->GetBrowserView().GetActorOverlayView()->children().size(), 1u); + EXPECT_EQ(browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->children() + .size(), + 1u); EXPECT_FALSE(browser() ->GetBrowserView() - .GetActorOverlayView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() ->children()[0] ->GetVisible()); } @@ -193,37 +244,67 @@ actor::ui::StartingToActOnTab(tab_handle, actor::TaskId(1)), base::DoNothing()); ASSERT_TRUE(base::test::RunUntil([&]() { - return browser()->GetBrowserView().GetActorOverlayView()->GetVisible(); + return browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->GetVisible(); })); - EXPECT_EQ( - browser()->GetBrowserView().GetActorOverlayView()->children().size(), 1u); + EXPECT_EQ(browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->children() + .size(), + 1u); EXPECT_TRUE(browser() ->GetBrowserView() - .GetActorOverlayView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() ->children()[0] ->GetVisible()); browser()->tab_strip_model()->AppendWebContents( content::WebContents::Create(content::WebContents::CreateParams(profile)), /*foreground=*/true); ASSERT_TRUE(base::test::RunUntil([&]() { - return !browser()->GetBrowserView().GetActorOverlayView()->GetVisible(); + return !browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->GetVisible(); })); - EXPECT_EQ( - browser()->GetBrowserView().GetActorOverlayView()->children().size(), 1u); + EXPECT_EQ(browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->children() + .size(), + 1u); EXPECT_FALSE(browser() ->GetBrowserView() - .GetActorOverlayView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() ->children()[0] ->GetVisible()); browser()->tab_strip_model()->ActivateTabAt(0); ASSERT_TRUE(base::test::RunUntil([&]() { - return browser()->GetBrowserView().GetActorOverlayView()->GetVisible(); + return browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->GetVisible(); })); - EXPECT_EQ( - browser()->GetBrowserView().GetActorOverlayView()->children().size(), 1u); + EXPECT_EQ(browser() + ->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->children() + .size(), + 1u); EXPECT_TRUE(browser() ->GetBrowserView() - .GetActorOverlayView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() ->children()[0] ->GetVisible()); } @@ -264,7 +345,10 @@ actor::ui::StartingToActOnTab(tab_2->GetHandle(), actor::TaskId(1)), base::DoNothing()); ASSERT_TRUE(base::test::RunUntil([&]() { - return browser_1->GetBrowserView().GetActorOverlayView()->GetVisible(); + return browser_1->GetBrowserView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() + ->GetVisible(); })); // Loop to repeatedly move the actuated tab between the two windows. // This verifies the overlay's persistence and correct re-parenting across @@ -283,7 +367,8 @@ // Verify the overlay is visible in the *new* browser holding tab_2. ASSERT_TRUE(base::test::RunUntil([&]() { return target_browser->GetBrowserView() - .GetActorOverlayView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() ->GetVisible(); })); // Verify tab counts after each move. @@ -297,7 +382,8 @@ ASSERT_TRUE(base::test::RunUntil([&]() { // Overlay should become invisible in the browser that currently holds tab_1 return !target_browser->GetBrowserView() - .GetActorOverlayView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() ->GetVisible(); })); } @@ -329,7 +415,8 @@ // Verify the overlay is visible in the current browser. ASSERT_TRUE(base::test::RunUntil([&]() { return browser_with_actuated_tab->GetBrowserView() - .GetActorOverlayView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() ->GetVisible(); })); // Add a new tab to ensure the source window always has at least two tabs @@ -347,7 +434,8 @@ // actuated tab. ASSERT_TRUE(base::test::RunUntil([&]() { return !browser_with_actuated_tab->GetBrowserView() - .GetActorOverlayView() + .GetActiveContentsContainerView() + ->GetActorOverlayView() ->GetVisible(); })); }
diff --git a/chrome/browser/android/compositor/layer/tab_layer.cc b/chrome/browser/android/compositor/layer/tab_layer.cc index f89f84b2..b97cf86 100644 --- a/chrome/browser/android/compositor/layer/tab_layer.cc +++ b/chrome/browser/android/compositor/layer/tab_layer.cc
@@ -193,8 +193,8 @@ toolbar_resource_id, toolbar_background_color, anonymize_toolbar, toolbar_textbox_background_color, toolbar_textbox_resource_id, 0, content_offset, false, false, viz::OffsetTag()); - toolbar_layer_->UpdateProgressBar(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, false); + toolbar_layer_->UpdateProgressBar(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + false); float toolbar_impact_height = 0; if (show_toolbar)
diff --git a/chrome/browser/android/compositor/layer/toolbar_layer.cc b/chrome/browser/android/compositor/layer/toolbar_layer.cc index 9f7ae374f..b487a6a 100644 --- a/chrome/browser/android/compositor/layer/toolbar_layer.cc +++ b/chrome/browser/android/compositor/layer/toolbar_layer.cc
@@ -143,18 +143,12 @@ int progress_bar_static_background_x, int progress_bar_static_background_width, int progress_bar_static_background_color, - int progress_bar_end_indicator_x, - int progress_bar_end_indicator_y, - int progress_bar_end_indicator_width, - int progress_bar_end_indicator_height, float corner_radius, bool progress_bar_visual_update_available) { bool is_progress_bar_visible = SkColorGetA(progress_bar_background_color); progress_bar_background_layer_->SetHideLayerAndSubtree(!is_progress_bar_visible); progress_bar_layer_->SetHideLayerAndSubtree(!is_progress_bar_visible); - progress_bar_end_circle_layer_->SetHideLayerAndSubtree( - !(is_progress_bar_visible && progress_bar_visual_update_available)); progress_bar_static_background_layer_->SetHideLayerAndSubtree( !(is_progress_bar_visible && progress_bar_visual_update_available)); @@ -179,14 +173,6 @@ progress_bar_layer_->SetRoundedCorner(gfx::RoundedCornersF(corner_radius)); if (progress_bar_visual_update_available) { - // Display the end circle and static background layer. - progress_bar_end_circle_layer_->SetPosition( - gfx::PointF(progress_bar_end_indicator_x, progress_bar_end_indicator_y)); - progress_bar_end_circle_layer_->SetBounds( - gfx::Size(progress_bar_end_indicator_width, progress_bar_end_indicator_height)); - progress_bar_end_circle_layer_->SetBackgroundColor(SkColor4f::FromColor(progress_bar_color)); - progress_bar_end_circle_layer_->SetRoundedCorner(gfx::RoundedCornersF(corner_radius)); - progress_bar_static_background_layer_->SetPosition(gfx::PointF( progress_bar_static_background_x, progress_bar_y)); progress_bar_static_background_layer_->SetBounds(gfx::Size( @@ -205,7 +191,6 @@ progress_bar_layer_->SetOpacity(opacity); progress_bar_background_layer_->SetOpacity(opacity); - progress_bar_end_circle_layer_->SetOpacity(opacity); progress_bar_static_background_layer_->SetOpacity(opacity); } @@ -217,7 +202,6 @@ bitmap_layer_(cc::slim::UIResourceLayer::Create()), progress_bar_layer_(cc::slim::SolidColorLayer::Create()), progress_bar_background_layer_(cc::slim::SolidColorLayer::Create()), - progress_bar_end_circle_layer_(cc::slim::SolidColorLayer::Create()), progress_bar_static_background_layer_(cc::slim::SolidColorLayer::Create()), debug_layer_(cc::slim::SolidColorLayer::Create()) { toolbar_background_layer_->SetIsDrawable(true); @@ -238,10 +222,6 @@ progress_bar_background_layer_->SetHideLayerAndSubtree(true); layer_->AddChild(progress_bar_background_layer_); - progress_bar_end_circle_layer_->SetIsDrawable(true); - progress_bar_end_circle_layer_->SetHideLayerAndSubtree(true); - layer_->AddChild(progress_bar_end_circle_layer_); - progress_bar_layer_->SetIsDrawable(true); progress_bar_layer_->SetHideLayerAndSubtree(true); layer_->AddChild(progress_bar_layer_);
diff --git a/chrome/browser/android/compositor/layer/toolbar_layer.h b/chrome/browser/android/compositor/layer/toolbar_layer.h index 13a6222c..4f181de 100644 --- a/chrome/browser/android/compositor/layer/toolbar_layer.h +++ b/chrome/browser/android/compositor/layer/toolbar_layer.h
@@ -55,10 +55,6 @@ int progress_bar_static_background_x, int progress_bar_static_background_width, int progress_bar_static_background_color, - int progress_bar_end_indicator_x, - int progress_bar_end_indicator_y, - int progress_bar_end_indicator_width, - int progress_bar_end_indicator_height, float corner_radius, bool progress_bar_visual_update_available); @@ -79,7 +75,6 @@ scoped_refptr<cc::slim::UIResourceLayer> bitmap_layer_; scoped_refptr<cc::slim::SolidColorLayer> progress_bar_layer_; scoped_refptr<cc::slim::SolidColorLayer> progress_bar_background_layer_; - scoped_refptr<cc::slim::SolidColorLayer> progress_bar_end_circle_layer_; scoped_refptr<cc::slim::SolidColorLayer> progress_bar_static_background_layer_; scoped_refptr<cc::slim::SolidColorLayer> debug_layer_; };
diff --git a/chrome/browser/android/compositor/scene_layer/top_toolbar_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/top_toolbar_scene_layer.cc index acd852e..7bb0120 100644 --- a/chrome/browser/android/compositor/scene_layer/top_toolbar_scene_layer.cc +++ b/chrome/browser/android/compositor/scene_layer/top_toolbar_scene_layer.cc
@@ -82,10 +82,6 @@ jint progress_bar_static_background_x, jint progress_bar_static_background_width, jint progress_bar_static_background_color, - jint progress_bar_end_indicator_x, - jint progress_bar_end_indicator_y, - jint progress_bar_end_indicator_width, - jint progress_bar_end_indicator_height, jfloat corner_radius, jboolean progress_bar_visual_update_available) { if (!toolbar_layer_) @@ -95,9 +91,8 @@ progress_bar_color, progress_bar_background_x, progress_bar_background_y, progress_bar_background_width, progress_bar_background_height, progress_bar_background_color, progress_bar_static_background_x, - progress_bar_static_background_width, progress_bar_static_background_color, - progress_bar_end_indicator_x, progress_bar_end_indicator_y, - progress_bar_end_indicator_width, progress_bar_end_indicator_height, corner_radius, + progress_bar_static_background_width, + progress_bar_static_background_color, corner_radius, progress_bar_visual_update_available); }
diff --git a/chrome/browser/android/compositor/scene_layer/top_toolbar_scene_layer.h b/chrome/browser/android/compositor/scene_layer/top_toolbar_scene_layer.h index 61a9369..7dc1d331 100644 --- a/chrome/browser/android/compositor/scene_layer/top_toolbar_scene_layer.h +++ b/chrome/browser/android/compositor/scene_layer/top_toolbar_scene_layer.h
@@ -59,10 +59,6 @@ jint progress_bar_static_background_x, jint progress_bar_static_background_width, jint progress_bar_static_background_color, - jint progress_bar_end_indicator_x, - jint progress_bar_end_indicator_y, - jint progress_bar_end_indicator_width, - jint progress_bar_end_indicator_height, jfloat corner_radius, jboolean progress_bar_visual_update_available);
diff --git a/chrome/browser/android/preferences/autofill/autofill_payment_methods_delegate.cc b/chrome/browser/android/preferences/autofill/autofill_payment_methods_delegate.cc index a466387..dc80ded 100644 --- a/chrome/browser/android/preferences/autofill/autofill_payment_methods_delegate.cc +++ b/chrome/browser/android/preferences/autofill/autofill_payment_methods_delegate.cc
@@ -17,6 +17,7 @@ #include "components/autofill/core/browser/data_manager/payments/payments_data_manager.h" #include "components/autofill/core/browser/data_manager/personal_data_manager.h" #include "components/autofill/core/browser/foundations/browser_autofill_manager.h" +#include "components/autofill/core/browser/payments/multiple_request_payments_network_interface.h" #include "components/autofill/core/browser/payments/payments_network_interface.h" #include "components/autofill/core/browser/payments/virtual_card_enrollment_flow.h" #include "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h" @@ -58,10 +59,23 @@ profile->GetURLLoaderFactory(), IdentityManagerFactory::GetForProfile(profile), &personal_data_manager_->payments_data_manager()); + multiple_request_payments_network_interface_ = + std::make_unique<payments::MultipleRequestPaymentsNetworkInterface>( + profile->GetURLLoaderFactory(), + *IdentityManagerFactory::GetForProfile(profile), + profile->IsOffTheRecord()); + + PaymentsNetworkInterfaceVariation interface; + if (base::FeatureList::IsEnabled( + features:: + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment)) { + interface = multiple_request_payments_network_interface_.get(); + } else { + interface = payments_network_interface_.get(); + } virtual_card_enrollment_manager_ = std::make_unique<VirtualCardEnrollmentManager>( - &personal_data_manager_->payments_data_manager(), - payments_network_interface_.get()); + &personal_data_manager_->payments_data_manager(), interface); } AutofillPaymentMethodsDelegate::~AutofillPaymentMethodsDelegate() = default;
diff --git a/chrome/browser/android/preferences/autofill/autofill_payment_methods_delegate.h b/chrome/browser/android/preferences/autofill/autofill_payment_methods_delegate.h index f748fac8..d43c5eb 100644 --- a/chrome/browser/android/preferences/autofill/autofill_payment_methods_delegate.h +++ b/chrome/browser/android/preferences/autofill/autofill_payment_methods_delegate.h
@@ -8,11 +8,11 @@ #include <jni.h> #include <stdint.h> -#include "build/build_config.h" - #include "base/android/jni_android.h" #include "base/android/scoped_java_ref.h" #include "base/memory/raw_ptr.h" +#include "build/build_config.h" +#include "components/autofill/core/browser/payments/multiple_request_payments_network_interface.h" #include "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h" using base::android::JavaParamRef; @@ -60,6 +60,8 @@ raw_ptr<PersonalDataManager> personal_data_manager_; // weak reference std::unique_ptr<payments::PaymentsNetworkInterface> payments_network_interface_; + std::unique_ptr<payments::MultipleRequestPaymentsNetworkInterface> + multiple_request_payments_network_interface_; std::unique_ptr<VirtualCardEnrollmentManager> virtual_card_enrollment_manager_; };
diff --git a/extensions/browser/api/app_window/app_window_apitest.cc b/chrome/browser/apps/platform_apps/app_window_apitest.cc similarity index 99% rename from extensions/browser/api/app_window/app_window_apitest.cc rename to chrome/browser/apps/platform_apps/app_window_apitest.cc index 48a720eb..53b4b44 100644 --- a/extensions/browser/api/app_window/app_window_apitest.cc +++ b/chrome/browser/apps/platform_apps/app_window_apitest.cc
@@ -26,6 +26,7 @@ #include "ui/gfx/geometry/rect.h" namespace extensions { +namespace { using AppWindowApiTest = PlatformAppBrowserTest; using ExperimentalAppWindowApiTest = ExperimentalPlatformAppBrowserTest; @@ -245,4 +246,5 @@ } #endif // BUILDFLAG(IS_CHROMEOS) +} // namespace } // namespace extensions
diff --git a/extensions/browser/app_window/app_window_browsertest.cc b/chrome/browser/apps/platform_apps/app_window_frame_browsertest.cc similarity index 89% rename from extensions/browser/app_window/app_window_browsertest.cc rename to chrome/browser/apps/platform_apps/app_window_frame_browsertest.cc index 28e9442d..1ae4b03 100644 --- a/extensions/browser/app_window/app_window_browsertest.cc +++ b/chrome/browser/apps/platform_apps/app_window_frame_browsertest.cc
@@ -16,7 +16,7 @@ namespace { -using AppWindowBrowserTest = PlatformAppBrowserTest; +using AppWindowFrameBrowserTest = PlatformAppBrowserTest; // This test is disabled on Linux because of the unpredictable nature of native // windows. We cannot assume that the window manager will insert any title bar @@ -29,7 +29,8 @@ // Verifies that the NativeAppWindows implement GetFrameInsets() correctly. // See http://crbug.com/346115 -IN_PROC_BROWSER_TEST_F(AppWindowBrowserTest, MAYBE_FrameInsetsForDefaultFrame) { +IN_PROC_BROWSER_TEST_F(AppWindowFrameBrowserTest, + MAYBE_FrameInsetsForDefaultFrame) { AppWindow* app_window = CreateTestAppWindow("{}"); NativeAppWindow* native_window = app_window->GetBaseWindow(); gfx::Insets insets = native_window->GetFrameInsets(); @@ -43,7 +44,7 @@ // Verifies that the NativeAppWindows implement GetFrameInsets() correctly. // See http://crbug.com/346115 -IN_PROC_BROWSER_TEST_F(AppWindowBrowserTest, FrameInsetsForColoredFrame) { +IN_PROC_BROWSER_TEST_F(AppWindowFrameBrowserTest, FrameInsetsForColoredFrame) { AppWindow* app_window = CreateTestAppWindow("{ \"frame\": { \"color\": \"#ffffff\" } }"); NativeAppWindow* native_window = app_window->GetBaseWindow(); @@ -58,7 +59,7 @@ // Verifies that the NativeAppWindows implement GetFrameInsets() correctly for // frameless windows. -IN_PROC_BROWSER_TEST_F(AppWindowBrowserTest, FrameInsetsForNoFrame) { +IN_PROC_BROWSER_TEST_F(AppWindowFrameBrowserTest, FrameInsetsForNoFrame) { AppWindow* app_window = CreateTestAppWindow("{ \"frame\": \"none\" }"); NativeAppWindow* native_window = app_window->GetBaseWindow(); gfx::Insets insets = native_window->GetFrameInsets(); @@ -72,7 +73,7 @@ CloseAppWindow(app_window); } -IN_PROC_BROWSER_TEST_F(AppWindowBrowserTest, IncognitoOpenUrl) { +IN_PROC_BROWSER_TEST_F(AppWindowFrameBrowserTest, IncognitoOpenUrl) { AppWindow* app_window = CreateTestAppWindow("{}"); content::WebContents* app_contents = @@ -91,7 +92,7 @@ CloseAppWindow(app_window); } -IN_PROC_BROWSER_TEST_F(AppWindowBrowserTest, DraggableFramelessWindow) { +IN_PROC_BROWSER_TEST_F(AppWindowFrameBrowserTest, DraggableFramelessWindow) { AppWindow* app_window = CreateTestAppWindow(R"({ "frame": "none" })"); base::RunLoop run_loop; @@ -116,7 +117,7 @@ #if BUILDFLAG(IS_CHROMEOS) // Disabled due to flake. https://crbug.com/1416579 -IN_PROC_BROWSER_TEST_F(AppWindowBrowserTest, +IN_PROC_BROWSER_TEST_F(AppWindowFrameBrowserTest, DISABLED_ShouldShowStaleContentOnEviction) { AppWindow* app_window = CreateTestAppWindow("{}"); // Make sure that a surface gets embedded in the frame evictor of the
diff --git a/chrome/browser/ash/extensions/external_cache.h b/chrome/browser/ash/extensions/external_cache.h index 8cdcc92..50996f8 100644 --- a/chrome/browser/ash/extensions/external_cache.h +++ b/chrome/browser/ash/extensions/external_cache.h
@@ -16,7 +16,6 @@ namespace base { class FilePath; -class Value; } // namespace base namespace chromeos {
diff --git a/chrome/browser/ash/policy/core/device_policy_decoder.h b/chrome/browser/ash/policy/core/device_policy_decoder.h index b65666a..c2f5db7 100644 --- a/chrome/browser/ash/policy/core/device_policy_decoder.h +++ b/chrome/browser/ash/policy/core/device_policy_decoder.h
@@ -16,10 +16,6 @@ class ChromeDeviceSettingsProto; } -namespace base { -class Value; -} - namespace policy { class ExternalDataManager;
diff --git a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandler.java b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandler.java index 6524e1e..4f658eb 100644 --- a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandler.java +++ b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandler.java
@@ -212,6 +212,11 @@ } } + @Override + public boolean invokeBackActionOnEscape() { + return false; + } + private Pair<Boolean, Boolean> determineBackPressAction(@Nullable Tab currentTab) { boolean minimizeApp; boolean shouldCloseTab;
diff --git a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandlerUnitTest.java b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandlerUnitTest.java index 8bac243..e84bb33 100644 --- a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandlerUnitTest.java +++ b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/MinimizeAppAndCloseTabBackPressHandlerUnitTest.java
@@ -186,6 +186,14 @@ histogram.assertExpected(); } + @Test + @SmallTest + public void testInvokeBackActionOnEscape() { + Assert.assertFalse( + "invokeBackActionOnEscape should return false.", + mHandler.invokeBackActionOnEscape()); + } + private void createBackPressHandler() { createBackPressHandler(false, false); }
diff --git a/chrome/browser/bluetooth/chrome_bluetooth_realtarget_browsertest.cc b/chrome/browser/bluetooth/chrome_bluetooth_realtarget_browsertest.cc index 544aaf0..1a82fb45 100644 --- a/chrome/browser/bluetooth/chrome_bluetooth_realtarget_browsertest.cc +++ b/chrome/browser/bluetooth/chrome_bluetooth_realtarget_browsertest.cc
@@ -130,14 +130,7 @@ TestBluetoothContentBrowserClient test_content_browser_client_; }; -// TODO(432549077): MacOS can not run on testers because of system permission. -#if BUILDFLAG(IS_MAC) -#define MAYBE_ConnectAndDisconnectDevice DISABLED_ConnectAndDisconnectDevice -#else -#define MAYBE_ConnectAndDisconnectDevice ConnectAndDisconnectDevice -#endif -IN_PROC_BROWSER_TEST_F(BluetoothRealTargetTest, - MAYBE_ConnectAndDisconnectDevice) { +IN_PROC_BROWSER_TEST_F(BluetoothRealTargetTest, ConnectAndDisconnectDevice) { content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); auto test_script = base::StringPrintf(
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarCoordinator.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarCoordinator.java index 9caa49ab..926ef6a 100644 --- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarCoordinator.java +++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarCoordinator.java
@@ -147,6 +147,14 @@ return mView; } + public boolean isVisible() { + return mView != null && mView.getVisibility() == VISIBLE; + } + + public void setVisibility(boolean isVisible) { + mMediator.setVisibility(isVisible); + } + private BookmarkBarButton inflateBookmarkBarButton(ViewGroup parent) { return (BookmarkBarButton) LayoutInflater.from(parent.getContext())
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java index 92041ef..30d25ed 100644 --- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java +++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarMediator.java
@@ -71,9 +71,11 @@ * @param heightSupplier A Supplier to fetch the height of the bookmark bar view. * @param itemsModel The model for the items which are rendered within the bookmark bar. * @param itemsOverflowSupplier The supplier for the current state of items overflow. - * @param currentTab The current tab if it exists. * @param model The model used to read/write bookmark bar properties. * @param profileSupplier The supplier for the currently active profile. + * @param currentTab The current tab if it exists. + * @param bookmarkOpener Used to open bookmarks. + * @param bookmarkManagerOpenerSupplier Used to open the bookmark manager. */ public BookmarkBarMediator( Activity activity, @@ -354,7 +356,7 @@ }); } - // TODO(crbug.com/339492600): Replace w/ positioning construct akin to `BottomControlsStacker`. + // TODO(crbug.com/430058918): Replace w/ positioning construct akin to `BottomControlsStacker`. private void updateTopMargin() { // NOTE: Top controls height is the sum of all top browser control heights which includes // that of the bookmark bar. Subtract the bookmark bar's height from the top controls height @@ -365,6 +367,8 @@ mBrowserControlsStateProvider.getTopControlsHeight() - mHeightSupplier.get()); } + // TODO(crbug.com/430058918): Mediator should not internally determine visibility by offsets. + @Deprecated private void updateVisibility() { mModel.set( BookmarkBarProperties.VISIBILITY, @@ -372,4 +376,8 @@ ? View.VISIBLE : View.GONE); } + + public void setVisibility(boolean isVisible) { + mModel.set(BookmarkBarProperties.VISIBILITY, isVisible ? View.VISIBLE : View.GONE); + } }
diff --git a/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarTest.java b/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarTest.java index 1b5206b4..dc2bc6a 100644 --- a/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarTest.java +++ b/chrome/browser/bookmarks/android/javatests/src/org/chromium/chrome/browser/bookmarks/bar/BookmarkBarTest.java
@@ -400,8 +400,15 @@ Criteria.checkThat(view.isLaidOut(), is(true)); Criteria.checkThat(viewStub, is(nullValue())); } else { - Criteria.checkThat(view, is(nullValue())); - Criteria.checkThat(viewStub, is(notNullValue())); + // When the BookmarkBar is not visible, it can be that it has not been + // constructed a first time, or it was constructed and is now hidden. + if (viewStub == null) { + // A null viewStub should mean the view is non-null but GONE. + Criteria.checkThat(view, is(notNullValue())); + Criteria.checkThat(view.getVisibility(), is(View.GONE)); + } else { + Criteria.checkThat(view, is(nullValue())); + } } }); }
diff --git a/chrome/browser/certificate_provider/test_certificate_provider_extension.h b/chrome/browser/certificate_provider/test_certificate_provider_extension.h index 8386d7f9..e391f9b 100644 --- a/chrome/browser/certificate_provider/test_certificate_provider_extension.h +++ b/chrome/browser/certificate_provider/test_certificate_provider_extension.h
@@ -20,7 +20,6 @@ namespace base { class FilePath; -class Value; } // namespace base namespace content {
diff --git a/chrome/browser/data_sharing/personal_collaboration_data/DEPS b/chrome/browser/data_sharing/personal_collaboration_data/DEPS new file mode 100644 index 0000000..84e1999 --- /dev/null +++ b/chrome/browser/data_sharing/personal_collaboration_data/DEPS
@@ -0,0 +1,4 @@ +include_rules = [ + # Minimize dependencies on internal code, but allow service construction. + "+components/data_sharing/internal/personal_collaboration_data/personal_collaboration_data_service_impl.h", +] \ No newline at end of file
diff --git a/chrome/browser/data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory.cc b/chrome/browser/data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory.cc new file mode 100644 index 0000000..994fb74 --- /dev/null +++ b/chrome/browser/data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory.cc
@@ -0,0 +1,58 @@ +// 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/data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory.h" + +#include <memory> + +#include "base/feature_list.h" +#include "base/no_destructor.h" +#include "chrome/browser/profiles/profile.h" +#include "components/data_sharing/internal/personal_collaboration_data/personal_collaboration_data_service_impl.h" +#include "components/data_sharing/public/features.h" +#include "components/data_sharing/public/personal_collaboration_data/personal_collaboration_data_service.h" +#include "content/public/browser/browser_context.h" + +namespace data_sharing::personal_collaboration_data { + +// static +PersonalCollaborationDataServiceFactory* +PersonalCollaborationDataServiceFactory::GetInstance() { + static base::NoDestructor<PersonalCollaborationDataServiceFactory> instance; + return instance.get(); +} + +// static +PersonalCollaborationDataService* +PersonalCollaborationDataServiceFactory::GetForProfile(Profile* profile) { + CHECK(profile); + return static_cast<PersonalCollaborationDataService*>( + GetInstance()->GetServiceForBrowserContext(profile, /*create=*/true)); +} + +PersonalCollaborationDataServiceFactory:: + PersonalCollaborationDataServiceFactory() + : ProfileKeyedServiceFactory( + "PersonalCollaborationDataService", + ProfileSelections::Builder() + .WithRegular(ProfileSelection::kOriginalOnly) + .Build()) {} + +PersonalCollaborationDataServiceFactory:: + ~PersonalCollaborationDataServiceFactory() = default; + +std::unique_ptr<KeyedService> +PersonalCollaborationDataServiceFactory::BuildServiceInstanceForBrowserContext( + content::BrowserContext* context) const { + // This service should only be accessed when data sharing feature is enabled. + CHECK(data_sharing::features::IsDataSharingFunctionalityEnabled()); + CHECK(!context->IsOffTheRecord()); + if (!base::FeatureList::IsEnabled( + features::kDataSharingAccountDataMigration)) { + return nullptr; + } + return std::make_unique<PersonalCollaborationDataServiceImpl>(); +} + +} // namespace data_sharing::personal_collaboration_data
diff --git a/chrome/browser/data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory.h b/chrome/browser/data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory.h new file mode 100644 index 0000000..a1456fe --- /dev/null +++ b/chrome/browser/data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory.h
@@ -0,0 +1,54 @@ +// 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_DATA_SHARING_PERSONAL_COLLABORATION_DATA_PERSONAL_COLLABORATION_DATA_SERVICE_FACTORY_H_ +#define CHROME_BROWSER_DATA_SHARING_PERSONAL_COLLABORATION_DATA_PERSONAL_COLLABORATION_DATA_SERVICE_FACTORY_H_ + +#include "chrome/browser/profiles/profile_keyed_service_factory.h" + +namespace base { +template <typename T> +class NoDestructor; +} // namespace base + +namespace content { +class BrowserContext; +} // namespace content + +class Profile; + +namespace data_sharing::personal_collaboration_data { +class PersonalCollaborationDataService; + +// A factory to create a unique PersonalCollaborationDataService. +class PersonalCollaborationDataServiceFactory + : public ProfileKeyedServiceFactory { + public: + // Gets the PersonalCollaborationDataService for the profile. Returns null for + // incognito/guest. + static PersonalCollaborationDataService* GetForProfile(Profile* profile); + + // Gets the lazy singleton instance of + // PersonalCollaborationDataServiceFactory. + static PersonalCollaborationDataServiceFactory* GetInstance(); + + // Disallow copy/assign. + PersonalCollaborationDataServiceFactory( + const PersonalCollaborationDataServiceFactory&) = delete; + void operator=(const PersonalCollaborationDataServiceFactory&) = delete; + + private: + friend base::NoDestructor<PersonalCollaborationDataServiceFactory>; + + PersonalCollaborationDataServiceFactory(); + ~PersonalCollaborationDataServiceFactory() override; + + // BrowserContextKeyedServiceFactory overrides. + std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext( + content::BrowserContext* context) const override; +}; + +} // namespace data_sharing::personal_collaboration_data + +#endif // CHROME_BROWSER_DATA_SHARING_PERSONAL_COLLABORATION_DATA_PERSONAL_COLLABORATION_DATA_SERVICE_FACTORY_H_
diff --git a/chrome/browser/data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory_unittest.cc b/chrome/browser/data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory_unittest.cc new file mode 100644 index 0000000..77146f4 --- /dev/null +++ b/chrome/browser/data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory_unittest.cc
@@ -0,0 +1,51 @@ +// 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/data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory.h" + +#include "base/test/scoped_feature_list.h" +#include "chrome/test/base/testing_profile.h" +#include "components/data_sharing/public/features.h" +#include "components/data_sharing/public/personal_collaboration_data/personal_collaboration_data_service.h" +#include "content/public/test/browser_task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace data_sharing::personal_collaboration_data { + +class PersonalCollaborationDataServiceFactoryTest : public testing::Test { + protected: + PersonalCollaborationDataServiceFactoryTest() = default; + ~PersonalCollaborationDataServiceFactoryTest() override = default; + + content::BrowserTaskEnvironment task_environment_; + std::unique_ptr<TestingProfile> profile_; + base::test::ScopedFeatureList scoped_feature_list_; +}; + +TEST_F(PersonalCollaborationDataServiceFactoryTest, + FeatureEnabledUsesRealService) { + scoped_feature_list_.InitWithFeatures( + {features::kDataSharingAccountDataMigration, + data_sharing::features::kDataSharingFeature}, + {}); + profile_ = TestingProfile::Builder().Build(); + + PersonalCollaborationDataService* service = + PersonalCollaborationDataServiceFactory::GetForProfile(profile_.get()); + EXPECT_TRUE(service); +} + +TEST_F(PersonalCollaborationDataServiceFactoryTest, + FeatureDisabledReturnNullService) { + scoped_feature_list_.InitWithFeatures( + {data_sharing::features::kDataSharingFeature}, + {features::kDataSharingAccountDataMigration}); + profile_ = TestingProfile::Builder().Build(); + + PersonalCollaborationDataService* service = + PersonalCollaborationDataServiceFactory::GetForProfile(profile_.get()); + EXPECT_FALSE(service); +} + +} // namespace data_sharing::personal_collaboration_data
diff --git a/chrome/browser/devtools/devtools_browsertest.cc b/chrome/browser/devtools/devtools_browsertest.cc index 13d55fc4..971c48a 100644 --- a/chrome/browser/devtools/devtools_browsertest.cc +++ b/chrome/browser/devtools/devtools_browsertest.cc
@@ -3874,7 +3874,8 @@ }); })(); )")) - .ExtractDict(); + .TakeValue() + .TakeDict(); EXPECT_TRUE(*result.FindBool("isSyncActive")); EXPECT_TRUE(*result.FindBool("arePreferencesSynced")); EXPECT_EQ(*result.FindString("accountEmail"), "user1@gmail.com"); @@ -4116,7 +4117,8 @@ }); })(); )")) - .ExtractDict(); + .TakeValue() + .TakeDict(); auto* configAidaAvailability = result.FindDict("aidaAvailability"); auto* configConsoleInsights = result.FindDict("devToolsConsoleInsights"); #if BUILDFLAG(GOOGLE_CHROME_BRANDING) @@ -4149,7 +4151,8 @@ }); })(); )")) - .ExtractDict(); + .TakeValue() + .TakeDict(); auto* configAidaAvailability = result.FindDict("aidaAvailability"); #if BUILDFLAG(GOOGLE_CHROME_BRANDING) EXPECT_TRUE(configAidaAvailability->FindBool("enabled").value()); @@ -4181,7 +4184,8 @@ }); })(); )")) - .ExtractDict(); + .TakeValue() + .TakeDict(); auto* configAidaAvailability = result.FindDict("aidaAvailability"); #if BUILDFLAG(GOOGLE_CHROME_BRANDING) EXPECT_TRUE(configAidaAvailability->FindBool("enabled").value()); @@ -4211,7 +4215,8 @@ }); })(); )")) - .ExtractDict(); + .TakeValue() + .TakeDict(); auto* configAidaAvailability = result.FindDict("aidaAvailability"); #if BUILDFLAG(GOOGLE_CHROME_BRANDING) EXPECT_TRUE(configAidaAvailability->FindBool("enabled").value()); @@ -4259,7 +4264,8 @@ }); })(); )")) - .ExtractDict(); + .TakeValue() + .TakeDict(); auto* configAidaAvailability = result.FindDict("aidaAvailability"); #if BUILDFLAG(GOOGLE_CHROME_BRANDING) EXPECT_TRUE(configAidaAvailability->FindBool("enabled").value()); @@ -4294,7 +4300,8 @@ }); })(); )")) - .ExtractDict(); + .TakeValue() + .TakeDict(); auto* configAidaAvailability = result.FindDict("aidaAvailability"); #if BUILDFLAG(GOOGLE_CHROME_BRANDING) EXPECT_TRUE(configAidaAvailability->FindBool("enabled").value()); @@ -4330,7 +4337,8 @@ }); })(); )")) - .ExtractDict(); + .TakeValue() + .TakeDict(); auto* configAidaAvailability = result.FindDict("aidaAvailability"); #if BUILDFLAG(GOOGLE_CHROME_BRANDING) EXPECT_TRUE(configAidaAvailability->FindBool("enabled").value());
diff --git a/chrome/browser/devtools/devtools_settings.h b/chrome/browser/devtools/devtools_settings.h index 57ddf854..b3d77a2 100644 --- a/chrome/browser/devtools/devtools_settings.h +++ b/chrome/browser/devtools/devtools_settings.h
@@ -14,10 +14,6 @@ class Profile; -namespace base { -class Value; -} - struct RegisterOptions { enum class SyncMode { kSync,
diff --git a/chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.h b/chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.h index 211d79f..cc2a144 100644 --- a/chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.h +++ b/chrome/browser/enterprise/connectors/test/deep_scanning_test_utils.h
@@ -29,6 +29,8 @@ #include "chrome/common/extensions/api/enterprise_reporting_private.h" #endif // BUILDFLAG(ENABLE_EXTENSIONS) +class Profile; + namespace policy { class MockCloudPolicyClient; }
diff --git a/chrome/browser/extensions/activity_log/activity_log.cc b/chrome/browser/extensions/activity_log/activity_log.cc index c6a60b64..5a0ffb2 100644 --- a/chrome/browser/extensions/activity_log/activity_log.cc +++ b/chrome/browser/extensions/activity_log/activity_log.cc
@@ -14,6 +14,7 @@ #include "base/command_line.h" #include "base/functional/bind.h" #include "base/json/json_string_value_serializer.h" +#include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/raw_ptr.h" #include "base/memory/scoped_refptr.h"
diff --git a/chrome/browser/extensions/api/BUILD.gn b/chrome/browser/extensions/api/BUILD.gn index fe40aab..a2877668 100644 --- a/chrome/browser/extensions/api/BUILD.gn +++ b/chrome/browser/extensions/api/BUILD.gn
@@ -45,6 +45,7 @@ "//chrome/browser/extensions/api/runtime", "//chrome/browser/extensions/api/scripting", "//chrome/browser/extensions/api/web_authentication_proxy", + "//chrome/browser/extensions/api/webrtc_audio_private", "//chrome/browser/extensions/api/webrtc_logging_private", ] @@ -76,7 +77,6 @@ "//chrome/browser/extensions/api/tab_groups", "//chrome/browser/extensions/api/web_navigation", "//chrome/browser/extensions/api/web_view", - "//chrome/browser/extensions/api/webrtc_audio_private", "//chrome/browser/extensions/api/webrtc_desktop_capture_private", ]
diff --git a/extensions/browser/api/bluetooth/bluetooth_apitest.cc b/chrome/browser/extensions/api/bluetooth/bluetooth_apitest.cc similarity index 96% rename from extensions/browser/api/bluetooth/bluetooth_apitest.cc rename to chrome/browser/extensions/api/bluetooth/bluetooth_apitest.cc index eea0c6e..b9ed5a1 100644 --- a/extensions/browser/api/bluetooth/bluetooth_apitest.cc +++ b/chrome/browser/extensions/api/bluetooth/bluetooth_apitest.cc
@@ -32,12 +32,11 @@ using device::BluetoothUUID; using device::MockBluetoothAdapter; using device::MockBluetoothDevice; -using extensions::Extension; -using extensions::ResultCatcher; namespace utils = extensions::api_test_utils; namespace api = extensions::api; +namespace extensions { namespace { using testing::_; @@ -46,13 +45,14 @@ static const char* kAdapterAddress = "A1:A2:A3:A4:A5:A6"; static const char* kName = "whatsinaname"; -class BluetoothApiTest : public extensions::ExtensionApiTest { +class BluetoothApiTest : public ExtensionApiTest { public: - BluetoothApiTest() {} + BluetoothApiTest() = default; + ~BluetoothApiTest() override = default; void SetUpOnMainThread() override { - extensions::ExtensionApiTest::SetUpOnMainThread(); - empty_extension_ = extensions::ExtensionBuilder("Test").Build(); + ExtensionApiTest::SetUpOnMainThread(); + empty_extension_ = ExtensionBuilder("Test").Build(); SetUpMockAdapter(); } @@ -122,12 +122,12 @@ std::unique_ptr<testing::NiceMock<MockBluetoothDevice>> device2_; std::unique_ptr<testing::NiceMock<MockBluetoothDevice>> device3_; - extensions::BluetoothEventRouter* event_router() { + BluetoothEventRouter* event_router() { return bluetooth_api()->event_router(); } - extensions::BluetoothAPI* bluetooth_api() { - return extensions::BluetoothAPI::Get(browser()->profile()); + BluetoothAPI* bluetooth_api() { + return BluetoothAPI::Get(browser()->profile()); } private: @@ -135,8 +135,6 @@ bool fail_next_call_ = false; }; -} // namespace - IN_PROC_BROWSER_TEST_F(BluetoothApiTest, GetAdapterState) { EXPECT_CALL(*mock_adapter_, GetAddress()) .WillOnce(testing::Return(kAdapterAddress)); @@ -209,7 +207,7 @@ .WillOnce(Invoke(this, &BluetoothApiTest::StartScanOverride)); start_function = setupFunction(new api::BluetoothStartDiscoveryFunction); utils::RunFunction(start_function.get(), "[]", browser()->profile(), - extensions::api_test_utils::FunctionMode::kNone); + utils::FunctionMode::kNone); testing::Mock::VerifyAndClearExpectations(mock_adapter_); // Simulate stop discovery with a failure @@ -449,3 +447,6 @@ EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); } + +} // namespace +} // namespace extensions
diff --git a/extensions/browser/api/bluetooth/bluetooth_private_apitest.cc b/chrome/browser/extensions/api/bluetooth/bluetooth_private_apitest.cc similarity index 96% rename from extensions/browser/api/bluetooth/bluetooth_private_apitest.cc rename to chrome/browser/extensions/api/bluetooth/bluetooth_private_apitest.cc index 7e75245..ca641d36 100644 --- a/extensions/browser/api/bluetooth/bluetooth_private_apitest.cc +++ b/chrome/browser/extensions/api/bluetooth/bluetooth_private_apitest.cc
@@ -44,8 +44,8 @@ namespace bt_private = extensions::api::bluetooth_private; namespace extensions { - namespace { + const char kTestExtensionId[] = "jofgjdphhceggjecimellaapdjjadibj"; const char kAdapterName[] = "Helix"; const char kDeviceName[] = "Red"; @@ -54,16 +54,11 @@ MATCHER_P(IsFilterEqual, a, "") { return arg->Equals(*a); } -} class BluetoothPrivateApiTest : public ExtensionApiTest { public: - BluetoothPrivateApiTest() - : adapter_name_(kAdapterName), - adapter_powered_(false), - adapter_discoverable_(false) {} - - ~BluetoothPrivateApiTest() override {} + BluetoothPrivateApiTest() = default; + ~BluetoothPrivateApiTest() override = default; void SetUpOnMainThread() override { ExtensionApiTest::SetUpOnMainThread(); @@ -152,11 +147,11 @@ } protected: - std::string adapter_name_; - bool adapter_powered_; - bool adapter_discoverable_; + std::string adapter_name_ = kAdapterName; + bool adapter_powered_ = false; + bool adapter_discoverable_ = false; - scoped_refptr<NiceMock<MockBluetoothAdapter> > mock_adapter_; + scoped_refptr<NiceMock<MockBluetoothAdapter>> mock_adapter_; std::unique_ptr<NiceMock<MockBluetoothDevice>> mock_device_; }; @@ -317,4 +312,5 @@ << message_; } +} // namespace } // namespace extensions
diff --git a/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc b/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc index 6f59ec1..e46db4f 100644 --- a/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc +++ b/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc
@@ -12,6 +12,7 @@ #include "base/run_loop.h" #include "base/task/sequenced_task_runner.h" #include "base/task/thread_pool.h" +#include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "chrome/browser/extensions/api/image_writer_private/operation.h" #include "chrome/services/removable_storage_writer/public/mojom/removable_storage_writer.mojom.h"
diff --git a/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.cc b/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.cc index 50b745df..85952f78 100644 --- a/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.cc +++ b/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.cc
@@ -18,6 +18,7 @@ #include "components/pdf/common/constants.h" #include "components/prefs/pref_service.h" #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h" +#include "pdf/buildflags.h" #include "url/url_constants.h" namespace extensions { @@ -27,6 +28,8 @@ namespace IsAllowedLocalFileAccess = api::pdf_viewer_private::IsAllowedLocalFileAccess; +namespace SaveToDrive = api::pdf_viewer_private::SaveToDrive; + namespace SetPdfPluginAttributes = api::pdf_viewer_private::SetPdfPluginAttributes; @@ -113,6 +116,24 @@ prefs->GetList(prefs::kPdfLocalFileAccessAllowedForDomains)))); } +PdfViewerPrivateSaveToDriveFunction::PdfViewerPrivateSaveToDriveFunction() = + default; + +PdfViewerPrivateSaveToDriveFunction::~PdfViewerPrivateSaveToDriveFunction() = + default; + +ExtensionFunction::ResponseAction PdfViewerPrivateSaveToDriveFunction::Run() { +#if BUILDFLAG(ENABLE_PDF_SAVE_TO_DRIVE) + std::optional<SaveToDrive::Params> params = + SaveToDrive::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + // TODO(crbug.com/424208776): Start the save to drive flow. + return RespondNow(NoArguments()); +#else + return RespondNow(Error("Not supported")); +#endif // BUILDFLAG(ENABLE_PDF_SAVE_TO_DRIVE) +} + PdfViewerPrivateSetPdfDocumentTitleFunction:: PdfViewerPrivateSetPdfDocumentTitleFunction() = default;
diff --git a/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.h b/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.h index 7950dd4..789dde8e 100644 --- a/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.h +++ b/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api.h
@@ -46,6 +46,24 @@ ResponseAction Run() override; }; +class PdfViewerPrivateSaveToDriveFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("pdfViewerPrivate.saveToDrive", + PDFVIEWERPRIVATE_SAVETODRIVE) + + PdfViewerPrivateSaveToDriveFunction(); + PdfViewerPrivateSaveToDriveFunction( + const PdfViewerPrivateSaveToDriveFunction&) = delete; + PdfViewerPrivateSaveToDriveFunction& operator=( + const PdfViewerPrivateSaveToDriveFunction&) = delete; + + protected: + ~PdfViewerPrivateSaveToDriveFunction() override; + + // Override from ExtensionFunction: + ResponseAction Run() override; +}; + class PdfViewerPrivateSetPdfDocumentTitleFunction : public ExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("pdfViewerPrivate.setPdfDocumentTitle",
diff --git a/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api_unittest.cc b/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api_unittest.cc index bc26c8f..33a133a 100644 --- a/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api_unittest.cc +++ b/chrome/browser/extensions/api/pdf_viewer_private/pdf_viewer_private_api_unittest.cc
@@ -20,6 +20,7 @@ #include "content/public/test/web_contents_tester.h" #include "extensions/browser/api_test_utils.h" #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h" +#include "pdf/buildflags.h" namespace extensions { @@ -312,4 +313,17 @@ function.get(), kSetPdfPluginAttributesArgs, profile())); } +#if BUILDFLAG(ENABLE_PDF_SAVE_TO_DRIVE) +// Succeed in sending a request to save a PDF to Drive. +TEST_F(PdfViewerPrivateApiUnitTest, SaveToDrive) { + CreateAndClaimStreamContainer(); + + auto function = base::MakeRefCounted<PdfViewerPrivateSaveToDriveFunction>(); + function->SetRenderFrameHost(extension_host()); + + EXPECT_TRUE( + api_test_utils::RunFunction(function, R"(["ORIGINAL"])", profile())); +} +#endif // BUILDFLAG(ENABLE_PDF_SAVE_TO_DRIVE) + } // namespace extensions
diff --git a/chrome/browser/extensions/api/permissions/permissions_apitest.cc b/chrome/browser/extensions/api/permissions/permissions_apitest.cc index 2a0ddc89..5c5551b2 100644 --- a/chrome/browser/extensions/api/permissions/permissions_apitest.cc +++ b/chrome/browser/extensions/api/permissions/permissions_apitest.cc
@@ -5,6 +5,7 @@ #include "base/files/file_util.h" #include "base/path_service.h" #include "base/test/scoped_feature_list.h" +#include "base/threading/thread_restrictions.h" #include "build/android_buildflags.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h"
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc index 2c5ef4d..3946769 100644 --- a/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc +++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api.cc
@@ -8,6 +8,7 @@ #include <memory> +#include "base/lazy_instance.h" #include "chrome/browser/extensions/api/web_navigation/web_navigation_api_constants.h" #include "chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.h" #include "chrome/browser/extensions/extension_tab_util.h"
diff --git a/chrome/browser/extensions/api/webrtc_audio_private/BUILD.gn b/chrome/browser/extensions/api/webrtc_audio_private/BUILD.gn index aed4f3b0..1c1565e3 100644 --- a/chrome/browser/extensions/api/webrtc_audio_private/BUILD.gn +++ b/chrome/browser/extensions/api/webrtc_audio_private/BUILD.gn
@@ -4,7 +4,7 @@ import("//extensions/buildflags/buildflags.gni") -assert(enable_extensions) +assert(enable_extensions_core) source_set("webrtc_audio_private") { sources = [
diff --git a/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h b/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h index 20d9bcc0..f53a5bb2 100644 --- a/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h +++ b/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.h
@@ -16,8 +16,11 @@ #include "content/public/browser/resource_context.h" #include "extensions/browser/browser_context_keyed_api_factory.h" #include "extensions/browser/extension_function.h" +#include "extensions/buildflags/buildflags.h" #include "media/audio/audio_device_description.h" +static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE)); + namespace media { class AudioSystem; }
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc index 77592eb..eeb9656 100644 --- a/chrome/browser/extensions/extension_tab_util.cc +++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -353,11 +353,10 @@ browser = chrome::FindTabbedBrowser(original_profile, false); if (!browser) { - browser = CreateBrowser(original_profile, user_gesture); + browser = CreateAndShowBrowser(original_profile, user_gesture, &error); if (!browser) { - return base::unexpected(kBrowserWindowNotAllowed); + return base::unexpected(error); } - browser->window()->Show(); } }
diff --git a/chrome/browser/feed/android/feed_surface_renderer_bridge.cc b/chrome/browser/feed/android/feed_surface_renderer_bridge.cc index db2e870..84683d9a 100644 --- a/chrome/browser/feed/android/feed_surface_renderer_bridge.cc +++ b/chrome/browser/feed/android/feed_surface_renderer_bridge.cc
@@ -370,6 +370,17 @@ .InMillisecondsFSinceUnixEpoch(); } +std::vector<std::string> JNI_FeedSurfaceRendererBridge_GetFeedUrls( + JNIEnv* env, + Profile* profile, + jint surface_id) { + FeedApi* feed_api = GetFeedApi(profile); + if (!feed_api) { + return {}; + } + return feed_api->GetFeedUrls(FromJavaSurfaceId(surface_id)); +} + static void JNI_FeedSurfaceRendererBridge_ReportInfoCardTrackViewStarted( JNIEnv* env, Profile* profile,
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java index 04cf2ee..a21ec72 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java
@@ -814,6 +814,11 @@ } @Override + public List<String> getFeedUrls() { + return mBridge.getFeedUrls(); + } + + @Override public void bind( RecyclerView rootView, FeedListContentManager manager,
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSurfaceProvider.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSurfaceProvider.java index 37ce2a4..e3e172f 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSurfaceProvider.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSurfaceProvider.java
@@ -15,6 +15,8 @@ import org.chromium.chrome.browser.ui.native_page.TouchEnabledDelegate; import org.chromium.components.browser_ui.widget.displaystyle.UiConfig; +import java.util.List; + /** Provides a surface that displays a list of interest feeds. */ @NullMarked public interface FeedSurfaceProvider { @@ -77,4 +79,9 @@ /** Supplier of the state of the feed stream being restored. See {@link RestoringState}. */ ObservableSupplier<Integer> getRestoringStateSupplier(); + + /** + * @return The list of feed article urls. + */ + List<String> getFeedUrls(); }
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSurfaceRendererBridge.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSurfaceRendererBridge.java index 90fc354..7949afb5 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSurfaceRendererBridge.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSurfaceRendererBridge.java
@@ -18,6 +18,8 @@ import org.chromium.chrome.browser.profiles.Profile; import org.chromium.url.GURL; +import java.util.List; + /** Java bridge to feed::SurfaceRenderer, provides shallow JNI bindings. */ @JNINamespace("feed::android") @NullMarked @@ -218,6 +220,10 @@ return FeedSurfaceRendererBridgeJni.get().getLastFetchTimeMs(mProfile, mSurfaceId); } + List<String> getFeedUrls() { + return FeedSurfaceRendererBridgeJni.get().getFeedUrls(mProfile, mSurfaceId); + } + void reportInfoCardTrackViewStarted(int type) { FeedSurfaceRendererBridgeJni.get() .reportInfoCardTrackViewStarted(mProfile, mSurfaceId, type); @@ -332,6 +338,9 @@ long getLastFetchTimeMs(@JniType("Profile*") Profile profile, int surfaceId); + @JniType("std::vector<std::string>") + List<String> getFeedUrls(@JniType("Profile*") Profile profile, int surfaceId); + void reportInfoCardTrackViewStarted( @JniType("Profile*") Profile profile, int surfaceId, int type);
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java index 88dcc0b..1f009ee 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/Stream.java
@@ -16,6 +16,7 @@ import org.chromium.chrome.browser.xsurface.feed.FeedSurfaceScope; import org.chromium.chrome.browser.xsurface.feed.FeedUserInteractionReliabilityLogger.ClosedReason; +import java.util.ArrayList; import java.util.List; /** Interface used for interacting with the Stream library in order to render a stream of cards. */ @@ -153,4 +154,9 @@ default @ClosedReason int getClosedReason() { return ClosedReason.LEAVE_FEED; } + + /** Returns a list of feed article urls. */ + default List<String> getFeedUrls() { + return new ArrayList<String>(); + } }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index e1a4eb14e..c3ed28f 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -2392,6 +2392,11 @@ "expiry_milestone": 125 }, { + "name": "draw-chrome-pages-edge-to-edge", + "owners": [ "clhager@google.com", "wenyufu@chromium.org", "edge-to-edge@chromium.org"], + "expiry_milestone": 150 + }, + { "name": "draw-cutout-edge-to-edge", "owners": ["wenyufu@chromium.org", "clhager@google.com", "edge-to-edge@chromium.org"], "expiry_milestone": 145
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 28169da..79838a6 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -4882,6 +4882,10 @@ const char kDeprecatedExternalPickerFunctionDescription[] = "Use the old-style opening of an External Picker when uploading files"; +const char kDrawChromePagesEdgeToEdgeName[] = "Draw Chrome Pages Edge-to-Edge"; +const char kDrawChromePagesEdgeToEdgeDescription[] = + "Enables drawing more native pages and secondary activities edge-to-edge."; + const char kDrawCutoutEdgeToEdgeName[] = "DrawCutoutEdgeToEdge"; const char kDrawCutoutEdgeToEdgeDescription[] = "Enables the Android feature Edge-to-Edge Feature to coordinate with the "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index eedb105c..b36aa1c 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -2829,6 +2829,9 @@ extern const char kDeprecatedExternalPickerFunctionName[]; extern const char kDeprecatedExternalPickerFunctionDescription[]; +extern const char kDrawChromePagesEdgeToEdgeName[]; +extern const char kDrawChromePagesEdgeToEdgeDescription[]; + extern const char kDrawCutoutEdgeToEdgeName[]; extern const char kDrawCutoutEdgeToEdgeDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc index d04c711..f357555 100644 --- a/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -286,6 +286,7 @@ &kDefaultBrowserPromoAndroid2, &kDisableInstanceLimit, &kDontAutoHideBrowserControls, + &kDrawChromePagesEdgeToEdge, &kCacheDeprecatedSystemLocationSetting, &kChromeSurveyNextAndroid, &kClampAutomotiveScaling, @@ -519,7 +520,7 @@ BASE_FEATURE(kAllowTabClosingUponMinimization, "AllowTabClosingUponMinimization", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); BASE_FEATURE(kAndroidAppIntegration, "AndroidAppIntegration", @@ -888,6 +889,10 @@ "DontAutoHideBrowserControls", base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kDrawChromePagesEdgeToEdge, + "DrawChromePagesEdgeToEdge", + base::FEATURE_ENABLED_BY_DEFAULT); + BASE_FEATURE(kCacheDeprecatedSystemLocationSetting, "CacheDeprecatedSystemLocationSetting", base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h index 22263a7..eb7d2fcd 100644 --- a/chrome/browser/flags/android/chrome_feature_list.h +++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -131,6 +131,7 @@ BASE_DECLARE_FEATURE(kDeviceAuthenticatorAndroidx); BASE_DECLARE_FEATURE(kDisableInstanceLimit); BASE_DECLARE_FEATURE(kDontPrefetchLibraries); +BASE_DECLARE_FEATURE(kDrawChromePagesEdgeToEdge); BASE_DECLARE_FEATURE(kEdgeToEdgeBottomChin); BASE_DECLARE_FEATURE(kEdgeToEdgeDebugging); BASE_DECLARE_FEATURE(kEdgeToEdgeEverywhere);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java index 42c2141..16f8788b 100644 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -378,6 +378,7 @@ public static final String DISPLAY_EDGE_TO_EDGE_FULLSCREEN = "DisplayEdgeToEdgeFullscreen"; public static final String DISPLAY_WILDCARD_CONTENT_SETTINGS = "DisplayWildcardInContentSettings"; + public static final String DRAW_CHROME_PAGES_EDGE_TO_EDGE = "DrawChromePagesEdgeToEdge"; public static final String DRAW_CUTOUT_EDGE_TO_EDGE = "DrawCutoutEdgeToEdge"; public static final String DYNAMIC_SAFE_AREA_INSETS = "DynamicSafeAreaInsets"; public static final String EDGE_TO_EDGE_BOTTOM_CHIN = "EdgeToEdgeBottomChin"; @@ -844,6 +845,8 @@ /* defaultValueInTests= */ true); public static final CachedFlag sDisplayEdgeToEdgeFullscreen = newCachedFlag(DISPLAY_EDGE_TO_EDGE_FULLSCREEN, false, true); + public static final CachedFlag sDrawChromePagesEdgeToEdge = + newCachedFlag(DRAW_CHROME_PAGES_EDGE_TO_EDGE, /* defaultValue= */ true); public static final CachedFlag sEdgeToEdgeBottomChin = newCachedFlag(EDGE_TO_EDGE_BOTTOM_CHIN, /* defaultValue= */ true); public static final CachedFlag sEdgeToEdgeDebugging = @@ -1104,6 +1107,7 @@ sCrossDeviceTabPaneAndroid, sDisableInstanceLimit, sDisplayEdgeToEdgeFullscreen, + sDrawChromePagesEdgeToEdge, sEdgeToEdgeBottomChin, sEdgeToEdgeDebugging, sEdgeToEdgeEverywhere, @@ -1800,4 +1804,8 @@ sAndroidTabDeclutterAutoDeleteTimeDeltaHours = sAndroidTabDeclutterAutoDelete.newIntParam( "android_tab_declutter_auto_delete_time_delta_hours", 90 * 24); + + public static final MutableBooleanParamWithSafeDefault sAndroidTabDeclutterAutoDeletePromoTest = + sAndroidTabDeclutterAutoDelete.newBooleanParam( + "android_tab_declutter_auto_delete_promo_test", false); }
diff --git a/chrome/browser/glic/host/context/glic_focused_tab_manager.cc b/chrome/browser/glic/host/context/glic_focused_tab_manager.cc index 2b4118e..e307694 100644 --- a/chrome/browser/glic/host/context/glic_focused_tab_manager.cc +++ b/chrome/browser/glic/host/context/glic_focused_tab_manager.cc
@@ -11,7 +11,6 @@ #include "chrome/browser/glic/host/context/glic_focused_browser_manager.h" #include "chrome/browser/glic/host/context/glic_sharing_utils.h" #include "chrome/browser/glic/host/context/glic_tab_data.h" -#include "chrome/browser/glic/widget/glic_window_controller.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_list.h" @@ -45,10 +44,8 @@ } // namespace GlicFocusedTabManager::GlicFocusedTabManager( - GlicWindowController* window_controller, GlicFocusedBrowserManager* focused_browser_manager) - : window_controller_(*window_controller), - focused_browser_manager_(focused_browser_manager) { + : focused_browser_manager_(focused_browser_manager) { focused_browser_subscription_ = focused_browser_manager_->AddFocusedBrowserChangedCallback( base::BindRepeating(&GlicFocusedTabManager::OnFocusedBrowserChanged,
diff --git a/chrome/browser/glic/host/context/glic_focused_tab_manager.h b/chrome/browser/glic/host/context/glic_focused_tab_manager.h index 8e11054..8d32c6e 100644 --- a/chrome/browser/glic/host/context/glic_focused_tab_manager.h +++ b/chrome/browser/glic/host/context/glic_focused_tab_manager.h
@@ -38,8 +38,8 @@ class GlicFocusedTabManager : public content::WebContentsObserver, public TabStripModelObserver { public: - GlicFocusedTabManager(GlicWindowController* window_controller, - GlicFocusedBrowserManager* focused_browser_manager); + explicit GlicFocusedTabManager( + GlicFocusedBrowserManager* focused_browser_manager); ~GlicFocusedTabManager() override; GlicFocusedTabManager(const GlicFocusedTabManager&) = delete; @@ -221,9 +221,6 @@ base::RepeatingCallbackList<void(const glic::mojom::TabData*)> focused_data_callback_list_; - // The Glic window controller. - raw_ref<GlicWindowController> window_controller_; - // Manages which browser window is considered "focused". raw_ptr<GlicFocusedBrowserManager> focused_browser_manager_;
diff --git a/chrome/browser/glic/host/context/glic_page_context_fetcher.cc b/chrome/browser/glic/host/context/glic_page_context_fetcher.cc index 0d8cc21..6315454 100644 --- a/chrome/browser/glic/host/context/glic_page_context_fetcher.cc +++ b/chrome/browser/glic/host/context/glic_page_context_fetcher.cc
@@ -6,6 +6,8 @@ #include "base/functional/callback.h" #include "base/time/time.h" +#include "chrome/browser/actor/actor_keyed_service.h" +#include "chrome/browser/actor/aggregated_journal.h" #include "chrome/browser/glic/host/context/glic_tab_data.h" #include "chrome/browser/glic/host/glic.mojom.h" #include "chrome/browser/glic/media/glic_media_integration.h" @@ -15,13 +17,31 @@ #include "components/optimization_guide/content/browser/page_content_proto_provider.h" #include "components/tabs/public/tab_interface.h" #include "mojo/public/cpp/base/proto_wrapper.h" +#include "mojo/public/cpp/base/proto_wrapper_passkeys.h" #include "url/origin.h" namespace glic { +class GlicPageContextFetcher { + public: + static void LogAnnotatedPageContent(actor::AggregatedJournal* journal, + const GURL& url, + mojo_base::ProtoWrapper& proto_wrapper) { + if (journal) { + auto byte_span = + proto_wrapper.byte_span(mojo_base::ProtoWrapperBytes::GetPassKey()); + if (byte_span.has_value()) { + journal->LogAnnotatedPageContent(url, actor::TaskId(), + byte_span.value()); + } + } + } +}; + namespace { void HandleFetchPageResult( + base::WeakPtr<content::WebContents> web_contents, glic::mojom::TabDataPtr tab_data, url::Origin last_committed_origin, std::unique_ptr<optimization_guide::proto::ContentNode> media_root_node, @@ -46,7 +66,21 @@ last_committed_origin, page_context.inner_text_result->inner_text, page_context.inner_text_result->truncated)); } + + actor::AggregatedJournal* journal = nullptr; + if (web_contents) { + if (auto* actor_keyed_service = + actor::ActorKeyedService::Get(web_contents->GetBrowserContext())) { + journal = &actor_keyed_service->GetJournal(); + } + } if (page_context.screenshot_result) { + if (journal) { + journal->LogScreenshot(tab_context->tab_data->url, actor::TaskId(), + "image/jpeg", + page_context.screenshot_result->jpeg_data); + } + tab_context->viewport_screenshot = glic::mojom::Screenshot::New( page_context.screenshot_result->dimensions.width(), page_context.screenshot_result->dimensions.height(), @@ -67,7 +101,6 @@ if (page_context.annotated_page_content_result) { auto annotated_page_data = mojom::AnnotatedPageData::New(); - if (media_root_node) { optimization_guide::proto::ContentNode* media_node = page_context.annotated_page_content_result->proto.mutable_root_node() @@ -78,6 +111,10 @@ annotated_page_data->annotated_page_content = mojo_base::ProtoWrapper( page_context.annotated_page_content_result->proto); + GlicPageContextFetcher::LogAnnotatedPageContent( + journal, tab_context->tab_data->url, + annotated_page_data->annotated_page_content.value()); + annotated_page_data->metadata = std::move(page_context.annotated_page_content_result->metadata); @@ -134,7 +171,8 @@ page_content_annotations::FetchPageContext( *web_contents, options, base::BindOnce( - &HandleFetchPageResult, CreateTabData(web_contents), + &HandleFetchPageResult, web_contents->GetWeakPtr(), + CreateTabData(web_contents), web_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin(), std::move(media_root_node), std::move(callback))); }
diff --git a/chrome/browser/glic/host/context/glic_sharing_manager_impl.cc b/chrome/browser/glic/host/context/glic_sharing_manager_impl.cc index 7a0d90e..f950ab4 100644 --- a/chrome/browser/glic/host/context/glic_sharing_manager_impl.cc +++ b/chrome/browser/glic/host/context/glic_sharing_manager_impl.cc
@@ -23,10 +23,9 @@ Host* host, GlicMetrics* metrics) : focused_browser_manager_(window_controller), - focused_tab_manager_(window_controller, &focused_browser_manager_), + focused_tab_manager_(&focused_browser_manager_), pinned_tab_manager_(profile), profile_(profile), - window_controller_(*window_controller), metrics_(metrics) {} GlicSharingManagerImpl::~GlicSharingManagerImpl() = default; @@ -154,7 +153,6 @@ tabs::TabHandle tab_handle, const mojom::GetTabContextOptions& options, base::OnceCallback<void(mojom::GetContextResultPtr)> callback) { - auto* tab = tab_handle.Get(); if (!tab) { std::move(callback).Run( @@ -163,18 +161,11 @@ } const bool is_pinned = pinned_tab_manager_.IsTabPinned(tab_handle); - - if (!is_pinned) { - if (!window_controller_->IsShowing()) { - std::move(callback).Run(mojom::GetContextResult::NewErrorReason( - "permission denied: window not showing")); - return; - } - if (!profile_->GetPrefs()->GetBoolean(prefs::kGlicTabContextEnabled)) { - std::move(callback).Run(mojom::GetContextResult::NewErrorReason( - "permission denied: context permission not enabled")); - return; - } + if (!is_pinned && + !profile_->GetPrefs()->GetBoolean(prefs::kGlicTabContextEnabled)) { + std::move(callback).Run(mojom::GetContextResult::NewErrorReason( + "permission denied: context permission not enabled")); + return; } const bool is_focused = focused_tab_manager_.IsTabFocused(tab_handle);
diff --git a/chrome/browser/glic/host/context/glic_sharing_manager_impl.h b/chrome/browser/glic/host/context/glic_sharing_manager_impl.h index b0f0fdd2..52f21ea6 100644 --- a/chrome/browser/glic/host/context/glic_sharing_manager_impl.h +++ b/chrome/browser/glic/host/context/glic_sharing_manager_impl.h
@@ -120,9 +120,6 @@ // The profile for which to manage sharing. raw_ptr<Profile> profile_; - // The Glic window controller. - raw_ref<GlicWindowController> window_controller_; - // Enables providing sharing-related-related input to metrics. raw_ptr<GlicMetrics> metrics_; };
diff --git a/chrome/browser/glic/host/glic_actor_controller.cc b/chrome/browser/glic/host/glic_actor_controller.cc index e2d11bb1..fcab1bb9 100644 --- a/chrome/browser/glic/host/glic_actor_controller.cc +++ b/chrome/browser/glic/host/glic_actor_controller.cc
@@ -72,7 +72,6 @@ void OnFetchPageContext( const GURL& url, - mojo_base::ProtoWrapperBytes::PassKey proto_pass_key, std::unique_ptr<actor::AggregatedJournal::PendingAsyncEntry> journal_entry, actor::mojom::ActionResultCode result_code, mojom::WebClientHandler::ActInFocusedTabCallback callback, @@ -90,27 +89,11 @@ tab_context_result->get_tab_context()->annotated_page_data && tab_context_result->get_tab_context() ->annotated_page_data->annotated_page_content.has_value()) { - auto byte_span = - tab_context_result->get_tab_context() - ->annotated_page_data->annotated_page_content->byte_span( - proto_pass_key); - if (byte_span.has_value()) { - journal_entry->GetJournal().LogAnnotatedPageContent( - url, journal_entry->GetTaskId(), byte_span.value()); - } - execution_engine->DidObserveContext( tab_context_result->get_tab_context() ->annotated_page_data->annotated_page_content.value()); } - if (tab_context_result->get_tab_context()->viewport_screenshot) { - journal_entry->GetJournal().LogScreenshot( - url, journal_entry->GetTaskId(), - tab_context_result->get_tab_context()->viewport_screenshot->mime_type, - tab_context_result->get_tab_context()->viewport_screenshot->data); - } - mojom::ActInFocusedTabResultPtr result = mojom::ActInFocusedTabResult::NewActInFocusedTabResponse( mojom::ActInFocusedTabResponse::New( @@ -336,7 +319,6 @@ FetchPageContext(tab, *ActionableOptions(options), base::BindOnce(OnFetchPageContext, url, - mojo_base::ProtoWrapperBytes::GetPassKey(), std::move(journal_entry), result_code, std::move(callback), task->GetExecutionEngine()->GetWeakPtr()));
diff --git a/chrome/browser/glic/host/glic_api_browsertest.cc b/chrome/browser/glic/host/glic_api_browsertest.cc index 717e8fe9..89de143 100644 --- a/chrome/browser/glic/host/glic_api_browsertest.cc +++ b/chrome/browser/glic/host/glic_api_browsertest.cc
@@ -312,7 +312,7 @@ ASSERT_THAT(result, content::EvalJsResult::IsOk()); if (result.is_dict()) { - base::Value::Dict dict = result.ExtractDict(); + const base::Value::Dict& dict = result.ExtractDict(); auto* id = dict.Find("id"); if (id && id->is_string() && id->GetString() == "next-step") { step_data_ = dict.Find("payload")->Clone(); @@ -1704,6 +1704,10 @@ ContinueJsTest(); } +IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTab, testGetPinCandidatesSingleTab) { + ExecuteJsTest(); +} + class GlicGetHostCapabilityApiTest : public GlicApiTestWithOneTab, public ::testing::WithParamInterface<bool> {
diff --git a/chrome/browser/glic/host/glic_page_handler.cc b/chrome/browser/glic/host/glic_page_handler.cc index d7e03f3..e59794c 100644 --- a/chrome/browser/glic/host/glic_page_handler.cc +++ b/chrome/browser/glic/host/glic_page_handler.cc
@@ -754,6 +754,11 @@ void GetContextFromFocusedTab( glic::mojom::GetTabContextOptionsPtr options, GetContextFromFocusedTabCallback callback) override { + if (ShouldDoApiActivationGating()) { + std::move(callback).Run(mojom::GetContextResult::NewErrorReason( + "permission denied: window not showing")); + return; + } auto* tab = glic_sharing_manager_->GetFocusedTabData().focus(); auto tab_handle = tab ? tab->GetHandle() : tabs::TabHandle::Null(); glic_sharing_manager_->GetContextFromTab(tab_handle, *options, @@ -763,7 +768,12 @@ void GetContextFromTab(int32_t tab_id, glic::mojom::GetTabContextOptionsPtr options, GetContextFromTabCallback callback) override { - // Activation gating is handled in this function. + if (ShouldDoApiActivationGating()) { + std::move(callback).Run(mojom::GetContextResult::NewErrorReason( + "permission denied: window not showing")); + return; + } + // Extra activation gating is done in this function. glic_sharing_manager_->GetContextFromTab(tabs::TabHandle(tab_id), *options, std::move(callback)); }
diff --git a/chrome/browser/lens/core/mojom/lens.mojom b/chrome/browser/lens/core/mojom/lens.mojom index 1c4225b..132c832 100644 --- a/chrome/browser/lens/core/mojom/lens.mojom +++ b/chrome/browser/lens/core/mojom/lens.mojom
@@ -94,6 +94,9 @@ // tab contents. AddBackgroundBlur(); + // Enables/disables the live blurring of the background + SetLiveBlur(bool enabled); + // When this method is called, the C++ coordinator will show the feedback // dialog. FeedbackRequestedByOverlay();
diff --git a/chrome/browser/media/encrypted_media_browsertest.cc b/chrome/browser/media/encrypted_media_browsertest.cc index 912908f..6eb0cd9d 100644 --- a/chrome/browser/media/encrypted_media_browsertest.cc +++ b/chrome/browser/media/encrypted_media_browsertest.cc
@@ -47,6 +47,7 @@ #if BUILDFLAG(IS_WIN) #include <mfapi.h> +#include "base/win/scoped_co_mem.h" #include "base/win/windows_version.h" #include "chrome/browser/media/media_foundation_service_monitor.h" #include "content/public/browser/gpu_data_manager.h" @@ -1556,7 +1557,7 @@ #if BUILDFLAG(ENABLE_PLATFORM_ENCRYPTED_DOLBY_VISION) bool IsVideoDecoderSupported(const GUID& video_decoder_guid) { MFT_REGISTER_TYPE_INFO inputInfo{MFMediaType_Video, video_decoder_guid}; - IMFActivate** activates; + base::win::ScopedCoMem<IMFActivate*> activates; unsigned int numActivates = 0; auto result = MFTEnumEx(MFT_CATEGORY_VIDEO_DECODER, MFT_ENUM_FLAG_SYNCMFT, &inputInfo, nullptr, &activates, &numActivates); @@ -1565,12 +1566,17 @@ return false; } + // Clean up the activates + for (unsigned int i = 0; i < numActivates; ++i) { + activates[i]->Release(); + } + return true; } bool IsVideoRendererEffectSupported(const wchar_t* profile) { bool supported = false; - IMFActivate** activates = nullptr; + base::win::ScopedCoMem<IMFActivate*> activates; unsigned int numActivates = 0; auto result = MFTEnumEx(MFT_CATEGORY_VIDEO_RENDERER_EFFECT, MFT_ENUM_FLAG_SORTANDFILTER, nullptr, nullptr, @@ -1598,8 +1604,9 @@ PropVariantClear(&var); } - if (*activates) { - (*activates)->Release(); + // Clean up the activates + for (unsigned int i = 0; i < numActivates; ++i) { + activates[i]->Release(); } return supported;
diff --git a/chrome/browser/new_tab_page/modules/v2/tab_groups/tab_groups.mojom b/chrome/browser/new_tab_page/modules/v2/tab_groups/tab_groups.mojom index 43071cfe..9ecc565d 100644 --- a/chrome/browser/new_tab_page/modules/v2/tab_groups/tab_groups.mojom +++ b/chrome/browser/new_tab_page/modules/v2/tab_groups/tab_groups.mojom
@@ -10,8 +10,10 @@ struct TabGroup { // Title of the tab group. string title; - // Url to open the tab group. - url.mojom.Url url; + // The total number of tabs in the tab group. + int32 total_tab_count; + // The URLs for up to the first three tabs. Used for fetching favicon. + array<url.mojom.Url> favicon_urls; }; // Browser-side handler for requests from NTP Module UI.
diff --git a/chrome/browser/new_tab_page/modules/v2/tab_groups/tab_groups_page_handler.cc b/chrome/browser/new_tab_page/modules/v2/tab_groups/tab_groups_page_handler.cc index c1b8ce8..93a6811 100644 --- a/chrome/browser/new_tab_page/modules/v2/tab_groups/tab_groups_page_handler.cc +++ b/chrome/browser/new_tab_page/modules/v2/tab_groups/tab_groups_page_handler.cc
@@ -10,6 +10,24 @@ #include "components/search/ntp_features.h" #include "content/public/browser/web_contents.h" +namespace { + +ntp::tab_groups::mojom::TabGroupPtr MakeTabGroup( + const char* title, + std::initializer_list<const char*> urls, + int total_tabs) { + auto group = ntp::tab_groups::mojom::TabGroup::New(); + group->title = title; + group->favicon_urls.reserve(urls.size()); + for (const char* url : urls) { + group->favicon_urls.emplace_back(url); + } + group->total_tab_count = total_tabs; + return group; +} + +} // namespace + TabGroupsPageHandler::TabGroupsPageHandler( mojo::PendingReceiver<ntp::tab_groups::mojom::PageHandler> pending_page_handler, @@ -31,13 +49,29 @@ if (data_type_param.find("Fake Data") != std::string::npos) { // Generate fake data. - const int kSampleTabGroupsCount = 4; - for (int i = 0; i < kSampleTabGroupsCount; ++i) { - auto tab_group = ntp::tab_groups::mojom::TabGroup::New(); - tab_group->title = "Tab Group " + base::NumberToString(i + 1); - tab_group->url = GURL("https://www.google.com"); - tab_groups_mojom.push_back(std::move(tab_group)); - } + tab_groups_mojom.push_back( + MakeTabGroup("Tab Group 1 (3 tabs total)", + {"https://www.google.com", "https://www.youtube.com", + "https://www.wikipedia.org"}, + 3)); + + tab_groups_mojom.push_back( + MakeTabGroup("Tab Group 2 (4 tabs total)", + {"https://www.google.com", "https://www.youtube.com", + "https://www.wikipedia.org", "https://maps.google.com"}, + 4)); + + tab_groups_mojom.push_back( + MakeTabGroup("Tab Group 3 (8 tabs total)", + {"https://www.google.com", "https://www.youtube.com", + "https://www.wikipedia.org", "https://maps.google.com"}, + 8)); + + tab_groups_mojom.push_back( + MakeTabGroup("Tab Group 4 (199 tabs total)", + {"https://www.google.com", "https://www.youtube.com", + "https://www.wikipedia.org", "https://maps.google.com"}, + 199)); } std::move(callback).Run(std::move(tab_groups_mojom));
diff --git a/chrome/browser/new_tab_page/modules/v2/tab_groups/tab_groups_page_handler_unittest.cc b/chrome/browser/new_tab_page/modules/v2/tab_groups/tab_groups_page_handler_unittest.cc index 0e28817..be12f55b 100644 --- a/chrome/browser/new_tab_page/modules/v2/tab_groups/tab_groups_page_handler_unittest.cc +++ b/chrome/browser/new_tab_page/modules/v2/tab_groups/tab_groups_page_handler_unittest.cc
@@ -65,10 +65,29 @@ auto tab_groups_mojom = RunGetTabGroups(); ASSERT_FALSE(tab_groups_mojom.empty()); - int num_tab_groups = static_cast<int>(tab_groups_mojom.size()); - for (int i = 0; i < num_tab_groups; ++i) { - auto& tab_group = tab_groups_mojom[i]; - ASSERT_EQ("Tab Group " + base::NumberToString(i + 1), tab_group->title); - ASSERT_EQ(GURL("https://www.google.com"), tab_group->url); - } + + const auto& group1 = tab_groups_mojom[0]; + EXPECT_EQ("Tab Group 1 (3 tabs total)", group1->title); + EXPECT_EQ(3, group1->total_tab_count); + EXPECT_EQ(3u, group1->favicon_urls.size()); + EXPECT_EQ(group1->total_tab_count, + static_cast<int>(group1->favicon_urls.size())); + EXPECT_EQ(GURL("https://www.google.com"), group1->favicon_urls[0]); + EXPECT_EQ(GURL("https://www.youtube.com"), group1->favicon_urls[1]); + EXPECT_EQ(GURL("https://www.wikipedia.org"), group1->favicon_urls[2]); + + const auto& group2 = tab_groups_mojom[1]; + EXPECT_EQ("Tab Group 2 (4 tabs total)", group2->title); + EXPECT_EQ(4u, group2->favicon_urls.size()); + EXPECT_EQ(4, group2->total_tab_count); + + const auto& group3 = tab_groups_mojom[2]; + EXPECT_EQ("Tab Group 3 (8 tabs total)", group3->title); + EXPECT_EQ(4u, group3->favicon_urls.size()); + EXPECT_EQ(8, group3->total_tab_count); + + const auto& group4 = tab_groups_mojom[3]; + EXPECT_EQ("Tab Group 4 (199 tabs total)", group4->title); + EXPECT_EQ(4u, group4->favicon_urls.size()); + EXPECT_EQ(199, group4->total_tab_count); }
diff --git a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportFlow.java b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportFlow.java index b3b5426..62bdc5e 100644 --- a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportFlow.java +++ b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportFlow.java
@@ -169,7 +169,8 @@ @Initializer @Override - public void onCreate(Bundle savedInstanceState, Delegate delegate, String callerMetricsId) { + public void onCreate( + @Nullable Bundle savedInstanceState, Delegate delegate, String callerMetricsId) { mDelegate = delegate; mCallerMetricsId = callerMetricsId;
diff --git a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportFlowInterface.java b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportFlowInterface.java index cbda48a..b586b4f 100644 --- a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportFlowInterface.java +++ b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/settings/ExportFlowInterface.java
@@ -13,6 +13,7 @@ import androidx.fragment.app.FragmentManager; import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.profiles.Profile; /** An interface for the implementations of {@link ExportFlow}. */ @@ -66,7 +67,8 @@ * @param callerMetricsId The unique string, which identifies the caller. This will be used as * the prefix for metrics histograms names. */ - public void onCreate(Bundle savedInstanceState, Delegate delegate, String callerMetricsId); + public void onCreate( + @Nullable Bundle savedInstanceState, Delegate delegate, String callerMetricsId); /** Starts the password export flow. */ public void startExporting();
diff --git a/chrome/browser/picture_in_picture/video_picture_in_picture_window_controller_browsertest.cc b/chrome/browser/picture_in_picture/video_picture_in_picture_window_controller_browsertest.cc index 3652c05..093ab78 100644 --- a/chrome/browser/picture_in_picture/video_picture_in_picture_window_controller_browsertest.cc +++ b/chrome/browser/picture_in_picture/video_picture_in_picture_window_controller_browsertest.cc
@@ -2260,11 +2260,6 @@ browser()->tab_strip_model()->GetActiveWebContents(); ASSERT_TRUE(active_web_contents->GetLastCommittedURL().SchemeIsFile()); - // Set MediaSession Play action handler to ensure a MediaSession routed frame - // is created. - ASSERT_TRUE( - ExecJs(active_web_contents, "setMediaSessionActionHandler('play');")); - // Verify that the overlay window is trusted for media playback. EXPECT_TRUE(IsTrustedForMediaPlayback()); } @@ -2289,11 +2284,6 @@ ASSERT_TRUE(active_web_contents->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()); - // Set MediaSession Play action handler to ensure a MediaSession routed frame - // is created. - ASSERT_TRUE( - ExecJs(active_web_contents, "setMediaSessionActionHandler('play');")); - // Verify that the overlay window is not trusted for media playback. SetExpectedHasHighEngagement(false); EXPECT_FALSE(IsTrustedForMediaPlayback()); @@ -2319,11 +2309,6 @@ ASSERT_TRUE(active_web_contents->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()); - // Set MediaSession Play action handler to ensure a MediaSession routed frame - // is created. - ASSERT_TRUE( - ExecJs(active_web_contents, "setMediaSessionActionHandler('play');")); - // Verify that the overlay window is trusted for media playback. SetExpectedHasHighEngagement(true); EXPECT_TRUE(IsTrustedForMediaPlayback()); @@ -2349,10 +2334,6 @@ auto* sub_frame = ChildFrameAt(main_frame, 0); ASSERT_TRUE(sub_frame); - // Set MediaSession Play action handler to ensure a MediaSession routed frame - // is created. - ASSERT_TRUE(ExecJs(sub_frame, "setMediaSessionPlayActionHandler();")); - // Add picture-in-picture event listener to sub frame. ASSERT_TRUE(ExecJs(sub_frame, "addPictureInPictureEventListeners();")); @@ -2365,29 +2346,3 @@ SetExpectedHasHighEngagement(true); EXPECT_FALSE(IsTrustedForMediaPlayback()); } - -IN_PROC_BROWSER_TEST_F(VideoPictureInPictureWindowControllerBrowserTest, - IsTrustedForMediaPlayback_NoRoutedFrame) { - ASSERT_TRUE(ui_test_utils::NavigateToURL( - browser(), - embedded_test_server()->GetURL( - "example.com", "/media/picture-in-picture/window-size.html"))); - - content::WebContents* active_web_contents = - browser()->tab_strip_model()->GetActiveWebContents(); - ASSERT_TRUE(active_web_contents); - - SetUpWindowController(active_web_contents); - ASSERT_TRUE(window_controller() != nullptr); - - // Open Picture-in-Picture window. - ASSERT_EQ(true, EvalJs(active_web_contents, "enterPictureInPicture();")); - EXPECT_TRUE(window_controller()->GetWindowForTesting()->IsVisible()); - - ASSERT_TRUE(active_web_contents->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()); - - // Verify that the overlay window is not trusted for media playback, even with - // high media engagement. - SetExpectedHasHighEngagement(true); - EXPECT_FALSE(IsTrustedForMediaPlayback()); -}
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc index e31ae32..924cdc59 100644 --- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc +++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -57,6 +57,7 @@ #include "chrome/browser/contextual_cueing/contextual_cueing_service_factory.h" #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" #include "chrome/browser/data_sharing/data_sharing_service_factory.h" +#include "chrome/browser/data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory.h" #include "chrome/browser/device_api/managed_configuration_api_factory.h" #include "chrome/browser/device_reauth/chrome_device_authenticator_factory.h" #include "chrome/browser/digital_credentials/digital_credentials_keyed_service.h" @@ -842,6 +843,8 @@ data_controls::ChromeRulesServiceFactory::GetInstance(); #endif data_sharing::DataSharingServiceFactory::GetInstance(); + data_sharing::personal_collaboration_data:: + PersonalCollaborationDataServiceFactory::GetInstance(); #if !BUILDFLAG(IS_ANDROID) DependencyParserModelLoaderFactory::GetInstance(); DevToolsAndroidBridge::Factory::GetInstance();
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h index 5e84d59..8556cf3f 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu.h +++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h
@@ -40,10 +40,6 @@ #include "ui/gfx/geometry/vector2d.h" #include "ui/menus/simple_menu_model.h" -#if BUILDFLAG(ENABLE_COMPOSE) -#include "chrome/browser/compose/chrome_compose_client.h" -#endif - #if BUILDFLAG(ENABLE_LENS_DESKTOP_GOOGLE_BRANDED_FEATURES) #include "chrome/browser/lens/region_search/lens_region_search_controller.h" #endif @@ -55,6 +51,9 @@ class AccessibilityLabelsMenuObserver; class Browser; +#if BUILDFLAG(ENABLE_COMPOSE) +class ChromeComposeClient; +#endif class ClickToCallContextMenuObserver; class LinkToTextMenuObserver; class PrintPreviewContextMenuObserver;
diff --git a/chrome/browser/resources/ash/print_preview/ui/destination_dialog_cros.ts b/chrome/browser/resources/ash/print_preview/ui/destination_dialog_cros.ts index 0741f5d4..e8bee60 100644 --- a/chrome/browser/resources/ash/print_preview/ui/destination_dialog_cros.ts +++ b/chrome/browser/resources/ash/print_preview/ui/destination_dialog_cros.ts
@@ -375,6 +375,12 @@ this.$.dialog.showModal(); + // Workaround to force the iron-list in print-preview-destination-list to + // render all destinations and resize to fill dialog body. + if (this.uiState_ === UiState.DESTINATION_LIST) { + window.dispatchEvent(new CustomEvent('resize')); + } + // Display throbber for a minimum period of time while destinations are // still loading to avoid empty state UI flashing. if (!this.minLoadingTimeElapsed_) {
diff --git a/chrome/browser/resources/glic/glic_api/glic_api.ts b/chrome/browser/resources/glic/glic_api/glic_api.ts index bfd18ff8..c6e0e610 100644 --- a/chrome/browser/resources/glic/glic_api/glic_api.ts +++ b/chrome/browser/resources/glic/glic_api/glic_api.ts
@@ -603,11 +603,6 @@ * * Dynamic updates can be a costly operation so the observable value should be * released/destroyed as soon as it's not useful anymore. - * - * TODO(b/432258121): A race condition can occur when a consumer - * unsubscribes and a new one subscribes. An update from the first - * subscription that is already in-flight may be delivered to the second - * consumer. */ getPinCandidates? (options: GetPinCandidatesOptions): ObservableValue<PinCandidate[]>;
diff --git a/chrome/browser/resources/glic/glic_api_impl/glic_api_client.ts b/chrome/browser/resources/glic/glic_api_impl/glic_api_client.ts index 45aa749..2bb4a7b 100644 --- a/chrome/browser/resources/glic/glic_api_impl/glic_api_client.ts +++ b/chrome/browser/resources/glic/glic_api_impl/glic_api_client.ts
@@ -160,15 +160,12 @@ this.host.getOsHotkeyState().assignAndSignal(payload); } - // TODO(crbug.com/432258121): A race condition can occur when a consumer - // unsubscribes and a new one subscribes. An update from the first - // subscription that is already in-flight may be delivered to the second - // consumer. glicWebClientPinCandidatesChanged(payload: { candidates: PinCandidatePrivate[], + observationId: number, }): void { - this.host.pinCandidates.assignAndSignal(payload.candidates.map( - c => ({tabData: convertTabDataFromPrivate(c.tabData)}))); + this.host.pinCandidates?.processUpdate( + payload.candidates, payload.observationId); } glicWebClientNotifyPinnedTabsChanged(payload: {tabData: TabDataPrivate[]}): @@ -224,7 +221,9 @@ private metrics: GlicBrowserHostMetricsImpl; private manuallyResizing = ObservableValueImpl.withValue<boolean>(false); pinnedTabs = ObservableValueImpl.withNoValue<TabData[]>(); - pinCandidates = ObservableValueImpl.withNoValue<PinCandidate[]>(); + pinCandidates: PinCandidatesObservable|undefined; + // Makes IDs that are unique within the scope of this class. + idGenerator = new IdGenerator(); private currentZeroStateSuggestionOptions: ZeroStateSuggestionsOptions = { isFirstRun: false, supportedTools: [], @@ -677,17 +676,9 @@ getPinCandidates? (options: GetPinCandidatesOptions): ObservableValue<PinCandidate[]> { - this.pinCandidates = - ObservableValueImpl.withNoValue<PinCandidate[]>((isActive: boolean) => { - if (isActive) { - this.sender.requestNoResponse( - 'glicBrowserSubscribeToPinCandidates', {options}); - } else { - this.sender.requestNoResponse( - 'glicBrowserUnsubscribeFromPinCandidates', undefined); - } - }); - return this.pinCandidates; + this.pinCandidates?.setObsolete(); + return this.pinCandidates = new PinCandidatesObservable( + this.idGenerator.next(), this.sender, options); } async getZeroStateSuggestionsForFocusedTab? @@ -843,6 +834,61 @@ } } +class IdGenerator { + private nextId = 1; + + next(): number { + return this.nextId++; + } +} + +class PinCandidatesObservable extends ObservableValueImpl<PinCandidate[]> { + private isObsolete = false; + + constructor( + private readonly observationId: number, + private sender: PostMessageRequestSender, + private options: GetPinCandidatesOptions) { + super(false); + } + + override activeSubscriptionChanged(hasActiveSubscription: boolean): void { + super.activeSubscriptionChanged(hasActiveSubscription); + if (this.isObsolete) { + console.warn(`getPinCandidates() observable is in use while obsolete.`); + return; + } + if (hasActiveSubscription) { + this.sender.requestNoResponse( + 'glicBrowserSubscribeToPinCandidates', + {options: this.options, observationId: this.observationId}); + } else { + this.sender.requestNoResponse( + 'glicBrowserUnsubscribeFromPinCandidates', + {observationId: this.observationId}); + } + } + + processUpdate(candidates: PinCandidatePrivate[], observationId: number) { + if (this.observationId !== observationId) { + return; + } + + this.assignAndSignal( + candidates.map(c => ({tabData: convertTabDataFromPrivate(c.tabData)}))); + } + + // Mark this observable as obsolete. It should not be used any further. + // Only one PinCandidatesObservable is active at one time. + setObsolete() { + if (this.hasActiveSubscription()) { + console.warn( + `getPinCandidates() observable was made obsolete with subscribers.`); + } + this.isObsolete = true; + } +} + // Converts an RgbaImage into a Blob through the canvas API. Output is a PNG. async function rgbaImageToBlob(image: RgbaImage): Promise<Blob> { const canvas = document.createElement('canvas');
diff --git a/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts b/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts index fadcdff..133c6a6 100644 --- a/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts +++ b/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts
@@ -260,7 +260,8 @@ } class PinCandidatesObserverImpl implements PinCandidatesObserver { - constructor(private sender: PostMessageRequestSender) {} + constructor( + private sender: PostMessageRequestSender, public observationId: number) {} onPinCandidatesChanged(candidates: PinCandidateMojo[]): void { const extras = new ResponseExtras(); @@ -270,16 +271,26 @@ candidates.map(c => ({ tabData: tabDataToClient(c.tabData, extras), })), + observationId: this.observationId, }, extras.transfers); } } -// Handles all requests to the host. +/** + * Handles all requests to the host. + * + * Each function is a message handler, automatically called when the host + * receives a message with the corresponding request name. + * + * Any new state or function that's not a handler should be added to + * `GlicApiHost`. + */ class HostMessageHandler implements HostMessageHandlerInterface { // Undefined until the web client is initialized. private receiver: WebClientReceiver|undefined; - private pinCandidatesObserver: PinCandidatesObserverReceiver|undefined; + + // Reminder: Don't add more state here! See `HostMessageHandler`'s comment. constructor( private handler: WebClientHandlerInterface, @@ -291,7 +302,6 @@ this.receiver.$.close(); this.receiver = undefined; } - this.glicBrowserUnsubscribeFromPinCandidates(); } async glicBrowserWebClientCreated(_request: void, extras: ResponseExtras): @@ -839,18 +849,26 @@ glicBrowserSubscribeToPinCandidates(request: { options: GetPinCandidatesOptions, + observationId: number, }): void { - const observer = new PinCandidatesObserverImpl(this.sender); - this.pinCandidatesObserver = new PinCandidatesObserverReceiver(observer); + const observer = + new PinCandidatesObserverImpl(this.sender, request.observationId); + const receiver = new PinCandidatesObserverReceiver(observer); + this.host.pinCandidatesObserver = {receiver, observer}; this.handler.subscribeToPinCandidates( getPinCandidatesOptionsFromClient(request.options), - this.pinCandidatesObserver.$.bindNewPipeAndPassRemote()); + receiver.$.bindNewPipeAndPassRemote()); } - glicBrowserUnsubscribeFromPinCandidates(): void { - if (this.pinCandidatesObserver) { - this.pinCandidatesObserver.$.close(); - this.pinCandidatesObserver = undefined; + glicBrowserUnsubscribeFromPinCandidates(request: {observationId: number}): + void { + if (!this.host.pinCandidatesObserver) { + return; + } + if (this.host.pinCandidatesObserver.observer.observationId === + request.observationId) { + this.host.pinCandidatesObserver.receiver.$.close(); + this.host.pinCandidatesObserver = undefined; } } @@ -921,6 +939,12 @@ } } +/** + * The host side of the Glic API. + * + * Its primary job is to route calls between the client (over postMessage) and + * the browser (over Mojo). + */ export class GlicApiHost implements PostMessageRequestHandler { private senderId = newSenderId(); private messageHandler: HostMessageHandler; @@ -937,6 +961,10 @@ private browserIsActive = true; private hasShownDebuggerAttachedWarning = false; detailedWebClientState = DetailedWebClientState.BOOTSTRAP_PENDING; + pinCandidatesObserver?: { + receiver: PinCandidatesObserverReceiver, + observer: PinCandidatesObserverImpl, + }; constructor( private browserProxy: BrowserProxy, private windowProxy: WindowProxy, @@ -969,6 +997,7 @@ this.postMessageReceiver.destroy(); this.messageHandler.destroy(); this.sender.destroy(); + this.closePinCandidatesObserver(); } // Called when the webview page is loaded. @@ -991,7 +1020,7 @@ this.panelOpenState = state; this.clientActiveObs.assignAndSignal(this.isClientActive()); if (state === PanelOpenState.CLOSED) { - this.messageHandler.glicBrowserUnsubscribeFromPinCandidates(); + this.closePinCandidatesObserver(); } } @@ -1227,6 +1256,13 @@ `Glic.Api.RequestCounts.${suffix}`, event, GlicRequestEvent.MAX_VALUE + 1); } + + closePinCandidatesObserver() { + if (this.pinCandidatesObserver) { + this.pinCandidatesObserver.receiver.$.close(); + this.pinCandidatesObserver = undefined; + } + } } // LINT.IfChange(GlicRequestEvent)
diff --git a/chrome/browser/resources/glic/glic_api_impl/request_types.ts b/chrome/browser/resources/glic/glic_api_impl/request_types.ts index dfaac14a..7455dde 100644 --- a/chrome/browser/resources/glic/glic_api_impl/request_types.ts +++ b/chrome/browser/resources/glic/glic_api_impl/request_types.ts
@@ -318,9 +318,14 @@ glicBrowserSubscribeToPinCandidates: { request: { options: GetPinCandidatesOptions, + observationId: number, }, }; - glicBrowserUnsubscribeFromPinCandidates: {}; + glicBrowserUnsubscribeFromPinCandidates: { + request: { + observationId: number, + }, + }; glicBrowserGetZeroStateSuggestionsForFocusedTab: { request: { isFirstRun?: boolean, @@ -438,6 +443,7 @@ glicWebClientPinCandidatesChanged: { request: { candidates: PinCandidatePrivate[], + observationId: number, }, }; glicWebClientZeroStateSuggestionsChanged: {
diff --git a/chrome/browser/resources/glic/observable.ts b/chrome/browser/resources/glic/observable.ts index 14c88a9f..86872824 100644 --- a/chrome/browser/resources/glic/observable.ts +++ b/chrome/browser/resources/glic/observable.ts
@@ -32,8 +32,8 @@ export class ObservableValue<T> { private subscribers: Set<ObservableSubscription<T>> = new Set(); - private constructor( - private isSet: boolean, private value: T|undefined, + protected constructor( + private isSet: boolean, private value?: T, private hasActiveSubscriptionCallback?: (hasActiveSubscription: boolean) => void) {} @@ -97,8 +97,8 @@ } this.subscribers.delete(sub); - if (this.hasActiveSubscriptionCallback && this.subscribers.size === 0) { - this.hasActiveSubscriptionCallback(false); + if (this.subscribers.size === 0) { + this.activeSubscriptionChanged(false); } } @@ -106,8 +106,8 @@ subscribe(change: (newValue: T) => void): Subscriber { const newSub = new ObservableSubscription(change, this.onUnsubscribe.bind(this)); - if (this.hasActiveSubscriptionCallback && this.subscribers.size === 0) { - this.hasActiveSubscriptionCallback(true); + if (this.subscribers.size === 0) { + this.activeSubscriptionChanged(true); } this.subscribers.add(newSub); if (this.isSet) { @@ -116,6 +116,14 @@ return newSub; } + protected activeSubscriptionChanged(hasActiveSubscription: boolean): void { + this.hasActiveSubscriptionCallback?.(hasActiveSubscription); + } + + protected hasActiveSubscription(): boolean { + return this.subscribers.size > 0; + } + /** * Asynchronously waits until the ObservableValue's current value satisfies a * given criteria.
diff --git a/chrome/browser/resources/lens/overlay/selection_overlay.ts b/chrome/browser/resources/lens/overlay/selection_overlay.ts index 11e5677..55dd45a 100644 --- a/chrome/browser/resources/lens/overlay/selection_overlay.ts +++ b/chrome/browser/resources/lens/overlay/selection_overlay.ts
@@ -1338,6 +1338,11 @@ this.browserProxy.handler.addBackgroundBlur(); + // TODO(crbug.com/433758209): onInitialFlashAnimationEnd is currently a + // proxy for when to start blurring, but ideally the live blur is kept in + // sync with this.isResized. + this.browserProxy.handler.setLiveBlur(this.isResized); + // Let the parent know the initial flash image animation has finished. this.dispatchEvent(new CustomEvent( 'initial-flash-animation-end', {bubbles: true, composed: true}));
diff --git a/chrome/browser/resources/new_tab_page/lazy_load.ts b/chrome/browser/resources/new_tab_page/lazy_load.ts index 500a5ab..ea53592 100644 --- a/chrome/browser/resources/new_tab_page/lazy_load.ts +++ b/chrome/browser/resources/new_tab_page/lazy_load.ts
@@ -56,6 +56,7 @@ export {DisableModuleEvent, DismissModuleElementEvent, DismissModuleInstanceEvent, ModulesElement, NamedWidth, SUPPORTED_MODULE_WIDTHS} from './modules/v2/modules.js'; export {ModuleElement as MostRelevantTabResumptionModuleElement, mostRelevantTabResumptionDescriptor} from './modules/v2/most_relevant_tab_resumption/module.js'; export {MostRelevantTabResumptionProxyImpl} from './modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_proxy.js'; +export {IconContainerElement} from './modules/v2/tab_groups/icon_container.js'; export {ModuleElement as TabGroupsModuleElement, tabGroupsDescriptor} from './modules/v2/tab_groups/module.js'; export {TabGroupsProxyImpl} from './modules/v2/tab_groups/tab_groups_proxy.js'; export {NtpPromoProxy, NtpPromoProxyImpl} from './ntp_promo/ntp_promo_proxy.js';
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/icon_container.css b/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/icon_container.css new file mode 100644 index 0000000..890e17cf --- /dev/null +++ b/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/icon_container.css
@@ -0,0 +1,91 @@ +/* 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. */ + +/* #css_wrapper_metadata_start + * #type=style-lit + * #scheme=relative + * #css_wrapper_metadata_end */ + +:host { + --icon-container-size: 64px; + --icon-size: 16px; + --cell-size: 26px; + --cell-inner-radius: 2px; + --cell-outer-radius: 6px; + align-items: center; + background-color: var(--color-new-tab-page-module-icon-background); + border-radius: 8px; + display: flex; + gap: 4px; + height: var(--icon-container-size); + justify-content: center; + padding: 4px; + width: var(--icon-container-size); +} + +.icons-container { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-template-rows: repeat(2, 1fr); + gap: 4px; + height: 100%; + place-items: center; + width: 100%; +} + +.cell { + background-color: color-mix(in srgb, var(--color-sys-surface) 60%, transparent); + border-radius: var(--cell-inner-radius); + height: 100%; + overflow: hidden; + width: 100%; +} + +.cell.empty { + height: var(--cell-size); + width: var(--cell-size); +} + +.cell:nth-child(1) { + border-top-left-radius: var(--cell-outer-radius); +} + +.cell:nth-child(2) { + border-top-right-radius: var(--cell-outer-radius); +} + +.cell:nth-child(3) { + border-bottom-left-radius: var(--cell-outer-radius); +} + +.cell:nth-child(4) { + border-bottom-right-radius: var(--cell-outer-radius); +} + +.cell.overflow-count { + align-items: center; + color: var(--color-new-tab-page-secondary-foreground); + display: flex; + flex-direction: column; + font-size: var(--ntp-module-secondary-text-size); + font-style: normal; + font-weight: 400; + height: var(--cell-size); + justify-content: center; + line-height: var(--ntp-module-secondary-line-height); + width: var(--cell-size); +} + +.cell.icon { + background-color: var(--color-sys-surface); + display: grid; + height: var(--cell-size); + place-items: center; + width: var(--cell-size); +} + +.cell.icon > .icon { + height: var(--icon-size); + width: var(--icon-size); +} \ No newline at end of file
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/icon_container.ts b/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/icon_container.ts new file mode 100644 index 0000000..c6d1b65 --- /dev/null +++ b/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/icon_container.ts
@@ -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. + +import 'chrome://resources/cr_elements/cr_auto_img/cr_auto_img.js'; + +import {getFaviconForPageURL} from 'chrome://resources/js/icon.js'; +import {CrLitElement, html} from 'chrome://resources/lit/v3_0/lit.rollup.js'; + +import {getCss} from './icon_container.css.js'; + +const MAX_CELL_COUNT = 4; + +export class IconContainerElement extends CrLitElement { + static get is() { + return 'ntp-icon-container'; + } + + static override get styles() { + return getCss(); + } + + override render() { + const overflowCount = this.totalTabCount - MAX_CELL_COUNT + 1; + const overflowCells = Array(overflowCount > 1 ? 1 : 0).fill(overflowCount); + const faviconCells = + this.faviconUrls.slice(0, MAX_CELL_COUNT - overflowCells.length); + const emptyCells = + Array(MAX_CELL_COUNT - faviconCells.length - overflowCells.length) + .fill(null); + + // clang-format off + return html` + <div class="icons-container"> + ${faviconCells.map(url => this.renderIconCell(url))} + ${emptyCells.map(() => this.renderEmptyCell())} + ${overflowCells.map(() => this.renderOverflowCell(overflowCount))} + </div> + `; + // clang-format on + } + + static override get properties() { + return { + faviconUrls: {type: Array}, + totalTabCount: {type: Number}, + }; + } + + accessor faviconUrls: string[] = []; + accessor totalTabCount: number = 0; + + private renderEmptyCell() { + return html`<div class="cell empty"></div>`; + } + + private renderIconCell(url: string) { + // clang-format off + return html` + <div class="cell icon"> + <div class="icon" + style="background-image: ${getFaviconForPageURL(url, false)};"> + </div> + </div> + `; + // clang-format on + } + + private renderOverflowCell(count: number) { + // clang-format off + return html` + <div class="cell overflow-count"> + ${count <= 99 ? html`+${count}` : '99+'} + </div> + `; + // clang-format on + } +} + +declare global { + interface HTMLElementTagNameMap { + 'ntp-icon-container': IconContainerElement; + } +} + +customElements.define(IconContainerElement.is, IconContainerElement);
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/module.css b/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/module.css index 9fa8e468..b6da840 100644 --- a/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/module.css +++ b/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/module.css
@@ -34,18 +34,26 @@ background-color: var(--color-new-tab-page-module-item-background); border-radius: var(--ntp-module-item-border-radius); margin: 8px; + padding: 4px 0px; +} + +.tab-group-container { + cursor: pointer; + display: flex; + padding: 4px 16px 4px 8px; + align-items: center; + align-self: stretch; + position: relative; } .tab-group { - align-items: center; display: flex; - height: 56px; - position: relative; - text-decoration: none; - overflow: hidden; + align-items: center; + gap: 16px; + flex: 1 0 0; } -.tab-group:hover #hoverLayer { +.tab-group-container:hover #hoverLayer { background: var(--color-new-tab-page-module-item-background-hovered); display: block; inset: 0; @@ -57,39 +65,35 @@ display: none; } -.tab-group:first-of-type { +.tab-group-container:first-of-type { border-radius: var(--ntp-module-item-border-radius) var(--ntp-module-item-border-radius) 0 0; padding-top: var(--ntp-module-first-element-top-padding); } -.tab-group:last-of-type { +.tab-group-container:first-of-type:hover #hoverLayer { + border-radius: var(--ntp-module-item-border-radius) + var(--ntp-module-item-border-radius) 0 0; + top: calc(-1 * var(--ntp-module-first-element-top-padding)); +} + +.tab-group-container:last-of-type { border-radius: 0 0 var(--ntp-module-item-border-radius) var(--ntp-module-item-border-radius); padding-bottom: var(--ntp-module-first-element-top-padding); } -.icon-container { - align-items: center; - background-color: var(--color-new-tab-page-module-icon-background); - border-radius: 8px; - display: flex; - flex-shrink: 0; - height: var(--ntp-module-icon-size); - justify-content: center; - margin-inline: 8px; - position: relative; - width: var(--ntp-module-icon-size); -} - -.tab-group-icon { - height: 24px; - width: 24px; +.tab-group-container:last-of-type:hover #hoverLayer { + border-radius: 0 0 var(--ntp-module-item-border-radius) + var(--ntp-module-item-border-radius); + bottom: calc(-1 * var(--ntp-module-first-element-top-padding)); } .tab-group-info { - min-width: 0; - padding-inline-end: 16px; + display: flex; + flex-direction: column; + align-items: flex-start; + flex: 1 0 0; } .tab-group-title, @@ -101,9 +105,16 @@ } .tab-group-title { + align-self: stretch; color: var(--color-new-tab-page-primary-foreground); font-size: var(--ntp-module-text-size); + font-style: normal; + font-weight: 500; + height: 20px; line-height: var(--ntp-module-line-height); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .tab-group-description {
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/module.html b/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/module.html index 18953a95..c78f762 100644 --- a/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/module.html +++ b/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/module.html
@@ -6,14 +6,17 @@ </ntp-module-header-v2> <div id="tabGroups"> ${this.getTabGroups_().map(item => html` - <a class="tab-group" href="${item.url.url}"> + <div class="tab-group-container"> <div id="hoverLayer"></div> - <icon-container class="icon-container"> - <img is="cr-auto-img" class="tab-group-icon" draggable="false"> - </icon-container> - <div class="tab-group-info"> - <div class="tab-group-title">${item.title}</div> - </div> - </a> + <a class="tab-group"> + <ntp-icon-container class="icon-container" + .faviconUrls="${this.getFaviconUrls_(item.faviconUrls)}" + .totalTabCount="${item.totalTabCount}"> + </ntp-icon-container> + <div class="tab-group-info"> + <div class="tab-group-title">${item.title}</div> + </div> + </a> + </div> `)} </div> \ No newline at end of file
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/module.ts b/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/module.ts index 3b656043..734bcf72 100644 --- a/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/module.ts +++ b/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/module.ts
@@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'chrome://resources/cr_elements/cr_auto_img/cr_auto_img.js'; import '/strings.m.js'; import '../module_header.js'; +import './icon_container.js'; import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js'; @@ -80,6 +80,10 @@ protected getTabGroups_(): TabGroup[] { return this.tabGroups.slice(0, MAX_TAB_GROUPS); } + + protected getFaviconUrls_(objects: Array<{url: string}>): string[] { + return objects.map(obj => obj.url); + } } customElements.define(ModuleElement.is, ModuleElement);
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/tab_groups.gni b/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/tab_groups.gni index 645be1de..d0de4b05 100644 --- a/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/tab_groups.gni +++ b/chrome/browser/resources/new_tab_page/modules/v2/tab_groups/tab_groups.gni
@@ -3,9 +3,15 @@ # found in the LICENSE file. # List of files that don't need to be passed to html_to_wrapper(). -tab_groups_ts_files = [ "modules/v2/tab_groups/tab_groups_proxy.ts" ] +tab_groups_ts_files = [ + "modules/v2/tab_groups/icon_container.ts", + "modules/v2/tab_groups/tab_groups_proxy.ts", +] # List of files that should be passed to html_to_wrapper(). tab_groups_web_component_files = [ "modules/v2/tab_groups/module.ts" ] -tab_groups_css_files = [ "modules/v2/tab_groups/module.css" ] +tab_groups_css_files = [ + "modules/v2/tab_groups/icon_container.css", + "modules/v2/tab_groups/module.css", +]
diff --git a/chrome/browser/resources/pdf/constants.ts b/chrome/browser/resources/pdf/constants.ts index 7a8e8c4..26d73384 100644 --- a/chrome/browser/resources/pdf/constants.ts +++ b/chrome/browser/resources/pdf/constants.ts
@@ -141,17 +141,6 @@ namedDestinationView?: string; } -/** - * Enumeration of save message request types. Must match `SaveRequestType` in - * pdf/pdf_view_web_plugin.h. - */ -export enum SaveRequestType { - ANNOTATION, - ORIGINAL, - EDITED, - SEARCHIFIED, // Saves the PDF with extracted text. -} - export interface Point { x: number; y: number;
diff --git a/chrome/browser/resources/pdf/controller.ts b/chrome/browser/resources/pdf/controller.ts index c930a95..778dc28 100644 --- a/chrome/browser/resources/pdf/controller.ts +++ b/chrome/browser/resources/pdf/controller.ts
@@ -9,13 +9,15 @@ // <if expr="enable_pdf_ink2"> import type {AnnotationBrush, AnnotationBrushType, AnnotationMode, TextAnnotation} from './constants.js'; // </if> -import type {NamedDestinationMessageData, Rect, SaveRequestType} from './constants.js'; +import type {NamedDestinationMessageData, Rect} from './constants.js'; // clang-format on import type {PdfPluginElement} from './internal_plugin.js'; import type {DestinationMessageData} from './pdf_viewer_utils.js'; import type {Viewport} from './viewport.js'; import {PinchPhase} from './viewport.js'; +type SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; + export interface MessageData { type: string; messageId?: string;
diff --git a/chrome/browser/resources/pdf/elements/viewer_save_controls_mixin.ts b/chrome/browser/resources/pdf/elements/viewer_save_controls_mixin.ts index be8ec7de..64cf41c 100644 --- a/chrome/browser/resources/pdf/elements/viewer_save_controls_mixin.ts +++ b/chrome/browser/resources/pdf/elements/viewer_save_controls_mixin.ts
@@ -13,9 +13,9 @@ import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js'; import type {CrLitElement, PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js'; -import {SaveRequestType} from '../constants.js'; - +const SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; type Constructor<T> = new (...args: any[]) => T; +type SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; export const ViewerSaveControlsMixin = <T extends Constructor<CrLitElement>>( superClass: T): T&Constructor<ViewerSaveControlsMixinInterface> => {
diff --git a/chrome/browser/resources/pdf/ink_controller.ts b/chrome/browser/resources/pdf/ink_controller.ts index b2600de..856fcfa 100644 --- a/chrome/browser/resources/pdf/ink_controller.ts +++ b/chrome/browser/resources/pdf/ink_controller.ts
@@ -5,11 +5,13 @@ import {assertNotReached} from 'chrome://resources/js/assert.js'; import type {AnnotationTool} from './annotation_tool.js'; -import type {SaveRequestType} from './constants.js'; import type {ContentController, SaveAttachmentMessageData} from './controller.js'; import type {ViewerInkHostElement} from './elements/viewer_ink_host.js'; import type {Viewport} from './viewport.js'; +const SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; +type SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; + /** Event types dispatched by the ink controller. */ export enum InkControllerEventType { HAS_UNSAVED_CHANGES = 'InkControllerEventType.HAS_UNSAVED_CHANGES',
diff --git a/chrome/browser/resources/pdf/pdf_viewer.ts b/chrome/browser/resources/pdf/pdf_viewer.ts index a24dd18..cfd14c7 100644 --- a/chrome/browser/resources/pdf/pdf_viewer.ts +++ b/chrome/browser/resources/pdf/pdf_viewer.ts
@@ -39,7 +39,7 @@ // <if expr="enable_ink or enable_pdf_ink2"> import {AnnotationMode} from './constants.js'; // </if> -import {FittingType, FormFieldFocusType, SaveRequestType} from './constants.js'; +import {FittingType, FormFieldFocusType} from './constants.js'; import type {MessageData} from './controller.js'; import {PluginController} from './controller.js'; // <if expr="enable_pdf_ink2"> @@ -82,6 +82,9 @@ import {hasCtrlModifier, hasCtrlModifierOnly, shouldIgnoreKeyEvents} from './pdf_viewer_utils.js'; // clang-format on +const SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; +type SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; + /** * Keep in sync with the values for enum PDFPostMessageDataType in * tools/metrics/histograms/metadata/pdf/enums.xml. @@ -1345,8 +1348,7 @@ // <if expr="enable_pdf_save_to_drive"> protected onSaveToDrive_(e: CustomEvent<SaveRequestType>) { - // TODO(crbug.com/427449996): Implement the logic to save the PDF to Drive. - console.warn('Saving to Drive is not implemented yet.' + e); + PdfViewerPrivateProxyImpl.getInstance().saveToDrive(e.detail); } // </if> enable_pdf_save_to_drive
diff --git a/chrome/browser/resources/pdf/pdf_viewer_private_proxy.ts b/chrome/browser/resources/pdf/pdf_viewer_private_proxy.ts index 5dada4c..8fd722ee 100644 --- a/chrome/browser/resources/pdf/pdf_viewer_private_proxy.ts +++ b/chrome/browser/resources/pdf/pdf_viewer_private_proxy.ts
@@ -2,15 +2,28 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// <if expr="enable_pdf_save_to_drive"> +type SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; +// </if> + // TODO(crbug.com/40825351): Move the other chrome.pdfViewerPrivate calls across // the PDF UI under this proxy. // `chrome.pdfViewerPrivate.isAllowedLocalFileAccess` is currently located in // `chrome/browser/resources/pdf/navigator.ts`. interface PdfViewerPrivateProxy { + // <if expr="enable_pdf_save_to_drive"> + saveToDrive(saveRequestType: SaveRequestType): void; + // </if> setPdfDocumentTitle(title: string): void; } export class PdfViewerPrivateProxyImpl implements PdfViewerPrivateProxy { + // <if expr="enable_pdf_save_to_drive"> + saveToDrive(saveRequestType: SaveRequestType): void { + chrome.pdfViewerPrivate.saveToDrive(saveRequestType); + } + // </if> + setPdfDocumentTitle(title: string): void { chrome.pdfViewerPrivate.setPdfDocumentTitle(title); }
diff --git a/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts b/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts index 23f49f8..312cb44 100644 --- a/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts +++ b/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts
@@ -19,7 +19,7 @@ // <if expr="enable_pdf_ink2 or enable_ink"> export {AnnotationMode} from './constants.js'; // </if> -export {Attachment, FittingType, FormFieldFocusType, Point, Rect, SaveRequestType} from './constants.js'; +export {Attachment, FittingType, FormFieldFocusType, Point, Rect} from './constants.js'; export {PluginController} from './controller.js'; // <if expr="enable_pdf_ink2"> export {PluginControllerEventType} from './controller.js';
diff --git a/chrome/browser/resources/settings/people_page/people_page.html b/chrome/browser/resources/settings/people_page/people_page.html index 649c15f..6ce7391 100644 --- a/chrome/browser/resources/settings/people_page/people_page.html +++ b/chrome/browser/resources/settings/people_page/people_page.html
@@ -69,7 +69,7 @@ <div> <if expr="not is_chromeos"> <template is="dom-if" if="[[shouldShowSyncAccountControl_( - syncStatus.syncSystemEnabled)]]"> + syncStatus.syncSystemEnabled, syncStatus.signedInStatus)]]" restamp> <settings-sync-account-control sync-status="[[syncStatus]]" prefs="{{prefs}}" @@ -81,45 +81,72 @@ "$i18n{peopleSignInPromptSecondaryWithNoAccount}"> </settings-sync-account-control> </template> -</if> <template is="dom-if" if="[[!shouldShowSyncAccountControl_( syncStatus.syncSystemEnabled, signinAllowed_)]]" restamp> - <div id="profile-row" class="cr-row first two-line" - actionable$="[[isProfileActionable_]]" - on-click="onProfileClick_"> - <template is="dom-if" if="[[syncStatus]]"> + <template is="dom-if" if="[[!shouldLinkToAccountSettingsPage_( + syncStatus.signedInState)]]" restamp> + <div id="profile-row" class="cr-row first two-line" + actionable$="[[isProfileActionable_]]" + on-click="onProfileClick_"> + <template is="dom-if" if="[[syncStatus]]"> + <div id="profile-icon" + style="background-image: [[getIconImageSet_( + profileIconUrl_)]]"> + </div> + <div class="flex cr-row-gap cr-padded-text text-elide"> + <span id="profile-name">[[profileName_]]</span> + </div> + <cr-icon-button class="subpage-arrow" + aria-label="$i18n{editPerson}" + aria-describedby="profile-name" + aria-roledescription="$i18n{subpageArrowRoleDescription}"> + </cr-icon-button> + </template> + </div> + </template> + <template is="dom-if" if="[[shouldLinkToAccountSettingsPage_( + syncStatus.signedInState)]]" restamp> + <cr-link-row id="account-subpage-row" on-click="onAccountClick_"> <div id="profile-icon" style="background-image: [[getIconImageSet_( - profileIconUrl_)]]"> + primaryAccountIconUrl_)]]"> </div> - <div class="flex cr-row-gap cr-padded-text text-elide"> - <span id="profile-name">[[profileName_]]</span> -<!-- When the user is signed-in, the settings-sync-account-control is always -shown on non-ChromeOS platforms --> -<if expr="is_chromeos"> - <div class="secondary" hidden="[[!isSyncing_( - syncStatus.signedInState)]]"> - [[syncStatus.signedInUsername]] + <div class="cr-row-gap cr-padded-text flex no-min-width"> + <div id="account-name" class="text-elide"> + [[primaryAccountName_]] </div> -</if> + <div id="account-email" class="secondary text-elide"> + [[primaryAccountEmail_]] + </div> </div> -<if expr="not is_chromeos"> - <cr-icon-button class="subpage-arrow" - aria-label="$i18n{editPerson}" - aria-describedby="profile-name" - aria-roledescription="$i18n{subpageArrowRoleDescription}"> - </cr-icon-button> -</if> -<if expr="is_chromeos"> - <cr-icon-button class="icon-external" - id="profile-subpage-arrow" - hidden="[[!isProfileActionable_]]" - aria-label="$i18n{accountManagerSubMenuLabel}" - aria-describedby="profile-name"></cr-icon-button> -</if> - </template> - </div> + </cr-link-row> + </template> </template> <!-- if="[[!shouldShowSyncAccountControl_()]]" --> +</if> + +<if expr="is_chromeos"> + <div id="profile-row" class="cr-row first two-line" + actionable$="[[isProfileActionable_]]" on-click="onProfileClick_"> + <template is="dom-if" if="[[syncStatus]]"> + <div id="profile-icon" + style="background-image: [[getIconImageSet_( + profileIconUrl_)]]"> + </div> + <div class="flex cr-row-gap cr-padded-text text-elide"> + <span id="profile-name">[[profileName_]]</span> + <div class="secondary" hidden="[[!isSyncing_( + syncStatus.signedInState)]]"> + [[syncStatus.signedInUsername]] + </div> + </div> + <cr-icon-button class="icon-external" + id="profile-subpage-arrow" + hidden="[[!isProfileActionable_]]" + aria-label="$i18n{accountManagerSubMenuLabel}" + aria-describedby="profile-name"></cr-icon-button> + </template> + </div> +</if> <cr-link-row id="sync-setup" label="$i18n{syncAndNonPersonalizedServices}" @@ -138,15 +165,11 @@ label="$i18n{profileNameAndPicture}" on-click="onProfileClick_" ></cr-link-row> </template> -</if> -<if expr="not is_chromeos"> <cr-link-row id="importDataDialogTrigger" label="$i18n{importTitle}" on-click="onImportDataClick_"></cr-link-row> -</if> -<if expr="not is_chromeos"> <template is="dom-if" if="[[isDasherlessProfile_]]"> <div id="sync-not-allowed" class="cr-row continuation"> <cr-icon id="info-icon" icon="cr:info-outline"></cr-icon>
diff --git a/chrome/browser/resources/settings/people_page/people_page.ts b/chrome/browser/resources/settings/people_page/people_page.ts index 1297174..8acae5e5 100644 --- a/chrome/browser/resources/settings/people_page/people_page.ts +++ b/chrome/browser/resources/settings/people_page/people_page.ts
@@ -185,6 +185,9 @@ }, showSignoutDialog_: Boolean, + primaryAccountName_: String, + primaryAccountEmail_: String, + primaryAccountIconUrl_: String, // </if> }; } @@ -204,6 +207,9 @@ declare private shouldShowAccountSettingsPage_: boolean; declare private showImportDataDialog_: boolean; declare private showSignoutDialog_: boolean; + declare private primaryAccountName_: string; + declare private primaryAccountEmail_: string; + declare private primaryAccountIconUrl_: string; // </if> private syncBrowserProxy_: SyncBrowserProxy = @@ -236,11 +242,10 @@ 'sync-status-changed', this.handleSyncStatus_.bind(this)); // <if expr="not is_chromeos"> - const handleStoredAccounts = (accounts: StoredAccount[]) => { - this.storedAccounts = accounts; - }; - this.syncBrowserProxy_.getStoredAccounts().then(handleStoredAccounts); - this.addWebUiListener('stored-accounts-updated', handleStoredAccounts); + this.syncBrowserProxy_.getStoredAccounts().then( + this.handleStoredAccounts_.bind(this)); + this.addWebUiListener( + 'stored-accounts-updated', this.handleStoredAccounts_.bind(this)); this.addWebUiListener('sync-settings-saved', () => { this.$.toast.show(); @@ -306,6 +311,10 @@ * Handler for when the sync state is pushed from the browser. */ private handleSyncStatus_(syncStatus: SyncStatus) { + // <if expr="is_chromeos"> + this.syncStatus = syncStatus; + // </if> + // <if expr="not is_chromeos"> // Sign-in impressions should be recorded only if the sign-in promo is // shown. They should be recorder only once, the first time // |this.syncStatus| is set. @@ -318,6 +327,7 @@ // SyncAccountControl records the impressions user actions. chrome.metricsPrivate.recordUserAction('Signin_Impression_FromSettings'); } + // </if> } // <if expr="not is_chromeos"> @@ -360,6 +370,10 @@ } // <if expr="not is_chromeos"> + private onAccountClick_() { + Router.getInstance().navigateTo(routes.ACCOUNT); + } + private onImportDataClick_() { Router.getInstance().navigateTo(routes.IMPORT_DATA); } @@ -368,6 +382,33 @@ Router.getInstance().navigateToPreviousRoute(); focusWithoutInk(this.$.importDataDialogTrigger); } + + private shouldLinkToAccountSettingsPage_(): boolean { + return this.shouldShowAccountSettingsPage_ && !!this.syncStatus && + this.syncStatus.signedInState === SignedInState.SIGNED_IN; + } + + private shouldShowSyncAccountControl_(): boolean { + if (this.syncStatus === undefined) { + return false; + } + return !!this.syncStatus!.syncSystemEnabled && this.signinAllowed_ && + !this.shouldLinkToAccountSettingsPage_(); + } + + private handleStoredAccounts_(accounts: StoredAccount[]) { + this.storedAccounts = accounts; + + // The user might not have any GAIA accounts (e.g. signed out). In this case + // the link row to the account settings page does not exist, so there's + // nothing to do. + if (accounts.length === 0) { + return; + } + this.primaryAccountName_ = accounts[0].fullName!; + this.primaryAccountEmail_ = accounts[0].email; + this.primaryAccountIconUrl_ = accounts[0].avatarImage!; + } // </if> /** @@ -379,18 +420,6 @@ chrome.metricsPrivate.recordUserAction('ManageGoogleAccount_Clicked'); } - private shouldShowSyncAccountControl_(): boolean { - // <if expr="is_chromeos"> - return false; - // </if> - // <if expr="not is_chromeos"> - if (this.syncStatus === undefined) { - return false; - } - return !!this.syncStatus!.syncSystemEnabled && this.signinAllowed_; - // </if> - } - /** * @return A CSS image-set for multiple scale factors. */ @@ -417,6 +446,9 @@ '#edit-profile' : '#profile-row .subpage-arrow'); } + if (routes.ACCOUNT) { + map.set(routes.ACCOUNT.path, '#account-subpage-row'); + } // </if> return map; }
diff --git a/chrome/browser/resources/settings/people_page/people_page_index.ts b/chrome/browser/resources/settings/people_page/people_page_index.ts index 9db7dfd..8d4d3aa 100644 --- a/chrome/browser/resources/settings/people_page/people_page_index.ts +++ b/chrome/browser/resources/settings/people_page/people_page_index.ts
@@ -85,6 +85,12 @@ 'syncControls', 'no-animation', 'no-animation'); break; // <if expr="not is_chromeos"> + case routes.SIGN_OUT: + // Switch to settings-people-page since the sign out dialog resides + // there, otherwise it will not be visible even if open. + this.$.viewManager.switchView( + 'parent', 'no-animation', 'no-animation'); + break; case routes.ACCOUNT: assert(this.shouldShowAccountSettingsPage_); this.$.viewManager.switchView(
diff --git a/chrome/browser/resources/watermark/BUILD.gn b/chrome/browser/resources/watermark/BUILD.gn index a2290ce2..77621087 100644 --- a/chrome/browser/resources/watermark/BUILD.gn +++ b/chrome/browser/resources/watermark/BUILD.gn
@@ -22,6 +22,7 @@ ts_deps = [ "//third_party/lit/v3_0:build_ts", + "//ui/webui/resources/cr_elements:build_ts", "//ui/webui/resources/js:build_ts", ] }
diff --git a/chrome/browser/resources/watermark/app.css b/chrome/browser/resources/watermark/app.css index 1836bc4..c5321f2 100644 --- a/chrome/browser/resources/watermark/app.css +++ b/chrome/browser/resources/watermark/app.css
@@ -7,8 +7,93 @@ * #scheme=relative * #css_wrapper_metadata_end */ +/* TODO(crbug.com/434676946): Adjust styling to align with UX guidelines + * before enabling the experiment. This includes replacing hard-coded + * values with named constants/tokens. */ + +:host { + display: block; + padding: 24px; +} + +.controls-card { + background: white; + border-radius: 12px; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .3), + 0 2px 6px 2px rgba(0, 0, 0, .15); + padding: 16px 24px; + width: 360px; +} + +.card-header { + align-items: center; + display: flex; + justify-content: space-between; + margin-bottom: 24px; +} + +.card-header cr-button { + border-radius: 100px; + flex-shrink: 0; + height: 32px; + padding: 0 16px; + border: 1px solid #A8C7FA; + color: #0B57D0; +} + +.header-text > h2 { + font-size: 16px; + font-weight: 500; + margin: 0; +} + +.header-text > p { + color: #5f6368; + font-size: 13px; + margin: 2px 0 0 0; +} + +.control-row { + align-items: center; + display: flex; + height: 40px; + justify-content: space-between; +} + +.control-row > span { + color: #202124; + font-size: 13px; +} + +cr-slider { + width: 170px; + --cr-slider-active-color: #1F6DD2; + --cr-slider-knob-color: #1A73E8; + --cr-slider-container-color: #D2E3FC; + --cr-slider-bar-height: 4px; + line-height: 20px; +} + +cr-input.font-size-input.stroked { + --cr-input-border-radius: 8px; + width: 70px; + --cr-input-border-color: #C7C7C7; + --cr-input-color: #1F1F1F; +} + +cr-input.font-size-input.stroked > #input { + text-align: center; +} + +#values { + border-top: 1px solid #e8eaed; + color: #5f6368; + font-family: 'Roboto Mono', monospace; + font-size: 12px; + margin-top: 16px; + padding-top: 16px; +} + #watermark { - color: red; - font-size: 2em; - font-weight: bold; + display: none; }
diff --git a/chrome/browser/resources/watermark/app.html.ts b/chrome/browser/resources/watermark/app.html.ts index e6819036..21ec284 100644 --- a/chrome/browser/resources/watermark/app.html.ts +++ b/chrome/browser/resources/watermark/app.html.ts
@@ -6,8 +6,47 @@ import type {WatermarkAppElement} from './app.js'; +// TODO(crbug.com/434714853): Replace with i18n strings + export function getHtml(this: WatermarkAppElement) { return html` -<h1>Watermark</h1> -<div id="watermark">${this.message_}</div>`; + <div class="controls-card"> + <div class="card-header"> + <div class="header-text"> + <h2>Watermark testing</h2> + <p>Customize the watermark style and test it live</p> + </div> + <cr-button @click="${this.onCopyJsonClick_}"> + Copy style in JSON + </cr-button> + </div> + + <div class="control-row"> + <span>Font size</span> + <cr-input id="fontSizeInput" class="font-size-input + stroked" type="number" + min="1" .value="${this.fontSize_.toString()}" + @value-changed="${this.onFontSizeChanged_}"> + </cr-input> + </div> + + <div class="control-row"> + <span>White outline opacity</span> + <cr-slider id="outlineOpacitySlider" min="0" max="100" + .value="${this.outlineOpacity_}" + .ticks="${this.opacityTicks_}" + @cr-slider-value-changed="${this.onOutlineOpacityChanged_}"> + </cr-slider> + </div> + + <div class="control-row"> + <span>Dark fill opacity</span> + <cr-slider id="fillOpacitySlider" min="0" max="100" + .value="${this.fillOpacity_}" + .ticks="${this.opacityTicks_}" + @cr-slider-value-changed="${this.onFillOpacityChanged_}"> + </cr-slider> + </div> + </div> + `; }
diff --git a/chrome/browser/resources/watermark/app.ts b/chrome/browser/resources/watermark/app.ts index 0258b88..5e2216f4 100644 --- a/chrome/browser/resources/watermark/app.ts +++ b/chrome/browser/resources/watermark/app.ts
@@ -3,13 +3,25 @@ // found in the LICENSE file. import '/strings.m.js'; +import 'chrome://resources/cr_elements/cr_button/cr_button.js'; +import 'chrome://resources/cr_elements/cr_input/cr_input.js'; +import 'chrome://resources/cr_elements/cr_slider/cr_slider.js'; +import type {CrInputElement} from '//resources/cr_elements/cr_input/cr_input.js'; +import type {CrSliderElement, SliderTick} from '//resources/cr_elements/cr_slider/cr_slider.js'; import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js'; -import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import {getCss} from './app.css.js'; import {getHtml} from './app.html.js'; +export interface WatermarkAppElement { + $: { + fontSizeInput: CrInputElement, + fillOpacitySlider: CrSliderElement, + outlineOpacitySlider: CrSliderElement, + }; +} + export class WatermarkAppElement extends CrLitElement { static get is() { return 'watermark-app'; @@ -25,11 +37,51 @@ static override get properties() { return { - message_: {type: String}, + fontSize_: {type: Number}, + fillOpacity_: {type: Number}, + outlineOpacity_: {type: Number}, + opacityTicks_: {type: Array}, }; } - protected accessor message_: string = loadTimeData.getString('message'); + protected accessor fontSize_: number = 24; + protected accessor fillOpacity_: number = 4; + protected accessor outlineOpacity_: number = 6; + protected accessor opacityTicks_: SliderTick[] = []; + + constructor() { + super(); + for (let i = 0; i <= 100; i++) { + this.opacityTicks_.push({label: String(i), value: i}); + } + } + + protected onCopyJsonClick_() { + navigator.clipboard.writeText(JSON.stringify( + { + WatermarkStyle: { + fill_opacity: this.fillOpacity_, + outline_opacity: this.outlineOpacity_, + font_size: this.fontSize_, + }, + }, + null, 2)); + } + + protected onFontSizeChanged_() { + const parsedValue = parseInt(this.$.fontSizeInput.value, 10); + if (!isNaN(parsedValue) && parsedValue >= 1) { + this.fontSize_ = parsedValue; + } + } + + protected onFillOpacityChanged_() { + this.fillOpacity_ = Math.round(this.$.fillOpacitySlider.value); + } + + protected onOutlineOpacityChanged_() { + this.outlineOpacity_ = Math.round(this.$.outlineOpacitySlider.value); + } } declare global {
diff --git a/chrome/browser/resources/watermark/watermark.html b/chrome/browser/resources/watermark/watermark.html index 733a972..03f31ed 100644 --- a/chrome/browser/resources/watermark/watermark.html +++ b/chrome/browser/resources/watermark/watermark.html
@@ -2,10 +2,13 @@ <html dir="$i18n{textdirection}" lang="$i18n{language}"> <head> <meta charset="utf-8"> - <title>Watermark Test Page</title> + <title>Watermark Customization</title> <style> body { margin: 0; + height: 100vh; + font-family: sans-serif; + overflow: hidden; } </style> </head>
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_tooltip/cr_tooltip_demo.css b/chrome/browser/resources/webui_gallery/demos/cr_tooltip/cr_tooltip_demo.css index ec42ce2..5c725306 100644 --- a/chrome/browser/resources/webui_gallery/demos/cr_tooltip/cr_tooltip_demo.css +++ b/chrome/browser/resources/webui_gallery/demos/cr_tooltip/cr_tooltip_demo.css
@@ -11,14 +11,8 @@ * #css_wrapper_metadata_end */ .target { - border: 1px black; + border: 1px solid light-dark(black, white); padding: 8px; margin: 40px; width: fit-content; } - -@media (prefers-color-scheme: dark) { - .target { - border: 1px white; - } -}
diff --git a/chrome/browser/serial/chrome_serial_realtarget_browsertest.cc b/chrome/browser/serial/chrome_serial_realtarget_browsertest.cc index fa63363a..3aeeb4b 100644 --- a/chrome/browser/serial/chrome_serial_realtarget_browsertest.cc +++ b/chrome/browser/serial/chrome_serial_realtarget_browsertest.cc
@@ -176,15 +176,7 @@ EXPECT_EQ(true, EvalJs(web_contents, test_script)); } -// TODO(432549077): MacOS can not run because of system permission. -#if BUILDFLAG(IS_MAC) -#define MAYBE_BluetoothSerialOpenAndClosePort \ - DISABLED_BluetoothSerialOpenAndClosePort -#else -#define MAYBE_BluetoothSerialOpenAndClosePort BluetoothSerialOpenAndClosePort -#endif -IN_PROC_BROWSER_TEST_F(SerialRealTargetTest, - MAYBE_BluetoothSerialOpenAndClosePort) { +IN_PROC_BROWSER_TEST_F(SerialRealTargetTest, BluetoothSerialOpenAndClosePort) { content::WebContents* web_contents = browser()->tab_strip_model()->GetActiveWebContents(); auto test_script = base::StringPrintf(
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java index f44dd176..95f5e56 100644 --- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java +++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java
@@ -12,6 +12,7 @@ import org.chromium.cc.input.BrowserControlsState; import org.chromium.chrome.browser.browser_controls.BrowserControlsOffsetTagsInfo; import org.chromium.chrome.browser.tab.Tab.LoadUrlResult; +import org.chromium.chrome.browser.tab.Tab.MediaState; import org.chromium.components.find_in_page.FindMatchRectsDetails; import org.chromium.components.find_in_page.FindNotificationDetails; import org.chromium.content_public.browser.LoadUrlParams; @@ -68,6 +69,9 @@ public void onFaviconUpdated(Tab tab, @Nullable Bitmap icon, @Nullable GURL iconUrl) {} @Override + public void onMediaStateChanged(Tab tab, @MediaState int mediaState) {} + + @Override public void onTitleUpdated(Tab tab) {} @Override
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java index 5bf4c6d..3c87347 100644 --- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java +++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -27,8 +27,10 @@ import org.chromium.ui.base.WindowAndroid; import org.chromium.url.GURL; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * Tab is a visual/functional unit that encapsulates the content (not just web site content from @@ -47,6 +49,24 @@ int DEFAULT_PAGE_LOAD = 1; } + /** Tracks the media indicator state of the tab. */ + @IntDef({ + MediaState.NONE, + MediaState.AUDIBLE, + MediaState.MUTED, + MediaState.RECORDING, + MediaState.SHARING + }) + @Target(ElementType.TYPE_USE) + @Retention(RetentionPolicy.SOURCE) + public @interface MediaState { + int NONE = 0; + int AUDIBLE = 1; + int MUTED = 2; + int RECORDING = 3; + int SHARING = 4; + } + /** The result of the loadUrl. */ class LoadUrlResult { /** Tab load status. */ @@ -491,6 +511,17 @@ */ void setIsPinned(boolean isPinned); + /** Returns the media state of the tab. */ + @MediaState + int getMediaState(); + + /** + * Sets the media state of the tab. + * + * @param mediaState The {@link MediaState} of the tab. + */ + void setMediaState(@MediaState int mediaState); + /** Called when the tab is restored from the archived tab model. */ void onTabRestoredFromArchivedTabModel();
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObserver.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObserver.java index ce60664..bcc2b64 100644 --- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObserver.java +++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabObserver.java
@@ -12,6 +12,7 @@ import org.chromium.cc.input.BrowserControlsState; import org.chromium.chrome.browser.browser_controls.BrowserControlsOffsetTagsInfo; import org.chromium.chrome.browser.tab.Tab.LoadUrlResult; +import org.chromium.chrome.browser.tab.Tab.MediaState; import org.chromium.components.find_in_page.FindMatchRectsDetails; import org.chromium.components.find_in_page.FindNotificationDetails; import org.chromium.content_public.browser.LoadUrlParams; @@ -115,13 +116,23 @@ void onFaviconUpdated(Tab tab, @Nullable Bitmap icon, @Nullable GURL iconUrl); /** + * Called when the media state changes + * + * @param tab The notifying {@link Tab}. + * @param mediaState The {@link MediaState} of the tab. + */ + void onMediaStateChanged(Tab tab, @MediaState int mediaState); + + /** * Called when the title of a {@link Tab} changes. + * * @param tab The notifying {@link Tab}. */ void onTitleUpdated(Tab tab); /** * Called when the URL of a {@link Tab} changes. + * * @param tab The notifying {@link Tab}. */ void onUrlUpdated(Tab tab);
diff --git a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserver.java b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserver.java index bf3126b..d070d420 100644 --- a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserver.java +++ b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserver.java
@@ -220,7 +220,7 @@ } @Override - public void didMergeTabToGroup(Tab movedTab) { + public void didMergeTabToGroup(Tab movedTab, boolean isDestinationTab) { if (!mIsObserving) return; LogUtils.log(TAG, "didMergeTabToGroup, rootId = " + movedTab.getRootId());
diff --git a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserverUnitTest.java b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserverUnitTest.java index ee37048..183110d2 100644 --- a/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserverUnitTest.java +++ b/chrome/browser/tab_group_sync/android/java/src/org/chromium/chrome/browser/tab_group_sync/TabGroupSyncLocalObserverUnitTest.java
@@ -553,7 +553,9 @@ @Test public void testDidMergeTabToGroup() { - mTabGroupModelFilterObserverCaptor.getValue().didMergeTabToGroup(mTab1); + mTabGroupModelFilterObserverCaptor + .getValue() + .didMergeTabToGroup(mTab1, /* isDestinationTab= */ true); verify(mTabGroupSyncService, times(1)).addGroup(mSavedTabGroupCaptor.capture()); Assert.assertEquals(LOCAL_TAB_GROUP_ID_1, mSavedTabGroupCaptor.getValue().localId); verify(mTabGroupModelFilter, never()).getRelatedTabList(anyInt());
diff --git a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabCardThemeUtil.java b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabCardThemeUtil.java index 921a6c1e..15b0e83 100644 --- a/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabCardThemeUtil.java +++ b/chrome/browser/tab_ui/android/java/src/org/chromium/chrome/browser/tab_ui/TabCardThemeUtil.java
@@ -44,6 +44,19 @@ } /** + * Returns the ColorStateList for media indicator based on the incognito mode or selected. + * + * @param context {@link Context} used to retrieve color. + * @param isIncognito Whether the color is used for incognito mode. + * @param isSelected Whether the tab is currently selected. + */ + public static ColorStateList getMediaIndicatorColorStateList( + Context context, boolean isIncognito, boolean isSelected) { + return ColorStateList.valueOf( + getChromeOwnedFaviconTintColor(context, isIncognito, isSelected)); + } + + /** * Returns the title text appearance for the tab grid card based on the incognito mode. * * @param context {@link Context} used to retrieve color.
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java index 8097daa..eae9236 100644 --- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java +++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java
@@ -244,7 +244,7 @@ } for (TabGroupModelFilterObserver observer : mGroupFilterObserver) { - observer.didMergeTabToGroup(tab); + observer.didMergeTabToGroup(tab, /* isDestinationTab= */ true); } } @@ -327,13 +327,13 @@ if (!wasDestinationTabInAGroup) { for (TabGroupModelFilterObserver observer : mGroupFilterObserver) { - observer.didMergeTabToGroup(destinationTab); + observer.didMergeTabToGroup(destinationTab, /* isDestinationTab= */ true); } } for (int i = 0; i < tabsToMerge.size(); i++) { Tab tab = tabsToMerge.get(i); for (TabGroupModelFilterObserver observer : mGroupFilterObserver) { - observer.didMergeTabToGroup(tab); + observer.didMergeTabToGroup(tab, /* isDestinationTab= */ false); } } @@ -428,7 +428,7 @@ destinationTab, destinationRootId, destinationTabGroupId); } for (TabGroupModelFilterObserver observer : mGroupFilterObserver) { - observer.didMergeTabToGroup(destinationTab); + observer.didMergeTabToGroup(destinationTab, /* isDestinationTab= */ true); } } @@ -681,7 +681,7 @@ if (isChangingGroups && isTabInTabGroup(tab)) { // Last shown tab IDs are not preserved across an undo. for (TabGroupModelFilterObserver observer : mGroupFilterObserver) { - observer.didMergeTabToGroup(tab); + observer.didMergeTabToGroup(tab, /* isDestinationTab= */ false); } } } @@ -1245,7 +1245,7 @@ resetFilterState(); for (TabGroupModelFilterObserver observer : mGroupFilterObserver) { - observer.didMergeTabToGroup(tab); + observer.didMergeTabToGroup(tab, /* isDestinationTab= */ false); } } else { reorder();
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImplUnitTest.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImplUnitTest.java index ff55e17..0e1f86d6 100644 --- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImplUnitTest.java +++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImplUnitTest.java
@@ -991,7 +991,8 @@ assertEquals(mTab1.getTabGroupId(), tabGroupId); verify(mTabGroupModelFilterObserver).didCreateNewGroup(mTab1, mTabGroupModelFilter); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab1); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab1, /* isDestinationTab= */ true); verify(mTabGroupModelFilterObserver, never()) .showUndoGroupSnackbar( anyList(), @@ -1168,7 +1169,8 @@ verify(mTabGroupModelFilterObserver).willMergeTabToGroup(mTab4, TAB1_ROOT_ID, tabGroupId); verify(mTabModel).moveTab(mTab4.getId(), POSITION2); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab4); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab4, /* isDestinationTab= */ false); verify(mTabGroupModelFilterObserver).didCreateNewGroup(mTab1, mTabGroupModelFilter); assertArrayEquals( mTabGroupModelFilter.getRelatedTabList(mTab4.getId()).toArray(), @@ -1196,9 +1198,12 @@ .willMergeTabToGroup(mTab6, TAB1_ROOT_ID, TAB5_TAB_GROUP_ID); verify(mTabModel).moveTab(mTab5.getId(), ++startIndex); verify(mTabModel).moveTab(mTab6.getId(), ++startIndex); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab1); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab5); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab6); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab1, /* isDestinationTab= */ true); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab5, /* isDestinationTab= */ false); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab6, /* isDestinationTab= */ false); verify(mTabGroupModelFilterObserver, never()) .didCreateNewGroup(mTab6, mTabGroupModelFilter); verify(mTabGroupModelFilterObserver) @@ -1237,8 +1242,10 @@ .willMergeTabToGroup(mTab6, TAB2_ROOT_ID, TAB2_TAB_GROUP_ID); verify(mTabModel).moveTab(mTab5.getId(), ++startIndex); verify(mTabModel).moveTab(mTab6.getId(), ++startIndex); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab5); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab6); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab5, /* isDestinationTab= */ false); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab6, /* isDestinationTab= */ false); verify(mTabGroupModelFilterObserver, never()) .didCreateNewGroup(mTab6, mTabGroupModelFilter); verify(mTabGroupModelFilterObserver) @@ -1268,7 +1275,8 @@ verify(mTabGroupModelFilterObserver).willMergeTabToGroup(mTab1, TAB4_ROOT_ID, tabGroupId); verify(mTabModel).moveTab(mTab1.getId(), startIndex); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab1); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab1, /* isDestinationTab= */ false); verify(mTabGroupModelFilterObserver).didCreateNewGroup(mTab4, mTabGroupModelFilter); assertArrayEquals( mTabGroupModelFilter.getRelatedTabList(mTab1.getId()).toArray(), @@ -1296,9 +1304,12 @@ .willMergeTabToGroup(mTab3, TAB4_ROOT_ID, TAB2_TAB_GROUP_ID); verify(mTabModel).moveTab(mTab2.getId(), startIndex); verify(mTabModel).moveTab(mTab3.getId(), startIndex); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab4); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab2); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab3); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab4, /* isDestinationTab= */ true); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab2, /* isDestinationTab= */ false); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab3, /* isDestinationTab= */ false); verify(mTabGroupModelFilterObserver, never()) .didCreateNewGroup(mTab2, mTabGroupModelFilter); verify(mTabGroupModelFilterObserver) @@ -1352,8 +1363,10 @@ .willMergeTabToGroup(mTab4, TAB5_ROOT_ID, TAB5_TAB_GROUP_ID); verify(mTabModel).moveTab(mTab1.getId(), POSITION6); verify(mTabModel).moveTab(mTab4.getId(), POSITION6); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab1); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab4); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab1, /* isDestinationTab= */ false); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab4, /* isDestinationTab= */ false); assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray()); // Attempt to merge single tabs with group tabs. @@ -1384,8 +1397,10 @@ verify(mTabGroupModelFilterObserver).willMergeTabToGroup(newTab, TAB1_ROOT_ID, tabGroupId); verify(mTabModel).moveTab(mTab4.getId(), POSITION1 + 1); verify(mTabModel).moveTab(newTab.getId(), POSITION1 + 2); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab4); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(newTab); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab4, /* isDestinationTab= */ false); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(newTab, /* isDestinationTab= */ false); assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray()); // Attempt to merge all single tabs, resulting in a new group creation. @@ -1412,8 +1427,10 @@ verify(mTabGroupModelFilterObserver).willMergeTabToGroup(newTab, TAB4_ROOT_ID, tabGroupId); verify(mTabModel).moveTab(mTab1.getId(), POSITION4); verify(mTabModel).moveTab(newTab.getId(), POSITION4 + 1); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab1); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(newTab); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab1, /* isDestinationTab= */ false); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(newTab, /* isDestinationTab= */ false); assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray()); // Attempt to merge all single tabs, resulting in a new group creation. @@ -1449,8 +1466,10 @@ verify(mTabGroupModelFilterObserver) .willMergeTabToGroup(newTab2, newTab0.getId(), tabGroupId); verify(mTabModel, never()).moveTab(anyInt(), anyInt()); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(newTab1); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(newTab2); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(newTab1, /* isDestinationTab= */ false); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(newTab2, /* isDestinationTab= */ false); assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray()); // Attempt to merge all single tabs, resulting in a new group creation. @@ -1488,7 +1507,8 @@ .willMergeTabToGroup(newTab0, newTab1.getId(), tabGroupId); verify(mTabModel).moveTab(newTab0.getId(), 8); // Skip newTab1, newTab2. - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(newTab0); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(newTab0, /* isDestinationTab= */ false); assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray()); // Attempt to merge a single tab with group tabs. @@ -1575,7 +1595,8 @@ verify(mTabGroupModelFilterObserver).willMergeTabToGroup(mTab4, TAB1_ROOT_ID, tabGroupId); verify(mTabModel).moveTab(mTab4.getId(), 1); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab4); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab4, /* isDestinationTab= */ false); verify(mTabGroupModelFilterObserver).didCreateNewGroup(mTab1, mTabGroupModelFilter); verify(mTabGroupModelFilterObserver, never()).didRemoveTabGroup(anyInt(), any(), anyInt()); assertArrayEquals( @@ -1650,7 +1671,8 @@ // No call should be made here. verify(mTabGroupModelFilterObserver, never()) .didMoveTabOutOfGroup(any(Tab.class), anyInt()); - verify(mTabGroupModelFilterObserver, never()).didMergeTabToGroup(any(Tab.class)); + verify(mTabGroupModelFilterObserver, never()) + .didMergeTabToGroup(any(Tab.class), anyBoolean()); verify(mTabGroupModelFilterObserver, never()) .didMoveWithinGroup(any(Tab.class), anyInt(), anyInt()); verify(mTabGroupModelFilterObserver, never()) @@ -1665,7 +1687,8 @@ // No call should be made here. verify(mTabGroupModelFilterObserver, never()) .didMoveTabOutOfGroup(any(Tab.class), anyInt()); - verify(mTabGroupModelFilterObserver, never()).didMergeTabToGroup(any(Tab.class)); + verify(mTabGroupModelFilterObserver, never()) + .didMergeTabToGroup(any(Tab.class), anyBoolean()); verify(mTabGroupModelFilterObserver, never()) .didMoveWithinGroup(any(Tab.class), anyInt(), anyInt()); verify(mTabGroupModelFilterObserver, never()) @@ -1698,7 +1721,7 @@ verifyNoMoreInteractions(mAttributesObserver); verify(mTabGroupModelFilterObserver).willMoveTabOutOfGroup(mTab4, TAB4_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).didMoveTabOutOfGroup(mTab4, POSITION2); - verify(mTabGroupModelFilterObserver, never()).didMergeTabToGroup(eq(mTab4)); + verify(mTabGroupModelFilterObserver, never()).didMergeTabToGroup(eq(mTab4), anyBoolean()); } @Test @@ -1712,7 +1735,8 @@ mTabGroupModelFilter.createSingleTabGroup(mTab4); assertThat(mTabGroupModelFilter.getTabGroupCount(), equalTo(3)); verify(mTabGroupModelFilterObserver).didCreateNewGroup(mTab4, mTabGroupModelFilter); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab4); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab4, /* isDestinationTab= */ true); verify(mTabGroupModelFilterObserver, never()) .showUndoGroupSnackbar(any(), any(), any(), any(), any(), anyInt(), anyBoolean()); @@ -1739,7 +1763,7 @@ assertThat(mTabGroupModelFilter.getTabGroupCount(), equalTo(3)); verify(mTabGroupModelFilterObserver).willMoveTabOutOfGroup(mTab1, TAB1_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).didMoveTabOutOfGroup(mTab1, POSITION3); - verify(mTabGroupModelFilterObserver, never()).didMergeTabToGroup(eq(mTab1)); + verify(mTabGroupModelFilterObserver, never()).didMergeTabToGroup(eq(mTab1), anyBoolean()); } @Test @@ -1776,7 +1800,7 @@ assertThat(mTabGroupModelFilter.getTabGroupCount(), equalTo(3)); verify(mTabGroupModelFilterObserver).willMoveTabOutOfGroup(mTab4, TAB4_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).didMoveTabOutOfGroup(mTab4, POSITION1); - verify(mTabGroupModelFilterObserver, never()).didMergeTabToGroup(eq(mTab4)); + verify(mTabGroupModelFilterObserver, never()).didMergeTabToGroup(eq(mTab4), anyBoolean()); } @Test @@ -1808,15 +1832,17 @@ mTabGroupModelFilter.undoGroupedTab(mTab6, POSITION6, TAB5_ROOT_ID, TAB5_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).willMoveTabOutOfGroup(mTab6, TAB5_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).didMoveTabOutOfGroup(mTab6, POSITION2); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab6); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab6, /* isDestinationTab= */ false); mTabGroupModelFilter.undoGroupedTab(mTab5, POSITION5, TAB5_ROOT_ID, TAB5_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).willMoveTabOutOfGroup(mTab5, TAB5_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).didMoveTabOutOfGroup(mTab5, POSITION2); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab5); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab5, /* isDestinationTab= */ false); mTabGroupModelFilter.undoGroupedTab(mTab4, POSITION4, TAB4_ROOT_ID, TAB4_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).willMoveTabOutOfGroup(mTab4, TAB4_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).didMoveTabOutOfGroup(mTab4, POSITION2); - verify(mTabGroupModelFilterObserver, never()).didMergeTabToGroup(eq(mTab4)); + verify(mTabGroupModelFilterObserver, never()).didMergeTabToGroup(eq(mTab4), anyBoolean()); assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray()); assertThat(mTab4.getRootId(), equalTo(TAB4_ROOT_ID)); @@ -1858,16 +1884,18 @@ mTabGroupModelFilter.undoGroupedTab(mTab3, POSITION2, TAB2_ROOT_ID, TAB2_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).willMoveTabOutOfGroup(mTab3, TAB2_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).didMoveTabOutOfGroup(mTab3, POSITION1); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab3); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab3, /* isDestinationTab= */ false); mTabGroupModelFilter.undoGroupedTab(mTab2, POSITION2, TAB2_ROOT_ID, TAB2_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).willMoveTabOutOfGroup(mTab2, TAB2_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).didMoveTabOutOfGroup(mTab2, POSITION1); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab2); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab2, /* isDestinationTab= */ false); mTabGroupModelFilter.undoGroupedTab(mTab1, POSITION1, TAB1_ROOT_ID, null); verify(mTabGroupModelFilterObserver) .willMoveTabOutOfGroup(mTab1, /* destinationTabGroupId= */ null); verify(mTabGroupModelFilterObserver).didMoveTabOutOfGroup(mTab1, POSITION1); - verify(mTabGroupModelFilterObserver, never()).didMergeTabToGroup(eq(mTab1)); + verify(mTabGroupModelFilterObserver, never()).didMergeTabToGroup(eq(mTab1), anyBoolean()); assertArrayEquals(mTabs.toArray(), expectedTabModel.toArray()); assertThat(mTab1.getRootId(), equalTo(TAB1_ROOT_ID)); @@ -1908,11 +1936,13 @@ mTabGroupModelFilter.undoGroupedTab(mTab6, POSITION6, TAB5_ROOT_ID, TAB5_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).willMoveTabOutOfGroup(mTab6, TAB5_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).didMoveTabOutOfGroup(mTab6, POSITION2); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab6); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab6, /* isDestinationTab= */ false); mTabGroupModelFilter.undoGroupedTab(mTab5, POSITION5, TAB5_ROOT_ID, TAB5_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).willMoveTabOutOfGroup(mTab5, TAB5_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).didMoveTabOutOfGroup(mTab5, POSITION2); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab5); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab5, /* isDestinationTab= */ false); assertThat(mTab5.getRootId(), equalTo(TAB5_ROOT_ID)); assertThat(mTab6.getRootId(), equalTo(TAB5_ROOT_ID)); @@ -1950,21 +1980,25 @@ mTabGroupModelFilter.undoGroupedTab(mTab6, POSITION6, TAB5_ROOT_ID, TAB5_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).willMoveTabOutOfGroup(mTab6, TAB5_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).didMoveTabOutOfGroup(mTab6, POSITION2); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab6); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab6, /* isDestinationTab= */ false); mTabGroupModelFilter.undoGroupedTab(mTab5, POSITION5, TAB5_ROOT_ID, TAB5_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).willMoveTabOutOfGroup(mTab5, TAB5_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver).didMoveTabOutOfGroup(mTab5, POSITION2); - verify(mTabGroupModelFilterObserver).didMergeTabToGroup(mTab5); + verify(mTabGroupModelFilterObserver) + .didMergeTabToGroup(mTab5, /* isDestinationTab= */ false); mTabGroupModelFilter.undoGroupedTab(mTab3, POSITION3, TAB2_ROOT_ID, TAB2_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver, never()) .willMoveTabOutOfGroup(mTab3, TAB2_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver, never()).didMoveTabOutOfGroup(eq(mTab3), anyInt()); - verify(mTabGroupModelFilterObserver, never()).didMergeTabToGroup(eq(mTab3)); + verify(mTabGroupModelFilterObserver, never()) + .didMergeTabToGroup(mTab3, /* isDestinationTab= */ false); mTabGroupModelFilter.undoGroupedTab(mTab2, POSITION2, TAB2_ROOT_ID, TAB2_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver, never()) .willMoveTabOutOfGroup(mTab2, TAB2_TAB_GROUP_ID); verify(mTabGroupModelFilterObserver, never()).didMoveTabOutOfGroup(eq(mTab2), anyInt()); - verify(mTabGroupModelFilterObserver, never()).didMergeTabToGroup(eq(mTab2)); + verify(mTabGroupModelFilterObserver, never()) + .didMergeTabToGroup(mTab2, /* isDestinationTab= */ false); assertThat(mTab5.getRootId(), equalTo(TAB5_ROOT_ID)); assertThat(mTab6.getRootId(), equalTo(TAB5_ROOT_ID));
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterObserver.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterObserver.java index acdc0f2e..d3e85e7 100644 --- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterObserver.java +++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterObserver.java
@@ -72,12 +72,14 @@ default void willMoveTabOutOfGroup(Tab movedTab, @Nullable Token destinationTabGroupId) {} /** - * This method is called after a tab is moved to form a group or moved into an existed group. + * This method is called after a tab is moved to a group. * - * @param movedTab The {@link Tab} which has been moved. If a group is merged to a tab or - * another group, this is the last tab of the merged group. + * @param movedTab The {@link Tab} which has been moved into the group. + * @param isDestinationTab Whether the tab is the destination tab of a merge operation. The + * destination tab is the tab that all the other tabs in the merge operation will be grouped + * into. */ - default void didMergeTabToGroup(Tab movedTab) {} + default void didMergeTabToGroup(Tab movedTab, boolean isDestinationTab) {} // TODO(crbug.com/434015906): Passing the last tab here is a limitation of the current TabGroupModelFilterImpl, we should fix this once tab collections is launched. /**
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 92979b6..e26c771 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -3822,7 +3822,6 @@ "views/extensions/expandable_container_view.h", "views/extensions/extension_context_menu_controller.cc", "views/extensions/extension_context_menu_controller.h", - "views/extensions/extension_install_blocked_dialog_view.cc", "views/extensions/extension_install_dialog_view.cc", "views/extensions/extension_install_dialog_view.h", "views/extensions/extension_install_friction_dialog_view.cc", @@ -5145,6 +5144,7 @@ if (enable_extensions_core) { sources += [ "extensions/extension_dialog_utils.h", + "extensions/extension_install_blocked_dialog.cc", "extensions/extension_uninstall_dialog_impl.cc", "webui/extensions/extension_icon_source.cc", "webui/extensions/extension_icon_source.h",
diff --git a/chrome/browser/ui/android/extensions/extension_action_popup_contents.cc b/chrome/browser/ui/android/extensions/extension_action_popup_contents.cc index 23a3f9b..2ff9ea8 100644 --- a/chrome/browser/ui/android/extensions/extension_action_popup_contents.cc +++ b/chrome/browser/ui/android/extensions/extension_action_popup_contents.cc
@@ -42,6 +42,12 @@ AttachCurrentThread(), reinterpret_cast<jlong>(this), host_->host_contents()); host_->set_view(this); + // Handle the containing view calling window.close(); + // The base::Unretained() below is safe because this object owns `host_`, so + // the callback will never fire if `this` is deleted. + host_->SetCloseHandler( + base::BindOnce(&ExtensionActionPopupContents::HandleCloseExtensionHost, + base::Unretained(this))); WebContentsObserver::Observe(host_->host_contents()); auto* primary_main_frame = host_->host_contents()->GetPrimaryMainFrame(); if (primary_main_frame->IsRenderFrameLive()) { @@ -117,6 +123,13 @@ render_frame_host->GetView()->EnableAutoResize(kMinSize, kMaxSize); } +void ExtensionActionPopupContents::HandleCloseExtensionHost( + ExtensionHost* host) { + DCHECK_EQ(host, host_.get()); + Java_ExtensionActionPopupContents_onClose(AttachCurrentThread(), + java_object_); +} + // JNI method to create an ExtensionActionPopupContents instance. // This is called from the Java side to initiate the display of an extension // popup.
diff --git a/chrome/browser/ui/android/extensions/extension_action_popup_contents.h b/chrome/browser/ui/android/extensions/extension_action_popup_contents.h index a0342791..eff2fc4d3 100644 --- a/chrome/browser/ui/android/extensions/extension_action_popup_contents.h +++ b/chrome/browser/ui/android/extensions/extension_action_popup_contents.h
@@ -17,6 +17,7 @@ namespace extensions { +class ExtensionHost; class ExtensionViewHost; // ExtensionActionPopupContents is the native C++ class responsible for managing @@ -66,6 +67,7 @@ private: void SetUpNewMainFrame(content::RenderFrameHost* render_frame_host); + void HandleCloseExtensionHost(extensions::ExtensionHost* host); std::unique_ptr<ExtensionViewHost> host_; base::android::ScopedJavaGlobalRef<jobject> java_object_;
diff --git a/chrome/browser/ui/android/extensions/java/src/org/chromium/chrome/browser/ui/extensions/ExtensionActionPopupContents.java b/chrome/browser/ui/android/extensions/java/src/org/chromium/chrome/browser/ui/extensions/ExtensionActionPopupContents.java index 8c77f1f2..8162c88 100644 --- a/chrome/browser/ui/android/extensions/java/src/org/chromium/chrome/browser/ui/extensions/ExtensionActionPopupContents.java +++ b/chrome/browser/ui/android/extensions/java/src/org/chromium/chrome/browser/ui/extensions/ExtensionActionPopupContents.java
@@ -108,15 +108,29 @@ } } + @CalledByNative + private void onClose() { + if (mDelegate != null) { + mDelegate.onClose(); + } + } + /** * Interface for receiving UI-related callbacks from an {@link ExtensionActionPopupContents}. * * <p>This allows embedders or UI coordinators to react to events like content resizing. */ public interface Delegate { + /** Called when the renderer requested to resize the window to fit the content size. */ void resizeDueToAutoResize(int width, int height); + /** Called when it finished loading the initial page. */ void onLoaded(); + + /** + * Called when the popup is requested to close programmatically (e.g. by window.close()). + */ + void onClose(); } @NativeMethods
diff --git a/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml b/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml index 0c97a23..fe0e894 100644 --- a/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml +++ b/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml
@@ -8,12 +8,24 @@ <!-- Toolbar dimensions --> <dimen name="toolbar_height_no_shadow">@dimen/default_action_bar_height</dimen> <dimen name="toolbar_url_focus_height_increase">8dp</dimen> - <dimen name="toolbar_button_width">48dp</dimen> - <dimen name="toolbar_button_height">56dp</dimen> - <dimen name="toolbar_button_width_desktop">24dp</dimen> - <dimen name="toolbar_button_height_desktop">24dp</dimen> <dimen name="toolbar_identity_disc_size">24dp</dimen> <dimen name="toolbar_url_focus_translation_x">10dp</dimen> + <dimen name="toolbar_button_width">48dp</dimen> + <dimen name="toolbar_button_height">56dp</dimen> + <dimen name="toolbar_button_margin_horizontal">0dp</dimen> + <dimen name="toolbar_button_margin_vertical">0dp</dimen> + <dimen name="toolbar_button_margin_horizontal_desktop">4dp</dimen> + <dimen name="toolbar_button_margin_vertical_desktop">8dp</dimen> + + <!-- Desktop toolbar button dimensions should align with the hover background size --> + <dimen name="toolbar_button_width_desktop">@dimen/large_icon_background_size</dimen> + <dimen name="toolbar_button_height_desktop">@dimen/large_icon_background_size</dimen> + + <!-- Close button dimensions --> + <dimen name="close_button_width">48dp</dimen> + <dimen name="close_button_height">48dp</dimen> + <dimen name="close_button_width_desktop">24dp</dimen> + <dimen name="close_button_height_desktop">24dp</dimen> <!-- URL bar --> <dimen name="url_bar_vertical_padding">@dimen/location_bar_vertical_margin</dimen>
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/tabgroup/TabGroupSuggestionProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/tabgroup/TabGroupSuggestionProcessor.java index 836f22f..a4ef658 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/tabgroup/TabGroupSuggestionProcessor.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/tabgroup/TabGroupSuggestionProcessor.java
@@ -29,6 +29,7 @@ import org.chromium.components.omnibox.OmniboxSuggestionType; import org.chromium.components.omnibox.suggestions.OmniboxSuggestionUiType; import org.chromium.components.tab_group_sync.SavedTabGroup; +import org.chromium.components.tab_groups.TabGroupColorId; import org.chromium.components.tab_groups.TabGroupColorPickerUtils; import org.chromium.ui.modelutil.PropertyModel; @@ -94,12 +95,13 @@ mContext.getResources() .getDimensionPixelSize( R.dimen.hub_search_tab_group_image_span_edge_size); + int plainColorId = Integer.parseInt(assumeNonNull(suggestion.getImageDominantColor())); + @TabGroupColorId + int colorId = TabGroupColorPickerUtils.getTabGroupCardColorId(plainColorId); @ColorInt int color = TabGroupColorPickerUtils.getTabGroupColorPickerItemColor( - mContext, - Integer.parseInt(assumeNonNull(suggestion.getImageDominantColor())), - /* isIncognito= */ false); + mContext, colorId, /* isIncognito= */ false); ShapeDrawable inlineColorIcon = new ShapeDrawable(new OvalShape()); inlineColorIcon.setBounds( /* left= */ 0,
diff --git a/chrome/browser/ui/android/pdf/BUILD.gn b/chrome/browser/ui/android/pdf/BUILD.gn index 89ccef8..7a63ccd 100644 --- a/chrome/browser/ui/android/pdf/BUILD.gn +++ b/chrome/browser/ui/android/pdf/BUILD.gn
@@ -46,6 +46,7 @@ "//third_party/androidx:androidx_core_core_java", "//third_party/androidx:androidx_fragment_fragment_java", "//third_party/androidx:androidx_pdf_pdf_viewer_fragment_java", + "//third_party/androidx:pdf_cherry_pick_crbug_394147799_java", "//third_party/jni_zero:jni_zero_java", "//ui/android:ui_java", "//url:url_java",
diff --git a/chrome/browser/ui/android/toolbar/java/res/layout/menu_button.xml b/chrome/browser/ui/android/toolbar/java/res/layout/menu_button.xml index 4c63901..f1594a1 100644 --- a/chrome/browser/ui/android/toolbar/java/res/layout/menu_button.xml +++ b/chrome/browser/ui/android/toolbar/java/res/layout/menu_button.xml
@@ -16,7 +16,7 @@ <org.chromium.ui.widget.ChromeImageButton android:id="@+id/menu_button" - style="@style/ToolbarMenuButton" + style="@style/ToolbarHoverableMenuButton.AdaptiveDensity" android:src="@drawable/ic_more_vert_24dp" android:contentDescription="@string/accessibility_toolbar_btn_menu" android:tooltipText="@string/accessibility_toolbar_btn_menu" @@ -25,7 +25,7 @@ <ImageView android:id="@+id/menu_badge" - style="@style/ToolbarMenuButton" + style="@style/ToolbarHoverableMenuButton.AdaptiveDensity" android:background="@drawable/toolbar_menu_button_ripple" android:src="@drawable/badge_update_dark" tools:ignore="ContentDescription"
diff --git a/chrome/browser/ui/android/toolbar/java/res/layout/toolbar_tablet.xml b/chrome/browser/ui/android/toolbar/java/res/layout/toolbar_tablet.xml index 30d3a14..27dc461 100644 --- a/chrome/browser/ui/android/toolbar/java/res/layout/toolbar_tablet.xml +++ b/chrome/browser/ui/android/toolbar/java/res/layout/toolbar_tablet.xml
@@ -27,7 +27,7 @@ <org.chromium.chrome.browser.toolbar.home_button.HomeButton android:id="@+id/home_button" - style="@style/ToolbarHoverableButton" + style="@style/ToolbarHoverableButton.AdaptiveDensity" android:src="@drawable/btn_toolbar_home" android:contentDescription="@string/accessibility_toolbar_btn_home" android:tooltipText="@string/accessibility_toolbar_btn_home" @@ -39,7 +39,7 @@ <org.chromium.ui.widget.ChromeImageButton android:id="@+id/back_button" - style="@style/ToolbarHoverableButton" + style="@style/ToolbarHoverableButton.AdaptiveDensity" android:src="@drawable/btn_back" android:contentDescription="@string/accessibility_toolbar_btn_back" android:tooltipText="@string/accessibility_toolbar_btn_back" @@ -47,7 +47,7 @@ <org.chromium.ui.widget.ChromeImageButton android:id="@+id/forward_button" - style="@style/ToolbarHoverableButton" + style="@style/ToolbarHoverableButton.AdaptiveDensity" android:src="@drawable/btn_forward" android:contentDescription="@string/accessibility_toolbar_btn_forward" android:tooltipText="@string/accessibility_toolbar_btn_forward" @@ -60,7 +60,7 @@ android:contentDescription="@string/accessibility_btn_refresh" android:tooltipText="@string/accessibility_btn_refresh" app:tint="@color/default_icon_color_tint_list" - style="@style/ToolbarHoverableButton" /> + style="@style/ToolbarHoverableButton.AdaptiveDensity" /> <org.chromium.chrome.browser.omnibox.LocationBarTablet android:id="@+id/location_bar" @@ -72,11 +72,12 @@ android:paddingEnd="@dimen/location_bar_end_padding" android:clipToPadding="false" /> + <!-- TODO(crbug.com/433976037): Reset horizontal margins when optional button expands to action chip. --> <ViewStub android:id="@+id/optional_button_stub" android:inflatedId="@+id/optional_toolbar_button" android:layout="@layout/optional_toolbar_button" - style="@style/ToolbarButton" + style="@style/ToolbarHoverableButton.AdaptiveDensity" android:paddingStart="8dp" android:visibility="gone" /> @@ -98,8 +99,7 @@ <org.chromium.chrome.browser.toolbar.top.ToggleTabStackButton android:id="@+id/tab_switcher_button" - style="@style/ToolbarHoverableButton" - android:layout_gravity="top" + style="@style/ToolbarHoverableButton.AdaptiveDensity" android:visibility="visible" app:menuMaxWidth="@dimen/tab_switcher_menu_width" app:menuVerticalOverlapAnchor="false" />
diff --git a/chrome/browser/ui/android/toolbar/java/res/values/styles.xml b/chrome/browser/ui/android/toolbar/java/res/values/styles.xml index 69f72db84..48b9747 100644 --- a/chrome/browser/ui/android/toolbar/java/res/values/styles.xml +++ b/chrome/browser/ui/android/toolbar/java/res/values/styles.xml
@@ -31,7 +31,19 @@ <style name="ToolbarHoverableButton" parent="ToolbarButton"> <item name="android:background">@drawable/default_icon_background</item> </style> - <style name="ToolbarMenuButton" parent="ToolbarButton"> + <!-- This desktop style adjusts dimensions based on is_desktop: buttons are smaller on desktop and include margins for maintaining the same visual spacing. --> + <style name="ToolbarHoverableButton.AdaptiveDensity" parent="ToolbarHoverableButton"> + <item name="android:layout_width">?attr/toolbarButtonWidth</item> + <item name="android:layout_height">?attr/toolbarButtonHeight</item> + <item name="android:layout_marginVertical">?attr/toolbarButtonMarginVertical</item> + <item name="android:layout_marginHorizontal">?attr/toolbarButtonMarginHorizontal</item> + </style> + <style name="ToolbarHoverableMenuButton.AdaptiveDensity" parent="ToolbarHoverableButton.AdaptiveDensity"> + <item name="android:layout_gravity">top</item> + <item name="android:paddingEnd">@dimen/button_end_padding</item> + <item name="android:background">@drawable/toolbar_menu_button_ripple</item> + </style> + <style name="ToolbarMenuButton" parent="ToolbarHoverableButton"> <item name="android:layout_gravity">top</item> <item name="android:paddingEnd">@dimen/button_end_padding</item> <item name="android:background">@drawable/toolbar_menu_button_ripple</item>
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionPopup.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionPopup.java index 2b14928..2127b66 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionPopup.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionActionPopup.java
@@ -157,5 +157,10 @@ public void onLoaded() { mPopupWindow.show(); } + + @Override + public void onClose() { + mPopupWindow.dismiss(); + } } }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonTest.java index 5a06085..a4b0f127 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonTest.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonTest.java
@@ -37,6 +37,7 @@ import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.chrome.browser.toolbar.R; import org.chromium.chrome.browser.ui.theme.BrandedColorScheme; +import org.chromium.ui.base.TestActivity; /** Unit tests for MenuButton. */ @RunWith(BaseRobolectricTestRunner.class) @@ -51,8 +52,7 @@ @Before public void setUp() { - mActivity = Robolectric.buildActivity(Activity.class).setup().get(); - mActivity.setTheme(R.style.Theme_MaterialComponents); + mActivity = Robolectric.buildActivity(TestActivity.class).setup().get(); mMenuButton = (MenuButton) ((ViewGroup)
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java index e99cc02..2f4836f 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
@@ -8,8 +8,8 @@ import static org.chromium.ui.accessibility.KeyboardFocusUtil.setFocusOnFirstFocusableDescendant; import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.ColorStateList; @@ -63,6 +63,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.List; /** The Toolbar object for Tablet screens. */ @SuppressLint("Instantiatable") @@ -615,73 +616,27 @@ private void runToolbarButtonsVisibilityAnimation(boolean visible) { if (mButtonVisibilityAnimators != null) mButtonVisibilityAnimators.cancel(); - mButtonVisibilityAnimators = - visible ? buildShowToolbarButtonsAnimation() : buildHideToolbarButtonsAnimation(); - mButtonVisibilityAnimators.start(); - } - - private AnimatorSet buildShowToolbarButtonsAnimation() { Collection<Animator> animators = new ArrayList<>(); + animators.add(createLocationBarButtonAnimator(visible)); + animators.add(mReloadButtonCoordinator.getFadeAnimator(visible)); + animators.add(mBackButtonCoordinator.getFadeAnimator(visible)); + animators.addAll(createLocationBarButtonsWhenUnfocusedAnimators(visible)); - animators.add(mLocationBar.createShowButtonAnimatorForTablet(mForwardButton)); - animators.add(mReloadButtonCoordinator.getFadeAnimator(/* shouldShow= */ true)); - animators.add(mBackButtonCoordinator.getFadeAnimator(/* shouldShow= */ true)); - - // Add animators for location bar. - animators.addAll( - mLocationBar.getShowButtonsWhenUnfocusedAnimatorsForTablet( - getStartPaddingDifferenceForButtonVisibilityAnimation())); - - AnimatorSet set = new AnimatorSet(); - set.playTogether(animators); - - set.addListener( - new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - keepControlsShownForAnimation(); - mForwardButton.setVisibility(View.VISIBLE); - mReloadButtonCoordinator.setVisibility(true); - mBackButtonCoordinator.setVisibility(true); - - // Set the padding at the start of the animation so the toolbar buttons - // don't jump when the animation ends. - setStartPaddingBasedOnButtonVisibility(true); - setIncognitoIndicatorVisibility(); - } - - @Override - public void onAnimationEnd(Animator animation) { - mButtonVisibilityAnimators = null; - allowBrowserControlsHide(); - } - }); - - return set; - } - - private AnimatorSet buildHideToolbarButtonsAnimation() { - Collection<Animator> animators = new ArrayList<>(); - - animators.add(mLocationBar.createHideButtonAnimatorForTablet(mForwardButton)); - - animators.add(mReloadButtonCoordinator.getFadeAnimator(/* shouldShow= */ false)); - animators.add(mBackButtonCoordinator.getFadeAnimator(/* shouldShow= */ false)); - - // Add animators for location bar. - animators.addAll( - mLocationBar.getHideButtonsWhenUnfocusedAnimatorsForTablet( - getStartPaddingDifferenceForButtonVisibilityAnimation())); - - AnimatorSet set = new AnimatorSet(); - set.playTogether(animators); - - set.addListener( + mButtonVisibilityAnimators = new AnimatorSet(); + mButtonVisibilityAnimators.playTogether(animators); + mButtonVisibilityAnimators.addListener( new CancelAwareAnimatorListener() { @Override public void onStart(Animator animator) { keepControlsShownForAnimation(); - + if (visible) { + mForwardButton.setVisibility(View.VISIBLE); + mReloadButtonCoordinator.setVisibility(true); + mBackButtonCoordinator.setVisibility(true); + // Set the padding at the start of the show animation so the toolbar + // buttons don't jump when the animation ends. + setStartPaddingBasedOnButtonVisibility(true); + } setIncognitoIndicatorVisibility(); } @@ -693,20 +648,33 @@ @Override public void onEnd(Animator animator) { - mForwardButton.setVisibility(View.GONE); - mReloadButtonCoordinator.setVisibility(false); - mBackButtonCoordinator.setVisibility(false); - - // Set the padding at the end of the animation so the toolbar buttons - // don't jump when the animation starts. - setStartPaddingBasedOnButtonVisibility(false); - + if (!visible) { + mForwardButton.setVisibility(View.GONE); + mReloadButtonCoordinator.setVisibility(false); + mBackButtonCoordinator.setVisibility(false); + // Set the padding at the end of the hide animation so the toolbar + // buttons don't jump when the animation starts. + setStartPaddingBasedOnButtonVisibility(false); + } mButtonVisibilityAnimators = null; allowBrowserControlsHide(); } }); + mButtonVisibilityAnimators.start(); + } - return set; + private ObjectAnimator createLocationBarButtonAnimator(boolean shouldShow) { + return shouldShow + ? mLocationBar.createShowButtonAnimatorForTablet(mForwardButton) + : mLocationBar.createHideButtonAnimatorForTablet(mForwardButton); + } + + private List<Animator> createLocationBarButtonsWhenUnfocusedAnimators(boolean shouldShow) { + int startPaddingDifference = getStartPaddingDifferenceForButtonVisibilityAnimation(); + return shouldShow + ? mLocationBar.getShowButtonsWhenUnfocusedAnimatorsForTablet(startPaddingDifference) + : mLocationBar.getHideButtonsWhenUnfocusedAnimatorsForTablet( + startPaddingDifference); } private int getDimensionPixelSize(@DimenRes int dimenId) {
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarSceneLayer.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarSceneLayer.java index cb9cfb1..25c1d07 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarSceneLayer.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarSceneLayer.java
@@ -77,10 +77,6 @@ progressInfo.progressBarStaticBackgroundRect.left, progressInfo.progressBarStaticBackgroundRect.width(), progressInfo.progressBarStaticBackgroundColor, - progressInfo.progressBarEndIndicator.left, - progressInfo.progressBarEndIndicator.top, - progressInfo.progressBarEndIndicator.width(), - progressInfo.progressBarEndIndicator.height(), progressInfo.cornerRadius, progressInfo.progressBarVisualUpdateAvailable); } @@ -139,10 +135,6 @@ int progressBarStaticBackgroundX, int progressBarStaticBackgroundWidth, int progressBarStaticBackgroundColor, - int progressBarEndIndicatorX, - int progressBarEndIndicatorY, - int progressBarEndIndicatorWidth, - int progressBarEndIndicatorHeight, float cornerRadius, boolean progressBarVisualUpdateAvailable); }
diff --git a/chrome/browser/ui/ash/projector/screencast_manager_browsertest.cc b/chrome/browser/ui/ash/projector/screencast_manager_browsertest.cc index f2ecd709..b8560f4 100644 --- a/chrome/browser/ui/ash/projector/screencast_manager_browsertest.cc +++ b/chrome/browser/ui/ash/projector/screencast_manager_browsertest.cc
@@ -58,7 +58,7 @@ void VerifyResponse(const content::EvalJsResult& result) { EXPECT_TRUE(result.is_ok()); - const base::Value::Dict dict = result.ExtractDict(); + const base::Value::Dict& dict = result.ExtractDict(); const std::string* file_id = dict.FindString("fileId"); ASSERT_TRUE(file_id); EXPECT_EQ(*file_id, kVideoFileId);
diff --git a/chrome/browser/ui/autofill/autofill_bubble_controller_base.h b/chrome/browser/ui/autofill/autofill_bubble_controller_base.h index 47c9700..ebe949544 100644 --- a/chrome/browser/ui/autofill/autofill_bubble_controller_base.h +++ b/chrome/browser/ui/autofill/autofill_bubble_controller_base.h
@@ -46,9 +46,9 @@ // potentially log metrics. virtual void DoShowBubble() = 0; - void Show(); + virtual void UpdatePageActionIcon(); - void UpdatePageActionIcon(); + void Show(); AutofillBubbleBase* bubble_view() const { return bubble_view_; } void set_bubble_view(AutofillBubbleBase* bubble_view) {
diff --git a/chrome/browser/ui/autofill/payments/BUILD.gn b/chrome/browser/ui/autofill/payments/BUILD.gn index 02ef6d8..5f92ad6 100644 --- a/chrome/browser/ui/autofill/payments/BUILD.gn +++ b/chrome/browser/ui/autofill/payments/BUILD.gn
@@ -23,6 +23,7 @@ public_deps = [ "//base", + "//chrome/browser/ui/tabs:tabs_public", "//components/autofill/core/browser", "//content/public/browser", "//ui/base", @@ -146,6 +147,7 @@ "//chrome/browser/ui/promos:utils", "//chrome/browser/ui/tabs:tab_strip", "//chrome/browser/ui/views/page_action", + "//chrome/browser/ui/views/page_action", "//chrome/common", "//components/autofill/core/common:credit_card_number_validation", "//components/commerce/core:feature_list",
diff --git a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc index 14e7339..4b507b5b 100644 --- a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc +++ b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc
@@ -738,10 +738,18 @@ VirtualCardEnrollmentManager* ChromePaymentsAutofillClient::GetVirtualCardEnrollmentManager() { if (!virtual_card_enrollment_manager_) { + PaymentsNetworkInterfaceVariation payments_network_interface; + if (base::FeatureList::IsEnabled( + features:: + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment)) { + payments_network_interface = GetMultipleRequestPaymentsNetworkInterface(); + } else { + payments_network_interface = GetPaymentsNetworkInterface(); + } virtual_card_enrollment_manager_ = std::make_unique<VirtualCardEnrollmentManager>( &client_->GetPersonalDataManager().payments_data_manager(), - GetPaymentsNetworkInterface(), &client_.get()); + payments_network_interface, &client_.get()); } return virtual_card_enrollment_manager_.get();
diff --git a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc index cf0b108..72a97ee 100644 --- a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc +++ b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc
@@ -18,9 +18,13 @@ #include "base/android/jni_android.h" #include "chrome/browser/mandatory_reauth/android/internal/jni/MandatoryReauthOptInBottomSheetControllerBridge_jni.h" #else +#include "chrome/browser/ui/actions/chrome_action_id.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" +#include "chrome/browser/ui/views/page_action/page_action_controller.h" +#include "components/tabs/public/tab_interface.h" #endif // BUILDFLAG(IS_ANDROID) namespace autofill { @@ -247,6 +251,36 @@ } #endif +void MandatoryReauthBubbleControllerImpl::UpdatePageActionIcon() { +// Page action icons do not exist for Android. +#if !BUILDFLAG(IS_ANDROID) + if (!IsPageActionMigrated(PageActionIconType::kMandatoryReauth)) { + AutofillBubbleControllerBase::UpdatePageActionIcon(); + } + + tabs::TabInterface* const tab_interface = + tabs::TabInterface::MaybeGetFromContents(web_contents()); + + if (!tab_interface) { + return; + } + + // NOTE: Consider creating a separate page action view controller file when + // the logic to show the page action become complex. + page_actions::PageActionController* page_action_controller = + tab_interface->GetTabFeatures()->page_action_controller(); + if (!page_action_controller) { + return; + } + + if (IsIconVisible()) { + page_action_controller->Show(kActionAutofillMandatoryReauth); + } else { + page_action_controller->Hide(kActionAutofillMandatoryReauth); + } +#endif // !BUILDFLAG(IS_ANDROID) +} + WEB_CONTENTS_USER_DATA_KEY_IMPL(MandatoryReauthBubbleControllerImpl); } // namespace autofill
diff --git a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.h b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.h index 8d8431f..663ea95 100644 --- a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.h +++ b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.h
@@ -55,6 +55,7 @@ // AutofillBubbleControllerBase: PageActionIconType GetPageActionIconType() override; void DoShowBubble() override; + void UpdatePageActionIcon() override; private: friend class content::WebContentsUserData<
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index e0410ab..926097dad 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc
@@ -1297,7 +1297,7 @@ void Browser::SetLockedForOnTask(bool locked) { on_task_locked_ = locked; - OnLockedForOnTaskUpdated(); + GetBrowserView().OnLockedForOnTaskUpdated(); } #endif @@ -3238,15 +3238,6 @@ } } -#if BUILDFLAG(IS_CHROMEOS) -void Browser::OnLockedForOnTaskUpdated() { - bool is_locked = IsLockedForOnTask(); - BrowserView* const browser_view = static_cast<BrowserView*>(window()); - browser_view->SetCanMinimize(!is_locked); - browser_view->SetShowCloseButton(!is_locked); -} -#endif - /////////////////////////////////////////////////////////////////////////////// // Browser, UI update coalescing and handling (private):
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h index c5665ec2..eb82055f 100644 --- a/chrome/browser/ui/browser.h +++ b/chrome/browser/ui/browser.h
@@ -1090,11 +1090,6 @@ // Handle changes to kDevToolsAvailability preference. void OnDevToolsAvailabilityChanged(); -#if BUILDFLAG(IS_CHROMEOS) - // Handle `on_task_locked_` state changes. - void OnLockedForOnTaskUpdated(); -#endif - // UI update coalescing and handling //////////////////////////////////////// // Asks the toolbar (and as such the location bar) to update its state to
diff --git a/chrome/browser/ui/browser_actions.cc b/chrome/browser/ui/browser_actions.cc index 9c3079b..bbae2b6 100644 --- a/chrome/browser/ui/browser_actions.cc +++ b/chrome/browser/ui/browser_actions.cc
@@ -435,6 +435,20 @@ omnibox::kProductSpecificationsAddIcon)) .Build()); + // Clicking the Mandatory Reauth page action is a no-op. This is because the + // icon is always shown with a dialog bubble. The expected behavior is to + // simply close this bubble, which happens automatically due to focus change + // when the user clicks the icon. Therefore, a `base::DoNothing()` callback is + // used. + root_action_item_->AddChild( + actions::ActionItem::Builder(base::DoNothing()) + .SetActionId(kActionAutofillMandatoryReauth) + .SetTooltipText(l10n_util::GetStringUTF16( + IDS_AUTOFILL_MANDATORY_REAUTH_ICON_TOOLTIP)) + .SetImage( + ui::ImageModel::FromVectorIcon(kCreditCardChromeRefreshIcon)) + .Build()); + //------- Chrome Menu Actions --------// root_action_item_->AddChild( ChromeMenuAction(base::BindRepeating(
diff --git a/chrome/browser/ui/browser_window/internal/browser_window_features.cc b/chrome/browser/ui/browser_window/internal/browser_window_features.cc index 2da5143..5bb2135 100644 --- a/chrome/browser/ui/browser_window/internal/browser_window_features.cc +++ b/chrome/browser/ui/browser_window/internal/browser_window_features.cc
@@ -503,9 +503,11 @@ } if (features::kGlicActorUiOverlay.Get()) { + // TODO(crbug.com/433999185): Handle split view. actor_overlay_window_controller_ = std::make_unique<actor::ui::ActorOverlayWindowController>( - browser_view->GetActorOverlayView()); + browser_view->GetActiveContentsContainerView() + ->GetActorOverlayView()); } }
diff --git a/chrome/browser/ui/browser_window_state.h b/chrome/browser/ui/browser_window_state.h index 0d5f3bf..ec08bd2 100644 --- a/chrome/browser/ui/browser_window_state.h +++ b/chrome/browser/ui/browser_window_state.h
@@ -17,7 +17,6 @@ namespace base { class CommandLine; -class Value; } // namespace base namespace gfx {
diff --git a/chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac_remote_cocoa.mm b/chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac_remote_cocoa.mm index 9f6c5fd..208dc72 100644 --- a/chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac_remote_cocoa.mm +++ b/chrome/browser/ui/cocoa/renderer_context_menu/render_view_context_menu_mac_remote_cocoa.mm
@@ -7,6 +7,7 @@ #include "chrome/browser/headless/headless_mode_util.h" #include "components/remote_cocoa/common/menu.mojom.h" #include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/web_contents.h" #include "ui/views/widget/widget.h" RenderViewContextMenuMacRemoteCocoa::RenderViewContextMenuMacRemoteCocoa(
diff --git a/chrome/browser/ui/views/extensions/extension_install_blocked_dialog_view.cc b/chrome/browser/ui/extensions/extension_install_blocked_dialog.cc similarity index 100% rename from chrome/browser/ui/views/extensions/extension_install_blocked_dialog_view.cc rename to chrome/browser/ui/extensions/extension_install_blocked_dialog.cc
diff --git a/chrome/browser/ui/views/extensions/extension_install_blocked_dialog_view_browsertest.cc b/chrome/browser/ui/extensions/extension_install_blocked_dialog_browsertest.cc similarity index 79% rename from chrome/browser/ui/views/extensions/extension_install_blocked_dialog_view_browsertest.cc rename to chrome/browser/ui/extensions/extension_install_blocked_dialog_browsertest.cc index 60ab4bcc7..817652c6 100644 --- a/chrome/browser/ui/views/extensions/extension_install_blocked_dialog_view_browsertest.cc +++ b/chrome/browser/ui/extensions/extension_install_blocked_dialog_browsertest.cc
@@ -13,10 +13,10 @@ #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test.h" -class ExtensionInstallBlockedDialogViewTest : public DialogBrowserTest { +class ExtensionInstallBlockedDialogTest : public DialogBrowserTest { public: - ExtensionInstallBlockedDialogViewTest() = default; - ~ExtensionInstallBlockedDialogViewTest() override = default; + ExtensionInstallBlockedDialogTest() = default; + ~ExtensionInstallBlockedDialogTest() override = default; void ShowUi(const std::string& name) override { extensions::ShowExtensionInstallBlockedDialog( @@ -38,18 +38,18 @@ std::u16string message_; }; -IN_PROC_BROWSER_TEST_F(ExtensionInstallBlockedDialogViewTest, +IN_PROC_BROWSER_TEST_F(ExtensionInstallBlockedDialogTest, InvokeUi_WithoutCustomMessage) { ShowAndVerifyUi(); } -IN_PROC_BROWSER_TEST_F(ExtensionInstallBlockedDialogViewTest, +IN_PROC_BROWSER_TEST_F(ExtensionInstallBlockedDialogTest, InvokeUi_WithCustomMessage) { set_message(u"message"); ShowAndVerifyUi(); } -IN_PROC_BROWSER_TEST_F(ExtensionInstallBlockedDialogViewTest, +IN_PROC_BROWSER_TEST_F(ExtensionInstallBlockedDialogTest, InvokeUi_WithLongCustomMessage) { set_message(u"long\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nmessage"); ShowAndVerifyUi();
diff --git a/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.cc b/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.cc index 92e92bc..72791bd 100644 --- a/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.cc +++ b/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.cc
@@ -66,6 +66,10 @@ screenshot_timer_.Stop(); } +bool LensOverlayBlurLayerDelegate::IsCapturingBackgroundImageForTesting() { + return screenshot_timer_.IsRunning(); +} + void LensOverlayBlurLayerDelegate::OnPaintLayer( const ui::PaintContext& context) { if (background_screenshot_.drawsNothing()) {
diff --git a/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.h b/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.h index b571fbc2..f38084a4 100644 --- a/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.h +++ b/chrome/browser/ui/lens/lens_overlay_blur_layer_delegate.h
@@ -46,6 +46,8 @@ // new layer size. void StopBackgroundImageCapture(); + bool IsCapturingBackgroundImageForTesting(); + private: // ui::LayerDelegate: void OnPaintLayer(const ui::PaintContext& context) override;
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.cc b/chrome/browser/ui/lens/lens_overlay_controller.cc index 7053cf0..e6590b5 100644 --- a/chrome/browser/ui/lens/lens_overlay_controller.cc +++ b/chrome/browser/ui/lens/lens_overlay_controller.cc
@@ -397,6 +397,11 @@ return invocation_time_since_epoch_.InMillisecondsSinceUnixEpoch(); } +lens::LensOverlayBlurLayerDelegate* +LensOverlayController::GetLensOverlayBlurLayerDelegateForTesting() { + return lens_overlay_blur_layer_delegate_.get(); +} + views::View* LensOverlayController::GetOverlayViewForTesting() { return overlay_view_.get(); }
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.h b/chrome/browser/ui/lens/lens_overlay_controller.h index 29f38c1..e758318 100644 --- a/chrome/browser/ui/lens/lens_overlay_controller.h +++ b/chrome/browser/ui/lens/lens_overlay_controller.h
@@ -241,6 +241,10 @@ // logging. uint64_t GetInvocationTimeSinceEpoch(); + // Testing helper method for checking the blur layer delegate. + lens::LensOverlayBlurLayerDelegate* + GetLensOverlayBlurLayerDelegateForTesting(); + // Testing helper method for checking view housing our overlay. views::View* GetOverlayViewForTesting(); @@ -718,10 +722,6 @@ // the page bytes can't be uploaded. void SuppressGhostLoader(); - // Enables/disables the background blur updating live. This should be used to - // save resources on blurring the background when not needed. - void SetLiveBlur(bool enabled); - // Called when the UI needs to show the overlay via a view that is a child of // the tab contents view. void ShowOverlay(); @@ -830,6 +830,7 @@ void ActivityRequestedByOverlay( ui::mojom::ClickModifiersPtr click_modifiers) override; void AddBackgroundBlur() override; + void SetLiveBlur(bool enabled) override; void ClosePreselectionBubble() override; void CloseRequestedByOverlayCloseButton() override; void CloseRequestedByOverlayBackgroundClick() override;
diff --git a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc index 20c9fcdb..fe29b7a 100644 --- a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc +++ b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
@@ -77,6 +77,7 @@ #include "chrome/browser/ui/lens/test_lens_search_controller.h" #include "chrome/browser/ui/location_bar/location_bar.h" #include "chrome/browser/ui/tabs/public/tab_features.h" +#include "chrome/browser/ui/tabs/split_tab_metrics.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/views/frame/browser_view.h" @@ -8756,3 +8757,65 @@ EXPECT_TRUE(kTestOverlayObject->Equals(*object)); EXPECT_EQ(kTestText->content_language, text->content_language); } + +class LensOverlayControllerSideBySideBrowserTest + : public LensOverlayControllerBrowserTest { + protected: + void SetupFeatureList() override { + feature_list_.InitWithFeaturesAndParameters( + {{lens::features::kLensOverlay, {{"use-blur", "true"}}}, + {features::kSideBySide, {}}}, + {}); + } +}; + +IN_PROC_BROWSER_TEST_F(LensOverlayControllerSideBySideBrowserTest, + BackgroundBlurNotLiveInitially) { + WaitForPaint(); + + // State should start in off. + auto* controller = GetLensOverlayController(); + ASSERT_EQ(controller->state(), State::kOff); + + // Showing UI should change the state to screenshot and eventually to overlay. + OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); + ASSERT_EQ(controller->state(), State::kScreenshot); + ASSERT_TRUE(base::test::RunUntil( + [&]() { return controller->state() == State::kOverlay; })); + + // Wait until AddBackgroundBlur is called. + ASSERT_TRUE(base::test::RunUntil( + [&]() { return controller->GetOverlayWebViewForTesting()->layer(); })); + + // In a normal tab, the screenshot is not resized initially, so background + // image capturing should not have been started. + EXPECT_FALSE(controller->GetLensOverlayBlurLayerDelegateForTesting() + ->IsCapturingBackgroundImageForTesting()); +} + +IN_PROC_BROWSER_TEST_F(LensOverlayControllerSideBySideBrowserTest, + BackgroundBlurLiveInitiallyInSplitTab) { + chrome::NewSplitTab(browser(), + split_tabs::SplitTabCreatedSource::kToolbarButton); + + WaitForPaint(); + + // State should start in off. + auto* controller = GetLensOverlayController(); + ASSERT_EQ(controller->state(), State::kOff); + + // Showing UI should change the state to screenshot and eventually to overlay. + OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); + ASSERT_EQ(controller->state(), State::kScreenshot); + ASSERT_TRUE(base::test::RunUntil( + [&]() { return controller->state() == State::kOverlay; })); + + // Wait until AddBackgroundBlur is called. + ASSERT_TRUE(base::test::RunUntil( + [&]() { return controller->GetOverlayWebViewForTesting()->layer(); })); + + // In a split tab, the screenshot is initially resized, so background image + // capturing should have been started. + EXPECT_TRUE(controller->GetLensOverlayBlurLayerDelegateForTesting() + ->IsCapturingBackgroundImageForTesting()); +}
diff --git a/chrome/browser/ui/page_action/page_action_icon_type.cc b/chrome/browser/ui/page_action/page_action_icon_type.cc index ad415721..6bac7fb 100644 --- a/chrome/browser/ui/page_action/page_action_icon_type.cc +++ b/chrome/browser/ui/page_action/page_action_icon_type.cc
@@ -46,6 +46,8 @@ return &features::kPageActionsMigrationCollaborationMessaging; case PageActionIconType::kPriceTracking: return &features::kPageActionsMigrationPriceTracking; + case PageActionIconType::kMandatoryReauth: + return &features::kPageActionsMigrationAutofillMandatoryReauth; default: return nullptr; }
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc index 8389d2f..15118e5 100644 --- a/chrome/browser/ui/ui_features.cc +++ b/chrome/browser/ui/ui_features.cc
@@ -532,6 +532,12 @@ "price_tracking", false); +BASE_FEATURE_PARAM(bool, + kPageActionsMigrationAutofillMandatoryReauth, + &kPageActionsMigration, + "mandatory_reauth", + false); + BASE_FEATURE(kSavePasswordsContextualUi, "SavePasswordsContextualUi", base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h index 9248ee5c..d1d8069 100644 --- a/chrome/browser/ui/ui_features.h +++ b/chrome/browser/ui/ui_features.h
@@ -335,6 +335,7 @@ BASE_DECLARE_FEATURE_PARAM(bool, kPageActionsMigrationFind); BASE_DECLARE_FEATURE_PARAM(bool, kPageActionsMigrationCollaborationMessaging); BASE_DECLARE_FEATURE_PARAM(bool, kPageActionsMigrationPriceTracking); +BASE_DECLARE_FEATURE_PARAM(bool, kPageActionsMigrationAutofillMandatoryReauth); // Determines whether the "save password" page action displays different UI if // the user has said to never save passwords for that site.
diff --git a/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc b/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc index 41fc194..2fc9b73 100644 --- a/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc +++ b/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc
@@ -301,14 +301,11 @@ MandatoryReauthBubbleController* controller, bool is_user_gesture, MandatoryReauthBubbleType bubble_type) { - PageActionIconView* icon_view = - toolbar_button_provider_->GetPageActionIconView( - PageActionIconType::kMandatoryReauth); - DCHECK(icon_view); - // TODO(crbug.com/376283953): An action ID should be created and used here - // when Mandatory Reauth is migrated to the new page actions framework. + IconLabelBubbleView* icon_view = toolbar_button_provider_->GetPageActionView( + kActionAutofillMandatoryReauth); + views::View* anchor_view = - toolbar_button_provider_->GetAnchorView(std::nullopt); + toolbar_button_provider_->GetAnchorView(kActionAutofillMandatoryReauth); switch (bubble_type) { case MandatoryReauthBubbleType::kOptIn: {
diff --git a/chrome/browser/ui/views/autofill/payments/mandatory_reauth_bubble_view_uitest.cc b/chrome/browser/ui/views/autofill/payments/mandatory_reauth_bubble_view_uitest.cc index 44813286..01f4ef7 100644 --- a/chrome/browser/ui/views/autofill/payments/mandatory_reauth_bubble_view_uitest.cc +++ b/chrome/browser/ui/views/autofill/payments/mandatory_reauth_bubble_view_uitest.cc
@@ -5,15 +5,19 @@ #include "base/functional/bind.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/mock_callback.h" +#include "base/test/scoped_feature_list.h" +#include "chrome/browser/ui/actions/chrome_action_id.h" #include "chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.h" #include "chrome/browser/ui/autofill/payments/mandatory_reauth_ui.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/views/autofill/payments/dialog_view_ids.h" #include "chrome/browser/ui/views/autofill/payments/mandatory_reauth_confirmation_bubble_view.h" #include "chrome/browser/ui/views/autofill/payments/mandatory_reauth_icon_view.h" #include "chrome/browser/ui/views/autofill/payments/mandatory_reauth_opt_in_bubble_view.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/frame/toolbar_button_provider.h" +#include "chrome/browser/ui/views/page_action/page_action_view.h" #include "chrome/test/base/in_process_browser_test.h" #include "components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.h" #include "components/autofill/core/browser/test_utils/autofill_test_utils.h" @@ -24,9 +28,23 @@ namespace autofill { -class MandatoryReauthBubbleViewUiTest : public InProcessBrowserTest { +class MandatoryReauthBubbleViewUiTest + : public InProcessBrowserTest, + public ::testing::WithParamInterface<bool> { public: - MandatoryReauthBubbleViewUiTest() = default; + MandatoryReauthBubbleViewUiTest() { + if (GetParam()) { + feature_list_.InitAndEnableFeatureWithParameters( + ::features::kPageActionsMigration, + { + { + ::features::kPageActionsMigrationAutofillMandatoryReauth.name, + "true", + }, + }); + } + } + ~MandatoryReauthBubbleViewUiTest() override = default; MandatoryReauthBubbleViewUiTest(const MandatoryReauthBubbleViewUiTest&) = delete; @@ -77,7 +95,19 @@ } views::BubbleDialogDelegate* GetReauthBubble() { - return GetIconView()->GetBubble(); + MandatoryReauthBubbleController* controller = GetController(); + if (!controller) { + return nullptr; + } + + if (controller->GetBubbleType() == + MandatoryReauthBubbleType::kConfirmation) { + return static_cast<autofill::MandatoryReauthConfirmationBubbleView*>( + controller->GetBubbleView()); + } + + return static_cast<autofill::MandatoryReauthOptInBubbleView*>( + controller->GetBubbleView()); } MandatoryReauthOptInBubbleView* GetOptInBubbleView() { @@ -92,14 +122,16 @@ controller->GetBubbleView()); } - MandatoryReauthIconView* GetIconView() { + IconLabelBubbleView* GetIconView() { BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser()); - PageActionIconView* icon = - browser_view->toolbar_button_provider()->GetPageActionIconView( - PageActionIconType::kMandatoryReauth); + + IconLabelBubbleView* icon = + browser_view->toolbar_button_provider()->GetPageActionView( + kActionAutofillMandatoryReauth); + DCHECK(icon); - return static_cast<MandatoryReauthIconView*>(icon); + return icon; } void ClickOnView(views::View* view) { @@ -162,9 +194,10 @@ protected: test::AutofillBrowserTestEnvironment autofill_test_environment_; + base::test::ScopedFeatureList feature_list_; }; -IN_PROC_BROWSER_TEST_F(MandatoryReauthBubbleViewUiTest, ShowBubble) { +IN_PROC_BROWSER_TEST_P(MandatoryReauthBubbleViewUiTest, ShowBubble) { base::HistogramTester histogram_tester; ShowBubble(); EXPECT_TRUE(GetReauthBubble()); @@ -176,7 +209,7 @@ autofill_metrics::MandatoryReauthOptInBubbleOffer::kShown, 1); } -IN_PROC_BROWSER_TEST_F(MandatoryReauthBubbleViewUiTest, +IN_PROC_BROWSER_TEST_P(MandatoryReauthBubbleViewUiTest, ClickOptInCancelButton) { base::HistogramTester histogram_tester; ShowBubble(); @@ -191,7 +224,7 @@ autofill_metrics::MandatoryReauthOptInBubbleResult::kCancelled, 1); } -IN_PROC_BROWSER_TEST_F(MandatoryReauthBubbleViewUiTest, ClickOptInOkButton) { +IN_PROC_BROWSER_TEST_P(MandatoryReauthBubbleViewUiTest, ClickOptInOkButton) { base::HistogramTester histogram_tester; ShowBubble(); EXPECT_CALL(accept_callback, Run).Times(1); @@ -205,7 +238,7 @@ autofill_metrics::MandatoryReauthOptInBubbleResult::kAccepted, 1); } -IN_PROC_BROWSER_TEST_F(MandatoryReauthBubbleViewUiTest, ClickOptInCloseButton) { +IN_PROC_BROWSER_TEST_P(MandatoryReauthBubbleViewUiTest, ClickOptInCloseButton) { base::HistogramTester histogram_tester; ShowBubble(); EXPECT_CALL(close_callback, Run).Times(1); @@ -219,7 +252,7 @@ autofill_metrics::MandatoryReauthOptInBubbleResult::kClosed, 1); } -IN_PROC_BROWSER_TEST_F(MandatoryReauthBubbleViewUiTest, ReshowOptInBubble) { +IN_PROC_BROWSER_TEST_P(MandatoryReauthBubbleViewUiTest, ReshowOptInBubble) { base::HistogramTester histogram_tester; ShowBubble(); ClickOnCloseButton(GetReauthBubble()); @@ -233,7 +266,7 @@ autofill_metrics::MandatoryReauthOptInBubbleOffer::kShown, 1); } -IN_PROC_BROWSER_TEST_F(MandatoryReauthBubbleViewUiTest, +IN_PROC_BROWSER_TEST_P(MandatoryReauthBubbleViewUiTest, ReshowConfirmationBubble) { base::HistogramTester histogram_tester; ShowBubble(); @@ -249,7 +282,7 @@ 1); } -IN_PROC_BROWSER_TEST_F(MandatoryReauthBubbleViewUiTest, +IN_PROC_BROWSER_TEST_P(MandatoryReauthBubbleViewUiTest, ClickConfirmationCloseButton) { ShowBubble(); ClickOnOkButton(GetReauthBubble()); @@ -268,7 +301,7 @@ MandatoryReauthBubbleType::kInactive); } -IN_PROC_BROWSER_TEST_F(MandatoryReauthBubbleViewUiTest, +IN_PROC_BROWSER_TEST_P(MandatoryReauthBubbleViewUiTest, ClickConfirmationSettingsLink) { base::HistogramTester histogram_tester; ShowBubble(); @@ -286,4 +319,8 @@ 1); } +INSTANTIATE_TEST_SUITE_P(All, + MandatoryReauthBubbleViewUiTest, + ::testing::Bool()); + } // namespace autofill
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc index d56f073..b2f35385 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_chromeos.cc
@@ -860,12 +860,8 @@ return false; } - if (IsTrustedPinned() && !browser_view()->browser()->IsLockedForOnTask()) { - // Avoid using immersive mode in locked fullscreen as it allows the user to - // exit the locked mode. Keep immersive mode enabled if the webapp is locked - // for OnTask (only relevant for non-web browser scenarios). - // TODO(crbug.com/429215055): Use explicit flag to enable / disable - // immersive mode for trusted_pinned state. + if (IsTrustedPinned() && + !GetFrameWindow()->GetProperty(chromeos::kUseImmersiveInTrustedPinned)) { return false; } if (display::Screen::GetScreen()->InTabletMode() &&
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index bd982cc2..5836866 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -313,6 +313,7 @@ #include "chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos.h" #include "chrome/grit/chrome_unscaled_resources.h" #include "chromeos/components/mgs/managed_guest_session_utils.h" +#include "chromeos/ui/base/window_properties.h" #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h" #include "chromeos/ui/frame/caption_buttons/frame_size_button.h" #include "chromeos/ui/wm/desks/desks_helper.h" @@ -1008,24 +1009,9 @@ watermark_view_ = contents_container->AddChildView( std::make_unique<enterprise_watermark::WatermarkView>()); - if (features::kGlicActorUiOverlay.Get()) { - auto actor_overlay_view = std::make_unique<views::View>(); - actor_overlay_view->SetID(VIEW_ID_ACTOR_OVERLAY); - actor_overlay_view->SetVisible(false); - actor_overlay_view->SetLayoutManager(std::make_unique<views::FillLayout>()); - actor_overlay_view_ = - contents_container->AddChildView(std::move(actor_overlay_view)); - } - -#if BUILDFLAG(ENABLE_GLIC) contents_container->SetLayoutManager(std::make_unique<ContentsLayoutManager>( devtools_web_view_, devtools_scrim_view_, contents_view, - lens_overlay_view_, watermark_view_, actor_overlay_view_)); -#else - contents_container->SetLayoutManager(std::make_unique<ContentsLayoutManager>( - devtools_web_view_, devtools_scrim_view_, contents_view, - lens_overlay_view_, watermark_view_, actor_overlay_view_)); -#endif + lens_overlay_view_, watermark_view_)); toolbar_ = top_container_->AddChildView( std::make_unique<ToolbarView>(browser_.get(), this)); @@ -1171,7 +1157,6 @@ left_aligned_side_panel_separator_ = nullptr; side_panel_rounded_corner_ = nullptr; toolbar_button_provider_ = nullptr; - actor_overlay_view_ = nullptr; // Child views maintain PrefMember attributes that point to // OffTheRecordProfile's PrefService which gets deleted by ~Browser. @@ -2523,6 +2508,22 @@ return &multi_contents_view_->drop_target_controller(); } +#if BUILDFLAG(IS_CHROMEOS) + +void BrowserView::OnLockedForOnTaskUpdated() { + bool locked_for_on_task = browser()->IsLockedForOnTask(); + // Use immersive mode for tabbed PWA. + if (browser()->CanSupportWindowFeature(Browser::FEATURE_TABSTRIP)) { + GetNativeWindow()->SetProperty(chromeos::kUseImmersiveInTrustedPinned, + locked_for_on_task); + } + // TODO(crbug.com/429215055): Move this logic to window manager. + SetCanMinimize(!locked_for_on_task); + SetShowCloseButton(!locked_for_on_task); +} + +#endif + base::CallbackListSubscription BrowserView::AddOnLinkOpeningFromGestureCallback( OnLinkOpeningFromGestureCallback callback) { return link_opened_from_gesture_callbacks_.Add(callback); @@ -3401,10 +3402,6 @@ return lens_overlay_view_; } -views::View* BrowserView::GetActorOverlayView() { - return actor_overlay_view_; -} - DownloadBubbleUIController* BrowserView::GetDownloadBubbleUIController() { #if !BUILDFLAG(IS_CHROMEOS) if (auto* download_controller =
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h index 3563541..080bbdfc 100644 --- a/chrome/browser/ui/views/frame/browser_view.h +++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -472,9 +472,6 @@ // Returns true if the browser is currently showing tabs in a split view. bool IsInSplitView() const; - // Returns the actor overlay view - views::View* GetActorOverlayView(); - // BrowserWindow: void Show() override; void ShowInactive() override; @@ -880,6 +877,11 @@ // Returns a `TabDragHandler`, if any available, to handle a tab drag. TabDragDelegate* GetTabDragDelegate(const gfx::Point& point_in_screen); +#if BUILDFLAG(IS_CHROMEOS) + // This is used only for SWA/PWA scenario. + void OnLockedForOnTaskUpdated(); +#endif + protected: // Enumerates where the devtools are docked relative to the browser's main // web contents. @@ -1254,12 +1256,6 @@ // contents_web_view_. raw_ptr<views::View> lens_overlay_view_ = nullptr; - // The view that contains the Glic Actor Overlay. The Actor Overlay is a UI - // overlay that is shown on top of the web contents. It therefore must always - // have the same bounds as the contents_web_view_, but also be above the - // contents_web_view_. - raw_ptr<views::View> actor_overlay_view_ = nullptr; - // The view that overlays a watermark on the contents container. raw_ptr<enterprise_watermark::WatermarkView> watermark_view_ = nullptr;
diff --git a/chrome/browser/ui/views/frame/contents_container_view.cc b/chrome/browser/ui/views/frame/contents_container_view.cc index 51e8703..3689ca4 100644 --- a/chrome/browser/ui/views/frame/contents_container_view.cc +++ b/chrome/browser/ui/views/frame/contents_container_view.cc
@@ -17,6 +17,7 @@ #include "chrome/browser/ui/views/frame/multi_contents_view_mini_toolbar.h" #include "chrome/browser/ui/views/frame/scrim_view.h" #include "chrome/browser/ui/views/new_tab_footer/footer_web_view.h" +#include "chrome/common/chrome_features.h" #include "components/search/ntp_features.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/color/color_provider.h" @@ -25,6 +26,7 @@ #include "ui/gfx/geometry/rect.h" #include "ui/views/border.h" #include "ui/views/layout/delegating_layout_manager.h" +#include "ui/views/layout/fill_layout.h" #include "ui/views/layout/proposed_layout.h" #include "ui/views/view_class_properties.h" @@ -77,6 +79,14 @@ inactive_split_scrim_view_->SetRoundedCorners(kContentRoundedCorners); } + if (features::kGlicActorUiOverlay.Get()) { + auto actor_overlay_view = std::make_unique<views::View>(); + actor_overlay_view->SetID(VIEW_ID_ACTOR_OVERLAY); + actor_overlay_view->SetVisible(false); + actor_overlay_view->SetLayoutManager(std::make_unique<views::FillLayout>()); + actor_overlay_view_ = AddChildView(std::move(actor_overlay_view)); + } + #if BUILDFLAG(ENABLE_GLIC) if (glic::GlicEnabling::IsProfileEligible(browser_view->GetProfile())) { glic_border_ = @@ -230,6 +240,13 @@ contents_bounds); } + // Actor Overlay view bounds are the same as the contents view. + if (actor_overlay_view_) { + layouts.child_layouts.emplace_back(actor_overlay_view_.get(), + actor_overlay_view_->GetVisible(), + contents_rect, size_bounds); + } + if (mini_toolbar_) { // |mini_toolbar_| should be offset in the bottom right corner, overlapping // the outline.
diff --git a/chrome/browser/ui/views/frame/contents_container_view.h b/chrome/browser/ui/views/frame/contents_container_view.h index bc5aeac..dbda8af 100644 --- a/chrome/browser/ui/views/frame/contents_container_view.h +++ b/chrome/browser/ui/views/frame/contents_container_view.h
@@ -35,6 +35,7 @@ ContentsWebView* GetContentsView() { return contents_view_; } MultiContentsViewMiniToolbar* GetMiniToolbar() { return mini_toolbar_; } ScrimView* GetContentsScrimView() { return contents_scrim_view_; } + views::View* GetActorOverlayView() { return actor_overlay_view_; } glic::GlicBorderView* GetGlicBorderView() { return glic_border_; } new_tab_footer::NewTabFooterWebView* GetNewTabFooterView() { return new_tab_footer_view_; @@ -74,6 +75,10 @@ // focused or site permissions dialogs are showing. raw_ptr<ScrimView> inactive_split_scrim_view_ = nullptr; + // The view that contains the Glic Actor Overlay. The Actor Overlay is a UI + // overlay that is shown on top of the web contents. + raw_ptr<views::View> actor_overlay_view_ = nullptr; + // The glic browser view that renders around the web contents area. raw_ptr<glic::GlicBorderView> glic_border_ = nullptr;
diff --git a/chrome/browser/ui/views/frame/contents_layout_manager.cc b/chrome/browser/ui/views/frame/contents_layout_manager.cc index 9e93742b..347ff56d 100644 --- a/chrome/browser/ui/views/frame/contents_layout_manager.cc +++ b/chrome/browser/ui/views/frame/contents_layout_manager.cc
@@ -11,14 +11,12 @@ views::View* devtools_scrim_view, views::View* contents_view, views::View* lens_overlay_view, - views::View* watermark_view, - views::View* actor_overlay_view) + views::View* watermark_view) : devtools_view_(devtools_view), devtools_scrim_view_(devtools_scrim_view), contents_view_(contents_view), lens_overlay_view_(lens_overlay_view), - watermark_view_(watermark_view), - actor_overlay_view_(actor_overlay_view) {} + watermark_view_(watermark_view) {} ContentsLayoutManager::~ContentsLayoutManager() = default; @@ -82,13 +80,6 @@ gfx::Rect(0, 0, width, height), views::SizeBounds(container_size)); } - // Actor Overlay view bounds are the same as the contents view. - if (actor_overlay_view_) { - layouts.child_layouts.emplace_back(actor_overlay_view_.get(), - actor_overlay_view_->GetVisible(), - contents_rect, optional_size_bound); - } - layouts.host_size = gfx::Size(width, height); return layouts; }
diff --git a/chrome/browser/ui/views/frame/contents_layout_manager.h b/chrome/browser/ui/views/frame/contents_layout_manager.h index 3d59b32..bf2457f0 100644 --- a/chrome/browser/ui/views/frame/contents_layout_manager.h +++ b/chrome/browser/ui/views/frame/contents_layout_manager.h
@@ -22,8 +22,7 @@ views::View* devtools_scrim_view, views::View* contents_view, views::View* lens_overlay_view, - views::View* watermark_view = nullptr, - views::View* actor_overlay_view = nullptr); + views::View* watermark_view = nullptr); ContentsLayoutManager(const ContentsLayoutManager&) = delete; ContentsLayoutManager& operator=(const ContentsLayoutManager&) = delete; @@ -45,7 +44,6 @@ const raw_ptr<views::View> contents_view_; const raw_ptr<views::View> lens_overlay_view_; const raw_ptr<views::View> watermark_view_; - const raw_ptr<views::View> actor_overlay_view_; DevToolsContentsResizingStrategy strategy_; };
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_impl.cc b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_impl.cc index ae44eb4..0281243 100644 --- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_impl.cc +++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_impl.cc
@@ -12,6 +12,7 @@ #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_content_view.h" #include "chrome/grit/generated_resources.h" +#include "components/javascript_dialogs/app_modal_dialog_queue.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/web_contents.h" #include "ui/base/metadata/metadata_impl_macros.h" @@ -143,6 +144,12 @@ // will automatically close when the page has loaded. if (GetReloadingView()->GetVisible() || GetContentView()->GetTrackingProtectionsButton()->GetSpinnerVisible()) { + // Always close the bubble if a JS dialog is being shown. + if (auto* app_modal_queue = + javascript_dialogs::AppModalDialogQueue::GetInstance(); + app_modal_queue && app_modal_queue->HasActiveDialog()) { + return true; + } return close_reason != views::Widget::ClosedReason::kLostFocus; }
diff --git a/chrome/browser/ui/views/page_action/action_ids.h b/chrome/browser/ui/views/page_action/action_ids.h index 917239bc..29cb8503 100644 --- a/chrome/browser/ui/views/page_action/action_ids.h +++ b/chrome/browser/ui/views/page_action/action_ids.h
@@ -15,7 +15,7 @@ // All ActionIds associated with a page action. // For now, the order of the page actions will be based on their position in // the array. -inline constexpr std::array<actions::ActionId, 13> kActionIds = { +inline constexpr std::array<actions::ActionId, 14> kActionIds = { kActionSidePanelShowLensOverlayResults, kActionShowTranslate, kActionShowMemorySaverChip, @@ -29,6 +29,7 @@ kActionCommerceProductSpecifications, kActionShowPasswordsBubbleOrPage, kActionShowCollaborationRecentActivity, + kActionAutofillMandatoryReauth, }; } // namespace page_actions
diff --git a/chrome/browser/ui/views/page_action/page_action_properties_provider.cc b/chrome/browser/ui/views/page_action/page_action_properties_provider.cc index 3d0a1d0..2996be1 100644 --- a/chrome/browser/ui/views/page_action/page_action_properties_provider.cc +++ b/chrome/browser/ui/views/page_action/page_action_properties_provider.cc
@@ -114,6 +114,13 @@ kCollaborationMessagingPageActionIconElementId, }, }, + { + kActionAutofillMandatoryReauth, + { + .histogram_name = "MandatoryReauth", + .type = PageActionIconType::kMandatoryReauth, + }, + }, }); } // namespace
diff --git a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc index 649b7b1..0e55cc9 100644 --- a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc +++ b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view_browsertest.cc
@@ -1716,8 +1716,10 @@ } // Ensure prerender navigations don't close the Safety Tip. +// +// TODO(https://crbug.com/434744048): Re-enable after fixing flakiness. IN_PROC_BROWSER_TEST_F(SafetyTipPageInfoBubbleViewPrerenderBrowserTest, - StillShowAfterPrerenderNavigation) { + DISABLED_StillShowAfterPrerenderNavigation) { // This domain is a lookalike of a top domain not in the top 500. const GURL kNavigatedUrl = embedded_test_server()->GetURL("accounts-google.com", "/title1.html");
diff --git a/chrome/browser/ui/views/uninstall_view_browsertest.cc b/chrome/browser/ui/views/uninstall_view_browsertest.cc index f1e70cd..dfde867 100644 --- a/chrome/browser/ui/views/uninstall_view_browsertest.cc +++ b/chrome/browser/ui/views/uninstall_view_browsertest.cc
@@ -6,6 +6,7 @@ #include <string> +#include "base/threading/thread_restrictions.h" #include "chrome/browser/ui/test/test_browser_dialog.h" #include "chrome/browser/ui/uninstall_browser_prompt.h" #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc index 9d0e451..3f14525 100644 --- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc +++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
@@ -3791,7 +3791,7 @@ const content::EvalJsResult list_result = content::EvalJs(web_contents, "navigator.subApps.list()"); - const base::Value::Dict list_result_dict = list_result.ExtractDict(); + const base::Value::Dict& list_result_dict = list_result.ExtractDict(); // Check that list() contained the sub_app_url key. EXPECT_NE(nullptr, list_result_dict.FindDict(sub_app_url));
diff --git a/chrome/browser/ui/web_applications/sub_apps_service_impl_browsertest.cc b/chrome/browser/ui/web_applications/sub_apps_service_impl_browsertest.cc index ebc8fc4..45299f6 100644 --- a/chrome/browser/ui/web_applications/sub_apps_service_impl_browsertest.cc +++ b/chrome/browser/ui/web_applications/sub_apps_service_impl_browsertest.cc
@@ -1101,7 +1101,7 @@ // Check List results for the main app contains 3 sub-apps. auto list_result_1 = ListSubAppsJS(iwa_frame_1); - auto dict_1 = list_result_1.ExtractDict(); + const auto& dict_1 = list_result_1.ExtractDict(); EXPECT_EQ(3ul, dict_1.size()); EXPECT_TRUE(dict_1.contains(kSubAppPath)); EXPECT_TRUE(dict_1.contains(kSubAppPath2));
diff --git a/chrome/browser/ui/webui/ash/manage_mirrorsync/manage_mirrorsync_dialog_browsertest.cc b/chrome/browser/ui/webui/ash/manage_mirrorsync/manage_mirrorsync_dialog_browsertest.cc index d792735..7080c87 100644 --- a/chrome/browser/ui/webui/ash/manage_mirrorsync/manage_mirrorsync_dialog_browsertest.cc +++ b/chrome/browser/ui/webui/ash/manage_mirrorsync/manage_mirrorsync_dialog_browsertest.cc
@@ -265,8 +265,9 @@ "const handler = BrowserProxy.getInstance().handler;" "const response = await handler.getSyncingPaths();" "return response; })())"; - auto response = content::EvalJs(dialog_contents_.get(), js_expression); - return response.ExtractDict(); + return content::EvalJs(dialog_contents_.get(), js_expression) + .TakeValue() + .TakeDict(); } void TearDown() override {
diff --git a/chrome/browser/ui/webui/new_tab_page/composebox/BUILD.gn b/chrome/browser/ui/webui/new_tab_page/composebox/BUILD.gn index 0d2d2c5..a797391 100644 --- a/chrome/browser/ui/webui/new_tab_page/composebox/BUILD.gn +++ b/chrome/browser/ui/webui/new_tab_page/composebox/BUILD.gn
@@ -42,7 +42,7 @@ source_set("unit_tests") { testonly = true sources = [ - "composebox_fieldtrial_unittest.cc", + "composebox_fieldtrial_config_unittest.cc", "composebox_handler_unittest.cc", ] deps = [ @@ -59,7 +59,7 @@ source_set("browser_tests") { testonly = true defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] - sources = [ "composebox_fieldtrial_browsertest.cc" ] + sources = [ "composebox_fieldtrial_entrypoint_browsertest.cc" ] deps = [ ":composebox", "//chrome/browser:browser_process",
diff --git a/chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial.cc b/chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial.cc index 2d45c421..67bc51f2 100644 --- a/chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial.cc +++ b/chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial.cc
@@ -148,8 +148,7 @@ return base::FeatureList::IsEnabled( kNtpSearchboxComposeEntrypointEnglishUS); } - return base::FeatureList::IsEnabled(kNtpSearchboxComposeEntrypoint) || - FeatureConfig::Get().enabled; + return base::FeatureList::IsEnabled(kNtpSearchboxComposeEntrypoint); } // If enabled, the Composebox will appear upon clicking the NTP Compose
diff --git a/chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial_browsertest.cc b/chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial_browsertest.cc deleted file mode 100644 index 82faa20..0000000 --- a/chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial_browsertest.cc +++ /dev/null
@@ -1,96 +0,0 @@ -// Copyright 2025 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial.h" - -#include "base/test/scoped_feature_list.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/test/base/chrome_test_utils.h" -#include "chrome/test/base/in_process_browser_test.h" -#include "chrome/test/base/scoped_browser_locale.h" -#include "components/variations/service/variations_service.h" -#include "content/public/test/browser_test.h" - -class NtpComposeboxFieldTrialBrowserTest : public InProcessBrowserTest { - public: - NtpComposeboxFieldTrialBrowserTest() { - feature_list_.InitWithFeatures( - {ntp_composebox::kNtpSearchboxComposeEntrypointEnglishUS, - ntp_composebox::kNtpSearchboxComposeEntrypoint}, - {}); - } - ~NtpComposeboxFieldTrialBrowserTest() override = default; - - protected: - base::test::ScopedFeatureList feature_list_; -}; - -class NtpComposeboxFieldTrialDisableEntrypointBrowserTest - : public InProcessBrowserTest { - public: - NtpComposeboxFieldTrialDisableEntrypointBrowserTest() { - feature_list_.InitWithFeatures( - {}, {ntp_composebox::kNtpSearchboxComposeEntrypointEnglishUS, - ntp_composebox::kNtpSearchboxComposeEntrypoint}); - } - ~NtpComposeboxFieldTrialDisableEntrypointBrowserTest() override = default; - - protected: - base::test::ScopedFeatureList feature_list_; -}; - -class NtpComposeboxFieldTrialOverrideEntrypointBrowserTest - : public InProcessBrowserTest { - public: - NtpComposeboxFieldTrialOverrideEntrypointBrowserTest() { - feature_list_.InitWithFeatures( - {ntp_composebox::kNtpSearchboxComposeEntrypoint}, - {ntp_composebox::kNtpSearchboxComposeEntrypointEnglishUS}); - } - ~NtpComposeboxFieldTrialOverrideEntrypointBrowserTest() override = default; - - protected: - base::test::ScopedFeatureList feature_list_; -}; - -IN_PROC_BROWSER_TEST_F(NtpComposeboxFieldTrialBrowserTest, - EnableEntrypointUSEnglishTot) { - auto locale = std::make_unique<ScopedBrowserLocale>("en-US"); - g_browser_process->variations_service()->OverrideStoredPermanentCountry("us"); - EXPECT_TRUE(ntp_composebox::IsNtpSearchboxComposeEntrypointEnabled( - g_browser_process)); -} - -IN_PROC_BROWSER_TEST_F(NtpComposeboxFieldTrialBrowserTest, - DisableEntrypointForNonUSEnglish) { - auto locale = std::make_unique<ScopedBrowserLocale>("en-CA"); - g_browser_process->variations_service()->OverrideStoredPermanentCountry("ca"); - EXPECT_TRUE(ntp_composebox::IsNtpSearchboxComposeEntrypointEnabled( - g_browser_process)); -} - -IN_PROC_BROWSER_TEST_F(NtpComposeboxFieldTrialDisableEntrypointBrowserTest, - DisableEntrypointUSEnglishTot) { - auto locale = std::make_unique<ScopedBrowserLocale>("en-US"); - g_browser_process->variations_service()->OverrideStoredPermanentCountry("us"); - EXPECT_FALSE(ntp_composebox::IsNtpSearchboxComposeEntrypointEnabled( - g_browser_process)); -} - -IN_PROC_BROWSER_TEST_F(NtpComposeboxFieldTrialDisableEntrypointBrowserTest, - DisableEntrypointForNonUSEnglish) { - auto locale = std::make_unique<ScopedBrowserLocale>("en-CA"); - g_browser_process->variations_service()->OverrideStoredPermanentCountry("ca"); - EXPECT_FALSE(ntp_composebox::IsNtpSearchboxComposeEntrypointEnabled( - g_browser_process)); -} - -IN_PROC_BROWSER_TEST_F(NtpComposeboxFieldTrialOverrideEntrypointBrowserTest, - OverrideEntrypointFeature) { - // If `kNtpSearchboxComposeEntrypoint` is overridden, show entrypoint even if - // `kNtpSearchboxComposeEntrypointEnglishUS` is disabled. - EXPECT_TRUE(ntp_composebox::IsNtpSearchboxComposeEntrypointEnabled( - g_browser_process)); -}
diff --git a/chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial_unittest.cc b/chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial_config_unittest.cc similarity index 95% rename from chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial_unittest.cc rename to chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial_config_unittest.cc index 527338f..7cb000d8 100644 --- a/chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial_unittest.cc +++ b/chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial_config_unittest.cc
@@ -2,13 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial.h" - #include <string> #include "base/base64.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" +#include "chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial.h" #include "chrome/grit/generated_resources.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -28,13 +27,13 @@ } // namespace -class NtpComposeboxFieldTrialTest : public testing::Test { +class NtpComposeboxFieldTrialConfigTest : public testing::Test { public: ScopedFeatureConfigForTesting scoped_config_; }; // Tests the NTP Composebox configuration when the feature is disabled. -TEST_F(NtpComposeboxFieldTrialTest, NTPComposeboxConfig_Disabled) { +TEST_F(NtpComposeboxFieldTrialConfigTest, NTPComposeboxConfig_Disabled) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndDisableFeature(kNtpComposebox); @@ -50,7 +49,7 @@ // Tests the NTP Composebox configuration when the feature is enabled with the // default parameter. -TEST_F(NtpComposeboxFieldTrialTest, +TEST_F(NtpComposeboxFieldTrialConfigTest, NTPComposeboxConfig_Enabled_DefaultConfiguration) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature(kNtpComposebox); @@ -87,7 +86,7 @@ // Tests the NTP Composebox configuration when the feature is enabled with an // invalid Base64-encoded proto parameter. -TEST_F(NtpComposeboxFieldTrialTest, +TEST_F(NtpComposeboxFieldTrialConfigTest, NTPComposeboxConfig_Enabled_Invalid_Configuration) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeatureWithParameters( @@ -106,7 +105,7 @@ // Tests the NTP Composebox configuration when the feature is enabled with a // valid Base64-encoded proto parameter that does not set a value. -TEST_F(NtpComposeboxFieldTrialTest, +TEST_F(NtpComposeboxFieldTrialConfigTest, NTPComposeboxConfig_Enabled_Valid_Unset_Configuration) { omnibox::NTPComposeboxConfig fieldtrial_config; @@ -126,7 +125,7 @@ // Tests the NTP Composebox configuration when the feature is enabled with a // valid Base64-encoded proto parameter that sets a value. -TEST_F(NtpComposeboxFieldTrialTest, +TEST_F(NtpComposeboxFieldTrialConfigTest, NTPComposeboxConfig_Enabled_Valid_Set_Configuration) { omnibox::NTPComposeboxConfig fieldtrial_config; fieldtrial_config.mutable_entry_point()->set_num_page_load_animations(5); @@ -150,7 +149,7 @@ // Tests that setting `mime_types_allowed` for images in the `fieldtrial_config` // overrides the default image mime types. -TEST_F(NtpComposeboxFieldTrialTest, +TEST_F(NtpComposeboxFieldTrialConfigTest, NTPComposeboxConfig_Enabled_Valid_OverrideImageMimeTypes) { omnibox::NTPComposeboxConfig fieldtrial_config; fieldtrial_config.mutable_composebox() @@ -176,7 +175,7 @@ // Tests that setting `mime_types_allowed` for attachments in the // `fieldtrial_config` overrides the default attachment mime types. -TEST_F(NtpComposeboxFieldTrialTest, +TEST_F(NtpComposeboxFieldTrialConfigTest, NTPComposeboxConfig_Enabled_Valid_OverrideAttachmentMimeTypes) { omnibox::NTPComposeboxConfig fieldtrial_config; fieldtrial_config.mutable_composebox() @@ -202,7 +201,7 @@ // Tests that providing an empty `mime_types_allowed` value in the fieldtrial // config does not clear the default value. -TEST_F(NtpComposeboxFieldTrialTest, +TEST_F(NtpComposeboxFieldTrialConfigTest, NTPComposeboxConfig_Enabled_Valid_ClearMimeTypes) { omnibox::NTPComposeboxConfig fieldtrial_config; // Providing an empty `mime_types_allowed`, will not clear the default
diff --git a/chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial_entrypoint_browsertest.cc b/chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial_entrypoint_browsertest.cc new file mode 100644 index 0000000..b205511 --- /dev/null +++ b/chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial_entrypoint_browsertest.cc
@@ -0,0 +1,116 @@ +// 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 <optional> +#include <tuple> +#include <vector> + +#include "base/test/scoped_feature_list.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/ui/webui/new_tab_page/composebox/composebox_fieldtrial.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/scoped_browser_locale.h" +#include "components/variations/service/variations_service.h" +#include "content/public/test/browser_test.h" + +class NtpComposeboxFieldTrialEntrypointBrowserTest + : public InProcessBrowserTest, + public ::testing::WithParamInterface<std::tuple<std::string, + std::string, + std::optional<bool>, + std::optional<bool>>> { + public: + NtpComposeboxFieldTrialEntrypointBrowserTest() = default; + ~NtpComposeboxFieldTrialEntrypointBrowserTest() override = default; + + protected: + void SetUp() override { + auto entrypoint = std::get<2>(GetParam()); + auto entrypoint_en_us = std::get<3>(GetParam()); + std::vector<base::test::FeatureRef> enabled_features; + std::vector<base::test::FeatureRef> disabled_features; + if (entrypoint.has_value()) { + if (*entrypoint) { + enabled_features.push_back( + ntp_composebox::kNtpSearchboxComposeEntrypoint); + } else { + disabled_features.push_back( + ntp_composebox::kNtpSearchboxComposeEntrypoint); + } + } + if (entrypoint_en_us.has_value()) { + if (*entrypoint_en_us) { + enabled_features.push_back( + ntp_composebox::kNtpSearchboxComposeEntrypointEnglishUS); + } else { + disabled_features.push_back( + ntp_composebox::kNtpSearchboxComposeEntrypointEnglishUS); + } + } + feature_list_.InitWithFeatures(enabled_features, disabled_features); + + InProcessBrowserTest::SetUp(); + } + + void SetUpOnMainThread() override { + scoped_browser_locale_ = + std::make_unique<ScopedBrowserLocale>(std::get<0>(GetParam())); + g_browser_process->variations_service()->OverrideStoredPermanentCountry( + std::get<1>(GetParam())); + + InProcessBrowserTest::SetUpOnMainThread(); + } + + void TearDownOnMainThread() override { + scoped_browser_locale_.reset(); + + InProcessBrowserTest::TearDownOnMainThread(); + } + + base::test::ScopedFeatureList feature_list_; + std::unique_ptr<ScopedBrowserLocale> scoped_browser_locale_; +}; + +INSTANTIATE_TEST_SUITE_P(, + NtpComposeboxFieldTrialEntrypointBrowserTest, + ::testing::Combine( + // Values for the locale. + ::testing::Values("en-US", "es-MX"), + // Values for the country. + ::testing::Values("us", "ca"), + // Values for the generic entrypoint feature state. + ::testing::Values(std::nullopt, true, false), + // Values for the en-US entrypoint feature state. + ::testing::Values(std::nullopt, true, false))); + +IN_PROC_BROWSER_TEST_P(NtpComposeboxFieldTrialEntrypointBrowserTest, Test) { + auto [locale, country, entrypoint, entrypoint_english_us] = GetParam(); + bool expected_enabled; + if (locale == "en-US" && country == "us") { + // For en-US in US, the generic `entrypoint` feature takes precedence. If + // not set, the `entrypoint_english_us` feature is checked. + if (entrypoint.has_value()) { + expected_enabled = *entrypoint; + } else if (entrypoint_english_us.has_value()) { + expected_enabled = *entrypoint_english_us; + } else { + // Fallback to the default state of the en-US specific feature. + expected_enabled = base::FeatureList::IsEnabled( + ntp_composebox::kNtpSearchboxComposeEntrypointEnglishUS); + } + } else { + // For all other countries and locales, only the generic `entrypoint` + // feature matters. The `entrypoint_english_us` feature is ignored. + if (entrypoint.has_value()) { + expected_enabled = *entrypoint; + } else { + // Fallback to the default state of the generic feature. + expected_enabled = base::FeatureList::IsEnabled( + ntp_composebox::kNtpSearchboxComposeEntrypoint); + } + } + EXPECT_EQ( + ntp_composebox::IsNtpSearchboxComposeEntrypointEnabled(g_browser_process), + expected_enabled); +}
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc index f49c5fc..d6d1306 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -533,8 +533,9 @@ composebox_config.max_num_files()); source->AddBoolean("searchboxShowComposeEntrypoint", - ntp_composebox::IsNtpSearchboxComposeEntrypointEnabled( - g_browser_process) && + (ntp_composebox::IsNtpSearchboxComposeEntrypointEnabled( + g_browser_process) || + ntp_composebox::FeatureConfig::Get().enabled) && omnibox::IsAimAllowedByPolicy(profile->GetPrefs())); source->AddBoolean("searchboxShowComposebox", ntp_composebox::FeatureConfig::Get().enabled &&
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.h b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.h index 36846d3..802a2861 100644 --- a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.h +++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.h
@@ -17,10 +17,6 @@ #include "chrome/browser/ui/webui/print_preview/printer_handler.h" #include "chromeos/crosapi/mojom/local_printer.mojom.h" -namespace base { -class Value; -} - namespace content { class WebContents; }
diff --git a/chrome/browser/ui/webui/settings/sync_settings_interactive_uitest.cc b/chrome/browser/ui/webui/settings/sync_settings_interactive_uitest.cc index a1cde68..e1759434 100644 --- a/chrome/browser/ui/webui/settings/sync_settings_interactive_uitest.cc +++ b/chrome/browser/ui/webui/settings/sync_settings_interactive_uitest.cc
@@ -140,18 +140,18 @@ const DeepQuery kTurnHistorySyncOn = {"settings-ui", "settings-main", "settings-people-page-index", - "settings-people-page", + "settings-account-page", "settings-sync-account-control", "cr-button#sync-button"}; const DeepQuery kHistoryOptinAcceptButton = {"history-sync-optin-app", "#acceptButton"}; const DeepQuery kHistoryOptinRejectButton = {"history-sync-optin-app", "#rejectButton"}; - const GURL kSyncSettingsUrl = GURL("chrome://settings/syncSetup"); + const GURL kAccountSettingsUrl = GURL("chrome://settings/account"); RunTestSequence( InstrumentTab(kTabId, 0, browser()), - NavigateWebContents(kTabId, kSyncSettingsUrl), + NavigateWebContents(kTabId, kAccountSettingsUrl), WaitForStateChange(kTabId, PageWithMatchingTitle("Settings")), WaitForStateChange(kTabId, UiElementHasAppeared(kTurnHistorySyncOn)), ClickButton(kTabId, kTurnHistorySyncOn),
diff --git a/chrome/browser/ui/webui/watermark/watermark_ui.cc b/chrome/browser/ui/webui/watermark/watermark_ui.cc index 21979d4..b0167a8 100644 --- a/chrome/browser/ui/webui/watermark/watermark_ui.cc +++ b/chrome/browser/ui/webui/watermark/watermark_ui.cc
@@ -35,9 +35,6 @@ // Add required resources. webui::SetupWebUIDataSource(source, kWatermarkResources, IDR_WATERMARK_WATERMARK_HTML); - - // Pass a simple message to the frontend. - source->AddString("message", "This is a watermark page!"); } WatermarkUI::~WatermarkUI() = default;
diff --git a/chrome/browser/web_applications/commands/web_install_from_url_command_browsertest.cc b/chrome/browser/web_applications/commands/web_install_from_url_command_browsertest.cc index c8d6f42..82a7be4 100644 --- a/chrome/browser/web_applications/commands/web_install_from_url_command_browsertest.cc +++ b/chrome/browser/web_applications/commands/web_install_from_url_command_browsertest.cc
@@ -48,6 +48,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/features_generated.h" +#include "third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/models/image_model.h" #include "ui/gfx/codec/png_codec.h" @@ -205,6 +206,9 @@ histograms.ExpectUniqueSample("WebApp.LaunchSource", kLaunchSource, 1); histograms.ExpectUniqueSample("WebApp.NewCraftedAppInstalled.ByUser", /*sample=*/true, 1); + histograms.ExpectBucketCount("Blink.UseCounter.WebDXFeatures", + blink::mojom::WebDXFeature::kDRAFT_WebInstallAPI, + 1); EXPECT_THAT(histograms, test::ForAllGetAllSamples( @@ -241,6 +245,9 @@ histograms.ExpectUniqueSample("WebApp.LaunchSource", kLaunchSource, 1); histograms.ExpectUniqueSample("WebApp.NewCraftedAppInstalled.ByUser", /*sample=*/true, 1); + histograms.ExpectBucketCount("Blink.UseCounter.WebDXFeatures", + blink::mojom::WebDXFeature::kDRAFT_WebInstallAPI, + 1); EXPECT_THAT(histograms, test::ForAllGetAllSamples(
diff --git a/chrome/browser/web_applications/scope_extension_info.h b/chrome/browser/web_applications/scope_extension_info.h index a4acaf6..5b54bdb2 100644 --- a/chrome/browser/web_applications/scope_extension_info.h +++ b/chrome/browser/web_applications/scope_extension_info.h
@@ -13,10 +13,6 @@ #include "url/gurl.h" #include "url/origin.h" -namespace base { -class Value; -} // namespace base - namespace web_app { namespace proto {
diff --git a/chrome/browser/web_applications/web_install_browsertest.cc b/chrome/browser/web_applications/web_install_browsertest.cc index 36a185d..b89a4116 100644 --- a/chrome/browser/web_applications/web_install_browsertest.cc +++ b/chrome/browser/web_applications/web_install_browsertest.cc
@@ -32,6 +32,7 @@ #include "net/test/embedded_test_server/embedded_test_server.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/features_generated.h" +#include "third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom.h" #include "ui/views/widget/any_widget_observer.h" namespace { @@ -210,6 +211,9 @@ // Validate JS results. EXPECT_TRUE(ResultExists(app_web_contents)); EXPECT_FALSE(ErrorExists(app_web_contents)); + histograms.ExpectBucketCount("Blink.UseCounter.WebDXFeatures", + blink::mojom::WebDXFeature::kDRAFT_WebInstallAPI, + 1); histograms.ExpectUniqueSample("WebApp.Install.Source.Success", kInstallSource, 1);
diff --git a/chrome/browser/webauthn/enclave_manager.cc b/chrome/browser/webauthn/enclave_manager.cc index d8c1590e..7af0036 100644 --- a/chrome/browser/webauthn/enclave_manager.cc +++ b/chrome/browser/webauthn/enclave_manager.cc
@@ -211,6 +211,29 @@ static const char kPinRenewalFailureHistogram[] = "WebAuthentication.PinRenewalFailureCause"; +// The parsed response to an enclave "recovery_key_store/wrap" command. +struct EnclaveRecoveryKeyStoreWrapResponse { + EnclaveRecoveryKeyStoreWrapResponse() = default; + ~EnclaveRecoveryKeyStoreWrapResponse() = default; + EnclaveRecoveryKeyStoreWrapResponse( + const EnclaveRecoveryKeyStoreWrapResponse& other) = delete; + EnclaveRecoveryKeyStoreWrapResponse& operator=( + const EnclaveRecoveryKeyStoreWrapResponse& other) = delete; + EnclaveRecoveryKeyStoreWrapResponse( + EnclaveRecoveryKeyStoreWrapResponse&& other) = default; + EnclaveRecoveryKeyStoreWrapResponse& operator=( + EnclaveRecoveryKeyStoreWrapResponse&& other) = default; + + // The protobuf that can be sent to the recovery key store. + std::unique_ptr<trusted_vault_pb::Vault> vault; + + // The chosen cohort public key. + std::vector<uint8_t> cohort_public_key; + + // The cert.xml serial number used to select a cohort. + int cert_xml_serial_number; +}; + // Since protobuf maps `bytes` to `std::string` (rather than // `std::vector<uint8_t>`), functions for jumping between these representations // are needed. @@ -789,9 +812,10 @@ }; // Convert the response to an enclave "recovery_key_store/wrap" command, into a -// protobuf that can be sent to the recovery key store service. -std::optional<std::unique_ptr<trusted_vault_pb::Vault>> -RecoveryKeyStoreWrapResponseToProto( +// protobuf that can be sent to the recovery key store service and extracts the +// data required to build a wrapped PIN. +std::optional<EnclaveRecoveryKeyStoreWrapResponse> +ParseRecoveryKeyStoreWrapResponse( const PinMetadata& pin_metadata, const cbor::Value& recovery_key_store_wrap_response) { if (!recovery_key_store_wrap_response.is_map()) { @@ -840,6 +864,12 @@ return std::nullopt; } + it = response.find(cbor::Value("serial")); + if (it == response.end() || !it->second.is_integer()) { + return std::nullopt; + } + int cert_xml_serial_number = it->second.GetInteger(); + auto vault = std::make_unique<trusted_vault_pb::Vault>(); auto* params = vault->mutable_vault_parameters(); params->set_backend_public_key(VecToString(cohort_public_key)); @@ -873,7 +903,11 @@ } vault->set_vault_metadata(std::move(metadata_bytes)); - return vault; + EnclaveRecoveryKeyStoreWrapResponse result; + result.vault = std::move(vault); + result.cohort_public_key = std::move(cohort_public_key); + result.cert_xml_serial_number = cert_xml_serial_number; + return result; } base::flat_map<int32_t, std::vector<uint8_t>> GetNewSecretsToStore( @@ -1020,7 +1054,7 @@ // Parse a Vault and security domain member keys from a CBOR map. These maps // result from enclave operations that return a Vault for insertion into the // security domain. -static std::optional<std::pair<std::unique_ptr<trusted_vault_pb::Vault>, +static std::optional<std::pair<EnclaveRecoveryKeyStoreWrapResponse, trusted_vault::MemberKeysSource>> ParseVaultAndMemberResponse(const int32_t key_version, const PinMetadata& pin_metadata, @@ -1030,9 +1064,9 @@ FIDO_LOG(ERROR) << "response missing 'wrapped'"; return std::nullopt; } - std::optional<std::unique_ptr<trusted_vault_pb::Vault>> vault = - RecoveryKeyStoreWrapResponseToProto(pin_metadata, it->second); - if (!vault) { + std::optional<EnclaveRecoveryKeyStoreWrapResponse> wrap_response = + ParseRecoveryKeyStoreWrapResponse(pin_metadata, it->second); + if (!wrap_response) { FIDO_LOG(ERROR) << "Failed to translate response into an UpdateVaultProto"; return std::nullopt; } @@ -1054,7 +1088,8 @@ auto member_keys_source = trusted_vault::MemberKeys(key_version, wrapped_sds, member_proof); - return std::make_pair(std::move(*vault), std::move(member_keys_source)); + return std::make_pair(std::move(*wrap_response), + std::move(member_keys_source)); } class UvKeyCreationLockImpl : public EnclaveManager::UvKeyCreationLock { @@ -2179,23 +2214,21 @@ .find(cbor::Value(enclave::kResponseSuccessKey)) ->second; - std::optional<std::unique_ptr<trusted_vault_pb::Vault>> vault = - RecoveryKeyStoreWrapResponseToProto(hashed_pin_->metadata, - recovery_key_store_wrap_response); - if (!vault) { + recovery_key_store_wrap_response_ = ParseRecoveryKeyStoreWrapResponse( + hashed_pin_->metadata, recovery_key_store_wrap_response); + if (!recovery_key_store_wrap_response_) { FIDO_LOG(ERROR) << "Failed to translate response into an UpdateVaultProto"; state_ = State::kStop; return; } - vault_ = std::move(*vault); wrapping_response_ = std::move(response); state_ = State::kWaitingForRecoveryKeyStore; recovery_key_store_request_ = manager_->recovery_key_store_conn_->UpdateRecoveryKeyStore( - *primary_account_info_, *vault_, + *primary_account_info_, *recovery_key_store_wrap_response_->vault, base::BindOnce( [](base::WeakPtr<StateMachine> machine, trusted_vault::RecoveryKeyStoreStatus status) { @@ -2231,10 +2264,13 @@ CHECK(wrapped_pin_proto_->wrapped_pin().empty()); wrapped_pin_proto_->set_wrapped_pin(BuildWrappedPIN( *hashed_pin_, ToSizedSpan<32>(wrapped_pin_proto_->claim_key()), - vault_.get(), store_keys_args_for_joining_->keys.back())); + *recovery_key_store_wrap_response_, + store_keys_args_for_joining_->keys.back())); } const std::string& vault_public_key = - vault_->application_keys()[0].asymmetric_key_pair().public_key(); + recovery_key_store_wrap_response_->vault->application_keys()[0] + .asymmetric_key_pair() + .public_key(); const auto secure_box_pub_key = trusted_vault::SecureBoxPublicKey::CreateByImport( base::as_byte_span(vault_public_key)); @@ -2472,7 +2508,7 @@ return false; } const int32_t key_version = GetCurrentWrappedSecretForUser(user_).first; - std::optional<std::pair<std::unique_ptr<trusted_vault_pb::Vault>, + std::optional<std::pair<EnclaveRecoveryKeyStoreWrapResponse, trusted_vault::MemberKeysSource>> result = ParseVaultAndMemberResponse(key_version, pin_metadata, response_value.GetMap()); @@ -2484,12 +2520,13 @@ } return false; } - std::tie(vault_, member_keys_source_) = std::move(*result); + std::tie(recovery_key_store_wrap_response_, member_keys_source_) = + std::move(*result); state_ = State::kWaitingForRecoveryKeyStore; recovery_key_store_request_ = manager_->recovery_key_store_conn_->UpdateRecoveryKeyStore( - *primary_account_info_, *vault_, + *primary_account_info_, *recovery_key_store_wrap_response_->vault, base::BindOnce( [](base::WeakPtr<StateMachine> machine, trusted_vault::RecoveryKeyStoreStatus status) { @@ -2592,7 +2629,7 @@ static std::string BuildWrappedPIN( const HashedPIN& hashed_pin, base::span<const uint8_t, 32> claim_key, - const trusted_vault_pb::Vault* vault, + const EnclaveRecoveryKeyStoreWrapResponse& vault_details, base::span<const uint8_t> security_domain_secret) { cbor::Value::MapValue map; map.emplace(1, base::span<const uint8_t>(hashed_pin.hashed)); @@ -2600,11 +2637,17 @@ map.emplace(2, 0); // Generation number. } map.emplace(3, claim_key); - map.emplace(4, base::as_byte_span(vault->vault_parameters().counter_id())); + map.emplace(4, base::as_byte_span( + vault_details.vault->vault_parameters().counter_id())); // The vault handle in the wrapped PIN doesn't include the first byte, // which is the type of the vault entry. - map.emplace(5, base::as_byte_span(vault->vault_parameters().vault_handle()) + map.emplace(5, base::as_byte_span( + vault_details.vault->vault_parameters().vault_handle()) .subspan<1>()); + if (base::FeatureList::IsEnabled(device::kWebAuthnWrapCohortData)) { + map.emplace(6, vault_details.cert_xml_serial_number); + map.emplace(7, vault_details.cohort_public_key); + } const std::vector<uint8_t> cbor_bytes = cbor::Writer::Write(cbor::Value(std::move(map))).value(); return VecToString(EncryptWrappedPIN(security_domain_secret, cbor_bytes)); @@ -2652,7 +2695,8 @@ std::optional<std::string> cert_xml_; std::optional<std::string> sig_xml_; std::unique_ptr<HashedPIN> hashed_pin_; - std::unique_ptr<trusted_vault_pb::Vault> vault_; + std::optional<EnclaveRecoveryKeyStoreWrapResponse> + recovery_key_store_wrap_response_; std::unique_ptr<trusted_vault::RecoveryKeyStoreConnection::Request> recovery_key_store_request_; std::optional<cbor::Value> wrapping_response_; @@ -3598,6 +3642,8 @@ hashed->ToWrappedPIN(); const uint8_t kFakeCounterId[8] = {}; const uint8_t kFakeVaultHandle[16] = {}; + const uint8_t kFakeCohortPublicKey[16] = {}; + const int32_t kFakeSerialNumber = 1; cbor::Value::MapValue map; map.emplace(1, base::span<const uint8_t>(hashed->hashed)); @@ -3605,6 +3651,8 @@ map.emplace(3, ToSizedSpan<32>(wrapped_pin->claim_key())); map.emplace(4, base::span<const uint8_t>(kFakeCounterId)); map.emplace(5, base::span<const uint8_t>(kFakeVaultHandle)); + map.emplace(6, base::span<const uint8_t>(kFakeCohortPublicKey)); + map.emplace(7, kFakeSerialNumber); const std::vector<uint8_t> cbor_bytes = cbor::Writer::Write(cbor::Value(std::move(map))).value(); wrapped_pin->set_wrapped_pin(
diff --git a/chrome/browser/webauthn/enclave_manager_unittest.cc b/chrome/browser/webauthn/enclave_manager_unittest.cc index 6a25cd8..4374ece 100644 --- a/chrome/browser/webauthn/enclave_manager_unittest.cc +++ b/chrome/browser/webauthn/enclave_manager_unittest.cc
@@ -45,6 +45,7 @@ #include "chrome/browser/webauthn/proto/enclave_local_state.pb.h" #include "chrome/browser/webauthn/test_util.h" #include "chrome/browser/webauthn/unexportable_key_utils.h" +#include "components/cbor/reader.h" #include "components/os_crypt/sync/os_crypt_mocker.h" #include "components/signin/public/base/consent_level.h" #include "components/signin/public/identity_manager/identity_test_environment.h" @@ -52,6 +53,8 @@ #include "components/trusted_vault/command_line_switches.h" #include "components/trusted_vault/proto/vault.pb.h" #include "components/trusted_vault/trusted_vault_connection.h" +#include "crypto/aead.h" +#include "crypto/hkdf.h" #include "crypto/scoped_fake_unexportable_key_provider.h" #include "crypto/scoped_fake_user_verifying_key_provider.h" #include "crypto/user_verifying_key.h" @@ -202,6 +205,29 @@ return base::MakeRefCounted<device::JSONRequest>(std::move(json_request)); } +std::vector<uint8_t> DecryptWrappedPin( + base::span<const uint8_t> security_domain_secret, + base::span<const uint8_t> wrapped_pin) { + base::span<const uint8_t> nonce = wrapped_pin.first(12u); + base::span<const uint8_t> encrypted_pin = wrapped_pin.subspan(12u); + // This is "KeychainApplicationKey:chrome:GPM PIN data wrapping key". + static constexpr uint8_t kKeyPurposePinDataKey[] = { + 0x4b, 0x65, 0x79, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x41, 0x70, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, + 0x3a, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x3a, 0x47, 0x50, 0x4d, + 0x20, 0x50, 0x49, 0x4e, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x77, + 0x72, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x6b, 0x65, 0x79}; + const std::array<uint8_t, 32> derived_key = crypto::HkdfSha256<32>( + security_domain_secret, /*salt=*/base::span<const uint8_t>(), + kKeyPurposePinDataKey); + crypto::Aead aead(crypto::Aead::AeadAlgorithm::AES_256_GCM); + aead.Init(derived_key); + std::optional<std::vector<uint8_t>> pin = aead.Open( + encrypted_pin, nonce, /*additional_data=*/base::span<const uint8_t>()); + CHECK(pin.has_value()); + return *pin; +} + } // namespace class EnclaveManagerTest : public testing::Test, EnclaveManager::Observer { @@ -778,6 +804,21 @@ CHECK(security_domain_secret.has_value()); EXPECT_EQ(manager_.TakeSecret()->second, *security_domain_secret); + // Verify that the wrapped PIN Chrome generated contains the cohort details. + std::vector<uint8_t> wrapped_pin = DecryptWrappedPin( + *security_domain_secret, + base::as_byte_span(manager_.GetWrappedPIN()->wrapped_pin())); + std::optional<cbor::Value> cbor = cbor::Reader::Read(wrapped_pin); + const cbor::Value::MapValue& wrapped_pin_cbor = cbor->GetMap(); + int cert_xml_serial_number = + wrapped_pin_cbor.find(cbor::Value(6))->second.GetInteger(); + EXPECT_EQ(cert_xml_serial_number, FakeRecoveryKeyStore::kTestSerialNumber); + const std::vector<uint8_t> cohort_public_key = + wrapped_pin_cbor.find(cbor::Value(7))->second.GetBytestring(); + EXPECT_EQ(cohort_public_key, + recovery_key_store_->endpoint_public_key_bytes()); + + // Verify we can use the PIN to create a passkey and assert it. std::unique_ptr<device::enclave::ClaimedPIN> claimed_pin = EnclaveManager::MakeClaimedPINSlowly(pin, manager_.GetWrappedPIN()); std::unique_ptr<sync_pb::WebauthnCredentialSpecifics> entity; @@ -844,6 +885,21 @@ CHECK(security_domain_secret.has_value()); EXPECT_EQ(manager_.TakeSecret()->second, *security_domain_secret); + // Verify that the wrapped PIN the enclave generated contains the cohort + // details. + std::vector<uint8_t> wrapped_pin = DecryptWrappedPin( + *security_domain_secret, + base::as_byte_span(manager_.GetWrappedPIN()->wrapped_pin())); + std::optional<cbor::Value> cbor = cbor::Reader::Read(wrapped_pin); + const cbor::Value::MapValue& wrapped_pin_cbor = cbor->GetMap(); + int cert_xml_serial_number = + wrapped_pin_cbor.find(cbor::Value(6))->second.GetInteger(); + EXPECT_EQ(cert_xml_serial_number, FakeRecoveryKeyStore::kTestSerialNumber); + const std::vector<uint8_t> cohort_public_key = + wrapped_pin_cbor.find(cbor::Value(7))->second.GetBytestring(); + EXPECT_EQ(cohort_public_key, + recovery_key_store_->endpoint_public_key_bytes()); + std::unique_ptr<device::enclave::ClaimedPIN> claimed_pin = EnclaveManager::MakeClaimedPINSlowly(pin, manager_.GetWrappedPIN()); std::unique_ptr<sync_pb::WebauthnCredentialSpecifics> entity;
diff --git a/chrome/browser/webauthn/fake_recovery_key_store.cc b/chrome/browser/webauthn/fake_recovery_key_store.cc index 9282dda4..5fc8726 100644 --- a/chrome/browser/webauthn/fake_recovery_key_store.cc +++ b/chrome/browser/webauthn/fake_recovery_key_store.cc
@@ -15,8 +15,10 @@ #include "base/strings/string_util.h" #include "components/trusted_vault/proto/recovery_key_store.pb.h" #include "components/trusted_vault/proto/vault.pb.h" +#include "crypto/evp.h" #include "crypto/sha2.h" #include "device/fido/enclave/constants.h" +#include "net/cert/asn1_util.h" #include "net/cert/x509_util.h" #include "services/network/public/cpp/data_element.h" #include "services/network/public/cpp/resource_request.h" @@ -306,7 +308,7 @@ R"(<?xml version="1.0" encoding="UTF-8"?> <certificate> <metadata> - <serial>2</serial> + <serial>$3</serial> <creation-time>$1</creation-time> <refresh-interval>2592000</refresh-interval> <previous> @@ -322,7 +324,8 @@ </certificate> )", {base::NumberToString(kTestTime), - base::Base64Encode(endpoint_cert_der_)}, + base::Base64Encode(endpoint_cert_der_), + base::NumberToString(kTestSerialNumber)}, /*offsets=*/nullptr); const auto certs_xml_hash = @@ -382,6 +385,21 @@ return result; } + std::vector<uint8_t> endpoint_public_key_bytes() const override { + std::vector<uint8_t> spki = + crypto::evp::PublicKeyToBytes(endpoint_key_.get()); + std::string_view spki_view = std::string_view( + reinterpret_cast<const char*>(spki.data()), spki.size()); + // Extract the public key from the SPKI. + std::string_view ec_point_oct; + CHECK(net::asn1::ExtractSubjectPublicKeyFromSPKI(spki_view, &ec_point_oct)); + // ExtractSubjectPublicKeyFromSPKI does not remove the initial octet + // encoding the number of unused bits in the ASN.1 BIT STRING so we do it + // here. The public key is always byte-aligned. + ec_point_oct.remove_prefix(1); + return std::vector<uint8_t>(ec_point_oct.begin(), ec_point_oct.end()); + } + private: MaybeResponse OnRequest(const network::ResourceRequest& request) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/chrome/browser/webauthn/fake_recovery_key_store.h b/chrome/browser/webauthn/fake_recovery_key_store.h index 56c9485..68ccd899 100644 --- a/chrome/browser/webauthn/fake_recovery_key_store.h +++ b/chrome/browser/webauthn/fake_recovery_key_store.h
@@ -9,6 +9,7 @@ #include <memory> #include <optional> #include <string> +#include <vector> #include "base/containers/span.h" #include "base/functional/callback_forward.h" @@ -27,6 +28,9 @@ // records uploaded vaults like the real service. class FakeRecoveryKeyStore { public: + // The cert.xml serial number. + static constexpr int kTestSerialNumber = 2; + // If present, values of this type contain an HTTP status code (e.g. 200) and // the body of the response. using MaybeResponse = @@ -51,6 +55,9 @@ // Returns the cohort private scalar as 32 bytes in big-endian order. virtual std::array<uint8_t, 32> endpoint_private_key_bytes() const = 0; + + // Returns the cohort public key bytes. + virtual std::vector<uint8_t> endpoint_public_key_bytes() const = 0; }; #endif // CHROME_BROWSER_WEBAUTHN_FAKE_RECOVERY_KEY_STORE_H_
diff --git a/chrome/browser/webauthn/proto/enclave_local_state.proto b/chrome/browser/webauthn/proto/enclave_local_state.proto index 7387f20..03bbbc5 100644 --- a/chrome/browser/webauthn/proto/enclave_local_state.proto +++ b/chrome/browser/webauthn/proto/enclave_local_state.proto
@@ -19,8 +19,17 @@ reserved "generation"; // The PIN, encrypted to the security domain secret. The plaintext is a CBOR - // structure containing the scrypt-hash of the PIN, claim key, plus recovery - // key store counter ID and handle. + // map with the following key-value pairs: + // 1: Scrypt-hash of the PIN. + // 2: Optional: Generation number (deprecated, may not be populated). + // 3: Claim key. + // 4: Vault counter ID. + // 5: Vault handle. + // 6: Optional: cert.xml serial number used at the time of wrapping. + // 7: Optional: Cohort public key. + // + // Keys 6 and 7 may not be populated by all clients, but if one is + // populated, the other is required. bytes wrapped_pin = 1; // A key used to encrypt claimed PIN hashes for validation at the enclave.
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt index e0447e58..3d84b971 100644 --- a/chrome/build/android-arm32.pgo.txt +++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@ -chrome-android32-main-1753703834-34fbca7acfaaef8fb9b9d37ed0a10d5411c487ab-00b4ad128c8a23e002bf0aff55209bf9081634d6.profdata +chrome-android32-main-1753725509-296829b5e1f88c249e884e0c3eb03de3662863eb-678e35b06ff74f481a526cc9285c0d94d472eae1.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt index 398db0bb..63b11310 100644 --- a/chrome/build/android-arm64.pgo.txt +++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@ -chrome-android64-main-1753716491-799971efd1bd9e12ca044c1681675c44a6616f88-752f4528cb0ccb7de727027fbb32d18ba821d75e.profdata +chrome-android64-main-1753735281-d8463cc1f387d461310bf89a6515a2ec7d6f4275-056aa4137ec627a6d936f4c8dd5f8f924283a1d0.profdata
diff --git a/chrome/build/android-desktop-arm64.pgo.txt b/chrome/build/android-desktop-arm64.pgo.txt index 402677d..a023127 100644 --- a/chrome/build/android-desktop-arm64.pgo.txt +++ b/chrome/build/android-desktop-arm64.pgo.txt
@@ -1 +1 @@ -chrome-android-desktop-arm64-main-1753667744-9f9c3ae807048e15073d54749495ba1174fc0926-99d7c1fa7439386adcedfd100138623678ec44d9.profdata +chrome-android-desktop-arm64-main-1753724556-49b7b71d9462bf114012c30b7f825baa1d2697f1-dd99ed4d80b6cb2bb77b7933a437baad56e365d6.profdata
diff --git a/chrome/build/android-desktop-x64.pgo.txt b/chrome/build/android-desktop-x64.pgo.txt index 075fe01..279dd9b 100644 --- a/chrome/build/android-desktop-x64.pgo.txt +++ b/chrome/build/android-desktop-x64.pgo.txt
@@ -1 +1 @@ -chrome-android-desktop-x64-main-1753703834-e54ef5b2ea3c9fbfe6876b32731a6d44c52dd667-00b4ad128c8a23e002bf0aff55209bf9081634d6.profdata +chrome-android-desktop-x64-main-1753725509-e71bcaa545a56de548946dc010ce0905fe72ff3e-678e35b06ff74f481a526cc9285c0d94d472eae1.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index a207219e..b580836 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1753711104-17f4f6cbf74fd276367fbc9f51557a7d09f548ef-8b0dff453d92ff7c5ce7847f8300e9bd5e4d68b9.profdata +chrome-mac-arm-main-1753718100-d9d213ba985fb4b1e29be09058b5977f4cbe8621-a6fbb461fbe3f28e30ffc7fdaac36fd57d27047d.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index 2aea319..57cbf230 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-main-1753703834-cc323e91beca6aa30289bf2498daa6d41ba276c0-00b4ad128c8a23e002bf0aff55209bf9081634d6.profdata +chrome-mac-main-1753725509-0dd45913ac86e079b9f62f131a6964236c117e0d-678e35b06ff74f481a526cc9285c0d94d472eae1.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt index 5849624..ef4d1d7 100644 --- a/chrome/build/win-arm64.pgo.txt +++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@ -chrome-win-arm64-main-1753703834-dfcf0cddf790c7d94fd15f3a16086bb0f31559d3-00b4ad128c8a23e002bf0aff55209bf9081634d6.profdata +chrome-win-arm64-main-1753725509-c5a1dd0506bb5dd15be2379572c3135c62fda59e-678e35b06ff74f481a526cc9285c0d94d472eae1.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index 455f1f5..dcdce01 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1753703834-81e4dfea4489bf53f9e4e2d1d4d8b2112180c466-00b4ad128c8a23e002bf0aff55209bf9081634d6.profdata +chrome-win32-main-1753714784-6d800008452c6c246d1b7bb312352792c70c3c8c-ceedd8a3c64d996d6841322a94b09f4968d5999a.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 420c40e..10477e7b 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1753693180-f3e7da45b03318648a0cd63dde8b5bc0184ef076-0f3347ef628389435b51b460cd3952c5b277c83c.profdata +chrome-win64-main-1753714784-b13e0f69be83ed71087a61c59e5fc32dbe752475-ceedd8a3c64d996d6841322a94b09f4968d5999a.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc index 0dfaa87..da8fa0e 100644 --- a/chrome/common/chrome_features.cc +++ b/chrome/common/chrome_features.cc
@@ -346,7 +346,7 @@ BASE_FEATURE(kGlicActor, "GlicActor", base::FEATURE_ENABLED_BY_DEFAULT); // Controls whether the Actor UI components are enabled. -BASE_FEATURE(kGlicActorUi, "GlicActorUi", base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kGlicActorUi, "GlicActorUi", base::FEATURE_ENABLED_BY_DEFAULT); const char kGlicActorUiTaskIconName[] = "glic-actor-ui-task-icon"; const char kGlicActorUiOverlayName[] = "glic-actor-ui-overlay";
diff --git a/chrome/common/extensions/api/api_sources.gni b/chrome/common/extensions/api/api_sources.gni index 8c9ff51..6fdd848 100644 --- a/chrome/common/extensions/api/api_sources.gni +++ b/chrome/common/extensions/api/api_sources.gni
@@ -35,6 +35,7 @@ # Despite the name, these APIs do not rely on any WebRTC-specific bits and # as such do not belong in a conditional. + "webrtc_audio_private.idl", "webrtc_logging_private.idl", "webstore_private.json", "windows.json", @@ -91,7 +92,6 @@ # Despite the name, these APIs do not rely on any WebRTC-specific bits and # as such do not belong in a conditional. - "webrtc_audio_private.idl", "webrtc_desktop_capture_private.idl", ]
diff --git a/chrome/common/extensions/api/autofill_private.idl b/chrome/common/extensions/api/autofill_private.idl index 1c492cb..5e200638 100644 --- a/chrome/common/extensions/api/autofill_private.idl +++ b/chrome/common/extensions/api/autofill_private.idl
@@ -60,7 +60,6 @@ CREDIT_CARD_TYPE, CREDIT_CARD_VERIFICATION_CODE, COMPANY_NAME, - FIELD_WITH_DEFAULT_VALUE, MERCHANT_EMAIL_SIGNUP, MERCHANT_PROMO_CODE, PASSWORD,
diff --git a/chrome/common/extensions/api/pdf_viewer_private.idl b/chrome/common/extensions/api/pdf_viewer_private.idl index 0de5d9f6..372eeab 100644 --- a/chrome/common/extensions/api/pdf_viewer_private.idl +++ b/chrome/common/extensions/api/pdf_viewer_private.idl
@@ -6,6 +6,15 @@ // functionality that the PDF Viewer needs from outside the PDF plugin. This API // is exclusively for the PDF Viewer. namespace pdfViewerPrivate { + // Must match `SaveRequestType` in `pdf/pdf_view_web_plugin.h` and + // `tools/typescript/definitions/pdf_viewer_private.d.ts`. + enum SaveRequestType { + ANNOTATION, + ORIGINAL, + EDITED, + SEARCHIFIED + }; + // Nearly identical to mimeHandlerPrivate.StreamInfo, but without a mime type // nor a response header field. Those fields are unused by the PDF viewer. dictionary StreamInfo { @@ -52,6 +61,11 @@ DOMString url, IsAllowedLocalFileAccessCallback callback); + // Sends a request to save the PDF to Google Drive. + static void saveToDrive( + SaveRequestType saveRequestType, + optional VoidCallback callback); + // Sets the current tab title to `title` for a full-page PDF. static void setPdfDocumentTitle( DOMString title,
diff --git a/chrome/enterprise_companion/installer_win.cc b/chrome/enterprise_companion/installer_win.cc index 097e2aa..a3039b3 100644 --- a/chrome/enterprise_companion/installer_win.cc +++ b/chrome/enterprise_companion/installer_win.cc
@@ -55,7 +55,7 @@ // shouldn't be replaced. return base::File(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE | - base::File::FLAG_WIN_SHARE_DELETE) + base::File::FLAG_WIN_EXCLUSIVE_WRITE) .IsValid(); }()) { return true;
diff --git a/chrome/renderer/actor/click_tool.cc b/chrome/renderer/actor/click_tool.cc index 5452e4c..92dea7cd 100644 --- a/chrome/renderer/actor/click_tool.cc +++ b/chrome/renderer/actor/click_tool.cc
@@ -84,6 +84,10 @@ } } + journal_->Log( + task_id_, "ClickTool::Execute", + absl::StrFormat("Dispatching click at point %s", click_point.ToString())); + mojom::ActionResultPtr result = CreateAndDispatchClick( button, click_count, click_point, frame_->GetWebFrame()->FrameWidget()); std::move(callback).Run(std::move(result));
diff --git a/chrome/renderer/actor/scroll_tool.cc b/chrome/renderer/actor/scroll_tool.cc index 405813a..0a8ceaa 100644 --- a/chrome/renderer/actor/scroll_tool.cc +++ b/chrome/renderer/actor/scroll_tool.cc
@@ -69,6 +69,14 @@ targeting_smooth_scroller_ = scrolling_element.HasScrollBehaviorSmooth(); + journal_->Log( + task_id_, "ScrollTool::Execute", + absl::StrFormat("Scrolling element %s from %s by offset %s (CSS " + "pixels). Smooth scroll: %v.", + base::ToString(scrolling_element), + start_offset_css.ToString(), offset_css.ToString(), + targeting_smooth_scroller_)); + std::move(callback).Run( did_scroll ? MakeOkResult()
diff --git a/chrome/renderer/actor/type_tool.cc b/chrome/renderer/actor/type_tool.cc index 8dc40e6..af8d69f 100644 --- a/chrome/renderer/actor/type_tool.cc +++ b/chrome/renderer/actor/type_tool.cc
@@ -302,12 +302,19 @@ // Injecting a click to get focus. gfx::PointF coordinate = validated_result->target; + journal_->Log( + task_id_, "TypeTool::Execute", + absl::StrFormat("Click to focus on %s", base::ToString(coordinate))); mojom::ActionResultPtr click_result = CreateAndDispatchClick(blink::WebMouseEvent::Button::kLeft, 1, coordinate, frame_->GetWebFrame()->FrameWidget()); // Cancel rest of typing if initial click failed. if (!IsOk(*click_result)) { + journal_->Log( + task_id_, "TypeTool::Execute", + absl::StrFormat("Initial click to focus target failed. Reason: %s", + click_result->message)); std::move(callback).Run(std::move(click_result)); return; } @@ -324,8 +331,16 @@ // TypeAction modes don't make sense. WebElement focused = frame_->GetWebFrame()->GetDocument().FocusedElement(); if (focused && focused.IsEditable()) { + journal_->Log( + task_id_, "TypeTool::Execute", + absl::StrFormat("Focused element is now %s", base::ToString(focused))); PrepareTargetForMode(*frame_->GetWebFrame(), action_->mode); } else { + journal_->Log( + task_id_, "TypeTool::Execute", + absl::StrFormat( + "Target %s is not editable. Typing will proceed without clearing.", + base::ToString(focused))); // TODO(crbug.com/421133798): If the target isn't editable, the existing // TypeAction modes don't make sense. ACTOR_LOG() << "Warning: TypeAction::Mode cannot be applied when targeting " @@ -344,6 +359,10 @@ std::move(callback).Run(MakeOkResult()); } else { + journal_->Log(task_id_, "TypeTool::Execute", + absl::StrFormat( + "Use incremental typing with %s delay", + base::ToString(features::kGlicActorKeyUpDuration.Get()))); task_runner_ = base::SequencedTaskRunner::GetCurrentDefault(); target_and_keys_ = std::move(validated_result).value(); task_runner_->PostDelayedTask( @@ -451,6 +470,9 @@ for (char c : action_->text) { std::optional<KeyParams> params = GetKeyParamsForChar(c); if (!params.has_value()) { + journal_->Log( + task_id_, "TypeTool::Validate", + absl::StrFormat("Failed to map character '%c' to a key event.", c)); return base::unexpected( MakeResult(mojom::ActionResultCode::kTypeFailedMappingCharToKey, absl::StrFormat("Failed on char[%c]", c)));
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index c0dd240..6fa7105 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -4487,7 +4487,9 @@ "../browser/apps/platform_apps/api/sync_file_system/sync_file_system_browsertest.cc", "../browser/apps/platform_apps/app_browsertest.cc", "../browser/apps/platform_apps/app_speech_recognition_browsertest.cc", + "../browser/apps/platform_apps/app_window_apitest.cc", "../browser/apps/platform_apps/app_window_browsertest.cc", + "../browser/apps/platform_apps/app_window_frame_browsertest.cc", "../browser/apps/platform_apps/audio_focus_web_contents_observer_browsertest.cc", "../browser/apps/platform_apps/event_page_browsertest.cc", "../browser/apps/platform_apps/platform_app_navigation_redirector_browsertest.cc", @@ -4508,6 +4510,8 @@ "../browser/extensions/api/autofill_private/autofill_private_api_unittest.cc", "../browser/extensions/api/autofill_private/autofill_private_apitest.cc", "../browser/extensions/api/autofill_private/autofill_util_unittest.cc", + "../browser/extensions/api/bluetooth/bluetooth_apitest.cc", + "../browser/extensions/api/bluetooth/bluetooth_private_apitest.cc", "../browser/extensions/api/braille_display_private/braille_display_private_apitest.cc", "../browser/extensions/api/command_line_private/command_line_private_apitest.cc", "../browser/extensions/api/content_settings/content_settings_apitest.cc", @@ -4791,6 +4795,7 @@ sources += [ "../browser/extensions/api/automation/automation_apitest.cc", "../browser/extensions/api/image_writer_private/image_writer_private_apitest.cc", + "../browser/ui/extensions/extension_install_blocked_dialog_browsertest.cc", "../browser/ui/extensions/extension_uninstall_dialog_impl_browsertest.cc", ] @@ -4915,11 +4920,6 @@ # tabs_test.cc, app_browsertest.cc, host_zoom_map_browsertest.cc and ats_browsertest.cc. deps += [ "//chrome/browser/ui/zoom" ] - # TODO(rockot) bug 505926: The chrome_extensions_browsertests_sources - # target should be deleted and this line removed. See the - # chrome_extensions_browsertests_sources target for more. - deps += [ "//extensions:chrome_extensions_browsertests_sources" ] - data += [ "//chrome/test/data/extensions/", "//extensions/test/data/", @@ -4993,7 +4993,6 @@ "../browser/ui/views/extensions/dialogs/reload_page_dialog_browsertest.cc", "../browser/ui/views/extensions/dialogs/settings_overridden_dialog_browsertest.cc", "../browser/ui/views/extensions/dialogs/upload_extension_to_account_dialog_browsertest.cc", - "../browser/ui/views/extensions/extension_install_blocked_dialog_view_browsertest.cc", "../browser/ui/views/extensions/extension_install_dialog_view_browsertest.cc", "../browser/ui/views/extensions/extension_install_friction_dialog_view_browsertest.cc", "../browser/ui/views/extensions/extension_installed_bubble_view_browsertest.cc", @@ -6325,6 +6324,7 @@ "../browser/custom_handlers/chrome_protocol_handler_registry_unittest.cc", "../browser/data_sharing/data_sharing_navigation_throttle_unittest.cc", "../browser/data_sharing/data_sharing_service_factory_unittest.cc", + "../browser/data_sharing/personal_collaboration_data/personal_collaboration_data_service_factory_unittest.cc", "../browser/digital_credentials/digital_identity_low_risk_origins_unittest.cc", "../browser/download/chrome_download_manager_delegate_unittest.cc", "../browser/download/deferred_client_wrapper_unittest.cc", @@ -6980,6 +6980,7 @@ "//components/browsing_topics", "//components/captive_portal/content", "//components/captive_portal/core:buildflags", + "//components/cbor", "//components/certificate_transparency", "//components/certificate_transparency:proto", "//components/collaboration:test_support",
diff --git a/chrome/test/data/android/display_cutout/OWNERS b/chrome/test/data/android/display_cutout/OWNERS new file mode 100644 index 0000000..825f537 --- /dev/null +++ b/chrome/test/data/android/display_cutout/OWNERS
@@ -0,0 +1 @@ +file://chrome/android/java/src/org/chromium/chrome/browser/display_cutout/OWNERS \ No newline at end of file
diff --git a/chrome/test/data/pdf/BUILD.gn b/chrome/test/data/pdf/BUILD.gn index 7f6fd73..17a58c6 100644 --- a/chrome/test/data/pdf/BUILD.gn +++ b/chrome/test/data/pdf/BUILD.gn
@@ -106,6 +106,7 @@ "//tools/typescript/definitions/chrome_test.d.ts", "//tools/typescript/definitions/metrics_private.d.ts", "//tools/typescript/definitions/mime_handler_private.d.ts", + "//tools/typescript/definitions/pdf_viewer_private.d.ts", "//tools/typescript/definitions/pending.d.ts", ] if (enable_ink) {
diff --git a/chrome/test/data/pdf/annotations_feature_enabled_test.ts b/chrome/test/data/pdf/annotations_feature_enabled_test.ts index ce57de1..8f4a977e 100644 --- a/chrome/test/data/pdf/annotations_feature_enabled_test.ts +++ b/chrome/test/data/pdf/annotations_feature_enabled_test.ts
@@ -3,7 +3,7 @@ // found in the LICENSE file. import type {AnnotationTool, ViewerInkHostElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; -import {AnnotationMode, SaveRequestType} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; +import {AnnotationMode} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; import {assert} from 'chrome://resources/js/assert.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; @@ -13,6 +13,7 @@ window.onunhandledrejection = e => chrome.test.fail(e.reason); const viewer = document.body.querySelector('pdf-viewer')!; +const SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; function animationFrame(): Promise<void> { return new Promise(resolve => requestAnimationFrame(() => resolve()));
diff --git a/chrome/test/data/pdf/download_controls_test.ts b/chrome/test/data/pdf/download_controls_test.ts index 828212c..fedcc40 100644 --- a/chrome/test/data/pdf/download_controls_test.ts +++ b/chrome/test/data/pdf/download_controls_test.ts
@@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {SaveRequestType} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; import {eventToPromise} from 'chrome://webui-test/test_util.js'; +const SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; +type SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; + const tests = [ /** * Test that the toolbar shows an option to download the edited PDF if
diff --git a/chrome/test/data/pdf/ink2_save_test.ts b/chrome/test/data/pdf/ink2_save_test.ts index 8281558..088d5fe 100644 --- a/chrome/test/data/pdf/ink2_save_test.ts +++ b/chrome/test/data/pdf/ink2_save_test.ts
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {AnnotationMode, PluginController, PluginControllerEventType, SaveRequestType, UserAction} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; +import {AnnotationMode, PluginController, PluginControllerEventType, UserAction} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; import {assert} from 'chrome://resources/js/assert.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import {eventToPromise, isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js'; @@ -14,6 +14,8 @@ const controller = PluginController.getInstance(); const mockPlugin = setupTestMockPluginForInk(); const mockMetricsPrivate = setupMockMetricsPrivate(); +const SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; +type SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; function getDownloadControls() { return getRequiredElement(viewerToolbar, 'viewer-download-controls');
diff --git a/chrome/test/data/pdf/save_controls_mixin_test.ts b/chrome/test/data/pdf/save_controls_mixin_test.ts index 0e6095b..e22e534 100644 --- a/chrome/test/data/pdf/save_controls_mixin_test.ts +++ b/chrome/test/data/pdf/save_controls_mixin_test.ts
@@ -2,12 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {SaveRequestType, ViewerSaveControlsMixin} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; +import {ViewerSaveControlsMixin} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; import type {CrActionMenuElement, CrIconButtonElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; import {CrLitElement, html} from 'chrome://resources/lit/v3_0/lit.rollup.js'; import {eventToPromise} from 'chrome://webui-test/test_util.js'; +const SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; const TestElementBase = ViewerSaveControlsMixin(CrLitElement); +type SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; interface TestElement { $: {
diff --git a/chrome/test/data/pdf/save_to_drive_controls_test.ts b/chrome/test/data/pdf/save_to_drive_controls_test.ts index e565712..92bc4ba 100644 --- a/chrome/test/data/pdf/save_to_drive_controls_test.ts +++ b/chrome/test/data/pdf/save_to_drive_controls_test.ts
@@ -4,9 +4,11 @@ import 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; -import {SaveRequestType} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js'; +const SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; +type SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; + const tests = [ /** * Test that the toolbar shows an option to save the edited PDF to Google
diff --git a/chrome/test/data/pdf/test_util.ts b/chrome/test/data/pdf/test_util.ts index ae75534a..6153c1f3 100644 --- a/chrome/test/data/pdf/test_util.ts +++ b/chrome/test/data/pdf/test_util.ts
@@ -9,7 +9,7 @@ import {resetForTesting as resetMetricsForTesting, UserAction, Viewport} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; // <if expr="enable_pdf_ink2"> import type {AnnotationBrush, BeforeUnloadProxy, InkBrushSelectorElement, InkColorSelectorElement, InkSizeSelectorElement, SelectableIconButtonElement, ViewerBottomToolbarDropdownElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; -import {AnnotationBrushType, BeforeUnloadProxyImpl, DEFAULT_TEXTBOX_WIDTH, MIN_TEXTBOX_SIZE_PX, hexToColor, Ink2Manager, TEXT_COLORS, TextAlignment, TextStyle, PluginController, PluginControllerEventType, SaveRequestType} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; +import {AnnotationBrushType, BeforeUnloadProxyImpl, DEFAULT_TEXTBOX_WIDTH, MIN_TEXTBOX_SIZE_PX, hexToColor, Ink2Manager, TEXT_COLORS, TextAlignment, TextStyle, PluginController, PluginControllerEventType} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js'; // </if> import {assert} from 'chrome://resources/js/assert.js'; import {CrLitElement, html} from 'chrome://resources/lit/v3_0/lit.rollup.js'; @@ -19,6 +19,9 @@ import {eventToPromise, isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js'; // clang-format on +const SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; +type SaveRequestType = chrome.pdfViewerPrivate.SaveRequestType; + export class MockElement { dir: string = ''; offsetWidth: number;
diff --git a/chrome/test/data/webui/cr_elements/cr_tooltip_test.ts b/chrome/test/data/webui/cr_elements/cr_tooltip_test.ts index f56470b..6e111d3 100644 --- a/chrome/test/data/webui/cr_elements/cr_tooltip_test.ts +++ b/chrome/test/data/webui/cr_elements/cr_tooltip_test.ts
@@ -103,6 +103,36 @@ assertTrue(tooltip.$.tooltip.hidden); }); + test('can hover over tooltip', async () => { + const text = parent.shadowRoot.querySelector<HTMLElement>('#tooltip-text'); + assertTrue(!!text); + text.textContent = 'test'; + tooltip.show(); + await eventToPromise('animationend', tooltip.$.tooltip); + assertFalse(tooltip.$.tooltip.hidden); + + // Pointer leaves the target, but not enough time has passed to hide the + // tooltip. + const hideDelayMs = 100; + tooltip.hideDelay = hideDelayMs; + parent.dispatchEvent( + new CustomEvent('pointerleave', {bubbles: true, composed: true})); + await new Promise(resolve => setTimeout(resolve, hideDelayMs / 2)); + assertFalse(tooltip.$.tooltip.hidden); + + // Pointer enters the tooltip, which cancels the hide. + tooltip.dispatchEvent( + new CustomEvent('pointerenter', {bubbles: true, composed: true})); + await new Promise(resolve => setTimeout(resolve, hideDelayMs)); + assertFalse(tooltip.$.tooltip.hidden); + + // Pointer leaves tooltip, which should eventually hide the tooltip. + tooltip.dispatchEvent( + new CustomEvent('pointerleave', {bubbles: true, composed: true})); + await eventToPromise('animationend', tooltip.$.tooltip); + assertTrue(tooltip.$.tooltip.hidden); + }); + test('positioning', async () => { const text = parent.shadowRoot.querySelector<HTMLElement>('#tooltip-text'); assertTrue(!!text);
diff --git a/chrome/test/data/webui/glic/api_test.ts b/chrome/test/data/webui/glic/api_test.ts index 24d00cb..57db22d 100644 --- a/chrome/test/data/webui/glic/api_test.ts +++ b/chrome/test/data/webui/glic/api_test.ts
@@ -7,7 +7,7 @@ // --gn_target chrome/test/data/webui/glic:build_ts import {HostCapability, ScrollToErrorReason, WebClientMode} from '/glic/glic_api/glic_api.js'; -import type {FocusedTabData, GlicBrowserHost, GlicHostRegistry, GlicWebClient, Observable, OpenPanelInfo, PanelOpeningData, ScrollToError, Subscriber, UserProfileInfo, ZeroStateSuggestionsV2} from '/glic/glic_api/glic_api.js'; +import type {FocusedTabData, GetPinCandidatesOptions, GlicBrowserHost, GlicHostRegistry, GlicWebClient, Observable, OpenPanelInfo, PanelOpeningData, ScrollToError, Subscriber, UserProfileInfo, ZeroStateSuggestionsV2} from '/glic/glic_api/glic_api.js'; import {ObservableValue} from '/glic/observable.js'; import {createGlicHostRegistryOnLoad} from './api_boot.js'; @@ -45,6 +45,7 @@ this.current = await waitFor(this.getSignal(this.readIndex++).promise); return this.current; } + /** Returns true if all values have been read. */ isEmpty(): boolean { return this.readIndex === this.writeIndex; } @@ -1157,6 +1158,56 @@ this.capabilitiesToString(Array.from(capabilities))}`); } + // Test getPinCandidates() in some different scenarios where there is a single + // browser tab. + async testGetPinCandidatesSingleTab() { + assertTrue(!!this.host.pinTabs); + assertTrue(!!this.host.getPinCandidates); + + // Gets pinned candidates and asserts that their comma-separated titles + // equal `expected`. + const getCandidatesEquals = + async (options: GetPinCandidatesOptions, expected: string) => { + const sequence = observeSequence(this.host.getPinCandidates!(options)); + const candidates = await sequence.next(); + sequence.unsubscribe(); + assertEquals(candidates.map(c => c.tabData.title).join(', '), expected); + }; + + await getCandidatesEquals({maxCandidates: 1}, 'Test Page'); + await getCandidatesEquals({maxCandidates: 1, query: 'zxyzyz'}, 'Test Page'); + await getCandidatesEquals( + {maxCandidates: 1, query: 'Test Page'}, 'Test Page'); + await getCandidatesEquals({maxCandidates: 0}, ''); + + + // Test some races. + + // 1. Calling getPinCandidates a second time will reset the first + // observable. We should receive nothing from it. + let racedSequence = + observeSequence(this.host.getPinCandidates!({maxCandidates: 1})); + await getCandidatesEquals({maxCandidates: 1}, 'Test Page'); + assertTrue(racedSequence.isEmpty()); + racedSequence.unsubscribe(); + + // 2. Unsubscribing the obsolete observable should do nothing to the new + // one. + racedSequence = + observeSequence(this.host.getPinCandidates!({maxCandidates: 1})); + const racedSequence2 = + observeSequence(this.host.getPinCandidates!({maxCandidates: 1})); + racedSequence.unsubscribe(); + assertEquals(1, (await racedSequence2.next()).length); + + // Pin the current focus. A pinned tab isn't a valid candidate. + const focus = + await observeSequence(this.host.getFocusedTabStateV2!()).next(); + await this.host.pinTabs([checkDefined(focus.hasFocus?.tabData.tabId)]); + + await getCandidatesEquals({maxCandidates: 1}, ''); + } + async testGetModelQualityClientId() { assertTrue(!!this.host.getModelQualityClientId); const clientId = await this.host.getModelQualityClientId(); @@ -1429,7 +1480,7 @@ // Sets up the test and starts running it. async run(maxTimeoutMs: number, payload: any): Promise<TestResult> { - assertTrue(this.testFound, `Test not found`); + assertTrue(this.testFound, `Test not found: "${this.testName}"`); maxTimeoutEndTime = performance.now() + maxTimeoutMs; console.info(`Running test ${this.testName} with payload ${ JSON.stringify(payload)}`);
diff --git a/chrome/test/data/webui/lens/overlay/test_overlay_browser_proxy.ts b/chrome/test/data/webui/lens/overlay/test_overlay_browser_proxy.ts index 0009454d..c5cab192 100644 --- a/chrome/test/data/webui/lens/overlay/test_overlay_browser_proxy.ts +++ b/chrome/test/data/webui/lens/overlay/test_overlay_browser_proxy.ts
@@ -26,6 +26,7 @@ 'closeRequestedByOverlayCloseButton', 'closeRequestedByOverlayBackgroundClick', 'addBackgroundBlur', + 'setLiveBlur', 'closePreselectionBubble', 'feedbackRequestedByOverlay', 'getOverlayInvocationSource', @@ -65,6 +66,10 @@ this.methodCalled('addBackgroundBlur'); } + setLiveBlur() { + this.methodCalled('setLiveBlur'); + } + closePreselectionBubble() { this.methodCalled('closePreselectionBubble'); }
diff --git a/chrome/test/data/webui/new_tab_page/modules/v2/tab_groups/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/v2/tab_groups/module_test.ts index 6b0fea4..89ab9f0c 100644 --- a/chrome/test/data/webui/new_tab_page/modules/v2/tab_groups/module_test.ts +++ b/chrome/test/data/webui/new_tab_page/modules/v2/tab_groups/module_test.ts
@@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import type {TabGroupsModuleElement} from 'chrome://new-tab-page/lazy_load.js'; +import type {IconContainerElement, TabGroupsModuleElement} from 'chrome://new-tab-page/lazy_load.js'; import {tabGroupsDescriptor, TabGroupsProxyImpl} from 'chrome://new-tab-page/lazy_load.js'; import {PageHandlerRemote} from 'chrome://new-tab-page/tab_groups.mojom-webui.js'; import type {TabGroup} from 'chrome://new-tab-page/tab_groups.mojom-webui.js'; -import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js'; +import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js'; import type {TestMock} from 'chrome://webui-test/test_mock.js'; import {isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js'; @@ -32,21 +32,43 @@ return module; } - test('No module created if no tab groups data', async () => { + test('no module created if no tab groups data', async () => { const module = await createModule([]); assertEquals(null, module); }); - test('creates module', async () => { + test('create module', async () => { // Arrange. const tabGroups: TabGroup[] = [ { title: 'Tab Group 1', - url: {url: 'http://www.google.com/'}, + faviconUrls: [ + {url: 'https://www.google.com'}, + {url: 'https://www.youtube.com'}, + {url: 'https://www.wikipedia.org'}, + {url: 'https://maps.google.com'}, + ], + totalTabCount: 4, }, { title: 'Tab Group 2', - url: {url: 'http://www.google.com/'}, + faviconUrls: [ + {url: 'https://www.google.com'}, + {url: 'https://www.youtube.com'}, + {url: 'https://www.wikipedia.org'}, + {url: 'https://maps.google.com'}, + ], + totalTabCount: 8, + }, + { + title: 'Tab Group 3', + faviconUrls: [ + {url: 'https://www.google.com'}, + {url: 'https://www.youtube.com'}, + {url: 'https://www.wikipedia.org'}, + {url: 'https://maps.google.com'}, + ], + totalTabCount: 188, }, ]; const module = await createModule(tabGroups); @@ -63,16 +85,128 @@ assertTrue(!!groups); assertEquals(tabGroups.length, groups.length); - assertTrue(!!groups[0]); - assertEquals( - 'Tab Group 1', - groups[0].querySelector('.tab-group-title')!.textContent); - assertEquals('http://www.google.com/', groups[0].href); + // Verify tab group 1. + for (let i = 0; i < groups.length; ++i) { + assertEquals( + `Tab Group ${i + 1}`, + groups[i]!.querySelector('.tab-group-title')!.textContent); + const iconContainer = + groups[i]!.querySelector<IconContainerElement>('ntp-icon-container')!; + assertTrue(!!iconContainer); + assertDeepEquals( + tabGroups[i]!.faviconUrls.map(u => u.url), iconContainer.faviconUrls); + assertEquals(tabGroups[i]!.totalTabCount, iconContainer.totalTabCount); + } + }); - assertTrue(!!groups[1]); - assertEquals( - 'Tab Group 2', - groups[1].querySelector('.tab-group-title')!.textContent); - assertEquals('http://www.google.com/', groups[1].href); + function getIconContainerElement( + module: TabGroupsModuleElement, index: number): IconContainerElement { + const groups = + module.shadowRoot.querySelectorAll<HTMLAnchorElement>('.tab-group'); + return groups[index]!.querySelector<IconContainerElement>( + 'ntp-icon-container')!; + } + + test('show empty cells if there are less than four tabs', async () => { + // Arrange. + const module = await createModule([{ + title: 'Tab Group', + faviconUrls: [{url: 'https://www.google.com'}], + totalTabCount: 1, + }]); + + // Assert. + const iconContainer = getIconContainerElement(module, 0); + const cells = iconContainer.shadowRoot.querySelectorAll('.cell'); + assertEquals(4, cells.length); + + const iconCells = iconContainer.shadowRoot.querySelectorAll('.cell.icon'); + const emptyCells = iconContainer.shadowRoot.querySelectorAll('.cell.empty'); + const overflowCells = + iconContainer.shadowRoot.querySelectorAll('.cell.overflow-count'); + assertEquals(1, iconCells.length); + assertEquals(3, emptyCells.length); + assertEquals(0, overflowCells.length); + }); + + test('show four favicons when there are exactly four tabs', async () => { + // Arrange. + const module = await createModule([{ + title: 'Tab Group', + faviconUrls: [ + {url: 'https://www.google.com'}, + {url: 'https://www.youtube.com'}, + {url: 'https://www.wikipedia.org'}, + {url: 'https://maps.google.com'}, + ], + totalTabCount: 4, + }]); + + // Assert. + const iconContainer = getIconContainerElement(module, 0); + const cells = iconContainer.shadowRoot.querySelectorAll('.cell'); + assertEquals(4, cells.length); + + const iconCells = iconContainer.shadowRoot.querySelectorAll('.cell.icon'); + const overflowCells = + iconContainer.shadowRoot.querySelectorAll('.cell.overflow-count'); + assertEquals(4, iconCells.length); + assertEquals(0, overflowCells.length); + }); + + test('show +N when more than one tab remains', async () => { + // Arrange. + const module = await createModule([{ + title: 'Tab Group', + faviconUrls: [ + {url: 'https://www.google.com'}, + {url: 'https://www.youtube.com'}, + {url: 'https://www.wikipedia.org'}, + {url: 'https://maps.google.com'}, + ], + totalTabCount: 8, + }]); + + // Assert. + const iconContainer = getIconContainerElement(module, 0); + const cells = iconContainer.shadowRoot.querySelectorAll('.cell'); + assertEquals(4, cells.length); + + const iconCells = iconContainer.shadowRoot.querySelectorAll('.cell.icon'); + const overflowCells = + iconContainer.shadowRoot.querySelectorAll('.cell.overflow-count'); + assertEquals(3, iconCells.length); + assertEquals(1, overflowCells.length); + + const overflowText = overflowCells[0]!.textContent!.trim(); + assertEquals('+5', overflowText); + }); + + test('caps at 99+ when more than 99 tabs remain', async () => { + // Arrange. + const module = await createModule([{ + title: 'Tab Group', + faviconUrls: [ + {url: 'https://www.google.com'}, + {url: 'https://www.youtube.com'}, + {url: 'https://www.wikipedia.org'}, + {url: 'https://maps.google.com'}, + ], + totalTabCount: 188, + }]); + + // Assert. + const iconContainer = getIconContainerElement(module, 0); + const cells = iconContainer.shadowRoot.querySelectorAll('.cell'); + assertEquals(4, cells.length); + + const iconCells = iconContainer.shadowRoot.querySelectorAll('.cell.icon'); + const overflowCells = + iconContainer.shadowRoot.querySelectorAll('.cell.overflow-count'); + assertEquals(3, iconCells.length); + assertEquals(1, overflowCells.length); + + const overflowText = overflowCells[0]!.textContent!.trim(); + assertEquals('99+', overflowText); }); });
diff --git a/chrome/test/data/webui/settings/people_page_test.ts b/chrome/test/data/webui/settings/people_page_test.ts index 64bacb2..830b19f 100644 --- a/chrome/test/data/webui/settings/people_page_test.ts +++ b/chrome/test/data/webui/settings/people_page_test.ts
@@ -6,40 +6,38 @@ import 'chrome://settings/lazy_load.js'; import {webUIListenerCallback} from 'chrome://resources/js/cr.js'; -// <if expr="not is_chromeos"> -import {listenOnce} from 'chrome://resources/js/util.js'; -// </if> - import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; -// <if expr="not is_chromeos"> -import type {CrCheckboxElement} from 'chrome://settings/lazy_load.js'; -// </if> - import {loadTimeData} from 'chrome://settings/settings.js'; import type {SettingsPeoplePageElement} from 'chrome://settings/settings.js'; import {ProfileInfoBrowserProxyImpl, Router, routes, SignedInState, StatusAction, SyncBrowserProxyImpl} from 'chrome://settings/settings.js'; import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; -// <if expr="not is_chromeos"> -import {assertLT} from 'chrome://webui-test/chai_assert.js'; -import {flushTasks} from 'chrome://webui-test/polymer_test_util.js'; -import {waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js'; - -// </if> import {simulateSyncStatus} from './sync_test_util.js'; -// <if expr="not is_chromeos"> -import {simulateStoredAccounts} from './sync_test_util.js'; -// </if> - import {TestProfileInfoBrowserProxy} from './test_profile_info_browser_proxy.js'; import {TestSyncBrowserProxy} from './test_sync_browser_proxy.js'; +// <if expr="not is_chromeos"> +import {listenOnce} from 'chrome://resources/js/util.js'; +import type {CrCheckboxElement} from 'chrome://settings/lazy_load.js'; +import {assertLT} from 'chrome://webui-test/chai_assert.js'; +import {flushTasks, waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js'; +import type {StoredAccount} from 'chrome://settings/settings.js'; +import { isChildVisible } from 'chrome://webui-test/test_util.js'; + +import {simulateStoredAccounts} from './sync_test_util.js'; +// </if> + // clang-format on let peoplePage: SettingsPeoplePageElement; let profileInfoBrowserProxy: TestProfileInfoBrowserProxy; let syncBrowserProxy: TestSyncBrowserProxy; +function reset() { + peoplePage.remove(); + Router.getInstance().navigateTo(routes.BASIC); +} + suite('ProfileInfoTests', function() { suiteSetup(function() { // <if expr="is_chromeos"> @@ -67,7 +65,7 @@ }); teardown(function() { - peoplePage.remove(); + reset(); }); test('GetProfileInfo', function() { @@ -114,7 +112,7 @@ }); teardown(function() { - peoplePage.remove(); + reset(); }); test('ShowCorrectRows', async function() { @@ -138,9 +136,8 @@ suite('SyncStatusTests', function() { setup(function() { - loadTimeData.overrideValues({ - signinAllowed: true, - }); + loadTimeData.overrideValues( + {signinAllowed: true, replaceSyncPromosWithSignInPromos: false}); syncBrowserProxy = new TestSyncBrowserProxy(); SyncBrowserProxyImpl.setInstance(syncBrowserProxy); @@ -153,7 +150,7 @@ }); teardown(function() { - peoplePage.remove(); + reset(); }); test('Toast', function() { @@ -176,45 +173,31 @@ assertFalse(!!peoplePage.shadowRoot!.querySelector('#profile-row')); // The control element should exist when policy allows. - const accountControl = - peoplePage.shadowRoot!.querySelector('settings-sync-account-control')!; - assertTrue(window.getComputedStyle(accountControl)['display'] !== 'none'); + assertTrue(isChildVisible(peoplePage, 'settings-sync-account-control')); // Control element doesn't exist when policy forbids sync. simulateSyncStatus({ syncSystemEnabled: false, statusAction: StatusAction.NO_ACTION, }); - assertEquals('none', window.getComputedStyle(accountControl)['display']); + assertFalse(isChildVisible(peoplePage, '#manage-google-account')); - const manageGoogleAccount = - peoplePage.shadowRoot!.querySelector('#manage-google-account')!; - - // Do not show Google Account when stored accounts or sync status - // could not be retrieved. - simulateStoredAccounts(undefined); - simulateSyncStatus(undefined); - assertEquals( - 'none', window.getComputedStyle(manageGoogleAccount)['display']); - + // Do not show Google Account when sync status could not be retrieved. simulateStoredAccounts([]); simulateSyncStatus(undefined); - assertEquals( - 'none', window.getComputedStyle(manageGoogleAccount)['display']); - - simulateStoredAccounts(undefined); - simulateSyncStatus({ - statusAction: StatusAction.NO_ACTION, - }); - assertEquals( - 'none', window.getComputedStyle(manageGoogleAccount)['display']); + assertFalse(isChildVisible(peoplePage, '#manage-google-account')); simulateStoredAccounts([]); simulateSyncStatus({ statusAction: StatusAction.NO_ACTION, }); - assertEquals( - 'none', window.getComputedStyle(manageGoogleAccount)['display']); + assertFalse(isChildVisible(peoplePage, '#manage-google-account')); + + simulateStoredAccounts([]); + simulateSyncStatus({ + statusAction: StatusAction.NO_ACTION, + }); + assertFalse(isChildVisible(peoplePage, '#manage-google-account')); // A stored account with sync off but no error should result in the // Google Account being shown. @@ -224,8 +207,7 @@ hasError: false, statusAction: StatusAction.NO_ACTION, }); - assertTrue( - window.getComputedStyle(manageGoogleAccount)['display'] !== 'none'); + assertTrue(isChildVisible(peoplePage, '#manage-google-account')); // A stored account with sync off and error should not result in the // Google Account being shown. @@ -235,8 +217,7 @@ hasError: true, statusAction: StatusAction.NO_ACTION, }); - assertEquals( - 'none', window.getComputedStyle(manageGoogleAccount)['display']); + assertFalse(isChildVisible(peoplePage, '#manage-google-account')); // A stored account with sync on but no error should result in the // Google Account being shown. @@ -246,8 +227,7 @@ hasError: false, statusAction: StatusAction.NO_ACTION, }); - assertTrue( - window.getComputedStyle(manageGoogleAccount)['display'] !== 'none'); + assertTrue(isChildVisible(peoplePage, '#manage-google-account')); // A stored account with sync on but with error should not result in // the Google Account being shown. @@ -257,8 +237,7 @@ hasError: true, statusAction: StatusAction.NO_ACTION, }); - assertEquals( - 'none', window.getComputedStyle(manageGoogleAccount)['display']); + assertFalse(isChildVisible(peoplePage, '#manage-google-account')); }); test('SignOutNavigationNormalProfile', async function() { @@ -435,7 +414,7 @@ }); teardown(function() { - peoplePage.remove(); + reset(); }); test('ShowCorrectSyncRow', function() { @@ -454,3 +433,156 @@ assertEquals(Router.getInstance().getCurrentRoute(), routes.SYNC); }); }); + +// <if expr="not is_chromeos"> +suite('PeoplePageAccountSettings', function() { + setup(function() { + loadTimeData.overrideValues( + {signinAllowed: true, replaceSyncPromosWithSignInPromos: true}); + + syncBrowserProxy = new TestSyncBrowserProxy(); + SyncBrowserProxyImpl.setInstance(syncBrowserProxy); + + profileInfoBrowserProxy = new TestProfileInfoBrowserProxy(); + ProfileInfoBrowserProxyImpl.setInstance(profileInfoBrowserProxy); + + document.body.innerHTML = window.trustedTypes!.emptyHTML; + peoplePage = document.createElement('settings-people-page'); + document.body.appendChild(peoplePage); + }); + + teardown(function() { + reset(); + }); + + async function simulateSignedInState( + state: SignedInState, accounts: StoredAccount[]) { + await syncBrowserProxy.whenCalled('getSyncStatus'); + simulateSyncStatus({ + signedInState: state, + syncSystemEnabled: true, + hasError: false, + statusAction: StatusAction.NO_ACTION, + }); + await flush(); + + await syncBrowserProxy.whenCalled('getStoredAccounts'); + simulateStoredAccounts(accounts); + return flush(); + } + + test('ShowCorrectRowsSignedIn', async function() { + await simulateSignedInState( + SignedInState.SIGNED_IN, [{email: 'foo@foo.com'}]); + + // The account card and the profile should not exist. Instead, there is a + // link row which leads to the account settings page. + assertFalse(isChildVisible(peoplePage, 'settings-sync-account-control')); + assertFalse(isChildVisible(peoplePage, '#profile-row')); + assertTrue(isChildVisible(peoplePage, '#account-subpage-row')); + + // The other rows are shown correctly. + assertTrue(isChildVisible(peoplePage, '#sync-setup')); + assertTrue(isChildVisible(peoplePage, '#edit-profile')); + assertTrue(isChildVisible(peoplePage, '#manage-google-account')); + assertTrue(isChildVisible(peoplePage, '#importDataDialogTrigger')); + }); + + test('ShowCorrectRowsSignedOut', async function() { + await simulateSignedInState(SignedInState.SIGNED_OUT, []); + + // The first item should be an account card. + assertTrue(isChildVisible(peoplePage, 'settings-sync-account-control')); + assertFalse(isChildVisible(peoplePage, '#profile-row')); + assertFalse(isChildVisible(peoplePage, '#account-subpage-row')); + + // The other rows are shown correctly. + assertTrue(isChildVisible(peoplePage, '#sync-setup')); + assertTrue(isChildVisible(peoplePage, '#edit-profile')); + assertFalse(isChildVisible(peoplePage, '#manage-google-account')); + assertTrue(isChildVisible(peoplePage, '#importDataDialogTrigger')); + }); + + test('ShowCorrectRowsSyncing', async function() { + await simulateSignedInState( + SignedInState.SYNCING, [{email: 'foo@foo.com'}]); + + // The first item should be an account card. + assertTrue(isChildVisible(peoplePage, 'settings-sync-account-control')); + assertFalse(isChildVisible(peoplePage, '#profile-row')); + assertFalse(isChildVisible(peoplePage, '#account-subpage-row')); + + // The other rows are shown correctly. + assertTrue(isChildVisible(peoplePage, '#sync-setup')); + assertTrue(isChildVisible(peoplePage, '#edit-profile')); + assertTrue(isChildVisible(peoplePage, '#manage-google-account')); + assertTrue(isChildVisible(peoplePage, '#importDataDialogTrigger')); + }); + + test('ShowCorrectRowsSignInPending', async function() { + await simulateSignedInState( + SignedInState.SIGNED_IN_PAUSED, [{email: 'foo@foo.com'}]); + + // The first item should be an account card. + assertTrue(isChildVisible(peoplePage, 'settings-sync-account-control')); + assertFalse(isChildVisible(peoplePage, '#profile-row')); + assertFalse(isChildVisible(peoplePage, '#account-subpage-row')); + + // The other rows are shown correctly. + assertTrue(isChildVisible(peoplePage, '#sync-setup')); + assertTrue(isChildVisible(peoplePage, '#edit-profile')); + assertTrue(isChildVisible(peoplePage, '#manage-google-account')); + assertTrue(isChildVisible(peoplePage, '#importDataDialogTrigger')); + }); + + test('ShowCorrectRowsWebSignedIn', async function() { + await simulateSignedInState( + SignedInState.WEB_ONLY_SIGNED_IN, [{email: 'foo@foo.com'}]); + + // The first item should be an account card. + assertTrue(isChildVisible(peoplePage, 'settings-sync-account-control')); + assertFalse(isChildVisible(peoplePage, '#profile-row')); + assertFalse(isChildVisible(peoplePage, '#account-subpage-row')); + + // The other rows are shown correctly. + assertTrue(isChildVisible(peoplePage, '#sync-setup')); + assertTrue(isChildVisible(peoplePage, '#edit-profile')); + assertTrue(isChildVisible(peoplePage, '#manage-google-account')); + assertTrue(isChildVisible(peoplePage, '#importDataDialogTrigger')); + }); + + test('ClickingAccountLinkRowLeadsToAccountSettings', async function() { + await simulateSignedInState( + SignedInState.SIGNED_IN, [{email: 'foo@foo.com'}]); + + peoplePage.shadowRoot!.querySelector<HTMLElement>( + '#account-subpage-row')!.click(); + assertEquals(routes.ACCOUNT, Router.getInstance().getCurrentRoute()); + }); + + test('AccountLinkRowHasAccountInfo', async function() { + const expectedAccount = { + fullName: 'Test Name', + email: 'test@email.com', + avatarImage: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAA' + + 'AAABAAEAAAICTAEAOw==', + }; + await simulateSignedInState(SignedInState.SIGNED_IN, [expectedAccount]); + + const accountName = + peoplePage.shadowRoot!.querySelector( + '#account-name')!.textContent!.trim(); + const accountEmail = + peoplePage.shadowRoot!.querySelector( + '#account-email')!.textContent!.trim(); + + assertEquals(expectedAccount.fullName, accountName); + assertEquals(expectedAccount.email, accountEmail); + + const bgImage = + peoplePage.shadowRoot!.querySelector<HTMLElement>( + '#profile-icon')!.style.backgroundImage; + assertTrue(bgImage.includes(expectedAccount.avatarImage)); + }); +}); +// </if>
diff --git a/chrome/test/interaction/interactive_browser_test_interactive_uitest.cc b/chrome/test/interaction/interactive_browser_test_interactive_uitest.cc index bb9ed962..dafff713 100644 --- a/chrome/test/interaction/interactive_browser_test_interactive_uitest.cc +++ b/chrome/test/interaction/interactive_browser_test_interactive_uitest.cc
@@ -57,10 +57,6 @@ #include "ui/views/widget/widget_observer.h" #include "url/gurl.h" -#if BUILDFLAG(IS_WIN) -#include "base/win/windows_version.h" -#endif - namespace { constexpr char kDocumentWithNamedElement[] = "/select.html"; constexpr char kDocumentWithTitle[] = "/title3.html"; @@ -89,49 +85,41 @@ } }; -// TODO(https://crbug.com/432623498): Times out on windows rel. -#if BUILDFLAG(IS_WIN) -#define MAYBE_PressButtonAndMouseMoveClick DISABLED_PressButtonAndMouseMoveClick -#else -#define MAYBE_PressButtonAndMouseMoveClick PressButtonAndMouseMoveClick -#endif IN_PROC_BROWSER_TEST_F(InteractiveBrowserTestUiTest, - MAYBE_PressButtonAndMouseMoveClick) { + PressButtonAndMouseMoveClick) { RelativePositionSpecifier pos = CenterPoint(); #if BUILDFLAG(IS_WIN) - if (base::win::OSInfo::GetInstance()->version() < base::win::Version::WIN11) { - // Handler for http://crbug.com/392854216 (menu may overlap button). - pos = base::BindOnce([](ui::TrackedElement* el) { - gfx::Rect bounds = el->GetScreenBounds(); - auto* const menu_item = - ui::ElementTracker::GetElementTracker()->GetElementInAnyContext( - AppMenuModel::kMoreToolsMenuItem); - const gfx::Rect widget_bounds = - menu_item->AsA<views::TrackedElementViews>() - ->view() - ->GetWidget() - ->GetWindowBoundsInScreen(); + // Handler for http://crbug.com/392854216 and https://crbug.com/432623498 + // (menu may overlap button). + pos = base::BindOnce([](ui::TrackedElement* el) { + gfx::Rect bounds = el->GetScreenBounds(); + auto* const menu_item = + ui::ElementTracker::GetElementTracker()->GetElementInAnyContext( + AppMenuModel::kMoreToolsMenuItem); + const gfx::Rect widget_bounds = menu_item->AsA<views::TrackedElementViews>() + ->view() + ->GetWidget() + ->GetWindowBoundsInScreen(); - // Create a rectangle where all points are strictly inside the original - // bounds. - bounds.Inset(gfx::Insets::TLBR(1, 1, 2, 2)); + // Create a rectangle where all points are strictly inside the original + // bounds. + bounds.Inset(gfx::Insets::TLBR(1, 1, 2, 2)); - // Test points around the rectangle to find one that does not intersect - // the menu widget. - for (const auto& point : - {bounds.CenterPoint(), bounds.bottom_center(), bounds.left_center(), - bounds.right_center(), bounds.origin(), bounds.top_right(), - bounds.bottom_right(), bounds.bottom_left()}) { - if (!widget_bounds.Contains(point)) { - return point; - } + // Test points around the rectangle to find one that does not intersect + // the menu widget. + for (const auto& point : + {bounds.CenterPoint(), bounds.bottom_center(), bounds.left_center(), + bounds.right_center(), bounds.origin(), bounds.top_right(), + bounds.bottom_right(), bounds.bottom_left()}) { + if (!widget_bounds.Contains(point)) { + return point; } + } - NOTREACHED() << "Menu widget ()" << widget_bounds.ToString() - << ") significantly overlaps menu button (" - << bounds.ToString() << ") cannot target button."; - }); - } + NOTREACHED() << "Menu widget ()" << widget_bounds.ToString() + << ") significantly overlaps menu button (" + << bounds.ToString() << ") cannot target button."; + }); #endif RunTestSequence(
diff --git a/chrome/updater/external_constants_override.h b/chrome/updater/external_constants_override.h index 3ccb7ef..df4e0a4 100644 --- a/chrome/updater/external_constants_override.h +++ b/chrome/updater/external_constants_override.h
@@ -22,7 +22,6 @@ namespace base { class FilePath; class TimeDelta; -class Value; } // namespace base namespace crx_file {
diff --git a/chrome/updater/test/integration_tests_impl.h b/chrome/updater/test/integration_tests_impl.h index 0c7a56d..83884c14 100644 --- a/chrome/updater/test/integration_tests_impl.h +++ b/chrome/updater/test/integration_tests_impl.h
@@ -36,7 +36,6 @@ namespace base { class CommandLine; class TimeDelta; -class Value; } // namespace base namespace updater {
diff --git a/chromeos/ui/base/window_properties.cc b/chromeos/ui/base/window_properties.cc index 1010bdbc..3f64eb5 100644 --- a/chromeos/ui/base/window_properties.cc +++ b/chromeos/ui/base/window_properties.cc
@@ -52,6 +52,8 @@ DEFINE_UI_CLASS_PROPERTY_KEY(bool, kSupportsFloatedStateKey, true) +DEFINE_UI_CLASS_PROPERTY_KEY(bool, kUseImmersiveInTrustedPinned, false) + DEFINE_UI_CLASS_PROPERTY_KEY(bool, kWindowManagerManagesOpacityKey, false) DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(std::u16string, kWindowOverviewTitleKey)
diff --git a/chromeos/ui/base/window_properties.h b/chromeos/ui/base/window_properties.h index 2c3accf4b..6965fc4 100644 --- a/chromeos/ui/base/window_properties.h +++ b/chromeos/ui/base/window_properties.h
@@ -126,6 +126,10 @@ COMPONENT_EXPORT(CHROMEOS_UI_BASE) extern const ui::ClassProperty<bool>* const kSupportsFloatedStateKey; +// Whether trusted-pinned window should use immersive frame. +COMPONENT_EXPORT(CHROMEOS_UI_BASE) +extern const ui::ClassProperty<bool>* const kUseImmersiveInTrustedPinned; + // A property key to tell if the window's opacity should be managed by WM. COMPONENT_EXPORT(CHROMEOS_UI_BASE) extern const ui::ClassProperty<bool>* const kWindowManagerManagesOpacityKey;
diff --git a/clank b/clank index 90e7403..0e2e94e0 160000 --- a/clank +++ b/clank
@@ -1 +1 @@ -Subproject commit 90e74033250d2ab075da14a221c397ee4c0e2599 +Subproject commit 0e2e94e036bdbd47b362c54b645e6da30ad2c932
diff --git a/components/app_restore/restore_data.h b/components/app_restore/restore_data.h index 01a0ee6..1b0dd1dc 100644 --- a/components/app_restore/restore_data.h +++ b/components/app_restore/restore_data.h
@@ -14,10 +14,6 @@ #include "components/app_restore/app_restore_data.h" #include "components/app_restore/window_info.h" -namespace base { -class Value; -} - namespace app_restore { struct AppLaunchInfo;
diff --git a/components/autofill/core/browser/data_model/addresses/autofill_i18n_api.cc b/components/autofill/core/browser/data_model/addresses/autofill_i18n_api.cc index 2d0b031..abf02a9 100644 --- a/components/autofill/core/browser/data_model/addresses/autofill_i18n_api.cc +++ b/components/autofill/core/browser/data_model/addresses/autofill_i18n_api.cc
@@ -176,7 +176,6 @@ case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: case CREDIT_CARD_TYPE: case CREDIT_CARD_VERIFICATION_CODE: - case FIELD_WITH_DEFAULT_VALUE: case MERCHANT_EMAIL_SIGNUP: case MERCHANT_PROMO_CODE: case PASSWORD:
diff --git a/components/autofill/core/browser/field_type_utils.cc b/components/autofill/core/browser/field_type_utils.cc index 34dc9cf..faa8f9d9 100644 --- a/components/autofill/core/browser/field_type_utils.cc +++ b/components/autofill/core/browser/field_type_utils.cc
@@ -98,7 +98,6 @@ case CREDIT_CARD_TYPE: case CREDIT_CARD_VERIFICATION_CODE: case COMPANY_NAME: - case FIELD_WITH_DEFAULT_VALUE: case MERCHANT_EMAIL_SIGNUP: case MERCHANT_PROMO_CODE: case PASSWORD: @@ -229,7 +228,6 @@ case CREDIT_CARD_TYPE: case CREDIT_CARD_VERIFICATION_CODE: case COMPANY_NAME: - case FIELD_WITH_DEFAULT_VALUE: case MERCHANT_EMAIL_SIGNUP: case MERCHANT_PROMO_CODE: case PASSWORD: @@ -364,7 +362,6 @@ case CREDIT_CARD_TYPE: case CREDIT_CARD_VERIFICATION_CODE: case COMPANY_NAME: - case FIELD_WITH_DEFAULT_VALUE: case MERCHANT_EMAIL_SIGNUP: case MERCHANT_PROMO_CODE: case PASSWORD:
diff --git a/components/autofill/core/browser/field_types.cc b/components/autofill/core/browser/field_types.cc index 3c01a45..056f43f 100644 --- a/components/autofill/core/browser/field_types.cc +++ b/components/autofill/core/browser/field_types.cc
@@ -78,7 +78,6 @@ {"CREDIT_CARD_TYPE", CREDIT_CARD_TYPE}, {"CREDIT_CARD_VERIFICATION_CODE", CREDIT_CARD_VERIFICATION_CODE}, {"COMPANY_NAME", COMPANY_NAME}, - {"FIELD_WITH_DEFAULT_VALUE", FIELD_WITH_DEFAULT_VALUE}, {"MERCHANT_EMAIL_SIGNUP", MERCHANT_EMAIL_SIGNUP}, {"MERCHANT_PROMO_CODE", MERCHANT_PROMO_CODE}, {"PASSWORD", PASSWORD}, @@ -319,7 +318,6 @@ case NO_SERVER_DATA: case EMPTY_TYPE: case AMBIGUOUS_TYPE: - case FIELD_WITH_DEFAULT_VALUE: case MERCHANT_EMAIL_SIGNUP: case PRICE: case NUMERIC_QUANTITY: @@ -358,7 +356,6 @@ switch (type) { case NO_SERVER_DATA: case UNKNOWN_TYPE: - case FIELD_WITH_DEFAULT_VALUE: case EMPTY_TYPE: case NOT_ACCOUNT_CREATION_PASSWORD: case NOT_NEW_PASSWORD:
diff --git a/components/autofill/core/browser/field_types.h b/components/autofill/core/browser/field_types.h index 3657aa7..e1e13a7 100644 --- a/components/autofill/core/browser/field_types.h +++ b/components/autofill/core/browser/field_types.h
@@ -183,9 +183,7 @@ COMPANY_NAME = 60, - // Generic type whose default value is known. - FIELD_WITH_DEFAULT_VALUE = 61, - + // FIELD_WITH_DEFAULT_VALUE value 61 is deprecated. // PHONE_BILLING values [62, 66] are deprecated. // NAME_BILLING values [67, 72] are deprecated. @@ -627,6 +625,8 @@ t == 94 || // Billing addresses (values [37,43], 78, 80, 82, 84) are deprecated. (37 <= t && t <= 43) || t == 78 || t == 80 || t == 82 || t == 84 || + // FIELD_WITH_DEFAULT_VALUE is deprecated. + t == 61 || // Billing phone numbers (values [62,66]) are deprecated. (62 <= t && t <= 66) || // Billing names (values [67,72]) are deprecated. @@ -830,7 +830,6 @@ case NO_SERVER_DATA: case EMPTY_TYPE: case AMBIGUOUS_TYPE: - case FIELD_WITH_DEFAULT_VALUE: case MERCHANT_EMAIL_SIGNUP: case MERCHANT_PROMO_CODE: return FieldTypeGroup::kNoGroup;
diff --git a/components/autofill/core/browser/field_types_unittest.cc b/components/autofill/core/browser/field_types_unittest.cc index 4d14344..1334308 100644 --- a/components/autofill/core/browser/field_types_unittest.cc +++ b/components/autofill/core/browser/field_types_unittest.cc
@@ -66,7 +66,6 @@ CREDIT_CARD_TYPE, CREDIT_CARD_VERIFICATION_CODE, COMPANY_NAME, - FIELD_WITH_DEFAULT_VALUE, MERCHANT_EMAIL_SIGNUP, MERCHANT_PROMO_CODE, PASSWORD,
diff --git a/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc b/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc index d3e03af..9856299 100644 --- a/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc +++ b/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc
@@ -662,7 +662,6 @@ case NO_SERVER_DATA: case EMPTY_TYPE: case AMBIGUOUS_TYPE: - case FIELD_WITH_DEFAULT_VALUE: case MERCHANT_EMAIL_SIGNUP: case PRICE: case NUMERIC_QUANTITY:
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager.cc b/components/autofill/core/browser/foundations/browser_autofill_manager.cc index 7d6b312..ae8fcfca 100644 --- a/components/autofill/core/browser/foundations/browser_autofill_manager.cc +++ b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
@@ -747,6 +747,16 @@ ->GetCreditCardAccessManager(); } +payments::AmountExtractionManager& +BrowserAutofillManager::GetAmountExtractionManager() { + if (!amount_extraction_manager_) { + amount_extraction_manager_ = + std::make_unique<AmountExtractionManager>(this); + } + + return *amount_extraction_manager_; +} + payments::BnplManager* BrowserAutofillManager::GetPaymentsBnplManager() { #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ BUILDFLAG(IS_CHROMEOS) @@ -1650,7 +1660,7 @@ // should happen, and if so, triggers amount extraction. if (autofill_field) { const DenseSet<AmountExtractionManager::EligibleFeature> eligible_features = - amount_extraction_manager_->GetEligibleFeatures( + GetAmountExtractionManager().GetEligibleFeatures( context, ShouldSuppressSuggestions(context.suppress_reason, log_manager()), !suggestions.empty(), autofill_field->Type().GetStorableType()); @@ -1666,7 +1676,7 @@ } NOTREACHED(); } - amount_extraction_manager_->TriggerCheckoutAmountExtraction(); + GetAmountExtractionManager().TriggerCheckoutAmountExtraction(); } } @@ -2542,6 +2552,8 @@ touch_to_fill_delegate_->Reset(); } form_filler_->Reset(); + amount_extraction_manager_.reset(); + bnpl_manager_.reset(); // The order below is relevant: // `credit_card_access_manager_` has a reference to `metrics_`.
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager.h b/components/autofill/core/browser/foundations/browser_autofill_manager.h index 5b7bb962..b4f513ea 100644 --- a/components/autofill/core/browser/foundations/browser_autofill_manager.h +++ b/components/autofill/core/browser/foundations/browser_autofill_manager.h
@@ -45,7 +45,6 @@ #include "components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h" #include "components/autofill/core/browser/metrics/form_events/loyalty_card_form_event_logger.h" #include "components/autofill/core/browser/metrics/log_event.h" -#include "components/autofill/core/browser/payments/amount_extraction_manager.h" #include "components/autofill/core/browser/payments/autofill_offer_manager.h" #include "components/autofill/core/browser/payments/card_unmask_delegate.h" #include "components/autofill/core/browser/payments/full_card_request.h" @@ -200,6 +199,10 @@ // current platform. virtual payments::BnplManager* GetPaymentsBnplManager(); + // Gets the amount extraction manager owned by `this`. This will be used for + // flows that require amount extraction from the page. + payments::AmountExtractionManager& GetAmountExtractionManager(); + // Handles post-filling logic of `form`, like notifying observers and logging // form metrics. // `filled_field_ids` are the IDs of fields that were filled by the browser. @@ -656,9 +659,8 @@ // The amount extraction manager, used to trigger the final checkout // amount from merchant websites. - std::unique_ptr<payments::AmountExtractionManager> - amount_extraction_manager_ = - std::make_unique<payments::AmountExtractionManager>(this); + // Lazily initialized: access only through GetAmountExtractionManager(). + std::unique_ptr<payments::AmountExtractionManager> amount_extraction_manager_; // Helper class to autofill forms and fields. Do not use directly, use // form_filler() instead, because tests inject test objects.
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager_test_api.h b/components/autofill/core/browser/foundations/browser_autofill_manager_test_api.h index 8f45973e..b75774a6 100644 --- a/components/autofill/core/browser/foundations/browser_autofill_manager_test_api.h +++ b/components/autofill/core/browser/foundations/browser_autofill_manager_test_api.h
@@ -69,11 +69,6 @@ manager_->bnpl_manager_ = std::move(bnpl_manager); } - payments::AmountExtractionManager& - get_amount_extraction_manager_for_testing() { - return *manager_->amount_extraction_manager_; - } - void OnFormProcessed(const FormData& form, const FormStructure& form_structure) { manager_->OnFormProcessed(form, form_structure);
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc index 148b076..ddb5031 100644 --- a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc +++ b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
@@ -1449,7 +1449,7 @@ MockAmountExtractionManager& amount_extraction_manager() { return static_cast<MockAmountExtractionManager&>( - test_api(manager()).get_amount_extraction_manager_for_testing()); + manager().GetAmountExtractionManager()); } TestAutofillExternalDelegate* external_delegate() {
diff --git a/components/autofill/core/browser/metrics/prediction_quality_metrics.cc b/components/autofill/core/browser/metrics/prediction_quality_metrics.cc index 47a27ae..dfba60e 100644 --- a/components/autofill/core/browser/metrics/prediction_quality_metrics.cc +++ b/components/autofill/core/browser/metrics/prediction_quality_metrics.cc
@@ -362,7 +362,6 @@ case CREDIT_CARD_TYPE: case CREDIT_CARD_VERIFICATION_CODE: case COMPANY_NAME: - case FIELD_WITH_DEFAULT_VALUE: case MERCHANT_EMAIL_SIGNUP: case MERCHANT_PROMO_CODE: case PASSWORD:
diff --git a/components/autofill/core/browser/payments/bnpl_manager.cc b/components/autofill/core/browser/payments/bnpl_manager.cc index 81960bf2..ada0771b 100644 --- a/components/autofill/core/browser/payments/bnpl_manager.cc +++ b/components/autofill/core/browser/payments/bnpl_manager.cc
@@ -237,12 +237,21 @@ void BnplManager::OnIssuerSelected(BnplIssuer selected_issuer) { ongoing_flow_state_->issuer = std::move(selected_issuer); - if (ongoing_flow_state_->issuer.payment_instrument().has_value()) { + if (ongoing_flow_state_->issuer.payment_instrument().has_value() && + !AcceptTosActionRequired()) { ongoing_flow_state_->instrument_id = base::NumberToString( ongoing_flow_state_->issuer.payment_instrument()->instrument_id()); LoadRiskDataForFetchingRedirectUrl(); } else { + GetLegalMessageFromServer(); + } +} + +void BnplManager::GetLegalMessageFromServer() { + if (AcceptTosActionRequired()) { + GetDetailsForUpdateBnplPaymentInstrument(); + } else { GetDetailsForCreateBnplPaymentInstrument(); } } @@ -355,13 +364,14 @@ void BnplManager::OnRedirectUrlFetched( PaymentsAutofillClient::PaymentsRpcResult result, const BnplFetchUrlResponseDetails& response) { - if (ongoing_flow_state_->issuer.payment_instrument().has_value()) { - // If the BNPL issuer selected is linked, the issuer selection dialog must - // be showing, so close it. + if (ongoing_flow_state_->issuer.payment_instrument().has_value() && + !AcceptTosActionRequired()) { + // If the BNPL issuer selected is linked and doesn't require ToS acceptance, + // then the issuer selection dialog must be showing, so close it. payments_autofill_client().DismissSelectBnplIssuerDialog(); } else { - // If the BNPL issuer selected is not linked, the ToS dialog must be - // showing, so close it. + // If the BNPL issuer selected is not linked, or is linked but requires ToS + // acceptance, then the ToS dialog must be showing, so close it. payments_autofill_client().CloseBnplTos(); } @@ -495,7 +505,11 @@ void BnplManager::OnTosDialogAccepted() { if (!ongoing_flow_state_->risk_data.empty()) { - CreateBnplPaymentInstrument(); + if (AcceptTosActionRequired()) { + UpdateBnplPaymentInstrument(); + } else { + CreateBnplPaymentInstrument(); + } return; } @@ -511,7 +525,11 @@ void BnplManager::OnRiskDataLoadedAfterTosDialogAcceptance( const std::string& risk_data) { ongoing_flow_state_->risk_data = risk_data; - CreateBnplPaymentInstrument(); + if (AcceptTosActionRequired()) { + UpdateBnplPaymentInstrument(); + } else { + CreateBnplPaymentInstrument(); + } } void BnplManager::CreateBnplPaymentInstrument() { @@ -667,4 +685,11 @@ }); } +bool BnplManager::AcceptTosActionRequired() const { + return ongoing_flow_state_->issuer.payment_instrument().has_value() && + base::Contains(ongoing_flow_state_->issuer.payment_instrument() + ->action_required(), + PaymentInstrument::ActionRequired::kAcceptTos); +} + } // namespace autofill::payments
diff --git a/components/autofill/core/browser/payments/bnpl_manager.h b/components/autofill/core/browser/payments/bnpl_manager.h index c971e81..4b524b4 100644 --- a/components/autofill/core/browser/payments/bnpl_manager.h +++ b/components/autofill/core/browser/payments/bnpl_manager.h
@@ -87,6 +87,10 @@ // 3. The URL being visited is within the BNPL issuer allowlist. bool IsEligibleForBnpl() const; + // Returns true if the issuer for the ongoing flow contains the required + // action `PaymentInstrument::ActionRequired::kAcceptTos`. + bool AcceptTosActionRequired() const; + private: friend class BnplManagerTestApi; friend class BnplManagerTest; @@ -161,6 +165,10 @@ // or terms of services depending on the issuer. void OnIssuerSelected(BnplIssuer selected_issuer); + // This function makes the appropriate server call to retrieve the ToS legal + // message for the issuer. + void GetLegalMessageFromServer(); + // This function makes the appropriate call to the payments server to get info // from the server for creating an instrument for the selected issuer. void GetDetailsForCreateBnplPaymentInstrument();
diff --git a/components/autofill/core/browser/payments/bnpl_manager_unittest.cc b/components/autofill/core/browser/payments/bnpl_manager_unittest.cc index 40299f0..2c72d53 100644 --- a/components/autofill/core/browser/payments/bnpl_manager_unittest.cc +++ b/components/autofill/core/browser/payments/bnpl_manager_unittest.cc
@@ -151,6 +151,11 @@ class BnplManagerTest : public Test { public: + using GetDetailsForUpdateBnplPaymentInstrumentRequestDetails:: + GetDetailsForUpdateBnplPaymentInstrumentType::kGetDetailsForAcceptTos; + using UpdateBnplPaymentInstrumentRequestDetails:: + UpdateBnplPaymentInstrumentType::kAcceptTos; + const int64_t kBillingCustomerNumber = 1234; const std::string kRiskData = "RISK_DATA"; const std::string kInstrumentId = "INSTRUMENT_ID"; @@ -421,6 +426,94 @@ autofill_client_->GetPaymentsAutofillClient()->risk_data_loaded()); } +// Tests that the user accepting the ToS dialog for a linked issuer where ToS +// acceptance is required triggers an UpdatePaymentInstrument request with the +// loaded risk data, if it is present. +TEST_F(BnplManagerTest, + TosDialogAccepted_PrefetchedRiskDataLoaded_TosAcceptanceRequired) { + bnpl_manager_->OnDidAcceptBnplSuggestion(/*final_checkout_amount=*/kAmount, + base::DoNothing()); + auto* ongoing_flow_state = test_api(*bnpl_manager_).GetOngoingFlowState(); + const std::string test_context_token = "test_context_token"; + BnplIssuer issuer = test::GetTestLinkedBnplIssuer( + IssuerId::kBnplKlarna, + DenseSet<PaymentInstrument::ActionRequired>{ + PaymentInstrument::ActionRequired::kAcceptTos}); + ongoing_flow_state->context_token = test_context_token; + ongoing_flow_state->issuer = issuer; + + ASSERT_FALSE(ongoing_flow_state->risk_data.empty()); + + autofill_client_->GetPaymentsAutofillClient()->set_risk_data_loaded(false); + + EXPECT_CALL( + *payments_network_interface_, + UpdateBnplPaymentInstrument(/*request_details=*/ + FieldsAre( + autofill_client_->GetAppLocale(), + GetBillingCustomerId( + autofill_client_ + ->GetPaymentsAutofillClient() + ->GetPaymentsDataManager()), + autofill::ConvertToBnplIssuerIdString( + issuer.issuer_id()), + /*instrument_id=*/12345, + test_context_token, /*risk_data=*/_, + /*type=*/kAcceptTos), + /*callback=*/_)); + + test_api(*bnpl_manager_).OnTosDialogAccepted(); + + EXPECT_FALSE(ongoing_flow_state->risk_data.empty()); + + // Since risk data was cached, it was directly used, thus loading risk data + // was skipped. + EXPECT_FALSE( + autofill_client_->GetPaymentsAutofillClient()->risk_data_loaded()); +} + +// Tests that the the user accepting the ToS dialog for a linked issuer where +// ToS acceptance is required triggers an UpdatePaymentInstrument request and +// loads risk data after ToS dialog acceptance if it was not already loaded. +TEST_F(BnplManagerTest, + TosDialogAccepted_PrefetchedRiskDataNotLoaded_TosAcceptanceRequired) { + bnpl_manager_->OnDidAcceptBnplSuggestion(/*final_checkout_amount=*/1000000, + base::DoNothing()); + auto* ongoing_flow_state = test_api(*bnpl_manager_).GetOngoingFlowState(); + const std::string test_context_token = "test_context_token"; + BnplIssuer issuer = test::GetTestLinkedBnplIssuer( + IssuerId::kBnplKlarna, + DenseSet<PaymentInstrument::ActionRequired>{ + PaymentInstrument::ActionRequired::kAcceptTos}); + ongoing_flow_state->context_token = test_context_token; + ongoing_flow_state->issuer = issuer; + ongoing_flow_state->risk_data.clear(); + + ASSERT_TRUE(ongoing_flow_state->risk_data.empty()); + + EXPECT_CALL( + *payments_network_interface_, + UpdateBnplPaymentInstrument(/*request_details=*/ + FieldsAre( + autofill_client_->GetAppLocale(), + GetBillingCustomerId( + autofill_client_ + ->GetPaymentsAutofillClient() + ->GetPaymentsDataManager()), + autofill::ConvertToBnplIssuerIdString( + issuer.issuer_id()), + issuer.payment_instrument() + ->instrument_id(), + test_context_token, + /*risk_data=*/_, /*type=*/kAcceptTos), + /*callback=*/_)); + test_api(*bnpl_manager_).OnTosDialogAccepted(); + + EXPECT_FALSE(ongoing_flow_state->risk_data.empty()); + EXPECT_TRUE( + autofill_client_->GetPaymentsAutofillClient()->risk_data_loaded()); +} + // Tests that FetchVcnDetails calls the payments network interface with the // request details filled out correctly, and verifies that the VCN is correctly // filled and the state of BnplManager is reset. @@ -870,6 +963,39 @@ unlinked_issuer); } +// Tests that `OnIssuerSelected()` calls with a linked issuer where ToS +// acceptance is required will call the payments network interface with the +// request details filled out correctly. +TEST_F( + BnplManagerTest, + OnIssuerSelected_CallsGetDetailsForUpdateBnplPaymentInstrument_TosAcceptanceRequired) { + bnpl_manager_->OnDidAcceptBnplSuggestion(kAmount, base::DoNothing()); + + ASSERT_EQ(test_api(*bnpl_manager_).GetOngoingFlowState()->app_locale, + kAppLocale); + ASSERT_EQ( + test_api(*bnpl_manager_).GetOngoingFlowState()->billing_customer_number, + kBillingCustomerNumber); + + BnplIssuer issuer = test::GetTestLinkedBnplIssuer( + IssuerId::kBnplKlarna, + DenseSet<PaymentInstrument::ActionRequired>{ + PaymentInstrument::ActionRequired::kAcceptTos}); + + EXPECT_CALL(*payments_network_interface_, + GetDetailsForUpdateBnplPaymentInstrument( + /*request_details=*/ + FieldsAre(kAppLocale, kBillingCustomerNumber, + issuer.payment_instrument()->instrument_id(), + /*type=*/kGetDetailsForAcceptTos), + /*callback=*/_)) + .Times(1); + + OnIssuerSelected(issuer); + + EXPECT_EQ(test_api(*bnpl_manager_).GetOngoingFlowState()->issuer, issuer); +} + // Tests that `OnDidGetLegalMessageFromServer` set the BNPL manager state if the // request has completed successfully, and shows the ToS dialog. This test also // ensures the ToS dialog is closed after receiving a redirect URL for an @@ -1008,16 +1134,13 @@ BnplIssuer issuer = test::GetTestLinkedBnplIssuer(); test_api(*bnpl_manager_).GetOngoingFlowState()->issuer = issuer; - EXPECT_CALL( - *payments_network_interface_, - GetDetailsForUpdateBnplPaymentInstrument( - /*request_details=*/ - FieldsAre(kAppLocale, kBillingCustomerNumber, - issuer.payment_instrument()->instrument_id(), - GetDetailsForUpdateBnplPaymentInstrumentRequestDetails:: - GetDetailsForUpdateBnplPaymentInstrumentType:: - kGetDetailsForAcceptTos), - /*callback=*/_)); + EXPECT_CALL(*payments_network_interface_, + GetDetailsForUpdateBnplPaymentInstrument( + /*request_details=*/ + FieldsAre(kAppLocale, kBillingCustomerNumber, + issuer.payment_instrument()->instrument_id(), + /*type=*/kGetDetailsForAcceptTos), + /*callback=*/_)); test_api(*bnpl_manager_).GetDetailsForUpdateBnplPaymentInstrument(); } @@ -1038,9 +1161,7 @@ FieldsAre(kAppLocale, kBillingCustomerNumber, autofill::ConvertToBnplIssuerIdString(issuer.issuer_id()), issuer.payment_instrument()->instrument_id(), kContextToken, - kRiskData, - UpdateBnplPaymentInstrumentRequestDetails:: - UpdateBnplPaymentInstrumentType::kAcceptTos), + kRiskData, /*type=*/kAcceptTos), /*callback=*/_)); test_api(*bnpl_manager_).UpdateBnplPaymentInstrument(); @@ -1603,6 +1724,43 @@ EXPECT_EQ(test_api(*bnpl_manager_).GetOngoingFlowState(), nullptr); } +// Tests that when UpdateBnplPaymentInstrument fails with an error the error +// dialog is shown and the flow is reset. +TEST_F(BnplManagerTest, UpdateBnplPaymentInstrument_Failure) { + bnpl_manager_->OnDidAcceptBnplSuggestion(kAmount, base::DoNothing()); + auto* ongoing_flow_state = test_api(*bnpl_manager_).GetOngoingFlowState(); + ongoing_flow_state->app_locale = kAppLocale; + ongoing_flow_state->billing_customer_number = kBillingCustomerNumber; + ongoing_flow_state->context_token = kContextToken; + ongoing_flow_state->issuer = test::GetTestLinkedBnplIssuer( + IssuerId::kBnplKlarna, + DenseSet<PaymentInstrument::ActionRequired>{ + PaymentInstrument::ActionRequired::kAcceptTos}); + ongoing_flow_state->risk_data = kRiskData; + + EXPECT_CALL( + *payments_network_interface_, + UpdateBnplPaymentInstrument( + FieldsAre( + kAppLocale, kBillingCustomerNumber, + autofill::ConvertToBnplIssuerIdString( + ongoing_flow_state->issuer.issuer_id()), + ongoing_flow_state->issuer.payment_instrument()->instrument_id(), + kContextToken, kRiskData, + /*type=*/kAcceptTos), + _)) + .WillOnce(base::test::RunOnceCallback<1>( + PaymentsAutofillClient::PaymentsRpcResult::kPermanentFailure)); + EXPECT_CALL(GetPaymentsAutofillClient(), CloseBnplTos); + + test_api(*bnpl_manager_).UpdateBnplPaymentInstrument(); + + EXPECT_TRUE(autofill_client_->GetPaymentsAutofillClient() + ->autofill_error_dialog_shown()); + + EXPECT_EQ(test_api(*bnpl_manager_).GetOngoingFlowState(), nullptr); +} + // Tests the sorting logic of `GetSortedBnplIssuerContext` for BNPL Issuers // based on their linked status and eligibility. The expected order is: // 1. Linked & Eligible
diff --git a/components/autofill/core/browser/payments/test_payments_autofill_client.cc b/components/autofill/core/browser/payments/test_payments_autofill_client.cc index 17be4a2..17a72fe9 100644 --- a/components/autofill/core/browser/payments/test_payments_autofill_client.cc +++ b/components/autofill/core/browser/payments/test_payments_autofill_client.cc
@@ -137,10 +137,18 @@ VirtualCardEnrollmentManager* TestPaymentsAutofillClient::GetVirtualCardEnrollmentManager() { if (!virtual_card_enrollment_manager_) { + PaymentsNetworkInterfaceVariation payments_network_interface; + if (base::FeatureList::IsEnabled( + features:: + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment)) { + payments_network_interface = GetMultipleRequestPaymentsNetworkInterface(); + } else { + payments_network_interface = GetPaymentsNetworkInterface(); + } virtual_card_enrollment_manager_ = std::make_unique<VirtualCardEnrollmentManager>( &client_->GetPersonalDataManager().payments_data_manager(), - GetPaymentsNetworkInterface(), &client_.get()); + payments_network_interface, &client_.get()); } return virtual_card_enrollment_manager_.get();
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc index c722494ff..4d53d47e 100644 --- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc +++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
@@ -15,6 +15,7 @@ #include "components/autofill/core/browser/foundations/autofill_client.h" #include "components/autofill/core/browser/metrics/payments/virtual_card_enrollment_metrics.h" #include "components/autofill/core/browser/payments/autofill_payments_feature_availability.h" +#include "components/autofill/core/browser/payments/multiple_request_payments_network_interface.h" #include "components/autofill/core/browser/payments/payments_autofill_client.h" #include "components/autofill/core/browser/payments/payments_network_interface.h" #include "components/autofill/core/browser/payments/payments_util.h" @@ -54,7 +55,7 @@ VirtualCardEnrollmentManager::VirtualCardEnrollmentManager( PaymentsDataManager* payments_data_manager, - payments::PaymentsNetworkInterface* payments_network_interface, + PaymentsNetworkInterfaceVariation payments_network_interface, AutofillClient* autofill_client) : autofill_client_(autofill_client), payments_data_manager_(CHECK_DEREF(payments_data_manager)), @@ -191,8 +192,8 @@ if (base::FeatureList::IsEnabled( features:: kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment)) { - autofill_client_->GetPaymentsAutofillClient() - ->GetMultipleRequestPaymentsNetworkInterface() + std::get<payments::MultipleRequestPaymentsNetworkInterface*>( + payments_network_interface_) ->UpdateVirtualCardEnrollment( request_details, base::BindOnce(&VirtualCardEnrollmentManager:: @@ -200,12 +201,13 @@ weak_ptr_factory_.GetWeakPtr(), VirtualCardEnrollmentRequestType::kEnroll)); } else { - payments_network_interface_->UpdateVirtualCardEnrollment( - request_details, - base::BindOnce(&VirtualCardEnrollmentManager:: - OnDidGetUpdateVirtualCardEnrollmentResponse, - weak_ptr_factory_.GetWeakPtr(), - VirtualCardEnrollmentRequestType::kEnroll)); + std::get<payments::PaymentsNetworkInterface*>(payments_network_interface_) + ->UpdateVirtualCardEnrollment( + request_details, + base::BindOnce(&VirtualCardEnrollmentManager:: + OnDidGetUpdateVirtualCardEnrollmentResponse, + weak_ptr_factory_.GetWeakPtr(), + VirtualCardEnrollmentRequestType::kEnroll)); } } @@ -237,8 +239,8 @@ if (base::FeatureList::IsEnabled( features:: kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment)) { - autofill_client_->GetPaymentsAutofillClient() - ->GetMultipleRequestPaymentsNetworkInterface() + std::get<payments::MultipleRequestPaymentsNetworkInterface*>( + payments_network_interface_) ->UpdateVirtualCardEnrollment( request_details, base::BindOnce(&VirtualCardEnrollmentManager:: @@ -246,12 +248,13 @@ weak_ptr_factory_.GetWeakPtr(), VirtualCardEnrollmentRequestType::kUnenroll)); } else { - payments_network_interface_->UpdateVirtualCardEnrollment( - request_details, - base::BindOnce(&VirtualCardEnrollmentManager:: - OnDidGetUpdateVirtualCardEnrollmentResponse, - weak_ptr_factory_.GetWeakPtr(), - VirtualCardEnrollmentRequestType::kUnenroll)); + std::get<payments::PaymentsNetworkInterface*>(payments_network_interface_) + ->UpdateVirtualCardEnrollment( + request_details, + base::BindOnce(&VirtualCardEnrollmentManager:: + OnDidGetUpdateVirtualCardEnrollmentResponse, + weak_ptr_factory_.GetWeakPtr(), + VirtualCardEnrollmentRequestType::kUnenroll)); } } @@ -384,7 +387,8 @@ if (!base::FeatureList::IsEnabled( features:: kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment)) { - payments_network_interface_->CancelRequest(); + std::get<payments::PaymentsNetworkInterface*>(payments_network_interface_) + ->CancelRequest(); } // Invalidating all WeakPtrs effectively cancels any pending requests. weak_ptr_factory_.InvalidateWeakPtrs(); @@ -490,8 +494,8 @@ if (base::FeatureList::IsEnabled( features:: kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment)) { - autofill_client_->GetPaymentsAutofillClient() - ->GetMultipleRequestPaymentsNetworkInterface() + std::get<payments::MultipleRequestPaymentsNetworkInterface*>( + payments_network_interface_) ->GetVirtualCardEnrollmentDetails( request_details, base::BindOnce( @@ -499,11 +503,12 @@ weak_ptr_factory_.GetWeakPtr())); } else { - payments_network_interface_->GetVirtualCardEnrollmentDetails( - request_details, - base::BindOnce( - &VirtualCardEnrollmentManager::OnDidGetDetailsForEnrollResponse, - weak_ptr_factory_.GetWeakPtr())); + std::get<payments::PaymentsNetworkInterface*>(payments_network_interface_) + ->GetVirtualCardEnrollmentDetails( + request_details, + base::BindOnce( + &VirtualCardEnrollmentManager::OnDidGetDetailsForEnrollResponse, + weak_ptr_factory_.GetWeakPtr())); } LogGetDetailsForEnrollmentRequestAttempt(request_details.source);
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h index 50db57f..62acdd0c 100644 --- a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h +++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
@@ -28,11 +28,15 @@ class CreditCard; class PaymentsDataManager; +using PaymentsNetworkInterfaceVariation = + std::variant<payments::PaymentsNetworkInterface*, + payments::MultipleRequestPaymentsNetworkInterface*>; + // This struct is passed into the controller when we show the // VirtualCardEnrollmentBubble, and it lets the controller customize the // bubble based on the fields in this struct. For example, we will show -// different last 4 digits of a credit card based on the |credit_card| object -// in this struct. +// different last 4 digits of a credit card based on the |credit_card| +// object in this struct. struct VirtualCardEnrollmentFields { VirtualCardEnrollmentFields(); VirtualCardEnrollmentFields(const VirtualCardEnrollmentFields&); @@ -99,7 +103,7 @@ // The parameters should outlive the VirtualCardEnrollmentManager. VirtualCardEnrollmentManager( PaymentsDataManager* payments_data_manager, - payments::PaymentsNetworkInterface* payments_network_interface, + PaymentsNetworkInterfaceVariation payments_network_interface, AutofillClient* autofill_client = nullptr); VirtualCardEnrollmentManager(const VirtualCardEnrollmentManager&) = delete; VirtualCardEnrollmentManager& operator=(const VirtualCardEnrollmentManager&) = @@ -384,7 +388,7 @@ // The associated `payments_network_interface_` that is used for all requests // to the server. - const raw_ptr<payments::PaymentsNetworkInterface> payments_network_interface_; + const PaymentsNetworkInterfaceVariation payments_network_interface_; // The database that is used to count instrument_id-keyed strikes to suppress // prompting users to enroll in virtual cards.
diff --git a/components/autofill/core/browser/test_utils/autofill_test_utils.cc b/components/autofill/core/browser/test_utils/autofill_test_utils.cc index e5359a2..5dd9d70 100644 --- a/components/autofill/core/browser/test_utils/autofill_test_utils.cc +++ b/components/autofill/core/browser/test_utils/autofill_test_utils.cc
@@ -1300,7 +1300,7 @@ BnplIssuer GetTestLinkedBnplIssuer( autofill::BnplIssuer::IssuerId issuer_id, - DenseSet<PaymentInstrument::ActionRequired> action_required) { + DenseSet<PaymentInstrument::ActionRequired> actions_required) { std::vector<BnplIssuer::EligiblePriceRange> eligible_price_ranges; // Currency: USD, price lower bound: $50, price upper bound: $200. eligible_price_ranges.emplace_back(/*currency=*/"USD", @@ -1308,7 +1308,7 @@ /*price_upper_bound=*/200'000'000); return BnplIssuer( /*instrument_id=*/12345, issuer_id, std::move(eligible_price_ranges), - std::move(action_required)); + std::move(actions_required)); } BnplIssuer GetTestUnlinkedBnplIssuer() {
diff --git a/components/autofill/core/browser/test_utils/autofill_test_utils.h b/components/autofill/core/browser/test_utils/autofill_test_utils.h index 6e27d6c..585193e1 100644 --- a/components/autofill/core/browser/test_utils/autofill_test_utils.h +++ b/components/autofill/core/browser/test_utils/autofill_test_utils.h
@@ -496,7 +496,7 @@ BnplIssuer GetTestLinkedBnplIssuer( autofill::BnplIssuer::IssuerId issuer_id = autofill::BnplIssuer::IssuerId::kBnplAffirm, - DenseSet<PaymentInstrument::ActionRequired> action_required = + DenseSet<PaymentInstrument::ActionRequired> actions_required = DenseSet<PaymentInstrument::ActionRequired>()); // Returns an unlinked BNPL issuer with fake data.
diff --git a/components/autofill/core/browser/ui/addresses/autofill_address_util.cc b/components/autofill/core/browser/ui/addresses/autofill_address_util.cc index f0ce80f..b9284db5 100644 --- a/components/autofill/core/browser/ui/addresses/autofill_address_util.cc +++ b/components/autofill/core/browser/ui/addresses/autofill_address_util.cc
@@ -401,7 +401,6 @@ case NO_SERVER_DATA: case EMPTY_TYPE: case AMBIGUOUS_TYPE: - case FIELD_WITH_DEFAULT_VALUE: case MERCHANT_EMAIL_SIGNUP: case PRICE: case NUMERIC_QUANTITY:
diff --git a/components/browser_ui/display_cutout/OWNERS b/components/browser_ui/display_cutout/OWNERS new file mode 100644 index 0000000..825f537 --- /dev/null +++ b/components/browser_ui/display_cutout/OWNERS
@@ -0,0 +1 @@ +file://chrome/android/java/src/org/chromium/chrome/browser/display_cutout/OWNERS \ No newline at end of file
diff --git a/components/browser_ui/site_settings/DEPS b/components/browser_ui/site_settings/DEPS index 2b869f4..b74e318 100644 --- a/components/browser_ui/site_settings/DEPS +++ b/components/browser_ui/site_settings/DEPS
@@ -9,6 +9,7 @@ "+components/location/android", "+components/permissions", "+components/prefs/android", + "+components/site_isolation/site_isolation_policy.h", "+components/subresource_filter/android", "+components/url_formatter/android", "+components/user_prefs",
diff --git a/components/browser_ui/site_settings/android/BUILD.gn b/components/browser_ui/site_settings/android/BUILD.gn index 5fb8b5c..c921afde 100644 --- a/components/browser_ui/site_settings/android/BUILD.gn +++ b/components/browser_ui/site_settings/android/BUILD.gn
@@ -29,6 +29,7 @@ "//components/content_settings/core/browser", "//components/content_settings/core/browser:cookie_settings", "//components/permissions", + "//components/site_isolation", "//components/subresource_filter/android", "//components/user_prefs", "//content/public/browser",
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/JavascriptOptimizerCategory.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/JavascriptOptimizerCategory.java index 997b6c0..81cec67 100644 --- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/JavascriptOptimizerCategory.java +++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/JavascriptOptimizerCategory.java
@@ -20,6 +20,7 @@ @NullMarked public class JavascriptOptimizerCategory extends SiteSettingsCategory { private final boolean mBlockedByOs; + private final boolean mBlockAddingException; public JavascriptOptimizerCategory(BrowserContextHandle browserContextHandle) { super(browserContextHandle, Type.JAVASCRIPT_OPTIMIZER, /* androidPermission= */ ""); @@ -28,6 +29,8 @@ browserContextHandle, ContentSettingsType.JAVASCRIPT_OPTIMIZER) && WebsitePreferenceBridge.isJavascriptOptimizerOsProvidedSetting( browserContextHandle, ContentSettingsType.JAVASCRIPT_OPTIMIZER); + mBlockAddingException = + !WebsitePreferenceBridge.canAddExceptionsForJavascriptOptimizerSetting(); } @Override @@ -51,4 +54,11 @@ icon.setColorFilter(disabledColor, PorterDuff.Mode.SRC_IN); return icon; } + + @Override + protected int getBlockAddingExceptionsReasonResourceId() { + return mBlockAddingException + ? R.string.website_settings_js_opt_add_exceptions_disabled_reason + : 0; + } }
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java index fcd4ff2..97dbc67 100644 --- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java +++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
@@ -238,6 +238,7 @@ public static final String DESKTOP_SITE_WINDOW_TOGGLE_KEY = "desktop_site_window"; public static final String EXPLAIN_PROTECTED_MEDIA_KEY = "protected_content_learn_more"; public static final String ADD_EXCEPTION_KEY = "add_exception"; + public static final String ADD_EXCEPTION_DISABLED_REASON_KEY = "add_exception_disabled_reason"; public static final String INFO_TEXT_KEY = "info_text"; public static final String ANTI_ABUSE_WHEN_ON_HEADER = "anti_abuse_when_on_header"; public static final String ANTI_ABUSE_WHEN_ON_SECTION_ONE = "anti_abuse_when_on_section_one"; @@ -938,7 +939,7 @@ configureGlobalToggles(); - boolean allowSpecifyingExceptions = false; + boolean shouldAddExceptionButton = false; switch (mCategory.getType()) { case SiteSettingsCategory.Type.SOUND: @@ -947,38 +948,54 @@ case SiteSettingsCategory.Type.FEDERATED_IDENTITY_API: case SiteSettingsCategory.Type.REQUEST_DESKTOP_SITE: case SiteSettingsCategory.Type.JAVASCRIPT_OPTIMIZER: - allowSpecifyingExceptions = true; + shouldAddExceptionButton = true; break; case SiteSettingsCategory.Type.BACKGROUND_SYNC: case SiteSettingsCategory.Type.AUTOMATIC_DOWNLOADS: - allowSpecifyingExceptions = !isCategoryEnabled(); + shouldAddExceptionButton = !isCategoryEnabled(); break; case SiteSettingsCategory.Type.AUTO_DARK_WEB_CONTENT: - allowSpecifyingExceptions = isCategoryEnabled(); + shouldAddExceptionButton = isCategoryEnabled(); break; case SiteSettingsCategory.Type.THIRD_PARTY_COOKIES: - allowSpecifyingExceptions = getCookieControlsMode() != CookieControlsMode.OFF; + shouldAddExceptionButton = getCookieControlsMode() != CookieControlsMode.OFF; break; default: break; } int exceptionDialogMessageResourceId = getAddExceptionDialogMessageResourceId(); - assert allowSpecifyingExceptions == (exceptionDialogMessageResourceId != 0); - if (allowSpecifyingExceptions) { + assert shouldAddExceptionButton == (exceptionDialogMessageResourceId != 0); + + if (shouldAddExceptionButton) { + int blockAddingExceptionsReasonResourceId = + mCategory.getBlockAddingExceptionsReasonResourceId(); boolean enableAddExceptionButton = (!mCategory.isManaged() - || mCategory.getType() - == SiteSettingsCategory.Type.THIRD_PARTY_COOKIES); + || mCategory.getType() + == SiteSettingsCategory.Type.THIRD_PARTY_COOKIES) + && blockAddingExceptionsReasonResourceId == 0; + String exceptionDialogMessage = + exceptionDialogMessageResourceId != 0 + ? getString(exceptionDialogMessageResourceId) + : ""; getPreferenceScreen() .addPreference( new AddExceptionPreference( getStyledContext(), ADD_EXCEPTION_KEY, - getString(exceptionDialogMessageResourceId), + exceptionDialogMessage, enableAddExceptionButton, mCategory, this)); + + if (blockAddingExceptionsReasonResourceId != 0) { + ChromeBasePreference reason = new ChromeBasePreference(getStyledContext()); + reason.setKey(ADD_EXCEPTION_DISABLED_REASON_KEY); + reason.setTitle(getString(blockAddingExceptionsReasonResourceId)); + reason.setIcon(mCategory.getDisabledInAndroidIcon(getContext())); + getPreferenceScreen().addPreference(reason); + } } }
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsCategory.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsCategory.java index 62f194dc..ab6778ee 100644 --- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsCategory.java +++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsCategory.java
@@ -573,6 +573,14 @@ } /** + * Returns resource id for message about why adding exceptions is blocked. 0 should be returned + * if no message should be shown. + */ + protected int getBlockAddingExceptionsReasonResourceId() { + return 0; + } + + /** * Returns whether to show the 'permission blocked' message. Majority of the time, that is * warranted when the permission is either blocked per app or globally. But there are exceptions * to this, so the sub-classes can overwrite.
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java index 19b0fcd..5b0b7981 100644 --- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java +++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java
@@ -479,6 +479,14 @@ } /** + * Returns whether the Android device supports adding exceptions for the javascript-optimizer + * content-setting. + */ + public static boolean canAddExceptionsForJavascriptOptimizerSetting() { + return WebsitePreferenceBridgeJni.get().canAddExceptionsForJavascriptOptimizerSetting(); + } + + /** * Convert pattern to domain wildcard pattern. If fail to extract domain from the pattern, * return the original pattern. * @@ -573,6 +581,8 @@ String embedder, @ContentSettingValues int value); + boolean canAddExceptionsForJavascriptOptimizerSetting(); + GeolocationSetting getGeolocationSettingForOrigin( BrowserContextHandle browserContextHandle, @ContentSettingsType.EnumType int contentSettingsType,
diff --git a/components/browser_ui/site_settings/android/website_preference_bridge.cc b/components/browser_ui/site_settings/android/website_preference_bridge.cc index 90f3ecf..6a23b3a 100644 --- a/components/browser_ui/site_settings/android/website_preference_bridge.cc +++ b/components/browser_ui/site_settings/android/website_preference_bridge.cc
@@ -45,6 +45,7 @@ #include "components/permissions/permission_uma_util.h" #include "components/permissions/permission_util.h" #include "components/permissions/permissions_client.h" +#include "components/site_isolation/site_isolation_policy.h" #include "components/user_prefs/user_prefs.h" #include "content/public/browser/android/browser_context_handle.h" #include "content/public/browser/browser_context.h" @@ -182,8 +183,9 @@ const std::string embedder = settings_it.secondary_pattern.ToString(); ScopedJavaLocalRef<jstring> jembedder; - if (embedder != origin) + if (embedder != origin) { jembedder = ConvertUTF8ToJavaString(env, embedder); + } seen_origins.push_back(origin); insertionFunc(env, static_cast<int>(content_type), list, @@ -228,10 +230,11 @@ // TODO(raymes): This check to see if '*' is the embedder is a hack that fixes // crbug.com/738377. In general querying the settings for patterns is broken // and needs to be fixed. See crbug.com/738757. - if (embedder_str == "*") + if (embedder_str == "*") { embedding_origin = requesting_origin; - else + } else { embedding_origin = GURL(embedder_str); + } // If `content_type` is permission, then we need to apply a set of // verifications before reading its value in `HostContentSettingsMap`. @@ -468,6 +471,16 @@ } } +static jboolean +JNI_WebsitePreferenceBridge_CanAddExceptionsForJavascriptOptimizerSetting( + JNIEnv* env) { + // If origin isolation for JavaScript-optimization exceptions is disabled, + // Javascript-optimization exceptions only work for TLDs (ex foo.com) and not + // for suborigins (ex bar.foo.com). + return site_isolation::SiteIsolationPolicy:: + IsOriginIsolationForJsOptExceptionsSupported(); +} + static ScopedJavaLocalRef<jobject> JNI_WebsitePreferenceBridge_GetGeolocationSettingForOrigin( JNIEnv* env, @@ -578,8 +591,9 @@ GetChooserContext(jbrowser_context_handle, type); // The ObjectPermissionContextBase can be null if the embedder doesn't support // the given ContentSettingsType. - if (!context) + if (!context) { return; + } for (const auto& object : context->GetAllGrantedObjects()) { // Remove the trailing slash so that origins are matched correctly in // SingleWebsitePreferences.mergePermissionInfoForTopLevelOrigin. @@ -662,8 +676,9 @@ storage::UsageInfoEntries::const_iterator i; for (i = entries.begin(); i != entries.end(); ++i) { - if (i->usage <= 0) + if (i->usage <= 0) { continue; + } ScopedJavaLocalRef<jstring> host = ConvertUTF8ToJavaString(env, i->host); Java_WebsitePreferenceBridge_insertStorageInfoIntoList(env, list, host, @@ -1113,8 +1128,9 @@ static_cast<ContentSettingsType>(content_settings_type)); ScopedJavaLocalRef<jstring> jembedder; for (const GURL& embargoed_origin : embargoed_origins) { - if (base::Contains(seen_origins, embargoed_origin.spec())) + if (base::Contains(seen_origins, embargoed_origin.spec())) { continue; + } std::string embargoed_origin_pattern = ContentSettingsPattern::FromURLNoWildcard(embargoed_origin).ToString(); Java_WebsitePreferenceBridge_addContentSettingExceptionToList(
diff --git a/components/browser_ui/strings/android/site_settings.grdp b/components/browser_ui/strings/android/site_settings.grdp index 54d6a0d..6e51b78 100644 --- a/components/browser_ui/strings/android/site_settings.grdp +++ b/components/browser_ui/strings/android/site_settings.grdp
@@ -539,6 +539,10 @@ Do not speed up sites with Chrome's V8 engine but make Chrome slightly more resistant to attacks </message> + <message name="IDS_WEBSITE_SETTINGS_JS_OPT_ADD_EXCEPTIONS_DISABLED_REASON" desc="Explains that the add-exception button is disabled because the Android device does not have enough RAM."> + Exceptions for specific websites disallowed because Android device does not have enough RAM. + </message> + <!-- Bluetooth --> <message name="IDS_WEBSITE_SETTINGS_CATEGORY_BLUETOOTH_ASK" desc="Summary text explaining that the Bluetooth permission is set to ask the user for permission to access individual devices. To be shown in the list of permission categories.">
diff --git a/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_JS_OPT_ADD_EXCEPTIONS_DISABLED_REASON.png.sha1 b/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_JS_OPT_ADD_EXCEPTIONS_DISABLED_REASON.png.sha1 new file mode 100644 index 0000000..26720b6f --- /dev/null +++ b/components/browser_ui/strings/android/site_settings_grdp/IDS_WEBSITE_SETTINGS_JS_OPT_ADD_EXCEPTIONS_DISABLED_REASON.png.sha1
@@ -0,0 +1 @@ +8f19d11f78f2aeb236441cf6be2ea419ad42c669 \ No newline at end of file
diff --git a/components/browser_ui/theme/android/java/res/values/attrs.xml b/components/browser_ui/theme/android/java/res/values/attrs.xml index b8207f6..d43d27c 100644 --- a/components/browser_ui/theme/android/java/res/values/attrs.xml +++ b/components/browser_ui/theme/android/java/res/values/attrs.xml
@@ -10,4 +10,8 @@ <attr name="defaultMediumFontFamily" format="reference|string" /> <attr name="toolbarButtonWidth" format="dimension" /> <attr name="toolbarButtonHeight" format="dimension" /> + <attr name="toolbarButtonMarginHorizontal" format="dimension" /> + <attr name="toolbarButtonMarginVertical" format="dimension" /> + <attr name="closeButtonWidth" format="dimension" /> + <attr name="closeButtonHeight" format="dimension" /> </resources> \ No newline at end of file
diff --git a/components/browser_ui/theme/android/java/res/values/themes.xml b/components/browser_ui/theme/android/java/res/values/themes.xml index 7dfaee3..5346551 100644 --- a/components/browser_ui/theme/android/java/res/values/themes.xml +++ b/components/browser_ui/theme/android/java/res/values/themes.xml
@@ -123,9 +123,15 @@ <item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="35">true</item> - <!-- Toolbar button size. --> + <!-- Toolbar button dimensions. --> <item name="toolbarButtonWidth">@dimen/toolbar_button_width</item> <item name="toolbarButtonHeight">@dimen/toolbar_button_height</item> + <item name="toolbarButtonMarginHorizontal">@dimen/toolbar_button_margin_horizontal</item> + <item name="toolbarButtonMarginVertical">@dimen/toolbar_button_margin_vertical</item> + + <!-- Close button dimensions. --> + <item name="closeButtonWidth">@dimen/close_button_width</item> + <item name="closeButtonHeight">@dimen/close_button_height</item> </style> <style name="Base.V31.Theme.BrowserUI" parent="Base.V21.Theme.BrowserUI" /> @@ -210,9 +216,15 @@ <item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="35">true</item> - <!-- Toolbar button size. --> + <!-- Toolbar button dimensions. --> <item name="toolbarButtonWidth">@dimen/toolbar_button_width</item> <item name="toolbarButtonHeight">@dimen/toolbar_button_height</item> + <item name="toolbarButtonMarginHorizontal">@dimen/toolbar_button_margin_horizontal</item> + <item name="toolbarButtonMarginVertical">@dimen/toolbar_button_margin_vertical</item> + + <!-- Close button dimensions. --> + <item name="closeButtonWidth">@dimen/close_button_width</item> + <item name="closeButtonHeight">@dimen/close_button_height</item> </style> <!-- Overridden by night mode. --> <style name="Theme.BrowserUI.DialogWhenLarge.DayNight" parent="Theme.BrowserUI.DialogWhenLarge"/> @@ -312,9 +324,15 @@ <item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="35">true</item> - <!-- Toolbar button size. --> + <!-- Toolbar button dimensions. --> <item name="toolbarButtonWidth">@dimen/toolbar_button_width</item> <item name="toolbarButtonHeight">@dimen/toolbar_button_height</item> + <item name="toolbarButtonMarginHorizontal">@dimen/toolbar_button_margin_horizontal</item> + <item name="toolbarButtonMarginVertical">@dimen/toolbar_button_margin_vertical</item> + + <!-- Close button dimensions. --> + <item name="closeButtonWidth">@dimen/close_button_width</item> + <item name="closeButtonHeight">@dimen/close_button_height</item> </style> <!-- Overridden by night mode. --> <style name="Theme.BrowserUI.AlertDialog.NoActionBar.DayNight" parent="Theme.BrowserUI.AlertDialog.NoActionBar"/> @@ -359,8 +377,12 @@ <item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="35">false</item> </style> - <style name="ThemeOverlay.BrowserUI.ToolbarButtonSizeDesktop" parent=""> + <style name="ThemeOverlay.BrowserUI.DesktopDensity" parent=""> + <item name="closeButtonWidth">@dimen/close_button_width_desktop</item> + <item name="closeButtonHeight">@dimen/close_button_height_desktop</item> <item name="toolbarButtonWidth">@dimen/toolbar_button_width_desktop</item> <item name="toolbarButtonHeight">@dimen/toolbar_button_height_desktop</item> + <item name="toolbarButtonMarginHorizontal">@dimen/toolbar_button_margin_horizontal_desktop</item> + <item name="toolbarButtonMarginVertical">@dimen/toolbar_button_margin_vertical_desktop</item> </style> </resources>
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ClipDrawableProgressBar.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ClipDrawableProgressBar.java index 4182cfe..fe873aa 100644 --- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ClipDrawableProgressBar.java +++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ClipDrawableProgressBar.java
@@ -32,7 +32,6 @@ public final Rect progressBarRect = new Rect(); public final Rect progressBarBackgroundRect = new Rect(); public final Rect progressBarStaticBackgroundRect = new Rect(); - public final Rect progressBarEndIndicator = new Rect(); public int progressBarColor; public int progressBarBackgroundColor; @@ -62,7 +61,6 @@ @Nullable private ColorDrawable mForegroundColorDrawable; @Nullable private GradientDrawable mForegroundGradientDrawable; @Nullable private GradientDrawable mBackgroundGradientDrawable; - @Nullable private GradientDrawable mEndCapCircleDrawable; private int mForegroundColor; private int mBackgroundColor; private int mStaticBackgroundColor; @@ -89,7 +87,6 @@ public ClipDrawableProgressBar(Context context, AttributeSet attrs) { super(context, attrs); - setScaleType(ScaleType.FIT_XY); // Ensure the drawable fills the ImageView mDesiredVisibility = getVisibility(); mForegroundColor = SemanticColorUtils.getProgressBarForeground(getContext()); @@ -127,20 +124,12 @@ // Background will be fully visible initially. backgroundScaleDrawable.setLevel(DRAWABLE_MAX_LEVEL); - // Create the end circular stop indicator - mEndCapCircleDrawable = createGradientDrawable(mForegroundColor, GradientDrawable.OVAL); - mEndCapCircleDrawable.setSize(mProgressBarHeight, mProgressBarHeight); - - // A LayerDrawable with the 2 moving components, foreground and background, and the - // end stop indicator. Layers are drawn in the order they are added to the array, + // A LayerDrawable with the 2 moving components, foreground and background. Layers + // are drawn in the order they are added to the array, // with the last one appearing on top. - Drawable[] layers = - {foregroundScaleDrawable, backgroundScaleDrawable, mEndCapCircleDrawable}; + Drawable[] layers = {foregroundScaleDrawable, backgroundScaleDrawable}; LayerDrawable layerDrawable = new LayerDrawable(layers); - // The circle (layer 2) will be drawn at the right end of the progress bar. - layerDrawable.setLayerGravity(2, Gravity.END | Gravity.CENTER_VERTICAL); - setImageDrawable(layerDrawable); } else { mForegroundColorDrawable = new ColorDrawable(mForegroundColor); @@ -282,7 +271,6 @@ } } - int endIndicatorSize = getBottom() - getTop(); if (ViewCompat.getLayoutDirection(this) == LAYOUT_DIRECTION_LTR) { drawingInfoOut.progressBarStaticBackgroundRect.set( getLeft(), getTop(), getRight(), getBottom()); @@ -304,11 +292,6 @@ getRight(), getBottom()); } - drawingInfoOut.progressBarEndIndicator.set( - getRight() - endIndicatorSize, - getTop(), - getRight(), - getBottom()); } else { drawingInfoOut.progressBarStaticBackgroundRect.set( getRight(), getTop(), getLeft(), getBottom()); @@ -330,11 +313,6 @@ drawingInfoOut.progressBarRect.left, getBottom()); } - drawingInfoOut.progressBarEndIndicator.set( - getLeft(), - getTop(), - getLeft() + endIndicatorSize, - getBottom()); } } @@ -397,9 +375,7 @@ public void setForegroundColor(int color) { if (useGradientDrawable()) { assert mForegroundGradientDrawable != null; - assert mEndCapCircleDrawable != null; mForegroundGradientDrawable.setColor(color); - mEndCapCircleDrawable.setColor(color); } else { assert mForegroundColorDrawable != null; mForegroundColorDrawable.setColor(color);
diff --git a/components/components_strings.grd b/components/components_strings.grd index 039e559..3050751 100644 --- a/components/components_strings.grd +++ b/components/components_strings.grd
@@ -337,6 +337,7 @@ <part file="tab_resume_strings.grdp" /> <part file="translate_strings.grdp" /> <part file="undo_strings.grdp" /> + <part file="user_data_importer_strings.grdp" /> <part file="user_education_strings.grdp" /> <part file="version_ui_strings.grdp" /> <part file="webapps_strings.grdp" />
diff --git a/components/data_sharing/internal/BUILD.gn b/components/data_sharing/internal/BUILD.gn index 5d3a00ea..b42236f 100644 --- a/components/data_sharing/internal/BUILD.gn +++ b/components/data_sharing/internal/BUILD.gn
@@ -86,6 +86,19 @@ } } +static_library("personal_collaboration_data_internal") { + sources = [ + "personal_collaboration_data/personal_collaboration_data_service_impl.cc", + "personal_collaboration_data/personal_collaboration_data_service_impl.h", + ] + + deps = [ + "//base", + "//components/data_sharing/public", + "//components/keyed_service/core", + ] +} + source_set("unit_tests") { testonly = true @@ -101,6 +114,7 @@ "group_data_proto_utils_unittest.cc", "group_data_store_unittest.cc", "logger_impl_unittest.cc", + "personal_collaboration_data/personal_collaboration_data_service_impl_unittest.cc", "preview_server_proxy_unittest.cc", ] @@ -114,6 +128,7 @@ deps = [ ":internal", + ":personal_collaboration_data_internal", "//base", "//base/test:test_support", "//components/data_sharing:test_support",
diff --git a/components/data_sharing/internal/personal_collaboration_data/personal_collaboration_data_service_impl.cc b/components/data_sharing/internal/personal_collaboration_data/personal_collaboration_data_service_impl.cc new file mode 100644 index 0000000..9f921871 --- /dev/null +++ b/components/data_sharing/internal/personal_collaboration_data/personal_collaboration_data_service_impl.cc
@@ -0,0 +1,19 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/data_sharing/internal/personal_collaboration_data/personal_collaboration_data_service_impl.h" + +namespace data_sharing::personal_collaboration_data { + +PersonalCollaborationDataServiceImpl::PersonalCollaborationDataServiceImpl() = + default; + +PersonalCollaborationDataServiceImpl::~PersonalCollaborationDataServiceImpl() = + default; + +bool PersonalCollaborationDataServiceImpl::IsInitialized() const { + return false; +} + +} // namespace data_sharing::personal_collaboration_data
diff --git a/components/data_sharing/internal/personal_collaboration_data/personal_collaboration_data_service_impl.h b/components/data_sharing/internal/personal_collaboration_data/personal_collaboration_data_service_impl.h new file mode 100644 index 0000000..723143c --- /dev/null +++ b/components/data_sharing/internal/personal_collaboration_data/personal_collaboration_data_service_impl.h
@@ -0,0 +1,31 @@ +// 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_DATA_SHARING_INTERNAL_PERSONAL_COLLABORATION_DATA_PERSONAL_COLLABORATION_DATA_SERVICE_IMPL_H_ +#define COMPONENTS_DATA_SHARING_INTERNAL_PERSONAL_COLLABORATION_DATA_PERSONAL_COLLABORATION_DATA_SERVICE_IMPL_H_ + +#include "components/data_sharing/public/personal_collaboration_data/personal_collaboration_data_service.h" + +namespace data_sharing::personal_collaboration_data { + +// The core class for managing personal account linked collaboration data. +class PersonalCollaborationDataServiceImpl + : public PersonalCollaborationDataService { + public: + PersonalCollaborationDataServiceImpl(); + ~PersonalCollaborationDataServiceImpl() override; + + // Disallow copy/assign. + PersonalCollaborationDataServiceImpl( + const PersonalCollaborationDataServiceImpl&) = delete; + PersonalCollaborationDataServiceImpl& operator=( + const PersonalCollaborationDataServiceImpl&) = delete; + + // PersonalCollaborationDataService implementation. + bool IsInitialized() const override; +}; + +} // namespace data_sharing::personal_collaboration_data + +#endif // COMPONENTS_DATA_SHARING_INTERNAL_PERSONAL_COLLABORATION_DATA_PERSONAL_COLLABORATION_DATA_SERVICE_IMPL_H_
diff --git a/components/data_sharing/internal/personal_collaboration_data/personal_collaboration_data_service_impl_unittest.cc b/components/data_sharing/internal/personal_collaboration_data/personal_collaboration_data_service_impl_unittest.cc new file mode 100644 index 0000000..ad1cf45 --- /dev/null +++ b/components/data_sharing/internal/personal_collaboration_data/personal_collaboration_data_service_impl_unittest.cc
@@ -0,0 +1,27 @@ +// 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/data_sharing/internal/personal_collaboration_data/personal_collaboration_data_service_impl.h" + +#include <memory> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace data_sharing::personal_collaboration_data { + +class PersonalCollaborationDataServiceImplTest : public testing::Test { + public: + void SetUp() override { + service_ = std::make_unique<PersonalCollaborationDataServiceImpl>(); + } + + protected: + std::unique_ptr<PersonalCollaborationDataServiceImpl> service_; +}; + +TEST_F(PersonalCollaborationDataServiceImplTest, TestServiceConstruction) { + EXPECT_FALSE(service_->IsInitialized()); +} + +} // namespace data_sharing::personal_collaboration_data
diff --git a/components/data_sharing/public/BUILD.gn b/components/data_sharing/public/BUILD.gn index 0983dcde..5b392dc 100644 --- a/components/data_sharing/public/BUILD.gn +++ b/components/data_sharing/public/BUILD.gn
@@ -18,6 +18,7 @@ "data_sharing_sdk_delegate.h", "data_sharing_service.h", "data_sharing_ui_delegate.h", + "personal_collaboration_data/personal_collaboration_data_service.h", "share_url_interception_context.cc", "share_url_interception_context.h", ]
diff --git a/components/data_sharing/public/personal_collaboration_data/personal_collaboration_data_service.h b/components/data_sharing/public/personal_collaboration_data/personal_collaboration_data_service.h new file mode 100644 index 0000000..0a218df --- /dev/null +++ b/components/data_sharing/public/personal_collaboration_data/personal_collaboration_data_service.h
@@ -0,0 +1,25 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_DATA_SHARING_PUBLIC_PERSONAL_COLLABORATION_DATA_PERSONAL_COLLABORATION_DATA_SERVICE_H_ +#define COMPONENTS_DATA_SHARING_PUBLIC_PERSONAL_COLLABORATION_DATA_PERSONAL_COLLABORATION_DATA_SERVICE_H_ + +#include "base/supports_user_data.h" +#include "components/keyed_service/core/keyed_service.h" + +namespace data_sharing::personal_collaboration_data { + +// The core class for managing personal account linked collaboration data. +class PersonalCollaborationDataService : public KeyedService, + public base::SupportsUserData { + public: + ~PersonalCollaborationDataService() override = default; + + // Returns whether the service has fully initialized. + virtual bool IsInitialized() const = 0; +}; + +} // namespace data_sharing::personal_collaboration_data + +#endif // COMPONENTS_DATA_SHARING_PUBLIC_PERSONAL_COLLABORATION_DATA_PERSONAL_COLLABORATION_DATA_SERVICE_H_
diff --git a/components/domain_reliability/beacon.h b/components/domain_reliability/beacon.h index b33caf2..174aa6bc 100644 --- a/components/domain_reliability/beacon.h +++ b/components/domain_reliability/beacon.h
@@ -15,10 +15,6 @@ #include "net/base/net_error_details.h" #include "url/gurl.h" -namespace base { -class Value; -} // namespace base - namespace domain_reliability { // The per-request data that is uploaded to the Domain Reliability collector.
diff --git a/components/facilitated_payments/core/features/features.cc b/components/facilitated_payments/core/features/features.cc index 14c92ef..c3c01fc 100644 --- a/components/facilitated_payments/core/features/features.cc +++ b/components/facilitated_payments/core/features/features.cc
@@ -36,7 +36,7 @@ // When enabled, Pix will be able to send multiple server request at a time. BASE_FEATURE(kSupportMultipleServerRequestsForPixPayments, "SupportMultipleServerRequestsForPixPayments", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); #if BUILDFLAG(IS_ANDROID) // When enabled, Chrome will offer an app list when a supported payment link is
diff --git a/components/feed/core/v2/api_test/feed_api_stream_unittest.cc b/components/feed/core/v2/api_test/feed_api_stream_unittest.cc index 9ad65b5d..8707d8b 100644 --- a/components/feed/core/v2/api_test/feed_api_stream_unittest.cc +++ b/components/feed/core/v2/api_test/feed_api_stream_unittest.cc
@@ -4180,6 +4180,16 @@ EXPECT_FALSE(response_translator_.InjectedResponseConsumed()); } +TEST_F(FeedApiTest, GetFeedUrls) { + response_translator_.InjectResponse(MakeTypicalInitialModelState()); + TestForYouSurface surface(stream_.get()); + WaitForIdleTaskQueue(); + std::vector<std::string> urls = stream_->GetFeedUrls(surface.GetSurfaceId()); + ASSERT_EQ(2u, urls.size()); + EXPECT_EQ("http://content0", urls.at(0)); + EXPECT_EQ("http://content1", urls.at(1)); +} + class SignedOutViewDemotionTest : public FeedApiTest { public: void SetUp() override {
diff --git a/components/feed/core/v2/feed_stream.cc b/components/feed/core/v2/feed_stream.cc index 970af1c..0a539ef1 100644 --- a/components/feed/core/v2/feed_stream.cc +++ b/components/feed/core/v2/feed_stream.cc
@@ -961,6 +961,32 @@ return (fetch_time > base::Time::Now()) ? base::Time() : fetch_time; } +std::vector<std::string> FeedStream::GetFeedUrls(SurfaceId surface_id) { + FeedStreamSurface* surface = FindSurface(surface_id); + if (!surface) { + return {}; + } + Stream& stream = GetStream(surface->GetStreamType()); + if (!stream.model) { + return {}; + } + + std::vector<std::string> urls; + for (ContentRevision content_revision : stream.model->GetContentList()) { + const feedstore::Content* content = + stream.model->FindContent(content_revision); + if (content) { + std::string url = content->prefetch_metadata(0).uri(); + // Exclude video streaming cards. + if (!url.starts_with("https://www.youtube.com") && + !url.starts_with("https://m.youtube.com")) { + urls.push_back(url); + } + } + } + return urls; +} + void FeedStream::LoadModelForTesting(const StreamType& stream_type, std::unique_ptr<StreamModel> model) { LoadModel(stream_type, std::move(model));
diff --git a/components/feed/core/v2/feed_stream.h b/components/feed/core/v2/feed_stream.h index 65bed18..5dbf368 100644 --- a/components/feed/core/v2/feed_stream.h +++ b/components/feed/core/v2/feed_stream.h
@@ -200,6 +200,7 @@ SurfaceId surface_id, base::TimeDelta elapsed) override; base::Time GetLastFetchTime(SurfaceId surface_id) override; + std::vector<std::string> GetFeedUrls(SurfaceId surface_id) override; void SetContentOrder(const StreamType& stream_type, ContentOrder content_order) override; ContentOrder GetContentOrder(const StreamType& stream_type) const override;
diff --git a/components/feed/core/v2/public/feed_api.h b/components/feed/core/v2/public/feed_api.h index 295a2ebd..0dfae57 100644 --- a/components/feed/core/v2/public/feed_api.h +++ b/components/feed/core/v2/public/feed_api.h
@@ -242,6 +242,9 @@ SurfaceId surface_id, base::TimeDelta elapsed) = 0; + // Returns a list of feed article urls. + virtual std::vector<std::string> GetFeedUrls(SurfaceId surface_id) = 0; + // The following methods are used for the internals page. virtual DebugStreamData GetDebugStreamData() = 0;
diff --git a/components/feed/core/v2/public/test/stub_feed_api.cc b/components/feed/core/v2/public/test/stub_feed_api.cc index 7eaa52f..2a197f7 100644 --- a/components/feed/core/v2/public/test/stub_feed_api.cc +++ b/components/feed/core/v2/public/test/stub_feed_api.cc
@@ -64,6 +64,10 @@ return base::Time(); } +std::vector<std::string> StubFeedApi::GetFeedUrls(SurfaceId surface_id) { + return {}; +} + ContentOrder StubFeedApi::GetContentOrder(const StreamType& stream_type) const { return ContentOrder::kUnspecified; }
diff --git a/components/feed/core/v2/public/test/stub_feed_api.h b/components/feed/core/v2/public/test/stub_feed_api.h index 5dd4dd3..2f503532 100644 --- a/components/feed/core/v2/public/test/stub_feed_api.h +++ b/components/feed/core/v2/public/test/stub_feed_api.h
@@ -122,6 +122,7 @@ void SetForcedStreamUpdateForDebugging( const feedui::StreamUpdate& stream_update) override {} base::Time GetLastFetchTime(SurfaceId surface_id) override; + std::vector<std::string> GetFeedUrls(SurfaceId surface_id) override; void SetContentOrder(const StreamType& stream_type, ContentOrder content_order) override {} ContentOrder GetContentOrder(const StreamType& stream_type) const override;
diff --git a/components/ip_protection/common/ip_protection_core_impl.cc b/components/ip_protection/common/ip_protection_core_impl.cc index e30ff90..609fad6 100644 --- a/components/ip_protection/common/ip_protection_core_impl.cc +++ b/components/ip_protection/common/ip_protection_core_impl.cc
@@ -41,7 +41,7 @@ // only with SCHEME_QUIC. std::vector<net::ProxyChain> MakeQuicProxyList( const std::vector<net::ProxyChain>& proxy_list, - bool include_https_fallback = true) { + bool include_https_fallback) { if (proxy_list.empty()) { return proxy_list; }
diff --git a/components/optimization_guide/content/browser/frame_metadata_observer_browsertest.cc b/components/optimization_guide/content/browser/frame_metadata_observer_browsertest.cc index e28fd57..9d777f0 100644 --- a/components/optimization_guide/content/browser/frame_metadata_observer_browsertest.cc +++ b/components/optimization_guide/content/browser/frame_metadata_observer_browsertest.cc
@@ -18,6 +18,7 @@ #include "services/metrics/public/cpp/ukm_builders.h" #include "services/service_manager/public/cpp/interface_provider.h" #include "third_party/blink/public/common/features_generated.h" +#include "third_party/blink/public/mojom/content_extraction/ai_page_content_metadata.mojom.h" #include "third_party/blink/public/mojom/content_extraction/frame_metadata_observer_registry.mojom.h" #include "ui/display/display_switches.h" @@ -27,9 +28,6 @@ namespace { -// Allow 1px differences from rounding. -#define EXPECT_ALMOST_EQ(a, b) EXPECT_LE(abs(a - b), 1); - base::FilePath GetTestDataDir() { return base::FilePath{ FILE_PATH_LITERAL("components/test/data/optimization_guide")}; @@ -37,7 +35,8 @@ class FrameMetadataObserverBrowserTest : public content::ContentBrowserTest, - public blink::mojom::PaidContentMetadataObserver { + public blink::mojom::PaidContentMetadataObserver, + public blink::mojom::MetaTagsObserver { public: FrameMetadataObserverBrowserTest() = default; @@ -75,7 +74,8 @@ } bool LoadPage(GURL url) { - callback_waiter_.Clear(); + paid_content_callback_waiter_.Clear(); + metadata_callback_waiter_.Clear(); return content::NavigateToURL(web_contents(), url); } @@ -84,11 +84,16 @@ web_contents()->GetPrimaryMainFrame()); } - void AddObserver() { - auto* rfh = web_contents()->GetPrimaryMainFrame(); - rfh->GetRemoteInterfaces()->GetInterface( + void BindRegistry() { + if (frame_metadata_observer_registry_.is_bound()) { + return; + } + web_contents()->GetPrimaryMainFrame()->GetRemoteInterfaces()->GetInterface( frame_metadata_observer_registry_.BindNewPipeAndPassReceiver()); + } + void AddPaidContentObserver() { + BindRegistry(); mojo::PendingRemote<blink::mojom::PaidContentMetadataObserver> remote; frame_metadata_observer_receiver_.Bind( remote.InitWithNewPipeAndPassReceiver()); @@ -99,21 +104,57 @@ // Invoked when the frame metadata changes. void OnPaidContentMetadataChanged(bool has_paid_content) override { - callback_waiter_.SetValue(has_paid_content); + paid_content_callback_waiter_.SetValue(has_paid_content); } - void WaitForCallback() { - ASSERT_TRUE(callback_waiter_.Wait()) + void WaitForPaidContentChanged() { + ASSERT_TRUE(paid_content_callback_waiter_.Wait()) << "Timed out waiting for OnPaidContentMetadataChanged callback"; } - bool hasPaidContent() { return callback_waiter_.Get(); } + bool hasPaidContent() { return paid_content_callback_waiter_.Get(); } + + void AddMetaTagsObserver(const std::vector<std::string>& names) { + BindRegistry(); + mojo::PendingRemote<blink::mojom::MetaTagsObserver> remote; + meta_tags_observer_receiver_.Bind(remote.InitWithNewPipeAndPassReceiver()); + + frame_metadata_observer_registry_->AddMetaTagsObserver(names, + std::move(remote)); + } + + // Invoked when the frame metadata changes. + void OnMetaTagsChanged(blink::mojom::PageMetadataPtr page_metadata) override { + metadata_callback_waiter_.SetValue(true); + page_metadata_ = std::move(page_metadata); + } + + void WaitForMetaTagsChanged() { + ASSERT_TRUE(metadata_callback_waiter_.Wait()) + << "Timed out waiting for OnMetaTagsChanged callback"; + } + + bool was_meta_tags_changed_called() { + return metadata_callback_waiter_.Get(); + } + blink::mojom::PageMetadataPtr& page_metadata() { return page_metadata_; } net::EmbeddedTestServer* https_server() { return https_server_.get(); } + void VerifyAuthorMetaTag() { + EXPECT_TRUE(was_meta_tags_changed_called()); + + blink::mojom::PageMetadataPtr& metadata = page_metadata(); + EXPECT_EQ(metadata->frame_metadata.size(), 1u); + EXPECT_EQ(metadata->frame_metadata[0]->meta_tags.size(), 1u); + EXPECT_EQ(metadata->frame_metadata[0]->meta_tags[0]->name, "author"); + EXPECT_EQ(metadata->frame_metadata[0]->meta_tags[0]->content, "Gary"); + } + protected: // TestFuture that will be signaled when the callback runs. - base::test::TestFuture<bool> callback_waiter_; + base::test::TestFuture<bool> paid_content_callback_waiter_; + base::test::TestFuture<bool> metadata_callback_waiter_; private: std::unique_ptr<net::EmbeddedTestServer> https_server_; @@ -121,13 +162,17 @@ frame_metadata_observer_registry_; mojo::Receiver<blink::mojom::PaidContentMetadataObserver> frame_metadata_observer_receiver_{this}; + mojo::Receiver<blink::mojom::MetaTagsObserver> meta_tags_observer_receiver_{ + this}; + + blink::mojom::PageMetadataPtr page_metadata_; }; IN_PROC_BROWSER_TEST_F(FrameMetadataObserverBrowserTest, PaidContent) { ASSERT_TRUE(LoadPage(https_server()->GetURL("/paid_content.html"))); - AddObserver(); - WaitForCallback(); + AddPaidContentObserver(); + WaitForPaidContentChanged(); EXPECT_TRUE(hasPaidContent()); } @@ -135,8 +180,8 @@ IN_PROC_BROWSER_TEST_F(FrameMetadataObserverBrowserTest, NoPaidContent) { ASSERT_TRUE(LoadPage(https_server()->GetURL("/simple.html"))); - AddObserver(); - WaitForCallback(); + AddPaidContentObserver(); + WaitForPaidContentChanged(); EXPECT_FALSE(hasPaidContent()); } @@ -147,12 +192,62 @@ // Wait for the page to load before adding the observer. ASSERT_TRUE(WaitForRenderFrameReady()); - AddObserver(); - WaitForCallback(); + AddPaidContentObserver(); + WaitForPaidContentChanged(); EXPECT_TRUE(hasPaidContent()); } +IN_PROC_BROWSER_TEST_F(FrameMetadataObserverBrowserTest, MetaTags) { + ASSERT_TRUE(LoadPage(https_server()->GetURL("/meta_tags.html"))); + + const std::vector<std::string> names = {"author", "subject"}; + + AddMetaTagsObserver(names); + WaitForMetaTagsChanged(); + + VerifyAuthorMetaTag(); +} + +IN_PROC_BROWSER_TEST_F(FrameMetadataObserverBrowserTest, MetaTagsLateObserver) { + ASSERT_TRUE(LoadPage(https_server()->GetURL("/meta_tags.html"))); + + // Wait for the page to load before adding the observer. + ASSERT_TRUE(WaitForRenderFrameReady()); + + const std::vector<std::string> names = {"author", "subject"}; + AddMetaTagsObserver(names); + WaitForMetaTagsChanged(); + + VerifyAuthorMetaTag(); +} + +IN_PROC_BROWSER_TEST_F(FrameMetadataObserverBrowserTest, MetaTagsNameMismatch) { + ASSERT_TRUE(LoadPage(https_server()->GetURL("/meta_tags.html"))); + + const std::vector<std::string> names = {"subject", "category"}; + + AddMetaTagsObserver(names); + WaitForMetaTagsChanged(); + + EXPECT_TRUE(was_meta_tags_changed_called()); + blink::mojom::PageMetadataPtr& metadata = page_metadata(); + EXPECT_TRUE(metadata->frame_metadata.empty()); +} + +IN_PROC_BROWSER_TEST_F(FrameMetadataObserverBrowserTest, NoMetaTags) { + ASSERT_TRUE(LoadPage(https_server()->GetURL("/simple.html"))); + + const std::vector<std::string> names = {"author", "subject"}; + AddMetaTagsObserver(names); + + WaitForMetaTagsChanged(); + + EXPECT_TRUE(was_meta_tags_changed_called()); + blink::mojom::PageMetadataPtr& metadata = page_metadata(); + EXPECT_TRUE(metadata->frame_metadata.empty()); +} + } // namespace } // namespace optimization_guide
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal index c66de765..dd2a475 160000 --- a/components/optimization_guide/internal +++ b/components/optimization_guide/internal
@@ -1 +1 @@ -Subproject commit c66de765dcd2a1b6a180828665edf21f1403a832 +Subproject commit dd2a4751e727af7bf8fb5baa1fb52e070f354c17
diff --git a/components/optimization_guide/proto/features/common_quality_data.proto b/components/optimization_guide/proto/features/common_quality_data.proto index ca2712d..aabe026 100644 --- a/components/optimization_guide/proto/features/common_quality_data.proto +++ b/components/optimization_guide/proto/features/common_quality_data.proto
@@ -301,6 +301,12 @@ ContentAttributeType attribute_type = 3 [features = { field_presence: EXPLICIT }]; // This is the geometry of the common_ancestor_dom_node_id. + // + // In the default mode, this is only populated if this is the + // `accessibility_focused_dom_node_id`. + // + // Populated for all nodes in + // `ANNOTATED_PAGE_CONTENT_MODE_ACTIONABLE_ELEMENTS.` Geometry geometry = 4 [features = { field_presence: EXPLICIT }]; // The interaction information for the content node.
diff --git a/components/optimization_guide/proto/features/password_change_submission.proto b/components/optimization_guide/proto/features/password_change_submission.proto index 59d6890..dd4f922 100644 --- a/components/optimization_guide/proto/features/password_change_submission.proto +++ b/components/optimization_guide/proto/features/password_change_submission.proto
@@ -240,6 +240,9 @@ // Password type that was used for the login. PasswordType password_type = 2 [features = { field_presence: EXPLICIT }]; + // Domain of the page. + string domain = 3 [features = { field_presence: EXPLICIT }]; + enum PasswordType { UNKNOWN = 0;
diff --git a/components/payments/content/BUILD.gn b/components/payments/content/BUILD.gn index 320aab9..1f2f05d 100644 --- a/components/payments/content/BUILD.gn +++ b/components/payments/content/BUILD.gn
@@ -41,6 +41,8 @@ "secure_payment_confirmation_app.h", "secure_payment_confirmation_app_factory.cc", "secure_payment_confirmation_app_factory.h", + "secure_payment_confirmation_credential_finder.cc", + "secure_payment_confirmation_credential_finder.h", "secure_payment_confirmation_model.cc", "secure_payment_confirmation_model.h", "secure_payment_confirmation_no_creds.cc", @@ -240,11 +242,14 @@ "mock_android_app_communication.h", "mock_payment_app_factory_delegate.cc", "mock_payment_app_factory_delegate.h", + "mock_secure_payment_confirmation_credential_finder.cc", + "mock_secure_payment_confirmation_credential_finder.h", "payment_event_response_util_unittest.cc", "payment_manifest_web_data_service_unittest.cc", "payment_method_manifest_table_unittest.cc", "secure_payment_confirmation_app_factory_unittest.cc", "secure_payment_confirmation_app_unittest.cc", + "secure_payment_confirmation_credential_finder_unittest.cc", "secure_payment_confirmation_model_unittest.cc", "secure_payment_confirmation_no_creds_model_unittest.cc", "secure_payment_confirmation_service_unittest.cc",
diff --git a/components/payments/content/mock_secure_payment_confirmation_credential_finder.cc b/components/payments/content/mock_secure_payment_confirmation_credential_finder.cc new file mode 100644 index 0000000..ab86c348 --- /dev/null +++ b/components/payments/content/mock_secure_payment_confirmation_credential_finder.cc
@@ -0,0 +1,14 @@ +// 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/payments/content/mock_secure_payment_confirmation_credential_finder.h" + +namespace payments { + +MockSecurePaymentConfirmationCredentialFinder:: + MockSecurePaymentConfirmationCredentialFinder() = default; +MockSecurePaymentConfirmationCredentialFinder:: + ~MockSecurePaymentConfirmationCredentialFinder() = default; + +} // namespace payments
diff --git a/components/payments/content/mock_secure_payment_confirmation_credential_finder.h b/components/payments/content/mock_secure_payment_confirmation_credential_finder.h new file mode 100644 index 0000000..593b533 --- /dev/null +++ b/components/payments/content/mock_secure_payment_confirmation_credential_finder.h
@@ -0,0 +1,35 @@ +// 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_PAYMENTS_CONTENT_MOCK_SECURE_PAYMENT_CONFIRMATION_CREDENTIAL_FINDER_H_ +#define COMPONENTS_PAYMENTS_CONTENT_MOCK_SECURE_PAYMENT_CONFIRMATION_CREDENTIAL_FINDER_H_ + +#include "components/payments/content/payment_manifest_web_data_service.h" +#include "components/payments/content/secure_payment_confirmation_credential_finder.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace payments { + +class MockSecurePaymentConfirmationCredentialFinder + : public SecurePaymentConfirmationCredentialFinder { + public: + MockSecurePaymentConfirmationCredentialFinder(); + ~MockSecurePaymentConfirmationCredentialFinder() override; + + // SecurePaymentConfirmationCredentialFinder: + MOCK_METHOD( + void, + GetMatchingCredentials, + (const std::vector<std::vector<uint8_t>>& credential_ids, + const std::string& relying_party_id, + const url::Origin& caller_origin, + webauthn::InternalAuthenticator* authenticator, + scoped_refptr<payments::PaymentManifestWebDataService> web_data_service, + SecurePaymentConfirmationCredentialFinderCallback result_callback), + (override)); +}; + +} // namespace payments + +#endif // COMPONENTS_PAYMENTS_CONTENT_MOCK_SECURE_PAYMENT_CONFIRMATION_CREDENTIAL_FINDER_H_
diff --git a/components/payments/content/secure_payment_confirmation_app_factory.cc b/components/payments/content/secure_payment_confirmation_app_factory.cc index 009e650..1beeb7f 100644 --- a/components/payments/content/secure_payment_confirmation_app_factory.cc +++ b/components/payments/content/secure_payment_confirmation_app_factory.cc
@@ -33,8 +33,6 @@ #include "components/payments/core/secure_payment_confirmation_credential.h" #include "components/payments/core/sizes.h" #include "components/webauthn/core/browser/internal_authenticator.h" -#include "components/webdata/common/web_data_results.h" -#include "components/webdata/common/web_data_service_base.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/browser/webauthn_security_utils.h" @@ -178,15 +176,6 @@ return true; } -// Determine if a given origin that is calling SPC with a given RP ID requires -// the credentials to be third-party enabled (i.e., the calling party is not the -// RP ID). -bool RequiresThirdPartyPaymentBit(const url::Origin& caller_origin, - const std::string& relying_party_id) { - return !content::OriginIsAllowedToClaimRelyingPartyId(relying_party_id, - caller_origin); -} - struct IconInfo { GURL url; std::optional<int> request_id; @@ -254,8 +243,9 @@ OnIsUserVerifyingPlatformAuthenticatorAvailable( std::unique_ptr<Request> request, bool is_available) { - if (!request->delegate || !request->delegate->GetWebContents()) + if (!request->delegate || !request->delegate->GetWebContents()) { return; + } if (!request->authenticator || (!is_available && !base::FeatureList::IsEnabled( @@ -263,10 +253,11 @@ #if BUILDFLAG(IS_ANDROID) if (base::FeatureList::IsEnabled( blink::features::kSecurePaymentConfirmationUxRefresh)) { - request->delegate->SetCanMakePaymentEvenWithoutApps(); // Skip getting matching credential IDs since the authenticator is not // available. - OnRetrievedCredentials(std::move(request), /*credentials=*/{}); + OnRetrievedCredentials( + std::move(request), + std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>>()); return; } #endif // BUILDFLAG(IS_ANDROID) @@ -275,62 +266,23 @@ return; } - // If we are relying on underlying credential-store level support for SPC, but - // it isn't available, ensure that canMakePayment() will return false by - // returning early here. - // - // This helps websites avoid a failure scenario when SPC appears to be - // available, but in practice it is non-functional due to lack of platform - // support. - if (base::FeatureList::IsEnabled( - features::kSecurePaymentConfirmationUseCredentialStoreAPIs) && - !request->authenticator->IsGetMatchingCredentialIdsSupported()) { - request->delegate->OnDoneCreatingPaymentApps(); - return; - } - - // Regardless of whether any credentials match, canMakePayment() and - // hasEnrolledInstrument() should return true for SPC when a user-verifying - // platform authenticator device is available. - request->delegate->SetCanMakePaymentEvenWithoutApps(); - - // If we have credential-store level support for SPC, we can query the store - // directly. Otherwise, we have to rely on the user profile database. - // - // Currently, credential store APIs are only available on Android. - if (base::FeatureList::IsEnabled( - features::kSecurePaymentConfirmationUseCredentialStoreAPIs)) { - std::string relying_party_id = request->mojo_request->rp_id; - const bool require_third_party_payment_bit = RequiresThirdPartyPaymentBit( - request->delegate->GetFrameSecurityOrigin(), relying_party_id); - - auto* request_ptr = request.get(); - request_ptr->authenticator->GetMatchingCredentialIds( - std::move(request_ptr->mojo_request->rp_id), - std::move(request_ptr->mojo_request->credential_ids), - require_third_party_payment_bit, - base::BindOnce(&SecurePaymentConfirmationAppFactory:: - OnGetMatchingCredentialIdsFromStore, - weak_ptr_factory_.GetWeakPtr(), std::move(request), - std::move(relying_party_id))); - } else { - WebDataServiceBase::Handle handle = - request->web_data_service->GetSecurePaymentConfirmationCredentials( - std::move(request->mojo_request->credential_ids), - std::move(request->mojo_request->rp_id), this); - requests_[handle] = std::move(request); - } + auto* request_ptr = request.get(); + credential_finder_->GetMatchingCredentials( + request_ptr->mojo_request->credential_ids, + request_ptr->mojo_request->rp_id, + request_ptr->delegate->GetFrameSecurityOrigin(), + request_ptr->authenticator.get(), request_ptr->web_data_service, + base::BindOnce( + &SecurePaymentConfirmationAppFactory::OnRetrievedCredentials, + weak_ptr_factory_.GetWeakPtr(), std::move(request))); } SecurePaymentConfirmationAppFactory::SecurePaymentConfirmationAppFactory() - : PaymentAppFactory(PaymentApp::Type::INTERNAL) {} - -SecurePaymentConfirmationAppFactory::~SecurePaymentConfirmationAppFactory() { - std::ranges::for_each(requests_, [&](const auto& pair) { - if (pair.second->web_data_service) - pair.second->web_data_service->CancelRequest(pair.first); - }); -} + : PaymentAppFactory(PaymentApp::Type::INTERNAL), + credential_finder_( + std::make_unique<SecurePaymentConfirmationCredentialFinder>()) {} +SecurePaymentConfirmationAppFactory::~SecurePaymentConfirmationAppFactory() = + default; void SecurePaymentConfirmationAppFactory::Create( base::WeakPtr<Delegate> delegate) { @@ -399,32 +351,6 @@ delegate->OnDoneCreatingPaymentApps(); } -void SecurePaymentConfirmationAppFactory::OnWebDataServiceRequestDone( - WebDataServiceBase::Handle handle, - std::unique_ptr<WDTypedResult> result) { - auto iterator = requests_.find(handle); - if (iterator == requests_.end()) - return; - - std::unique_ptr<Request> request = std::move(iterator->second); - requests_.erase(iterator); - DCHECK(request.get()); - if (!request->delegate || !request->web_contents()) - return; - - if (result && result->GetType() == SECURE_PAYMENT_CONFIRMATION) { - std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>> - credentials = static_cast<WDResult<std::vector< - std::unique_ptr<SecurePaymentConfirmationCredential>>>*>( - result.get()) - ->GetValue(); - OnRetrievedCredentials(std::move(request), std::move(credentials)); - } else { - request->delegate->OnDoneCreatingPaymentApps(); - return; - } -} - #if BUILDFLAG(IS_ANDROID) void SecurePaymentConfirmationAppFactory::SetBrowserBoundKeyStoreForTesting( scoped_refptr<BrowserBoundKeyStore> key_store) { @@ -432,28 +358,36 @@ } #endif // BUILDFLAG(IS_ANDROID) -void SecurePaymentConfirmationAppFactory::OnGetMatchingCredentialIdsFromStore( - std::unique_ptr<Request> request, - std::string relying_party_id, - std::vector<std::vector<uint8_t>> matching_credentials) { - std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>> credentials; - for (std::vector<uint8_t>& credential_id : matching_credentials) { - credentials.emplace_back( - std::make_unique<SecurePaymentConfirmationCredential>( - std::move(credential_id), relying_party_id, - /*user_id=*/std::vector<uint8_t>())); - } - OnRetrievedCredentials(std::move(request), std::move(credentials)); +void SecurePaymentConfirmationAppFactory::SetCredentialFinderForTesting( + std::unique_ptr<SecurePaymentConfirmationCredentialFinder> + credential_finder) { + credential_finder_ = std::move(credential_finder); } void SecurePaymentConfirmationAppFactory::OnRetrievedCredentials( std::unique_ptr<Request> request, - std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>> + std::optional< + std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>>> credentials) { + if (!request->delegate || !request->delegate->GetWebContents()) { + return; + } + + if (!credentials.has_value()) { + request->delegate->OnDoneCreatingPaymentApps(); + return; + } + + // Regardless of whether any credentials match, canMakePayment() and + // hasEnrolledInstrument() should return true for SPC when a user-verifying + // platform authenticator device is available. + request->delegate->SetCanMakePaymentEvenWithoutApps(); + // For the pilot phase, arbitrarily use the first matching credential. // TODO(crbug.com/40142088): Handle multiple credentials. - if (!credentials.empty()) - request->credential = std::move(credentials.front()); + if (!credentials->empty()) { + request->credential = std::move(credentials->front()); + } // Download the icons for the payment instrument icon and the payment entity // logos. These download URLs were passed into the PaymentRequest API. If
diff --git a/components/payments/content/secure_payment_confirmation_app_factory.h b/components/payments/content/secure_payment_confirmation_app_factory.h index 337bd5d..466c5fc3 100644 --- a/components/payments/content/secure_payment_confirmation_app_factory.h +++ b/components/payments/content/secure_payment_confirmation_app_factory.h
@@ -11,7 +11,7 @@ #include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" #include "components/payments/content/payment_app_factory.h" -#include "components/webdata/common/web_data_service_consumer.h" +#include "components/payments/content/secure_payment_confirmation_credential_finder.h" namespace payments { @@ -20,8 +20,7 @@ #endif // BUILDFLAG(IS_ANDROID) struct SecurePaymentConfirmationCredential; -class SecurePaymentConfirmationAppFactory : public PaymentAppFactory, - public WebDataServiceConsumer { +class SecurePaymentConfirmationAppFactory : public PaymentAppFactory { public: SecurePaymentConfirmationAppFactory(); ~SecurePaymentConfirmationAppFactory() override; @@ -34,16 +33,15 @@ // PaymentAppFactory: void Create(base::WeakPtr<Delegate> delegate) override; - // WebDataServiceConsumer: - void OnWebDataServiceRequestDone( - WebDataServiceBase::Handle handle, - std::unique_ptr<WDTypedResult> result) override; - #if BUILDFLAG(IS_ANDROID) void SetBrowserBoundKeyStoreForTesting( scoped_refptr<BrowserBoundKeyStore> key_store); #endif // BUILDFLAG(IS_ANDROID) + void SetCredentialFinderForTesting( + std::unique_ptr<SecurePaymentConfirmationCredentialFinder> + credential_finder); + private: struct Request; @@ -51,20 +49,10 @@ std::unique_ptr<Request> request, bool is_available); - // On platforms where we have credential-store level support for retrieving - // credentials (i.e., rather than using the user profile database), this - // callback will be called with the retrieved and matching credential ids. - // - // |relying_party_id| and |matching_credentials| are always std::move'd in, - // and so are not const-ref. - void OnGetMatchingCredentialIdsFromStore( - std::unique_ptr<Request> request, - std::string relying_party_id, - std::vector<std::vector<uint8_t>> matching_credentials); - void OnRetrievedCredentials( std::unique_ptr<Request> request, - std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>> + std::optional< + std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>>> credentials); void OnRetrievedBrowserBoundKeyId( @@ -75,10 +63,12 @@ // been set into the Request. void DidDownloadAllIcons(std::unique_ptr<Request> request); - std::map<WebDataServiceBase::Handle, std::unique_ptr<Request>> requests_; #if BUILDFLAG(IS_ANDROID) scoped_refptr<BrowserBoundKeyStore> browser_bound_key_store_for_testing_; #endif // BUILDFLAG(IS_ANDROID) + + std::unique_ptr<SecurePaymentConfirmationCredentialFinder> credential_finder_; + base::WeakPtrFactory<SecurePaymentConfirmationAppFactory> weak_ptr_factory_{ this}; };
diff --git a/components/payments/content/secure_payment_confirmation_app_factory_unittest.cc b/components/payments/content/secure_payment_confirmation_app_factory_unittest.cc index 3d7f4418..c5112fc8 100644 --- a/components/payments/content/secure_payment_confirmation_app_factory_unittest.cc +++ b/components/payments/content/secure_payment_confirmation_app_factory_unittest.cc
@@ -18,11 +18,11 @@ #include "components/payments/content/browser_binding/passkey_browser_binder.h" #include "components/payments/content/mock_payment_app_factory_delegate.h" #include "components/payments/content/mock_payment_manifest_web_data_service.h" +#include "components/payments/content/mock_secure_payment_confirmation_credential_finder.h" #include "components/payments/core/features.h" #include "components/payments/core/native_error_strings.h" #include "components/payments/core/secure_payment_confirmation_credential.h" #include "components/webauthn/core/browser/mock_internal_authenticator.h" -#include "components/webdata/common/web_data_results.h" #include "content/public/test/browser_task_environment.h" #include "content/public/test/test_browser_context.h" #include "content/public/test/test_web_contents_factory.h" @@ -37,7 +37,6 @@ #if BUILDFLAG(IS_ANDROID) #include "components/payments/content/secure_payment_confirmation_app.h" -#include "components/webdata/common/web_data_service_consumer.h" #endif // BUILDFLAG(IS_ANDROID) namespace payments { @@ -64,11 +63,6 @@ struct MockAuthenticatorOptions { bool is_user_verifying_platform_authenticator_available = true; - bool is_matching_credential_api_supported = true; - // When std::nullopt, GetMatchingCredentialIds() is not mocked and could - // be mocked by the caller of CreateMockInternalAuthenticator. - std::optional<std::vector<std::vector<uint8_t>>> - response_to_get_matching_credential_ids = std::nullopt; }; class SecurePaymentConfirmationAppFactoryTest : public testing::Test { @@ -85,6 +79,27 @@ ASSERT_TRUE(base::Base64Decode(kCredentialIdBase64, &credential_id_bytes_)); secure_payment_confirmation_app_factory_ = std::make_unique<SecurePaymentConfirmationAppFactory>(); + + auto mock_credential_finder = + std::make_unique<MockSecurePaymentConfirmationCredentialFinder>(); + mock_credential_finder_ = mock_credential_finder.get(); + secure_payment_confirmation_app_factory_->SetCredentialFinderForTesting( + std::move(mock_credential_finder)); + + mock_authenticator_ = CreateMockInternalAuthenticator(); + mock_service_ = base::MakeRefCounted<MockPaymentManifestWebDataService>(); + } + + std::unique_ptr<MockPaymentAppFactoryDelegate> CreateMockDelegate( + mojom::PaymentMethodDataPtr method_data) { + auto mock_delegate = std::make_unique<MockPaymentAppFactoryDelegate>( + web_contents_, std::move(method_data)); + EXPECT_CALL(*mock_delegate, CreateInternalAuthenticator()) + .WillOnce(Return(ByMove(std::move(mock_authenticator_)))); + EXPECT_CALL(*mock_delegate, GetPaymentManifestWebDataService()) + .WillRepeatedly(Return(mock_service_)); + + return mock_delegate; } std::unique_ptr<webauthn::MockInternalAuthenticator> @@ -95,13 +110,6 @@ IsUserVerifyingPlatformAuthenticatorAvailable(_)) .WillByDefault(RunOnceCallback<0>( options.is_user_verifying_platform_authenticator_available)); - ON_CALL(*mock_authenticator, IsGetMatchingCredentialIdsSupported()) - .WillByDefault(Return(options.is_matching_credential_api_supported)); - if (options.response_to_get_matching_credential_ids) { - EXPECT_CALL(*mock_authenticator, GetMatchingCredentialIds(_, _, _, _)) - .WillOnce(RunOnceCallback<3>( - std::move(*options.response_to_get_matching_credential_ids))); - } return mock_authenticator; } @@ -128,6 +136,20 @@ return spc_request; } + void MockFindMatchingCredential(std::string credential_id_bytes) { + std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>> + credentials; + std::vector<uint8_t> credential_id(credential_id_bytes.begin(), + credential_id_bytes.end()); + credentials.emplace_back( + std::make_unique<SecurePaymentConfirmationCredential>( + std::move(credential_id), kRpId, + /*user_id=*/std::vector<uint8_t>())); + + EXPECT_CALL(*mock_credential_finder_, GetMatchingCredentials) + .WillOnce(RunOnceCallback<5>(std::move(credentials))); + } + // Using mock time in this environment to reduce flakiness in TSAN builders. content::BrowserTaskEnvironment task_environment_{ base::test::TaskEnvironment::TimeSource::MOCK_TIME}; @@ -139,6 +161,12 @@ secure_payment_confirmation_app_factory_; std::string challenge_bytes_; std::string credential_id_bytes_; + std::unique_ptr<webauthn::MockInternalAuthenticator> mock_authenticator_; + scoped_refptr<MockPaymentManifestWebDataService> mock_service_; + // Owned by `secure_payment_confirmation_app_factory_`, so must be declared + // after it to avoid a dangling raw_ptr during destruction. + raw_ptr<MockSecurePaymentConfirmationCredentialFinder> + mock_credential_finder_; }; // Test that parsing a valid SecureConfirmationPaymentRequest succeeds. @@ -500,6 +528,103 @@ secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr()); } +TEST_F(SecurePaymentConfirmationAppFactoryTest, + AppDisabledIfCredentialFetchingFails) { + auto method_data = mojom::PaymentMethodData::New(); + method_data->supported_method = "secure-payment-confirmation"; + method_data->secure_payment_confirmation = + CreateSecurePaymentConfirmationRequest(); + + std::unique_ptr<MockPaymentAppFactoryDelegate> mock_delegate = + CreateMockDelegate(std::move(method_data)); + url::Origin caller_origin = url::Origin::Create(GURL("https://rp.example")); + EXPECT_CALL(*mock_delegate, GetFrameSecurityOrigin()) + .WillOnce(ReturnRef(caller_origin)); + + EXPECT_CALL(*mock_credential_finder_, GetMatchingCredentials) + .WillOnce(RunOnceCallback<5>(std::nullopt)); + + // When the credential store APIs are unavailable, we do not create an SPC app + // (which in turn makes canMakePayment() return false). + EXPECT_CALL(*mock_delegate, OnPaymentAppCreated(_)).Times(0); + EXPECT_CALL(*mock_delegate, OnPaymentAppCreationError(_, _)).Times(0); + EXPECT_CALL(*mock_delegate, OnDoneCreatingPaymentApps()).Times(1); + + secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr()); +} + +// Test that the payment instrument details string is made available to the +// SecurePaymentConfirmationApp. +TEST_F(SecurePaymentConfirmationAppFactoryTest, + SecureConfirmationPaymentRequest_PaymentInstrumentDetails) { + url::Origin caller_origin = url::Origin::Create(GURL("https://site.example")); + auto method_data = mojom::PaymentMethodData::New(); + method_data->supported_method = "secure-payment-confirmation"; + method_data->secure_payment_confirmation = + CreateSecurePaymentConfirmationRequest(); + method_data->secure_payment_confirmation->instrument->details = + "instrument details"; + std::vector<std::vector<uint8_t>> credential_ids = + method_data->secure_payment_confirmation->credential_ids; + ASSERT_EQ(credential_ids.size(), 1u); + GURL icon = method_data->secure_payment_confirmation->instrument->icon; + + std::unique_ptr<MockPaymentAppFactoryDelegate> mock_delegate = + CreateMockDelegate(std::move(method_data)); + EXPECT_CALL(*mock_delegate, GetFrameSecurityOrigin()) + .WillOnce(ReturnRef(caller_origin)); + + std::unique_ptr<PaymentApp> secure_payment_confirmation_app; + EXPECT_CALL(*mock_delegate, OnPaymentAppCreated(_)) + .WillOnce(MoveArg<0>(&secure_payment_confirmation_app)); + + MockFindMatchingCredential(credential_id_bytes_); + + secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr()); + std::vector<gfx::Size> icon_sizes({{32, 32}}); + std::vector<SkBitmap> icon_bitmaps(1); + icon_bitmaps[0].allocN32Pixels(/*width=*/32, /*height=*/32); + static_cast<content::TestWebContents*>(web_contents_.get()) + ->TestDidDownloadImage(icon, /*http_status_code=*/200, + std::move(icon_bitmaps), std::move(icon_sizes)); + + ASSERT_TRUE(secure_payment_confirmation_app); + EXPECT_EQ(secure_payment_confirmation_app->GetSublabel(), + u"instrument details"); +} + +// Test that SecurePaymentConfirmationAppFactory passes the input credentials, +// relying party, etc, down into the credential finder. +TEST_F(SecurePaymentConfirmationAppFactoryTest, + CallsCredentialFinderWithCorrectParameters) { + auto method_data = mojom::PaymentMethodData::New(); + method_data->supported_method = "secure-payment-confirmation"; + method_data->secure_payment_confirmation = + CreateSecurePaymentConfirmationRequest(); + + std::vector<std::vector<uint8_t>> credential_ids = + method_data->secure_payment_confirmation->credential_ids; + std::string relying_party_id = + method_data->secure_payment_confirmation->rp_id; + url::Origin caller_origin = url::Origin::Create(GURL("https://site.example")); + + std::unique_ptr<MockPaymentAppFactoryDelegate> mock_delegate = + CreateMockDelegate(std::move(method_data)); + EXPECT_CALL(*mock_delegate, GetFrameSecurityOrigin()) + .WillOnce(ReturnRef(caller_origin)); + + // Ensure that the SecurePaymentConfirmationAppFactory extracts and passes in + // the correct set of credentials, relying party id, and caller origin. The + // remaining parameters are the InternalAuthenticator, the + // PaymentManifestWebDataService, and the result callback - these are not + // important to verify. + EXPECT_CALL(*mock_credential_finder_, + GetMatchingCredentials(Eq(credential_ids), relying_party_id, + caller_origin, _, _, _)); + + secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr()); +} + // Class wrapping tests relating to payment entity logos support in // SecurePaymentConfirmationAppFactory. class SecurePaymentConfirmationAppFactoryPaymentEntitiesLogosTest @@ -511,58 +636,8 @@ GURL("https://payment-entity-2.example/icon.png"); const GURL kPaymentEntity3LogoUrl = GURL("https://payment-entity-3.example/icon.png"); - const WebDataServiceBase::Handle kWebDataServiceHandle = 1234; - SecurePaymentConfirmationAppFactoryPaymentEntitiesLogosTest() { - // For test setup simplicity, force tests in this fixture to use the (mocked - // out) database storage path. - feature_list_.InitWithFeatures( - /*enabled_features=*/{blink::features:: - kSecurePaymentConfirmationUxRefresh}, - /*disabled_features=*/{ - features::kSecurePaymentConfirmationUseCredentialStoreAPIs}); - } - - void SetUp() override { - SecurePaymentConfirmationAppFactoryTest::SetUp(); - - mock_authenticator_ = CreateMockInternalAuthenticator(); - - mock_service_ = base::MakeRefCounted<MockPaymentManifestWebDataService>(); - EXPECT_CALL(*mock_service_, - GetSecurePaymentConfirmationCredentials(_, _, _)) - .WillOnce(Return(kWebDataServiceHandle)); - } - - std::unique_ptr<MockPaymentAppFactoryDelegate> CreateMockDelegate( - mojom::PaymentMethodDataPtr method_data) { - auto mock_delegate = std::make_unique<MockPaymentAppFactoryDelegate>( - web_contents_, std::move(method_data)); - EXPECT_CALL(*mock_delegate, CreateInternalAuthenticator()) - .WillOnce(Return(ByMove(std::move(mock_authenticator_)))); - EXPECT_CALL(*mock_delegate, GetPaymentManifestWebDataService()) - .WillRepeatedly(Return(mock_service_)); - - return mock_delegate; - } - - void FakeCredentialFetchedFromDatabase(std::string credential_id_bytes) { - std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>> - credentials; - std::vector<uint8_t> credential_id(credential_id_bytes.begin(), - credential_id_bytes.end()); - credentials.emplace_back( - std::make_unique<SecurePaymentConfirmationCredential>( - std::move(credential_id), kRpId, - /*user_id=*/std::vector<uint8_t>())); - - std::unique_ptr<WDTypedResult> result = std::make_unique<WDResult< - std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>>>>( - SECURE_PAYMENT_CONFIRMATION, std::move(credentials)); - - secure_payment_confirmation_app_factory_->OnWebDataServiceRequestDone( - kWebDataServiceHandle, std::move(result)); - } + SecurePaymentConfirmationAppFactoryPaymentEntitiesLogosTest() = default; // The height can be set here, and expected in a test using // IsSkBitmapWithHeight(). @@ -582,9 +657,8 @@ std::move(icon_sizes))); } - std::unique_ptr<webauthn::MockInternalAuthenticator> mock_authenticator_; - scoped_refptr<MockPaymentManifestWebDataService> mock_service_; - base::test::ScopedFeatureList feature_list_; + base::test::ScopedFeatureList feature_list_{ + blink::features::kSecurePaymentConfirmationUxRefresh}; }; Matcher<const SkBitmap*> IsSkBitmapWithHeight(int height) { @@ -621,14 +695,18 @@ std::unique_ptr<MockPaymentAppFactoryDelegate> mock_delegate = CreateMockDelegate(std::move(method_data)); + url::Origin caller_origin = url::Origin::Create(GURL("https://rp.example")); + EXPECT_CALL(*mock_delegate, GetFrameSecurityOrigin()) + .WillOnce(ReturnRef(caller_origin)); std::unique_ptr<PaymentApp> created_payment_app; EXPECT_CALL(*mock_delegate, OnPaymentAppCreated(_)) .WillOnce(MoveArg<0>(&created_payment_app)); + MockFindMatchingCredential(credential_id_bytes_); + secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr()); - FakeCredentialFetchedFromDatabase(credential_id_bytes_); FakeImageDownloaded(kInstrumentIconUrl); FakeImageDownloaded(kPaymentEntity1LogoUrl, /*succeeded=*/true, /*height=*/50); @@ -648,177 +726,9 @@ kPaymentEntity2LogoUrl))); } -class SecurePaymentConfirmationAppFactoryUsingCredentialStoreAPIsTest - : public SecurePaymentConfirmationAppFactoryTest { - public: - SecurePaymentConfirmationAppFactoryUsingCredentialStoreAPIsTest() { - feature_list_.InitAndEnableFeature( - features::kSecurePaymentConfirmationUseCredentialStoreAPIs); - } - - // Tests that the third-party payment bit is set as required or not correctly - // for a given origin. The RP ID for this setup is 'rp.example'. - void TestThirdPartyPaymentBitSetCorrectly( - url::Origin caller_origin, - bool expected_require_third_party_payment_bit) { - auto method_data = mojom::PaymentMethodData::New(); - method_data->supported_method = "secure-payment-confirmation"; - method_data->secure_payment_confirmation = - CreateSecurePaymentConfirmationRequest(); - - auto mock_delegate = std::make_unique<MockPaymentAppFactoryDelegate>( - web_contents_, std::move(method_data)); - - std::unique_ptr<webauthn::MockInternalAuthenticator> mock_authenticator = - CreateMockInternalAuthenticator(); - - // This is the core 'test' line of this method. It ensures that the - // authenticator device is asked for the right RP ID and credentials, and - // that the 'third-party payment bit required' flag is set as expected. - std::vector<std::vector<uint8_t>> expected_credential_ids; - expected_credential_ids.emplace_back(credential_id_bytes_.begin(), - credential_id_bytes_.end()); - EXPECT_CALL( - *mock_authenticator, - GetMatchingCredentialIds("rp.example", Eq(expected_credential_ids), - expected_require_third_party_payment_bit, _)) - .Times(1); - - scoped_refptr<MockPaymentManifestWebDataService> mock_service = - base::MakeRefCounted<MockPaymentManifestWebDataService>(); - - EXPECT_CALL(*mock_delegate, CreateInternalAuthenticator()) - .WillOnce(Return(ByMove(std::move(mock_authenticator)))); - EXPECT_CALL(*mock_delegate, GetPaymentManifestWebDataService()) - .WillRepeatedly(Return(mock_service)); - EXPECT_CALL(*mock_delegate, GetFrameSecurityOrigin()) - .WillOnce(ReturnRef(caller_origin)); - - secure_payment_confirmation_app_factory_->Create( - mock_delegate->GetWeakPtr()); - } - - private: - base::test::ScopedFeatureList feature_list_; -}; - -TEST_F( - SecurePaymentConfirmationAppFactoryUsingCredentialStoreAPIsTest, - CorrectlyCalculatesThirdPartyPaymentRequirement_OriginDifferentFromRpId) { - // Because the RP ID is 'rp.example', and our origin is - // 'https://site.example', this is a third-party payment authentication. - url::Origin caller_origin = url::Origin::Create(GURL("https://site.example")); - TestThirdPartyPaymentBitSetCorrectly( - caller_origin, /*expected_require_third_party_payment_bit=*/true); -} - -TEST_F(SecurePaymentConfirmationAppFactoryUsingCredentialStoreAPIsTest, - CorrectlyCalculatesThirdPartyPaymentRequirement_OriginSameAsRpId) { - // Because the RP ID is 'rp.example', and our origin is 'https://rp.example' - // too, this is a first-party payment authentication. - url::Origin caller_origin = url::Origin::Create(GURL("https://rp.example")); - TestThirdPartyPaymentBitSetCorrectly( - caller_origin, /*expected_require_third_party_payment_bit=*/false); -} - -TEST_F( - SecurePaymentConfirmationAppFactoryUsingCredentialStoreAPIsTest, - CorrectlyCalculatesThirdPartyPaymentRequirGement_OriginSameDomainAsRpId) { - // Because the RP ID is 'rp.example', and our origin is - // 'https://www.rp.example', this is a first-party payment authentication. - url::Origin caller_origin = - url::Origin::Create(GURL("https://www.rp.example")); - TestThirdPartyPaymentBitSetCorrectly( - caller_origin, /*expected_require_third_party_payment_bit=*/false); -} - -TEST_F(SecurePaymentConfirmationAppFactoryUsingCredentialStoreAPIsTest, - AppDisabledIfCredentialStoreAPIsUnavailable) { - auto method_data = mojom::PaymentMethodData::New(); - method_data->supported_method = "secure-payment-confirmation"; - method_data->secure_payment_confirmation = - CreateSecurePaymentConfirmationRequest(); - - auto mock_delegate = std::make_unique<MockPaymentAppFactoryDelegate>( - web_contents_, std::move(method_data)); - - std::unique_ptr<webauthn::MockInternalAuthenticator> mock_authenticator = - CreateMockInternalAuthenticator( - {.is_matching_credential_api_supported = false}); - // Expect IsGetMatchingCredentialIdsSupported() to be called to ensure we - // reach the point of checking for the API (instead of returning early due to - // another reason). - EXPECT_CALL(*mock_authenticator, IsGetMatchingCredentialIdsSupported); - - scoped_refptr<MockPaymentManifestWebDataService> mock_service = - base::MakeRefCounted<MockPaymentManifestWebDataService>(); - - EXPECT_CALL(*mock_delegate, CreateInternalAuthenticator()) - .WillOnce(Return(ByMove(std::move(mock_authenticator)))); - EXPECT_CALL(*mock_delegate, GetPaymentManifestWebDataService()) - .WillRepeatedly(Return(mock_service)); - - // When the credential store APIs are unavailable, we do not create an SPC app - // (which in turn makes canMakePayment() return false). - EXPECT_CALL(*mock_delegate, OnPaymentAppCreated(_)).Times(0); - EXPECT_CALL(*mock_delegate, OnPaymentAppCreationError(_, _)).Times(0); - EXPECT_CALL(*mock_delegate, OnDoneCreatingPaymentApps()).Times(1); - - secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr()); -} - -// Test that the payment instrument details string is made available to the -// SecurePaymentConfirmationApp. -TEST_F(SecurePaymentConfirmationAppFactoryUsingCredentialStoreAPIsTest, - SecureConfirmationPaymentRequest_PaymentInstrumentDetails) { - url::Origin caller_origin = url::Origin::Create(GURL("https://site.example")); - auto method_data = mojom::PaymentMethodData::New(); - method_data->supported_method = "secure-payment-confirmation"; - method_data->secure_payment_confirmation = - CreateSecurePaymentConfirmationRequest(); - method_data->secure_payment_confirmation->instrument->details = - "instrument details"; - std::vector<std::vector<uint8_t>> credential_ids = - method_data->secure_payment_confirmation->credential_ids; - ASSERT_EQ(credential_ids.size(), 1u); - GURL icon = method_data->secure_payment_confirmation->instrument->icon; - - std::unique_ptr<webauthn::MockInternalAuthenticator> mock_authenticator = - CreateMockInternalAuthenticator( - {.response_to_get_matching_credential_ids = - method_data->secure_payment_confirmation->credential_ids}); - - auto mock_delegate = std::make_unique<MockPaymentAppFactoryDelegate>( - web_contents_, std::move(method_data)); - - scoped_refptr<MockPaymentManifestWebDataService> mock_service = - base::MakeRefCounted<MockPaymentManifestWebDataService>(); - EXPECT_CALL(*mock_delegate, CreateInternalAuthenticator()) - .WillOnce(Return(ByMove(std::move(mock_authenticator)))); - EXPECT_CALL(*mock_delegate, GetPaymentManifestWebDataService()) - .WillRepeatedly(Return(mock_service)); - EXPECT_CALL(*mock_delegate, GetFrameSecurityOrigin()) - .WillOnce(ReturnRef(caller_origin)); - std::unique_ptr<PaymentApp> secure_payment_confirmation_app; - EXPECT_CALL(*mock_delegate, OnPaymentAppCreated(_)) - .WillOnce(MoveArg<0>(&secure_payment_confirmation_app)); - - secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr()); - std::vector<gfx::Size> icon_sizes({{32, 32}}); - std::vector<SkBitmap> icon_bitmaps(1); - icon_bitmaps[0].allocN32Pixels(/*width=*/32, /*height=*/32); - static_cast<content::TestWebContents*>(web_contents_.get()) - ->TestDidDownloadImage(icon, /*http_status_code=*/200, - std::move(icon_bitmaps), std::move(icon_sizes)); - - ASSERT_TRUE(secure_payment_confirmation_app); - EXPECT_EQ(secure_payment_confirmation_app->GetSublabel(), - u"instrument details"); -} - #if BUILDFLAG(IS_ANDROID) class SecurePaymentConfirmationAppFactoryBrowserBoundKeysTest - : public SecurePaymentConfirmationAppFactoryUsingCredentialStoreAPIsTest { + : public SecurePaymentConfirmationAppFactoryTest { protected: scoped_refptr<FakeBrowserBoundKeyStore> browser_bound_key_store_ = base::MakeRefCounted<FakeBrowserBoundKeyStore>(); @@ -849,26 +759,16 @@ secure_payment_confirmation_app_factory_->SetBrowserBoundKeyStoreForTesting( browser_bound_key_store_); - std::unique_ptr<webauthn::MockInternalAuthenticator> mock_authenticator = - CreateMockInternalAuthenticator( - {.response_to_get_matching_credential_ids = - method_data->secure_payment_confirmation->credential_ids}); - - auto mock_delegate = std::make_unique<MockPaymentAppFactoryDelegate>( - web_contents_, std::move(method_data)); - - scoped_refptr<MockPaymentManifestWebDataService> mock_service = - base::MakeRefCounted<MockPaymentManifestWebDataService>(); - EXPECT_CALL(*mock_delegate, CreateInternalAuthenticator()) - .WillOnce(Return(ByMove(std::move(mock_authenticator)))); - EXPECT_CALL(*mock_delegate, GetPaymentManifestWebDataService()) - .WillRepeatedly(Return(mock_service)); + std::unique_ptr<MockPaymentAppFactoryDelegate> mock_delegate = + CreateMockDelegate(std::move(method_data)); EXPECT_CALL(*mock_delegate, GetFrameSecurityOrigin()) .WillOnce(ReturnRef(caller_origin)); std::unique_ptr<PaymentApp> secure_payment_confirmation_app; EXPECT_CALL(*mock_delegate, OnPaymentAppCreated(_)) .WillOnce(MoveArg<0>(&secure_payment_confirmation_app)); + MockFindMatchingCredential(credential_id_bytes_); + secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr()); std::vector<gfx::Size> icon_sizes({{32, 32}}); std::vector<SkBitmap> icon_bitmaps(1); @@ -885,7 +785,7 @@ ASSERT_TRUE(passkey_browser_binder); EXPECT_EQ(browser_bound_key_store_.get(), passkey_browser_binder->GetBrowserBoundKeyStoreForTesting()); - EXPECT_EQ(mock_service.get(), + EXPECT_EQ(mock_service_.get(), passkey_browser_binder->GetWebDataServiceForTesting()); } @@ -911,20 +811,8 @@ CreateSecurePaymentConfirmationRequest(); GURL icon = method_data->secure_payment_confirmation->instrument->icon; - std::unique_ptr<webauthn::MockInternalAuthenticator> mock_authenticator = - CreateMockInternalAuthenticator( - {.response_to_get_matching_credential_ids = - std::vector<std::vector<uint8_t>>()}); - - auto mock_delegate = std::make_unique<MockPaymentAppFactoryDelegate>( - web_contents_, std::move(method_data)); - - scoped_refptr<MockPaymentManifestWebDataService> mock_service = - base::MakeRefCounted<MockPaymentManifestWebDataService>(); - EXPECT_CALL(*mock_delegate, CreateInternalAuthenticator()) - .WillOnce(Return(ByMove(std::move(mock_authenticator)))); - EXPECT_CALL(*mock_delegate, GetPaymentManifestWebDataService()) - .WillRepeatedly(Return(mock_service)); + std::unique_ptr<MockPaymentAppFactoryDelegate> mock_delegate = + CreateMockDelegate(std::move(method_data)); url::Origin caller_origin = url::Origin::Create(GURL("https://site.example")); EXPECT_CALL(*mock_delegate, GetFrameSecurityOrigin()) .WillOnce(ReturnRef(caller_origin)); @@ -934,6 +822,10 @@ EXPECT_CALL(*mock_delegate, OnPaymentAppCreationError(_, _)).Times(0); EXPECT_CALL(*mock_delegate, OnDoneCreatingPaymentApps()).Times(1); + EXPECT_CALL(*mock_credential_finder_, GetMatchingCredentials) + .WillOnce(RunOnceCallback<5>( + std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>>())); + secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr()); std::vector<gfx::Size> icon_sizes({{32, 32}}); std::vector<SkBitmap> icon_bitmaps(1); @@ -953,10 +845,11 @@ blink::features::kSecurePaymentConfirmationUxRefresh}; }; -// Test that the SecurePaymentConfirmationApp can be created without an -// authenticator. +// Test that the SecurePaymentConfirmationApp can be created even when there is +// no user-verify platform authenticator available. This will ultimately create +// a fallback experience for the user. TEST_F(SecurePaymentConfirmationAppFactoryUxRefreshTest, - Fallback_NoAuthenticator) { + Fallback_NoUserVerifyingPlatformAuthenticator) { auto method_data = mojom::PaymentMethodData::New(); method_data->supported_method = "secure-payment-confirmation"; method_data->secure_payment_confirmation = @@ -1004,20 +897,8 @@ CreateSecurePaymentConfirmationRequest(); GURL icon = method_data->secure_payment_confirmation->instrument->icon; - std::unique_ptr<webauthn::MockInternalAuthenticator> mock_authenticator = - CreateMockInternalAuthenticator( - {.response_to_get_matching_credential_ids = - std::vector<std::vector<uint8_t>>()}); - - auto mock_delegate = std::make_unique<MockPaymentAppFactoryDelegate>( - web_contents_, std::move(method_data)); - - scoped_refptr<MockPaymentManifestWebDataService> mock_service = - base::MakeRefCounted<MockPaymentManifestWebDataService>(); - EXPECT_CALL(*mock_delegate, CreateInternalAuthenticator()) - .WillOnce(Return(ByMove(std::move(mock_authenticator)))); - EXPECT_CALL(*mock_delegate, GetPaymentManifestWebDataService()) - .WillRepeatedly(Return(mock_service)); + std::unique_ptr<MockPaymentAppFactoryDelegate> mock_delegate = + CreateMockDelegate(std::move(method_data)); url::Origin caller_origin = url::Origin::Create(GURL("https://site.example")); EXPECT_CALL(*mock_delegate, GetFrameSecurityOrigin()) .WillOnce(ReturnRef(caller_origin)); @@ -1027,6 +908,10 @@ EXPECT_CALL(*mock_delegate, OnPaymentAppCreationError(_, _)).Times(0); EXPECT_CALL(*mock_delegate, OnDoneCreatingPaymentApps()).Times(1); + EXPECT_CALL(*mock_credential_finder_, GetMatchingCredentials) + .WillOnce(RunOnceCallback<5>( + std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>>())); + secure_payment_confirmation_app_factory_->Create(mock_delegate->GetWeakPtr()); std::vector<gfx::Size> icon_sizes({{32, 32}}); std::vector<SkBitmap> icon_bitmaps(1);
diff --git a/components/payments/content/secure_payment_confirmation_credential_finder.cc b/components/payments/content/secure_payment_confirmation_credential_finder.cc new file mode 100644 index 0000000..0883ffc --- /dev/null +++ b/components/payments/content/secure_payment_confirmation_credential_finder.cc
@@ -0,0 +1,122 @@ +// 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/payments/content/secure_payment_confirmation_credential_finder.h" + +#include "base/feature_list.h" +#include "components/payments/content/payment_manifest_web_data_service.h" +#include "components/payments/core/features.h" +#include "components/payments/core/secure_payment_confirmation_credential.h" +#include "components/webauthn/core/browser/internal_authenticator.h" +#include "content/public/browser/webauthn_security_utils.h" +#include "url/origin.h" + +namespace payments { + +namespace { +// Determine if a given origin that is calling SPC with a given RP ID requires +// the credentials to be third-party enabled (i.e., the calling party is not the +// RP ID). +bool RequiresThirdPartyPaymentBit(const url::Origin& caller_origin, + const std::string& relying_party_id) { + return !content::OriginIsAllowedToClaimRelyingPartyId(relying_party_id, + caller_origin); +} +} // namespace + +SecurePaymentConfirmationCredentialFinder:: + SecurePaymentConfirmationCredentialFinder() = default; +SecurePaymentConfirmationCredentialFinder:: + ~SecurePaymentConfirmationCredentialFinder() { + std::ranges::for_each(requests_, [&](const auto& pair) { + if (pair.second.second) { + pair.second.second->CancelRequest(pair.first); + } + }); +} + +void SecurePaymentConfirmationCredentialFinder::GetMatchingCredentials( + const std::vector<std::vector<uint8_t>>& credential_ids, + const std::string& relying_party_id, + const url::Origin& caller_origin, + webauthn::InternalAuthenticator* authenticator, + scoped_refptr<payments::PaymentManifestWebDataService> web_data_service, + SecurePaymentConfirmationCredentialFinderCallback result_callback) { + // If we have credential-store level support for SPC, we can query the store + // directly. Otherwise, we have to rely on the user profile database. + // + // Currently, credential store APIs are only available on Android. + if (base::FeatureList::IsEnabled( + features::kSecurePaymentConfirmationUseCredentialStoreAPIs)) { + // If we are relying on underlying credential-store level support for SPC, + // but it isn't available, ensure that canMakePayment() will return false by + // returning failure here. + // + // This helps websites avoid a failure scenario when SPC appears to be + // available, but in practice it is non-functional due to lack of platform + // support. + if (!authenticator->IsGetMatchingCredentialIdsSupported()) { + std::move(result_callback).Run(std::nullopt); + return; + } + + const bool require_third_party_payment_bit = + RequiresThirdPartyPaymentBit(caller_origin, relying_party_id); + + authenticator->GetMatchingCredentialIds( + relying_party_id, std::move(credential_ids), + require_third_party_payment_bit, + base::BindOnce(&SecurePaymentConfirmationCredentialFinder:: + OnGetMatchingCredentialIdsFromStore, + weak_ptr_factory_.GetWeakPtr(), + std::move(result_callback), relying_party_id)); + } else { + WebDataServiceBase::Handle handle = + web_data_service->GetSecurePaymentConfirmationCredentials( + std::move(credential_ids), std::move(relying_party_id), this); + requests_[handle] = + std::make_pair(std::move(result_callback), web_data_service); + } +} + +void SecurePaymentConfirmationCredentialFinder::OnWebDataServiceRequestDone( + WebDataServiceBase::Handle handle, + std::unique_ptr<WDTypedResult> result) { + auto iterator = requests_.find(handle); + if (iterator == requests_.end()) { + return; + } + + SecurePaymentConfirmationCredentialFinderCallback callback = + std::move(iterator->second.first); + requests_.erase(iterator); + + if (result && result->GetType() == SECURE_PAYMENT_CONFIRMATION) { + std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>> + credentials = static_cast<WDResult<std::vector< + std::unique_ptr<SecurePaymentConfirmationCredential>>>*>( + result.get()) + ->GetValue(); + std::move(callback).Run(std::move(credentials)); + } else { + std::move(callback).Run(std::nullopt); + } +} + +void SecurePaymentConfirmationCredentialFinder:: + OnGetMatchingCredentialIdsFromStore( + SecurePaymentConfirmationCredentialFinderCallback callback, + std::string relying_party_id, + std::vector<std::vector<uint8_t>> matching_credentials) { + std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>> credentials; + for (std::vector<uint8_t>& credential_id : matching_credentials) { + credentials.emplace_back( + std::make_unique<SecurePaymentConfirmationCredential>( + std::move(credential_id), relying_party_id, + /*user_id=*/std::vector<uint8_t>())); + } + std::move(callback).Run(std::move(credentials)); +} + +} // namespace payments
diff --git a/components/payments/content/secure_payment_confirmation_credential_finder.h b/components/payments/content/secure_payment_confirmation_credential_finder.h new file mode 100644 index 0000000..253680a7 --- /dev/null +++ b/components/payments/content/secure_payment_confirmation_credential_finder.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 COMPONENTS_PAYMENTS_CONTENT_SECURE_PAYMENT_CONFIRMATION_CREDENTIAL_FINDER_H_ +#define COMPONENTS_PAYMENTS_CONTENT_SECURE_PAYMENT_CONFIRMATION_CREDENTIAL_FINDER_H_ + +#include <map> +#include <string> + +#include "base/memory/weak_ptr.h" +#include "components/webdata/common/web_data_service_consumer.h" + +namespace url { +class Origin; +} + +namespace webauthn { +class InternalAuthenticator; +} + +namespace payments { + +class PaymentManifestWebDataService; +struct SecurePaymentConfirmationCredential; + +// Wraps retrieval and matching of SPC credentials, from either the user profile +// database or OS-level APIs. +class SecurePaymentConfirmationCredentialFinder + : public WebDataServiceConsumer { + public: + SecurePaymentConfirmationCredentialFinder(); + ~SecurePaymentConfirmationCredentialFinder() override; + + using SecurePaymentConfirmationCredentialFinderCallback = + base::OnceCallback<void( + std::optional< + std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>>> + credentials)>; + + // Retrieve available SPC credentials that match the input `credential_ids` + // and `relying_party_id`, and which if necessary have the third-party payment + // bit (i.e., if `relying_party_id` and `caller_origin` are different). + // + // The `callback` will be called with the resulting credentials, or + // std::nullopt if an error was encountered. The callback may be called either + // synchronously or asynchronously. + virtual void GetMatchingCredentials( + const std::vector<std::vector<uint8_t>>& credential_ids, + const std::string& relying_party_id, + const url::Origin& caller_origin, + webauthn::InternalAuthenticator* authenticator, + scoped_refptr<payments::PaymentManifestWebDataService> web_data_service, + SecurePaymentConfirmationCredentialFinderCallback result_callback); + + // WebDataServiceConsumer: + void OnWebDataServiceRequestDone( + WebDataServiceBase::Handle handle, + std::unique_ptr<WDTypedResult> result) override; + + private: + // On platforms where we have credential-store level support for retrieving + // credentials (i.e., rather than using the user profile database), this + // callback will be called with the retrieved and matching credential ids. + // + // |relying_party_id| and |matching_credentials| are always std::move'd in, + // and so are not const-ref. + void OnGetMatchingCredentialIdsFromStore( + SecurePaymentConfirmationCredentialFinderCallback callback, + std::string relying_party_id, + std::vector<std::vector<uint8_t>> matching_credentials); + + // On platforms where we are using the user profile database, this map holds + // in-progress requests to the database mapped to the callback which should be + // called with the result. + std::map<WebDataServiceBase::Handle, + std::pair<SecurePaymentConfirmationCredentialFinderCallback, + scoped_refptr<payments::PaymentManifestWebDataService>>> + requests_; + + base::WeakPtrFactory<SecurePaymentConfirmationCredentialFinder> + weak_ptr_factory_{this}; +}; + +} // namespace payments + +#endif // COMPONENTS_PAYMENTS_CONTENT_SECURE_PAYMENT_CONFIRMATION_CREDENTIAL_FINDER_H_
diff --git a/components/payments/content/secure_payment_confirmation_credential_finder_unittest.cc b/components/payments/content/secure_payment_confirmation_credential_finder_unittest.cc new file mode 100644 index 0000000..73fec17 --- /dev/null +++ b/components/payments/content/secure_payment_confirmation_credential_finder_unittest.cc
@@ -0,0 +1,260 @@ +// 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/payments/content/secure_payment_confirmation_credential_finder.h" + +#include <memory> +#include <vector> + +#include "base/functional/callback_helpers.h" +#include "base/memory/scoped_refptr.h" +#include "base/test/bind.h" +#include "base/test/gmock_callback_support.h" +#include "base/test/mock_callback.h" +#include "base/test/scoped_feature_list.h" +#include "components/payments/content/mock_payment_manifest_web_data_service.h" +#include "components/payments/core/features.h" +#include "components/payments/core/secure_payment_confirmation_credential.h" +#include "components/webauthn/core/browser/mock_internal_authenticator.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/browser_task_environment.h" +#include "content/public/test/test_browser_context.h" +#include "content/public/test/test_web_contents_factory.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" +#include "url/origin.h" + +namespace payments { +namespace { + +using ::base::test::RunOnceCallback; +using ::testing::_; +using ::testing::Eq; +using ::testing::Return; + +const std::vector<std::vector<uint8_t>> kInputCredentialIds = { + {0x01, 0x02, 0x03}, + {0x04, 0x05, 0x06}}; +const std::vector<std::vector<uint8_t>> kAvailableCredentialIds = { + {0x04, 0x05, 0x06}}; +const std::string kRelyingPartyId = "rp.example"; + +class SecurePaymentConfirmationCredentialFinderTest : public testing::Test { + protected: + SecurePaymentConfirmationCredentialFinderTest() + : web_contents_(web_contents_factory_.CreateWebContents(&context_)), + mock_authenticator_( + std::make_unique<webauthn::MockInternalAuthenticator>( + web_contents_)), + mock_service_( + base::MakeRefCounted<MockPaymentManifestWebDataService>()) {} + + // Required for test environment setup. + content::BrowserTaskEnvironment task_environment_; + content::TestBrowserContext context_; + content::TestWebContentsFactory web_contents_factory_; + raw_ptr<content::WebContents> web_contents_; + + // Mocks of the underlying authenticator and user database service. + std::unique_ptr<webauthn::MockInternalAuthenticator> mock_authenticator_; + scoped_refptr<MockPaymentManifestWebDataService> mock_service_; + + // The class under test. + SecurePaymentConfirmationCredentialFinder credential_finder_; +}; + +// Tests for the user profile database fetching path. +class SecurePaymentConfirmationCredentialFinderUserDatabaseTest + : public SecurePaymentConfirmationCredentialFinderTest { + protected: + SecurePaymentConfirmationCredentialFinderUserDatabaseTest() { + feature_list_.InitAndDisableFeature( + features::kSecurePaymentConfirmationUseCredentialStoreAPIs); + } + + private: + base::test::ScopedFeatureList feature_list_; +}; + +// Tests that the credential finder uses the database service and that returned +// credentials are propagated to the callback. +TEST_F(SecurePaymentConfirmationCredentialFinderUserDatabaseTest, + ReturnsCredentialsOnSuccess) { + WebDataServiceBase::Handle handle = 1234; + EXPECT_CALL(*mock_service_, GetSecurePaymentConfirmationCredentials( + Eq(kInputCredentialIds), kRelyingPartyId, _)) + .WillOnce(Return(handle)); + + std::optional< + std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>>> + actual_credentials; + auto callback = base::BindLambdaForTesting( + [&actual_credentials]( + std::optional< + std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>>> + result) { actual_credentials = std::move(result); }); + + url::Origin caller_origin = url::Origin::Create(GURL("https://rp.example")); + credential_finder_.GetMatchingCredentials( + kInputCredentialIds, kRelyingPartyId, caller_origin, + /*authenticator=*/nullptr, mock_service_, std::move(callback)); + + // Simulate the web data service returning the credentials. + std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>> credentials; + credentials.emplace_back( + std::make_unique<SecurePaymentConfirmationCredential>( + kAvailableCredentialIds[0], kRelyingPartyId, + /*user_id=*/std::vector<uint8_t>())); + auto result = std::make_unique<WDResult< + std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>>>>( + SECURE_PAYMENT_CONFIRMATION, std::move(credentials)); + credential_finder_.OnWebDataServiceRequestDone(handle, std::move(result)); + + // The credential finder should have received the credentials and sent them + // back to the callback. + ASSERT_TRUE(actual_credentials.has_value()); + ASSERT_EQ(actual_credentials->size(), 1u); + EXPECT_EQ((*actual_credentials)[0]->credential_id, + kAvailableCredentialIds[0]); + EXPECT_EQ((*actual_credentials)[0]->relying_party_id, kRelyingPartyId); +} + +// Tests that if the web data service returns a result that is not for SPC, we +// return a std::nullopt. +TEST_F(SecurePaymentConfirmationCredentialFinderUserDatabaseTest, + ReturnsNulloptOnFailure) { + WebDataServiceBase::Handle handle = 1234; + EXPECT_CALL(*mock_service_, GetSecurePaymentConfirmationCredentials( + Eq(kInputCredentialIds), kRelyingPartyId, _)) + .WillOnce(Return(handle)); + + base::MockCallback<SecurePaymentConfirmationCredentialFinder:: + SecurePaymentConfirmationCredentialFinderCallback> + mock_callback; + EXPECT_CALL(mock_callback, Run(Eq(std::nullopt))); + + url::Origin caller_origin = url::Origin::Create(GURL("https://rp.example")); + credential_finder_.GetMatchingCredentials( + kInputCredentialIds, kRelyingPartyId, caller_origin, + /*authenticator=*/nullptr, mock_service_, mock_callback.Get()); + + // Simulate the web data service returning a non-SPC result; this should not + // generally happen, but the finder should handle it and return std::nullopt. + auto result = std::make_unique<WDResult<int>>(AUTOFILL_PROFILES_RESULT, 0); + credential_finder_.OnWebDataServiceRequestDone(handle, std::move(result)); +} + +// Tests for the credential store APIs path. +class SecurePaymentConfirmationCredentialFinderCredentialStoreApisTest + : public SecurePaymentConfirmationCredentialFinderTest { + protected: + SecurePaymentConfirmationCredentialFinderCredentialStoreApisTest() { + ON_CALL(*mock_authenticator_, IsGetMatchingCredentialIdsSupported()) + .WillByDefault(Return(true)); + } + + private: + base::test::ScopedFeatureList feature_list_{ + features::kSecurePaymentConfirmationUseCredentialStoreAPIs}; +}; + +// Tests that the credential finder uses the credential store API path, and that +// returned credentials are propagated to the callback. +TEST_F(SecurePaymentConfirmationCredentialFinderCredentialStoreApisTest, + ReturnsCredentialsOnSuccess) { + std::optional< + std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>>> + actual_credentials; + auto callback = base::BindLambdaForTesting( + [&actual_credentials]( + std::optional< + std::vector<std::unique_ptr<SecurePaymentConfirmationCredential>>> + result) { actual_credentials = std::move(result); }); + + EXPECT_CALL( + *mock_authenticator_, + GetMatchingCredentialIds(kRelyingPartyId, Eq(kInputCredentialIds), _, _)) + .WillOnce(RunOnceCallback<3>(kAvailableCredentialIds)); + + url::Origin caller_origin = url::Origin::Create(GURL("https://rp.example")); + credential_finder_.GetMatchingCredentials( + kInputCredentialIds, kRelyingPartyId, caller_origin, + mock_authenticator_.get(), mock_service_, std::move(callback)); + + // The credential finder should have received the credentials, converted them, + // and sent them back to the callback. + ASSERT_TRUE(actual_credentials.has_value()); + ASSERT_EQ(actual_credentials->size(), 1u); + EXPECT_EQ((*actual_credentials)[0]->credential_id, + kAvailableCredentialIds[0]); + EXPECT_EQ((*actual_credentials)[0]->relying_party_id, kRelyingPartyId); +} + +// Test that if the credential store APIs are not available, the finder +// returns a std::nullopt to the callback. +TEST_F(SecurePaymentConfirmationCredentialFinderCredentialStoreApisTest, + ReturnsNulloptWhenNotSupported) { + EXPECT_CALL(*mock_authenticator_, IsGetMatchingCredentialIdsSupported()) + .WillOnce(Return(false)); + EXPECT_CALL(*mock_authenticator_, GetMatchingCredentialIds).Times(0); + + base::MockCallback<SecurePaymentConfirmationCredentialFinder:: + SecurePaymentConfirmationCredentialFinderCallback> + mock_callback; + EXPECT_CALL(mock_callback, Run(Eq(std::nullopt))); + + url::Origin caller_origin = url::Origin::Create(GURL("https://rp.example")); + credential_finder_.GetMatchingCredentials( + kInputCredentialIds, kRelyingPartyId, caller_origin, + mock_authenticator_.get(), mock_service_, mock_callback.Get()); +} + +TEST_F( + SecurePaymentConfirmationCredentialFinderCredentialStoreApisTest, + CorrectlyCalculatesThirdPartyPaymentRequirement_OriginDifferentFromRpId) { + url::Origin caller_origin = url::Origin::Create(GURL("https://site.example")); + + // Because the RP ID is 'rp.example', and our origin is + // 'https://site.example', this is a third-party payment authentication. + EXPECT_CALL(*mock_authenticator_, + GetMatchingCredentialIds( + _, _, /*require_third_party_payment_bit=*/true, _)); + credential_finder_.GetMatchingCredentials( + kInputCredentialIds, kRelyingPartyId, caller_origin, + mock_authenticator_.get(), mock_service_, base::DoNothing()); +} + +TEST_F(SecurePaymentConfirmationCredentialFinderCredentialStoreApisTest, + CorrectlyCalculatesThirdPartyPaymentRequirement_OriginSameAsRpId) { + url::Origin caller_origin = url::Origin::Create(GURL("https://rp.example")); + + // Because the RP ID is 'rp.example', and our origin is 'https://rp.example' + // too, this is a first-party payment authentication. + EXPECT_CALL(*mock_authenticator_, + GetMatchingCredentialIds( + _, _, /*require_third_party_payment_bit=*/false, _)); + credential_finder_.GetMatchingCredentials( + kInputCredentialIds, kRelyingPartyId, caller_origin, + mock_authenticator_.get(), mock_service_, base::DoNothing()); +} + +TEST_F(SecurePaymentConfirmationCredentialFinderCredentialStoreApisTest, + CorrectlyCalculatesThirdPartyPaymentRequirement_OriginSameDomainAsRpId) { + url::Origin caller_origin = + url::Origin::Create(GURL("https://subdomain.rp.example")); + + // Because the RP ID is 'rp.example', and our origin is + // 'https://subdomain.rp.example' (a registrable-domain-match), this is a + // first-party payment authentication. + EXPECT_CALL(*mock_authenticator_, + GetMatchingCredentialIds( + _, _, /*require_third_party_payment_bit=*/false, _)); + credential_finder_.GetMatchingCredentials( + kInputCredentialIds, kRelyingPartyId, caller_origin, + mock_authenticator_.get(), mock_service_, base::DoNothing()); +} + +} // namespace +} // namespace payments
diff --git a/components/payments/core/payment_address.h b/components/payments/core/payment_address.h index 71fbde6..74e966e 100644 --- a/components/payments/core/payment_address.h +++ b/components/payments/core/payment_address.h
@@ -12,10 +12,6 @@ // following spec: // https://w3c.github.io/payment-request/#dom-paymentaddress -namespace base { -class Value; -} - namespace payments { // Returns a Value::Dict with the properties of this PaymentAddress.
diff --git a/components/payments/core/payment_currency_amount.h b/components/payments/core/payment_currency_amount.h index ad6e74a2..86ec731 100644 --- a/components/payments/core/payment_currency_amount.h +++ b/components/payments/core/payment_currency_amount.h
@@ -12,10 +12,6 @@ // the following spec: // https://w3c.github.io/browser-payment-api/#dom-paymentcurrencyamount -namespace base { -class Value; -} - namespace payments { // Populates the properties of |amount| from |dictionary_value|.
diff --git a/components/policy/core/browser/webui/json_generation.h b/components/policy/core/browser/webui/json_generation.h index 3ab0c1e..90124cc 100644 --- a/components/policy/core/browser/webui/json_generation.h +++ b/components/policy/core/browser/webui/json_generation.h
@@ -12,10 +12,6 @@ #include "base/values.h" #include "components/policy/policy_export.h" -namespace base { -class Value; -} - namespace policy { POLICY_EXPORT extern const char kChromeMetadataVersionKey[];
diff --git a/components/policy/test_support/request_handler_for_psm_auto_enrollment.cc b/components/policy/test_support/request_handler_for_psm_auto_enrollment.cc index 9f15732..0c0088e 100644 --- a/components/policy/test_support/request_handler_for_psm_auto_enrollment.cc +++ b/components/policy/test_support/request_handler_for_psm_auto_enrollment.cc
@@ -9,6 +9,7 @@ #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/path_service.h" +#include "base/threading/thread_restrictions.h" #include "components/policy/core/common/cloud/cloud_policy_constants.h" #include "components/policy/proto/device_management_backend.pb.h" #include "components/policy/test_support/test_server_helpers.h"
diff --git a/components/privacy_sandbox_strings.grd b/components/privacy_sandbox_strings.grd index f03fca6..b575bee 100644 --- a/components/privacy_sandbox_strings.grd +++ b/components/privacy_sandbox_strings.grd
@@ -381,61 +381,61 @@ Tracking protections enabled </message> <!-- Page Info --> - <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_HEADER" desc="The header for the 'Privacy and site data' subpage in the page info bubble." formatter_data="android_java" translateable="false"> + <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_HEADER" desc="The header for the 'Privacy and site data' subpage in the page info bubble." formatter_data="android_java"> Privacy and site data </message> - <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_TOOLTIP" desc="A tooltip that appears when the user hovers over the 'Privacy and site data' button. Options on the next screen include: a button to pause or resume tracking protections, a link to the tracking protections settings page, and a button for the on-device site data dialog." translateable="false"> + <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_TOOLTIP" desc="A tooltip that appears when the user hovers over the 'Privacy and site data' button. Options on the next screen include: a button to pause or resume tracking protections, a link to the tracking protections settings page, and a button for the on-device site data dialog."> Options for privacy and site data </message> - <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_DESCRIPTION" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are blocked and that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text)." translateable="false"> + <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_DESCRIPTION" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are blocked and that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text)."> Third-party cookies are blocked. Incognito also provides features that can limit the information available to companies whose content is embedded on a site you visit. </message> - <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_DESCRIPTION_ANDROID" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are blocked and that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text). Contains a link to the 'Incognito tracking protections' settings page where these features can be managed." formatter_data="android_java" translateable="false"> + <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_DESCRIPTION_ANDROID" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are blocked and that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text). Contains a link to the 'Incognito tracking protections' settings page where these features can be managed." formatter_data="android_java"> Third-party cookies are blocked. Incognito also provides features that can limit the information available to companies whose content is embedded on a site you visit. Manage Incognito tracking protections in <ph name="BEGIN_LINK"><link></ph>Settings<ph name="END_LINK"></link></ph>. </message> - <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_PAUSED_PROTECTIONS_DESCRIPTION_ANDROID" formatter_data="android_java" desc="Descriptive text in the 'Privacy and site data' subpage shown when tracking protections are paused on the current site. Explains that tracking protections will remain paused on this site until the user closes all Incognito windows. Additionally contains a feedback link so the user can given feedback as to why they paused tracking protections on this site." translateable="false"> + <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_PAUSED_PROTECTIONS_DESCRIPTION_ANDROID" formatter_data="android_java" desc="Descriptive text in the 'Privacy and site data' subpage shown when tracking protections are paused on the current site. Explains that tracking protections will remain paused on this site until the user closes all Incognito windows. Additionally contains a feedback link so the user can given feedback as to why they paused tracking protections on this site."> Protections are paused until all Incognito windows are closed. <ph name="BEGIN_LINK"><link></ph>Send feedback<ph name="END_LINK"></link></ph> </message> <if expr="_google_chrome"> <then> - <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_ENTERPRISE_ALLOWED_DESCRIPTION" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user's administrator. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text)." translateable="false"> + <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_ENTERPRISE_ALLOWED_DESCRIPTION" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user's administrator. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text)."> Chrome blocks third-party cookies in Incognito, but your administrator allowed third-party cookies for this site. Incognito also provides features that can limit the information available to companies whose content is embedded on a site you visit. </message> - <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_EXTENSION_ALLOWED_DESCRIPTION" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by an extension. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text)." translateable="false"> + <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_EXTENSION_ALLOWED_DESCRIPTION" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by an extension. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text)."> Chrome blocks third-party cookies in Incognito, but an extension allowed third-party cookies for this site. Incognito also provides features that can limit the information available to companies whose content is embedded on a site you visit. </message> - <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_USER_ALLOWED_DESCRIPTION" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text)." translateable="false"> + <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_USER_ALLOWED_DESCRIPTION" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text)."> Chrome blocks third-party cookies in Incognito, but you made an exception for this site. Incognito also provides features that can limit the information available to companies whose content is embedded on a site you visit. </message> - <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_ENTERPRISE_ALLOWED_DESCRIPTION_ANDROID" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user's administrator. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text). Contains a link to the 'Incognito tracking protections' settings page where these features can be managed." formatter_data="android_java" translateable="false"> + <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_ENTERPRISE_ALLOWED_DESCRIPTION_ANDROID" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user's administrator. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text). Contains a link to the 'Incognito tracking protections' settings page where these features can be managed." formatter_data="android_java"> Chrome blocks third-party cookies in Incognito, but your administrator allowed third-party cookies for this site. Incognito also provides features that can limit the information available to companies whose content is embedded on a site you visit. Manage Incognito tracking protections in <ph name="BEGIN_LINK"><link></ph>Settings<ph name="END_LINK"></link></ph>. </message> - <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_USER_ALLOWED_DESCRIPTION_ANDROID" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text). Contains a link to the 'Incognito tracking protections' settings page where these features can be managed." formatter_data="android_java" translateable="false"> + <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_USER_ALLOWED_DESCRIPTION_ANDROID" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text). Contains a link to the 'Incognito tracking protections' settings page where these features can be managed." formatter_data="android_java"> Chrome blocks third-party cookies in Incognito, but you made an exception for this site. Incognito also provides features that can limit the information available to companies whose content is embedded on a site you visit. Manage Incognito tracking protections in <ph name="BEGIN_LINK"><link></ph>Settings<ph name="END_LINK"></link></ph>. </message> </then> <else> - <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_ENTERPRISE_ALLOWED_DESCRIPTION" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user's administrator. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text)." translateable="false"> + <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_ENTERPRISE_ALLOWED_DESCRIPTION" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user's administrator. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text)."> Chromium blocks third-party cookies in Incognito, but your administrator allowed third-party cookies for this site. Incognito also provides features that can limit the information available to companies whose content is embedded on a site you visit. </message> - <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_EXTENSION_ALLOWED_DESCRIPTION" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by an extension. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text)." translateable="false"> + <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_EXTENSION_ALLOWED_DESCRIPTION" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by an extension. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text)."> Chromium blocks third-party cookies in Incognito, but an extension allowed third-party cookies for this site. Incognito also provides features that can limit the information available to companies whose content is embedded on a site you visit. </message> - <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_USER_ALLOWED_DESCRIPTION" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text)." translateable="false"> + <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_USER_ALLOWED_DESCRIPTION" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text)."> Chromium blocks third-party cookies in Incognito, but you made an exception for this site. Incognito also provides features that can limit the information available to companies whose content is embedded on a site you visit. </message> - <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_ENTERPRISE_ALLOWED_DESCRIPTION_ANDROID" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user's administrator. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text). Contains a link to the 'Incognito tracking protections' settings page where these features can be managed." formatter_data="android_java" translateable="false"> + <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_ENTERPRISE_ALLOWED_DESCRIPTION_ANDROID" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user's administrator. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text). Contains a link to the 'Incognito tracking protections' settings page where these features can be managed." formatter_data="android_java"> Chromium blocks third-party cookies in Incognito, but your administrator allowed third-party cookies for this site. Incognito also provides features that can limit the information available to companies whose content is embedded on a site you visit. Manage Incognito tracking protections in <ph name="BEGIN_LINK"><link></ph>Settings<ph name="END_LINK"></link></ph>. </message> - <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_USER_ALLOWED_DESCRIPTION_ANDROID" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text). Contains a link to the 'Incognito tracking protections' settings page where these features can be managed." formatter_data="android_java" translateable="false"> + <message name="IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_USER_ALLOWED_DESCRIPTION_ANDROID" desc="Descriptive text in the 'Privacy and site data' subpage explaining that third-party cookies are allowed on the current site due to an exception made by the user. Also mentions that there are additional features available in Incognito that can limit information accessed by companies via their embedded content (e.g. images, ads, or text). Contains a link to the 'Incognito tracking protections' settings page where these features can be managed." formatter_data="android_java"> Chromium blocks third-party cookies in Incognito, but you made an exception for this site. Incognito also provides features that can limit the information available to companies whose content is embedded on a site you visit. Manage Incognito tracking protections in <ph name="BEGIN_LINK"><link></ph>Settings<ph name="END_LINK"></link></ph>. </message> </else> </if> - <message name="IDS_PAGE_INFO_INCOGNITO_TRACKING_PROTECTIONS_SETTINGS_BUTTON_TITLE" desc="Title of the button that opens the settings page for Incognito tracking protections." translateable="false"> + <message name="IDS_PAGE_INFO_INCOGNITO_TRACKING_PROTECTIONS_SETTINGS_BUTTON_TITLE" desc="Title of the button that opens the settings page for Incognito tracking protections."> Settings </message> - <message name="IDS_PAGE_INFO_INCOGNITO_TRACKING_PROTECTIONS_SETTINGS_BUTTON_SUBTITLE" desc="Subtitle of the button that opens the settings page for Incognito tracking protections." translateable="false"> + <message name="IDS_PAGE_INFO_INCOGNITO_TRACKING_PROTECTIONS_SETTINGS_BUTTON_SUBTITLE" desc="Subtitle of the button that opens the settings page for Incognito tracking protections."> Manage Incognito tracking protections </message> <!-- Privacy UX WebUI -->
diff --git a/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_INCOGNITO_TRACKING_PROTECTIONS_SETTINGS_BUTTON_SUBTITLE.png.sha1 b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_INCOGNITO_TRACKING_PROTECTIONS_SETTINGS_BUTTON_SUBTITLE.png.sha1 new file mode 100644 index 0000000..f33a9e1b --- /dev/null +++ b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_INCOGNITO_TRACKING_PROTECTIONS_SETTINGS_BUTTON_SUBTITLE.png.sha1
@@ -0,0 +1 @@ +408f787681174180186e683c54a1a5c369742019 \ No newline at end of file
diff --git a/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_INCOGNITO_TRACKING_PROTECTIONS_SETTINGS_BUTTON_TITLE.png.sha1 b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_INCOGNITO_TRACKING_PROTECTIONS_SETTINGS_BUTTON_TITLE.png.sha1 new file mode 100644 index 0000000..5ed1bdd --- /dev/null +++ b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_INCOGNITO_TRACKING_PROTECTIONS_SETTINGS_BUTTON_TITLE.png.sha1
@@ -0,0 +1 @@ +a933430686869d26a473f0958a45106609ea9c6b \ No newline at end of file
diff --git a/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_ENTERPRISE_ALLOWED_DESCRIPTION.png.sha1 b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_ENTERPRISE_ALLOWED_DESCRIPTION.png.sha1 new file mode 100644 index 0000000..0541f19 --- /dev/null +++ b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_ENTERPRISE_ALLOWED_DESCRIPTION.png.sha1
@@ -0,0 +1 @@ +a9b9d7d6b25c84572b0a0e60e02673efbc009fb9 \ No newline at end of file
diff --git a/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_ENTERPRISE_ALLOWED_DESCRIPTION_ANDROID.png.sha1 b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_ENTERPRISE_ALLOWED_DESCRIPTION_ANDROID.png.sha1 new file mode 100644 index 0000000..066de96a --- /dev/null +++ b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_ENTERPRISE_ALLOWED_DESCRIPTION_ANDROID.png.sha1
@@ -0,0 +1 @@ +b4e61f10ce8b3d784fef1ac14fa92000a6fd2b1b \ No newline at end of file
diff --git a/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_EXTENSION_ALLOWED_DESCRIPTION.png.sha1 b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_EXTENSION_ALLOWED_DESCRIPTION.png.sha1 new file mode 100644 index 0000000..412433c09 --- /dev/null +++ b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_EXTENSION_ALLOWED_DESCRIPTION.png.sha1
@@ -0,0 +1 @@ +1a7062f77059f042812ae626eff442159c33f55d \ No newline at end of file
diff --git a/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_USER_ALLOWED_DESCRIPTION.png.sha1 b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_USER_ALLOWED_DESCRIPTION.png.sha1 new file mode 100644 index 0000000..c79cae3 --- /dev/null +++ b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_USER_ALLOWED_DESCRIPTION.png.sha1
@@ -0,0 +1 @@ +a8683cfbfbe4a4dd56674bae25acb7a5620bbb60 \ No newline at end of file
diff --git a/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_USER_ALLOWED_DESCRIPTION_ANDROID.png.sha1 b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_USER_ALLOWED_DESCRIPTION_ANDROID.png.sha1 new file mode 100644 index 0000000..53f9270 --- /dev/null +++ b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_3PCS_USER_ALLOWED_DESCRIPTION_ANDROID.png.sha1
@@ -0,0 +1 @@ +d2700beaab55036e24f93f171acd0f70815120ef \ No newline at end of file
diff --git a/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_DESCRIPTION.png.sha1 b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_DESCRIPTION.png.sha1 new file mode 100644 index 0000000..10a1b446 --- /dev/null +++ b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_DESCRIPTION.png.sha1
@@ -0,0 +1 @@ +5bc01ebd0721a9a81ddf52931a2d18cae57e108f \ No newline at end of file
diff --git a/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_DESCRIPTION_ANDROID.png.sha1 b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_DESCRIPTION_ANDROID.png.sha1 new file mode 100644 index 0000000..30c2cc0 --- /dev/null +++ b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_DESCRIPTION_ANDROID.png.sha1
@@ -0,0 +1 @@ +9565d33c9b4895efa2bddf341b20d880e002105a \ No newline at end of file
diff --git a/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_HEADER.png.sha1 b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_HEADER.png.sha1 new file mode 100644 index 0000000..2adcf90 --- /dev/null +++ b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_HEADER.png.sha1
@@ -0,0 +1 @@ +ec06a6fe9620181b3a5c26c416334f771b2230b2 \ No newline at end of file
diff --git a/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_PAUSED_PROTECTIONS_DESCRIPTION_ANDROID.png.sha1 b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_PAUSED_PROTECTIONS_DESCRIPTION_ANDROID.png.sha1 new file mode 100644 index 0000000..a8665cd --- /dev/null +++ b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_PAUSED_PROTECTIONS_DESCRIPTION_ANDROID.png.sha1
@@ -0,0 +1 @@ +4a2e2e6cc378402ab4cf7fdb848f00903cf98069 \ No newline at end of file
diff --git a/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_TOOLTIP.png.sha1 b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_TOOLTIP.png.sha1 new file mode 100644 index 0000000..42d9347f --- /dev/null +++ b/components/privacy_sandbox_strings_grd/IDS_PAGE_INFO_PRIVACY_SITE_DATA_TOOLTIP.png.sha1
@@ -0,0 +1 @@ +7798059c1a9c0fb599ba62a7280457f2bb545d26 \ No newline at end of file
diff --git a/components/site_isolation/site_isolation_policy.cc b/components/site_isolation/site_isolation_policy.cc index 0b66ba9..268a7c0 100644 --- a/components/site_isolation/site_isolation_policy.cc +++ b/components/site_isolation/site_isolation_policy.cc
@@ -188,18 +188,23 @@ // static bool SiteIsolationPolicy::IsOriginIsolationForJsOptExceptionsEnabled() { if (content::SiteIsolationPolicy::IsStrictOriginIsolationEnabled() || - content::SiteIsolationPolicy::AreOriginKeyedProcessesEnabledByDefault() || - !content::SiteIsolationPolicy::AreDynamicIsolatedOriginsEnabled()) { + content::SiteIsolationPolicy::AreOriginKeyedProcessesEnabledByDefault()) { // Origin isolation for JavaScript optimizer exceptions isn't needed if // origin isolation is enabled for everything because an origin gets passed // into AreV8OptimizationsDisabledForSite() and the return value will match - // the outcome that is specified by the configured rules. If dynamic origin - // isolation is not enabled, then this feature won't have any effect so we - // just skip it. + // the outcome that is specified by the configured rules. return false; } - return base::FeatureList::IsEnabled( - site_isolation::features::kOriginIsolationForJsOptExceptions); + return IsOriginIsolationForJsOptExceptionsSupported() && + base::FeatureList::IsEnabled( + site_isolation::features::kOriginIsolationForJsOptExceptions); +} + +// static +bool SiteIsolationPolicy::IsOriginIsolationForJsOptExceptionsSupported() { + // Dynamic origin isolation is required for the + // features::kOriginIsolationForJsOptExceptions feature to work. + return content::SiteIsolationPolicy::AreDynamicIsolatedOriginsEnabled(); } // static
diff --git a/components/site_isolation/site_isolation_policy.h b/components/site_isolation/site_isolation_policy.h index 8896265..6456df6 100644 --- a/components/site_isolation/site_isolation_policy.h +++ b/components/site_isolation/site_isolation_policy.h
@@ -42,9 +42,16 @@ static bool IsIsolationForOAuthSitesEnabled(); // Returns true if the isolation mode for isolating origins that match - // JavaScript optimizer exceptions is enabled. + // JavaScript optimizer exceptions is enabled. This is different than + // IsOriginIsolationForJsOptExceptionsSupported() because + // IsOriginIsolationForJsOptExceptionsEnabled() returns false if the + // feature is unneeded due to strict origin isolation being enabled. static bool IsOriginIsolationForJsOptExceptionsEnabled(); + // Returns whether isolating origins that match JavaScript optimizer + // exceptions is supported. + static bool IsOriginIsolationForJsOptExceptionsSupported(); + // Returns true if Site Isolation related enterprise policies should take // effect (e.g. such policies might not be applicable to low-end Android // devices because of 1) performance impact and 2) infeasibility of
diff --git a/components/supervised_user/core/browser/supervised_user_pref_store.h b/components/supervised_user/core/browser/supervised_user_pref_store.h index f30e281..6e9ceae 100644 --- a/components/supervised_user/core/browser/supervised_user_pref_store.h +++ b/components/supervised_user/core/browser/supervised_user_pref_store.h
@@ -17,10 +17,6 @@ #include "components/supervised_user/core/common/supervised_users.h" #include "base/memory/weak_ptr.h" -namespace base { -class Value; -} - class PrefValueMap; namespace supervised_user {
diff --git a/components/sync_preferences/pref_model_associator.h b/components/sync_preferences/pref_model_associator.h index 7b95763..cd022c7 100644 --- a/components/sync_preferences/pref_model_associator.h +++ b/components/sync_preferences/pref_model_associator.h
@@ -26,10 +26,6 @@ #include "components/sync_preferences/pref_model_associator_client.h" #include "components/sync_preferences/synced_pref_observer.h" -namespace base { -class Value; -} - namespace sync_pb { class EntitySpecifics; class PreferenceSpecifics;
diff --git a/components/tab_groups/android/java/src/org/chromium/components/tab_groups/TabGroupColorPickerUtils.java b/components/tab_groups/android/java/src/org/chromium/components/tab_groups/TabGroupColorPickerUtils.java index 306a3bf3..94109f7 100644 --- a/components/tab_groups/android/java/src/org/chromium/components/tab_groups/TabGroupColorPickerUtils.java +++ b/components/tab_groups/android/java/src/org/chromium/components/tab_groups/TabGroupColorPickerUtils.java
@@ -329,6 +329,32 @@ }; } + /** + * Get the {@link TabGroupColorId} associated with a tab group color plain integer. This + * function should only be used for mapping a tab group color back to its IntDef value. + * + * @param colorId The plain color id corresponding to the color of the Tab Group. + */ + public static @TabGroupColorId int getTabGroupCardColorId(int colorId) { + return switch (colorId) { + // LINT.IfChange + case 0 -> TabGroupColorId.GREY; + case 1 -> TabGroupColorId.BLUE; + case 2 -> TabGroupColorId.RED; + case 3 -> TabGroupColorId.YELLOW; + case 4 -> TabGroupColorId.GREEN; + case 5 -> TabGroupColorId.PINK; + case 6 -> TabGroupColorId.PURPLE; + case 7 -> TabGroupColorId.CYAN; + case 8 -> TabGroupColorId.ORANGE; + default -> { + assert false : "Invalid tab group color id " + colorId; + yield TabGroupColorId.GREY; + } + // LINT.ThenChange(//components/tab_groups/tab_group_color.h) + }; + } + private static @ColorInt int resolveGroupRelatedColor( Context context, @ColorRes int colorRes, boolean isIncognito) { @ColorInt int color = ContextCompat.getColor(context, colorRes);
diff --git a/components/tab_groups/tab_group_color.h b/components/tab_groups/tab_group_color.h index 7f735fb..86b4654b 100644 --- a/components/tab_groups/tab_group_color.h +++ b/components/tab_groups/tab_group_color.h
@@ -35,6 +35,7 @@ // A Java counterpart will be generated for this enum. // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.tab_groups // +// LINT.IfChange enum class TabGroupColorId { kGrey = 0, kBlue = 1, @@ -48,6 +49,7 @@ // Next value: 9 kNumEntries = 9, }; +// LINT.ThenChange(//components/tab_groups/android/java/src/org/chromium/components/tab_groups/TabGroupColorPickerUtils.java) using ColorLabelMap = base::flat_map<TabGroupColorId, std::u16string>;
diff --git a/components/test/data/optimization_guide/meta_tags.html b/components/test/data/optimization_guide/meta_tags.html new file mode 100644 index 0000000..f8cbbc5c --- /dev/null +++ b/components/test/data/optimization_guide/meta_tags.html
@@ -0,0 +1,3 @@ +<html> +<head><meta name="author" content="Gary"></head> +</html>
diff --git a/components/test/data/optimization_guide/unit_tests_bundle_data.filelist b/components/test/data/optimization_guide/unit_tests_bundle_data.filelist index 6baef98..240b1e77 100644 --- a/components/test/data/optimization_guide/unit_tests_bundle_data.filelist +++ b/components/test/data/optimization_guide/unit_tests_bundle_data.filelist
@@ -26,6 +26,7 @@ //components/test/data/optimization_guide/media_data/video.html //components/test/data/optimization_guide/media_data/video.webm //components/test/data/optimization_guide/media_data/video_in_iframe.html +//components/test/data/optimization_guide/meta_tags.html //components/test/data/optimization_guide/page_topics_128_model.tflite //components/test/data/optimization_guide/paid_content.html //components/test/data/optimization_guide/paragraph.html
diff --git a/components/user_data_importer/DEPS b/components/user_data_importer/DEPS index 4b90f4c..d346767 100644 --- a/components/user_data_importer/DEPS +++ b/components/user_data_importer/DEPS
@@ -12,5 +12,7 @@ "+components/password_manager/core/common", "+components/password_manager/services/csv_password", "+components/reading_list/core", + "+components/strings/grit", "+mojo/public/cpp/bindings", + "+ui/base/l10n", ]
diff --git a/components/user_data_importer/utility/BUILD.gn b/components/user_data_importer/utility/BUILD.gn index 90c3713..e1cb6ec 100644 --- a/components/user_data_importer/utility/BUILD.gn +++ b/components/user_data_importer/utility/BUILD.gn
@@ -9,6 +9,8 @@ sources = [ "bookmark_parser.cc", "bookmark_parser.h", + "bookmark_util.cc", + "bookmark_util.h", "history_callback_from_rust.h", "safari_data_import_client.h", "safari_data_importer.cc", @@ -24,6 +26,7 @@ "//components/password_manager/core/browser/import:importer", "//components/password_manager/core/browser/ui", "//components/reading_list/core", + "//components/strings", "//components/user_data_importer/common", ] }
diff --git a/components/user_data_importer/utility/bookmark_util.cc b/components/user_data_importer/utility/bookmark_util.cc new file mode 100644 index 0000000..9e53d209 --- /dev/null +++ b/components/user_data_importer/utility/bookmark_util.cc
@@ -0,0 +1,143 @@ +// 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/user_data_importer/utility/bookmark_util.h" + +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "components/bookmarks/browser/bookmark_model.h" +#include "components/bookmarks/browser/bookmark_node.h" +#include "components/reading_list/core/reading_list_model.h" + +namespace user_data_importer { + +namespace { + +using bookmarks::BookmarkModel; +using bookmarks::BookmarkNode; + +std::u16string EscapeAndJoinPath(const std::vector<std::u16string>& path) { + if (path.empty()) { + return u""; + } + std::vector<std::u16string> escaped_path; + escaped_path.reserve(path.size()); + + for (std::u16string component : path) { + base::ReplaceChars(component, u"\\", u"\\\\", &component); + base::ReplaceChars(component, u"/", u"\\/", &component); + escaped_path.push_back(std::move(component)); + } + return base::JoinString(escaped_path, u"/"); +} + +const BookmarkNode* CreateImportBookmarksFolder( + BookmarkModel* bookmark_model, + const std::u16string& folder_title) { + CHECK(bookmark_model); + CHECK(bookmark_model->loaded()); + + const BookmarkNode* other_node = bookmark_model->account_other_node(); + if (!other_node) { + other_node = bookmark_model->other_node(); + } + + CHECK(other_node); + + return bookmark_model->AddFolder(other_node, 0, folder_title); +} + +} // namespace + +size_t ImportBookmarks(BookmarkModel* bookmark_model, + std::vector<ImportedBookmarkEntry> bookmarks, + const std::u16string& import_folder_title) { + CHECK(bookmark_model); + CHECK(bookmark_model->loaded()); + + const BookmarkNode* import_folder = + CreateImportBookmarksFolder(bookmark_model, import_folder_title); + + CHECK(import_folder); + + if (bookmarks.empty()) { + return 0; + } + + std::map<std::u16string, const BookmarkNode*> folder_cache; + folder_cache[EscapeAndJoinPath({})] = import_folder; + + bookmark_model->BeginExtensiveChanges(); + + size_t imported_count = 0u; + + for (const ImportedBookmarkEntry& bookmark_entry : bookmarks) { + const BookmarkNode* parent = import_folder; + std::vector<std::u16string> current_path; + + for (const auto& path_component : bookmark_entry.path) { + current_path.push_back(path_component); + const std::u16string current_path_key = EscapeAndJoinPath(current_path); + + auto cached_folder = folder_cache.find(current_path_key); + + // TODO(crbug.com/407587751): Replace with a CHECK. + if (cached_folder != folder_cache.end()) { + parent = cached_folder->second; + } else { + const BookmarkNode* new_folder = bookmark_model->AddFolder( + parent, parent->children().size(), path_component); + folder_cache[current_path_key] = new_folder; + parent = new_folder; + } + } + + if (bookmark_entry.is_folder) { + const BookmarkNode* new_folder = bookmark_model->AddFolder( + parent, parent->children().size(), bookmark_entry.title); + + std::vector<std::u16string> new_folder_path = bookmark_entry.path; + new_folder_path.push_back(bookmark_entry.title); + folder_cache[EscapeAndJoinPath(new_folder_path)] = new_folder; + } else { + if (!bookmark_entry.url.is_valid()) { + continue; + } + bookmark_model->AddURL(parent, parent->children().size(), + bookmark_entry.title, bookmark_entry.url); + ++imported_count; + } + } + + bookmark_model->EndExtensiveChanges(); + + return imported_count; +} + +size_t ImportReadingList(ReadingListModel* reading_list_model, + std::vector<ImportedBookmarkEntry> reading_list) { + if (reading_list.empty() || !reading_list_model) { + return 0; + } + + size_t imported_count = 0u; + + auto scoped_reading_list_batch_update = + reading_list_model->BeginBatchUpdates(); + + for (const ImportedBookmarkEntry& reading_list_entry : reading_list) { + if (!reading_list_entry.url.is_valid()) { + continue; + } + reading_list_model->AddOrReplaceEntry( + reading_list_entry.url, base::UTF16ToUTF8(reading_list_entry.title), + reading_list::ADDED_VIA_IMPORT, + /*estimated_read_time=*/base::TimeDelta()); + ++imported_count; + } + + return imported_count; +} + +} // namespace user_data_importer
diff --git a/components/user_data_importer/utility/bookmark_util.h b/components/user_data_importer/utility/bookmark_util.h new file mode 100644 index 0000000..8c48154 --- /dev/null +++ b/components/user_data_importer/utility/bookmark_util.h
@@ -0,0 +1,31 @@ +// 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_USER_DATA_IMPORTER_UTILITY_BOOKMARK_UTIL_H_ +#define COMPONENTS_USER_DATA_IMPORTER_UTILITY_BOOKMARK_UTIL_H_ + +#include "components/user_data_importer/common/imported_bookmark_entry.h" + +namespace bookmarks { +class BookmarkModel; +} // namespace bookmarks + +class ReadingListModel; + +namespace user_data_importer { + +// Imports bookmarks into the provided BookmarkModel. +// Returns the number of imported bookmarks. +size_t ImportBookmarks(bookmarks::BookmarkModel* bookmark_model, + std::vector<ImportedBookmarkEntry> bookmarks, + const std::u16string& import_folder_title); + +// Imports reading list entries into the provided ReadingListModel. +// Returns the number of imported reading list entries. +size_t ImportReadingList(ReadingListModel* reading_list_model, + std::vector<ImportedBookmarkEntry> reading_list); + +} // namespace user_data_importer + +#endif // COMPONENTS_USER_DATA_IMPORTER_UTILITY_BOOKMARK_UTIL_H_
diff --git a/components/user_data_importer/utility/safari_data_importer.cc b/components/user_data_importer/utility/safari_data_importer.cc index fbde81f..bf28376 100644 --- a/components/user_data_importer/utility/safari_data_importer.cc +++ b/components/user_data_importer/utility/safari_data_importer.cc
@@ -21,9 +21,11 @@ #include "components/history/core/browser/history_service.h" #include "components/password_manager/core/browser/ui/saved_passwords_presenter.h" #include "components/reading_list/core/reading_list_model.h" -#include "components/user_data_importer/common/imported_bookmark_entry.h" +#include "components/strings/grit/components_strings.h" +#include "components/user_data_importer/utility/bookmark_util.h" #include "components/user_data_importer/utility/history_callback_from_rust.h" #include "components/user_data_importer/utility/zip_ffi_glue.rs.h" +#include "ui/base/l10n/l10n_util.h" namespace { @@ -244,14 +246,19 @@ std::move(done_history_closure)), history_size_threshold_); - // TODO(crbug.com/407587751): Move this to a task. - password_importer_->ContinueImport( - selected_password_ids, - base::BindOnce(&SafariDataImportClient::OnPasswordsImported, - client_->AsWeakPtr())); + if (password_importer_ && + password_importer_->IsState( + password_manager::PasswordImporter::kUserInteractionRequired)) { + // TODO(crbug.com/407587751): Move this to a task. + password_importer_->ContinueImport( + selected_password_ids, + base::BindOnce(&SafariDataImportClient::OnPasswordsImported, + client_->AsWeakPtr())); + } - // TODO(crbug.com/407587751): Import other types here. - client_->OnBookmarksImported(/*count=*/0); + GetRunner()->PostTask( + FROM_HERE, base::BindOnce(&SafariDataImporter::ContinueImportBookmarks, + weak_factory_.GetWeakPtr())); GetRunner()->PostTask( FROM_HERE, base::BindOnce(&SafariDataImporter::ContinueImportPaymentCards, @@ -433,7 +440,14 @@ pending_bookmarks_ = std::move(value.bookmarks); pending_reading_list_ = std::move(value.reading_list); - client_->OnBookmarksReady(pending_bookmarks_.size() + + size_t importable_bookmarks_count = 0; + for (const auto& bookmark : pending_bookmarks_) { + if (!bookmark.is_folder) { + ++importable_bookmarks_count; + } + } + + client_->OnBookmarksReady(importable_bookmarks_count + pending_reading_list_.size()); } @@ -498,10 +512,21 @@ payments_data_manager_->AddCreditCard(credit_card); } - imported_credit_cards++; + ++imported_credit_cards; } client_->OnPaymentCardsImported(imported_credit_cards); } +void SafariDataImporter::ContinueImportBookmarks() { + size_t imported_bookmarks_count = user_data_importer::ImportBookmarks( + &*bookmark_model_, std::move(pending_bookmarks_), + l10n_util::GetStringUTF16(IDS_IMPORTED_FROM_SAFARI_FOLDER)); + size_t imported_reading_list_count = user_data_importer::ImportReadingList( + &*reading_list_model_, std::move(pending_reading_list_)); + + client_->OnBookmarksImported(imported_bookmarks_count + + imported_reading_list_count); +} + } // namespace user_data_importer
diff --git a/components/user_data_importer/utility/safari_data_importer.h b/components/user_data_importer/utility/safari_data_importer.h index 7547135d..5e0a4d0 100644 --- a/components/user_data_importer/utility/safari_data_importer.h +++ b/components/user_data_importer/utility/safari_data_importer.h
@@ -162,6 +162,10 @@ // Imports Credit Cards to the Payments Data Manager. void ContinueImportPaymentCards(); + // Imports bookmarks and reading list entries from pending data into the + // corresponding BookmarkModel and ReadingListModel. + void ContinueImportBookmarks(); + // Objects used by this importer to do work (esp. parsing) // A queue for tasks which may block (e.g., I/O).
diff --git a/components/user_data_importer/utility/safari_data_importer_unittest.cc b/components/user_data_importer/utility/safari_data_importer_unittest.cc index 6a9a5f7..3e2b728e 100644 --- a/components/user_data_importer/utility/safari_data_importer_unittest.cc +++ b/components/user_data_importer/utility/safari_data_importer_unittest.cc
@@ -20,7 +20,9 @@ #include "components/affiliations/core/browser/fake_affiliation_service.h" #include "components/autofill/core/browser/foundations/test_autofill_client.h" #include "components/bookmarks/browser/bookmark_model.h" +#include "components/bookmarks/test/bookmark_test_helpers.h" #include "components/bookmarks/test/test_bookmark_client.h" +#include "components/bookmarks/test/test_matchers.h" #include "components/history/core/browser/history_service.h" #include "components/history/core/test/history_service_test_util.h" #include "components/password_manager/core/browser/import/csv_password_sequence.h" @@ -41,6 +43,9 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using bookmarks::test::IsFolder; +using bookmarks::test::IsUrlBookmark; + using password_manager::ImportEntry; using password_manager::ImportResults; @@ -101,15 +106,22 @@ /*create_db=*/false); auto bookmark_client = std::make_unique<bookmarks::TestBookmarkClient>(); - bookmark_model_ = - std::make_unique<bookmarks::BookmarkModel>(std::move(bookmark_client)); + + bookmark_model_ = bookmarks::TestBookmarkClient::CreateModelWithClient( + std::move(bookmark_client)); auto storage = std::make_unique<FakeReadingListModelStorage>(); + + base::WeakPtr<FakeReadingListModelStorage> storage_ptr = + storage->AsWeakPtr(); + reading_list_model_ = std::make_unique<ReadingListModelImpl>( std::move(storage), syncer::StorageType::kUnspecified, syncer::WipeModelUponSyncDisabledBehavior::kNever, base::DefaultClock::GetInstance()); + storage_ptr->TriggerLoadCompletion(); + importer_ = std::make_unique<SafariDataImporter>( &client_, &presenter_, &autofill_client_.GetPersonalDataManager().payments_data_manager(), @@ -206,6 +218,12 @@ .WillRepeatedly(Assign(&bookmarks_idle_, true)); } + const bookmarks::BookmarkNode* GetOtherBookmarkNode() { + return bookmark_model_->other_node(); + } + + ReadingListModel* GetReadingListModel() { return reading_list_model_.get(); } + testing::StrictMock<MockSafariDataImportClient> client_; base::ScopedMockClockOverride clock_; @@ -334,13 +352,7 @@ } TEST_F(SafariDataImporterTest, Bookmarks_Folders) { -// TODO(crbug.com/407587751): Align iOS and Blink implementation on if non-empty -// folders should be added explicitly. -#if BUILDFLAG(IS_IOS) - ExpectBookmarksReady(6u); -#else - ExpectBookmarksReady(4u); -#endif + ExpectBookmarksReady(3u); PrepareBookmarks( R"(<!DOCTYPE NETSCAPE-Bookmark-file-1> @@ -501,13 +513,7 @@ #endif // BUILDFLAG(IS_IOS) TEST_F(SafariDataImporterTest, Bookmarks_MiscJunk) { - // TODO(crbug.com/407587751): Align iOS and Blink implementation on if - // non-empty folders should be added explicitly. -#if BUILDFLAG(IS_IOS) - ExpectBookmarksReady(3u); -#else ExpectBookmarksReady(2u); -#endif PrepareBookmarks(R"( <!DOCTYPE NETSCAPE-Bookmark-file-1> @@ -699,13 +705,13 @@ EXPECT_CALL(client_, OnPasswordsReady( AllOf(Field(&ImportResults::number_imported, 0u), Field(&ImportResults::number_to_import, 3u)))); - // TODO(crbug.com/407587751): Align iOS and Blink implementation on if - // non-empty folders should be added explicitly. + #if BUILDFLAG(IS_IOS) - ExpectBookmarksReady(7u); -#else ExpectBookmarksReady(6u); +#else + ExpectBookmarksReady(5u); #endif + EXPECT_CALL(client_, OnPaymentCardsReady(3u)); EXPECT_CALL(client_, OnHistoryReady(13u, _)); // Approximation. @@ -718,7 +724,8 @@ EXPECT_CALL(client_, OnPasswordsImported( AllOf(Field(&ImportResults::number_imported, 3u), Field(&ImportResults::number_to_import, 0u)))); - EXPECT_CALL(client_, OnBookmarksImported(0u)); + + EXPECT_CALL(client_, OnBookmarksImported(5u)); EXPECT_CALL(client_, OnPaymentCardsImported(3u)); EXPECT_CALL(client_, OnHistoryImported(7u)); // Actual. @@ -733,12 +740,10 @@ Field(&ImportResults::number_to_import, 3u)))) .Times(2); - // TODO(crbug.com/407587751): Align iOS and Blink implementation on if - // non-empty folders should be added explicitly. #if BUILDFLAG(IS_IOS) - ExpectBookmarksReady(7u, /*times=*/2); -#else ExpectBookmarksReady(6u, /*times=*/2); +#else + ExpectBookmarksReady(5u, /*times=*/2); #endif EXPECT_CALL(client_, OnPaymentCardsReady(3u)).Times(2); @@ -748,4 +753,220 @@ PrepareImportFromFile(); } +// Tests importing a single bookmark into the "Imported from Safari" folder. +TEST_F(SafariDataImporterTest, ImportSingleBookmark) { + ExpectBookmarksReady(1u); + PrepareBookmarks( + R"(<!DOCTYPE NETSCAPE-Bookmark-file-1> + <DT><A HREF="https://www.example.com/">Single Bookmark</A>)"); + + EXPECT_CALL(client_, OnBookmarksImported(1u)); + EXPECT_CALL(client_, OnHistoryImported(0)); + EXPECT_CALL(client_, OnPaymentCardsImported(0)); + + CompleteImport({}); + + const bookmarks::BookmarkNode* other_node = GetOtherBookmarkNode(); + EXPECT_THAT(other_node->children(), + ElementsAre(IsFolder( + u"Imported from Safari", + ElementsAre(IsUrlBookmark( + u"Single Bookmark", GURL("https://www.example.com/")))))); +} + +// Tests importing multiple bookmarks into the "Imported from Safari" folder. +TEST_F(SafariDataImporterTest, ImportsMultipleBookmarks) { + ExpectBookmarksReady(2u); + PrepareBookmarks( + R"(<!DOCTYPE NETSCAPE-Bookmark-file-1> + <DL> + <DT><A HREF="https://www.one.com/">First Bookmark</A> + <DT><A HREF="https://www.two.com/">Second Bookmark</A> + </DL>)"); + + EXPECT_CALL(client_, OnBookmarksImported(2u)); + EXPECT_CALL(client_, OnHistoryImported(0)); + EXPECT_CALL(client_, OnPaymentCardsImported(0)); + CompleteImport({}); + + const bookmarks::BookmarkNode* other_node = GetOtherBookmarkNode(); + EXPECT_THAT(other_node->children(), + ElementsAre(IsFolder( + u"Imported from Safari", + ElementsAre(IsUrlBookmark(u"First Bookmark", + GURL("https://www.one.com/")), + IsUrlBookmark(u"Second Bookmark", + GURL("https://www.two.com/")))))); +} + +// Tests that the folder hierarchy is preserved when importing a nested +// bookmark. +TEST_F(SafariDataImporterTest, ImportsNestedBookmark) { + ExpectBookmarksReady(1u); + PrepareBookmarks( + R"(<!DOCTYPE NETSCAPE-Bookmark-file-1> + <DL> + <DT><H3>Top Folder</H3> + <DL> + <DT><H3>Second Folder</H3> + <DL> + <DT><A HREF="https://www.nested.com/">Nested Bookmark</A> + </DL> + </DL> + </DL>)"); + + EXPECT_CALL(client_, OnBookmarksImported(1u)); + EXPECT_CALL(client_, OnHistoryImported(0)); + EXPECT_CALL(client_, OnPaymentCardsImported(0)); + CompleteImport({}); + + const bookmarks::BookmarkNode* other_node = GetOtherBookmarkNode(); + EXPECT_THAT( + other_node->children(), + ElementsAre(IsFolder( + u"Imported from Safari", + ElementsAre(IsFolder( + u"Top Folder", + ElementsAre(IsFolder(u"Second Folder", + ElementsAre(IsUrlBookmark( + u"Nested Bookmark", + GURL("https://www.nested.com/")))))))))); +} + +// Tests that an empty bookmark folder is imported correctly. +TEST_F(SafariDataImporterTest, ImportsEmptyFolder) { + ExpectBookmarksReady(0u); + PrepareBookmarks( + R"(<!DOCTYPE NETSCAPE-Bookmark-file-1> + <DL> + <DT><H3>Empty Folder</H3> + <DL></DL> + </DL>)"); + + EXPECT_CALL(client_, OnBookmarksImported(0u)); + EXPECT_CALL(client_, OnHistoryImported(0)); + EXPECT_CALL(client_, OnPaymentCardsImported(0)); + CompleteImport({}); + + const bookmarks::BookmarkNode* other_node = GetOtherBookmarkNode(); + EXPECT_THAT( + other_node->children(), + ElementsAre(IsFolder(u"Imported from Safari", + ElementsAre(IsFolder(u"Empty Folder", IsEmpty()))))); +} + +// Tests that the reading lists are imported into the Reading List model on iOS. +#if BUILDFLAG(IS_IOS) +TEST_F(SafariDataImporterTest, ImportsMultipleReadingListItems) { + ExpectBookmarksReady(5u); + PrepareBookmarks( + R"(<!DOCTYPE NETSCAPE-Bookmark-file-1> + <DL> + <DT><H3 id="com.apple.ReadingList">Reading List</H3> + <DL> + <DT><A HREF="https://www.item1.com/">First Item</A> + <DT><A HREF="https://www.item2.com/">Second Item</A> + <DT>Third Item No URL</DT> + <DT><A HREF="invalid_url">Invalid URL</A> + <DT><A HREF="https://www.item3.com/">Third Item</A> + </DL> + </DL>)"); + + EXPECT_CALL(client_, OnBookmarksImported(3u)); + EXPECT_CALL(client_, OnHistoryImported(0)); + EXPECT_CALL(client_, OnPaymentCardsImported(0)); + CompleteImport({}); + + const ReadingListModel* model = GetReadingListModel(); + + const auto& reading_list_entries = model->GetKeys(); + ASSERT_EQ(reading_list_entries.size(), 3u); + + const ReadingListEntry* entry1 = + model->GetEntryByURL(GURL("https://www.item1.com/")).get(); + ASSERT_TRUE(entry1); + EXPECT_EQ(entry1->Title(), "First Item"); + + const ReadingListEntry* entry2 = + model->GetEntryByURL(GURL("https://www.item2.com/")).get(); + ASSERT_TRUE(entry2); + EXPECT_EQ(entry2->Title(), "Second Item"); + + const ReadingListEntry* entry3 = + model->GetEntryByURL(GURL("https://www.item3.com/")).get(); + ASSERT_TRUE(entry3); + EXPECT_EQ(entry3->Title(), "Third Item"); +} +#endif // BUILDFLAG(IS_IOS) + +TEST_F(SafariDataImporterTest, DuplicateBookmarkFolders) { +// TODO(crbug.com/407587751): Align behaviour of ContentBookmarkParser and +// IOSBookmarkParser. +#if BUILDFLAG(IS_IOS) + ExpectBookmarksReady(3u); +#else + ExpectBookmarksReady(2u); +#endif + + PrepareBookmarks( + R"(<!DOCTYPE NETSCAPE-Bookmark-file-1> + <DL> + <DT><H3>Folder A</H3> + <DL> + <DT><A HREF="https://www.example1.com/">Bookmark 1</A> + </DL> + <DT><H3>Folder A</H3> <DL> + <DT><H3>Folder B</H3> + <DL> + <DT><A HREF="https://www.example2.com/">Bookmark 2</A> + </DL> + </DL> + <DT><H3>Folder A</H3> <DL> + <DT><A HREF="https://www.example3.com/">Bookmark 3</A> + </DL> + </DL>)"); + +// TODO(crbug.com/407587751): Align behaviour of ContentBookmarkParser and +// IOSBookmarkParser. +#if BUILDFLAG(IS_IOS) + EXPECT_CALL(client_, OnBookmarksImported(3u)); +#else + EXPECT_CALL(client_, OnBookmarksImported(2u)); +#endif + + EXPECT_CALL(client_, OnHistoryImported(0)); + EXPECT_CALL(client_, OnPaymentCardsImported(0)); + CompleteImport({}); + + const bookmarks::BookmarkNode* import_folder = + GetOtherBookmarkNode()->children().at(0).get(); + +#if BUILDFLAG(IS_IOS) + EXPECT_THAT( + import_folder->children(), + ElementsAre( + IsFolder(u"Folder A", + ElementsAre(IsUrlBookmark( + u"Bookmark 1", GURL("https://www.example1.com/")))), + IsFolder(u"Folder A", + ElementsAre(IsFolder( + u"Folder B", ElementsAre(IsUrlBookmark( + u"Bookmark 2", + GURL("https://www.example2.com/")))))), + IsFolder(u"Folder A", + ElementsAre(IsUrlBookmark( + u"Bookmark 3", GURL("https://www.example3.com/")))))); +#else + EXPECT_THAT( + import_folder->children(), + ElementsAre( + IsFolder(u"Folder A", + ElementsAre(IsUrlBookmark( + u"Bookmark 1", GURL("https://www.example1.com/")))), + IsFolder(u"Folder B", + ElementsAre(IsUrlBookmark( + u"Bookmark 2", GURL("https://www.example2.com/")))))); +#endif +} + } // namespace user_data_importer
diff --git a/components/user_data_importer_strings.grdp b/components/user_data_importer_strings.grdp new file mode 100644 index 0000000..70e2336 --- /dev/null +++ b/components/user_data_importer_strings.grdp
@@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<grit-part> + <message name="IDS_IMPORTED_FROM_SAFARI_FOLDER" desc="The title of a folder that contains bookmarks imported from the Safari browser."> + Imported from Safari + </message> +</grit-part> \ No newline at end of file
diff --git a/components/user_data_importer_strings_grdp/IDS_IMPORTED_FROM_SAFARI_FOLDER.png.sha1 b/components/user_data_importer_strings_grdp/IDS_IMPORTED_FROM_SAFARI_FOLDER.png.sha1 new file mode 100644 index 0000000..fe9ec51 --- /dev/null +++ b/components/user_data_importer_strings_grdp/IDS_IMPORTED_FROM_SAFARI_FOLDER.png.sha1
@@ -0,0 +1 @@ +d0adaea468672a6524ecac8877b4bb268f4b427d \ No newline at end of file
diff --git a/components/user_education/common/ntp_promo/ntp_promo_controller.cc b/components/user_education/common/ntp_promo/ntp_promo_controller.cc index 248f1ebe..23dc67ad 100644 --- a/components/user_education/common/ntp_promo/ntp_promo_controller.cc +++ b/components/user_education/common/ntp_promo/ntp_promo_controller.cc
@@ -155,7 +155,11 @@ storage_service_->ReadNtpPromoData(id).value_or(KeyedNtpPromoData()); if (data.last_top_spot_session != current_session) { data.last_top_spot_session = current_session; - ++data.top_spot_session_count; + // If this promo is reclaiming the top spot, start a fresh count. + if (id != GetMostRecentTopSpotPromo()) { + data.top_spot_session_count = 0; + } + data.top_spot_session_count++; storage_service_->SaveNtpPromoData(id, data); } } @@ -175,4 +179,18 @@ return promos; } +NtpPromoIdentifier NtpPromoController::GetMostRecentTopSpotPromo() { + int most_recent_session = 0; + NtpPromoIdentifier most_recent_id; + for (const auto& id : registry_->GetNtpPromoIdentifiers()) { + auto prefs = + storage_service_->ReadNtpPromoData(id).value_or(KeyedNtpPromoData()); + if (prefs.last_top_spot_session > most_recent_session) { + most_recent_session = prefs.last_top_spot_session; + most_recent_id = id; + } + } + return most_recent_id; +} + } // namespace user_education
diff --git a/components/user_education/common/ntp_promo/ntp_promo_controller.h b/components/user_education/common/ntp_promo/ntp_promo_controller.h index fd9a2328..ca14eb04 100644 --- a/components/user_education/common/ntp_promo/ntp_promo_controller.h +++ b/components/user_education/common/ntp_promo/ntp_promo_controller.h
@@ -88,6 +88,10 @@ // Updates the data on the promo shown in the top spot. void OnPromoShownInTopSpot(NtpPromoIdentifier id); + // Checks which promo ID (if any) was most recently shown in the top spot. + // Returns an empty string if there is no recorded top-spot promo. + NtpPromoIdentifier GetMostRecentTopSpotPromo(); + // Assembles a vector of showable promo objects (ie. the presentation parts // of the promo) to be sent to the NTP. std::vector<NtpShowablePromo> MakeShowablePromos(
diff --git a/components/user_education/common/ntp_promo/ntp_promo_controller_unittest.cc b/components/user_education/common/ntp_promo/ntp_promo_controller_unittest.cc index e55dade..ac32b279 100644 --- a/components/user_education/common/ntp_promo/ntp_promo_controller_unittest.cc +++ b/components/user_education/common/ntp_promo/ntp_promo_controller_unittest.cc
@@ -46,6 +46,12 @@ /*show_after=*/{}, user_education::Metadata())); } + // Register a promo with empty callbacks. + void RegisterPromo(NtpPromoIdentifier id) { + RegisterPromo(id, NtpPromoSpecification::EligibilityCallback(), + NtpPromoSpecification::ActionCallback()); + } + base::test::TaskEnvironment task_environment_{ base::test::TaskEnvironment::TimeSource::MOCK_TIME}; NtpPromoRegistry registry_; @@ -185,14 +191,15 @@ EXPECT_EQ(prefs.value().last_clicked, base::Time::Now()); } -TEST_F(NtpPromoControllerTest, OnPromosShown_CompletedPromoOnly) { +TEST_F(NtpPromoControllerTest, CompletedPromoShown) { const auto old_value = storage_service_.ReadNtpPromoData(kPromoId); controller_.OnPromosShown({}, {kPromoId}); const auto new_value = storage_service_.ReadNtpPromoData(kPromoId); EXPECT_EQ(old_value, new_value); } -TEST_F(NtpPromoControllerTest, OnPromosShown_EligiblePromo_NoPreviousData) { +TEST_F(NtpPromoControllerTest, TopSpotPromoShownFirstTime) { + RegisterPromo(kPromoId); const auto old_value = storage_service_.ReadNtpPromoData(kPromoId); EXPECT_EQ(std::nullopt, old_value); controller_.OnPromosShown({kPromoId}, {}); @@ -201,18 +208,58 @@ EXPECT_EQ(1, new_value->top_spot_session_count); } -TEST_F(NtpPromoControllerTest, OnPromosShown_EligiblePromo_PreviousData) { +// When the shown top spot promo was previously in the top spot, during the +// same browsing session, prefs shouldn't change. +TEST_F(NtpPromoControllerTest, TopSpotPromoShownInSameSession) { + RegisterPromo(kPromoId); + KeyedNtpPromoData old_value; + old_value.last_top_spot_session = kSessionNumber; + old_value.top_spot_session_count = 2; + storage_service_.SaveNtpPromoData(kPromoId, old_value); + controller_.OnPromosShown({kPromoId}, {}); + const auto new_value = storage_service_.ReadNtpPromoData(kPromoId); + EXPECT_EQ(kSessionNumber, new_value->last_top_spot_session); + EXPECT_EQ(2, new_value->top_spot_session_count); +} + +// When the shown top spot promo was previously in the top spot, during the +// previous browsing session, the top spot session count should be incremented. +TEST_F(NtpPromoControllerTest, TopSpotPromoShownInNewSession) { + RegisterPromo(kPromoId); KeyedNtpPromoData old_value; old_value.last_top_spot_session = kSessionNumber - 1; old_value.top_spot_session_count = 2; storage_service_.SaveNtpPromoData(kPromoId, old_value); controller_.OnPromosShown({kPromoId}, {}); const auto new_value = storage_service_.ReadNtpPromoData(kPromoId); - EXPECT_EQ(10, new_value->last_top_spot_session); + EXPECT_EQ(kSessionNumber, new_value->last_top_spot_session); EXPECT_EQ(3, new_value->top_spot_session_count); } -TEST_F(NtpPromoControllerTest, OnPromosShown_MultiplePromos) { +// When the shown top spot promo was not previously in the top spot, it should +// clear its top spot count to start a fresh stay at the top of the list. +TEST_F(NtpPromoControllerTest, TopSpotPromoShownReclaimsTopSpot) { + RegisterPromo(kPromoId); + RegisterPromo(kPromo2Id); + + // Have Promo2 be the most recent top-spot holder. + KeyedNtpPromoData old_promo_2; + old_promo_2.last_top_spot_session = kSessionNumber - 1; + storage_service_.SaveNtpPromoData(kPromo2Id, old_promo_2); + // Have Promo be a previous top-spot holder, before Promo2. + KeyedNtpPromoData old_value; + old_promo_2.last_top_spot_session = kSessionNumber - 2; + old_promo_2.top_spot_session_count = 3; + storage_service_.SaveNtpPromoData(kPromoId, old_value); + + // Showing Promo should clear its top spot count and restart at 1. + controller_.OnPromosShown({kPromoId}, {}); + const auto new_value = storage_service_.ReadNtpPromoData(kPromoId); + EXPECT_EQ(kSessionNumber, new_value->last_top_spot_session); + EXPECT_EQ(1, new_value->top_spot_session_count); +} + +TEST_F(NtpPromoControllerTest, OnMultiplePromosShown) { const auto old_value2 = storage_service_.ReadNtpPromoData(kPromo2Id); controller_.OnPromosShown({kPromoId, kPromo2Id}, {}); const auto new_value = storage_service_.ReadNtpPromoData(kPromoId);
diff --git a/content/browser/bluetooth/README.md b/content/browser/bluetooth/README.md index 31da2c8..292bc8b 100644 --- a/content/browser/bluetooth/README.md +++ b/content/browser/bluetooth/README.md
@@ -20,8 +20,8 @@ which is created per origin. The new permissions system is implemented by providing an implementation for the -`//content/public/browser/bluetooth_delegate.h` interface. In Chrome and -WebLayer, the implementation of this interface is provided by +`//content/public/browser/bluetooth_delegate.h` interface. In Chrome, +the implementation of this interface is provided by `//components/permissions/bluetooth_delegate_impl.h` which forwards permission queries to `//components/permissions/contexts/bluetooth_chooser_context.h`. This class uses `//components/permissions/object_permission_context_base.h` as the
diff --git a/content/browser/display_cutout/OWNERS b/content/browser/display_cutout/OWNERS new file mode 100644 index 0000000..825f537 --- /dev/null +++ b/content/browser/display_cutout/OWNERS
@@ -0,0 +1 @@ +file://chrome/android/java/src/org/chromium/chrome/browser/display_cutout/OWNERS \ No newline at end of file
diff --git a/content/browser/gpu/gpu_disk_cache_factory.cc b/content/browser/gpu/gpu_disk_cache_factory.cc index c0ae026..5a12dfb 100644 --- a/content/browser/gpu/gpu_disk_cache_factory.cc +++ b/content/browser/gpu/gpu_disk_cache_factory.cc
@@ -14,6 +14,7 @@ namespace { +gpu::GpuDiskCacheFactory* g_gpu_disk_cache_factory_for_testing = nullptr; gpu::GpuDiskCacheFactory* factory_instance = nullptr; void CreateFactoryInstance() { @@ -52,7 +53,25 @@ } gpu::GpuDiskCacheFactory* GetGpuDiskCacheFactorySingleton() { + if (g_gpu_disk_cache_factory_for_testing) + return g_gpu_disk_cache_factory_for_testing; return factory_instance; } -} // namespace content +void SetGpuDiskCacheFactorySingletonForTesting( + gpu::GpuDiskCacheFactory* factory) { + g_gpu_disk_cache_factory_for_testing = factory; +} + +void DestroyGpuDiskCacheFactorySingletonForTesting() { + if (g_gpu_disk_cache_factory_for_testing) { + delete g_gpu_disk_cache_factory_for_testing; + g_gpu_disk_cache_factory_for_testing = nullptr; + } + if (factory_instance) { + delete factory_instance; + factory_instance = nullptr; + } +} + +} // namespace content \ No newline at end of file
diff --git a/content/browser/gpu/gpu_disk_cache_factory.h b/content/browser/gpu/gpu_disk_cache_factory.h index e0257147..2e0293df 100644 --- a/content/browser/gpu/gpu_disk_cache_factory.h +++ b/content/browser/gpu/gpu_disk_cache_factory.h
@@ -17,6 +17,13 @@ // This can return nullptr if an instance has not yet been created. CONTENT_EXPORT gpu::GpuDiskCacheFactory* GetGpuDiskCacheFactorySingleton(); +// Sets the GpuDiskCacheFactory singleton instance for testing. +CONTENT_EXPORT void SetGpuDiskCacheFactorySingletonForTesting( + gpu::GpuDiskCacheFactory* factory); + +// Tears down the GpuDiskCacheFactory singleton instance. For use in tests. +CONTENT_EXPORT void DestroyGpuDiskCacheFactorySingletonForTesting(); + } // namespace content -#endif // CONTENT_BROWSER_GPU_GPU_DISK_CACHE_FACTORY_H_ +#endif // CONTENT_BROWSER_GPU_GPU_DISK_CACHE_FACTORY_H_ \ No newline at end of file
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc index 2ed80272..7319f7b 100644 --- a/content/browser/gpu/gpu_process_host.cc +++ b/content/browser/gpu/gpu_process_host.cc
@@ -775,7 +775,7 @@ static_cast<int>(content::RESULT_CODE_GPU_DEAD_ON_ARRIVAL)) { // Add a sample to Stability.Counts2's GPU crash bucket. // - // On Android Chrome and Android WebLayer, GPU crashes are logged via + // On Android Chrome, GPU crashes are logged via // ContentStabilityMetricsProvider::OnCrashDumpProcessed() and // StabilityMetricsHelper::IncreaseGpuCrashCount(). metrics::StabilityMetricsHelper::RecordStabilityEvent(
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc index 5ebd46d9..c95e4498 100644 --- a/content/browser/loader/navigation_url_loader_impl.cc +++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -771,8 +771,8 @@ resource_request_->navigation_redirect_chain [resource_request_->navigation_redirect_chain.size() - 2]))) { - if (url_loader_) { - url_loader_->ResetForFollowRedirect( + if (loader_holder_.url_loader()) { + loader_holder_.url_loader()->ResetForFollowRedirect( *resource_request_.get(), url_loader_removed_headers_, url_loader_modified_headers_, url_loader_modified_cors_exempt_headers_); @@ -780,7 +780,7 @@ url_loader_modified_headers_.Clear(); url_loader_modified_cors_exempt_headers_.Clear(); } - url_loader_.reset(); + loader_holder_.ResetLoader(); } received_response_ = false; head_update_params_ = ResponseHeadUpdateParams(); @@ -846,28 +846,70 @@ // If `url_loader_` already exists, this means we are following a redirect // using an interceptor. In this case we should make sure to reset the // loader, similar to what is done in Restart(). - if (url_loader_) { - url_loader_->ResetForFollowRedirect( + if (loader_holder_.url_loader()) { + loader_holder_.url_loader()->ResetForFollowRedirect( *resource_request_.get(), url_loader_removed_headers_, url_loader_modified_headers_, url_loader_modified_cors_exempt_headers_); url_loader_removed_headers_.clear(); url_loader_modified_headers_.Clear(); url_loader_modified_cors_exempt_headers_.Clear(); - url_loader_.reset(); } + loader_holder_.ResetLoader(); CreateThrottlingLoaderAndStart(std::move(single_request_factory), std::move(additional_throttles)); } +NavigationURLLoaderImpl::LoaderHolder::LoaderHolder( + network::mojom::URLLoaderClient* receiver) + : response_loader_receiver_(receiver) {} + +NavigationURLLoaderImpl::LoaderHolder::~LoaderHolder() = default; + +void NavigationURLLoaderImpl::LoaderHolder::Reset() { + response_loader_receiver_.reset(); + url_loader_.reset(); +} + +void NavigationURLLoaderImpl::LoaderHolder::ResetLoader() { + url_loader_.reset(); +} + +void NavigationURLLoaderImpl::LoaderHolder::BindReceiver( + mojo::PendingReceiver<network::mojom::URLLoaderClient> pending_receiver, + scoped_refptr<base::SequencedTaskRunner> task_runner) { + response_loader_receiver_.reset(); + response_loader_receiver_.Bind(std::move(pending_receiver), + std::move(task_runner)); + url_loader_.reset(); +} + +void NavigationURLLoaderImpl::LoaderHolder::SetLoader( + std::unique_ptr<blink::ThrottlingURLLoader> url_loader) { + url_loader_ = std::move(url_loader); +} + +network::mojom::URLLoaderClientEndpointsPtr +NavigationURLLoaderImpl::LoaderHolder::Unbind() { + if (url_loader_) { + // Even after this point `url_loader_` should be alive and accessed via + // `url_loader()`. + // TODO(https://crbug.com/40251638): Clean up this behavior if needed. + return url_loader_->Unbind(); + } else { + return network::mojom::URLLoaderClientEndpoints::New( + std::move(response_url_loader_), response_loader_receiver_.Unbind()); + } +} + void NavigationURLLoaderImpl::StartNonInterceptedRequest( ResponseHeadUpdateParams head_update_params) { // If we already have the default `url_loader_` we must come here after a - // redirect. No interceptors wanted to intercept the redirected request, so - // let the loader just follow the redirect. - if (url_loader_) { + // redirect. No interceptors wanted to intercept the redirected request, + // so let the loader just follow the redirect. + if (loader_holder_.url_loader()) { DCHECK(!redirect_info_.new_url.is_empty()); - url_loader_->FollowRedirect( + loader_holder_.url_loader()->FollowRedirect( std::move(url_loader_removed_headers_), std::move(url_loader_modified_headers_), std::move(url_loader_modified_cors_exempt_headers_)); @@ -883,7 +925,7 @@ factory = GetOrCreateNonNetworkLoaderFactory(); } - response_loader_receiver_.reset(); + loader_holder_.Reset(); CreateThrottlingLoaderAndStart(std::move(factory), /*additional_throttles=*/{}); } @@ -1036,7 +1078,7 @@ "navigation", "NavigationURLLoaderImpl::CreateThrottlingLoaderAndStart", TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); - CHECK(!url_loader_); + CHECK(!loader_holder_.url_loader()); std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles = CreateURLLoaderThrottles(); @@ -1047,13 +1089,15 @@ uint32_t options = GetURLLoaderOptions(resource_request_->is_outermost_main_frame); - url_loader_ = blink::ThrottlingURLLoader::CreateLoaderAndStart( - std::move(factory), std::move(throttles), global_request_id_.request_id, - options, resource_request_.get(), /*client=*/this, + loader_holder_.SetLoader(blink::ThrottlingURLLoader::CreateLoader( + std::move(throttles), /*client=*/this, kNavigationUrlLoaderTrafficAnnotation, + /*client_receiver_delegate=*/nullptr)); + loader_holder_.url_loader()->Start( + std::move(factory), global_request_id_.request_id, options, + resource_request_.get(), GetUIThreadTaskRunner({BrowserTaskType::kNavigationNetworkResponse}), /*cors_exempt_header_list=*/std::nullopt, - /*client_receiver_delegate=*/nullptr, &request_info_->common_params->initiator_origin_trial_features); } @@ -1160,14 +1204,8 @@ return; } - network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints; - - if (url_loader_) { - url_loader_client_endpoints = url_loader_->Unbind(); - } else { - url_loader_client_endpoints = network::mojom::URLLoaderClientEndpoints::New( - std::move(response_url_loader_), response_loader_receiver_.Unbind()); - } + network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints = + loader_holder_.Unbind(); // 304 responses should abort the navigation, rather than display the page. // This needs to be after the URLLoader has been moved to @@ -1177,7 +1215,7 @@ head->headers->response_code() == net::HTTP_NOT_MODIFIED) { // Call CancelWithError instead of OnComplete so that if there is an // intercepting URLLoaderFactory it gets notified. - url_loader_->CancelWithError( + loader_holder_.url_loader()->CancelWithError( net::ERR_ABORTED, std::string_view(base::NumberToString(net::ERR_ABORTED))); return; @@ -1283,11 +1321,11 @@ } } if (error != net::OK) { - if (url_loader_) { + if (loader_holder_.url_loader()) { // Call CancelWithError instead of OnComplete so that if there is an // intercepting URLLoaderFactory (created through the embedder's // ContentBrowserClient::WillCreateURLLoaderFactory) it gets notified. - url_loader_->CancelWithError( + loader_holder_.url_loader()->CancelWithError( error, std::string_view(base::NumberToString(error))); } else { // TODO(crbug.com/40118809): Make sure ResetWithReason() is called @@ -1494,7 +1532,7 @@ // If the request is restarted, all of the client hints should be replaced // the "original"/non-edited values. resource_request_->headers.MergeFrom(modified_headers); - url_loader_.reset(); + loader_holder_.ResetLoader(); Restart(); } @@ -1522,14 +1560,12 @@ bool skip_other_interceptors = false; if (interceptor->MaybeCreateLoaderForResponse( status, *resource_request_, response, &response_body_, - &response_url_loader_, &response_client_receiver, url_loader_.get(), - &skip_other_interceptors)) { - response_loader_receiver_.reset(); - response_loader_receiver_.Bind( + loader_holder_.response_url_loader(), &response_client_receiver, + loader_holder_.url_loader(), &skip_other_interceptors)) { + loader_holder_.BindReceiver( std::move(response_client_receiver), GetUIThreadTaskRunner({BrowserTaskType::kNavigationNetworkResponse})); default_loader_used_ = false; - url_loader_.reset(); // Consumed above. response_body_.reset(); // Consumed above. if (skip_other_interceptors) { std::vector<std::unique_ptr<NavigationLoaderInterceptor>>
diff --git a/content/browser/loader/navigation_url_loader_impl.h b/content/browser/loader/navigation_url_loader_impl.h index cd1b7730..6541d067 100644 --- a/content/browser/loader/navigation_url_loader_impl.h +++ b/content/browser/loader/navigation_url_loader_impl.h
@@ -317,15 +317,6 @@ // current navigation. bool default_loader_used_ = false; - // URLLoaderClient receiver for loaders created for responses received from - // the network loader. - mojo::Receiver<network::mojom::URLLoaderClient> response_loader_receiver_{ - this}; - - // URLLoader instance for response loaders, i.e loaders created for handling - // responses received from the network URLLoader. - mojo::PendingRemote<network::mojom::URLLoader> response_url_loader_; - // Set to true if we receive a valid response from a URLLoader, i.e. // URLLoaderClient::OnReceiveResponse() is called. bool received_response_ = false; @@ -350,7 +341,72 @@ std::map<std::string, scoped_refptr<network::SharedURLLoaderFactory>> non_network_url_loader_factories_; - std::unique_ptr<blink::ThrottlingURLLoader> url_loader_; + // `NavigationURLLoaderImpl` performs the loading (receiving the + // `URLLoaderClient` callbacks and related operations) in multiple ways (e.g. + // through `url_loader_` or `response_loader_receiver_`). `LoaderHolder` + // centralizes these, to ensure that: + // - All related operations are cancelled when `Reset()` is called. + // - The state transitions are consistent. + // + // Note: This isn't a complete encapsulation. Some states and operations + // (especially related to https://crbug.com/434182226) are centralized and + // checked in `LoaderHolder`, while other operations are still performed + // directly via `url_loader()`. + class LoaderHolder final { + public: + explicit LoaderHolder(network::mojom::URLLoaderClient* receiver); + ~LoaderHolder(); + + blink::ThrottlingURLLoader* url_loader() const { return url_loader_.get(); } + mojo::PendingRemote<network::mojom::URLLoader>* response_url_loader() { + return &response_url_loader_; + } + + // Cancel the current loading, if any. + // Any associated pending operations should be cancelled. + // TODO(https://crbug.com/434182226): Still some known pending operations + // are not cancelled. Actually cancel them. + void Reset(); + + // Resets `url_loader_`. + // TODO(https://crbug.com/434182226): Use `Reset()` instead to always reset + // `response_loader_receiver_` as well. + void ResetLoader(); + + // For starting loading via `url_loader` (transitioning from `kNone` to + // `kLoadingViaLoader`). THe caller should actually start the loading by + // calling `url_loader->Start()`. + void SetLoader(std::unique_ptr<blink::ThrottlingURLLoader> url_loader); + + // Switches to loading via `pending_receiver` (transitioning from + // `kLoadingViaLoader` to `kLoadingViaReceiver`). The caller might already + // call `url_loader()->Unbind()` etc. + void BindReceiver( + mojo::PendingReceiver<network::mojom::URLLoaderClient> pending_receiver, + scoped_refptr<base::SequencedTaskRunner> task_runner); + + // Unbind the endpoints from ``NavigationURLLoaderImpl`` to + // `URLLoaderClientEndpointsPtr` (transitioning to `kUnbound`). + [[nodiscard]] network::mojom::URLLoaderClientEndpointsPtr Unbind(); + + private: + // `NavigationURLLoaderImpl`'s `URLLoaderClient` methods are called either + // via `url_loader_` or `response_loader_receiver_`. + std::unique_ptr<blink::ThrottlingURLLoader> url_loader_; + mojo::Receiver<network::mojom::URLLoaderClient> response_loader_receiver_; + + // URLLoader instance for response loaders, i.e loaders created for handling + // responses received from the network URLLoader. + // + // NOTE: This looks like coupled with + // `LoaderHolder::response_loader_receiver_` but actually isn't, because + // `response_url_loader_` is never touched during + // `MaybeCreateLoaderForResponse()` (at least within Chromium codesearch). + // For now this is kept here as-is but probably can be removed. + mojo::PendingRemote<network::mojom::URLLoader> response_url_loader_; + }; + + LoaderHolder loader_holder_{this}; std::unique_ptr<NavigationEarlyHintsManager> early_hints_manager_;
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc index 82438d8..507a444 100644 --- a/content/browser/media/session/media_session_impl.cc +++ b/content/browser/media/session/media_session_impl.cc
@@ -797,10 +797,10 @@ } RenderFrameHost* MediaSessionImpl::GetRoutedFrame() { - if (!routed_service_) { - return nullptr; + if (routed_service_) { + return routed_service_->GetRenderFrameHost(); } - return routed_service_->GetRenderFrameHost(); + return ComputeFrameForRouting(/*ensure_service=*/false); } std::optional<media_session::MediaPosition> @@ -1628,7 +1628,9 @@ } void MediaSessionImpl::UpdateRoutedService() { - MediaSessionServiceImpl* new_service = ComputeServiceForRouting(); + RenderFrameHost* rfh = ComputeFrameForRouting(/*ensure_service=*/true); + MediaSessionServiceImpl* new_service = + rfh ? services_[rfh->GetGlobalId()] : nullptr; if (new_service == routed_service_) return; @@ -1641,40 +1643,46 @@ RebuildAndNotifyMediaPositionChanged(); } -MediaSessionServiceImpl* MediaSessionImpl::ComputeServiceForRouting() { - // The service selection strategy is: select a frame that has a playing/paused - // player and has a corresponding MediaSessionService and return the - // corresponding MediaSessionService. If multiple frames satisfy the criteria, - // prefer the top-most frame. +// Select a frame that has a playing or paused media player, or has a +// MediaSessionService created to handle media session APIs without having a +// media player. Select the top-most frame if multiple frames satisfy the +// criteria. If |ensure_service| is set to true, the selected frame must also +// have a corresponding MediaSessionService. +RenderFrameHost* MediaSessionImpl::ComputeFrameForRouting(bool ensure_service) { + // First collect all the frames that have a playing or paused media player. std::set<RenderFrameHost*> frames; for (const auto& player : normal_players_) { RenderFrameHost* frame = player.first.observer->render_frame_host(); - if (frame) + if (frame) { frames.insert(frame); + } } - for (const auto& player : one_shot_players_) { RenderFrameHost* frame = player.observer->render_frame_host(); - if (frame) + if (frame) { frames.insert(frame); + } } + // Compute to find the frame with the minimum depth. RenderFrameHost* best_frame = nullptr; size_t min_depth = std::numeric_limits<size_t>::max(); std::map<RenderFrameHost*, size_t> map_rfh_to_depth; for (RenderFrameHost* frame : frames) { size_t depth = ComputeFrameDepth(frame, &map_rfh_to_depth); - if (depth >= min_depth) + if (depth >= min_depth) { continue; - if (!IsServiceActiveForRenderFrameHost(frame)) + } + if (ensure_service && !IsServiceActiveForRenderFrameHost(frame)) { continue; + } best_frame = frame; min_depth = depth; } - // If we don't have a suitable frame yet, then take the topmost frame that has - // a MediaSessionService. + // If we cannot find a suitable frame, take the top-most frame with an active + // MediaSessionService. if (!best_frame && base::FeatureList::IsEnabled( blink::features::kMediaSessionEnterPictureInPicture)) { // `FrameTree::Nodes()` iterates in breadth-first order, so this is @@ -1691,7 +1699,7 @@ } } - return best_frame ? services_[best_frame->GetGlobalId()] : nullptr; + return best_frame; } void MediaSessionImpl::OnMediaMutedStatusChanged(bool mute) {
diff --git a/content/browser/media/session/media_session_impl.h b/content/browser/media/session/media_session_impl.h index 3c0a98fe..4539e43 100644 --- a/content/browser/media/session/media_session_impl.h +++ b/content/browser/media/session/media_session_impl.h
@@ -209,9 +209,8 @@ const base::UnguessableToken& group_id) override; // Returns the `RenderFrameHost` for the currently MediaSession routed - // service. - // TODO(crbug.com/409427125): Also return a frame if no service is created for - // a player. + // service, if the routed service exists, otherwise returns the top most frame + // with an active media player. RenderFrameHost* GetRoutedFrame() override; // Returns the current media session info synchronously for a one-off request. @@ -475,9 +474,12 @@ // Returns whether the frame |rfh| uses MediaSession API. bool IsServiceActiveForRenderFrameHost(RenderFrameHost* rfh); - // Compute the MediaSessionService that should be routed, which will be used - // to update |routed_service_|. - CONTENT_EXPORT MediaSessionServiceImpl* ComputeServiceForRouting(); + // Compute the frame that should be routed for media session. If + // |ensure_service| is true, the routed frame must have an active + // MediaSessionService, otherwise it does not, e.g. when no MediaSession API + // has been called but there is an active media player. This method can be + // used to compute both the routed frame and routed service. + CONTENT_EXPORT RenderFrameHost* ComputeFrameForRouting(bool ensure_service); // Rebuilds |actions_| and notifies observers if they have changed. void RebuildAndNotifyActionsChanged();
diff --git a/content/browser/media/session/media_session_impl_service_routing_unittest.cc b/content/browser/media/session/media_session_impl_service_routing_unittest.cc index 1ae9b89a..dd2b9c1 100644 --- a/content/browser/media/session/media_session_impl_service_routing_unittest.cc +++ b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
@@ -243,7 +243,11 @@ } MediaSessionServiceImpl* ComputeServiceForRouting() { - return MediaSessionImpl::Get(contents())->ComputeServiceForRouting(); + auto* frame = static_cast<TestRenderFrameHost*>( + MediaSessionImpl::Get(contents()) + ->ComputeFrameForRouting(/*ensure_service=*/true)); + return services_.find(frame) != services_.end() ? services_[frame].get() + : nullptr; } MediaSessionImpl* GetMediaSession() { @@ -1377,6 +1381,28 @@ } } +TEST_F(MediaSessionImplServiceRoutingTest, GetRoutedFrameForPlayers) { + StartPlayerForFrame(main_frame_); + ASSERT_EQ(main_frame_, GetMediaSession()->GetRoutedFrame()); + StartPlayerForFrame(sub_frame_); + ASSERT_EQ(main_frame_, GetMediaSession()->GetRoutedFrame()); + ClearPlayersForFrame(main_frame_); + ASSERT_EQ(sub_frame_, GetMediaSession()->GetRoutedFrame()); + ClearPlayersForFrame(sub_frame_); + ASSERT_EQ(nullptr, GetMediaSession()->GetRoutedFrame()); +} + +TEST_F(MediaSessionImplServiceRoutingTest, GetRoutedFrameForServices) { + CreateServiceForFrame(main_frame_); + ASSERT_EQ(main_frame_, GetMediaSession()->GetRoutedFrame()); + CreateServiceForFrame(sub_frame_); + ASSERT_EQ(main_frame_, GetMediaSession()->GetRoutedFrame()); + DestroyServiceForFrame(main_frame_); + ASSERT_EQ(sub_frame_, GetMediaSession()->GetRoutedFrame()); + DestroyServiceForFrame(sub_frame_); + ASSERT_EQ(nullptr, GetMediaSession()->GetRoutedFrame()); +} + // Test duration duration update throttle behavior for routed service. // TODO (jazzhsu): Remove these tests once media session supports livestream. class MediaSessionImplServiceRoutingThrottleTest
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc index 0ad2140d..0e76d78 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc
@@ -6558,18 +6558,31 @@ } // Sticky user activation should only be preserved for same-site subframe - // navigations. This is done to prevent newly navigated documents from - // re-using the sticky user activation state from the previously navigated - // document in the frame. We persist user activation across same-site - // navigations for compatibility reasons, and this does not need to match the - // same-site checks used in the process model. See: crbug.com/736415. - // TODO(crbug.com/40228985): Remove this once we find a way to reset - // activation unconditionally without breaking sites in practice. + // navigations, and same-origin top-frame navigations behind the feature flag + // StickyUserActivationAcrossSameOriginNavigation. These checks limit newly + // navigated documents from reusing the sticky user activation state from the + // previously navigated document in the frame. + // + // - We persist user activation across same-site navigations for compatibility + // reasons, and this does not need to match the same-site checks used in the + // process model. See https://crbug.com/40527366. + // + // TODO(crbug.com/40228985): Remove this once we find a way to reset + // activation unconditionally without breaking sites in practice. + // + // - The feature flag StickyUserActivationAcrossSameOriginNavigation relaxes + // the preservation of sticky activation to include same-origin navigations + // to ease multi-page app development which currently face problem with, for + // example, virtual keyboards. See https://crbug.com/433729626. commit_params_->should_have_sticky_user_activation = - !frame_tree_node_->IsMainFrame() && old_frame_host->HasStickyUserActivation() && - net::SchemefulSite::IsSameSite(old_frame_host->GetLastCommittedOrigin(), - origin_to_commit); + ((!frame_tree_node_->IsMainFrame() && + net::SchemefulSite::IsSameSite(old_frame_host->GetLastCommittedOrigin(), + origin_to_commit)) || + (base::FeatureList::IsEnabled( + blink::features::kStickyUserActivationAcrossSameOriginNavigation) && + frame_tree_node_->IsMainFrame() && + old_frame_host->GetLastCommittedOrigin() == origin_to_commit)); // Generate a UKM source and track it on NavigationRequest. This will be // passed down to the blink::Document to be created, if any, and used for UKM
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc index e44ece8..f1a93f8 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -436,7 +436,7 @@ uint32_t g_accessibility_reset_token = 0; // Whether to allow injecting javascript into any kind of frame, for Android -// WebView, WebLayer, Fuchsia web.ContextProvider and CastOS content shell. +// WebView, Fuchsia web.ContextProvider and CastOS content shell. bool g_allow_injecting_javascript = false; const char kDotGoogleDotCom[] = ".google.com";
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc index 008ed131..eeb6a0f 100644 --- a/content/browser/site_per_process_browsertest.cc +++ b/content/browser/site_per_process_browsertest.cc
@@ -13709,7 +13709,7 @@ // The cross-site navigation should have cleared the user activation. CheckStickyUserActivationState(child->current_frame_host(), false); - // Ensure that a top-level navigation cannot happen. + // Ensure that a top-level navigation from the iframe cannot happen. EXPECT_TRUE(ExecJs(child->current_frame_host(), JsReplace("window.open($1, $2)", http_url, "_top"), EXECUTE_SCRIPT_NO_USER_GESTURE)); @@ -13752,7 +13752,7 @@ // activation from the previous page. CheckStickyUserActivationState(child->current_frame_host(), true); - // Ensure that top-level navigations can still happen. + // Ensure that a top-level navigation from the iframe can still happen. EXPECT_TRUE(ExecJs(child->current_frame_host(), JsReplace("window.open($1, $2)", http_url, "_top"), EXECUTE_SCRIPT_NO_USER_GESTURE)); @@ -13792,7 +13792,7 @@ // previous page. CheckStickyUserActivationState(child->current_frame_host(), true); - // Ensure that top-level navigations can still happen. + // Ensure that a top-level navigation from the iframe can still happen. EXPECT_TRUE(ExecJs(child->current_frame_host(), JsReplace("window.open($1, $2)", http_url, "_top"), EXECUTE_SCRIPT_NO_USER_GESTURE)); @@ -13800,6 +13800,120 @@ EXPECT_EQ(http_url, shell()->web_contents()->GetLastCommittedURL()); } +class StickyActivationAcrossSameOriginNavBrowserTest + : public SitePerProcessBrowserTest { + public: + StickyActivationAcrossSameOriginNavBrowserTest() { + scoped_feature_list_.InitAndEnableFeature( + blink::features::kStickyUserActivationAcrossSameOriginNavigation); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +// Test that a cross-site navigation in the top frame clears user activation. +IN_PROC_BROWSER_TEST_P(StickyActivationAcrossSameOriginNavBrowserTest, + UserActivationAfterCrossSiteNavInTopFrame) { + GURL starting_url(embedded_test_server()->GetURL("a.com", "/title1.html")); + EXPECT_TRUE(NavigateToURL(shell(), starting_url)); + FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root(); + + // Sanity check that there is no sticky user activation at first. + CheckStickyUserActivationState(root->current_frame_host(), false); + + // Perform a cross-site navigation and verify there is still no sticky user + // activation. + GURL first_nav_url(embedded_test_server()->GetURL("b.com", "/title1.html")); + EXPECT_TRUE( + NavigateToURLFromRendererWithoutUserGesture(shell(), first_nav_url)); + CheckStickyUserActivationState(root->current_frame_host(), false); + + // Give the frame user activation. + EXPECT_TRUE(ExecJs(root, "// No-op script")); + CheckStickyUserActivationState(root->current_frame_host(), true); + + // Perform another cross-site navigation. + GURL second_nav_url(embedded_test_server()->GetURL( + "c.com", "/cross_site_iframe_factory.html?c(c)")); + EXPECT_TRUE( + NavigateToURLFromRendererWithoutUserGesture(shell(), second_nav_url)); + + // The navigation should have cleared the user activation. + CheckStickyUserActivationState(root->current_frame_host(), false); + CheckStickyUserActivationState(root->child_at(0)->current_frame_host(), + false); +} + +// Test that a same-site cross-origin navigation in the top frame clears user +// activation. +IN_PROC_BROWSER_TEST_P(StickyActivationAcrossSameOriginNavBrowserTest, + UserActivationAfterSameSiteNavInTopFrame) { + GURL starting_url( + embedded_test_server()->GetURL("sub1.a.com", "/title1.html")); + EXPECT_TRUE(NavigateToURL(shell(), starting_url)); + FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root(); + + // Sanity check that there is no sticky user activation at first. + CheckStickyUserActivationState(root->current_frame_host(), false); + + // Perform a same-site cross-origin navigation and verify there is still no + // sticky user activation. + GURL first_nav_url( + embedded_test_server()->GetURL("sub2.a.com", "/title1.html")); + EXPECT_TRUE( + NavigateToURLFromRendererWithoutUserGesture(shell(), first_nav_url)); + CheckStickyUserActivationState(root->current_frame_host(), false); + + // Give the frame user activation. + EXPECT_TRUE(ExecJs(root, "// No-op script")); + CheckStickyUserActivationState(root->current_frame_host(), true); + + // Perform another same-site cross-origin navigation in the iframe. + GURL second_nav_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(a)")); + EXPECT_TRUE( + NavigateToURLFromRendererWithoutUserGesture(shell(), second_nav_url)); + + // The navigation should have cleared the user activation. + CheckStickyUserActivationState(root->current_frame_host(), false); + CheckStickyUserActivationState(root->child_at(0)->current_frame_host(), + false); +} + +// Test that a same-origin navigation in the top frame keeps user activation. +IN_PROC_BROWSER_TEST_P(StickyActivationAcrossSameOriginNavBrowserTest, + UserActivationAfterSameOriginNavInTopFrame) { + GURL starting_url(embedded_test_server()->GetURL("a.com", "/title1.html")); + EXPECT_TRUE(NavigateToURL(shell(), starting_url)); + FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root(); + + // Sanity check that there is no sticky user activation at first. + CheckStickyUserActivationState(root->current_frame_host(), false); + + // Perform a same-origin navigation and verify there is still no sticky user + // activation. + GURL first_nav_url(embedded_test_server()->GetURL("a.com", "/title2.html")); + EXPECT_TRUE( + NavigateToURLFromRendererWithoutUserGesture(shell(), first_nav_url)); + CheckStickyUserActivationState(root->current_frame_host(), false); + + // Give the frame user activation. + EXPECT_TRUE(ExecJs(root, "// No-op script")); + CheckStickyUserActivationState(root->current_frame_host(), true); + + // Perform another same-origin navigation in the iframe. + GURL second_nav_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(a)")); + EXPECT_TRUE( + NavigateToURLFromRendererWithoutUserGesture(shell(), second_nav_url)); + + // The navigation should keep the user activation at the top frame only. + CheckStickyUserActivationState(root->current_frame_host(), true); + CheckStickyUserActivationState(root->child_at(0)->current_frame_host(), + false); +} + // Test which captures behavior of navigation to about:blank in a newly created // WebContents when an initial SiteInstance is supplied as part of the creation. IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, @@ -14532,5 +14646,8 @@ All, SitePerProcessWithMainFrameThresholdAndSiteRestrictionTest, testing::ValuesIn(RenderDocumentFeatureLevelValues())); +INSTANTIATE_TEST_SUITE_P(All, + StickyActivationAcrossSameOriginNavBrowserTest, + testing::ValuesIn(RenderDocumentFeatureLevelValues())); } // namespace content
diff --git a/content/browser/speech/tts_utterance_impl.h b/content/browser/speech/tts_utterance_impl.h index 95212cb..f9589760 100644 --- a/content/browser/speech/tts_utterance_impl.h +++ b/content/browser/speech/tts_utterance_impl.h
@@ -16,10 +16,6 @@ #include "content/common/content_export.h" #include "content/public/browser/tts_utterance.h" -namespace base { -class Value; -} - namespace content { class BrowserContext; class WebContents;
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc index d00eb60..5d5c12d 100644 --- a/content/browser/storage_partition_impl.cc +++ b/content/browser/storage_partition_impl.cc
@@ -3072,7 +3072,15 @@ } } - if (remove_mask_ & REMOVE_DATA_MASK_SHADER_CACHE) { + if ((remove_mask_ & REMOVE_DATA_MASK_SHADER_CACHE) && + // Old behavior: Always execute the code block below. + // New behavior: If kDisablePartialStorageCleanupForGPUDiskCache == true + // then consider the behavior of perform_storage_cleanup + // in executing the code. Note that the feature flag is only relevant + // when perform_storage_cleanup is false. + (!base::FeatureList::IsEnabled( + features::kDisablePartialStorageCleanupForGPUDiskCache) || + perform_storage_cleanup)) { gpu::GpuDiskCacheFactory* gpu_cache_factory = GetGpuDiskCacheFactorySingleton(); // May be null in tests where it is difficult to plumb through a test
diff --git a/content/browser/storage_partition_impl_unittest.cc b/content/browser/storage_partition_impl_unittest.cc index 11afe0e6..9469110 100644 --- a/content/browser/storage_partition_impl_unittest.cc +++ b/content/browser/storage_partition_impl_unittest.cc
@@ -2688,4 +2688,102 @@ EXPECT_TRUE(tester.ContainsCookie(kOrigin, kOtherPartitionKey)); } + +class MockGpuDiskCacheFactory : public gpu::GpuDiskCacheFactory { + public: + MockGpuDiskCacheFactory() = default; + ~MockGpuDiskCacheFactory() override = default; + + MOCK_METHOD(void, + ClearByPath, + (const base::FilePath&, base::Time, base::Time, base::OnceClosure), + (override)); +}; + +class StoragePartitionImplShaderCacheTest : public StoragePartitionImplTest { + public: + StoragePartitionImplShaderCacheTest() { + InitGpuDiskCacheFactorySingleton(); + SetGpuDiskCacheFactorySingletonForTesting(&mock_gpu_disk_cache_factory_); + } + + ~StoragePartitionImplShaderCacheTest() override { + SetGpuDiskCacheFactorySingletonForTesting(nullptr); + DestroyGpuDiskCacheFactorySingletonForTesting(); + } + + protected: + StoragePartition* storage_partition() { + return browser_context()->GetDefaultStoragePartition(); + } + + base::test::ScopedFeatureList feature_list_; + MockGpuDiskCacheFactory mock_gpu_disk_cache_factory_; +}; + +TEST_F(StoragePartitionImplShaderCacheTest, + ClearData_PartialCleanupDisabled_NoStorageCleanup) { + feature_list_.InitAndEnableFeature( + features::kDisablePartialStorageCleanupForGPUDiskCache); + + EXPECT_CALL(mock_gpu_disk_cache_factory_, ClearByPath(_, _, _, _)).Times(0); + + base::RunLoop run_loop; + storage_partition()->ClearData( + StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE, + StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL, + /*filter_builder=*/nullptr, + /*storage_key_policy_matcher=*/{}, + /*cookie_deletion_filter=*/nullptr, + /*perform_storage_cleanup=*/false, base::Time(), base::Time::Max(), + run_loop.QuitClosure()); + run_loop.Run(); +} + +TEST_F(StoragePartitionImplShaderCacheTest, + ClearData_PartialCleanupEnabled_WithStorageCleanup) { + feature_list_.InitAndDisableFeature( + features::kDisablePartialStorageCleanupForGPUDiskCache); + + EXPECT_CALL(mock_gpu_disk_cache_factory_, ClearByPath(_, _, _, _)) + .Times(gpu::kGpuDiskCacheTypes.size()) + .WillRepeatedly(testing::Invoke( + [](const base::FilePath&, base::Time, base::Time, + base::OnceClosure callback) { std::move(callback).Run(); })); + + base::RunLoop run_loop; + storage_partition()->ClearData( + StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE, + StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL, + /*filter_builder=*/nullptr, + /*storage_key_policy_matcher=*/{}, + /*cookie_deletion_filter=*/nullptr, + /*perform_storage_cleanup=*/true, base::Time(), base::Time::Max(), + run_loop.QuitClosure()); + run_loop.Run(); +} + +TEST_F(StoragePartitionImplShaderCacheTest, + ClearData_PartialCleanupDisabled_WithStorageCleanup) { + feature_list_.InitAndEnableFeature( + features::kDisablePartialStorageCleanupForGPUDiskCache); + + EXPECT_CALL(mock_gpu_disk_cache_factory_, ClearByPath(_, _, _, _)) + .Times(gpu::kGpuDiskCacheTypes.size()) + .WillRepeatedly(testing::Invoke( + [](const base::FilePath&, base::Time, base::Time, + base::OnceClosure callback) { std::move(callback).Run(); })); + + base::RunLoop run_loop; + storage_partition()->ClearData( + StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE, + StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL, + /*filter_builder=*/nullptr, + /*storage_key_policy_matcher=*/{}, + /*cookie_deletion_filter=*/nullptr, + /*perform_storage_cleanup=*/true, base::Time(), base::Time::Max(), + run_loop.QuitClosure()); + run_loop.Run(); +} } // namespace content +
diff --git a/content/public/android/java/src/org/chromium/content_public/common/ContentUrlConstants.java b/content/public/android/java/src/org/chromium/content_public/common/ContentUrlConstants.java index 1df07e2..610c05f 100644 --- a/content/public/android/java/src/org/chromium/content_public/common/ContentUrlConstants.java +++ b/content/public/android/java/src/org/chromium/content_public/common/ContentUrlConstants.java
@@ -6,7 +6,7 @@ import org.chromium.build.annotations.NullMarked; -/** URL constants used by both Chrome, WebLayer and WebView. */ +/** URL constants used by both Chrome and WebView. */ @NullMarked public final class ContentUrlConstants { public static final String ABOUT_SCHEME = "about";
diff --git a/content/public/browser/media_session.h b/content/public/browser/media_session.h index e171ade..d0d5f8a 100644 --- a/content/public/browser/media_session.h +++ b/content/public/browser/media_session.h
@@ -75,7 +75,8 @@ virtual void SetAudioFocusGroupId(const base::UnguessableToken& group_id) = 0; // Returns the `RenderFrameHost` for the currently MediaSession routed - // service, if the routed service exists, nullptr otherwise. + // service, if the routed service exists, otherwise returns the top most frame + // with an active media player. virtual RenderFrameHost* GetRoutedFrame() = 0; // Returns the current media session info synchronously for a one-off request.
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h index 183b6eb..290fce7c 100644 --- a/content/public/browser/render_frame_host.h +++ b/content/public/browser/render_frame_host.h
@@ -160,7 +160,7 @@ const GlobalRenderFrameHostToken& frame_token); // Globally allows for injecting JavaScript into the main world. This feature - // is present only to support Android WebView, WebLayer, Fuchsia web.Contexts, + // is present only to support Android WebView, Fuchsia web.Contexts, // and CastOS content shell. It must not be used in other configurations. static void AllowInjectingJavaScript();
diff --git a/content/public/browser/web_contents_observer.h b/content/public/browser/web_contents_observer.h index 4472536..c6af261 100644 --- a/content/public/browser/web_contents_observer.h +++ b/content/public/browser/web_contents_observer.h
@@ -35,7 +35,6 @@ #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-forward.h" #include "third_party/blink/public/mojom/media/capture_handle_config.mojom-forward.h" #include "third_party/skia/include/core/SkColor.h" -#include "ui/accessibility/ax_location_and_scroll_updates.h" #include "ui/base/page_transition_types.h" #include "ui/base/window_open_disposition.h"
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc index 778e24e2..10d1daf 100644 --- a/content/public/common/content_features.cc +++ b/content/public/common/content_features.cc
@@ -378,6 +378,13 @@ "WebContentsDiscard", base::FEATURE_DISABLED_BY_DEFAULT); +// When this feature is enabled, partial storage cleanup will be +// disabled for the GPU disk cache. (Performance improvement) +BASE_FEATURE(kDisablePartialStorageCleanupForGPUDiskCache, + "PerformStorageCleanupForGPUDiskCache", + base::FEATURE_DISABLED_BY_DEFAULT); + + // Enable drawing under System Bars within DisplayCutout. BASE_FEATURE(kDrawCutoutEdgeToEdge, "DrawCutoutEdgeToEdge",
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h index 2fa3503..be94bcb 100644 --- a/content/public/common/content_features.h +++ b/content/public/common/content_features.h
@@ -103,6 +103,7 @@ kBtmClientBounceDetectionTimeout; CONTENT_EXPORT BASE_DECLARE_FEATURE(kBtmDualUse); CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebContentsDiscard); +CONTENT_EXPORT BASE_DECLARE_FEATURE(kDisablePartialStorageCleanupForGPUDiskCache); CONTENT_EXPORT BASE_DECLARE_FEATURE(kDrawCutoutEdgeToEdge); CONTENT_EXPORT BASE_DECLARE_FEATURE(kEarlyEstablishGpuChannel); CONTENT_EXPORT BASE_DECLARE_FEATURE(kEnableCanvas2DLayers);
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc index 406d4b5..b11dc95 100644 --- a/content/public/test/browser_test_utils.cc +++ b/content/public/test/browser_test_utils.cc
@@ -1671,14 +1671,14 @@ return value()->GetList().Clone(); } -base::Value::Dict EvalJsResult::ExtractDict() const { +const base::Value::Dict& EvalJsResult::ExtractDict() const { CHECK(is_ok()) << "Can't ExtractDict() because the script encountered a problem: " << *error(); CHECK(value()->is_dict()) << "Can't ExtractDict() because script result: " << *value() << "is not a dictionary."; - return value()->GetDict().Clone(); + return value()->GetDict(); } const std::string& EvalJsResult::ExtractError() const {
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h index 114c949a..48265b7 100644 --- a/content/public/test/browser_test_utils.h +++ b/content/public/test/browser_test_utils.h
@@ -881,7 +881,7 @@ [[nodiscard]] bool ExtractBool() const; [[nodiscard]] double ExtractDouble() const; [[nodiscard]] base::Value::List ExtractList() const; - [[nodiscard]] base::Value::Dict ExtractDict() const; + [[nodiscard]] const base::Value::Dict& ExtractDict() const; [[nodiscard]] const std::string& ExtractError() const; bool is_ok() const { return std::holds_alternative<base::Value>(data_); }
diff --git a/content/utility/on_device_model/on_device_model_sandbox_init.cc b/content/utility/on_device_model/on_device_model_sandbox_init.cc index b7f6e706..ecfb356e 100644 --- a/content/utility/on_device_model/on_device_model_sandbox_init.cc +++ b/content/utility/on_device_model/on_device_model_sandbox_init.cc
@@ -10,6 +10,7 @@ #include "base/native_library.h" #include "base/path_service.h" #include "build/build_config.h" +#include "build/chromecast_buildflags.h" #if defined(ENABLE_ML_INTERNAL) #include "services/on_device_model/ml/chrome_ml.h" // nogncheck @@ -24,7 +25,8 @@ #include "sandbox/policy/linux/sandbox_linux.h" #endif -#if !BUILDFLAG(IS_FUCHSIA) +#if !BUILDFLAG(IS_FUCHSIA) && \ + !(BUILDFLAG(IS_LINUX) && BUILDFLAG(ENABLE_CAST_RECEIVER)) #include "base/feature_list.h" #include "third_party/dawn/include/dawn/dawn_proc.h" // nogncheck #include "third_party/dawn/include/dawn/native/DawnNative.h" // nogncheck @@ -63,7 +65,8 @@ } #endif -#if !BUILDFLAG(IS_FUCHSIA) +#if !BUILDFLAG(IS_FUCHSIA) && \ + !(BUILDFLAG(IS_LINUX) && BUILDFLAG(ENABLE_CAST_RECEIVER)) // If this feature is enabled, a WebGPU device is created for each valid // adapter. This makes sure any relevant drivers or other libs are loaded before // enabling the sandbox. @@ -97,7 +100,8 @@ } #endif -#if !BUILDFLAG(IS_FUCHSIA) +#if !BUILDFLAG(IS_FUCHSIA) && \ + !(BUILDFLAG(IS_LINUX) && BUILDFLAG(ENABLE_CAST_RECEIVER)) if (base::FeatureList::IsEnabled(kOnDeviceModelWarmDrivers) #if defined(ENABLE_ML_INTERNAL) && !ml::IsGpuBlocked(ml::ChromeML::Get()->api(), /*log_histogram=*/false)
diff --git a/device/BUILD.gn b/device/BUILD.gn index 4d84e82..a2c1eca 100644 --- a/device/BUILD.gn +++ b/device/BUILD.gn
@@ -210,7 +210,7 @@ ] deps += [ - "//components/sync/protocol:protocol", + "//components/sync/protocol", "//device/fido:mocks", "//services/data_decoder/public/cpp:test_support", ]
diff --git a/device/fido/BUILD.gn b/device/fido/BUILD.gn index e98293e..33c54fd 100644 --- a/device/fido/BUILD.gn +++ b/device/fido/BUILD.gn
@@ -240,8 +240,8 @@ ] deps += [ - "//components/sync/protocol:protocol", - "//device/fido/enclave/proto:proto", + "//components/sync/protocol", + "//device/fido/enclave/proto", "//services/device/public/cpp/hid", "//services/device/public/cpp/usb", "//services/device/public/mojom",
diff --git a/device/fido/features.cc b/device/fido/features.cc index c77edc7..533b4ec 100644 --- a/device/fido/features.cc +++ b/device/fido/features.cc
@@ -226,4 +226,9 @@ "WebAuthenticationSendPinGeneration", base::FEATURE_ENABLED_BY_DEFAULT); +// Default enabled in M140. Remove in or after M143. +BASE_FEATURE(kWebAuthnWrapCohortData, + "WebAuthenticationWrapCohortData", + base::FEATURE_ENABLED_BY_DEFAULT); + } // namespace device
diff --git a/device/fido/features.h b/device/fido/features.h index fd35090..3a246bf 100644 --- a/device/fido/features.h +++ b/device/fido/features.h
@@ -165,6 +165,10 @@ COMPONENT_EXPORT(DEVICE_FIDO) BASE_DECLARE_FEATURE(kWebAuthnSendPinGeneration); +// Adds the cohort public key and cert.xml serial number to GPM wrapped PINs. +COMPONENT_EXPORT(DEVICE_FIDO) +BASE_DECLARE_FEATURE(kWebAuthnWrapCohortData); + } // namespace device #endif // DEVICE_FIDO_FEATURES_H_
diff --git a/device/vr/BUILD.gn b/device/vr/BUILD.gn index a46b20f..ba2dd76 100644 --- a/device/vr/BUILD.gn +++ b/device/vr/BUILD.gn
@@ -136,7 +136,7 @@ ":vr_base", ":vr_util", "//base", - "//components/ukm:ukm", + "//components/ukm", "//device/base", "//device/vr/buildflags", "//device/vr/public/mojom:isolated_xr_service",
diff --git a/device/vr/android/BUILD.gn b/device/vr/android/BUILD.gn index 8974e35..fd820c8 100644 --- a/device/vr/android/BUILD.gn +++ b/device/vr/android/BUILD.gn
@@ -29,10 +29,10 @@ "//device/vr:vr_base", "//device/vr/public/cpp:features", "//gpu/command_buffer/service:gles2", - "//gpu/ipc/common:common", + "//gpu/ipc/common", "//ui/display", "//ui/gfx", - "//ui/gl:gl", + "//ui/gl", "//ui/gl/init", ]
diff --git a/device/vr/openxr/android/openxr_hit_test_manager_android.cc b/device/vr/openxr/android/openxr_hit_test_manager_android.cc index ea2ec56..330b077 100644 --- a/device/vr/openxr/android/openxr_hit_test_manager_android.cc +++ b/device/vr/openxr/android/openxr_hit_test_manager_android.cc
@@ -40,15 +40,14 @@ return {}; } - XrTime predicted_display_time = plane_manager_->predicted_display_time(); XrTrackableTrackerANDROID plane_tracker = plane_manager_->plane_tracker(); - if (predicted_display_time == 0 || plane_tracker == XR_NULL_HANDLE || + if (predicted_display_time_ == 0 || plane_tracker == XR_NULL_HANDLE || mojo_space_ == XR_NULL_HANDLE) { DVLOG(3) << __func__ << ": plane_manager_ not yet ready for hit-testing. " "predicted_display_time=" - << predicted_display_time << ", plane_tracker=" << plane_tracker + << predicted_display_time_ << ", plane_tracker=" << plane_tracker << ", mojo_space=" << mojo_space_; return {}; } @@ -62,7 +61,7 @@ raycast_info.trajectory = XrVector3f{ray_direction.x(), ray_direction.y(), ray_direction.z()}; raycast_info.space = mojo_space_; - raycast_info.time = predicted_display_time; + raycast_info.time = predicted_display_time_; XrRaycastHitResultANDROID xr_results_array[kMaxHitTestResults]; XrRaycastHitResultsANDROID xr_hit_results = { @@ -96,4 +95,9 @@ return hit_results; } +void OpenXrHitTestManagerAndroid::OnStartProcessingHitTests( + XrTime predicted_display_time) { + predicted_display_time_ = predicted_display_time; +} + } // namespace device
diff --git a/device/vr/openxr/android/openxr_hit_test_manager_android.h b/device/vr/openxr/android/openxr_hit_test_manager_android.h index af8cbe6..534c883 100644 --- a/device/vr/openxr/android/openxr_hit_test_manager_android.h +++ b/device/vr/openxr/android/openxr_hit_test_manager_android.h
@@ -28,9 +28,12 @@ const gfx::Vector3dF& direction) override; private: + void OnStartProcessingHitTests(XrTime predicted_display_time) override; + raw_ptr<OpenXrPlaneManagerAndroid> plane_manager_; const raw_ref<const OpenXrExtensionHelper> extension_helper_; XrSession session_; + XrTime predicted_display_time_ = 0; XrSpace mojo_space_; };
diff --git a/device/vr/openxr/android/openxr_plane_manager_android.cc b/device/vr/openxr/android/openxr_plane_manager_android.cc index 060adb7c..f68a180 100644 --- a/device/vr/openxr/android/openxr_plane_manager_android.cc +++ b/device/vr/openxr/android/openxr_plane_manager_android.cc
@@ -34,9 +34,4 @@ } } -void OpenXrPlaneManagerAndroid::OnFrameUpdate(XrTime predicted_display_time, - XrSpace mojo_space) { - predicted_display_time_ = predicted_display_time; -} - } // namespace device
diff --git a/device/vr/openxr/android/openxr_plane_manager_android.h b/device/vr/openxr/android/openxr_plane_manager_android.h index 320d375..a97d1c2 100644 --- a/device/vr/openxr/android/openxr_plane_manager_android.h +++ b/device/vr/openxr/android/openxr_plane_manager_android.h
@@ -14,22 +14,20 @@ class OpenXrExtensionHelper; +// A simple manager for handling planes on Android. Note that due to the way +// the trackables system works, this is really just a thin wrapper around the +// plane_tracker. class OpenXrPlaneManagerAndroid : public OpenXrPlaneManager { public: OpenXrPlaneManagerAndroid(const OpenXrExtensionHelper& extension_helper, XrSession session); ~OpenXrPlaneManagerAndroid() override; - void OnFrameUpdate(XrTime predicted_display_time, - XrSpace mojo_space) override; - XrTrackableTrackerANDROID plane_tracker() const { return plane_tracker_; } - XrTime predicted_display_time() const { return predicted_display_time_; } private: const raw_ref<const OpenXrExtensionHelper> extension_helper_; XrSession session_; - XrTime predicted_display_time_ = 0; XrTrackableTrackerANDROID plane_tracker_ = XR_NULL_HANDLE; };
diff --git a/device/vr/openxr/android/openxr_scene_understanding_manager_android.cc b/device/vr/openxr/android/openxr_scene_understanding_manager_android.cc index d8559a84..07368072 100644 --- a/device/vr/openxr/android/openxr_scene_understanding_manager_android.cc +++ b/device/vr/openxr/android/openxr_scene_understanding_manager_android.cc
@@ -44,13 +44,6 @@ return hit_test_manager_.get(); } -void OpenXRSceneUnderstandingManagerAndroid::OnFrameUpdate( - XrTime predicted_display_time) { - if (plane_manager_) { - plane_manager_->OnFrameUpdate(predicted_display_time, mojo_space_); - } -} - OpenXrSceneUnderstandingManagerAndroidFactory:: OpenXrSceneUnderstandingManagerAndroidFactory() = default; OpenXrSceneUnderstandingManagerAndroidFactory::
diff --git a/device/vr/openxr/android/openxr_scene_understanding_manager_android.h b/device/vr/openxr/android/openxr_scene_understanding_manager_android.h index 341b62bd..b5b8826b 100644 --- a/device/vr/openxr/android/openxr_scene_understanding_manager_android.h +++ b/device/vr/openxr/android/openxr_scene_understanding_manager_android.h
@@ -29,10 +29,7 @@ // OpenXRSceneUnderstandingManager OpenXrPlaneManager* GetPlaneManager() override; OpenXrHitTestManager* GetHitTestManager() override; - private: - void OnFrameUpdate(XrTime predicted_display_time) override; - const raw_ref<const OpenXrExtensionHelper> extension_helper_; XrSpace mojo_space_;
diff --git a/device/vr/openxr/msft/openxr_hit_test_manager_msft.cc b/device/vr/openxr/msft/openxr_hit_test_manager_msft.cc index 2a50beb9..ff5b210 100644 --- a/device/vr/openxr/msft/openxr_hit_test_manager_msft.cc +++ b/device/vr/openxr/msft/openxr_hit_test_manager_msft.cc
@@ -15,8 +15,9 @@ namespace device { OpenXrHitTestManagerMsft::OpenXrHitTestManagerMsft( - OpenXrPlaneManagerMsft* plane_manager) - : plane_manager_(plane_manager) {} + OpenXrPlaneManagerMsft* plane_manager, + XrSpace mojo_space) + : plane_manager_(plane_manager), mojo_space_(mojo_space) {} OpenXrHitTestManagerMsft::~OpenXrHitTestManagerMsft() = default; @@ -123,6 +124,13 @@ return hit_results; } +void OpenXrHitTestManagerMsft::OnStartProcessingHitTests( + XrTime predicted_display_time) { + // Ensure that the PlaneManager is updated so that we can get the latest + // state for hit tests. + plane_manager_->EnsureFrameUpdated(predicted_display_time, mojo_space_); +} + bool OpenXrHitTestManagerMsft::OnNewHitTestSubscription() { plane_manager_->Start(); return true;
diff --git a/device/vr/openxr/msft/openxr_hit_test_manager_msft.h b/device/vr/openxr/msft/openxr_hit_test_manager_msft.h index f17b4cdc..153d319 100644 --- a/device/vr/openxr/msft/openxr_hit_test_manager_msft.h +++ b/device/vr/openxr/msft/openxr_hit_test_manager_msft.h
@@ -16,7 +16,8 @@ class OpenXrHitTestManagerMsft : public OpenXrHitTestManager { public: - explicit OpenXrHitTestManagerMsft(OpenXrPlaneManagerMsft* plane_manager); + explicit OpenXrHitTestManagerMsft(OpenXrPlaneManagerMsft* plane_manager, + XrSpace mojo_space); ~OpenXrHitTestManagerMsft() override; std::vector<mojom::XRHitResultPtr> RequestHitTest( @@ -26,11 +27,13 @@ protected: bool OnNewHitTestSubscription() override; void OnAllHitTestSubscriptionsRemoved() override; + void OnStartProcessingHitTests(XrTime predicted_display_time) override; std::optional<float> GetRayPlaneDistance(const gfx::Point3F& ray_origin, const gfx::Vector3dF& ray_vector, const gfx::Point3F& plane_origin, const gfx::Vector3dF& plane_normal); raw_ptr<OpenXrPlaneManagerMsft> plane_manager_; + XrSpace mojo_space_; }; } // namespace device
diff --git a/device/vr/openxr/msft/openxr_plane_manager_msft.cc b/device/vr/openxr/msft/openxr_plane_manager_msft.cc index b32965b4..8172f29 100644 --- a/device/vr/openxr/msft/openxr_plane_manager_msft.cc +++ b/device/vr/openxr/msft/openxr_plane_manager_msft.cc
@@ -58,8 +58,13 @@ scene_compute_state_ = SceneComputeState::kOff; } -void OpenXrPlaneManagerMsft::OnFrameUpdate(XrTime predicted_display_time, - XrSpace mojo_space) { +void OpenXrPlaneManagerMsft::EnsureFrameUpdated(XrTime predicted_display_time, + XrSpace mojo_space) { + if (last_predicted_display_time_ == predicted_display_time) { + return; + } + last_predicted_display_time_ = predicted_display_time; + switch (scene_compute_state_) { case SceneComputeState::kOff: // Start/Stop are the only way to start the SceneObserver.
diff --git a/device/vr/openxr/msft/openxr_plane_manager_msft.h b/device/vr/openxr/msft/openxr_plane_manager_msft.h index c9fe879..1d7bf4c 100644 --- a/device/vr/openxr/msft/openxr_plane_manager_msft.h +++ b/device/vr/openxr/msft/openxr_plane_manager_msft.h
@@ -28,8 +28,7 @@ XrSession session); ~OpenXrPlaneManagerMsft() override; - void OnFrameUpdate(XrTime predicted_display_time, - XrSpace mojo_space) override; + void EnsureFrameUpdated(XrTime predicted_display_time, XrSpace mojo_space); // Helper methods to start/stop the plane manager. Note that at present these // are only expected to be called by `OpenXrHitTestManagerMsft`. During a @@ -48,6 +47,7 @@ std::unique_ptr<OpenXrSceneMsft> scene_; OpenXrSceneBoundsMsft scene_bounds_; XrTime next_scene_update_time_{0}; + XrTime last_predicted_display_time_{0}; enum class SceneComputeState { kOff, kIdle, kWaiting }; SceneComputeState scene_compute_state_{SceneComputeState::kOff};
diff --git a/device/vr/openxr/msft/openxr_scene_understanding_manager_msft.cc b/device/vr/openxr/msft/openxr_scene_understanding_manager_msft.cc index 7e7d5a8..5066081e 100644 --- a/device/vr/openxr/msft/openxr_scene_understanding_manager_msft.cc +++ b/device/vr/openxr/msft/openxr_scene_understanding_manager_msft.cc
@@ -29,8 +29,8 @@ mojo_space_(mojo_space) { plane_manager_ = std::make_unique<OpenXrPlaneManagerMsft>(extension_helper, session); - hit_test_manager_ = - std::make_unique<OpenXrHitTestManagerMsft>(plane_manager_.get()); + hit_test_manager_ = std::make_unique<OpenXrHitTestManagerMsft>( + plane_manager_.get(), mojo_space_); } OpenXRSceneUnderstandingManagerMSFT::~OpenXRSceneUnderstandingManagerMSFT() = @@ -44,13 +44,6 @@ return hit_test_manager_.get(); } -void OpenXRSceneUnderstandingManagerMSFT::OnFrameUpdate( - XrTime predicted_display_time) { - if (plane_manager_) { - plane_manager_->OnFrameUpdate(predicted_display_time, mojo_space_); - } -} - OpenXrSceneUnderstandingManagerMsftFactory:: OpenXrSceneUnderstandingManagerMsftFactory() = default; OpenXrSceneUnderstandingManagerMsftFactory::
diff --git a/device/vr/openxr/msft/openxr_scene_understanding_manager_msft.h b/device/vr/openxr/msft/openxr_scene_understanding_manager_msft.h index f02b7b9..0d8c8be1 100644 --- a/device/vr/openxr/msft/openxr_scene_understanding_manager_msft.h +++ b/device/vr/openxr/msft/openxr_scene_understanding_manager_msft.h
@@ -39,8 +39,6 @@ OpenXrHitTestManager* GetHitTestManager() override; private: - void OnFrameUpdate(XrTime predicted_display_time) override; - const raw_ref<const OpenXrExtensionHelper> extension_helper_; XrSpace mojo_space_;
diff --git a/device/vr/openxr/openxr_api_wrapper.cc b/device/vr/openxr/openxr_api_wrapper.cc index e612160..1e28cf4 100644 --- a/device/vr/openxr/openxr_api_wrapper.cc +++ b/device/vr/openxr/openxr_api_wrapper.cc
@@ -438,11 +438,6 @@ return light_estimator_.get(); } -OpenXRSceneUnderstandingManager* -OpenXrApiWrapper::GetSceneUnderstandingManager() { - return scene_understanding_manager_.get(); -} - OpenXrDepthSensor* OpenXrApiWrapper::GetDepthSensor() { return depth_sensor_.get(); }
diff --git a/device/vr/openxr/openxr_api_wrapper.h b/device/vr/openxr/openxr_api_wrapper.h index 9f26062..80d9ddd 100644 --- a/device/vr/openxr/openxr_api_wrapper.h +++ b/device/vr/openxr/openxr_api_wrapper.h
@@ -115,7 +115,6 @@ OpenXrAnchorManager* GetAnchorManager(); OpenXrHitTestManager* GetHitTestManager(); OpenXrLightEstimator* GetLightEstimator(); - OpenXRSceneUnderstandingManager* GetSceneUnderstandingManager(); OpenXrDepthSensor* GetDepthSensor(); void OnContextProviderCreated(
diff --git a/device/vr/openxr/openxr_hit_test_manager.cc b/device/vr/openxr/openxr_hit_test_manager.cc index 4014612..cfec5c91 100644 --- a/device/vr/openxr/openxr_hit_test_manager.cc +++ b/device/vr/openxr/openxr_hit_test_manager.cc
@@ -107,12 +107,14 @@ mojom::XRHitTestSubscriptionResultsDataPtr OpenXrHitTestManager::GetHitTestResults( + XrTime predicted_display_time, const gfx::Transform& mojo_from_viewer, const std::vector<mojom::XRInputSourceStatePtr>& input_state) { TRACE_EVENT2("xr", "GetHitTestResults", "subscription_count", hit_test_subscription_id_to_data_.size(), "transient_subscription_count", hit_test_subscription_id_to_transient_hit_test_data_.size()); + OnStartProcessingHitTests(predicted_display_time); mojom::XRHitTestSubscriptionResultsDataPtr result = mojom::XRHitTestSubscriptionResultsData::New();
diff --git a/device/vr/openxr/openxr_hit_test_manager.h b/device/vr/openxr/openxr_hit_test_manager.h index e563a78a..34dd294 100644 --- a/device/vr/openxr/openxr_hit_test_manager.h +++ b/device/vr/openxr/openxr_hit_test_manager.h
@@ -46,6 +46,7 @@ void UnsubscribeFromHitTest(HitTestSubscriptionId subscription_id); mojom::XRHitTestSubscriptionResultsDataPtr GetHitTestResults( + XrTime predicted_display_time, const gfx::Transform& mojo_from_viewer, const std::vector<mojom::XRInputSourceStatePtr>& input_state); @@ -54,6 +55,7 @@ // new subscription and that it is not actually registered. virtual bool OnNewHitTestSubscription(); virtual void OnAllHitTestSubscriptionsRemoved(); + virtual void OnStartProcessingHitTests(XrTime predicted_display_time) {} // Called to get hit test results in the mojom space from the specified origin // and in the specified direction. Results should be appended to the end of
diff --git a/device/vr/openxr/openxr_plane_manager.h b/device/vr/openxr/openxr_plane_manager.h index 777b9555..c3a1da7 100644 --- a/device/vr/openxr/openxr_plane_manager.h +++ b/device/vr/openxr/openxr_plane_manager.h
@@ -18,9 +18,6 @@ class OpenXrPlaneManager { public: virtual ~OpenXrPlaneManager(); - - virtual void OnFrameUpdate(XrTime predicted_display_time, - XrSpace mojo_space) = 0; }; } // namespace device
diff --git a/device/vr/openxr/openxr_render_loop.cc b/device/vr/openxr/openxr_render_loop.cc index 0c60f54..6e2174a 100644 --- a/device/vr/openxr/openxr_render_loop.cc +++ b/device/vr/openxr/openxr_render_loop.cc
@@ -707,25 +707,18 @@ } } - OpenXRSceneUnderstandingManager* scene_understanding_manager = - openxr_->GetSceneUnderstandingManager(); - + // Get results for hit test subscriptions. OpenXrHitTestManager* hit_test_manager = openxr_->GetHitTestManager(); - - if (scene_understanding_manager && - frame_data->render_info->mojo_from_viewer && + if (hit_test_manager && frame_data->render_info->mojo_from_viewer && frame_data->render_info->mojo_from_viewer->position && frame_data->render_info->mojo_from_viewer->orientation) { - scene_understanding_manager->OnFrameUpdate(frame_time); device::Pose mojo_from_viewer( *frame_data->render_info->mojo_from_viewer->position, *frame_data->render_info->mojo_from_viewer->orientation); - // Get results for hit test subscriptions. - if (hit_test_manager) { - frame_data->hit_test_subscription_results = - hit_test_manager->GetHitTestResults(mojo_from_viewer.ToTransform(), - frame_data->input_state.value()); - } + frame_data->hit_test_subscription_results = + hit_test_manager->GetHitTestResults(frame_time, + mojo_from_viewer.ToTransform(), + frame_data->input_state.value()); } // If we don't have a depth_sensor, depth wasn't enabled.
diff --git a/device/vr/openxr/openxr_scene_understanding_manager.cc b/device/vr/openxr/openxr_scene_understanding_manager.cc index 049ae40..210d5cfe 100644 --- a/device/vr/openxr/openxr_scene_understanding_manager.cc +++ b/device/vr/openxr/openxr_scene_understanding_manager.cc
@@ -11,5 +11,4 @@ OpenXRSceneUnderstandingManager::OpenXRSceneUnderstandingManager() = default; OpenXRSceneUnderstandingManager::~OpenXRSceneUnderstandingManager() = default; - } // namespace device
diff --git a/device/vr/openxr/openxr_scene_understanding_manager.h b/device/vr/openxr/openxr_scene_understanding_manager.h index 9e3410c..3f802776 100644 --- a/device/vr/openxr/openxr_scene_understanding_manager.h +++ b/device/vr/openxr/openxr_scene_understanding_manager.h
@@ -23,7 +23,6 @@ OpenXRSceneUnderstandingManager(); virtual ~OpenXRSceneUnderstandingManager(); - virtual void OnFrameUpdate(XrTime predicted_display_time) = 0; virtual OpenXrPlaneManager* GetPlaneManager() = 0; virtual OpenXrHitTestManager* GetHitTestManager() = 0; };
diff --git a/device/vr/public/mojom/BUILD.gn b/device/vr/public/mojom/BUILD.gn index a969521e..6086d481 100644 --- a/device/vr/public/mojom/BUILD.gn +++ b/device/vr/public/mojom/BUILD.gn
@@ -20,7 +20,7 @@ "//gpu/ipc/common:interfaces", "//mojo/public/mojom/base", "//services/viz/public/mojom:shared_image_format", - "//ui/display/mojom:mojom", + "//ui/display/mojom", "//ui/gfx/geometry/mojom", "//ui/gfx/mojom", ] @@ -94,7 +94,7 @@ ] deps = [ - "//base:base", + "//base", "//skia", "//ui/gfx/geometry", "//ui/gfx/geometry:geometry_skia",
diff --git a/docs/adding_to_third_party.md b/docs/adding_to_third_party.md index e7e3ddd..2b9f0b9 100644 --- a/docs/adding_to_third_party.md +++ b/docs/adding_to_third_party.md
@@ -71,8 +71,10 @@ as existing, will it be easy for another developer to understand which should be used where? Will you commit to consolidating uses in Chromium and remove the alternative libraries? -* Is the library memory safe? If not, is there an alternative library -available that is memory safe and meets Chromium's needs? +* Is the library written in a [memory safe + language](security/rule-of-2.md#unsafe-implementation-languages)? If not, is + there an alternative library available that is memory safe and meets + Chromium's needs? * You will be responsible for [owning the library](#add-owners), which includes updating it for security and stability fixes. For C/C++, this is your responsibility. For [Rust](#rust), minor version updates are regularly
diff --git a/docs/mac/icons.md b/docs/mac/icons.md index df1bab7e..cba765c9 100644 --- a/docs/mac/icons.md +++ b/docs/mac/icons.md
@@ -5,15 +5,31 @@ Mac Chromium stores its app icon and document icon badge in an asset catalog. Unlike iOS Chromium, which compiles an `.xcassets` directory into a `.car` file at build time, Mac Chromium has the `.car` file pre-built and checked in. This -is done because (unlike on iOS) the asset catalog file only holds two icons and -thus isn’t often changing, and because of internal technical constraints in +is done because (unlike on iOS) the asset catalog file only holds three items +and thus isn’t often changing, and because of internal technical constraints in tooling. This may change in the future. -Chromium’s asset catalog has two multi-size image sets: one named `AppIcon` used -for the app icon, and one named `Icon` used for badging documents using the -`UTTypeIcons`/`UTTypeIconBadgeName`/`UTTypeIconText` `Info.plist` keys. The -asset catalog `.xcassets` source, as well as the compiled `.car` result, are -checked into the `//chrome/app/theme` directory. +Chromium’s asset catalog contains three logical items: + +1. A collection of assets (`Icon Image`, `MultiSized Image`, `PackedImage`) + named `AppIcon` used for the app icon on macOS releases prior to macOS 26. +2. A collection of assets (`Color`, `Image`, `IconGroup`, `IconImageStack`, + `Named Gradient`, `Vector`) named `AppIcon` used for the app icon on macOS 26 + and subsequent releases. +3. A collection of assets (`Icon Image`, `MultiSized Image`) named `Icon` + used for badging documents using the + `UTTypeIcons`/`UTTypeIconBadgeName`/`UTTypeIconText` `Info.plist` keys. + +The sources for this catalog are an `.xcassets` directory as well as an `.icon` +Icon Composer document package. These sources, as well as the compiled `.car` +result, are checked into the `//chrome/app/theme` directory. + +The current state of app icons (as of July 2025) is that Chrome compiles a +“split app icon” asset catalog: the `AppIcon` bitmaps used for macOS releases +prior to macOS 26 are those of the old, 2022-era app icon, while the `AppIcon` +vectors used for macOS 26 and subsequent releases are of the new, 2025-era Icon +Composer app icon. As users migrate to macOS 26 and subsequent releases, this +will likely be revisited. ### Compiling the asset catalog @@ -23,8 +39,10 @@ (or the path to whichever `.xcassets` file you want to compile) -The script will put the files resulting from the asset catalog compilation into -the directory containing the `.xcassets` file that was processed. +There is no need to specify the corresponding `.icon` file; its name will be +derived from the name of the `.xcassets` file specified. The script will compile +the two source files, and will put the files resulting from the asset catalog +compilation into the directory containing the source files that were processed. ## `.icns` files
diff --git a/docs/website b/docs/website index 1906b0b..c5d4064 160000 --- a/docs/website +++ b/docs/website
@@ -1 +1 @@ -Subproject commit 1906b0bbaa142dc87acd171137d5a234aeba74c8 +Subproject commit c5d4064bbf9dc71893bfa60db578e8f92995bd57
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn index 14a02c0..77da5497 100644 --- a/extensions/BUILD.gn +++ b/extensions/BUILD.gn
@@ -347,76 +347,4 @@ data_deps = [ "//third_party/angle:includes" ] } - - # TODO(rockot) bug 505926: These should be moved to extensions_browsertests but have - # old dependencies on chrome files. The chrome dependencies should be removed - # and these moved to the extensions_browsertests target. Currently, we solve - # the problem by making this a source set and linking it into - # //chrome/test:browser_tests. - source_set("chrome_extensions_browsertests_sources") { - testonly = true - sources = [ - "browser/api/app_window/app_window_apitest.cc", - "browser/api/bluetooth/bluetooth_apitest.cc", - "browser/api/bluetooth/bluetooth_private_apitest.cc", - "browser/app_window/app_window_browsertest.cc", - ] - - defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] - - # These are the deps from browser_tests minus some internal Chrome ones that - # aren't allowed to be included here and that aren't needed. - deps = [ - "//base", - "//base:i18n", - "//base/test:test_support", - "//build:chromeos_buildflags", - "//chrome/browser", - "//chrome/common/extensions/api", - "//chrome/renderer", - "//chrome/test:test_support", - "//components/autofill/content/browser:risk_proto", - "//components/autofill/content/renderer:test_support", - "//components/captive_portal/core:test_support", - "//components/dom_distiller/content/browser", - "//components/dom_distiller/core:test_support", - "//components/guest_view/browser:test_support", - "//components/javascript_dialogs", - "//components/resources", - "//components/strings", - "//components/sync", - "//components/sync:test_support", - "//components/translate/core/common", - "//crypto:test_support", - "//device/bluetooth:mocks", - "//extensions/browser/api/bluetooth", - "//extensions/common/api", - "//extensions/renderer", - "//google_apis:test_support", - "//media:test_support", - "//net", - "//net:test_support", - "//skia", - "//testing/gmock", - "//testing/gtest", - "//testing/perf", - "//third_party/blink/public:blink", - "//third_party/icu", - "//third_party/leveldatabase", - "//third_party/libaddressinput", - "//third_party/webrtc_overrides:webrtc_component", - "//third_party/widevine/cdm:headers", - "//ui/accessibility:test_support", - "//ui/base:test_support", - "//ui/base/idle:test_support", - "//ui/compositor:test_support", - "//ui/resources", - "//ui/web_dialogs:test_support", - "//v8", - ] - - if (is_chromeos) { - deps += [ "//components/user_manager:test_support" ] - } - } }
diff --git a/extensions/browser/api/declarative_webrequest/webrequest_action.cc b/extensions/browser/api/declarative_webrequest/webrequest_action.cc index b5aa425..e1c09ec 100644 --- a/extensions/browser/api/declarative_webrequest/webrequest_action.cc +++ b/extensions/browser/api/declarative_webrequest/webrequest_action.cc
@@ -10,7 +10,7 @@ #include <utility> #include "base/check_op.h" -#include "base/lazy_instance.h" +#include "base/no_destructor.h" #include "base/notreached.h" #include "base/strings/string_util.h" #include "base/values.h" @@ -424,8 +424,10 @@ } }; -base::LazyInstance<WebRequestActionFactory>::Leaky - g_web_request_action_factory = LAZY_INSTANCE_INITIALIZER; +WebRequestActionFactory& GetWebRequestActionFactory() { + static base::NoDestructor<WebRequestActionFactory> instance; + return *instance; +} } // namespace @@ -490,7 +492,7 @@ json_action.FindString(keys::kInstanceTypeKey); INPUT_FORMAT_VALIDATE(instance_type); - WebRequestActionFactory& factory = g_web_request_action_factory.Get(); + WebRequestActionFactory& factory = GetWebRequestActionFactory(); return factory.factory.Instantiate(*instance_type, json_action, error, bad_message); }
diff --git a/extensions/browser/api/networking_private/BUILD.gn b/extensions/browser/api/networking_private/BUILD.gn index 7482c85..3fd97a2 100644 --- a/extensions/browser/api/networking_private/BUILD.gn +++ b/extensions/browser/api/networking_private/BUILD.gn
@@ -49,7 +49,10 @@ "networking_private_linux.h", ] - deps += [ "//dbus" ] + deps += [ + "//components/dbus", + "//dbus", + ] } else if (is_win || is_mac || is_fuchsia) { sources = default_sources + [ "networking_private_event_router_nonchromeos.cc",
diff --git a/extensions/browser/api/networking_private/DEPS b/extensions/browser/api/networking_private/DEPS index 5e6c563..b40b023 100644 --- a/extensions/browser/api/networking_private/DEPS +++ b/extensions/browser/api/networking_private/DEPS
@@ -1,6 +1,7 @@ include_rules = [ "+chrome/browser/ash/crosapi", "+components/account_id", + "+components/dbus", "+components/onc", "+components/proxy_config", "+components/user_manager",
diff --git a/extensions/browser/api/networking_private/networking_private_linux.cc b/extensions/browser/api/networking_private/networking_private_linux.cc index 7d3f4098..946d546 100644 --- a/extensions/browser/api/networking_private/networking_private_linux.cc +++ b/extensions/browser/api/networking_private/networking_private_linux.cc
@@ -7,18 +7,23 @@ #include <stddef.h> #include <memory> +#include <optional> #include <string> #include <utility> +#include "base/barrier_closure.h" #include "base/containers/span.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/functional/callback_helpers.h" +#include "base/location.h" +#include "base/logging.h" #include "base/memory/scoped_refptr.h" #include "base/observer_list.h" #include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" +#include "components/dbus/thread_linux/dbus_thread_linux.h" #include "components/onc/onc_constants.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -115,80 +120,97 @@ // Fires the appropriate callback when the network connect operation succeeds // or fails. void OnNetworkConnectOperationCompleted( - std::unique_ptr<std::string> error, NetworkingPrivateDelegate::VoidCallback success_callback, - NetworkingPrivateDelegate::FailureCallback failure_callback) { - if (!error->empty()) { - std::move(failure_callback).Run(*error); + NetworkingPrivateDelegate::FailureCallback failure_callback, + std::string error) { + if (!error.empty()) { + std::move(failure_callback).Run(error); return; } std::move(success_callback).Run(); } -// Fires the appropriate callback when the network properties are returned -// from the |dbus_thread_|. -void GetCachedNetworkPropertiesCallback( - std::unique_ptr<std::string> error, - std::unique_ptr<base::Value::Dict> properties, - NetworkingPrivateDelegate::DictionaryCallback success_callback, - NetworkingPrivateDelegate::FailureCallback failure_callback) { - if (!error->empty()) { - std::move(failure_callback).Run(*error); - return; - } - std::move(success_callback).Run(std::move(*properties)); -} - -// Fires the appropriate callback when the network properties are returned -// from the |dbus_thread_|. -void GetCachedNetworkPropertiesResultCallback( - std::unique_ptr<std::string> error, - std::unique_ptr<base::Value::Dict> properties, - NetworkingPrivateDelegate::PropertiesCallback callback) { - if (!error->empty()) { - LOG(ERROR) << "GetCachedNetworkProperties failed: " << *error; - std::move(callback).Run(std::nullopt, *error); - return; - } - std::move(callback).Run(std::move(*properties), std::nullopt); -} - } // namespace -NetworkingPrivateLinux::NetworkingPrivateLinux() - : dbus_thread_("Networking Private DBus"), network_manager_proxy_(nullptr) { - base::Thread::Options thread_options(base::MessagePumpType::IO, 0); +class NetworkingPrivateLinux::GetAllWiFiAccessPointsState + : public base::RefCounted<GetAllWiFiAccessPointsState> { + public: + explicit GetAllWiFiAccessPointsState( + base::OnceCallback<void(std::unique_ptr<NetworkMap>)> final_callback) + : callback(std::move(final_callback)), + network_map(std::make_unique<NetworkMap>()) {} - dbus_thread_.StartWithOptions(std::move(thread_options)); - dbus_thread_.task_runner()->PostTask( - FROM_HERE, base::BindOnce(&NetworkingPrivateLinux::Initialize, - base::Unretained(this))); -} + base::OnceCallback<void(std::unique_ptr<NetworkMap>)> callback; + std::unique_ptr<NetworkMap> network_map; -NetworkingPrivateLinux::~NetworkingPrivateLinux() { - if (dbus_) { - // dbus_thread_.Stop() below will wait for this task. - dbus_thread_.task_runner()->PostTask( - FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, dbus_)); + private: + friend class base::RefCounted<GetAllWiFiAccessPointsState>; + + ~GetAllWiFiAccessPointsState() { + if (callback) { + std::move(callback).Run(std::move(network_map)); + } } - dbus_thread_.Stop(); -} +}; -void NetworkingPrivateLinux::AssertOnDBusThread() { - DCHECK(dbus_task_runner_->RunsTasksInCurrentSequence()); -} +struct NetworkingPrivateLinux::GetAccessPointInfoState { + GetAccessPointInfoState( + const dbus::ObjectPath& access_point_path, + const dbus::ObjectPath& device_path, + const dbus::ObjectPath& connected_access_point_path, + base::OnceCallback<void(std::optional<base::Value::Dict>)> final_callback, + dbus::ObjectProxy* proxy) + : access_point_path(access_point_path), + device_path(device_path), + connected_access_point_path(connected_access_point_path), + callback(std::move(final_callback)), + access_point_proxy(proxy) {} -void NetworkingPrivateLinux::Initialize() { - dbus_task_runner_ = dbus_thread_.task_runner(); - // This has to be called after the task runner is initialized. - AssertOnDBusThread(); + ~GetAccessPointInfoState() { + if (callback) { + if (failed) { + std::move(callback).Run(std::nullopt); + } else { + std::move(callback).Run(std::move(access_point_info)); + } + } + } - dbus::Bus::Options dbus_options; - dbus_options.bus_type = dbus::Bus::SYSTEM; - dbus_options.connection_type = dbus::Bus::PRIVATE; - dbus_options.dbus_task_runner = dbus_task_runner_; + const dbus::ObjectPath access_point_path; + const dbus::ObjectPath device_path; + const dbus::ObjectPath connected_access_point_path; + base::OnceCallback<void(std::optional<base::Value::Dict>)> callback; + raw_ptr<dbus::ObjectProxy> access_point_proxy; + base::Value::Dict access_point_info; + uint32_t wpa_security_flags = 0; + bool failed = false; +}; - dbus_ = base::MakeRefCounted<dbus::Bus>(dbus_options); +class NetworkingPrivateLinux::GetConnectedAccessPointState + : public base::RefCounted<GetConnectedAccessPointState> { + public: + GetConnectedAccessPointState( + const dbus::ObjectPath& device_path, + base::OnceCallback<void(dbus::ObjectPath)> final_callback) + : device_path(device_path), callback(std::move(final_callback)) {} + + const dbus::ObjectPath device_path; + base::OnceCallback<void(dbus::ObjectPath)> callback; + std::vector<dbus::ObjectPath> connection_paths; + + private: + friend class base::RefCounted<GetConnectedAccessPointState>; + + ~GetConnectedAccessPointState() { + if (callback) { + std::move(callback).Run(dbus::ObjectPath()); + } + } +}; + +NetworkingPrivateLinux::NetworkingPrivateLinux() { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + dbus_ = dbus_thread_linux::GetSharedSystemBus(); network_manager_proxy_ = dbus_->GetObjectProxy( networking_private::kNetworkManagerNamespace, dbus::ObjectPath(networking_private::kNetworkManagerPath)); @@ -196,16 +218,18 @@ if (!network_manager_proxy_) { LOG(ERROR) << "Platform does not support NetworkManager over DBUS"; } - - network_map_ = std::make_unique<NetworkMap>(); } +NetworkingPrivateLinux::~NetworkingPrivateLinux() = default; + bool NetworkingPrivateLinux::CheckNetworkManagerSupported() { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); return network_manager_proxy_ != nullptr; } void NetworkingPrivateLinux::GetProperties(const std::string& guid, PropertiesCallback callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!network_manager_proxy_) { LOG(WARNING) << "NetworkManager over DBus is not supported"; std::move(callback).Run(std::nullopt, @@ -213,27 +237,22 @@ return; } - auto error = std::make_unique<std::string>(); - auto network_properties = std::make_unique<base::Value::Dict>(); + std::string error; + base::Value::Dict network_properties; - // Runs GetCachedNetworkProperties() on |dbus_thread|. We can safely pass the - // internal raw pointers since it is guaranteed to outlive - // GetCachedNetworkProperties() because ownership is given to the callback. - std::string* error_ptr = error.get(); - base::Value::Dict* network_properties_ptr = network_properties.get(); - dbus_thread_.task_runner()->PostTaskAndReply( - FROM_HERE, - base::BindOnce(&NetworkingPrivateLinux::GetCachedNetworkProperties, - base::Unretained(this), guid, - base::Unretained(network_properties_ptr), - base::Unretained(error_ptr)), - base::BindOnce(&GetCachedNetworkPropertiesResultCallback, - std::move(error), std::move(network_properties), - std::move(callback))); + GetCachedNetworkProperties(guid, &network_properties, &error); + + if (!error.empty()) { + LOG(ERROR) << "GetCachedNetworkProperties failed: " << error; + std::move(callback).Run(std::nullopt, error); + return; + } + std::move(callback).Run(std::move(network_properties), std::nullopt); } void NetworkingPrivateLinux::GetManagedProperties(const std::string& guid, PropertiesCallback callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); LOG(WARNING) << "GetManagedProperties is not supported"; std::move(callback).Run(std::nullopt, extensions::networking_private::kErrorNotSupported); @@ -242,35 +261,28 @@ void NetworkingPrivateLinux::GetState(const std::string& guid, DictionaryCallback success_callback, FailureCallback failure_callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!CheckNetworkManagerSupported()) { ReportNotSupported("GetState", std::move(failure_callback)); return; } - auto error = std::make_unique<std::string>(); - auto network_properties = std::make_unique<base::Value::Dict>(); + std::string error; + base::Value::Dict network_properties; + GetCachedNetworkProperties(guid, &network_properties, &error); - // Runs GetCachedNetworkProperties() on |dbus_thread|. We can safely pass the - // internal raw pointers since it is guaranteed to outlive - // GetCachedNetworkProperties() because ownership is given to the callback. - std::string* error_ptr = error.get(); - base::Value::Dict* network_properties_ptr = network_properties.get(); - dbus_thread_.task_runner()->PostTaskAndReply( - FROM_HERE, - base::BindOnce(&NetworkingPrivateLinux::GetCachedNetworkProperties, - base::Unretained(this), guid, - base::Unretained(network_properties_ptr), - base::Unretained(error_ptr)), - base::BindOnce(&GetCachedNetworkPropertiesCallback, std::move(error), - std::move(network_properties), std::move(success_callback), - std::move(failure_callback))); + if (!error.empty()) { + std::move(failure_callback).Run(error); + return; + } + std::move(success_callback).Run(std::move(network_properties)); } void NetworkingPrivateLinux::GetCachedNetworkProperties( const std::string& guid, base::Value::Dict* properties, std::string* error) { - AssertOnDBusThread(); + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); std::string ssid; if (!GuidToSsid(guid, &ssid)) { @@ -279,8 +291,8 @@ } NetworkMap::const_iterator network_iter = - network_map_->find(base::UTF8ToUTF16(ssid)); - if (network_iter == network_map_->end()) { + network_map_.find(base::UTF8ToUTF16(ssid)); + if (network_iter == network_map_.end()) { *error = "Unknown network GUID"; return; } @@ -293,6 +305,7 @@ bool allow_set_shared_config, VoidCallback success_callback, FailureCallback failure_callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); ReportNotSupported("SetProperties", std::move(failure_callback)); } @@ -300,6 +313,7 @@ base::Value::Dict properties, StringCallback success_callback, FailureCallback failure_callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); ReportNotSupported("CreateNetwork", std::move(failure_callback)); } @@ -307,6 +321,7 @@ bool allow_forget_shared_config, VoidCallback success_callback, FailureCallback failure_callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // TODO(zentaro): Implement for Linux. ReportNotSupported("ForgetNetwork", std::move(failure_callback)); } @@ -317,13 +332,12 @@ int limit, NetworkListCallback success_callback, FailureCallback failure_callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!CheckNetworkManagerSupported()) { ReportNotSupported("GetNetworks", std::move(failure_callback)); return; } - auto network_map = std::make_unique<NetworkMap>(); - if (!(network_type == ::onc::network_type::kWiFi || network_type == ::onc::network_type::kWireless || network_type == ::onc::network_type::kAllTypes)) { @@ -333,38 +347,28 @@ return; } - // Runs GetAllWiFiAccessPoints on the dbus_thread and returns the + // Runs GetAllWiFiAccessPoints and returns the // results back to OnAccessPointsFound where the callback is fired. - NetworkMap* network_map_ptr = network_map.get(); - dbus_thread_.task_runner()->PostTaskAndReply( - FROM_HERE, - base::BindOnce(&NetworkingPrivateLinux::GetAllWiFiAccessPoints, - base::Unretained(this), configured_only, visible_only, - limit, base::Unretained(network_map_ptr)), + GetAllWiFiAccessPoints( + configured_only, visible_only, limit, base::BindOnce(&NetworkingPrivateLinux::OnAccessPointsFound, - base::Unretained(this), std::move(network_map), + weak_ptr_factory_.GetWeakPtr(), std::move(success_callback), std::move(failure_callback))); } bool NetworkingPrivateLinux::GetNetworksForScanRequest() { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!network_manager_proxy_) { return false; } - auto network_map = std::make_unique<NetworkMap>(); - - // Runs GetAllWiFiAccessPoints on the dbus_thread and returns the + // Runs GetAllWiFiAccessPoints and returns the // results back to SendNetworkListChangedEvent to fire the event. No // callbacks are used in this case. - NetworkMap* network_map_ptr = network_map.get(); - dbus_thread_.task_runner()->PostTaskAndReply( - FROM_HERE, - base::BindOnce(&NetworkingPrivateLinux::GetAllWiFiAccessPoints, - base::Unretained(this), false /* configured_only */, - false /* visible_only */, 0 /* limit */, - base::Unretained(network_map_ptr)), + GetAllWiFiAccessPoints( + false /* configured_only */, false /* visible_only */, 0 /* limit */, base::BindOnce(&NetworkingPrivateLinux::OnAccessPointsFoundViaScan, - base::Unretained(this), std::move(network_map))); + weak_ptr_factory_.GetWeakPtr())); return true; } @@ -376,9 +380,10 @@ // 'ssid': 'FooNetwork' // } // } -void NetworkingPrivateLinux::ConnectToNetwork(const std::string& guid, - std::string* error) { - AssertOnDBusThread(); +void NetworkingPrivateLinux::ConnectToNetwork( + const std::string& guid, + base::OnceCallback<void(std::string)> callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); std::string device_path_str; std::string access_point_path_str; std::string ssid; @@ -386,14 +391,14 @@ if (!ParseNetworkGuid(guid, &device_path_str, &access_point_path_str, &ssid)) { - *error = "Invalid Network GUID format"; + std::move(callback).Run("Invalid Network GUID format"); return; } // Set the connection state to connecting in the map. if (!SetConnectionStateAndPostEvent(guid, ssid, ::onc::connection_state::kConnecting)) { - *error = "Unknown network GUID"; + std::move(callback).Run("Unknown network GUID"); return; } @@ -438,56 +443,65 @@ builder.AppendObjectPath(device_path); builder.AppendObjectPath(access_point_path); - std::unique_ptr<dbus::Response> response( - network_manager_proxy_ - ->CallMethodAndBlock(&method_call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) - .value_or(nullptr)); + network_manager_proxy_->CallMethod( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::BindOnce(&NetworkingPrivateLinux::OnConnectToNetworkResponse, + weak_ptr_factory_.GetWeakPtr(), guid, ssid, + std::move(callback))); +} + +void NetworkingPrivateLinux::OnConnectToNetworkResponse( + const std::string& guid, + const std::string& ssid, + base::OnceCallback<void(std::string)> callback, + dbus::Response* response) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!response) { LOG(ERROR) << "Failed to add a new connection"; - *error = "Failed to connect."; // Set the connection state to NotConnected in the map. SetConnectionStateAndPostEvent(guid, ssid, ::onc::connection_state::kNotConnected); + std::move(callback).Run("Failed to connect."); return; } - dbus::MessageReader reader(response.get()); + dbus::MessageReader reader(response); dbus::ObjectPath connection_settings_path; dbus::ObjectPath active_connection_path; if (!reader.PopObjectPath(&connection_settings_path)) { LOG(ERROR) << "Unexpected response for add connection path " << ": " << response->ToString(); - *error = "Failed to connect."; // Set the connection state to NotConnected in the map. SetConnectionStateAndPostEvent(guid, ssid, ::onc::connection_state::kNotConnected); + std::move(callback).Run("Failed to connect."); return; } if (!reader.PopObjectPath(&active_connection_path)) { LOG(ERROR) << "Unexpected response for connection path " << ": " << response->ToString(); - *error = "Failed to connect."; // Set the connection state to NotConnected in the map. SetConnectionStateAndPostEvent(guid, ssid, ::onc::connection_state::kNotConnected); + std::move(callback).Run("Failed to connect."); return; } // Set the connection state to Connected in the map. SetConnectionStateAndPostEvent(guid, ssid, ::onc::connection_state::kConnected); - return; + std::move(callback).Run(""); } -void NetworkingPrivateLinux::DisconnectFromNetwork(const std::string& guid, - std::string* error) { - AssertOnDBusThread(); +void NetworkingPrivateLinux::DisconnectFromNetwork( + const std::string& guid, + base::OnceCallback<void(std::string)> callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); std::string device_path_str; std::string access_point_path_str; std::string ssid; @@ -495,18 +509,15 @@ if (!ParseNetworkGuid(guid, &device_path_str, &access_point_path_str, &ssid)) { - *error = "Invalid Network GUID format"; + std::move(callback).Run("Invalid Network GUID format"); return; } - auto network_map = std::make_unique<NetworkMap>(); - GetAllWiFiAccessPoints(false /* configured_only */, false /* visible_only */, - 0 /* limit */, network_map.get()); - NetworkMap::const_iterator network_iter = - network_map->find(base::UTF8ToUTF16(ssid)); - if (network_iter == network_map->end()) { + network_map_.find(base::UTF8ToUTF16(ssid)); + if (network_iter == network_map_.end()) { // This network doesn't exist so there's nothing to do. + std::move(callback).Run(""); return; } @@ -514,6 +525,7 @@ *network_iter->second.FindString(kAccessPointInfoConnectionState); if (connection_state == ::onc::connection_state::kNotConnected) { // Already disconnected so nothing to do. + std::move(callback).Run(""); return; } @@ -524,56 +536,55 @@ dbus::MethodCall method_call( networking_private::kNetworkManagerDeviceNamespace, networking_private::kNetworkManagerDisconnectMethod); - std::unique_ptr<dbus::Response> response( - device_proxy - ->CallMethodAndBlock(&method_call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) - .value_or(nullptr)); + device_proxy->CallMethod( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::BindOnce(&NetworkingPrivateLinux::OnDisconnectResponse, + weak_ptr_factory_.GetWeakPtr(), guid, ssid, + std::move(callback))); +} +void NetworkingPrivateLinux::OnDisconnectResponse( + const std::string& guid, + const std::string& ssid, + base::OnceCallback<void(std::string)> callback, + dbus::Response* response) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!response) { - LOG(WARNING) << "Failed to disconnect network on device " - << device_path_str; - *error = "Failed to disconnect network"; + LOG(WARNING) << "Failed to disconnect network on device " << guid; + std::move(callback).Run("Failed to disconnect network"); + } else { + SetConnectionStateAndPostEvent(guid, ssid, + ::onc::connection_state::kNotConnected); + std::move(callback).Run(""); } } void NetworkingPrivateLinux::StartConnect(const std::string& guid, VoidCallback success_callback, FailureCallback failure_callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!CheckNetworkManagerSupported()) { ReportNotSupported("StartConnect", std::move(failure_callback)); return; } - std::unique_ptr<std::string> error(new std::string); - - // Runs ConnectToNetwork on |dbus_thread|. - std::string* error_ptr = error.get(); - dbus_thread_.task_runner()->PostTaskAndReply( - FROM_HERE, - base::BindOnce(&NetworkingPrivateLinux::ConnectToNetwork, - base::Unretained(this), guid, base::Unretained(error_ptr)), - base::BindOnce(&OnNetworkConnectOperationCompleted, std::move(error), - std::move(success_callback), std::move(failure_callback))); + ConnectToNetwork(guid, base::BindOnce(&OnNetworkConnectOperationCompleted, + std::move(success_callback), + std::move(failure_callback))); } void NetworkingPrivateLinux::StartDisconnect(const std::string& guid, VoidCallback success_callback, FailureCallback failure_callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!CheckNetworkManagerSupported()) { ReportNotSupported("StartDisconnect", std::move(failure_callback)); return; } - std::unique_ptr<std::string> error(new std::string); - - // Runs DisconnectFromNetwork on |dbus_thread|. - std::string* error_ptr = error.get(); - dbus_thread_.task_runner()->PostTaskAndReply( - FROM_HERE, - base::BindOnce(&NetworkingPrivateLinux::DisconnectFromNetwork, - base::Unretained(this), guid, base::Unretained(error_ptr)), - base::BindOnce(&OnNetworkConnectOperationCompleted, std::move(error), + DisconnectFromNetwork( + guid, + base::BindOnce(&OnNetworkConnectOperationCompleted, std::move(success_callback), std::move(failure_callback))); } @@ -581,6 +592,7 @@ const std::string& guid, StringCallback success_callback, FailureCallback failure_callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); ReportNotSupported("GetCaptivePortalStatus", std::move(failure_callback)); } @@ -590,6 +602,7 @@ const std::string& puk, VoidCallback success_callback, FailureCallback failure_callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); ReportNotSupported("UnlockCellularSim", std::move(failure_callback)); } @@ -600,6 +613,7 @@ const std::string& new_pin, VoidCallback success_callback, FailureCallback failure_callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); ReportNotSupported("SetCellularSimState", std::move(failure_callback)); } @@ -608,12 +622,14 @@ const std::string& network_id, VoidCallback success_callback, FailureCallback failure_callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); ReportNotSupported("SelectCellularMobileNetwork", std::move(failure_callback)); } void NetworkingPrivateLinux::GetEnabledNetworkTypes( EnabledNetworkTypesCallback callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); base::Value::List network_list; network_list.Append(::onc::network_type::kWiFi); std::move(callback).Run(std::move(network_list)); @@ -621,6 +637,7 @@ void NetworkingPrivateLinux::GetDeviceStateList( DeviceStateListCallback callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DeviceStateList device_state_list; api::networking_private::DeviceStateProperties& properties = device_state_list.emplace_back(); @@ -630,61 +647,78 @@ } void NetworkingPrivateLinux::GetGlobalPolicy(GetGlobalPolicyCallback callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); std::move(callback).Run(base::Value::Dict()); } void NetworkingPrivateLinux ::GetCertificateLists( GetCertificateListsCallback callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); std::move(callback).Run(base::Value::Dict()); } void NetworkingPrivateLinux::EnableNetworkType(const std::string& type, BoolCallback callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); std::move(callback).Run(false); } void NetworkingPrivateLinux::DisableNetworkType(const std::string& type, BoolCallback callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); std::move(callback).Run(false); } void NetworkingPrivateLinux::RequestScan(const std::string& /* type */, BoolCallback callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); std::move(callback).Run(GetNetworksForScanRequest()); } void NetworkingPrivateLinux::AddObserver( NetworkingPrivateDelegateObserver* observer) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); network_events_observers_.AddObserver(observer); } void NetworkingPrivateLinux::RemoveObserver( NetworkingPrivateDelegateObserver* observer) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); network_events_observers_.RemoveObserver(observer); } void NetworkingPrivateLinux::OnAccessPointsFound( - std::unique_ptr<NetworkMap> network_map, NetworkListCallback success_callback, - FailureCallback failure_callback) { + FailureCallback failure_callback, + std::unique_ptr<NetworkMap> network_map) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!network_map) { + std::move(failure_callback).Run("Failed to get network list."); + return; + } base::Value::List network_list = CopyNetworkMapToList(*network_map); // Give ownership to the member variable. - network_map_.swap(network_map); + network_map_.swap(*network_map); SendNetworkListChangedEvent(network_list); std::move(success_callback).Run(std::move(network_list)); } void NetworkingPrivateLinux::OnAccessPointsFoundViaScan( std::unique_ptr<NetworkMap> network_map) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!network_map) { + return; + } base::Value::List network_list = CopyNetworkMapToList(*network_map); // Give ownership to the member variable. - network_map_.swap(network_map); + network_map_.swap(*network_map); SendNetworkListChangedEvent(network_list); } void NetworkingPrivateLinux::SendNetworkListChangedEvent( const base::Value::List& network_list) { - GuidList guidsForEventCallback; + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + GuidList guids_for_event_callback; for (const auto& network : network_list) { if (!network.is_dict()) { @@ -692,42 +726,54 @@ } if (const std::string* guid = network.GetDict().FindString(kAccessPointInfoGuid)) { - guidsForEventCallback.push_back(*guid); + guids_for_event_callback.push_back(*guid); } } - OnNetworkListChangedEventOnUIThread(guidsForEventCallback); + for (auto& observer : network_events_observers_) { + observer.OnNetworkListChangedEvent(guids_for_event_callback); + } } -bool NetworkingPrivateLinux::GetNetworkDevices( - std::vector<dbus::ObjectPath>* device_paths) { - AssertOnDBusThread(); +void NetworkingPrivateLinux::GetNetworkDevices( + base::OnceCallback<void(std::optional<std::vector<dbus::ObjectPath>>)> + callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); dbus::MethodCall method_call( networking_private::kNetworkManagerNamespace, networking_private::kNetworkManagerGetDevicesMethod); - std::unique_ptr<dbus::Response> device_response( - network_manager_proxy_ - ->CallMethodAndBlock(&method_call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) - .value_or(nullptr)); - - if (!device_response) { - return false; - } - - dbus::MessageReader reader(device_response.get()); - if (!reader.PopArrayOfObjectPaths(device_paths)) { - LOG(WARNING) << "Unexpected response: " << device_response->ToString(); - return false; - } - - return true; + network_manager_proxy_->CallMethod( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::BindOnce(&NetworkingPrivateLinux::OnGetNetworkDevicesResponse, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } -NetworkingPrivateLinux::DeviceType NetworkingPrivateLinux::GetDeviceType( - const dbus::ObjectPath& device_path) { - AssertOnDBusThread(); +void NetworkingPrivateLinux::OnGetNetworkDevicesResponse( + base::OnceCallback<void(std::optional<std::vector<dbus::ObjectPath>>)> + callback, + dbus::Response* response) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!response) { + std::move(callback).Run(std::nullopt); + return; + } + + std::vector<dbus::ObjectPath> device_paths; + dbus::MessageReader reader(response); + if (!reader.PopArrayOfObjectPaths(&device_paths)) { + LOG(WARNING) << "Unexpected response: " << response->ToString(); + std::move(callback).Run(std::nullopt); + return; + } + + std::move(callback).Run(std::move(device_paths)); +} + +void NetworkingPrivateLinux::GetDeviceType( + const dbus::ObjectPath& device_path, + base::OnceCallback<void(std::optional<DeviceType>)> callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); dbus::ObjectProxy* device_proxy = dbus_->GetObjectProxy( networking_private::kNetworkManagerNamespace, device_path); dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES, @@ -736,261 +782,356 @@ builder.AppendString(networking_private::kNetworkManagerDeviceNamespace); builder.AppendString(networking_private::kNetworkManagerDeviceType); - std::unique_ptr<dbus::Response> response( - device_proxy - ->CallMethodAndBlock(&method_call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) - .value_or(nullptr)); - - if (!response) { - LOG(ERROR) << "Failed to get the device type for device " - << device_path.value(); - return NetworkingPrivateLinux::NM_DEVICE_TYPE_UNKNOWN; - } - - dbus::MessageReader reader(response.get()); - uint32_t device_type = 0; - if (!reader.PopVariantOfUint32(&device_type)) { - LOG(ERROR) << "Unexpected response for device " << device_type << ": " - << response->ToString(); - return NM_DEVICE_TYPE_UNKNOWN; - } - - return static_cast<NetworkingPrivateLinux::DeviceType>(device_type); + device_proxy->CallMethod( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::BindOnce(&NetworkingPrivateLinux::OnGetDeviceTypeResponse, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } -void NetworkingPrivateLinux::GetAllWiFiAccessPoints(bool configured_only, - bool visible_only, - int limit, - NetworkMap* network_map) { - AssertOnDBusThread(); +void NetworkingPrivateLinux::OnGetDeviceTypeResponse( + base::OnceCallback<void(std::optional<DeviceType>)> callback, + dbus::Response* response) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!response) { + LOG(ERROR) << "Failed to get the device type for device"; + std::move(callback).Run(NM_DEVICE_TYPE_UNKNOWN); + return; + } + + dbus::MessageReader reader(response); + uint32_t device_type = 0; + if (!reader.PopVariantOfUint32(&device_type)) { + LOG(ERROR) << "Unexpected response for device type: " + << response->ToString(); + std::move(callback).Run(NM_DEVICE_TYPE_UNKNOWN); + return; + } + + std::move(callback).Run(static_cast<DeviceType>(device_type)); +} + +void NetworkingPrivateLinux::GetAllWiFiAccessPoints( + bool configured_only, + bool visible_only, + int limit, + base::OnceCallback<void(std::unique_ptr<NetworkMap>)> callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // TODO(zentaro): The filters are not implemented and are ignored. - std::vector<dbus::ObjectPath> device_paths; - if (!GetNetworkDevices(&device_paths)) { + auto state = + base::MakeRefCounted<GetAllWiFiAccessPointsState>(std::move(callback)); + + GetNetworkDevices(base::BindOnce( + &NetworkingPrivateLinux::OnGetNetworkDevicesForGetAllWiFiAccessPoints, + weak_ptr_factory_.GetWeakPtr(), std::move(state))); +} + +void NetworkingPrivateLinux::OnGetNetworkDevicesForGetAllWiFiAccessPoints( + scoped_refptr<GetAllWiFiAccessPointsState> state, + std::optional<std::vector<dbus::ObjectPath>> device_paths) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!device_paths) { LOG(ERROR) << "Failed to enumerate network devices"; return; } - for (const auto& device_path : device_paths) { - NetworkingPrivateLinux::DeviceType device_type = GetDeviceType(device_path); + if (device_paths->empty()) { + return; + } - // Get the access points for each WiFi adapter. Other network types are - // ignored. - if (device_type != NetworkingPrivateLinux::NM_DEVICE_TYPE_WIFI) { - continue; - } - - // Found a wlan adapter - if (!AddAccessPointsFromDevice(device_path, network_map)) { - // Ignore devices we can't enumerate. - LOG(WARNING) << "Failed to add access points from device " - << device_path.value(); - } + for (const auto& device_path : *device_paths) { + GetDeviceType( + device_path, + base::BindOnce( + &NetworkingPrivateLinux::OnGetDeviceTypeForGetAllWiFiAccessPoints, + weak_ptr_factory_.GetWeakPtr(), state, device_path)); } } -std::unique_ptr<dbus::Response> NetworkingPrivateLinux::GetAccessPointProperty( +void NetworkingPrivateLinux::OnGetDeviceTypeForGetAllWiFiAccessPoints( + scoped_refptr<GetAllWiFiAccessPointsState> state, + const dbus::ObjectPath& device_path, + std::optional<DeviceType> device_type) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!device_type.has_value() || + *device_type != NetworkingPrivateLinux::NM_DEVICE_TYPE_WIFI) { + return; + } + + // Found a wlan adapter + AddAccessPointsFromDevice( + device_path, state, + base::BindOnce( + &NetworkingPrivateLinux::OnAddAccessPointsFromDeviceFinished, + weak_ptr_factory_.GetWeakPtr(), state)); +} + +void NetworkingPrivateLinux::OnAddAccessPointsFromDeviceFinished( + scoped_refptr<GetAllWiFiAccessPointsState> state, + bool success) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!success) { + // Ignore devices we can't enumerate. + LOG(WARNING) << "Failed to add access points from a device."; + } +} + +void NetworkingPrivateLinux::GetAccessPointProperty( dbus::ObjectProxy* access_point_proxy, - const std::string& property_name) { - AssertOnDBusThread(); + const std::string& property_name, + base::OnceCallback<void(dbus::Response*)> callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES, networking_private::kNetworkManagerGetMethod); dbus::MessageWriter builder(&method_call); builder.AppendString(networking_private::kNetworkManagerAccessPointNamespace); builder.AppendString(property_name); - std::unique_ptr<dbus::Response> response = - access_point_proxy - ->CallMethodAndBlock(&method_call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) - .value_or(nullptr); - if (!response) { - LOG(ERROR) << "Failed to get property for " << property_name; - } - return response; + access_point_proxy->CallMethod(&method_call, + dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + std::move(callback)); } -bool NetworkingPrivateLinux::GetAccessPointInfo( +void NetworkingPrivateLinux::GetAccessPointInfo( const dbus::ObjectPath& access_point_path, - base::Value::Dict* access_point_info) { - AssertOnDBusThread(); + const dbus::ObjectPath& device_path, + const dbus::ObjectPath& connected_access_point_path, + base::OnceCallback<void(std::optional<base::Value::Dict>)> callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); dbus::ObjectProxy* access_point_proxy = dbus_->GetObjectProxy( networking_private::kNetworkManagerNamespace, access_point_path); - // Read the SSID. The GUID is derived from the Ssid. - { - std::unique_ptr<dbus::Response> response(GetAccessPointProperty( - access_point_proxy, networking_private::kNetworkManagerSsidProperty)); + auto state = std::make_unique<GetAccessPointInfoState>( + access_point_path, device_path, connected_access_point_path, + std::move(callback), access_point_proxy); - if (!response) { - return false; - } + GetAccessPointProperty( + access_point_proxy, networking_private::kNetworkManagerSsidProperty, + base::BindOnce(&NetworkingPrivateLinux::OnGetSsidForAccessPointInfo, + weak_ptr_factory_.GetWeakPtr(), std::move(state))); +} - // The response should contain a variant that contains an array of bytes. - dbus::MessageReader reader(response.get()); - dbus::MessageReader variant_reader(response.get()); - if (!reader.PopVariant(&variant_reader)) { - LOG(ERROR) << "Unexpected response for " << access_point_path.value() - << ": " << response->ToString(); - return false; - } - - std::string ssidUTF8; - if (!variant_reader.PopString(&ssidUTF8)) { - LOG(ERROR) << "Unexpected response for " << access_point_path.value() - << ": " << response->ToString(); - return false; - } - std::u16string ssid = base::UTF8ToUTF16(ssidUTF8); - access_point_info->Set(kAccessPointInfoName, ssid); +void NetworkingPrivateLinux::OnGetSsidForAccessPointInfo( + std::unique_ptr<GetAccessPointInfoState> state, + dbus::Response* response) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!response) { + state->failed = true; + return; } - // Read signal strength. - { - std::unique_ptr<dbus::Response> response(GetAccessPointProperty( - access_point_proxy, - networking_private::kNetworkManagerStrengthProperty)); - if (!response) { - return false; - } - - dbus::MessageReader reader(response.get()); - uint8_t strength = 0; - if (!reader.PopVariantOfByte(&strength)) { - LOG(ERROR) << "Unexpected response for " << access_point_path.value() - << ": " << response->ToString(); - return false; - } - - access_point_info->SetByDottedPath(kAccessPointInfoWifiSignalStrengthDotted, - strength); + // The response should contain a variant that contains an array of bytes. + dbus::MessageReader reader(response); + dbus::MessageReader variant_reader(response); + if (!reader.PopVariant(&variant_reader)) { + LOG(ERROR) << "Unexpected response for " << state->access_point_path.value() + << ": " << response->ToString(); + state->failed = true; + return; } - // Read the security type. This is from the WpaFlags and RsnFlags property - // which are of the same type and can be OR'd together to find all supported - // security modes. + std::string ssidUTF8; + if (!variant_reader.PopString(&ssidUTF8)) { + LOG(ERROR) << "Unexpected response for " << state->access_point_path.value() + << ": " << response->ToString(); + state->failed = true; + return; + } + state->access_point_info.Set(kAccessPointInfoName, + base::UTF8ToUTF16(ssidUTF8)); + std::string network_guid = ConstructNetworkGuid( + state->device_path, state->access_point_path, ssidUTF8); + state->access_point_info.Set(kAccessPointInfoGuid, network_guid); - uint32_t wpa_security_flags = 0; - { - std::unique_ptr<dbus::Response> response(GetAccessPointProperty( - access_point_proxy, - networking_private::kNetworkManagerWpaFlagsProperty)); - if (!response) { - return false; - } + auto* access_point_proxy = state->access_point_proxy.get(); + GetAccessPointProperty( + access_point_proxy, networking_private::kNetworkManagerStrengthProperty, + base::BindOnce(&NetworkingPrivateLinux::OnGetStrengthForAccessPointInfo, + weak_ptr_factory_.GetWeakPtr(), std::move(state))); +} - dbus::MessageReader reader(response.get()); - - if (!reader.PopVariantOfUint32(&wpa_security_flags)) { - LOG(ERROR) << "Unexpected response for " << access_point_path.value() - << ": " << response->ToString(); - return false; - } +void NetworkingPrivateLinux::OnGetStrengthForAccessPointInfo( + std::unique_ptr<GetAccessPointInfoState> state, + dbus::Response* response) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!response) { + state->failed = true; + return; } + dbus::MessageReader reader(response); + uint8_t strength = 0; + if (!reader.PopVariantOfByte(&strength)) { + LOG(ERROR) << "Unexpected response for " << state->access_point_path.value() + << ": " << response->ToString(); + state->failed = true; + return; + } + state->access_point_info.SetByDottedPath( + kAccessPointInfoWifiSignalStrengthDotted, strength); + + auto* access_point_proxy = state->access_point_proxy.get(); + GetAccessPointProperty( + access_point_proxy, networking_private::kNetworkManagerWpaFlagsProperty, + base::BindOnce(&NetworkingPrivateLinux::OnGetWpaFlagsForAccessPointInfo, + weak_ptr_factory_.GetWeakPtr(), std::move(state))); +} + +void NetworkingPrivateLinux::OnGetWpaFlagsForAccessPointInfo( + std::unique_ptr<GetAccessPointInfoState> state, + dbus::Response* response) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!response) { + state->failed = true; + return; + } + + dbus::MessageReader reader(response); + if (!reader.PopVariantOfUint32(&state->wpa_security_flags)) { + LOG(ERROR) << "Unexpected response for " << state->access_point_path.value() + << ": " << response->ToString(); + state->failed = true; + return; + } + + auto* access_point_proxy = state->access_point_proxy.get(); + GetAccessPointProperty( + access_point_proxy, networking_private::kNetworkManagerRsnFlagsProperty, + base::BindOnce(&NetworkingPrivateLinux::OnGetRsnFlagsForAccessPointInfo, + weak_ptr_factory_.GetWeakPtr(), std::move(state))); +} + +void NetworkingPrivateLinux::OnGetRsnFlagsForAccessPointInfo( + std::unique_ptr<GetAccessPointInfoState> state, + dbus::Response* response) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!response) { + state->failed = true; + return; + } + + dbus::MessageReader reader(response); uint32_t rsn_security_flags = 0; - { - std::unique_ptr<dbus::Response> response(GetAccessPointProperty( - access_point_proxy, - networking_private::kNetworkManagerRsnFlagsProperty)); - if (!response) { - return false; - } - - dbus::MessageReader reader(response.get()); - - if (!reader.PopVariantOfUint32(&rsn_security_flags)) { - LOG(ERROR) << "Unexpected response for " << access_point_path.value() - << ": " << response->ToString(); - return false; - } + if (!reader.PopVariantOfUint32(&rsn_security_flags)) { + LOG(ERROR) << "Unexpected response for " << state->access_point_path.value() + << ": " << response->ToString(); + state->failed = true; + return; } std::string security; - MapSecurityFlagsToString(rsn_security_flags | wpa_security_flags, &security); - access_point_info->SetByDottedPath(kAccessPointInfoWifiSecurityDotted, - security); - access_point_info->Set(kAccessPointInfoType, kAccessPointInfoTypeWifi); - access_point_info->Set(kAccessPointInfoConnectable, true); - return true; + MapSecurityFlagsToString(rsn_security_flags | state->wpa_security_flags, + &security); + state->access_point_info.SetByDottedPath(kAccessPointInfoWifiSecurityDotted, + security); + state->access_point_info.Set(kAccessPointInfoType, kAccessPointInfoTypeWifi); + state->access_point_info.Set(kAccessPointInfoConnectable, true); + std::string connection_state = + (state->access_point_path == state->connected_access_point_path) + ? ::onc::connection_state::kConnected + : ::onc::connection_state::kNotConnected; + state->access_point_info.Set(kAccessPointInfoConnectionState, + connection_state); } -bool NetworkingPrivateLinux::AddAccessPointsFromDevice( +void NetworkingPrivateLinux::AddAccessPointsFromDevice( const dbus::ObjectPath& device_path, - NetworkMap* network_map) { - AssertOnDBusThread(); - dbus::ObjectPath connected_access_point; - if (!GetConnectedAccessPoint(device_path, &connected_access_point)) { - return false; - } + scoped_refptr<GetAllWiFiAccessPointsState> state, + base::OnceCallback<void(bool)> callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + GetConnectedAccessPoint( + device_path, + base::BindOnce( + &NetworkingPrivateLinux::OnGetConnectedAccessPointForAddAccessPoints, + weak_ptr_factory_.GetWeakPtr(), device_path, state, + std::move(callback))); +} +void NetworkingPrivateLinux::OnGetConnectedAccessPointForAddAccessPoints( + const dbus::ObjectPath& device_path, + scoped_refptr<GetAllWiFiAccessPointsState> state, + base::OnceCallback<void(bool)> callback, + dbus::ObjectPath connected_access_point) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); dbus::ObjectProxy* device_proxy = dbus_->GetObjectProxy( networking_private::kNetworkManagerNamespace, device_path); dbus::MethodCall method_call( networking_private::kNetworkManagerWirelessDeviceNamespace, networking_private::kNetworkManagerGetAccessPointsMethod); - std::unique_ptr<dbus::Response> response( - device_proxy - ->CallMethodAndBlock(&method_call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) - .value_or(nullptr)); + device_proxy->CallMethod( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::BindOnce(&NetworkingPrivateLinux::OnGetAccessPointsForDevice, + weak_ptr_factory_.GetWeakPtr(), device_path, state, + std::move(callback), std::move(connected_access_point))); +} +void NetworkingPrivateLinux::OnGetAccessPointsForDevice( + const dbus::ObjectPath& device_path, + scoped_refptr<GetAllWiFiAccessPointsState> state, + base::OnceCallback<void(bool)> callback, + dbus::ObjectPath connected_access_point, + dbus::Response* response) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!response) { LOG(WARNING) << "Failed to get access points data for " << device_path.value(); - return false; + std::move(callback).Run(false); + return; } - dbus::MessageReader reader(response.get()); + dbus::MessageReader reader(response); std::vector<dbus::ObjectPath> access_point_paths; if (!reader.PopArrayOfObjectPaths(&access_point_paths)) { LOG(ERROR) << "Unexpected response for " << device_path.value() << ": " << response->ToString(); - return false; + std::move(callback).Run(false); + return; } + if (access_point_paths.empty()) { + std::move(callback).Run(true); + return; + } + + base::RepeatingClosure barrier = base::BarrierClosure( + access_point_paths.size(), base::BindOnce(std::move(callback), true)); + for (const auto& access_point_path : access_point_paths) { - base::Value::Dict access_point; - - if (GetAccessPointInfo(access_point_path, &access_point)) { - std::string connection_state = - (access_point_path == connected_access_point) - ? ::onc::connection_state::kConnected - : ::onc::connection_state::kNotConnected; - - access_point.Set(kAccessPointInfoConnectionState, connection_state); - std::string ssid = *access_point.FindString(kAccessPointInfoName); - - std::string network_guid = - ConstructNetworkGuid(device_path, access_point_path, ssid); - - // Adds the network to the map. Since each SSID can actually have multiple - // access point paths, this consolidates them. If it is already - // in the map it updates the signal strength and GUID paths if this - // network is stronger or the one that is connected. - AddOrUpdateAccessPoint(network_map, network_guid, &access_point); - } + GetAccessPointInfo( + access_point_path, device_path, connected_access_point, + base::BindOnce(&NetworkingPrivateLinux::OnGetAccessPointInfo, + weak_ptr_factory_.GetWeakPtr(), state, barrier)); } +} - return true; +void NetworkingPrivateLinux::OnGetAccessPointInfo( + scoped_refptr<GetAllWiFiAccessPointsState> state, + base::RepeatingClosure finished_callback, + std::optional<base::Value::Dict> access_point_info) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (access_point_info) { + AddOrUpdateAccessPoint(state->network_map.get(), + std::move(*access_point_info)); + } + finished_callback.Run(); } void NetworkingPrivateLinux::AddOrUpdateAccessPoint( NetworkMap* network_map, - const std::string& network_guid, - base::Value::Dict* access_point) { + base::Value::Dict access_point) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + CHECK(access_point.FindString(kAccessPointInfoGuid)); std::string connection_state = - *access_point->FindString(kAccessPointInfoConnectionState); - int signal_strength = *access_point->FindIntByDottedPath( - kAccessPointInfoWifiSignalStrengthDotted); + *access_point.FindString(kAccessPointInfoConnectionState); + int signal_strength = + access_point.FindIntByDottedPath(kAccessPointInfoWifiSignalStrengthDotted) + .value_or(0); std::u16string ssid = - base::UTF8ToUTF16(*access_point->FindString(kAccessPointInfoName)); - access_point->Set(kAccessPointInfoGuid, network_guid); + base::UTF8ToUTF16(*access_point.FindString(kAccessPointInfoName)); auto existing_access_point_iter = network_map->find(ssid); if (existing_access_point_iter == network_map->end()) { // Unseen access point. Add it to the map. - network_map->insert(NetworkMap::value_type(ssid, std::move(*access_point))); + network_map->insert(NetworkMap::value_type(ssid, std::move(access_point))); } else { // Already seen access point. Update the record if this is the connected // record or if the signal strength is higher. But don't override a weaker @@ -1012,13 +1153,15 @@ connection_state); existing_access_point.SetByDottedPath( kAccessPointInfoWifiSignalStrengthDotted, signal_strength); - existing_access_point.Set(kAccessPointInfoGuid, network_guid); + existing_access_point.Set(kAccessPointInfoGuid, + *access_point.FindString(kAccessPointInfoGuid)); } } } void NetworkingPrivateLinux::MapSecurityFlagsToString(uint32_t security_flags, std::string* security) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // Valid values are None, WEP-PSK, WEP-8021X, WPA-PSK, WPA-EAP if (security_flags == NetworkingPrivateLinux::NM_802_11_AP_SEC_NONE) { *security = kAccessPointSecurityNone; @@ -1036,67 +1179,95 @@ DVLOG(1) << "Network security setting " << *security; } -bool NetworkingPrivateLinux::GetConnectedAccessPoint( +void NetworkingPrivateLinux::GetConnectedAccessPoint( const dbus::ObjectPath& device_path, - dbus::ObjectPath* access_point_path) { - AssertOnDBusThread(); + base::OnceCallback<void(dbus::ObjectPath)> callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + OnGetActiveConnections(base::MakeRefCounted<GetConnectedAccessPointState>( + device_path, std::move(callback))); +} + +void NetworkingPrivateLinux::OnGetActiveConnections( + scoped_refptr<GetConnectedAccessPointState> state) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES, networking_private::kNetworkManagerGetMethod); dbus::MessageWriter builder(&method_call); builder.AppendString(networking_private::kNetworkManagerNamespace); builder.AppendString(networking_private::kNetworkManagerActiveConnections); - std::unique_ptr<dbus::Response> response( - network_manager_proxy_ - ->CallMethodAndBlock(&method_call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) - .value_or(nullptr)); - - if (!response) { - LOG(WARNING) << "Failed to get a list of active connections"; - return false; - } - - dbus::MessageReader reader(response.get()); - dbus::MessageReader variant_reader(response.get()); - if (!reader.PopVariant(&variant_reader)) { - LOG(ERROR) << "Unexpected response: " << response->ToString(); - return false; - } - - std::vector<dbus::ObjectPath> connection_paths; - if (!variant_reader.PopArrayOfObjectPaths(&connection_paths)) { - LOG(ERROR) << "Unexpected response: " << response->ToString(); - return false; - } - - for (const auto& connection_path : connection_paths) { - dbus::ObjectPath connections_device_path; - if (!GetDeviceOfConnection(connection_path, &connections_device_path)) { - return false; - } - - if (connections_device_path == device_path) { - if (!GetAccessPointForConnection(connection_path, access_point_path)) { - return false; - } - - break; - } - } - - return true; + network_manager_proxy_->CallMethod( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::BindOnce(&NetworkingPrivateLinux::OnGetActiveConnectionsResponse, + weak_ptr_factory_.GetWeakPtr(), std::move(state))); } -bool NetworkingPrivateLinux::GetDeviceOfConnection( +void NetworkingPrivateLinux::OnGetActiveConnectionsResponse( + scoped_refptr<GetConnectedAccessPointState> state, + dbus::Response* response) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!response) { + LOG(WARNING) << "Failed to get a list of active connections"; + return; + } + + dbus::MessageReader reader(response); + dbus::MessageReader variant_reader(response); + if (!reader.PopVariant(&variant_reader)) { + LOG(ERROR) << "Unexpected response: " << response->ToString(); + return; + } + + if (!variant_reader.PopArrayOfObjectPaths(&state->connection_paths)) { + LOG(ERROR) << "Unexpected response: " << response->ToString(); + return; + } + + if (state->connection_paths.empty()) { + return; + } + + for (const auto& connection_path : state->connection_paths) { + GetDeviceOfConnection( + connection_path, + base::BindOnce(&NetworkingPrivateLinux::OnGetDeviceOfConnection, + weak_ptr_factory_.GetWeakPtr(), state, connection_path)); + } +} + +void NetworkingPrivateLinux::OnGetDeviceOfConnection( + scoped_refptr<GetConnectedAccessPointState> state, + const dbus::ObjectPath& connection_path, + dbus::ObjectPath device_path) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (device_path == state->device_path) { + GetAccessPointForConnection( + connection_path, + base::BindOnce(&NetworkingPrivateLinux::OnGetAccessPointForConnection, + weak_ptr_factory_.GetWeakPtr(), state)); + return; + } +} + +void NetworkingPrivateLinux::OnGetAccessPointForConnection( + scoped_refptr<GetConnectedAccessPointState> state, + dbus::ObjectPath access_point_path) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!access_point_path.value().empty() && state->callback) { + std::move(state->callback).Run(access_point_path); + } +} + +void NetworkingPrivateLinux::GetDeviceOfConnection( dbus::ObjectPath connection_path, - dbus::ObjectPath* device_path) { - AssertOnDBusThread(); + base::OnceCallback<void(dbus::ObjectPath)> callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); dbus::ObjectProxy* connection_proxy = dbus_->GetObjectProxy( networking_private::kNetworkManagerNamespace, connection_path); if (!connection_proxy) { - return false; + std::move(callback).Run(dbus::ObjectPath()); + return; } dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES, @@ -1106,48 +1277,54 @@ networking_private::kNetworkManagerActiveConnectionNamespace); builder.AppendString("Devices"); - std::unique_ptr<dbus::Response> response( - connection_proxy - ->CallMethodAndBlock(&method_call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) - .value_or(nullptr)); + connection_proxy->CallMethod( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::BindOnce(&NetworkingPrivateLinux::OnGetDeviceOfConnectionResponse, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); +} +void NetworkingPrivateLinux::OnGetDeviceOfConnectionResponse( + base::OnceCallback<void(dbus::ObjectPath)> callback, + dbus::Response* response) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!response) { LOG(ERROR) << "Failed to get devices"; - return false; + std::move(callback).Run(dbus::ObjectPath()); + return; } - dbus::MessageReader reader(response.get()); - dbus::MessageReader variant_reader(response.get()); + dbus::MessageReader reader(response); + dbus::MessageReader variant_reader(response); if (!reader.PopVariant(&variant_reader)) { LOG(ERROR) << "Unexpected response: " << response->ToString(); - return false; + std::move(callback).Run(dbus::ObjectPath()); + return; } std::vector<dbus::ObjectPath> device_paths; if (!variant_reader.PopArrayOfObjectPaths(&device_paths)) { LOG(ERROR) << "Unexpected response: " << response->ToString(); - return false; + std::move(callback).Run(dbus::ObjectPath()); + return; } if (device_paths.size() == 1) { - *device_path = device_paths[0]; - - return true; + std::move(callback).Run(device_paths[0]); + } else { + std::move(callback).Run(dbus::ObjectPath()); } - - return false; } -bool NetworkingPrivateLinux::GetAccessPointForConnection( +void NetworkingPrivateLinux::GetAccessPointForConnection( dbus::ObjectPath connection_path, - dbus::ObjectPath* access_point_path) { - AssertOnDBusThread(); + base::OnceCallback<void(dbus::ObjectPath)> callback) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); dbus::ObjectProxy* connection_proxy = dbus_->GetObjectProxy( networking_private::kNetworkManagerNamespace, connection_path); if (!connection_proxy) { - return false; + std::move(callback).Run(dbus::ObjectPath()); + return; } dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES, @@ -1157,40 +1334,48 @@ networking_private::kNetworkManagerActiveConnectionNamespace); builder.AppendString(networking_private::kNetworkManagerSpecificObject); - std::unique_ptr<dbus::Response> response( - connection_proxy - ->CallMethodAndBlock(&method_call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT) - .value_or(nullptr)); + connection_proxy->CallMethod( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::BindOnce( + &NetworkingPrivateLinux::OnGetAccessPointForConnectionResponse, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); +} +void NetworkingPrivateLinux::OnGetAccessPointForConnectionResponse( + base::OnceCallback<void(dbus::ObjectPath)> callback, + dbus::Response* response) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (!response) { LOG(WARNING) << "Failed to get access point from active connection"; - return false; + std::move(callback).Run(dbus::ObjectPath()); + return; } - dbus::MessageReader reader(response.get()); - dbus::MessageReader variant_reader(response.get()); + dbus::MessageReader reader(response); + dbus::MessageReader variant_reader(response); if (!reader.PopVariant(&variant_reader)) { LOG(ERROR) << "Unexpected response: " << response->ToString(); - return false; + std::move(callback).Run(dbus::ObjectPath()); + return; } - if (!variant_reader.PopObjectPath(access_point_path)) { + dbus::ObjectPath access_point_path; + if (!variant_reader.PopObjectPath(&access_point_path)) { LOG(ERROR) << "Unexpected response: " << response->ToString(); - return false; + std::move(callback).Run(dbus::ObjectPath()); + return; } - return true; + std::move(callback).Run(access_point_path); } bool NetworkingPrivateLinux::SetConnectionStateAndPostEvent( const std::string& guid, const std::string& ssid, const std::string& connection_state) { - AssertOnDBusThread(); - - auto network_iter = network_map_->find(base::UTF8ToUTF16(ssid)); - if (network_iter == network_map_->end()) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + auto network_iter = network_map_.find(base::UTF8ToUTF16(ssid)); + if (network_iter == network_map_.end()) { return false; } @@ -1202,7 +1387,7 @@ // changed event. std::string* connected_network_guid = nullptr; if (connection_state == ::onc::connection_state::kConnected) { - for (auto& network : *network_map_) { + for (auto& network : network_map_) { if (std::string* other_connection_state = network.second.FindString(kAccessPointInfoConnectionState)) { if (*other_connection_state == ::onc::connection_state::kConnected) { @@ -1228,40 +1413,10 @@ changed_networks->push_back(*connected_network_guid); } - PostOnNetworksChangedToUIThread(std::move(changed_networks)); + for (auto& observer : network_events_observers_) { + observer.OnNetworksChangedEvent(*changed_networks); + } return true; } -void NetworkingPrivateLinux::OnNetworksChangedEventOnUIThread( - const GuidList& network_guids) { - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - for (auto& observer : network_events_observers_) { - observer.OnNetworksChangedEvent(network_guids); - } -} - -void NetworkingPrivateLinux::OnNetworkListChangedEventOnUIThread( - const GuidList& network_guids) { - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - for (auto& observer : network_events_observers_) { - observer.OnNetworkListChangedEvent(network_guids); - } -} - -void NetworkingPrivateLinux::PostOnNetworksChangedToUIThread( - std::unique_ptr<GuidList> guid_list) { - AssertOnDBusThread(); - - content::GetUIThreadTaskRunner({})->PostTask( - FROM_HERE, - base::BindOnce(&NetworkingPrivateLinux::OnNetworksChangedEventTask, - base::Unretained(this), std::move(guid_list))); -} - -void NetworkingPrivateLinux::OnNetworksChangedEventTask( - std::unique_ptr<GuidList> guid_list) { - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - OnNetworksChangedEventOnUIThread(*guid_list); -} - } // namespace extensions
diff --git a/extensions/browser/api/networking_private/networking_private_linux.h b/extensions/browser/api/networking_private/networking_private_linux.h index 792442d..4e56b399 100644 --- a/extensions/browser/api/networking_private/networking_private_linux.h +++ b/extensions/browser/api/networking_private/networking_private_linux.h
@@ -7,13 +7,15 @@ #include <stdint.h> +#include <map> +#include <memory> #include <string> #include <vector> #include "base/memory/raw_ptr.h" +#include "base/memory/scoped_refptr.h" +#include "base/memory/weak_ptr.h" #include "base/observer_list.h" -#include "base/task/sequenced_task_runner.h" -#include "base/threading/thread.h" #include "base/values.h" #include "extensions/browser/api/networking_private/networking_private_delegate.h" @@ -29,10 +31,9 @@ // Linux NetworkingPrivateDelegate implementation. class NetworkingPrivateLinux : public NetworkingPrivateDelegate { public: + using GuidList = std::vector<std::string>; using NetworkMap = std::map<std::u16string, base::Value::Dict>; - typedef std::vector<std::string> GuidList; - NetworkingPrivateLinux(); ~NetworkingPrivateLinux() override; @@ -103,7 +104,6 @@ void RemoveObserver(NetworkingPrivateDelegateObserver* observer) override; private: - // https://developer.gnome.org/NetworkManager/unstable/spec.html#type-NM_DEVICE_TYPE enum DeviceType { NM_DEVICE_TYPE_UNKNOWN = 0, @@ -126,17 +126,27 @@ NM_802_11_AP_SEC_KEY_MGMT_802_1X = 0x200 }; - // Initializes the DBus instance and the proxy object to the network manager. - // Must be called on |dbus_thread_|. - void Initialize(); + struct GetAccessPointInfoState; + class GetAllWiFiAccessPointsState; + class GetConnectedAccessPointState; // Enumerates all WiFi adapters and scans for access points on each. // Results are appended into the provided |network_map|. - // Must be called on |dbus_thread_|. - void GetAllWiFiAccessPoints(bool configured_only, - bool visible_only, - int limit, - NetworkMap* network_map); + void GetAllWiFiAccessPoints( + bool configured_only, + bool visible_only, + int limit, + base::OnceCallback<void(std::unique_ptr<NetworkMap>)> callback); + void OnGetNetworkDevicesForGetAllWiFiAccessPoints( + scoped_refptr<GetAllWiFiAccessPointsState> state, + std::optional<std::vector<dbus::ObjectPath>> device_paths); + void OnGetDeviceTypeForGetAllWiFiAccessPoints( + scoped_refptr<GetAllWiFiAccessPointsState> state, + const dbus::ObjectPath& device_path, + std::optional<DeviceType> device_type); + void OnAddAccessPointsFromDeviceFinished( + scoped_refptr<GetAllWiFiAccessPointsState> state, + bool success); // Helper function for handling a scan request. This function acts similarly // to the public GetNetworks to get visible networks and fire the @@ -144,16 +154,21 @@ bool GetNetworksForScanRequest(); // Initiates the connection to the network. - // Must be called on |dbus_thread_|. - void ConnectToNetwork(const std::string& guid, std::string* error); + void ConnectToNetwork(const std::string& guid, + base::OnceCallback<void(std::string)> callback); + void OnConnectToNetworkResponse( + const std::string& guid, + const std::string& ssid, + base::OnceCallback<void(std::string)> callback, + dbus::Response* response); // Initiates disconnection from the specified network. - // Must be called on |dbus_thread_| - void DisconnectFromNetwork(const std::string& guid, std::string* error); - - // Checks whether the current thread is the DBus thread. If not, DCHECK will - // fail. - void AssertOnDBusThread(); + void DisconnectFromNetwork(const std::string& guid, + base::OnceCallback<void(std::string)> callback); + void OnDisconnectResponse(const std::string& guid, + const std::string& ssid, + base::OnceCallback<void(std::string)> callback, + dbus::Response* response); // Verifies that NetworkManager interfaces are initialized. // Returns true if NetworkManager is initialized, otherwise returns false @@ -161,25 +176,49 @@ bool CheckNetworkManagerSupported(); // Gets all network devices on the system. - // Returns false if there is an error getting the device paths. - bool GetNetworkDevices(std::vector<dbus::ObjectPath>* device_paths); + void GetNetworkDevices( + base::OnceCallback<void(std::optional<std::vector<dbus::ObjectPath>>)> + callback); + void OnGetNetworkDevicesResponse( + base::OnceCallback<void(std::optional<std::vector<dbus::ObjectPath>>)> + callback, + dbus::Response* response); // Returns the DeviceType (eg. WiFi, ethernet). corresponding to the // |device_path|. - DeviceType GetDeviceType(const dbus::ObjectPath& device_path); + void GetDeviceType( + const dbus::ObjectPath& device_path, + base::OnceCallback<void(std::optional<DeviceType>)> callback); + void OnGetDeviceTypeResponse( + base::OnceCallback<void(std::optional<DeviceType>)> callback, + dbus::Response* response); // Helper function to enumerate WiFi networks. Takes a path to a Wireless // device, scans that device and appends networks to network_list. - // Returns false if there is an error getting the access points visible - // to the |device_path|. - bool AddAccessPointsFromDevice(const dbus::ObjectPath& device_path, - NetworkMap* network_map); + void AddAccessPointsFromDevice( + const dbus::ObjectPath& device_path, + scoped_refptr<GetAllWiFiAccessPointsState> state, + base::OnceCallback<void(bool)> callback); + void OnGetConnectedAccessPointForAddAccessPoints( + const dbus::ObjectPath& device_path, + scoped_refptr<GetAllWiFiAccessPointsState> state, + base::OnceCallback<void(bool)> callback, + dbus::ObjectPath connected_access_point); + void OnGetAccessPointsForDevice( + const dbus::ObjectPath& device_path, + scoped_refptr<GetAllWiFiAccessPointsState> state, + base::OnceCallback<void(bool)> callback, + dbus::ObjectPath connected_access_point, + dbus::Response* response); + void OnGetAccessPointInfo(scoped_refptr<GetAllWiFiAccessPointsState> state, + base::RepeatingClosure finished_callback, + std::optional<base::Value::Dict> access_point_info); // Reply callback accepts the map of networks and fires the // OnNetworkListChanged event and user callbacks. - void OnAccessPointsFound(std::unique_ptr<NetworkMap> network_map, - NetworkListCallback success_callback, - FailureCallback failure_callback); + void OnAccessPointsFound(NetworkListCallback success_callback, + FailureCallback failure_callback, + std::unique_ptr<NetworkMap> network_map); // Reply callback accepts the map of networks and fires the // OnNetworkListChanged event. @@ -190,24 +229,37 @@ void SendNetworkListChangedEvent(const base::Value::List& network_list); // Gets a dictionary of information about the access point. - // Returns false if there is an error getting information about the - // supplied |access_point_path|. - bool GetAccessPointInfo(const dbus::ObjectPath& access_point_path, - base::Value::Dict* access_point_info); + void GetAccessPointInfo( + const dbus::ObjectPath& access_point_path, + const dbus::ObjectPath& device_path, + const dbus::ObjectPath& connected_access_point_path, + base::OnceCallback<void(std::optional<base::Value::Dict>)> callback); + void OnGetSsidForAccessPointInfo( + std::unique_ptr<GetAccessPointInfoState> state, + dbus::Response* response); + void OnGetStrengthForAccessPointInfo( + std::unique_ptr<GetAccessPointInfoState> state, + dbus::Response* response); + void OnGetWpaFlagsForAccessPointInfo( + std::unique_ptr<GetAccessPointInfoState> state, + dbus::Response* response); + void OnGetRsnFlagsForAccessPointInfo( + std::unique_ptr<GetAccessPointInfoState> state, + dbus::Response* response); // Helper function to extract a property from a device. // Returns the dbus::Response object from calling Get on the supplied // |property_name|. - std::unique_ptr<dbus::Response> GetAccessPointProperty( + void GetAccessPointProperty( dbus::ObjectProxy* access_point_proxy, - const std::string& property_name); + const std::string& property_name, + base::OnceCallback<void(dbus::Response*)> callback); // If the access_point is not already in the map it is added. Otherwise // the access point is updated (eg. with the max of the signal // strength). void AddOrUpdateAccessPoint(NetworkMap* network_map, - const std::string& network_guid, - base::Value::Dict* access_point); + base::Value::Dict access_point); // Maps the WPA security flags to a human readable string. void MapSecurityFlagsToString(uint32_t securityFlags, std::string* security); @@ -215,22 +267,39 @@ // Gets the connected access point path on the given device. Internally gets // all active connections then checks if the device matches the requested // device, then gets the access point associated with the connection. - // Returns false if there is an error getting the connected access point. - bool GetConnectedAccessPoint(const dbus::ObjectPath& device_path, - dbus::ObjectPath* access_point_path); + void GetConnectedAccessPoint( + const dbus::ObjectPath& device_path, + base::OnceCallback<void(dbus::ObjectPath)> callback); + void OnGetActiveConnections( + scoped_refptr<GetConnectedAccessPointState> state); + void OnGetActiveConnectionsResponse( + scoped_refptr<GetConnectedAccessPointState> state, + dbus::Response* response); + void OnGetDeviceOfConnection( + scoped_refptr<GetConnectedAccessPointState> state, + const dbus::ObjectPath& connection_path, + dbus::ObjectPath device_path); + void OnGetAccessPointForConnection( + scoped_refptr<GetConnectedAccessPointState> state, + dbus::ObjectPath access_point_path); // Given a path to an active connection gets the path to the device - // that the connection belongs to. Returns false if there is an error getting - // the device corresponding to the supplied |connection_path|. - bool GetDeviceOfConnection(dbus::ObjectPath connection_path, - dbus::ObjectPath* device_path); + // that the connection belongs to. + void GetDeviceOfConnection( + dbus::ObjectPath connection_path, + base::OnceCallback<void(dbus::ObjectPath)> callback); + void OnGetDeviceOfConnectionResponse( + base::OnceCallback<void(dbus::ObjectPath)> callback, + dbus::Response* response); // Given a path to an active wireless connection gets the path to the // access point associated with that connection. - // Returns false if there is an error getting the |access_point_path| - // corresponding to the supplied |connection_path|. - bool GetAccessPointForConnection(dbus::ObjectPath connection_path, - dbus::ObjectPath* access_point_path); + void GetAccessPointForConnection( + dbus::ObjectPath connection_path, + base::OnceCallback<void(dbus::ObjectPath)> callback); + void OnGetAccessPointForConnectionResponse( + base::OnceCallback<void(dbus::ObjectPath)> callback, + dbus::Response* response); // Helper method to set the connection state in the |network_map_| and post // a change event. @@ -238,35 +307,21 @@ const std::string& ssid, const std::string& connection_state); - // Helper method to post an OnNetworkChanged event to the UI thread from the - // dbus thread. Used for connection status progress during |StartConnect|. - void PostOnNetworksChangedToUIThread(std::unique_ptr<GuidList> guid_list); - - // Helper method to be called from the UI thread and manage ownership of the - // passed vector from the |dbus_thread_|. - void OnNetworksChangedEventTask(std::unique_ptr<GuidList> guid_list); - void GetCachedNetworkProperties(const std::string& guid, base::Value::Dict* properties, std::string* error); - void OnNetworksChangedEventOnUIThread(const GuidList& network_guids); - - void OnNetworkListChangedEventOnUIThread(const GuidList& network_guids); - - // Thread used for DBus actions. - base::Thread dbus_thread_; - // DBus instance. Only access on |dbus_thread_|. + // DBus instance. scoped_refptr<dbus::Bus> dbus_; - // Task runner used by the |dbus_| object. - scoped_refptr<base::SequencedTaskRunner> dbus_task_runner_; - // This is owned by |dbus_| object. Only access on |dbus_thread_|. - raw_ptr<dbus::ObjectProxy> network_manager_proxy_; - // Holds the current mapping of known networks. Only access on |dbus_thread_|. - std::unique_ptr<NetworkMap> network_map_; + // This is owned by |dbus_| object. + raw_ptr<dbus::ObjectProxy> network_manager_proxy_ = nullptr; + // Holds the current mapping of known networks. + NetworkMap network_map_; // Observers to Network Events. base::ObserverList<NetworkingPrivateDelegateObserver>::Unchecked network_events_observers_; + + base::WeakPtrFactory<NetworkingPrivateLinux> weak_ptr_factory_{this}; }; } // namespace extensions
diff --git a/extensions/browser/api/system_info/system_info_api.cc b/extensions/browser/api/system_info/system_info_api.cc index 10da267..7bf8840 100644 --- a/extensions/browser/api/system_info/system_info_api.cc +++ b/extensions/browser/api/system_info/system_info_api.cc
@@ -14,6 +14,7 @@ #include "base/lazy_instance.h" #include "base/memory/raw_ptr.h" #include "base/memory/singleton.h" +#include "base/no_destructor.h" #include "base/values.h" #include "components/storage_monitor/removable_storage_observer.h" #include "components/storage_monitor/storage_info.h" @@ -100,12 +101,10 @@ contexts_with_storage_listeners_; }; -static base::LazyInstance<SystemInfoEventRouter>::Leaky - g_system_info_event_router = LAZY_INSTANCE_INITIALIZER; - // static SystemInfoEventRouter* SystemInfoEventRouter::GetInstance() { - return g_system_info_event_router.Pointer(); + static base::NoDestructor<SystemInfoEventRouter> instance; + return instance.get(); } SystemInfoEventRouter::SystemInfoEventRouter() = default;
diff --git a/extensions/browser/api/usb/usb_device_manager.cc b/extensions/browser/api/usb/usb_device_manager.cc index 30d0e52e..6342b0e6 100644 --- a/extensions/browser/api/usb/usb_device_manager.cc +++ b/extensions/browser/api/usb/usb_device_manager.cc
@@ -10,7 +10,7 @@ #include <utility> #include "base/containers/contains.h" -#include "base/lazy_instance.h" +#include "base/no_destructor.h" #include "base/observer_list.h" #include "base/strings/utf_string_conversions.h" #include "base/task/sequenced_task_runner.h" @@ -104,9 +104,6 @@ return false; } -base::LazyInstance<BrowserContextKeyedAPIFactory<UsbDeviceManager>>::Leaky - g_event_router_factory = LAZY_INSTANCE_INITIALIZER; - } // namespace // static @@ -118,7 +115,9 @@ // static BrowserContextKeyedAPIFactory<UsbDeviceManager>* UsbDeviceManager::GetFactoryInstance() { - return g_event_router_factory.Pointer(); + static base::NoDestructor<BrowserContextKeyedAPIFactory<UsbDeviceManager>> + instance; + return instance.get(); } void UsbDeviceManager::Observer::OnDeviceAdded(
diff --git a/extensions/browser/api/web_request/form_data_parser.cc b/extensions/browser/api/web_request/form_data_parser.cc index 084d7c8..380719b 100644 --- a/extensions/browser/api/web_request/form_data_parser.cc +++ b/extensions/browser/api/web_request/form_data_parser.cc
@@ -11,8 +11,8 @@ #include "base/check.h" #include "base/containers/to_vector.h" -#include "base/lazy_instance.h" #include "base/memory/raw_ptr.h" +#include "base/no_destructor.h" #include "base/notreached.h" #include "base/strings/escape.h" #include "base/strings/string_util.h" @@ -37,11 +37,11 @@ const char kContentTypeOctetString[] = "Content-Type: application/octet-stream\r\n"; -// A wrapper struct for static RE2 objects to be held as LazyInstance. +// A wrapper struct for static RE2 objects to be held as a singleton. struct Patterns { Patterns(); - // Patterns is only instantiated as a leaky LazyInstance, so the destructor - // is never called. + // Patterns is only instantiated as a NoDestructor static local instance, so + // the destructor is never called. ~Patterns() = delete; const RE2 transfer_padding_pattern; const RE2 closing_pattern; @@ -69,7 +69,10 @@ url_encoded_pattern(std::string("(") + kCharacterPattern + "*)=(" + kCharacterPattern + "*)") {} -base::LazyInstance<Patterns>::Leaky g_patterns = LAZY_INSTANCE_INITIALIZER; +const Patterns* GetPatterns() { + static base::NoDestructor<Patterns> instance; + return instance.get(); +} bool ConsumePrefix(std::string_view* str, std::string_view prefix) { if (!str->starts_with(prefix)) { @@ -100,10 +103,8 @@ private: // Returns the pattern to match a single name-value pair. This could be even // static, but then we would have to spend more code on initializing the - // cached pointer to g_patterns.Get(). - const RE2& pattern() const { - return patterns_->url_encoded_pattern; - } + // cached pointer to `GetPatterns()`. + const RE2& pattern() const { return patterns_->url_encoded_pattern; } // Auxiliary constant for using RE2. Number of arguments for parsing // name-value pairs (one for name, one for value). @@ -120,7 +121,7 @@ const RE2::Arg arg_value_; std::array<const RE2::Arg*, args_size_> args_; - // Caching the pointer to g_patterns.Get(). + // Caching the pointer to `GetPatterns()`. raw_ptr<const Patterns> patterns_; }; @@ -240,7 +241,7 @@ bool FinishReadingPart(std::string_view* data); // These methods could be even static, but then we would have to spend more - // code on initializing the cached pointer to g_patterns.Get(). + // code on initializing the cached pointer to `GetPatterns()`. const RE2& transfer_padding_pattern() const { return patterns_->transfer_padding_pattern; } @@ -279,7 +280,7 @@ // sequentially. std::string_view source_; - // Caching the pointer to g_patterns.Get(). + // Caching the pointer to `GetPatterns()`. raw_ptr<const Patterns> patterns_; }; @@ -356,7 +357,7 @@ source_malformed_(false), arg_name_(&name_), arg_value_(&value_), - patterns_(g_patterns.Pointer()) { + patterns_(GetPatterns()) { args_[0] = &arg_name_; args_[1] = &arg_value_; } @@ -419,7 +420,7 @@ const std::string& boundary_separator) : dash_boundary_separator_("--" + boundary_separator), state_(STATE_INIT), - patterns_(g_patterns.Pointer()) {} + patterns_(GetPatterns()) {} FormDataParserMultipart::~FormDataParserMultipart() = default;
diff --git a/extensions/browser/content_verifier/content_verify_job.cc b/extensions/browser/content_verifier/content_verify_job.cc index e7952cb0..a36da24e 100644 --- a/extensions/browser/content_verifier/content_verify_job.cc +++ b/extensions/browser/content_verifier/content_verify_job.cc
@@ -8,11 +8,11 @@ #include "base/containers/span.h" #include "base/functional/bind.h" -#include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/raw_ptr.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" +#include "base/no_destructor.h" #include "base/task/thread_pool.h" #include "base/timer/elapsed_timer.h" #include "content/public/browser/browser_thread.h" @@ -29,13 +29,10 @@ bool g_ignore_verification_for_tests = false; -base::LazyInstance<scoped_refptr<ContentVerifyJob::TestObserver>>::Leaky - g_content_verify_job_test_observer = LAZY_INSTANCE_INITIALIZER; - -scoped_refptr<ContentVerifyJob::TestObserver> GetTestObserver() { - if (!g_content_verify_job_test_observer.IsCreated()) - return nullptr; - return g_content_verify_job_test_observer.Get(); +scoped_refptr<ContentVerifyJob::TestObserver>& GetTestObserver() { + static base::NoDestructor<scoped_refptr<ContentVerifyJob::TestObserver>> + instance; + return *instance; } class ScopedElapsedTimer { @@ -321,11 +318,10 @@ // static void ContentVerifyJob::SetObserverForTests( scoped_refptr<TestObserver> observer) { - DCHECK(observer == nullptr || - g_content_verify_job_test_observer.Get() == nullptr) + DCHECK(observer == nullptr || GetTestObserver() == nullptr) << "SetObserverForTests does not support interleaving. Observers should " << "be set and then cleared one at a time."; - g_content_verify_job_test_observer.Get() = std::move(observer); + GetTestObserver() = std::move(observer); } void ContentVerifyJob::DispatchFailureCallback(FailureReason reason) {
diff --git a/extensions/browser/extension_api_frame_id_map.cc b/extensions/browser/extension_api_frame_id_map.cc index eab665d..963d4ce3 100644 --- a/extensions/browser/extension_api_frame_id_map.cc +++ b/extensions/browser/extension_api_frame_id_map.cc
@@ -25,15 +25,6 @@ namespace extensions { -namespace { - -// The map is accessed on the IO and UI thread, so construct it once and never -// delete it. -base::LazyInstance<ExtensionApiFrameIdMap>::Leaky g_map_instance = - LAZY_INSTANCE_INITIALIZER; - -} // namespace - const int ExtensionApiFrameIdMap::kInvalidFrameId = -1; const int ExtensionApiFrameIdMap::kTopFrameId = 0; @@ -76,7 +67,10 @@ // static ExtensionApiFrameIdMap* ExtensionApiFrameIdMap::Get() { - return g_map_instance.Pointer(); + // The map is accessed on the IO and UI thread, so construct it once and never + // delete it. + static base::NoDestructor<ExtensionApiFrameIdMap> instance; + return instance.get(); } // static
diff --git a/extensions/browser/extension_api_frame_id_map.h b/extensions/browser/extension_api_frame_id_map.h index 6bbd984..944ce6e 100644 --- a/extensions/browser/extension_api_frame_id_map.h +++ b/extensions/browser/extension_api_frame_id_map.h
@@ -9,7 +9,7 @@ #include <memory> #include <set> -#include "base/lazy_instance.h" +#include "base/no_destructor.h" #include "base/unguessable_token.h" #include "base/uuid.h" #include "content/public/browser/document_user_data.h" @@ -175,7 +175,8 @@ void OnRenderFrameDeleted(content::RenderFrameHost* render_frame_host); protected: - friend struct base::LazyInstanceTraitsBase<ExtensionApiFrameIdMap>; + friend class base::NoDestructor<ExtensionApiFrameIdMap>; + class ExtensionDocumentUserData : public content::DocumentUserData<ExtensionDocumentUserData> { public:
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index 5e0262f..3125c2d 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h
@@ -2005,6 +2005,7 @@ EXPERIMENTALACTOR_PERFORMACTIONS = 1942, EXPERIMENTALACTOR_CREATETASK = 1943, EXPERIMENTALACTOR_REQUESTTABOBSERVATION = 1944, + PDFVIEWERPRIVATE_SAVETODRIVE = 1945, // Last entry: Add new entries above, then run: // tools/metrics/histograms/update_extension_histograms.py ENUM_BOUNDARY
diff --git a/extensions/browser/pref_names.cc b/extensions/browser/pref_names.cc index 7d07ad6f..7aa113f 100644 --- a/extensions/browser/pref_names.cc +++ b/extensions/browser/pref_names.cc
@@ -32,11 +32,5 @@ NOTREACHED(); } -const char kPrefPreferences[] = "preferences"; -const char kPrefIncognitoPreferences[] = "incognito_preferences"; -const char kPrefRegularOnlyPreferences[] = "regular_only_preferences"; -const char kPrefContentSettings[] = "content_settings"; -const char kPrefIncognitoContentSettings[] = "incognito_content_settings"; - } // namespace pref_names } // namespace extensions
diff --git a/extensions/browser/pref_names.h b/extensions/browser/pref_names.h index 2aec75677..032badc 100644 --- a/extensions/browser/pref_names.h +++ b/extensions/browser/pref_names.h
@@ -123,19 +123,21 @@ // Properties in kExtensions dictionaries -------------------------------------- // Extension-controlled preferences. -extern const char kPrefPreferences[]; +inline constexpr char kPrefPreferences[] = "preferences"; // Extension-controlled incognito preferences. -extern const char kPrefIncognitoPreferences[]; +inline constexpr char kPrefIncognitoPreferences[] = "incognito_preferences"; // Extension-controlled regular-only preferences. -extern const char kPrefRegularOnlyPreferences[]; +inline constexpr char kPrefRegularOnlyPreferences[] = + "regular_only_preferences"; // Extension-set content settings. -extern const char kPrefContentSettings[]; +inline constexpr char kPrefContentSettings[] = "content_settings"; // Extension-set incognito content settings. -extern const char kPrefIncognitoContentSettings[]; +inline constexpr char kPrefIncognitoContentSettings[] = + "incognito_content_settings"; // Per-profile UUID to distinguish global shortcut sessions for // org.freedesktop.portal.GlobalShortcuts.
diff --git a/extensions/common/extension_api.cc b/extensions/common/extension_api.cc index 349ace8..ccbaa2f 100644 --- a/extensions/common/extension_api.cc +++ b/extensions/common/extension_api.cc
@@ -16,7 +16,6 @@ #include "base/containers/contains.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" -#include "base/lazy_instance.h" #include "base/strings/span_printf.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" @@ -85,23 +84,19 @@ return nullptr; } -struct ExtensionAPIStatic { - ExtensionAPIStatic() : api(ExtensionAPI::CreateWithDefaultConfiguration()) {} - std::unique_ptr<ExtensionAPI> api; -}; - -base::LazyInstance<ExtensionAPIStatic>::Leaky g_extension_api_static = - LAZY_INSTANCE_INITIALIZER; - -// May override |g_extension_api_static| for a test. +// May override `ExtensionAPI::GetSharedInstance()` for a test. ExtensionAPI* g_shared_instance_for_test = nullptr; } // namespace // static ExtensionAPI* ExtensionAPI::GetSharedInstance() { - return g_shared_instance_for_test ? g_shared_instance_for_test - : g_extension_api_static.Get().api.get(); + if (g_shared_instance_for_test) { + return g_shared_instance_for_test; + } + static ExtensionAPI* shared_instance = + ExtensionAPI::CreateWithDefaultConfiguration(); + return shared_instance; } // static
diff --git a/extensions/common/extension_api.h b/extensions/common/extension_api.h index fce6a3f..a4f527a 100644 --- a/extensions/common/extension_api.h +++ b/extensions/common/extension_api.h
@@ -12,7 +12,6 @@ #include "base/gtest_prod_util.h" #include "base/memory/raw_ptr.h" -#include "base/memory/singleton.h" #include "base/synchronization/lock.h" #include "base/thread_annotations.h" #include "base/values.h" @@ -154,7 +153,6 @@ private: FRIEND_TEST_ALL_PREFIXES(ExtensionAPITest, DefaultConfigurationFeatures); - friend struct base::DefaultSingletonTraits<ExtensionAPI>; void InitDefaultConfiguration();
diff --git a/extensions/common/features/feature_provider.cc b/extensions/common/features/feature_provider.cc index b41951b..988bb19 100644 --- a/extensions/common/features/feature_provider.cc +++ b/extensions/common/features/feature_provider.cc
@@ -10,10 +10,10 @@ #include "base/containers/map_util.h" #include "base/debug/alias.h" -#include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" +#include "base/no_destructor.h" #include "base/strings/span_printf.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" @@ -72,8 +72,10 @@ std::map<std::string, std::unique_ptr<FeatureProvider>> feature_providers_; }; -base::LazyInstance<FeatureProviderStatic>::Leaky g_feature_provider_static = - LAZY_INSTANCE_INITIALIZER; +const FeatureProviderStatic& GetFeatureProviderStatic() { + static base::NoDestructor<FeatureProviderStatic> instance; + return *instance; +} const Feature* GetFeatureFromProviderByName(const std::string& provider_name, const std::string& feature_name) { @@ -93,7 +95,7 @@ // static const FeatureProvider* FeatureProvider::GetByName(const std::string& name) { - return g_feature_provider_static.Get().GetFeatures(name); + return GetFeatureProviderStatic().GetFeatures(name); } // static
diff --git a/extensions/common/features/simple_feature.cc b/extensions/common/features/simple_feature.cc index aedb573..fc777a31 100644 --- a/extensions/common/features/simple_feature.cc +++ b/extensions/common/features/simple_feature.cc
@@ -14,6 +14,7 @@ #include "base/containers/contains.h" #include "base/feature_list.h" #include "base/functional/bind.h" +#include "base/no_destructor.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -47,10 +48,13 @@ } std::string hashed_id; }; + // A singleton copy of the --allowlisted-extension-id so that we don't need to // copy it from the CommandLine each time. -base::LazyInstance<AllowlistInfo>::Leaky g_allowlist_info = - LAZY_INSTANCE_INITIALIZER; +AllowlistInfo& GetAllowlistInfo() { + static base::NoDestructor<AllowlistInfo> instance; + return *instance; +} Feature::Availability IsAvailableToManifestForBind( const HashedExtensionId& hashed_id, @@ -179,7 +183,7 @@ } bool IsAllowlistedForTest(const HashedExtensionId& hashed_id) { - const std::string& allowlisted_id = g_allowlist_info.Get().hashed_id; + const std::string& allowlisted_id = GetAllowlistInfo().hashed_id; return !allowlisted_id.empty() && allowlisted_id == hashed_id.value(); } @@ -187,13 +191,13 @@ SimpleFeature::ScopedThreadUnsafeAllowlistForTest:: ScopedThreadUnsafeAllowlistForTest(const std::string& id) - : previous_id_(g_allowlist_info.Get().hashed_id) { - g_allowlist_info.Get().hashed_id = HashedIdInHex(id); + : previous_id_(GetAllowlistInfo().hashed_id) { + GetAllowlistInfo().hashed_id = HashedIdInHex(id); } SimpleFeature::ScopedThreadUnsafeAllowlistForTest:: ~ScopedThreadUnsafeAllowlistForTest() { - g_allowlist_info.Get().hashed_id = previous_id_; + GetAllowlistInfo().hashed_id = previous_id_; } SimpleFeature::SimpleFeature()
diff --git a/extensions/common/features/simple_feature.h b/extensions/common/features/simple_feature.h index 3cbed76e..83c7c11 100644 --- a/extensions/common/features/simple_feature.h +++ b/extensions/common/features/simple_feature.h
@@ -17,7 +17,6 @@ #include "base/functional/callback_forward.h" #include "base/gtest_prod_util.h" -#include "base/lazy_instance.h" #include "components/version_info/channel.h" #include "extensions/common/context_data.h" #include "extensions/common/extension.h"
diff --git a/extensions/common/permissions/permissions_info.cc b/extensions/common/permissions/permissions_info.cc index 731e759..2dc17c4 100644 --- a/extensions/common/permissions/permissions_info.cc +++ b/extensions/common/permissions/permissions_info.cc
@@ -6,19 +6,16 @@ #include "base/check.h" #include "base/containers/contains.h" -#include "base/lazy_instance.h" #include "base/memory/ptr_util.h" #include "base/strings/string_util.h" #include "extensions/common/alias.h" namespace extensions { -static base::LazyInstance<PermissionsInfo>::Leaky g_permissions_info = - LAZY_INSTANCE_INITIALIZER; - // static PermissionsInfo* PermissionsInfo::GetInstance() { - return g_permissions_info.Pointer(); + static base::NoDestructor<PermissionsInfo> instance; + return instance.get(); } void PermissionsInfo::RegisterPermissions(
diff --git a/extensions/common/permissions/permissions_info.h b/extensions/common/permissions/permissions_info.h index c9327658..2e126d2 100644 --- a/extensions/common/permissions/permissions_info.h +++ b/extensions/common/permissions/permissions_info.h
@@ -16,8 +16,8 @@ #include "base/containers/flat_map.h" #include "base/containers/span.h" #include "base/functional/callback.h" -#include "base/lazy_instance.h" #include "base/memory/raw_ptr.h" +#include "base/no_destructor.h" #include "extensions/common/mojom/api_permission_id.mojom-shared.h" #include "extensions/common/permissions/api_permission.h" #include "extensions/common/permissions/api_permission_set.h" @@ -62,7 +62,7 @@ size_t get_permission_count() const { return permission_count_; } private: - friend struct base::LazyInstanceTraitsBase<PermissionsInfo>; + friend class base::NoDestructor<PermissionsInfo>; PermissionsInfo();
diff --git a/extensions/renderer/api/messaging/one_time_message_handler.cc b/extensions/renderer/api/messaging/one_time_message_handler.cc index 9c23cf9..3f84ecb 100644 --- a/extensions/renderer/api/messaging/one_time_message_handler.cc +++ b/extensions/renderer/api/messaging/one_time_message_handler.cc
@@ -79,6 +79,11 @@ constexpr char OneTimeMessageContextData::kPerContextDataKey[]; +bool OnMessagePromisesSupported() { + return base::FeatureList::IsEnabled( + extensions_features::kRuntimeOnMessagePromiseReturnSupport); +} + void DelayedOneTimeMessageCallbackHelper( const v8::FunctionCallbackInfo<v8::Value>& info) { CHECK(info.Data()->IsExternal()); @@ -140,8 +145,6 @@ v8::Local<v8::Array> results_array = results_value.As<v8::Array>(); uint32_t results_count = results_array->Length(); - bool promise_return_support_enabled = base::FeatureList::IsEnabled( - extensions_features::kRuntimeOnMessagePromiseReturnSupport); for (uint32_t i = 0; i < results_count; ++i) { v8::MaybeLocal<v8::Value> maybe_result = results_array->Get(context, i); v8::Local<v8::Value> listener_return; @@ -161,7 +164,7 @@ // Check if any of the returns are a promise -- indicating the listener // will reply async. If they do, handle both the promise resolving or // rejecting. - if (listener_return->IsPromise() && promise_return_support_enabled) { + if (OnMessagePromisesSupported() && listener_return->IsPromise()) { std::ignore = listener_return.As<v8::Promise>()->Then( context, promise_resolved_function, promise_rejected_function); // TODO(crbug.com/40753031): Consider setting lastError for caller when @@ -411,7 +414,10 @@ CreateDelayedOneTimeMessageCallback(isolate, context, target_port_id, callback.get(), script_context); - port.response_function = v8::Global<v8::Function>(isolate, response_function); + if (OnMessagePromisesSupported()) { + port.response_function = + v8::Global<v8::Function>(isolate, response_function); + } v8::HandleScope handle_scope(isolate); @@ -698,6 +704,12 @@ return reinterpret_cast<CallbackID>(callback.get()) == raw_callback; }); + // TODO(crbug.com/40753031): When the promise support feature is on this needs + // to take into account if there are any other pending_callbacks that could be + // run and not close the port if that is true. Otherwise, as-is, the + // collection logic can close the port too early and prevent the message + // response from being sent back to the sender. + auto iter = data->receivers.find(port_id); // The channel may already be closed (if the receiver replied before the reply // callback was collected). @@ -766,8 +778,7 @@ } debug::ScopedPromiseRejectedResponseCrashKeys promise_rejected_crash_keys( - base::FeatureList::IsEnabled( - extensions_features::kRuntimeOnMessagePromiseReturnSupport)); + /*promise_support_feature_enabled=*/OnMessagePromisesSupported()); v8::Local<v8::Value> promise_reject_reason; // This is safe to CHECK() because when a promise rejects it always provides a // value. Even if `reject()` (with no argument) is called we see `undefined` @@ -838,16 +849,21 @@ NativeRendererMessagingService* messaging_service = bindings_system_->messaging_service(); - v8::Local<v8::Function> promise_resolved_function = - port.response_function.Get(isolate); - v8::Local<v8::Function> promise_rejected_function = - CreatePromiseRejectedFunction(isolate, context, port_id); + v8::Local<v8::Function> promise_resolved_function; + v8::Local<v8::Function> promise_rejected_function; + if (OnMessagePromisesSupported()) { + promise_resolved_function = port.response_function.Get(isolate); + promise_rejected_function = + CreatePromiseRejectedFunction(isolate, context, port_id); + } if (CheckAndHandleAsyncListenerReply(isolate, context, result, promise_resolved_function, promise_rejected_function)) { - // Ensure the global function doesn't outlive port closing. - port.response_function.SetWeak(); + if (OnMessagePromisesSupported()) { + // Ensure the global function doesn't outlive port closing. + port.response_function.SetWeak(); + } // Inform the browser that one of the listeners said they would be replying // later and leave the channel open. ScriptContext* script_context = GetScriptContextFromV8Context(context);
diff --git a/extensions/renderer/api/messaging/one_time_message_handler_unittest.cc b/extensions/renderer/api/messaging/one_time_message_handler_unittest.cc index 7dbe821..f848e176 100644 --- a/extensions/renderer/api/messaging/one_time_message_handler_unittest.cc +++ b/extensions/renderer/api/messaging/one_time_message_handler_unittest.cc
@@ -11,10 +11,12 @@ #include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "base/test/gmock_callback_support.h" +#include "base/test/with_feature_override.h" #include "extensions/common/api/messaging/message.h" #include "extensions/common/api/messaging/port_id.h" #include "extensions/common/extension.h" #include "extensions/common/extension_builder.h" +#include "extensions/common/extension_features.h" #include "extensions/common/mojom/context_type.mojom.h" #include "extensions/common/mojom/message_port.mojom-shared.h" #include "extensions/renderer/api/messaging/message_target.h" @@ -749,67 +751,6 @@ ::testing::Mock::VerifyAndClearExpectations(&mock_message_port_host1); } -TEST_F(OneTimeMessageHandlerTest, - SendResponseAndPromiseRejectCallbacksGarbageCollected) { - v8::HandleScope handle_scope(isolate()); - v8::Local<v8::Context> context = MainContext(); - - constexpr char kRegisterListener[] = - "(function() {\n" - " chrome.runtime.onMessage.addListener(\n" - " function(message, sender, reply) {\n" - " return true; // Reply later\n" - " });\n" - "})"; - v8::Local<v8::Function> add_listener = - FunctionFromString(context, kRegisterListener); - RunFunctionOnGlobal(add_listener, context, 0, nullptr); - - base::UnguessableToken other_context_id = base::UnguessableToken::Create(); - const PortId port_id(other_context_id, 0, false, - mojom::SerializationFormat::kJson); - mojo::PendingAssociatedRemote<mojom::MessagePort> message_port_remote; - mojo::PendingAssociatedReceiver<mojom::MessagePortHost> - message_port_host_receiver; - MockMessagePortHost mock_message_port_host; - - v8::Local<v8::Object> sender = v8::Object::New(isolate()); - message_handler()->AddReceiverForTesting( - script_context(), port_id, sender, messaging_util::kOnMessageEvent, - message_port_remote, message_port_host_receiver); - message_port_remote.EnableUnassociatedUsage(); - message_port_host_receiver.EnableUnassociatedUsage(); - mock_message_port_host.BindReceiver(std::move(message_port_host_receiver)); - - const Message message("\"Hi\"", mojom::SerializationFormat::kJson, false); - base::RunLoop run_loop; - - EXPECT_CALL(mock_message_port_host, ResponsePending()); - EXPECT_CALL(mock_message_port_host, - ClosePort( - /*close_channel=*/false, - /*error_message=*/testing::Eq(std::nullopt))) - .WillOnce(base::test::RunClosure(run_loop.QuitClosure())); - message_handler()->DeliverMessage(script_context(), message, port_id); - EXPECT_TRUE(message_handler()->HasPort(script_context(), port_id)); - // One callback is for the response function, the second is for the promise - // reject function. - EXPECT_EQ( - 2, message_handler()->GetPendingCallbackCountForTest(script_context())); - - // The listener didn't retain the reply callback and the listener didn't - // return a promise that could reject, so the JS callbacks should be garbage - // collected and the related pending callbacks for them should have been - // cleared as well so we don't leak them after the port closes. - RunGarbageCollection(); - run_loop.Run(); - ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender()); - ::testing::Mock::VerifyAndClearExpectations(&mock_message_port_host); - EXPECT_FALSE(message_handler()->HasPort(script_context(), port_id)); - EXPECT_EQ( - 0, message_handler()->GetPendingCallbackCountForTest(script_context())); -} - // runtime.onMessage requires that a listener return `true` if they intend to // respond to the message asynchronously. Verify that we close the port if no // listener does so. @@ -893,4 +834,84 @@ ::testing::Mock::VerifyAndClearExpectations(&mock_message_port_host); } +class OneTimeMessageHandlerGarbageCollectionTest + : public base::test::WithFeatureOverride, + public OneTimeMessageHandlerTest { + public: + OneTimeMessageHandlerGarbageCollectionTest() + : WithFeatureOverride( + extensions_features::kRuntimeOnMessagePromiseReturnSupport) {} + + OneTimeMessageHandlerGarbageCollectionTest( + const OneTimeMessageHandlerGarbageCollectionTest&) = delete; + OneTimeMessageHandlerGarbageCollectionTest& operator=( + const OneTimeMessageHandlerGarbageCollectionTest&) = delete; + ~OneTimeMessageHandlerGarbageCollectionTest() override = default; +}; + +TEST_P(OneTimeMessageHandlerGarbageCollectionTest, + SendResponseAndPromiseRejectCallbacksGarbageCollected) { + v8::HandleScope handle_scope(isolate()); + v8::Local<v8::Context> context = MainContext(); + + constexpr char kRegisterListener[] = + "(function() {\n" + " chrome.runtime.onMessage.addListener(\n" + " function(message, sender, reply) {\n" + " return true; // Reply later\n" + " });\n" + "})"; + v8::Local<v8::Function> add_listener = + FunctionFromString(context, kRegisterListener); + RunFunctionOnGlobal(add_listener, context, 0, nullptr); + + base::UnguessableToken other_context_id = base::UnguessableToken::Create(); + const PortId port_id(other_context_id, 0, false, + mojom::SerializationFormat::kJson); + mojo::PendingAssociatedRemote<mojom::MessagePort> message_port_remote; + mojo::PendingAssociatedReceiver<mojom::MessagePortHost> + message_port_host_receiver; + MockMessagePortHost mock_message_port_host; + + v8::Local<v8::Object> sender = v8::Object::New(isolate()); + message_handler()->AddReceiverForTesting( + script_context(), port_id, sender, messaging_util::kOnMessageEvent, + message_port_remote, message_port_host_receiver); + message_port_remote.EnableUnassociatedUsage(); + message_port_host_receiver.EnableUnassociatedUsage(); + mock_message_port_host.BindReceiver(std::move(message_port_host_receiver)); + + const Message message("\"Hi\"", mojom::SerializationFormat::kJson, false); + base::RunLoop run_loop; + + EXPECT_CALL(mock_message_port_host, ResponsePending()); + EXPECT_CALL(mock_message_port_host, + ClosePort( + /*close_channel=*/false, + /*error_message=*/testing::Eq(std::nullopt))) + .WillOnce(base::test::RunClosure(run_loop.QuitClosure())); + message_handler()->DeliverMessage(script_context(), message, port_id); + EXPECT_TRUE(message_handler()->HasPort(script_context(), port_id)); + // One callback is for the response function, the second is for the promise + // reject function (when the feature is enabled). + EXPECT_EQ( + IsParamFeatureEnabled() ? 2 : 1, + message_handler()->GetPendingCallbackCountForTest(script_context())); + + // The listener didn't retain the reply callback and the listener didn't + // return a promise that could reject, so the JS callbacks should be garbage + // collected and the related pending callbacks for them should have been + // cleared as well so we don't leak them after the port closes. + RunGarbageCollection(); + run_loop.Run(); + ::testing::Mock::VerifyAndClearExpectations(ipc_message_sender()); + ::testing::Mock::VerifyAndClearExpectations(&mock_message_port_host); + EXPECT_FALSE(message_handler()->HasPort(script_context(), port_id)); + EXPECT_EQ( + 0, message_handler()->GetPendingCallbackCountForTest(script_context())); +} + +INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE( + OneTimeMessageHandlerGarbageCollectionTest); + } // namespace extensions
diff --git a/fuchsia_web/runners/cast/api_bindings_client_browsertest.cc b/fuchsia_web/runners/cast/api_bindings_client_browsertest.cc index b6a88ad..87a2e60 100644 --- a/fuchsia_web/runners/cast/api_bindings_client_browsertest.cc +++ b/fuchsia_web/runners/cast/api_bindings_client_browsertest.cc
@@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "fuchsia_web/runners/cast/api_bindings_client.h" + #include <fuchsia/web/cpp/fidl.h> #include <lib/fidl/cpp/binding.h> @@ -11,6 +13,7 @@ #include "base/path_service.h" #include "base/test/bind.h" #include "base/test/test_future.h" +#include "base/threading/thread_restrictions.h" #include "components/cast/message_port/fuchsia/create_web_message.h" #include "components/cast/message_port/fuchsia/message_port_fuchsia.h" #include "content/public/test/browser_test.h" @@ -18,7 +21,6 @@ #include "fuchsia_web/common/test/frame_for_test.h" #include "fuchsia_web/common/test/frame_test_util.h" #include "fuchsia_web/common/test/test_navigation_listener.h" -#include "fuchsia_web/runners/cast/api_bindings_client.h" #include "fuchsia_web/runners/cast/named_message_port_connector_fuchsia.h" #include "fuchsia_web/runners/cast/test/fake_api_bindings.h" #include "fuchsia_web/webengine/test/web_engine_browser_test.h"
diff --git a/fuchsia_web/runners/cast/cast_runner.cml b/fuchsia_web/runners/cast/cast_runner.cml index 4075c9c..5fc2733 100644 --- a/fuchsia_web/runners/cast/cast_runner.cml +++ b/fuchsia_web/runners/cast/cast_runner.cml
@@ -18,8 +18,6 @@ // Enable graceful teardown since the web_instance uses dynamic // capabilities via this Component. lifecycle: { stop_event: "notify" }, - // Raise an exception if the runner tries to use a bad handle. - deny_bad_handles: "true", }, capabilities: [ {
diff --git a/fuchsia_web/webengine/web_instance-common.shard.cml b/fuchsia_web/webengine/web_instance-common.shard.cml index a531d9d4..da271b3 100644 --- a/fuchsia_web/webengine/web_instance-common.shard.cml +++ b/fuchsia_web/webengine/web_instance-common.shard.cml
@@ -5,9 +5,6 @@ program: { runner: "elf", binary: "web_engine_exe", - - // Raise an exception if the browser tries to use a bad handle. - deny_bad_handles: "true", }, capabilities: [ {
diff --git a/gpu/ipc/host/gpu_disk_cache.h b/gpu/ipc/host/gpu_disk_cache.h index 6b1753e..34d916e5 100644 --- a/gpu/ipc/host/gpu_disk_cache.h +++ b/gpu/ipc/host/gpu_disk_cache.h
@@ -121,7 +121,7 @@ GpuDiskCacheFactory(const GpuDiskCacheFactory&) = delete; GpuDiskCacheFactory& operator=(const GpuDiskCacheFactory&) = delete; - ~GpuDiskCacheFactory(); + virtual ~GpuDiskCacheFactory(); // Clear the given gpu disk |cache|. This supports unbounded deletes in // either direction by using null Time values for either |begin_time| or @@ -135,10 +135,10 @@ // deletes in either direction by using null Time values for either // |begin_time| or |end_time|. The |callback| will be executed when the // clear is complete. - void ClearByPath(const base::FilePath& path, - base::Time begin_time, - base::Time end_time, - base::OnceClosure callback); + virtual void ClearByPath(const base::FilePath& path, + base::Time begin_time, + base::Time end_time, + base::OnceClosure callback); // Looks up a |path| and returns a cache handle for it (registering it if // necessary) for the given |type|.
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd index a1d437f..fe6bd69 100644 --- a/ios/chrome/app/strings/ios_strings.grd +++ b/ios/chrome/app/strings/ios_strings.grd
@@ -412,6 +412,9 @@ <message name="IDS_IOS_AUTOFILL_CARD_NUMBER" desc="Title of the field representing the number (PAN) on a credit card. [Length: 10em] [iOS only]"> Card number </message> + <message name="IDS_IOS_AUTOFILL_CARD_SAVED" desc="Title for the save local card confirmation message info bar. [iOS only]"> + Card saved + </message> <message name="IDS_IOS_AUTOFILL_CITY" desc="Title of the field of a profile address representing the city/town of the address. [Length: 15em] [iOS only]"> City / Town </message> @@ -500,6 +503,9 @@ <message name="IDS_IOS_AUTOFILL_SAVE_CARD_CLOSE" desc="Title for the save credit card modal's bar button to close the view. [iOS only]"> Close </message> + <message name="IDS_IOS_AUTOFILL_SAVE_CARD_GOT_IT" desc="Title for the save local card confirmation message info bar button to close the view. [iOS only]"> + Got it + </message> <message name="IDS_IOS_AUTOFILL_SAVE_ELLIPSIS" desc="Title for the button that presents the View for Saving a credit card. The ellipsis indicate that a following action (the presentation) will take place. [Length: 10em] [iOS only]"> Save… </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_AUTOFILL_CARD_SAVED.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_AUTOFILL_CARD_SAVED.png.sha1 new file mode 100644 index 0000000..c273468 --- /dev/null +++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_AUTOFILL_CARD_SAVED.png.sha1
@@ -0,0 +1 @@ +aeebdc5ab09118cf6b7c390b79b85a588b230b8c \ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_AUTOFILL_SAVE_CARD_GOT_IT.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_AUTOFILL_SAVE_CARD_GOT_IT.png.sha1 new file mode 100644 index 0000000..c871708 --- /dev/null +++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_AUTOFILL_SAVE_CARD_GOT_IT.png.sha1
@@ -0,0 +1 @@ +20e1cc108270a41dd0f85376e6a59726a882d012 \ No newline at end of file
diff --git a/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/form_input_accessory_view_controller.mm b/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/form_input_accessory_view_controller.mm index ecfbbda..812e1c6a 100644 --- a/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/form_input_accessory_view_controller.mm +++ b/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/form_input_accessory_view_controller.mm
@@ -260,16 +260,16 @@ weakSelf.showScrollHint = NO; } }; - // Check if the view is in the current hierarchy before performing the layout. - if (self.formInputAccessoryView.window) { - [self.formInputAccessoryView layoutIfNeeded]; - self.formSuggestionViewMask.frame = self.formSuggestionContainerView.bounds; - } [self.formSuggestionView updateSuggestions:suggestions showScrollHint:self.showScrollHint accessoryTrailingView:self.formInputAccessoryView.trailingView completion:completion]; + // Check if the view is in the current hierarchy before performing the layout. + if (self.formInputAccessoryView.window) { + [self.formInputAccessoryView layoutIfNeeded]; + self.formSuggestionViewMask.frame = self.formSuggestionContainerView.bounds; + } self.brandingViewController.keyboardAccessoryVisible = self.formAccessoryVisible; [self announceVoiceOverMessageIfNeeded:[suggestions count]];
diff --git a/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.h b/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.h index 677623c6..11f6f31 100644 --- a/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.h +++ b/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.h
@@ -14,6 +14,7 @@ #import "components/autofill/core/browser/autofill_progress_dialog_type.h" #import "components/autofill/core/browser/payments/autofill_save_card_delegate.h" #import "components/autofill/core/browser/payments/autofill_save_card_ui_info.h" +#include "components/autofill/core/browser/payments/multiple_request_payments_network_interface.h" #import "components/autofill/core/browser/payments/payments_autofill_client.h" #import "components/autofill/core/browser/ui/payments/autofill_progress_dialog_controller_impl.h" #include "components/autofill/core/browser/ui/payments/card_expiration_date_fix_flow_controller_impl.h" @@ -95,6 +96,8 @@ void ShowAutofillErrorDialog( AutofillErrorDialogContext error_context) override; PaymentsNetworkInterface* GetPaymentsNetworkInterface() override; + MultipleRequestPaymentsNetworkInterface* + GetMultipleRequestPaymentsNetworkInterface() override; void ShowAutofillProgressDialog( AutofillProgressDialogType autofill_progress_dialog_type, base::OnceClosure cancel_callback) override; @@ -156,6 +159,8 @@ const raw_ref<infobars::InfoBarManager> infobar_manager_; std::unique_ptr<PaymentsNetworkInterface> payments_network_interface_; + std::unique_ptr<MultipleRequestPaymentsNetworkInterface> + multiple_request_payments_network_interface_; // TODO(crbug.com/40937065): Make these member variables as const raw_refs. const raw_ptr<PrefService> pref_service_;
diff --git a/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.mm b/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.mm index 8edcc7f..5e2d151 100644 --- a/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.mm +++ b/ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.mm
@@ -5,6 +5,7 @@ #import "ios/chrome/browser/autofill/ui_bundled/ios_chrome_payments_autofill_client.h" #import <optional> +#import <variant> #import "base/check_deref.h" #import "base/functional/callback.h" @@ -278,6 +279,17 @@ return payments_network_interface_.get(); } +MultipleRequestPaymentsNetworkInterface* +IOSChromePaymentsAutofillClient::GetMultipleRequestPaymentsNetworkInterface() { + if (!multiple_request_payments_network_interface_) { + multiple_request_payments_network_interface_ = + std::make_unique<payments::MultipleRequestPaymentsNetworkInterface>( + client_->GetURLLoaderFactory(), *client_->GetIdentityManager(), + web_state_->GetBrowserState()->IsOffTheRecord()); + } + return multiple_request_payments_network_interface_.get(); +} + void IOSChromePaymentsAutofillClient::ShowUnmaskPrompt( const CreditCard& card, const CardUnmaskPromptOptions& card_unmask_prompt_options, @@ -376,12 +388,19 @@ VirtualCardEnrollmentManager* IOSChromePaymentsAutofillClient::GetVirtualCardEnrollmentManager() { if (!virtual_card_enrollment_manager_) { + PaymentsNetworkInterfaceVariation payments_network_interface; + if (base::FeatureList::IsEnabled( + features:: + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment)) { + payments_network_interface = GetMultipleRequestPaymentsNetworkInterface(); + } else { + payments_network_interface = GetPaymentsNetworkInterface(); + } virtual_card_enrollment_manager_ = std::make_unique<VirtualCardEnrollmentManager>( &client_->GetPersonalDataManager().payments_data_manager(), - GetPaymentsNetworkInterface(), &client_.get()); + payments_network_interface, &client_.get()); } - return virtual_card_enrollment_manager_.get(); }
diff --git a/ios/chrome/browser/intelligence/bwg/coordinator/bwg_mediator.mm b/ios/chrome/browser/intelligence/bwg/coordinator/bwg_mediator.mm index 478473e..0c05f672 100644 --- a/ios/chrome/browser/intelligence/bwg/coordinator/bwg_mediator.mm +++ b/ios/chrome/browser/intelligence/bwg/coordinator/bwg_mediator.mm
@@ -8,6 +8,7 @@ #import "base/metrics/histogram_functions.h" #import "base/strings/sys_string_conversions.h" +#import "base/time/time.h" #import "components/prefs/pref_service.h" #import "ios/chrome/browser/intelligence/bwg/coordinator/bwg_mediator_delegate.h" #import "ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.h" @@ -40,6 +41,12 @@ // The PageContext wrapper used to provide context about a page. PageContextWrapper* _pageContextWrapper; + + // Start time for the preparation of the presentation of BWG overlay. + base::TimeTicks _BWGOverlayPreparationStartTime; + + // Whether the FRE was presented for the current BWG instance. + BOOL _didPresentBWGFRE; } - (instancetype)initWithPrefService:(PrefService*)prefService @@ -55,6 +62,8 @@ } - (void)presentBWGFlow { + _BWGOverlayPreparationStartTime = base::TimeTicks::Now(); + switch (BWGPromoConsentVariationsParam()) { case BWGPromoConsentVariations::kSkipConsent: [self prepareBWGOverlay]; @@ -68,10 +77,10 @@ break; } - BOOL didPresentBWGFRE = [self.delegate maybePresentBWGFRE]; + _didPresentBWGFRE = [self.delegate maybePresentBWGFRE]; // Not presenting the FRE implies that the promo was shown and user consent // was given which means we can navigate to the BWG overlay immediately. - if (!didPresentBWGFRE) { + if (!_didPresentBWGFRE) { [self prepareBWGOverlay]; } } @@ -144,6 +153,11 @@ BWGBrowserAgent->PresentBwgOverlay(self.baseViewController, std::move(pageContextWrapperResponse)); + base::UmaHistogramTimes( + _didPresentBWGFRE ? kStartupTimeWithFREHistogram + : kStartupTimeNoFREHistogram, + base::TimeTicks::Now() - _BWGOverlayPreparationStartTime); + // TODO(crbug.com/419064727): Dismiss bwg promo/consent. }
diff --git a/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.h b/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.h index 2b9fa8b..576a9898 100644 --- a/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.h +++ b/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.h
@@ -21,13 +21,14 @@ extern const char kConsentActionHistogram[]; // Enum for the IOS.Gemini.FRE.PromoAction and IOS.Gemini.FRE.ConsentAction -// histograms. LINT.IfChange(IOSGeminiFREAction) +// histograms. +// LINT.IfChange(IOSGeminiFREAction) enum class IOSGeminiFREAction { kAccept = 0, kDismiss = 1, kMaxValue = kDismiss, }; -// LINT.ThenChange(//tools/metrics/histograms/metadata/ios/enums.xml:IOSGeminiFREAction) +// LINT.ThenChange(/tools/metrics/histograms/metadata/ios/enums.xml:IOSGeminiFREAction) // Records the user action on the FRE Promo. void RecordFREPromoAction(IOSGeminiFREAction action); @@ -35,4 +36,10 @@ // Records the user action on the FRE Consent Screen. void RecordFREConsentAction(IOSGeminiFREAction action); +// UMA histogram key for IOS.Gemini.StartupTime.FirstRun. +extern const char kStartupTimeWithFREHistogram[]; + +// UMA histogram key for IOS.Gemini.StartupTime.NotFirstRun. +extern const char kStartupTimeNoFREHistogram[]; + #endif // IOS_CHROME_BROWSER_INTELLIGENCE_BWG_METRICS_BWG_METRICS_H_
diff --git a/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.mm b/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.mm index eb68250..31252f4 100644 --- a/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.mm +++ b/ios/chrome/browser/intelligence/bwg/metrics/bwg_metrics.mm
@@ -16,6 +16,10 @@ const char kConsentActionHistogram[] = "IOS.Gemini.FRE.ConsentAction"; +const char kStartupTimeWithFREHistogram[] = "IOS.Gemini.StartupTime.FirstRun"; + +const char kStartupTimeNoFREHistogram[] = "IOS.Gemini.StartupTime.NotFirstRun"; + void RecordFREPromoAction(IOSGeminiFREAction action) { base::UmaHistogramEnumeration(kPromoActionHistogram, action); }
diff --git a/ios/chrome/browser/intelligence/bwg/ui/bwg_consent_view_controller.mm b/ios/chrome/browser/intelligence/bwg/ui/bwg_consent_view_controller.mm index 38253392..84e4672 100644 --- a/ios/chrome/browser/intelligence/bwg/ui/bwg_consent_view_controller.mm +++ b/ios/chrome/browser/intelligence/bwg/ui/bwg_consent_view_controller.mm
@@ -455,7 +455,6 @@ bodyTextView.backgroundColor = [UIColor clearColor]; bodyTextView.scrollEnabled = NO; bodyTextView.editable = NO; - bodyTextView.selectable = NO; bodyTextView.delegate = self; bodyTextView.textContainerInset = UIEdgeInsetsZero; bodyTextView.textContainer.lineFragmentPadding = 0; @@ -473,7 +472,6 @@ footNoteTextView.backgroundColor = [UIColor clearColor]; footNoteTextView.scrollEnabled = NO; footNoteTextView.editable = NO; - footNoteTextView.selectable = NO; footNoteTextView.delegate = self; footNoteTextView.textContainerInset = UIEdgeInsetsZero;
diff --git a/ios/chrome/browser/overlays/ui_bundled/infobar_banner/BUILD.gn b/ios/chrome/browser/overlays/ui_bundled/infobar_banner/BUILD.gn index ab142d2..51cb739 100644 --- a/ios/chrome/browser/overlays/ui_bundled/infobar_banner/BUILD.gn +++ b/ios/chrome/browser/overlays/ui_bundled/infobar_banner/BUILD.gn
@@ -40,6 +40,7 @@ "//ios/chrome/browser/shared/public/commands", "//ios/chrome/browser/shared/public/features", "//ios/chrome/browser/shared/ui/util", + "//ios/chrome/browser/shared/ui/util:snackbar_util", "//ios/chrome/browser/shared/ui/util:util_swift", ]
diff --git a/ios/chrome/browser/overlays/ui_bundled/infobar_banner/infobar_banner_overlay_coordinator.mm b/ios/chrome/browser/overlays/ui_bundled/infobar_banner/infobar_banner_overlay_coordinator.mm index 478317b..5fcdec1c 100644 --- a/ios/chrome/browser/overlays/ui_bundled/infobar_banner/infobar_banner_overlay_coordinator.mm +++ b/ios/chrome/browser/overlays/ui_bundled/infobar_banner/infobar_banner_overlay_coordinator.mm
@@ -39,8 +39,10 @@ #import "ios/chrome/browser/shared/model/browser/browser.h" #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h" #import "ios/chrome/browser/shared/public/commands/non_modal_signin_promo_commands.h" +#import "ios/chrome/browser/shared/public/commands/snackbar_commands.h" #import "ios/chrome/browser/shared/public/features/features.h" #import "ios/chrome/browser/shared/ui/util/layout_guide_names.h" +#import "ios/chrome/browser/shared/ui/util/snackbar_util.h" #import "ios/chrome/browser/shared/ui/util/util_swift.h" @interface InfobarBannerOverlayCoordinator () <InfobarBannerPositioner> @@ -129,6 +131,13 @@ mediator.engagementTracker = feature_engagement::TrackerFactory::GetForProfile(self.profile); + if ([mediator isKindOfClass:[SaveCardInfobarBannerOverlayMediator class]]) { + SaveCardInfobarBannerOverlayMediator* saveCardMediator = + (SaveCardInfobarBannerOverlayMediator*)mediator; + saveCardMediator.snackbarCommandsHandler = HandlerForProtocol( + self.browser->GetCommandDispatcher(), SnackbarCommands); + } + self.mediator = mediator; // Present the banner. self.bannerViewController.modalPresentationStyle = UIModalPresentationCustom;
diff --git a/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/BUILD.gn b/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/BUILD.gn index f8451e9..1d200d50 100644 --- a/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/BUILD.gn +++ b/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/BUILD.gn
@@ -21,7 +21,10 @@ "//ios/chrome/browser/overlays/model/public/infobar_modal", "//ios/chrome/browser/overlays/ui_bundled:coordinators", "//ios/chrome/browser/overlays/ui_bundled/infobar_banner:mediators", + "//ios/chrome/browser/shared/public/commands", "//ios/chrome/browser/shared/ui/symbols", + "//ios/chrome/browser/shared/ui/util:snackbar_util", + "//ios/third_party/material_components_ios", "//ui/base", ] } @@ -38,6 +41,7 @@ "//components/infobars/core", "//components/prefs", "//components/signin/public/identity_manager", + "//ios/chrome/app/strings", "//ios/chrome/browser/autofill/model/credit_card:infobar_delegate", "//ios/chrome/browser/infobars/model", "//ios/chrome/browser/infobars/model:public", @@ -52,6 +56,9 @@ "//ios/chrome/browser/overlays/model/public/infobar_modal", "//ios/chrome/browser/overlays/model/test", "//ios/chrome/browser/overlays/ui_bundled/test", + "//ios/chrome/browser/shared/public/commands", + "//ios/chrome/browser/shared/ui/util:snackbar_util", + "//ios/third_party/material_components_ios", "//testing/gmock", "//testing/gtest", "//third_party/ocmock",
diff --git a/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.h b/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.h index 801c2fa..9b7bca0 100644 --- a/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.h +++ b/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.h
@@ -7,8 +7,13 @@ #import "ios/chrome/browser/overlays/ui_bundled/infobar_banner/infobar_banner_overlay_mediator.h" +@protocol SnackbarCommands; + // Mediator that configures an infobar banner for a save card infobar. @interface SaveCardInfobarBannerOverlayMediator : InfobarBannerOverlayMediator + +@property(nonatomic, weak) id<SnackbarCommands> snackbarCommandsHandler; + @end #endif // IOS_CHROME_BROWSER_OVERLAYS_UI_BUNDLED_INFOBAR_BANNER_SAVE_CARD_SAVE_CARD_INFOBAR_BANNER_OVERLAY_MEDIATOR_H_
diff --git a/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.mm b/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.mm index 39d9dd8..75abfa3a 100644 --- a/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.mm +++ b/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.mm
@@ -4,6 +4,8 @@ #import "ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.h" +#import <MaterialComponents/MaterialSnackbar.h> + #import "base/strings/sys_string_conversions.h" #import "ios/chrome/browser/autofill/model/credit_card/autofill_save_card_infobar_delegate_ios.h" #import "ios/chrome/browser/infobars/model/overlays/infobar_overlay_util.h" @@ -13,7 +15,9 @@ #import "ios/chrome/browser/overlays/ui_bundled/infobar_banner/infobar_banner_overlay_mediator+consumer_support.h" #import "ios/chrome/browser/overlays/ui_bundled/infobar_banner/infobar_banner_overlay_mediator.h" #import "ios/chrome/browser/overlays/ui_bundled/overlay_request_mediator+subclassing.h" +#import "ios/chrome/browser/shared/public/commands/snackbar_commands.h" #import "ios/chrome/browser/shared/ui/symbols/symbols.h" +#import "ios/chrome/browser/shared/ui/util/snackbar_util.h" #import "ios/chrome/grit/ios_strings.h" #import "ui/base/l10n/l10n_util.h" @@ -64,15 +68,45 @@ // legal requirement and shouldn't be changed. if (delegate->is_for_upload()) { [self presentInfobarModalFromBanner]; - return; + } else { + InfoBarIOS* infobar = GetOverlayRequestInfobar(self.request); + infobar->set_accepted(delegate->UpdateAndAccept( + delegate->cardholder_name(), delegate->expiration_date_month(), + delegate->expiration_date_year())); + + // Create and show the snackbar message. + MDCSnackbarMessage* message = [self createCardSavedSnackbarMessage]; + if (message) { + [self.snackbarCommandsHandler showSnackbarMessage:message]; + } + + [self dismissOverlay]; } +} - InfoBarIOS* infobar = GetOverlayRequestInfobar(self.request); - infobar->set_accepted(delegate->UpdateAndAccept( - delegate->cardholder_name(), delegate->expiration_date_month(), - delegate->expiration_date_year())); +- (MDCSnackbarMessage*)createCardSavedSnackbarMessage { + autofill::AutofillSaveCardInfoBarDelegateIOS* delegate = + self.saveCardDelegate; + if (!delegate) { + return nil; + } + NSString* titleText = base::SysUTF16ToNSString( + l10n_util::GetStringUTF16(IDS_IOS_AUTOFILL_CARD_SAVED)); - [self dismissOverlay]; + NSString* subTitleText = base::SysUTF16ToNSString(delegate->card_label()); + NSString* messageText = + [NSString stringWithFormat:@"%@\n%@", titleText, subTitleText]; + + MDCSnackbarMessage* message = + [MDCSnackbarMessage messageWithText:messageText]; + + // "Got it" button + MDCSnackbarMessageAction* action = [[MDCSnackbarMessageAction alloc] init]; + action.title = base::SysUTF16ToNSString( + l10n_util::GetStringUTF16(IDS_IOS_AUTOFILL_SAVE_CARD_GOT_IT)); + message.action = action; + + return message; } - (void)dismissInfobarBannerForUserInteraction:(BOOL)userInitiated {
diff --git a/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator_unittest.mm b/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator_unittest.mm index a1600a2..abd8bd41 100644 --- a/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator_unittest.mm +++ b/ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator_unittest.mm
@@ -4,6 +4,8 @@ #import "ios/chrome/browser/overlays/ui_bundled/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.h" +#import <MaterialComponents/MaterialSnackbar.h> + #import "base/feature_list.h" #import "base/functional/bind.h" #import "base/memory/raw_ptr.h" @@ -20,10 +22,13 @@ #import "ios/chrome/browser/infobars/ui_bundled/banners/infobar_banner_delegate.h" #import "ios/chrome/browser/infobars/ui_bundled/banners/test/fake_infobar_banner_consumer.h" #import "ios/chrome/browser/overlays/model/public/default/default_infobar_overlay_request_config.h" +#import "ios/chrome/browser/shared/public/commands/snackbar_commands.h" +#import "ios/chrome/grit/ios_strings.h" #import "testing/gtest_mac.h" #import "testing/platform_test.h" #import "third_party/ocmock/OCMock/OCMock.h" #import "third_party/ocmock/gtest_support.h" +#import "ui/base/l10n/l10n_util.h" namespace { @@ -47,6 +52,7 @@ public: ~SaveCardInfobarBannerOverlayMediatorTest() override { EXPECT_OCMOCK_VERIFY((id)mediator_); + EXPECT_OCMOCK_VERIFY(mock_snackbar_commands_handler_); } void InitInfobar(const bool for_upload) { @@ -70,6 +76,9 @@ initWithRequest:request_.get()]); mediator_.consumer = consumer_; + mock_snackbar_commands_handler_ = + OCMProtocolMock(@protocol(SnackbarCommands)); + mediator_.snackbarCommandsHandler = mock_snackbar_commands_handler_; } protected: @@ -78,6 +87,7 @@ raw_ptr<MockAutofillSaveCardInfoBarDelegateMobile> delegate_ = nil; FakeInfobarBannerConsumer* consumer_ = nil; SaveCardInfobarBannerOverlayMediator* mediator_ = nil; + id mock_snackbar_commands_handler_ = nil; }; TEST_F(SaveCardInfobarBannerOverlayMediatorTest, SetUpConsumer) { @@ -227,3 +237,49 @@ histogram_tester.ExpectTotalCount( kSaveCreditCardPromptResultHistogramStringForLocalSave, 2); } + +// Tests that a snackbar is shown when a card is saved locally (non-upload). +TEST_F(SaveCardInfobarBannerOverlayMediatorTest, ShowSnackbarForLocalSave) { + InitInfobar(/*for_upload=*/false); + + EXPECT_CALL(*delegate_, UpdateAndAccept(_, _, _)) + .WillOnce(testing::Return(true)); + + // Expected snackbar message content. + NSString* titleText = base::SysUTF16ToNSString( + l10n_util::GetStringUTF16(IDS_IOS_AUTOFILL_CARD_SAVED)); + NSString* subTitleText = base::SysUTF16ToNSString(delegate_->card_label()); + NSString* expectedMessageText = + [NSString stringWithFormat:@"%@\n%@", titleText, subTitleText]; + NSString* expectedButtonText = base::SysUTF16ToNSString( + l10n_util::GetStringUTF16(IDS_IOS_AUTOFILL_SAVE_CARD_GOT_IT)); + + // Set up expectation for the snackbar message. + OCMExpect([mock_snackbar_commands_handler_ + showSnackbarMessage:[OCMArg checkWithBlock:^BOOL( + MDCSnackbarMessage* message) { + EXPECT_NSEQ(expectedMessageText, message.text); + // Check that action is not nil, "Got it" button is present. + EXPECT_NE(message.action, nil); + if (message.action) { + EXPECT_NSEQ(expectedButtonText, message.action.title); + // Check that handler is nil, verifies the button can be tapped. + EXPECT_EQ(message.action.handler, nil); + } + return YES; + }]]); + + [mediator_ bannerInfobarButtonWasPressed:nil]; +} + +// Tests that no snackbar is shown when the save is for upload. +TEST_F(SaveCardInfobarBannerOverlayMediatorTest, NoSnackbarForUploadSave) { + InitInfobar(/*for_upload=*/true); + + OCMExpect([mediator_ presentInfobarModalFromBanner]); + // No expectation on mock_snackbar_commands_handler_ as it shouldn't be + // called. + OCMReject([mock_snackbar_commands_handler_ showSnackbarMessage:OCMOCK_ANY]); + + [mediator_ bannerInfobarButtonWasPressed:nil]; +}
diff --git a/ios/chrome/browser/shared/model/profile/test/test_profile_ios.mm b/ios/chrome/browser/shared/model/profile/test/test_profile_ios.mm index 7bbcc3cf..d3874df 100644 --- a/ios/chrome/browser/shared/model/profile/test/test_profile_ios.mm +++ b/ios/chrome/browser/shared/model/profile/test/test_profile_ios.mm
@@ -18,6 +18,7 @@ #import "base/task/single_thread_task_runner.h" #import "base/task/thread_pool.h" #import "base/test/test_file_util.h" +#import "base/threading/thread_restrictions.h" #import "components/keyed_service/ios/browser_state_dependency_manager.h" #import "components/policy/core/common/cloud/user_cloud_policy_manager.h" #import "components/profile_metrics/browser_profile_type.h"
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 index 9852ab7..cfb45c12 100644 --- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@ -5d52b7527c90688f85e727c212ff3b7326129343 \ No newline at end of file +c26b9b68912804c7dab128431caf4f4e28fb158f \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 index 77c5b92..a506bbdb 100644 --- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@ -a78f1b487fddbdedee4d43c03da9342fe774c9e7 \ No newline at end of file +bf0e8c218a3c3a26942e4e0397b8bb8f19e57b28 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 index d5acc99..770f98b79 100644 --- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@ -bf2609e4986518aac1da3b4f828eb858c446fe2d \ No newline at end of file +f593c24eaaed2905d207f6a68885d443648ef08e \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 index 9cdcbfc..86b2b21 100644 --- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@ -17a901a752ca392c6f5b8593bc5688b70b22a87c \ No newline at end of file +b01f339dbb8878cc01b2293089c017a5c7137a82 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 index c91e7ba..386d7251 100644 --- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@ -216150115cfd73007114d62dd4354c4d6ab6ab57 \ No newline at end of file +a2e173c831b9a7359c92687120418cbfad38e18d \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 index c5ae027f..fb35988 100644 --- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@ -c8ed7e8e99083121036732ee2198b9baa7abd00b \ No newline at end of file +6510b59b87094d5ad124ca32085d8346ecc1099d \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 index cb49aa0..350afb4 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -8ce158591387de46f60cdb89ae594428544796da \ No newline at end of file +10c72e95dff8ac43e9e84bb75c670ab3722fb8d1 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 index 1289db3..36109b94 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -8fd1d1cc7bac655960e28f956c894e353d5ba75a \ No newline at end of file +8ab62e2396653b9d815c438a404b7fc364094300 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 index 2e997140..bf7b788 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -e770dd4565e3857bcfedd6dc7425069c8e6daa87 \ No newline at end of file +d7dba46a08bca6c2dd8f25e4c98bd7af98737b58 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 index 4356485..1a53b59 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -24496e7355dd18e15d5006faf417c5eb09732a43 \ No newline at end of file +1a5b59714e963f631102ddc282e6df42fa6b471d \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 index a747f546..f94e5c6 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -7440a6cb51766464579c8abedf05f04e420cf8e9 \ No newline at end of file +bfeeed4d420bffdf0e23a39ebb8efcdb4f942e2b \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 index 8a9df16..0c54c5a 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -4f424c0a46424414fce4ffc20f16375437fdbf3e \ No newline at end of file +700dbb4f88f49e1e8643a0294046d88850be0c96 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 index 39d6456a..40698ac 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -8d7772b5c5d66f51bb4cb022d689e407a6b11501 \ No newline at end of file +57ce7de86299fba51b2fd88634b536d83a755a04 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 index b252810..0262323 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -4dc57da753a930e8a762eb20b82f3c344b9f427d \ No newline at end of file +fe8c2a14ecb9de13a83bd5f4e8996033a94d3313 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 index c9ca5715..28a7c7f 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -3e1c483d0192994925f8c9acfcbe9941d8b8e01b \ No newline at end of file +db9d9171c9caef635a3dfcc0000399d99454f31a \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 index afaca82..1c6b567 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -4e2766cdb2ab908217d870907c069a02bae7b528 \ No newline at end of file +e6247a2ab6491868b434335434eee486027756e9 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 index 64957bec..1e3e9ea 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -4375a92a996ced8c38e34f7737531f0a7992d7e8 \ No newline at end of file +ff4911a6c532100a44496e8c3ca9b11abb0fb9a2 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 index c2afa078..9dde8c1 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -6f3806dc086bf321df97c877cb75ae78fd77b53d \ No newline at end of file +bcce77eb2e221a4a7ff02d3cfdd9ca3649af2004 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 index 4e1786bf7..c5de915 100644 --- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -98a61905f7f4d3e3988b3bd205c75b96cacf0c0a \ No newline at end of file +6d1de83744f3d84db19d7c72ac5fc9126a66443e \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 index 13faeb3..bbca019 100644 --- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -c2bd50dbfaaf65e280ec3918608674d35148c22d \ No newline at end of file +cc371071531dbedb97dc06281946766122386938 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 index 6a7c205..e5d4bb8 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -dbdfbdd3574c775d8a0eea9cab7d1617cd0df848 \ No newline at end of file +687a533646a8f0eabc75e630a7e26b9086e0d3d0 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 index 58ee1390..31214a9 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -2e1a758312ffb1a74535a1652746c7c1caf2ad27 \ No newline at end of file +21dfb7a27cd1e3ba0193020020dcd6566b6613ef \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 index f8d97a9..7024081 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -9bf769b5baf74730a6c141aeec79af8dce382e9b \ No newline at end of file +ebb5e2651a2855282639497a8d5529aaff373b8e \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 index 5f494916..8d8271da 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -d6b6db16b57464f52d4b0629f5a657e088679a21 \ No newline at end of file +40ec0a3f19f211488a2a516e700105b786420d83 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 index 289cf38..b26b431e 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -407a349b67c7b2b073ee4fc7f2e78063ac0b476a \ No newline at end of file +a1ad983101f194b232e9900f547e055f67fbad82 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 index d3f4da07..0964cc2f 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -85709b6edd99a71c4f8df626f86d8038bf818854 \ No newline at end of file +8e1d3423c7d4477fc83bd493027bbabfbd57d9e2 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 index 41d3691..26249db7 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -941de4e0f01f6f6d8cf2339b69fecfc42dfba478 \ No newline at end of file +25d62251d9382a8451cff5bcbe3636bfab90f602 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 index 1d24c7e..e5b38896 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -56b24be303660dfd1b94ffdb3bb6fc404f0e8c93 \ No newline at end of file +fdb10eff3966dfafcea5778af57c9a9fa33bb23d \ No newline at end of file
diff --git a/ios/third_party/edo/src b/ios/third_party/edo/src index 4d0798b..5030b6a 160000 --- a/ios/third_party/edo/src +++ b/ios/third_party/edo/src
@@ -1 +1 @@ -Subproject commit 4d0798b9c79a7c0d7d553603fde0453342c70878 +Subproject commit 5030b6a79c86f3b0787061f9c933b7476631efe3
diff --git a/ios_internal b/ios_internal index 3039cfb..66f62a3 160000 --- a/ios_internal +++ b/ios_internal
@@ -1 +1 @@ -Subproject commit 3039cfbc4bd2cbf1cf1f0425581e86e7394cc794 +Subproject commit 66f62a3e6eaa6a83cdb803293328c40ab5db35aa
diff --git a/ipc/ipc.mojom b/ipc/ipc.mojom index 1b6d16e..c190a5b2 100644 --- a/ipc/ipc.mojom +++ b/ipc/ipc.mojom
@@ -14,13 +14,18 @@ array<mojo.native.SerializedHandle>? handles; }; +feature kMojoIpcChannelReceive { + const string name = "MojoIpcChannelReceive"; + const bool default_state = true; +}; + interface Channel { // Informs the remote end of this client's PID. Must be called exactly once, // before any calls to Receive() below. SetPeerPid(int32 pid); // Transmits a classical Chrome IPC message. - [UnlimitedSize] + [UnlimitedSize, RuntimeFeature=kMojoIpcChannelReceive] Receive(Message message); // Requests a Channel-associated interface. @@ -31,4 +36,3 @@ // A strictly nominal interface used to identify Channel bootstrap requests. // This is only used in `AgentSchedulingGroup` initialization. interface ChannelBootstrap {}; -
diff --git a/media/base/byte_queue.h b/media/base/byte_queue.h index be3ebc2..380d95aa 100644 --- a/media/base/byte_queue.h +++ b/media/base/byte_queue.h
@@ -44,7 +44,7 @@ // Get a read-only span view of the data. This is only valid until the next // Push() or Pop() call. - base::span<const uint8_t> Data() { return data_; } + base::span<const uint8_t> Data() const { return data_; } private: // Offset from the start of |buffer_| that marks the front of the queue.
diff --git a/media/formats/common/offset_byte_queue.cc b/media/formats/common/offset_byte_queue.cc index 521a2ee5..9a9865d 100644 --- a/media/formats/common/offset_byte_queue.cc +++ b/media/formats/common/offset_byte_queue.cc
@@ -27,7 +27,7 @@ return true; } -base::span<const uint8_t> OffsetByteQueue::Data() { +base::span<const uint8_t> OffsetByteQueue::Data() const { return queue_.Data().empty() ? base::span<const uint8_t>() : queue_.Data(); }
diff --git a/media/formats/common/offset_byte_queue.h b/media/formats/common/offset_byte_queue.h index a7c2913..62e55c0b 100644 --- a/media/formats/common/offset_byte_queue.h +++ b/media/formats/common/offset_byte_queue.h
@@ -30,7 +30,7 @@ // These work like their underlying ByteQueue counterparts. void Reset(); [[nodiscard]] bool Push(base::span<const uint8_t> buf); - base::span<const uint8_t> Data(); + base::span<const uint8_t> Data() const; void Pop(int count); // Get a read-only span view of the data after offset. This view is valid only @@ -49,8 +49,8 @@ // The head and tail positions, in terms of the file's absolute offsets. // tail() is an exclusive bound. - int64_t head() { return head_; } - int64_t tail() { return head_ + queue_.Data().size(); } + int64_t head() const { return head_; } + int64_t tail() const { return head_ + queue_.Data().size(); } private: ByteQueue queue_;
diff --git a/media/renderers/win/media_foundation_renderer_integration_test.cc b/media/renderers/win/media_foundation_renderer_integration_test.cc index 9df9525f..2ed2ad7d 100644 --- a/media/renderers/win/media_foundation_renderer_integration_test.cc +++ b/media/renderers/win/media_foundation_renderer_integration_test.cc
@@ -7,15 +7,15 @@ #pragma allow_unsafe_buffers #endif -#include "media/renderers/win/media_foundation_renderer.h" - #include <mfapi.h> #include <memory> +#include "base/win/scoped_co_mem.h" #include "base/win/windows_version.h" #include "media/base/media_util.h" #include "media/base/supported_types.h" +#include "media/renderers/win/media_foundation_renderer.h" #include "media/test/pipeline_integration_test_base.h" #include "media/test/test_media_source.h" @@ -37,7 +37,7 @@ bool CanDecodeVideoCodec(VideoCodec codec) { auto codecs = GetVideoCodecsMap(); MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Video, codecs[codec]}; - IMFActivate** activates = nullptr; + base::win::ScopedCoMem<IMFActivate*> activates; UINT32 count = 0; if (FAILED(MFTEnumEx(MFT_CATEGORY_VIDEO_DECODER, @@ -51,7 +51,6 @@ for (UINT32 i = 0; i < count; ++i) { activates[i]->Release(); } - CoTaskMemFree(activates); if (count == 0) { LOG(WARNING) << "No decoder for " << media::GetCodecName(codec);
diff --git a/media/video/gpu_memory_buffer_video_frame_pool.cc b/media/video/gpu_memory_buffer_video_frame_pool.cc index 82f8f5b8..9f67288 100644 --- a/media/video/gpu_memory_buffer_video_frame_pool.cc +++ b/media/video/gpu_memory_buffer_video_frame_pool.cc
@@ -789,10 +789,6 @@ bool copy_failed, scoped_refptr<VideoFrame> video_frame, FrameResource* frame_resource) { - if (!copy_failed && frame_resource->scoped_mapping) { - frame_resource->scoped_mapping.reset(); - } - TRACE_EVENT_NESTABLE_ASYNC_END0( "media", "CopyVideoFrameToGpuMemoryBuffer", TRACE_ID_WITH_SCOPE("CopyVideoFrameToGpuMemoryBuffer", @@ -978,6 +974,9 @@ scoped_refptr<VideoFrame> video_frame, FrameResource* frame_resource) { DCHECK(media_task_runner_->RunsTasksInCurrentSequence()); + if (frame_resource->scoped_mapping) { + frame_resource->scoped_mapping.reset(); + } if (copy_failed) { // Drop the resource if there was an error with it. If we're not in // shutdown we also need to remove the pool entry for the resource.
diff --git a/mojo/core/ipcz_driver/data_pipe.cc b/mojo/core/ipcz_driver/data_pipe.cc index b286db2..188f37b 100644 --- a/mojo/core/ipcz_driver/data_pipe.cc +++ b/mojo/core/ipcz_driver/data_pipe.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/core/ipcz_driver/data_pipe.h" #include <algorithm> @@ -18,6 +13,7 @@ #include <tuple> #include "base/check.h" +#include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "base/memory/unsafe_shared_memory_region.h" #include "base/numerics/safe_math.h" @@ -231,7 +227,7 @@ FlushUpdatesFromPeer(); const base::span<const uint8_t> input_bytes = - base::span(static_cast<const uint8_t*>(elements), num_bytes); + UNSAFE_TODO(base::span(static_cast<const uint8_t*>(elements), num_bytes)); scoped_refptr<PortalWrapper> portal; size_t write_size; { @@ -350,7 +346,8 @@ return MOJO_RESULT_INVALID_ARGUMENT; } - output_bytes = base::span(static_cast<uint8_t*>(elements), num_bytes); + output_bytes = + UNSAFE_TODO(base::span(static_cast<uint8_t*>(elements), num_bytes)); } size_t read_size = num_bytes; @@ -505,7 +502,7 @@ // SharedBuffer object. DCHECK_GE(data.size(), sizeof(DataPipeHeader)); auto& header = *reinterpret_cast<DataPipeHeader*>(data.data()); - memset(&header, 0, sizeof(header)); + UNSAFE_TODO(memset(&header, 0, sizeof(header))); header.size = sizeof(header); header.endpoint_type = endpoint_type_; header.element_size = base::checked_cast<uint32_t>(element_size_);
diff --git a/mojo/core/ipcz_driver/driver.cc b/mojo/core/ipcz_driver/driver.cc index 48de532..a6bda7d 100644 --- a/mojo/core/ipcz_driver/driver.cc +++ b/mojo/core/ipcz_driver/driver.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/core/ipcz_driver/driver.h" #include <cstddef> @@ -14,6 +9,7 @@ #include <tuple> #include <utility> +#include "base/compiler_specific.h" #include "base/containers/span.h" #include "base/memory/unsafe_shared_memory_region.h" #include "base/rand_util.h" @@ -88,9 +84,10 @@ // `data`. scoped_refptr<ObjectBase> object; const IpczResult result = transport->DeserializeObject( - base::span(static_cast<const uint8_t*>(const_cast<const void*>(data)), - num_bytes), - base::span(handles, num_handles), object); + UNSAFE_TODO( + base::span(static_cast<const uint8_t*>(const_cast<const void*>(data)), + num_bytes)), + UNSAFE_TODO(base::span(handles, num_handles)), object); if (result != IPCZ_RESULT_OK) { return result; } @@ -210,8 +207,9 @@ return IPCZ_RESULT_INVALID_ARGUMENT; } - transport->Transmit(base::span(static_cast<const uint8_t*>(data), num_bytes), - base::span(handles, num_handles)); + transport->Transmit( + UNSAFE_TODO(base::span(static_cast<const uint8_t*>(data), num_bytes)), + UNSAFE_TODO(base::span(handles, num_handles))); return IPCZ_RESULT_OK; }
diff --git a/mojo/core/ipcz_driver/mojo_message.cc b/mojo/core/ipcz_driver/mojo_message.cc index 014253f..469eefd 100644 --- a/mojo/core/ipcz_driver/mojo_message.cc +++ b/mojo/core/ipcz_driver/mojo_message.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/core/ipcz_driver/mojo_message.h" #include <algorithm> @@ -14,6 +9,7 @@ #include <cstdint> #include <utility> +#include "base/compiler_specific.h" #include "base/containers/span.h" #include "base/numerics/safe_conversions.h" #include "mojo/core/ipcz_api.h" @@ -134,11 +130,12 @@ // correct in that case; and in any other case we don't care what's copied, // as long as all subsequent reads operate on the private copy and not on // `data`. - memcpy(data_storage_.get(), const_cast<const void*>(data), num_bytes); + UNSAFE_TODO( + memcpy(data_storage_.get(), const_cast<const void*>(data), num_bytes)); } else { data_storage_.reset(); } - data_ = {data_storage_.get(), num_bytes}; + data_ = UNSAFE_TODO({data_storage_.get(), num_bytes}); data_storage_size_ = num_bytes; result = GetIpczAPI().EndGet(parcel_.get(), transaction, IPCZ_NO_FLAGS, @@ -167,7 +164,7 @@ data_storage_size_ = std::max(payload_buffer_size, uint32_t{kMinBufferSize}); DataPtr new_storage(new uint8_t[data_storage_size_]); data_storage_ = std::move(new_storage); - data_ = base::span(data_storage_.get(), 0u); + data_ = UNSAFE_TODO(base::span(data_storage_.get(), 0u)); if (buffer_size) { *buffer_size = base::checked_cast<uint32_t>(data_storage_size_); @@ -194,14 +191,14 @@ data_storage_size_ = std::max(data_size * kGrowthFactor, required_storage_size); DataPtr new_storage(new uint8_t[data_storage_size_]); - std::ranges::copy(base::span(data_storage_.get(), copy_size), + std::ranges::copy(UNSAFE_TODO(base::span(data_storage_.get(), copy_size)), new_storage.get()); data_storage_ = std::move(new_storage); } - data_ = base::span(data_storage_.get(), new_data_size); + data_ = UNSAFE_TODO(base::span(data_storage_.get(), new_data_size)); handles_.reserve(handles_.size() + num_handles); - for (MojoHandle handle : base::span(handles, num_handles)) { + for (MojoHandle handle : UNSAFE_TODO(base::span(handles, num_handles))) { handles_.push_back(handle); } if (buffer) { @@ -434,9 +431,9 @@ } // TODO(crbug.com/40270656): Do a volatile-friendly copy here. - memcpy(const_cast<void*>(data), data_.data(), data_.size()); + UNSAFE_TODO(memcpy(const_cast<void*>(data), data_.data(), data_.size())); for (size_t i = 0; i < handles_.size(); ++i) { - handles[i] = std::exchange(handles_[i], IPCZ_INVALID_HANDLE); + UNSAFE_TODO(handles[i]) = std::exchange(handles_[i], IPCZ_INVALID_HANDLE); } return IPCZ_RESULT_OK; }
diff --git a/mojo/core/ipcz_driver/mojo_trap.cc b/mojo/core/ipcz_driver/mojo_trap.cc index c1e4012e..66c2229 100644 --- a/mojo/core/ipcz_driver/mojo_trap.cc +++ b/mojo/core/ipcz_driver/mojo_trap.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/core/ipcz_driver/mojo_trap.h" #include <cstdint> @@ -15,6 +10,7 @@ #include <utility> #include "base/check_op.h" +#include "base/compiler_specific.h" #include "base/feature_list.h" #include "base/memory/ref_counted.h" #include "base/notreached.h" @@ -357,7 +353,7 @@ return MOJO_RESULT_FAILED_PRECONDITION; } - blocking_events[num_events_returned++] = event; + UNSAFE_TODO(blocking_events[num_events_returned++]) = event; } while (next_trigger != end_trigger && (num_events_returned == 0 || num_events_returned < event_capacity));
diff --git a/mojo/core/test/test_support_impl.cc b/mojo/core/test/test_support_impl.cc index 9dc0030..3e6e70c 100644 --- a/mojo/core/test/test_support_impl.cc +++ b/mojo/core/test/test_support_impl.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/core/test/test_support_impl.h" #include <stddef.h> @@ -17,6 +12,7 @@ #include <string_view> #include "base/check.h" +#include "base/compiler_specific.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/files/file_util.h" @@ -80,7 +76,7 @@ // |names.size() + 1| for null terminator. char** rv = static_cast<char**>(calloc(names.size() + 1, sizeof(char*))); for (size_t i = 0; i < names.size(); ++i) - rv[i] = base::strdup(names[i].c_str()); + UNSAFE_TODO(rv[i]) = UNSAFE_TODO(base::strdup(names[i].c_str())); return rv; }
diff --git a/mojo/core/trap_unittest.cc b/mojo/core/trap_unittest.cc index 585cbb6..efbce4bd 100644 --- a/mojo/core/trap_unittest.cc +++ b/mojo/core/trap_unittest.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/c/system/trap.h" #include <stdint.h> @@ -16,6 +11,7 @@ #include <memory> #include <set> +#include "base/compiler_specific.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/memory/ptr_util.h" @@ -1875,7 +1871,7 @@ } MojoHandle RandomHandle(MojoHandle* handles, size_t size) { - return handles[base::RandInt(0, static_cast<int>(size) - 1)]; + return UNSAFE_TODO(handles[base::RandInt(0, static_cast<int>(size) - 1)]); } void DoRandomThing(MojoHandle* traps, @@ -1955,9 +1951,10 @@ &DoRandomThing, traps, kNumTraps, watched_handles, kNumWatchedHandles); for (size_t i = 0; i < kNumTraps; ++i) - MojoCreateTrap(&ReadAllMessages, nullptr, &traps[i]); + MojoCreateTrap(&ReadAllMessages, nullptr, &UNSAFE_TODO(traps[i])); for (size_t i = 0; i < kNumWatchedHandles; i += 2) - CreateMessagePipe(&watched_handles[i], &watched_handles[i + 1]); + CreateMessagePipe(&UNSAFE_TODO(watched_handles[i]), + &UNSAFE_TODO(watched_handles[i + 1])); std::array<std::unique_ptr<ThreadedRunner>, kNumThreads> threads; for (size_t i = 0; i < kNumThreads; ++i) { @@ -1970,9 +1967,9 @@ for (size_t i = 0; i < kNumThreads; ++i) threads[i]->Join(); for (size_t i = 0; i < kNumTraps; ++i) - MojoClose(traps[i]); + MojoClose(UNSAFE_TODO(traps[i])); for (size_t i = 0; i < kNumWatchedHandles; ++i) - MojoClose(watched_handles[i]); + MojoClose(UNSAFE_TODO(watched_handles[i])); } } // namespace
diff --git a/mojo/public/c/system/tests/core_api_unittest.cc b/mojo/public/c/system/tests/core_api_unittest.cc index 880b28d..ce705b6 100644 --- a/mojo/public/c/system/tests/core_api_unittest.cc +++ b/mojo/public/c/system/tests/core_api_unittest.cc
@@ -2,19 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - // This file tests the C API. -#include "mojo/public/c/system/core.h" - #include <stdint.h> #include <string.h> +#include "base/compiler_specific.h" #include "mojo/core/embedder/embedder.h" +#include "mojo/public/c/system/core.h" #include "mojo/public/cpp/system/message_pipe.h" #include "mojo/public/cpp/system/wait.h" #include "testing/gtest/include/gtest/gtest.h" @@ -219,13 +214,13 @@ static const char kWorld[] = "world"; ASSERT_GE(buffer_size, sizeof(kWorld)); // Include the terminating null. - memcpy(write_pointer, kWorld, sizeof(kWorld)); + UNSAFE_TODO(memcpy(write_pointer, kWorld, sizeof(kWorld))); EXPECT_EQ( MOJO_RESULT_OK, MojoEndWriteData(hp, static_cast<uint32_t>(sizeof(kWorld)), nullptr)); // Read one character from |hc|. - memset(buffer, 0, sizeof(buffer)); + UNSAFE_TODO(memset(buffer, 0, sizeof(buffer))); buffer_size = 1; EXPECT_EQ(MOJO_RESULT_OK, MojoReadData(hc, nullptr, buffer, &buffer_size)); @@ -252,7 +247,7 @@ } ASSERT_EQ(result, MOJO_RESULT_OK); ASSERT_LE(buffer_size, sizeof(buffer) - 1); - memcpy(&buffer[read_offset], read_pointer, buffer_size); + UNSAFE_TODO(memcpy(&buffer[read_offset], read_pointer, buffer_size)); EXPECT_EQ(MOJO_RESULT_OK, MojoEndReadData(hc, buffer_size, nullptr)); read_offset += buffer_size; } @@ -291,7 +286,7 @@ void* pointer = nullptr; EXPECT_EQ(MOJO_RESULT_OK, MojoMapBuffer(h0, 0, 100, nullptr, &pointer)); ASSERT_TRUE(pointer); - static_cast<char*>(pointer)[50] = 'x'; + UNSAFE_TODO(static_cast<char*>(pointer)[50]) = 'x'; // Duplicate |h0| to |h1|. MojoHandle h1 = MOJO_HANDLE_INVALID; @@ -302,7 +297,7 @@ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0)); // The mapping should still be good. - static_cast<char*>(pointer)[51] = 'y'; + UNSAFE_TODO(static_cast<char*>(pointer)[51]) = 'y'; // Unmap it. EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(pointer)); @@ -314,7 +309,7 @@ // It should have what we wrote. EXPECT_EQ('x', static_cast<char*>(pointer)[0]); - EXPECT_EQ('y', static_cast<char*>(pointer)[1]); + UNSAFE_TODO(EXPECT_EQ('y', static_cast<char*>(pointer)[1])); // Unmap it. EXPECT_EQ(MOJO_RESULT_OK, MojoUnmapBuffer(pointer));
diff --git a/mojo/public/c/system/thunks.cc b/mojo/public/c/system/thunks.cc index b735bb3..e59ed45 100644 --- a/mojo/public/c/system/thunks.cc +++ b/mojo/public/c/system/thunks.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/c/system/thunks.h" #include <cstddef> @@ -563,7 +558,7 @@ uint32_t* buffer_size) { std::vector<MojoHandle> handles64(num_handles); for (size_t i = 0; i < num_handles; ++i) { - handles64[i] = handles[i]; + handles64[i] = UNSAFE_TODO(handles[i]); } return MojoAppendMessageData(message, payload_size, handles64.data(), num_handles, options, buffer, buffer_size); @@ -580,7 +575,7 @@ handles64.data(), num_handles); if (result == MOJO_RESULT_OK && num_handles) { for (size_t i = 0; i < *num_handles; ++i) { - handles[i] = static_cast<MojoHandle32>(handles64[i]); + UNSAFE_TODO(handles[i]) = static_cast<MojoHandle32>(handles64[i]); } } return result; @@ -770,7 +765,8 @@ // This should only have to check that the |g_thunks->size| is zero, but we // have multiple Mojo Core initializations in some test suites still. For now // we allow double calls as long as they're the same thunks as before. - DCHECK(g_thunks.size == 0 || !memcmp(&g_thunks, thunks, sizeof(g_thunks))) + UNSAFE_TODO(DCHECK(g_thunks.size == 0 || + !memcmp(&g_thunks, thunks, sizeof(g_thunks)))) << "Cannot set embedder thunks after Mojo API calls have been made."; g_thunks = *thunks;
diff --git a/mojo/public/cpp/base/big_buffer.cc b/mojo/public/cpp/base/big_buffer.cc index 372ef10..4d286e9 100644 --- a/mojo/public/cpp/base/big_buffer.cc +++ b/mojo/public/cpp/base/big_buffer.cc
@@ -2,17 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/cpp/base/big_buffer.h" #include <algorithm> #include <utility> #include "base/check.h" +#include "base/compiler_specific.h" #include "base/containers/heap_array.h" #include "base/notreached.h" #include "third_party/perfetto/include/perfetto/tracing/traced_value.h" @@ -190,9 +186,10 @@ return bytes_; } else if (storage_type_ == BigBuffer::StorageType::kSharedMemory) { DCHECK(shared_memory_.has_value()); - return base::span(static_cast<const uint8_t*>( - const_cast<const void*>(shared_memory_->memory())), - shared_memory_->size()); + return UNSAFE_TODO( + base::span(static_cast<const uint8_t*>( + const_cast<const void*>(shared_memory_->memory())), + shared_memory_->size())); } return base::span<const uint8_t>();
diff --git a/mojo/public/cpp/base/big_buffer_unittest.cc b/mojo/public/cpp/base/big_buffer_unittest.cc index d91c8c8..98ece47c 100644 --- a/mojo/public/cpp/base/big_buffer_unittest.cc +++ b/mojo/public/cpp/base/big_buffer_unittest.cc
@@ -2,16 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif +#include "mojo/public/cpp/base/big_buffer.h" #include <algorithm> #include <vector> +#include "base/compiler_specific.h" #include "base/rand_util.h" -#include "mojo/public/cpp/base/big_buffer.h" #include "mojo/public/cpp/base/big_buffer_mojom_traits.h" #include "mojo/public/cpp/test_support/test_utils.h" #include "mojo/public/mojom/base/big_buffer.mojom.h" @@ -23,8 +20,9 @@ namespace { bool BufferEquals(const BigBuffer& a, const BigBuffer& b) { - return a.size() == b.size() && std::equal(a.data(), a.data() + a.size(), - b.data(), b.data() + b.size()); + return a.size() == b.size() && + std::equal(a.data(), UNSAFE_TODO(a.data() + a.size()), b.data(), + UNSAFE_TODO(b.data() + b.size())); } } // namespace
diff --git a/mojo/public/cpp/base/big_string_mojom_traits.cc b/mojo/public/cpp/base/big_string_mojom_traits.cc index 12f2b03..f0ec771 100644 --- a/mojo/public/cpp/base/big_string_mojom_traits.cc +++ b/mojo/public/cpp/base/big_string_mojom_traits.cc
@@ -2,13 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/cpp/base/big_string_mojom_traits.h" +#include "base/compiler_specific.h" #include "mojo/public/cpp/base/big_buffer_mojom_traits.h" namespace mojo { @@ -17,7 +13,7 @@ mojo_base::BigBuffer StructTraits<mojo_base::mojom::BigStringDataView, std::string>::data(const std::string& str) { const auto* bytes = reinterpret_cast<const uint8_t*>(str.data()); - return mojo_base::BigBuffer(base::span(bytes, str.size())); + return mojo_base::BigBuffer(UNSAFE_TODO(base::span(bytes, str.size()))); } // static
diff --git a/mojo/public/cpp/base/proto_wrapper.cc b/mojo/public/cpp/base/proto_wrapper.cc index 7659203a..4cd9f2f 100644 --- a/mojo/public/cpp/base/proto_wrapper.cc +++ b/mojo/public/cpp/base/proto_wrapper.cc
@@ -2,16 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/cpp/base/proto_wrapper.h" #include <limits> #include "base/check_op.h" +#include "base/compiler_specific.h" #include "third_party/protobuf/src/google/protobuf/message_lite.h" namespace mojo_base { @@ -61,7 +57,7 @@ } else { // Make an in-process copy here as protobuf is not designed to // safely parse data that might be changing underneath it. - auto as_span = base::span(bytes_->data(), bytes_->size()); + auto as_span = UNSAFE_TODO(base::span(bytes_->data(), bytes_->size())); const std::vector<uint8_t> copy(as_span.begin(), as_span.end()); return message.ParseFromArray(copy.data(), copy.size()); }
diff --git a/mojo/public/cpp/base/proto_wrapper_passkeys.h b/mojo/public/cpp/base/proto_wrapper_passkeys.h index a69c47a4..fb7d63c 100644 --- a/mojo/public/cpp/base/proto_wrapper_passkeys.h +++ b/mojo/public/cpp/base/proto_wrapper_passkeys.h
@@ -15,7 +15,7 @@ } // namespace component_updater namespace glic { -class GlicActorController; +class GlicPageContextFetcher; } // namespace glic namespace paint_preview { @@ -43,7 +43,7 @@ friend class component_updater::ReadMaskedDomainListProto; friend class component_updater::PKIMetadataComponentInstallerService; friend class component_updater::MaskedDomainListComponentInstallerTest; - friend class glic::GlicActorController; + friend class glic::GlicPageContextFetcher; // Tests. FRIEND_TEST_ALL_PREFIXES(ProtoWrapperTest, ToFromBytes);
diff --git a/mojo/public/cpp/base/ref_counted_memory_unittest.cc b/mojo/public/cpp/base/ref_counted_memory_unittest.cc index 682dadd..32a75f98 100644 --- a/mojo/public/cpp/base/ref_counted_memory_unittest.cc +++ b/mojo/public/cpp/base/ref_counted_memory_unittest.cc
@@ -2,11 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - +#include "base/compiler_specific.h" #include "mojo/public/cpp/base/big_buffer_mojom_traits.h" #include "mojo/public/cpp/base/ref_counted_memory_mojom_traits.h" #include "mojo/public/cpp/test_support/test_utils.h" @@ -25,7 +21,7 @@ mojo::test::SerializeAndDeserialize<mojom::RefCountedMemory>(in, out)); ASSERT_EQ(out->size(), in->size()); for (size_t i = 0; i < out->size(); ++i) - EXPECT_EQ(in->front()[i], out->front()[i]); + UNSAFE_TODO(EXPECT_EQ(in->front()[i], out->front()[i])); } TEST(RefCountedMemoryTest, Null) {
diff --git a/mojo/public/cpp/base/string16_mojom_traits.cc b/mojo/public/cpp/base/string16_mojom_traits.cc index 60e4064d..1f5f5432 100644 --- a/mojo/public/cpp/base/string16_mojom_traits.cc +++ b/mojo/public/cpp/base/string16_mojom_traits.cc
@@ -2,13 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/cpp/base/string16_mojom_traits.h" +#include "base/compiler_specific.h" #include "mojo/public/cpp/base/big_buffer_mojom_traits.h" namespace mojo { @@ -28,7 +24,8 @@ StructTraits<mojo_base::mojom::BigString16DataView, std::u16string>::data( const std::u16string& str) { const auto* bytes = reinterpret_cast<const uint8_t*>(str.data()); - return mojo_base::BigBuffer(base::span(bytes, str.size() * sizeof(char16_t))); + return mojo_base::BigBuffer( + UNSAFE_TODO(base::span(bytes, str.size() * sizeof(char16_t)))); } // static
diff --git a/mojo/public/cpp/base/values_mojom_traits.cc b/mojo/public/cpp/base/values_mojom_traits.cc index 37fd982..0010fc9 100644 --- a/mojo/public/cpp/base/values_mojom_traits.cc +++ b/mojo/public/cpp/base/values_mojom_traits.cc
@@ -2,16 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/cpp/base/values_mojom_traits.h" #include <memory> #include <utility> +#include "base/compiler_specific.h" + namespace mojo { bool StructTraits< @@ -77,7 +74,7 @@ const char* data_pointer = reinterpret_cast<const char*>(binary_data_view.data()); base::Value::BlobStorage blob_storage( - data_pointer, data_pointer + binary_data_view.size()); + data_pointer, UNSAFE_TODO(data_pointer + binary_data_view.size())); *value_out = base::Value(std::move(blob_storage)); return true; }
diff --git a/mojo/public/cpp/bindings/lib/buffer.cc b/mojo/public/cpp/bindings/lib/buffer.cc index 856287e5..d6360bf 100644 --- a/mojo/public/cpp/bindings/lib/buffer.cc +++ b/mojo/public/cpp/bindings/lib/buffer.cc
@@ -2,17 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/cpp/bindings/lib/buffer.h" #include <cstring> #include "base/check.h" #include "base/check_op.h" +#include "base/compiler_specific.h" #include "base/notreached.h" #include "base/numerics/safe_conversions.h" #include "mojo/public/cpp/bindings/lib/bindings_internal.h" @@ -88,7 +84,8 @@ // TODO(rockot): We should consider only clearing the alignment padding. This // means being careful about generated bindings zeroing padding explicitly, // which itself gets particularly messy with e.g. packed bool bitfields. - memset(static_cast<uint8_t*>(data_) + block_start, 0, aligned_num_bytes); + UNSAFE_TODO( + memset(static_cast<uint8_t*>(data_) + block_start, 0, aligned_num_bytes)); return block_start; }
diff --git a/mojo/public/cpp/bindings/lib/message_dumper.cc b/mojo/public/cpp/bindings/lib/message_dumper.cc index 282f3b1..33e31a12 100644 --- a/mojo/public/cpp/bindings/lib/message_dumper.cc +++ b/mojo/public/cpp/bindings/lib/message_dumper.cc
@@ -2,13 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/377326291): Fix and remove. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/cpp/bindings/message_dumper.h" +#include "base/compiler_specific.h" #include "base/files/file.h" #include "base/files/file_path.h" #include "base/files/file_util.h" @@ -65,7 +61,7 @@ const char* method_name) : interface_name(interface_name), method_name(method_name), - data_bytes(data, data + data_size) {} + data_bytes(data, UNSAFE_TODO(data + data_size)) {} MessageDumper::MessageEntry::MessageEntry(const MessageEntry& entry) = default;
diff --git a/mojo/public/cpp/bindings/lib/message_header_validator.cc b/mojo/public/cpp/bindings/lib/message_header_validator.cc index 93b6304..026b7fb 100644 --- a/mojo/public/cpp/bindings/lib/message_header_validator.cc +++ b/mojo/public/cpp/bindings/lib/message_header_validator.cc
@@ -2,13 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/cpp/bindings/message_header_validator.h" +#include "base/compiler_specific.h" #include "mojo/public/cpp/bindings/lib/array_internal.h" #include "mojo/public/cpp/bindings/lib/validate_params.h" #include "mojo/public/cpp/bindings/lib/validation_context.h" @@ -101,7 +97,8 @@ size_t num_ids = header_v2->payload_interface_ids.Get()->size(); const uint32_t* ids = header_v2->payload_interface_ids.Get()->storage(); for (size_t i = 0; i < num_ids; ++i) { - if (!IsValidInterfaceId(ids[i]) || IsPrimaryInterfaceId(ids[i])) { + if (!IsValidInterfaceId(UNSAFE_TODO(ids[i])) || + IsPrimaryInterfaceId(UNSAFE_TODO(ids[i]))) { internal::ReportValidationError( validation_context, internal::VALIDATION_ERROR_ILLEGAL_INTERFACE_ID);
diff --git a/mojo/public/cpp/bindings/lib/native_struct_serialization.cc b/mojo/public/cpp/bindings/lib/native_struct_serialization.cc index 4f42611..bfbffa8 100644 --- a/mojo/public/cpp/bindings/lib/native_struct_serialization.cc +++ b/mojo/public/cpp/bindings/lib/native_struct_serialization.cc
@@ -2,13 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/390223051): Remove C-library calls to fix the errors. -#pragma allow_unsafe_libc_calls -#endif - #include "mojo/public/cpp/bindings/lib/native_struct_serialization.h" +#include "base/compiler_specific.h" #include "ipc/ipc_message_attachment.h" #include "ipc/ipc_message_attachment_set.h" #include "ipc/native_handle_type_converters.h" @@ -69,8 +65,8 @@ // Allocate a uint8 array, initialize its header, and copy the Pickle in. MessageFragment<Array_Data<uint8_t>> data_fragment(fragment.message()); data_fragment.AllocateArrayData(ipc_message->payload_size()); - memcpy(data_fragment->storage(), ipc_message->payload(), - ipc_message->payload_size()); + UNSAFE_TODO(memcpy(data_fragment->storage(), ipc_message->payload(), + ipc_message->payload_size())); fragment->data.Set(data_fragment.data()); if (ipc_message->attachment_set()->empty()) {
diff --git a/mojo/public/cpp/bindings/lib/sync_event_watcher.cc b/mojo/public/cpp/bindings/lib/sync_event_watcher.cc index f78ab1c0..6d17c7b7 100644 --- a/mojo/public/cpp/bindings/lib/sync_event_watcher.cc +++ b/mojo/public/cpp/bindings/lib/sync_event_watcher.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/cpp/bindings/sync_event_watcher.h" #include <utility>
diff --git a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc index b403c41a..a25b8f9 100644 --- a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc +++ b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/cpp/bindings/sync_handle_registry.h" #include <utility>
diff --git a/mojo/public/cpp/bindings/tests/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc index 68627a6..37006ff 100644 --- a/mojo/public/cpp/bindings/tests/connector_unittest.cc +++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/390223051): Remove C-library calls to fix the errors. -#pragma allow_unsafe_libc_calls -#endif - #include "mojo/public/cpp/bindings/connector.h" #include <stddef.h> @@ -16,6 +11,7 @@ #include <array> #include <utility> +#include "base/compiler_specific.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/memory/raw_ptr.h" @@ -110,7 +106,8 @@ std::vector<ScopedHandle> handles = std::vector<ScopedHandle>()) { const size_t size = strlen(text) + 1; // Plus null terminator. Message message(1, 0, size, 0, &handles); - memcpy(message.payload_buffer()->AllocateAndGet(size), text, size); + UNSAFE_TODO( + memcpy(message.payload_buffer()->AllocateAndGet(size), text, size)); return message; }
diff --git a/mojo/public/cpp/bindings/tests/message_unittest.cc b/mojo/public/cpp/bindings/tests/message_unittest.cc index a6b4cfa..db5ad49 100644 --- a/mojo/public/cpp/bindings/tests/message_unittest.cc +++ b/mojo/public/cpp/bindings/tests/message_unittest.cc
@@ -2,10 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif +#include "mojo/public/cpp/bindings/message.h" #include <stdint.h> @@ -13,7 +10,7 @@ #include <tuple> #include <vector> -#include "mojo/public/cpp/bindings/message.h" +#include "base/compiler_specific.h" #include "mojo/public/cpp/system/message_pipe.h" #include "testing/gtest/include/gtest/gtest.h" @@ -35,7 +32,8 @@ } bytes->resize(message.data_num_bytes()); - std::copy(message.data(), message.data() + message.data_num_bytes(), + std::copy(message.data(), + UNSAFE_TODO(message.data() + message.data_num_bytes()), bytes->begin()); MessagePipe pipe;
diff --git a/mojo/public/cpp/bindings/tests/router_test_util.cc b/mojo/public/cpp/bindings/tests/router_test_util.cc index fec0e0c..d4f9cb9 100644 --- a/mojo/public/cpp/bindings/tests/router_test_util.cc +++ b/mojo/public/cpp/bindings/tests/router_test_util.cc
@@ -2,17 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/390223051): Remove C-library calls to fix the errors. -#pragma allow_unsafe_libc_calls -#endif - #include "mojo/public/cpp/bindings/tests/router_test_util.h" #include <stddef.h> #include <stdint.h> #include <string.h> +#include "base/compiler_specific.h" #include "mojo/public/cpp/bindings/message.h" #include "mojo/public/cpp/bindings/tests/message_queue.h" #include "testing/gtest/include/gtest/gtest.h" @@ -24,8 +20,8 @@ size_t payload_size = strlen(text) + 1; // Plus null terminator. *message = Message(name, Message::kFlagExpectsResponse, payload_size, 0, nullptr); - memcpy(message->payload_buffer()->AllocateAndGet(payload_size), text, - payload_size); + UNSAFE_TODO(memcpy(message->payload_buffer()->AllocateAndGet(payload_size), + text, payload_size)); } void AllocResponseMessage(uint32_t name, @@ -35,8 +31,8 @@ size_t payload_size = strlen(text) + 1; // Plus null terminator. *message = Message(name, Message::kFlagIsResponse, payload_size, 0, nullptr); message->set_request_id(request_id); - memcpy(message->payload_buffer()->AllocateAndGet(payload_size), text, - payload_size); + UNSAFE_TODO(memcpy(message->payload_buffer()->AllocateAndGet(payload_size), + text, payload_size)); } MessageAccumulator::MessageAccumulator(MessageQueue* queue,
diff --git a/mojo/public/cpp/bindings/tests/sample_service_unittest.cc b/mojo/public/cpp/bindings/tests/sample_service_unittest.cc index 24e68bd..dcc64ef7 100644 --- a/mojo/public/cpp/bindings/tests/sample_service_unittest.cc +++ b/mojo/public/cpp/bindings/tests/sample_service_unittest.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include <stddef.h> #include <stdint.h> @@ -16,6 +11,7 @@ #include <string> #include <utility> +#include "base/compiler_specific.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" @@ -240,7 +236,7 @@ void DumpHex(const uint8_t* bytes, size_t num_bytes) { for (size_t i = 0; i < num_bytes; ++i) { std::cout << std::setw(2) << std::setfill('0') << std::hex - << uint32_t(bytes[i]); + << uint32_t(UNSAFE_TODO(bytes[i])); if (i % 16 == 15) { std::cout << std::endl;
diff --git a/mojo/public/cpp/bindings/tests/struct_unittest.cc b/mojo/public/cpp/bindings/tests/struct_unittest.cc index 506f526..1cd20a4 100644 --- a/mojo/public/cpp/bindings/tests/struct_unittest.cc +++ b/mojo/public/cpp/bindings/tests/struct_unittest.cc
@@ -2,17 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/390223051): Remove C-library calls to fix the errors. -#pragma allow_unsafe_libc_calls -#endif - #include <stddef.h> #include <stdint.h> #include <string.h> #include <utility> +#include "base/compiler_specific.h" #include "mojo/public/cpp/bindings/lib/message_fragment.h" #include "mojo/public/cpp/system/message_pipe.h" #include "mojo/public/interfaces/bindings/tests/test_export2.test-mojom.h" @@ -84,7 +80,7 @@ // Set the subsequent area to a special value, so that we can find out if we // mistakenly access the area. void* subsequent_area = message.payload_buffer()->AllocateAndGet(32); - memset(subsequent_area, 0xAA, 32); + UNSAFE_TODO(memset(subsequent_area, 0xAA, 32)); OutputDataType output_data = reinterpret_cast<OutputDataType>(message.mutable_payload());
diff --git a/mojo/public/cpp/bindings/tests/union_unittest.cc b/mojo/public/cpp/bindings/tests/union_unittest.cc index 58695c3..3ae1313 100644 --- a/mojo/public/cpp/bindings/tests/union_unittest.cc +++ b/mojo/public/cpp/bindings/tests/union_unittest.cc
@@ -2,17 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include <stddef.h> #include <stdint.h> #include <utility> #include <vector> +#include "base/compiler_specific.h" #include "base/containers/flat_map.h" #include "base/functional/bind.h" #include "base/rand_util.h" @@ -464,7 +460,7 @@ fragment->data.f_f_string.offset = 8; char* ptr = reinterpret_cast<char*>(&fragment->data.f_f_string); mojo::internal::ArrayHeader* array_header = - reinterpret_cast<mojo::internal::ArrayHeader*>(ptr + *ptr); + reinterpret_cast<mojo::internal::ArrayHeader*>(UNSAFE_TODO(ptr + *ptr)); array_header->num_bytes = 20; // This should go out of bounds. array_header->num_elements = 20; mojo::internal::ValidationContext validation_context(fragment.data(), 32, 0, @@ -536,11 +532,11 @@ std::vector<char> new_buf; new_buf.resize(size); - memcpy(new_buf.data(), data, size); + UNSAFE_TODO(memcpy(new_buf.data(), data, size)); - data = + data = UNSAFE_TODO( reinterpret_cast<mojo::internal::Array_Data<internal::ObjectUnion_Data>*>( - new_buf.data()); + new_buf.data())); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0, 0); constexpr const mojo::internal::ContainerValidateParams& validate_params =
diff --git a/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc b/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc index 89334ee..2396ccc 100644 --- a/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc +++ b/mojo/public/cpp/bindings/tests/validation_test_input_parser.cc
@@ -2,17 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h" -#include "base/containers/contains.h" -#include "base/memory/raw_ptr.h" -#include "base/memory/raw_ref.h" - #include <assert.h> #include <stddef.h> #include <stdint.h> @@ -24,6 +15,10 @@ #include <set> #include <utility> +#include "base/compiler_specific.h" +#include "base/containers/contains.h" +#include "base/memory/raw_ptr.h" +#include "base/memory/raw_ref.h" #include "mojo/public/c/system/macros.h" namespace mojo { @@ -89,7 +84,7 @@ void AppendData(T data) { size_t pos = data_->size(); data_->resize(pos + sizeof(T)); - memcpy(&(*data_)[pos], &data, sizeof(T)); + UNSAFE_TODO(memcpy(&(*data_)[pos], &data, sizeof(T))); } template <typename TargetType, typename InputType> @@ -110,7 +105,7 @@ } TargetType target_value = static_cast<TargetType>(value); assert(pos + sizeof(TargetType) <= data_->size()); - memcpy(&(*data_)[pos], &target_value, sizeof(TargetType)); + UNSAFE_TODO(memcpy(&(*data_)[pos], &target_value, sizeof(TargetType))); return true; } @@ -212,17 +207,17 @@ return false; } - if (StartsWith(Range(&(*input_)[0] + input_cursor_, - &(*input_)[0] + input_->size()), + if (StartsWith(Range(UNSAFE_TODO(&(*input_)[0] + input_cursor_), + UNSAFE_TODO(&(*input_)[0] + input_->size())), "//", 2)) { // Skip contents until the end of the line. input_cursor_ = input_->find_first_of(kEndOfLineChars, input_cursor_); } else { - range->first = &(*input_)[0] + input_cursor_; + range->first = UNSAFE_TODO(&(*input_)[0] + input_cursor_); input_cursor_ = input_->find_first_of(kItemDelimiters, input_cursor_); range->second = input_cursor_ >= input_->size() - ? &(*input_)[0] + input_->size() - : &(*input_)[0] + input_cursor_; + ? UNSAFE_TODO(&(*input_)[0] + input_->size()) + : UNSAFE_TODO(&(*input_)[0] + input_cursor_); return true; } } @@ -230,10 +225,12 @@ bool ValidationTestInputParser::ParseItem(const Range& range) { for (size_t i = 0; i < kDataTypeCount; ++i) { - if (StartsWith(range, kDataTypes[i].name, kDataTypes[i].name_size)) { - return (this->*kDataTypes[i].parse_data_func)( - kDataTypes[i], - std::string(range.first + kDataTypes[i].name_size, range.second)); + if (StartsWith(range, UNSAFE_TODO(kDataTypes[i]).name, + UNSAFE_TODO(kDataTypes[i]).name_size)) { + return (this->*UNSAFE_TODO(kDataTypes[i]).parse_data_func)( + UNSAFE_TODO(kDataTypes[i]), + std::string(UNSAFE_TODO(range.first + kDataTypes[i].name_size), + range.second)); } } @@ -268,8 +265,9 @@ const DataType& type, const std::string& value_string) { long long int value; - if (sscanf(value_string.c_str(), "%lli", &value) != 1) + if (UNSAFE_TODO(sscanf(value_string.c_str(), "%lli", &value)) != 1) { return false; + } switch (type.data_size) { case 1: @@ -291,8 +289,9 @@ static_assert(sizeof(float) == 4, "sizeof(float) is not 4"); float value; - if (sscanf(value_string.c_str(), "%f", &value) != 1) + if (UNSAFE_TODO(sscanf(value_string.c_str(), "%f", &value)) != 1) { return false; + } AppendData(value); return true; @@ -303,8 +302,9 @@ static_assert(sizeof(double) == 8, "sizeof(double) is not 8"); double value; - if (sscanf(value_string.c_str(), "%lf", &value) != 1) + if (UNSAFE_TODO(sscanf(value_string.c_str(), "%lf", &value)) != 1) { return false; + } AppendData(value); return true; @@ -392,7 +392,7 @@ if (static_cast<size_t>(range.second - range.first) < prefix_length) return false; - return memcmp(range.first, prefix, prefix_length) == 0; + return UNSAFE_TODO(memcmp(range.first, prefix, prefix_length)) == 0; } bool ValidationTestInputParser::ConvertToUnsignedInteger( @@ -403,7 +403,7 @@ format = "%llx"; else format = "%llu"; - return sscanf(value_string.c_str(), format, value) == 1; + return UNSAFE_TODO(sscanf(value_string.c_str(), format, value)) == 1; } } // namespace
diff --git a/mojo/public/cpp/bindings/tests/validation_unittest.cc b/mojo/public/cpp/bindings/tests/validation_unittest.cc index b128ed5..f7ccc4d4 100644 --- a/mojo/public/cpp/bindings/tests/validation_unittest.cc +++ b/mojo/public/cpp/bindings/tests/validation_unittest.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/390223051): Remove C-library calls to fix the errors. -#pragma allow_unsafe_libc_calls -#endif - #include <stddef.h> #include <stdint.h> #include <stdio.h> @@ -18,6 +13,7 @@ #include <utility> #include <vector> +#include "base/compiler_specific.h" #include "base/memory/raw_ptr.h" #include "base/numerics/safe_math.h" #include "base/run_loop.h" @@ -65,7 +61,7 @@ void Append(std::vector<uint8_t>* data_vector, T data) { size_t pos = data_vector->size(); data_vector->resize(pos + sizeof(T)); - memcpy(&(*data_vector)[pos], &data, sizeof(T)); + UNSAFE_TODO(memcpy(&(*data_vector)[pos], &data, sizeof(T))); } bool TestInputParser(const std::string& input, @@ -124,7 +120,7 @@ } fseek(fp, 0, SEEK_SET); result->resize(size); - size_t size_read = fread(&result->at(0), 1, size, fp); + size_t size_read = UNSAFE_TODO(fread(&result->at(0), 1, size, fp)); fclose(fp); return size == size_read; } @@ -180,7 +176,7 @@ *message = CreateRawMessage(data.size()); if (!data.empty()) - memcpy(message->mutable_data(), &data[0], data.size()); + UNSAFE_TODO(memcpy(message->mutable_data(), &data[0], data.size())); message->mutable_handles()->resize(num_handles); return true;
diff --git a/mojo/public/cpp/system/message_pipe.cc b/mojo/public/cpp/system/message_pipe.cc index 7a37f31..c087fc4 100644 --- a/mojo/public/cpp/system/message_pipe.cc +++ b/mojo/public/cpp/system/message_pipe.cc
@@ -2,16 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/cpp/system/message_pipe.h" #include <algorithm> #include <cstring> +#include "base/compiler_specific.h" #include "base/numerics/safe_math.h" namespace mojo { @@ -41,7 +37,7 @@ DCHECK(buffer); DCHECK_GE(buffer_size, base::checked_cast<uint32_t>(num_bytes)); if (num_bytes > 0) { - memcpy(buffer, bytes, num_bytes); + UNSAFE_TODO(memcpy(buffer, bytes, num_bytes)); } MojoWriteMessageOptions write_options; @@ -81,7 +77,8 @@ DCHECK(buffer); uint8_t* payload_data = reinterpret_cast<uint8_t*>(buffer); payload->resize(num_bytes); - std::copy(payload_data, payload_data + num_bytes, payload->begin()); + std::copy(payload_data, UNSAFE_TODO(payload_data + num_bytes), + payload->begin()); } else if (payload) { payload->clear(); }
diff --git a/mojo/public/cpp/system/tests/core_unittest.cc b/mojo/public/cpp/system/tests/core_unittest.cc index d26bb69..e7cb614 100644 --- a/mojo/public/cpp/system/tests/core_unittest.cc +++ b/mojo/public/cpp/system/tests/core_unittest.cc
@@ -2,20 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - // This file tests the C++ Mojo system core wrappers. // TODO(vtl): Maybe rename "CoreCppTest" -> "CoreTest" if/when this gets // compiled into a different binary from the C API tests. #include <stddef.h> #include <stdint.h> + #include <map> #include <utility> +#include "base/compiler_specific.h" #include "base/containers/contains.h" #include "mojo/public/cpp/system/buffer.h" #include "mojo/public/cpp/system/data_pipe.h" @@ -380,7 +377,7 @@ // Map everything. ScopedSharedBufferMapping mapping = h0->Map(100); ASSERT_TRUE(mapping); - static_cast<char*>(mapping.get())[50] = 'x'; + UNSAFE_TODO(static_cast<char*>(mapping.get())[50]) = 'x'; // Duplicate |h0| to |h1|. ScopedSharedBufferHandle h1 = @@ -391,7 +388,7 @@ h0.reset(); // The mapping should still be good. - static_cast<char*>(mapping.get())[51] = 'y'; + UNSAFE_TODO(static_cast<char*>(mapping.get())[51]) = 'y'; // Unmap it. mapping.reset(); @@ -402,7 +399,7 @@ // It should have what we wrote. EXPECT_EQ('x', static_cast<char*>(mapping.get())[0]); - EXPECT_EQ('y', static_cast<char*>(mapping.get())[1]); + UNSAFE_TODO(EXPECT_EQ('y', static_cast<char*>(mapping.get())[1])); // Unmap it. mapping.reset();
diff --git a/mojo/public/cpp/system/wait.cc b/mojo/public/cpp/system/wait.cc index b4a90aa..1b440e6a8 100644 --- a/mojo/public/cpp/system/wait.cc +++ b/mojo/public/cpp/system/wait.cc
@@ -2,16 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/cpp/system/wait.h" #include <memory> #include <vector> +#include "base/compiler_specific.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/synchronization/waitable_event.h" @@ -137,7 +133,8 @@ // successful. Otherwise balanced immediately below. contexts[i]->AddRef(); - rv = MojoAddTrigger(trap.get().value(), handles[i].value(), signals[i], + rv = MojoAddTrigger(trap.get().value(), UNSAFE_TODO(handles[i]).value(), + UNSAFE_TODO(signals[i]), MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, contexts[i]->context_value(), nullptr); if (rv == MOJO_RESULT_INVALID_ARGUMENT) { @@ -192,9 +189,10 @@ if (signals_states) { for (size_t i = 0; i < num_handles; ++i) { if (i == index) { - signals_states[i] = ready_state; + UNSAFE_TODO(signals_states[i]) = ready_state; } else { - signals_states[i] = handles[i].QuerySignalsState(); + UNSAFE_TODO(signals_states[i]) = + UNSAFE_TODO(handles[i]).QuerySignalsState(); } } }
diff --git a/mojo/public/cpp/system/wait_set.cc b/mojo/public/cpp/system/wait_set.cc index 75b80f2d..2ca6c1a 100644 --- a/mojo/public/cpp/system/wait_set.cc +++ b/mojo/public/cpp/system/wait_set.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/cpp/system/wait_set.h" #include <algorithm> @@ -16,6 +11,7 @@ #include <vector> #include "base/check_op.h" +#include "base/compiler_specific.h" #include "base/containers/span.h" #include "base/memory/ptr_util.h" #include "base/memory/raw_ptr.h" @@ -221,7 +217,7 @@ ready_handles[i] = it->first; ready_results[i] = it->second.result; if (signals_states) - signals_states[i] = it->second.signals_state; + UNSAFE_TODO(signals_states[i]) = it->second.signals_state; ready_handles_.erase(it); }
diff --git a/mojo/public/cpp/test_support/lib/test_support.cc b/mojo/public/cpp/test_support/lib/test_support.cc index 724eb50..8f78f7311 100644 --- a/mojo/public/cpp/test_support/lib/test_support.cc +++ b/mojo/public/cpp/test_support/lib/test_support.cc
@@ -2,15 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "mojo/public/cpp/test_support/test_support.h" #include <stdlib.h> +#include "base/compiler_specific.h" + namespace mojo { namespace test { @@ -19,7 +16,7 @@ char** names = MojoTestSupportEnumerateSourceRootRelativeDirectory( relative_path.c_str()); std::vector<std::string> results; - for (char** ptr = names; *ptr != nullptr; ++ptr) { + for (char** ptr = names; *ptr != nullptr; UNSAFE_TODO(++ptr)) { results.push_back(*ptr); free(*ptr); }
diff --git a/mojo/public/tools/fuzzers/mojo_fuzzer_message_dump.cc b/mojo/public/tools/fuzzers/mojo_fuzzer_message_dump.cc index 5807ffc..72f70f2 100644 --- a/mojo/public/tools/fuzzers/mojo_fuzzer_message_dump.cc +++ b/mojo/public/tools/fuzzers/mojo_fuzzer_message_dump.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/351564777): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include <stddef.h> #include <stdint.h> @@ -270,10 +265,10 @@ int main(int argc, char** argv) { if (argc < 2) { - printf("Usage: %s [output_directory]\n", argv[0]); + UNSAFE_TODO(printf("Usage: %s [output_directory]\n", argv[0])); exit(1); } - std::string output_directory(argv[1]); + std::string output_directory(UNSAFE_TODO(argv[1])); /* Dump the messages from a TaskExecutor, and wait for it to finish. */ env->main_thread_task_executor.task_runner()->PostTask(
diff --git a/mojo/public/tools/fuzzers/mojolpm.cc b/mojo/public/tools/fuzzers/mojolpm.cc index be5f6d35..62932f3c 100644 --- a/mojo/public/tools/fuzzers/mojolpm.cc +++ b/mojo/public/tools/fuzzers/mojolpm.cc
@@ -2,13 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/390223051): Remove C-library calls to fix the errors. -#pragma allow_unsafe_libc_calls -#endif - #include "mojo/public/tools/fuzzers/mojolpm.h" +#include "base/compiler_specific.h" #include "base/containers/span.h" #include "base/no_destructor.h" #include "mojo/public/c/system/data_pipe.h" @@ -436,7 +432,7 @@ if (!mem) { return; } - std::memcpy(mem.get(), input.data().data(), size); + UNSAFE_TODO(std::memcpy(mem.get(), input.data().data(), size)); } void HandleDataPipeConsumerClose(
diff --git a/mojo/public/tools/mojom/mojom/format/format.py b/mojo/public/tools/mojom/mojom/format/format.py index 4ea394c..67a841a 100644 --- a/mojo/public/tools/mojom/mojom/format/format.py +++ b/mojo/public/tools/mojom/mojom/format/format.py
@@ -37,10 +37,10 @@ If `contents` is provided, then it is used instead of reading the file. """ if contents is None: - with open(filename) as f: + with open(filename, newline='\n') as f: contents = f.read() tree = parser.Parse(contents, filename, with_comments=True) - output = StringIO() + output = StringIO(newline='\n') state = FormatState(output) _write_mojom(tree, state) return output.getvalue()
diff --git a/mojo/public/tools/mojom/mojom_format.py b/mojo/public/tools/mojom/mojom_format.py index 8ee8981a..aeacbac 100755 --- a/mojo/public/tools/mojom/mojom_format.py +++ b/mojo/public/tools/mojom/mojom_format.py
@@ -43,7 +43,7 @@ print(file) exit_code = 1 else: - with open(file, 'w') as f: + with open(file, 'w', newline='\n') as f: f.write(output) except Exception as e: print(f'Failed to format {file}', file=sys.stderr)
diff --git a/net/base/address_list.h b/net/base/address_list.h index 4166f96..8087e7b98 100644 --- a/net/base/address_list.h +++ b/net/base/address_list.h
@@ -18,10 +18,6 @@ struct addrinfo; -namespace base { -class Value; -} - namespace net { class IPAddress;
diff --git a/net/base/mime_util.cc b/net/base/mime_util.cc index 3e198e6..0347fffd 100644 --- a/net/base/mime_util.cc +++ b/net/base/mime_util.cc
@@ -10,12 +10,12 @@ #include <optional> #include <string> #include <string_view> +#include <type_traits> #include <unordered_set> #include "base/base64.h" #include "base/check_op.h" #include "base/containers/span.h" -#include "base/lazy_instance.h" #include "base/memory/raw_ptr_exclusion.h" #include "base/no_destructor.h" #include "base/rand_util.h" @@ -45,6 +45,14 @@ // Singleton utility class for mime types. class MimeUtil : public PlatformMimeUtil { public: + static MimeUtil& Get() { + // This variable is leaky because we need to access it from WorkerPool + // threads. Trivially destructible, so no NoDestructor. + static_assert(std::is_trivially_destructible<MimeUtil>::value); + static MimeUtil mime_util; + return mime_util; + } + bool GetMimeTypeFromExtension(const base::FilePath::StringType& ext, std::string* mime_type) const; @@ -72,8 +80,6 @@ bool IsValidTopLevelMimeType(std::string_view type_string) const; private: - friend struct base::LazyInstanceTraitsBase<MimeUtil>; - MimeUtil(); bool GetMimeTypeFromExtensionHelper(const base::FilePath::StringType& ext, @@ -81,10 +87,6 @@ std::string* mime_type) const; }; // class MimeUtil -// This variable is Leaky because we need to access it from WorkerPool threads. -static base::LazyInstance<MimeUtil>::Leaky g_mime_util = - LAZY_INSTANCE_INITIALIZER; - struct MimeInfo { const std::string_view mime_type; @@ -659,46 +661,45 @@ bool GetMimeTypeFromExtension(const base::FilePath::StringType& ext, std::string* mime_type) { - return g_mime_util.Get().GetMimeTypeFromExtension(ext, mime_type); + return MimeUtil::Get().GetMimeTypeFromExtension(ext, mime_type); } bool GetMimeTypeFromFile(const base::FilePath& file_path, std::string* mime_type) { - return g_mime_util.Get().GetMimeTypeFromFile(file_path, mime_type); + return MimeUtil::Get().GetMimeTypeFromFile(file_path, mime_type); } bool GetWellKnownMimeTypeFromExtension(const base::FilePath::StringType& ext, std::string* mime_type) { - return g_mime_util.Get().GetWellKnownMimeTypeFromExtension(ext, mime_type); + return MimeUtil::Get().GetWellKnownMimeTypeFromExtension(ext, mime_type); } bool GetWellKnownMimeTypeFromFile(const base::FilePath& file_path, std::string* mime_type) { - return g_mime_util.Get().GetWellKnownMimeTypeFromFile(file_path, mime_type); + return MimeUtil::Get().GetWellKnownMimeTypeFromFile(file_path, mime_type); } bool GetPreferredExtensionForMimeType(std::string_view mime_type, base::FilePath::StringType* extension) { - return g_mime_util.Get().GetPreferredExtensionForMimeType(mime_type, - extension); + return MimeUtil::Get().GetPreferredExtensionForMimeType(mime_type, extension); } bool MatchesMimeType(std::string_view mime_type_pattern, std::string_view mime_type, bool validate_mime_type) { - return g_mime_util.Get().MatchesMimeType(mime_type_pattern, mime_type, - validate_mime_type); + return MimeUtil::Get().MatchesMimeType(mime_type_pattern, mime_type, + validate_mime_type); } bool ParseMimeTypeWithoutParameter(std::string_view type_string, std::string* top_level_type, std::string* subtype) { - return g_mime_util.Get().ParseMimeTypeWithoutParameter( - type_string, top_level_type, subtype); + return MimeUtil::Get().ParseMimeTypeWithoutParameter(type_string, + top_level_type, subtype); } bool IsValidTopLevelMimeType(std::string_view type_string) { - return g_mime_util.Get().IsValidTopLevelMimeType(type_string); + return MimeUtil::Get().IsValidTopLevelMimeType(type_string); } namespace { @@ -819,8 +820,7 @@ const std::string& leading_mime_type, std::unordered_set<base::FilePath::StringType>* extensions) { for (auto* standard_type : standard_types) { - g_mime_util.Get().GetPlatformExtensionsForMimeType(standard_type, - extensions); + MimeUtil::Get().GetPlatformExtensionsForMimeType(standard_type, extensions); } // Also look up the extensions from hard-coded mappings in case that some @@ -886,8 +886,8 @@ leading_mime_type, &unique_extensions); } else { - g_mime_util.Get().GetPlatformExtensionsForMimeType(mime_type, - &unique_extensions); + MimeUtil::Get().GetPlatformExtensionsForMimeType(mime_type, + &unique_extensions); // Also look up the extensions from hard-coded mappings in case that some // supported extensions are not registered in the system registry, like ogg.
diff --git a/net/base/network_interfaces_win.cc b/net/base/network_interfaces_win.cc index 8a571e1..d273eaa 100644 --- a/net/base/network_interfaces_win.cc +++ b/net/base/network_interfaces_win.cc
@@ -7,12 +7,13 @@ #include <algorithm> #include <memory> #include <string_view> +#include <type_traits> #include "base/compiler_specific.h" #include "base/containers/heap_array.h" #include "base/containers/span.h" #include "base/files/file_path.h" -#include "base/lazy_instance.h" +#include "base/no_destructor.h" #include "base/strings/escape.h" #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" @@ -101,11 +102,10 @@ namespace internal { -base::LazyInstance<WlanApi>::Leaky lazy_wlanapi = - LAZY_INSTANCE_INITIALIZER; - WlanApi& WlanApi::GetInstance() { - return lazy_wlanapi.Get(); + static_assert(std::is_trivially_destructible<WlanApi>::value); + static WlanApi wlan_api; + return wlan_api; } WlanApi::WlanApi() : initialized(false) {
diff --git a/net/base/port_util.cc b/net/base/port_util.cc index 02197ce..49d0f744 100644 --- a/net/base/port_util.cc +++ b/net/base/port_util.cc
@@ -10,7 +10,6 @@ #include "base/containers/fixed_flat_map.h" #include "base/containers/flat_set.h" #include "base/feature_list.h" -#include "base/lazy_instance.h" #include "base/logging.h" #include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_functions.h" @@ -116,8 +115,10 @@ 10080, // Amanda }; -base::LazyInstance<std::multiset<int>>::Leaky g_explicitly_allowed_ports = - LAZY_INSTANCE_INITIALIZER; +std::multiset<int>& GetExplicitlyAllowedPorts() { + static base::NoDestructor<std::multiset<int>> explicitly_allowed_ports; + return *explicitly_allowed_ports; +} // List of ports which are permitted to be reenabled despite being in // kRestrictedList. When adding an port to this list you should also update the @@ -169,8 +170,9 @@ return false; // Allow explicitly allowed ports for any scheme. - if (g_explicitly_allowed_ports.Get().count(port) > 0) + if (GetExplicitlyAllowedPorts().count(port) > 0) { return true; + } // Finally check against the generic list of restricted ports for all // schemes. @@ -211,7 +213,7 @@ int port = endpoint.port(); // Allow explicitly allowed ports. - if (g_explicitly_allowed_ports.Get().count(port) > 0) { + if (GetExplicitlyAllowedPorts().count(port) > 0) { return true; } @@ -234,24 +236,24 @@ } size_t GetCountOfExplicitlyAllowedPorts() { - return g_explicitly_allowed_ports.Get().size(); + return GetExplicitlyAllowedPorts().size(); } // Specifies a comma separated list of port numbers that should be accepted // despite bans. If the string is invalid no allowed ports are stored. void SetExplicitlyAllowedPorts(base::span<const uint16_t> allowed_ports) { std::multiset<int> ports(allowed_ports.begin(), allowed_ports.end()); - g_explicitly_allowed_ports.Get() = std::move(ports); + GetExplicitlyAllowedPorts() = std::move(ports); } ScopedPortException::ScopedPortException(int port) : port_(port) { - g_explicitly_allowed_ports.Get().insert(port); + GetExplicitlyAllowedPorts().insert(port); } ScopedPortException::~ScopedPortException() { - auto it = g_explicitly_allowed_ports.Get().find(port_); - if (it != g_explicitly_allowed_ports.Get().end()) { - g_explicitly_allowed_ports.Get().erase(it); + auto it = GetExplicitlyAllowedPorts().find(port_); + if (it != GetExplicitlyAllowedPorts().end()) { + GetExplicitlyAllowedPorts().erase(it); } else { NOTREACHED(); }
diff --git a/net/base/winsock_init.cc b/net/base/winsock_init.cc index 0d26d6ae..bb4e34f 100644 --- a/net/base/winsock_init.cc +++ b/net/base/winsock_init.cc
@@ -6,8 +6,10 @@ #include <winsock2.h> +#include <type_traits> + #include "base/check.h" -#include "base/lazy_instance.h" +#include "base/no_destructor.h" namespace { @@ -32,17 +34,15 @@ } }; -// Worker pool threads that use the Windows Sockets API may still be running at -// shutdown. Leak instance and skip cleanup. -static base::LazyInstance<WinsockInitSingleton>::Leaky - g_winsock_init_singleton = LAZY_INSTANCE_INITIALIZER; - } // namespace namespace net { void EnsureWinsockInit() { - g_winsock_init_singleton.Get(); + // Worker pool threads that use the Windows Sockets API may still be running + // at shutdown. Leak instance and skip cleanup. + static_assert(std::is_trivially_destructible<WinsockInitSingleton>::value); + static WinsockInitSingleton singleton; } } // namespace net
diff --git a/net/cert/cert_verify_proc_builtin_unittest.cc b/net/cert/cert_verify_proc_builtin_unittest.cc index 17dcee1..b77b4188 100644 --- a/net/cert/cert_verify_proc_builtin_unittest.cc +++ b/net/cert/cert_verify_proc_builtin_unittest.cc
@@ -21,6 +21,7 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" +#include "base/threading/thread_restrictions.h" #include "base/time/time.h" #include "components/network_time/time_tracker/time_tracker.h" #include "net/base/features.h"
diff --git a/net/cert/ev_root_ca_metadata.cc b/net/cert/ev_root_ca_metadata.cc index 30a627f0..25be2e5 100644 --- a/net/cert/ev_root_ca_metadata.cc +++ b/net/cert/ev_root_ca_metadata.cc
@@ -7,8 +7,8 @@ #include <string_view> #include "base/containers/contains.h" -#include "base/lazy_instance.h" #include "base/logging.h" +#include "base/no_destructor.h" #include "build/build_config.h" #include "third_party/boringssl/src/pki/input.h" #if defined(PLATFORM_USES_CHROMIUM_EV_METADATA) @@ -41,12 +41,10 @@ #endif // defined(PLATFORM_USES_CHROMIUM_EV_METADATA) } // namespace -static base::LazyInstance<EVRootCAMetadata>::Leaky g_ev_root_ca_metadata = - LAZY_INSTANCE_INITIALIZER; - // static EVRootCAMetadata* EVRootCAMetadata::GetInstance() { - return g_ev_root_ca_metadata.Pointer(); + static base::NoDestructor<EVRootCAMetadata> ev_root_ca_metadata; + return ev_root_ca_metadata.get(); } #if defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
diff --git a/net/cert/ev_root_ca_metadata.h b/net/cert/ev_root_ca_metadata.h index 29ceafe4..9c02928 100644 --- a/net/cert/ev_root_ca_metadata.h +++ b/net/cert/ev_root_ca_metadata.h
@@ -5,13 +5,13 @@ #ifndef NET_CERT_EV_ROOT_CA_METADATA_H_ #define NET_CERT_EV_ROOT_CA_METADATA_H_ -#include "build/build_config.h" - #include <map> #include <set> #include <string> #include <vector> +#include "base/no_destructor.h" +#include "build/build_config.h" #include "crypto/crypto_buildflags.h" #include "net/base/net_export.h" #include "net/cert/x509_certificate.h" @@ -23,11 +23,6 @@ #define PLATFORM_USES_CHROMIUM_EV_METADATA #endif -namespace base { -template <typename T> -struct LazyInstanceTraitsBase; -} // namespace base - namespace bssl { namespace der { class Input; @@ -63,7 +58,7 @@ bool RemoveEVCA(const SHA256HashValue& fingerprint); private: - friend struct base::LazyInstanceTraitsBase<EVRootCAMetadata>; + friend class base::NoDestructor<EVRootCAMetadata>; EVRootCAMetadata(); ~EVRootCAMetadata();
diff --git a/net/cert/internal/system_trust_store.cc b/net/cert/internal/system_trust_store.cc index 4fb269b..b5e7af2 100644 --- a/net/cert/internal/system_trust_store.cc +++ b/net/cert/internal/system_trust_store.cc
@@ -34,7 +34,6 @@ #include "net/cert/internal/trust_store_mac.h" #include "net/cert/x509_util_apple.h" #elif BUILDFLAG(IS_FUCHSIA) -#include "base/lazy_instance.h" #include "third_party/boringssl/src/include/openssl/pool.h" #elif BUILDFLAG(IS_WIN) #include "net/cert/internal/trust_store_win.h" @@ -288,8 +287,10 @@ bssl::TrustStoreInMemory system_trust_store_; }; -base::LazyInstance<FuchsiaSystemCerts>::Leaky g_root_certs_fuchsia = - LAZY_INSTANCE_INITIALIZER; +FuchsiaSystemCerts& GetFuchsiaRootCerts() { + static base::NoDestructor<FuchsiaSystemCerts> certs; + return *certs; +} } // namespace @@ -298,12 +299,11 @@ SystemTrustStoreFuchsia() = default; bssl::TrustStore* GetTrustStore() override { - return g_root_certs_fuchsia.Get().system_trust_store(); + return GetFuchsiaRootCerts().system_trust_store(); } bool IsKnownRoot(const bssl::ParsedCertificate* trust_anchor) const override { - return g_root_certs_fuchsia.Get().system_trust_store()->Contains( - trust_anchor); + return GetFuchsiaRootCerts().system_trust_store()->Contains(trust_anchor); } };
diff --git a/net/cert/test_root_certs.cc b/net/cert/test_root_certs.cc index dca48d5c..b434fac 100644 --- a/net/cert/test_root_certs.cc +++ b/net/cert/test_root_certs.cc
@@ -20,9 +20,6 @@ bool g_has_instance = false; -base::LazyInstance<TestRootCerts>::Leaky - g_test_root_certs = LAZY_INSTANCE_INITIALIZER; - } // namespace bool ThreadSafeTrustStoreInMemory::IsEmpty() const { @@ -57,7 +54,8 @@ // static TestRootCerts* TestRootCerts::GetInstance() { - return g_test_root_certs.Pointer(); + static base::NoDestructor<TestRootCerts> test_root_certs; + return test_root_certs.get(); } bool TestRootCerts::HasInstance() {
diff --git a/net/cert/test_root_certs.h b/net/cert/test_root_certs.h index 49742acc..874274a 100644 --- a/net/cert/test_root_certs.h +++ b/net/cert/test_root_certs.h
@@ -8,8 +8,8 @@ #include <set> #include "base/containers/span.h" -#include "base/lazy_instance.h" #include "base/memory/scoped_refptr.h" +#include "base/no_destructor.h" #include "base/synchronization/lock.h" #include "build/build_config.h" #include "net/base/net_export.h" @@ -82,7 +82,7 @@ bssl::TrustStore* test_trust_store() { return &test_trust_store_; } private: - friend struct base::LazyInstanceTraitsBase<TestRootCerts>; + friend class base::NoDestructor<TestRootCerts>; friend class ScopedTestRoot; friend class ScopedTestKnownRoot;
diff --git a/net/device_bound_sessions/session_json_utils.cc b/net/device_bound_sessions/session_json_utils.cc index 3b625c4..556dc37 100644 --- a/net/device_bound_sessions/session_json_utils.cc +++ b/net/device_bound_sessions/session_json_utils.cc
@@ -92,15 +92,6 @@ unexportable_keys::UnexportableKeyId key_id, std::optional<std::string> expected_session_id, std::string_view response_json) { - // TODO(kristianm): Skip XSSI-escapes, see for example: - // https://hg.mozilla.org/mozilla-central/rev/4cee9ec9155e - // Discuss with others if XSSI should be part of the standard. - - // TODO(kristianm): Decide if the standard should require parsing - // to fail fully if any item is wrong, or if that item should be - // ignored. - - net::SchemefulSite fetcher_site(fetcher_url); std::optional<base::Value::Dict> maybe_root = base::JSONReader::ReadDict( response_json, base::JSON_PARSE_RFC, /*max_depth=*/5u); if (!maybe_root) {
diff --git a/net/disk_cache/backend_cleanup_tracker.cc b/net/disk_cache/backend_cleanup_tracker.cc index c25be4b..c64af746 100644 --- a/net/disk_cache/backend_cleanup_tracker.cc +++ b/net/disk_cache/backend_cleanup_tracker.cc
@@ -12,9 +12,9 @@ #include "base/files/file_path.h" #include "base/functional/callback.h" -#include "base/lazy_instance.h" #include "base/memory/raw_ptr.h" #include "base/memory/ref_counted.h" +#include "base/no_destructor.h" #include "base/synchronization/lock.h" #include "base/task/sequenced_task_runner.h" @@ -36,7 +36,10 @@ base::Lock lock; }; -static base::LazyInstance<AllBackendCleanupTrackers>::Leaky g_all_trackers; +AllBackendCleanupTrackers& GetAllTrackers() { + static base::NoDestructor<AllBackendCleanupTrackers> all_trackers; + return *all_trackers; +} } // namespace. @@ -44,12 +47,11 @@ scoped_refptr<BackendCleanupTracker> BackendCleanupTracker::TryCreate( const base::FilePath& path, base::OnceClosure retry_closure) { - AllBackendCleanupTrackers* all_trackers = g_all_trackers.Pointer(); - base::AutoLock lock(all_trackers->lock); + AllBackendCleanupTrackers& all_trackers = GetAllTrackers(); + base::AutoLock lock(all_trackers.lock); - std::pair<TrackerMap::iterator, bool> insert_result = - all_trackers->map.insert( - std::pair<base::FilePath, BackendCleanupTracker*>(path, nullptr)); + std::pair<TrackerMap::iterator, bool> insert_result = all_trackers.map.insert( + std::pair<base::FilePath, BackendCleanupTracker*>(path, nullptr)); if (insert_result.second) { auto tracker = base::WrapRefCounted(new BackendCleanupTracker(path)); insert_result.first->second = tracker.get(); @@ -65,7 +67,7 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(seq_checker_); // Despite the sequencing requirement we need to grab the table lock since // this may otherwise race against TryMakeContext. - base::AutoLock lock(g_all_trackers.Get().lock); + base::AutoLock lock(GetAllTrackers().lock); AddPostCleanupCallbackImpl(std::move(cb)); } @@ -81,9 +83,9 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(seq_checker_); { - AllBackendCleanupTrackers* all_trackers = g_all_trackers.Pointer(); - base::AutoLock lock(all_trackers->lock); - int rv = all_trackers->map.erase(path_); + AllBackendCleanupTrackers& all_trackers = GetAllTrackers(); + base::AutoLock lock(all_trackers.lock); + int rv = all_trackers.map.erase(path_); DCHECK_EQ(1, rv); }
diff --git a/net/disk_cache/simple/simple_backend_impl.cc b/net/disk_cache/simple/simple_backend_impl.cc index 9edfcaba..730d05d 100644 --- a/net/disk_cache/simple/simple_backend_impl.cc +++ b/net/disk_cache/simple/simple_backend_impl.cc
@@ -21,13 +21,13 @@ #include "base/files/file_util.h" #include "base/functional/bind.h" #include "base/functional/callback.h" -#include "base/lazy_instance.h" #include "base/location.h" #include "base/memory/ptr_util.h" #include "base/metrics/field_trial.h" #include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" +#include "base/no_destructor.h" #include "base/system/sys_info.h" #include "base/task/thread_pool/thread_pool_instance.h" #include "base/time/time.h" @@ -65,8 +65,10 @@ // Global context of all the files we have open --- this permits some to be // closed on demand if too many FDs are being used, to avoid running out. -base::LazyInstance<SimpleFileTracker>::Leaky g_simple_file_tracker = - LAZY_INSTANCE_INITIALIZER; +SimpleFileTracker* GetSimpleFileTracker() { + static base::NoDestructor<SimpleFileTracker> file_tracker; + return file_tracker.get(); +} // Detects if the files in the cache directory match the current disk cache // backend type and version. If the directory contains no cache, occupies it @@ -219,8 +221,7 @@ ? std::move(file_operations_factory) : base::MakeRefCounted<TrivialFileOperationsFactory>()), cleanup_tracker_(std::move(cleanup_tracker)), - file_tracker_(file_tracker ? file_tracker - : g_simple_file_tracker.Pointer()), + file_tracker_(file_tracker ? file_tracker : GetSimpleFileTracker()), path_(path), orig_max_size_(max_bytes), entry_operations_mode_(CacheTypeToOperationsMode(cache_type)),
diff --git a/net/dns/dns_config_watcher_mac.cc b/net/dns/dns_config_watcher_mac.cc index e318ebb6..3ad56087 100644 --- a/net/dns/dns_config_watcher_mac.cc +++ b/net/dns/dns_config_watcher_mac.cc
@@ -12,8 +12,8 @@ #include <dlfcn.h> #include "base/compiler_specific.h" -#include "base/lazy_instance.h" #include "base/memory/raw_ptr.h" +#include "base/no_destructor.h" #include "third_party/apple_apsl/dnsinfo.h" namespace { @@ -57,8 +57,8 @@ }; const DnsInfoApi& GetDnsInfoApi() { - static base::LazyInstance<DnsInfoApi>::Leaky api = LAZY_INSTANCE_INITIALIZER; - return api.Get(); + static base::NoDestructor<DnsInfoApi> api; + return *api; } struct DnsConfigTDeleter {
diff --git a/net/dns/dns_reloader.cc b/net/dns/dns_reloader.cc index 9ae4220..e95bf65 100644 --- a/net/dns/dns_reloader.cc +++ b/net/dns/dns_reloader.cc
@@ -40,7 +40,7 @@ #if defined(USE_RES_NINIT) -#include "base/lazy_instance.h" +#include "base/no_destructor.h" #include "base/notreached.h" #include "base/synchronization/lock.h" #include "base/task/current_thread.h" @@ -114,25 +114,26 @@ base::Lock lock_; // Protects resolver_generation_. int resolver_generation_ = 0; - friend struct base::LazyInstanceTraitsBase<DnsReloader>; + friend class base::NoDestructor<DnsReloader>; // We use thread local storage to identify which ReloadState to interact with. base::ThreadLocalOwnedPointer<ReloadState> tls_reload_state_; }; -base::LazyInstance<DnsReloader>::Leaky - g_dns_reloader = LAZY_INSTANCE_INITIALIZER; +DnsReloader* GetDnsReloader() { + static base::NoDestructor<DnsReloader> dns_reloader; + return dns_reloader.get(); +} } // namespace void EnsureDnsReloaderInit() { - g_dns_reloader.Pointer(); + GetDnsReloader(); } void DnsReloaderMaybeReload() { // This routine can be called by any of the DNS worker threads. - DnsReloader* dns_reloader = g_dns_reloader.Pointer(); - dns_reloader->MaybeReload(); + GetDnsReloader()->MaybeReload(); } } // namespace net
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h index ff4cf010..9bc11bf 100644 --- a/net/http/http_network_session.h +++ b/net/http/http_network_session.h
@@ -39,10 +39,6 @@ #include "net/ssl/ssl_client_session_cache.h" #include "net/third_party/quiche/src/quiche/http2/core/spdy_protocol.h" -namespace base { -class Value; -} - namespace net { class CertVerifier;
diff --git a/net/proxy_resolution/proxy_resolver_apple.cc b/net/proxy_resolution/proxy_resolver_apple.cc index 9b2225f..ba3f4b9 100644 --- a/net/proxy_resolution/proxy_resolver_apple.cc +++ b/net/proxy_resolution/proxy_resolver_apple.cc
@@ -12,8 +12,8 @@ #include "base/apple/foundation_util.h" #include "base/apple/scoped_cftyperef.h" #include "base/check.h" -#include "base/lazy_instance.h" #include "base/memory/raw_ref.h" +#include "base/no_destructor.h" #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" #include "base/synchronization/lock.h" @@ -48,8 +48,10 @@ // 1. Adding the source to the run loop. // 2. Handling the source result. // 3. Removing the source from the run loop. -static base::LazyInstance<base::Lock>::Leaky g_cfnetwork_pac_runloop_lock = - LAZY_INSTANCE_INITIALIZER; +base::Lock& GetCFNetworkPacRunloopLock() { + static base::NoDestructor<base::Lock> lock; + return *lock; +} // Forward declaration of the callback function used by the // SynchronizedRunLoopObserver class. @@ -271,13 +273,13 @@ // Add the run loop observer to synchronize events of // CFNetworkExecuteProxyAutoConfigurationURL sources. See the definition of // |g_cfnetwork_pac_runloop_lock|. - SynchronizedRunLoopObserver observer(g_cfnetwork_pac_runloop_lock.Get()); + SynchronizedRunLoopObserver observer(GetCFNetworkPacRunloopLock()); observer.AddToCurrentRunLoop(private_runloop_mode); // Make sure that no CFNetworkExecuteProxyAutoConfigurationURL sources // are added to the run loop concurrently. { - base::AutoLock lock(g_cfnetwork_pac_runloop_lock.Get()); + base::AutoLock lock(GetCFNetworkPacRunloopLock()); CFRunLoopAddSource(CFRunLoopGetCurrent(), runloop_source.get(), private_runloop_mode); } @@ -287,7 +289,7 @@ // Make sure that no CFNetworkExecuteProxyAutoConfigurationURL sources // are removed from the run loop concurrently. { - base::AutoLock lock(g_cfnetwork_pac_runloop_lock.Get()); + base::AutoLock lock(GetCFNetworkPacRunloopLock()); CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runloop_source.get(), private_runloop_mode); }
diff --git a/net/proxy_resolution/win/dhcpcsvc_init_win.cc b/net/proxy_resolution/win/dhcpcsvc_init_win.cc index d2e2a62..b048af0 100644 --- a/net/proxy_resolution/win/dhcpcsvc_init_win.cc +++ b/net/proxy_resolution/win/dhcpcsvc_init_win.cc
@@ -9,8 +9,9 @@ #include <dhcpcsdk.h> #include <dhcpv6csdk.h> +#include <type_traits> + #include "base/check_op.h" -#include "base/lazy_instance.h" namespace { @@ -23,17 +24,15 @@ } }; -// Worker pool threads that use the DHCP API may still be running at shutdown. -// Leak instance and skip cleanup. -static base::LazyInstance<DhcpcsvcInitSingleton>::Leaky - g_dhcpcsvc_init_singleton = LAZY_INSTANCE_INITIALIZER; - } // namespace namespace net { void EnsureDhcpcsvcInit() { - g_dhcpcsvc_init_singleton.Get(); + // Worker pool threads that use the DHCP API may still be running at shutdown. + // Leak instance and skip cleanup. + static_assert(std::is_trivially_destructible<DhcpcsvcInitSingleton>::value); + static DhcpcsvcInitSingleton instance; } } // namespace net
diff --git a/net/quic/quic_crypto_client_stream_factory.cc b/net/quic/quic_crypto_client_stream_factory.cc index 0983164..7935fb3 100644 --- a/net/quic/quic_crypto_client_stream_factory.cc +++ b/net/quic/quic_crypto_client_stream_factory.cc
@@ -4,7 +4,7 @@ #include "net/quic/quic_crypto_client_stream_factory.h" -#include "base/lazy_instance.h" +#include "base/no_destructor.h" #include "net/quic/crypto/proof_verifier_chromium.h" #include "net/quic/quic_chromium_client_session.h" #include "net/third_party/quiche/src/quiche/quic/core/quic_crypto_client_stream.h" @@ -26,15 +26,13 @@ } }; -static base::LazyInstance<DefaultCryptoStreamFactory>::Leaky - g_default_crypto_stream_factory = LAZY_INSTANCE_INITIALIZER; - } // namespace // static QuicCryptoClientStreamFactory* QuicCryptoClientStreamFactory::GetDefaultFactory() { - return g_default_crypto_stream_factory.Pointer(); + static base::NoDestructor<DefaultCryptoStreamFactory> factory; + return factory.get(); } } // namespace net
diff --git a/net/quic/quic_session_pool.h b/net/quic/quic_session_pool.h index 58ded8ff..2068d398 100644 --- a/net/quic/quic_session_pool.h +++ b/net/quic/quic_session_pool.h
@@ -70,10 +70,6 @@ #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h" #include "url/scheme_host_port.h" -namespace base { -class Value; -} // namespace base - namespace quic { class QuicAlarmFactory; class QuicClock;
diff --git a/net/socket/client_socket_factory.cc b/net/socket/client_socket_factory.cc index 89a154fd..c53d98d5 100644 --- a/net/socket/client_socket_factory.cc +++ b/net/socket/client_socket_factory.cc
@@ -6,7 +6,7 @@ #include <utility> -#include "base/lazy_instance.h" +#include "base/no_destructor.h" #include "build/build_config.h" #include "net/socket/ssl_client_socket.h" #include "net/socket/tcp_client_socket.h" @@ -53,14 +53,12 @@ } }; -static base::LazyInstance<DefaultClientSocketFactory>::Leaky - g_default_client_socket_factory = LAZY_INSTANCE_INITIALIZER; - } // namespace // static ClientSocketFactory* ClientSocketFactory::GetDefaultFactory() { - return g_default_client_socket_factory.Pointer(); + static base::NoDestructor<DefaultClientSocketFactory> factory; + return factory.get(); } } // namespace net
diff --git a/net/socket/udp_socket_win.cc b/net/socket/udp_socket_win.cc index 5e446eb..68401a5 100644 --- a/net/socket/udp_socket_win.cc +++ b/net/socket/udp_socket_win.cc
@@ -9,12 +9,12 @@ #include <mstcpip.h> #include <memory> +#include <type_traits> #include "base/check_op.h" #include "base/containers/span.h" #include "base/functional/bind.h" #include "base/functional/callback.h" -#include "base/lazy_instance.h" #include "base/memory/raw_ptr.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" @@ -196,9 +196,9 @@ } QwaveApi* QwaveApi::GetDefault() { - static base::LazyInstance<QwaveApi>::Leaky lazy_qwave = - LAZY_INSTANCE_INITIALIZER; - return lazy_qwave.Pointer(); + static_assert(std::is_trivially_destructible<QwaveApi>::value); + static QwaveApi qwave; + return &qwave; } bool QwaveApi::qwave_supported() const {
diff --git a/net/ssl/openssl_ssl_util.cc b/net/ssl/openssl_ssl_util.cc index 5495559..ac999ae 100644 --- a/net/ssl/openssl_ssl_util.cc +++ b/net/ssl/openssl_ssl_util.cc
@@ -6,9 +6,9 @@ #include <errno.h> +#include <type_traits> #include <utility> -#include "base/lazy_instance.h" #include "base/location.h" #include "base/logging.h" #include "base/notreached.h" @@ -49,11 +49,11 @@ int net_error_lib_; }; -base::LazyInstance<OpenSSLNetErrorLibSingleton>::Leaky g_openssl_net_error_lib = - LAZY_INSTANCE_INITIALIZER; - int OpenSSLNetErrorLib() { - return g_openssl_net_error_lib.Get().net_error_lib(); + static_assert( + std::is_trivially_destructible<OpenSSLNetErrorLibSingleton>::value); + static OpenSSLNetErrorLibSingleton instance; + return instance.net_error_lib(); } int MapOpenSSLErrorSSL(uint32_t error_code) {
diff --git a/net/ssl/ssl_platform_key_util.cc b/net/ssl/ssl_platform_key_util.cc index b5916c51..987a225 100644 --- a/net/ssl/ssl_platform_key_util.cc +++ b/net/ssl/ssl_platform_key_util.cc
@@ -6,8 +6,8 @@ #include <string_view> -#include "base/lazy_instance.h" #include "base/logging.h" +#include "base/no_destructor.h" #include "base/task/single_thread_task_runner.h" #include "base/threading/thread.h" #include "crypto/evp.h" @@ -44,13 +44,11 @@ base::Thread worker_thread_; }; -base::LazyInstance<SSLPlatformKeyTaskRunner>::Leaky g_platform_key_task_runner = - LAZY_INSTANCE_INITIALIZER; - } // namespace scoped_refptr<base::SingleThreadTaskRunner> GetSSLPlatformKeyTaskRunner() { - return g_platform_key_task_runner.Get().task_runner(); + static base::NoDestructor<SSLPlatformKeyTaskRunner> instance; + return instance->task_runner(); } bssl::UniquePtr<EVP_PKEY> GetClientCertPublicKey(
diff --git a/net/url_request/url_request_test_job.cc b/net/url_request/url_request_test_job.cc index 8c3918e6..52c0547d 100644 --- a/net/url_request/url_request_test_job.cc +++ b/net/url_request/url_request_test_job.cc
@@ -10,8 +10,8 @@ #include "base/compiler_specific.h" #include "base/functional/bind.h" -#include "base/lazy_instance.h" #include "base/location.h" +#include "base/no_destructor.h" #include "base/numerics/safe_conversions.h" #include "base/strings/string_util.h" #include "base/task/single_thread_task_runner.h" @@ -26,8 +26,11 @@ namespace { typedef std::list<URLRequestTestJob*> URLRequestJobList; -base::LazyInstance<URLRequestJobList>::Leaky - g_pending_jobs = LAZY_INSTANCE_INITIALIZER; + +URLRequestJobList& GetPendingJobs() { + static base::NoDestructor<URLRequestJobList> pending_jobs; + return *pending_jobs; +} } // namespace @@ -143,7 +146,7 @@ response_headers_length_(response_headers.size()) {} URLRequestTestJob::~URLRequestTestJob() { - std::erase(g_pending_jobs.Get(), this); + std::erase(GetPendingJobs(), this); } bool URLRequestTestJob::GetMimeType(std::string* mime_type) const { @@ -278,7 +281,7 @@ stage_ = DONE; URLRequestJob::Kill(); weak_factory_.InvalidateWeakPtrs(); - std::erase(g_pending_jobs.Get(), this); + std::erase(GetPendingJobs(), this); } void URLRequestTestJob::ProcessNextOperation() { @@ -327,16 +330,17 @@ weak_factory_.GetWeakPtr())); return; } - g_pending_jobs.Get().push_back(this); + GetPendingJobs().push_back(this); } // static bool URLRequestTestJob::ProcessOnePendingMessage() { - if (g_pending_jobs.Get().empty()) + if (GetPendingJobs().empty()) { return false; + } - URLRequestTestJob* next_job(g_pending_jobs.Get().front()); - g_pending_jobs.Get().pop_front(); + URLRequestTestJob* next_job(GetPendingJobs().front()); + GetPendingJobs().pop_front(); DCHECK(!next_job->auto_advance()); // auto_advance jobs should be in this q next_job->ProcessNextOperation();
diff --git a/pdf/pdf_view_web_plugin.cc b/pdf/pdf_view_web_plugin.cc index 9180527..d59ba22 100644 --- a/pdf/pdf_view_web_plugin.cc +++ b/pdf/pdf_view_web_plugin.cc
@@ -273,6 +273,20 @@ .Set("totalFileSize", base::checked_cast<int>(data.total_file_size)); } +PdfViewWebPlugin::SaveRequestType ParseSaveRequestType( + const std::string& save_request_type) { + if (save_request_type == "ANNOTATION") { + return PdfViewWebPlugin::SaveRequestType::kAnnotation; + } else if (save_request_type == "ORIGINAL") { + return PdfViewWebPlugin::SaveRequestType::kOriginal; + } else if (save_request_type == "EDITED") { + return PdfViewWebPlugin::SaveRequestType::kEdited; + } else if (save_request_type == "SEARCHIFIED") { + return PdfViewWebPlugin::SaveRequestType::kSearchified; + } + NOTREACHED(); +} + } // namespace #if BUILDFLAG(ENABLE_PDF_INK2) @@ -1820,7 +1834,7 @@ const base::Value::Dict& message) { const std::string& token = *message.FindString("token"); SaveRequestType request_type = - static_cast<SaveRequestType>(message.FindInt("saveRequestType").value()); + ParseSaveRequestType(*message.FindString("saveRequestType")); uint32_t offset = static_cast<uint32_t>(message.FindInt("offset").value()); uint32_t block_size = static_cast<uint32_t>(message.FindInt("blockSize").value()); @@ -1896,7 +1910,7 @@ void PdfViewWebPlugin::HandleSaveMessage(const base::Value::Dict& message) { const std::string& token = *message.FindString("token"); SaveRequestType request_type = - static_cast<SaveRequestType>(message.FindInt("saveRequestType").value()); + ParseSaveRequestType(*message.FindString("saveRequestType")); switch (request_type) { case SaveRequestType::kAnnotation:
diff --git a/pdf/pdf_view_web_plugin.h b/pdf/pdf_view_web_plugin.h index c330b25..f86c02e 100644 --- a/pdf/pdf_view_web_plugin.h +++ b/pdf/pdf_view_web_plugin.h
@@ -112,7 +112,9 @@ kFailed, }; - // Must match `SaveRequestType` in chrome/browser/resources/pdf/constants.ts. + // Must match `SaveRequestType` in + // `chrome/common/extensions/api/pdf_viewer_private.idl` and + // chromium/tools/typescript/definitions/pdf_viewer_private.d.ts. enum class SaveRequestType { kAnnotation = 0, kOriginal = 1,
diff --git a/pdf/pdf_view_web_plugin_unittest.cc b/pdf/pdf_view_web_plugin_unittest.cc index 75e325af..51eb30ab 100644 --- a/pdf/pdf_view_web_plugin_unittest.cc +++ b/pdf/pdf_view_web_plugin_unittest.cc
@@ -2156,7 +2156,7 @@ plugin_->OnMessage(ParseMessage(R"({ "type": "save", - "saveRequestType": 0, + "saveRequestType": "ANNOTATION", "token": "annotation-in-non-edit-mode", })")); @@ -2181,7 +2181,7 @@ plugin_->OnMessage(ParseMessage(R"({ "type": "save", - "saveRequestType": 0, + "saveRequestType": "ANNOTATION", "token": "annotation-in-edit-mode", })")); @@ -2205,7 +2205,7 @@ plugin_->OnMessage(ParseMessage(R"({ "type": "save", - "saveRequestType": 1, + "saveRequestType": "ORIGINAL", "token": "original-in-non-edit-mode", })")); @@ -2233,7 +2233,7 @@ plugin_->OnMessage(ParseMessage(R"({ "type": "save", - "saveRequestType": 1, + "saveRequestType": "ORIGINAL", "token": "original-in-edit-mode", })")); @@ -2257,7 +2257,7 @@ plugin_->OnMessage(ParseMessage(R"({ "type": "save", - "saveRequestType": 2, + "saveRequestType": "EDITED", "token": "edited-in-non-edit-mode", })")); } @@ -2279,7 +2279,7 @@ plugin_->OnMessage(ParseMessage(R"({ "type": "save", - "saveRequestType": 2, + "saveRequestType": "EDITED", "token": "edited-in-edit-mode", })")); } @@ -2291,9 +2291,24 @@ uint32_t offset, uint32_t block_size, std::string token) { + std::string request_type_string; + switch (request_type) { + case PdfViewWebPlugin::SaveRequestType::kAnnotation: + request_type_string = "ANNOTATION"; + break; + case PdfViewWebPlugin::SaveRequestType::kOriginal: + request_type_string = "ORIGINAL"; + break; + case PdfViewWebPlugin::SaveRequestType::kEdited: + request_type_string = "EDITED"; + break; + case PdfViewWebPlugin::SaveRequestType::kSearchified: + request_type_string = "SEARCHIFIED"; + break; + } return base::Value::Dict() .Set("type", "getSaveDataBlock") - .Set("saveRequestType", static_cast<int>(request_type)) + .Set("saveRequestType", request_type_string) .Set("offset", static_cast<int>(offset)) .Set("blockSize", static_cast<int>(block_size)) .Set("token", token); @@ -3384,7 +3399,7 @@ plugin_->OnMessage(ParseMessage(R"({ "type": "save", - "saveRequestType": 0, + "saveRequestType": "ANNOTATION", "token": "annotation-in-non-edit-mode", })")); @@ -3411,7 +3426,7 @@ plugin_->OnMessage(ParseMessage(R"({ "type": "save", - "saveRequestType": 0, + "saveRequestType": "ANNOTATION", "token": "annotation-in-edit-mode", })"));
diff --git a/remoting/host/linux/gnome_input_injector.cc b/remoting/host/linux/gnome_input_injector.cc index 232dea94..2628f85f 100644 --- a/remoting/host/linux/gnome_input_injector.cc +++ b/remoting/host/linux/gnome_input_injector.cc
@@ -5,13 +5,14 @@ #include "remoting/host/linux/gnome_input_injector.h" #include "base/notimplemented.h" -#include "remoting/host/linux/gnome_interaction_strategy.h" +#include "remoting/base/logging.h" +#include "remoting/host/linux/ei_sender_session.h" namespace remoting { -GnomeInputInjector::GnomeInputInjector( - base::WeakPtr<GnomeInteractionStrategy> session) - : session_(std::move(session)) {} +GnomeInputInjector::GnomeInputInjector(std::unique_ptr<EiSenderSession> session, + std::string_view stream_mapping_id) + : ei_session_(std::move(session)), stream_mapping_id_(stream_mapping_id) {} GnomeInputInjector::~GnomeInputInjector() = default; @@ -19,10 +20,11 @@ std::unique_ptr<protocol::ClipboardStub> client_clipboard) {} void GnomeInputInjector::InjectKeyEvent(const protocol::KeyEvent& event) { - if (!session_) { + if (!event.has_usb_keycode() || !event.has_pressed()) { + LOG(WARNING) << "Key event with no key info"; return; } - session_->InjectKeyEvent(event); + ei_session_->InjectKeyEvent(event.usb_keycode(), event.pressed()); } void GnomeInputInjector::InjectTextEvent(const protocol::TextEvent& event) { @@ -30,10 +32,42 @@ } void GnomeInputInjector::InjectMouseEvent(const protocol::MouseEvent& event) { - if (!session_) { - return; + bool event_sent = false; + if (event.has_fractional_coordinate() && + event.fractional_coordinate().has_x() && + event.fractional_coordinate().has_y()) { + ei_session_->InjectAbsolutePointerMove(stream_mapping_id_, + event.fractional_coordinate().x(), + event.fractional_coordinate().y()); + event_sent = true; + + } else if (event.has_delta_x() || event.has_delta_y()) { + ei_session_->InjectRelativePointerMove( + event.has_delta_x() ? event.delta_x() : 0, + event.has_delta_y() ? event.delta_y() : 0); + event_sent = true; } - session_->InjectMouseEvent(event); + + if (event.has_button() && event.has_button_down()) { + ei_session_->InjectButton(event.button(), event.button_down()); + event_sent = true; + } + + if (event.has_wheel_delta_x() || event.has_wheel_delta_y()) { + ei_session_->InjectScrollDelta( + event.has_wheel_delta_x() ? event.wheel_delta_x() : 0, + event.has_wheel_delta_y() ? event.wheel_delta_y() : 0); + event_sent = true; + } else if (event.has_wheel_ticks_x() || event.has_wheel_ticks_y()) { + ei_session_->InjectScrollDiscrete( + event.has_wheel_ticks_x() ? event.wheel_ticks_x() : 0, + event.has_wheel_ticks_y() ? event.wheel_ticks_y() : 0); + event_sent = true; + } + + if (!event_sent) { + LOG(WARNING) << "Mouse event with no relevant fields"; + } } void GnomeInputInjector::InjectTouchEvent(const protocol::TouchEvent& event) {
diff --git a/remoting/host/linux/gnome_input_injector.h b/remoting/host/linux/gnome_input_injector.h index 8c276e4..58b85bcd 100644 --- a/remoting/host/linux/gnome_input_injector.h +++ b/remoting/host/linux/gnome_input_injector.h
@@ -5,16 +5,27 @@ #ifndef REMOTING_HOST_LINUX_GNOME_INPUT_INJECTOR_H_ #define REMOTING_HOST_LINUX_GNOME_INPUT_INJECTOR_H_ -#include "base/memory/weak_ptr.h" +#include <string_view> + #include "remoting/host/input_injector.h" namespace remoting { -class GnomeInteractionStrategy; +class EiSenderSession; class GnomeInputInjector : public InputInjector { public: - explicit GnomeInputInjector(base::WeakPtr<GnomeInteractionStrategy> session); + // The stream's mapping-id is needed for injecting absolute mouse motion. + // Currently, there is only 1 capture-stream and its mapping-id never + // changes during the connection lifetime. + // TODO: crbug.com/432217140 - when multiple displays are supported, this + // parameter should be replaced with some kind of stream-mapping. This should + // convert the stream-id from the mouse-event's FractionalCoordinate to a + // mapping-id. Alternatively, EiSenderSession could maintain this mapping + // information, but this may depend on exactly how the stream-id will be + // implemented. + GnomeInputInjector(std::unique_ptr<EiSenderSession> session, + std::string_view stream_mapping_id); ~GnomeInputInjector() override; // InputInjector implementation @@ -31,7 +42,8 @@ void InjectClipboardEvent(const protocol::ClipboardEvent& event) override; private: - base::WeakPtr<GnomeInteractionStrategy> session_; + std::unique_ptr<EiSenderSession> ei_session_; + std::string stream_mapping_id_; }; } // namespace remoting
diff --git a/remoting/host/linux/gnome_interaction_strategy.cc b/remoting/host/linux/gnome_interaction_strategy.cc index 7120d66c..e2163ed 100644 --- a/remoting/host/linux/gnome_interaction_strategy.cc +++ b/remoting/host/linux/gnome_interaction_strategy.cc
@@ -112,7 +112,16 @@ std::unique_ptr<InputInjector> GnomeInteractionStrategy::CreateInputInjector() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return std::make_unique<GnomeInputInjector>(weak_ptr_factory_.GetWeakPtr()); + + // The EI session is guaranteed to exist, because this InteractionStrategy + // (and DesktopEnvironment) are only returned to the caller (ClientSession) + // after the EI session is initialized. + DCHECK(ei_session_); + + // Passing exclusive ownership to the input-injector allows it to use the EI + // session on a different thread. + return std::make_unique<GnomeInputInjector>(std::move(ei_session_), + capture_stream_.mapping_id()); } std::unique_ptr<DesktopResizer> @@ -399,57 +408,6 @@ std::move(init_callback_).Run(base::ok()); } -void GnomeInteractionStrategy::InjectKeyEvent(const protocol::KeyEvent& event) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!event.has_usb_keycode() || !event.has_pressed()) { - LOG(WARNING) << "Key event with no key info"; - return; - } - ei_session_->InjectKeyEvent(event.usb_keycode(), event.pressed()); -} - -void GnomeInteractionStrategy::InjectMouseEvent( - const protocol::MouseEvent& event) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - bool event_sent = false; - if (event.has_fractional_coordinate() && - event.fractional_coordinate().has_x() && - event.fractional_coordinate().has_y()) { - ei_session_->InjectAbsolutePointerMove(capture_stream_.mapping_id(), - event.fractional_coordinate().x(), - event.fractional_coordinate().y()); - event_sent = true; - - } else if (event.has_delta_x() || event.has_delta_y()) { - ei_session_->InjectRelativePointerMove( - event.has_delta_x() ? event.delta_x() : 0, - event.has_delta_y() ? event.delta_y() : 0); - event_sent = true; - } - - if (event.has_button() && event.has_button_down()) { - ei_session_->InjectButton(event.button(), event.button_down()); - event_sent = true; - } - - if (event.has_wheel_delta_x() || event.has_wheel_delta_y()) { - ei_session_->InjectScrollDelta( - event.has_wheel_delta_x() ? event.wheel_delta_x() : 0, - event.has_wheel_delta_y() ? event.wheel_delta_y() : 0); - event_sent = true; - } else if (event.has_wheel_ticks_x() || event.has_wheel_ticks_y()) { - ei_session_->InjectScrollDiscrete( - event.has_wheel_ticks_x() ? event.wheel_ticks_x() : 0, - event.has_wheel_ticks_y() ? event.wheel_ticks_y() : 0); - event_sent = true; - } - - if (event_sent) { - } else { - LOG(WARNING) << "Mouse event with no relevant fields"; - } -} - GnomeInteractionStrategyFactory::GnomeInteractionStrategyFactory( scoped_refptr<base::SequencedTaskRunner> ui_task_runner) : ui_task_runner_(std::move(ui_task_runner)) {}
diff --git a/remoting/host/linux/gnome_interaction_strategy.h b/remoting/host/linux/gnome_interaction_strategy.h index 2907062..6a389e10 100644 --- a/remoting/host/linux/gnome_interaction_strategy.h +++ b/remoting/host/linux/gnome_interaction_strategy.h
@@ -56,7 +56,6 @@ private: friend class GnomeDesktopResizer; friend class GnomeDisplayInfoLoader; - friend class GnomeInputInjector; friend class GnomeInteractionStrategyFactory; using InitCallback = @@ -82,10 +81,6 @@ void OnPipeWireStreamAdded(std::string mapping_id, std::tuple<std::uint32_t> args); - void InjectKeyEvent(const protocol::KeyEvent& event); - void InjectTextEvent(const protocol::TextEvent& event); - void InjectMouseEvent(const protocol::MouseEvent& event); - GDBusConnectionRef connection_ GUARDED_BY_CONTEXT(sequence_checker_); InitCallback init_callback_; gvariant::ObjectPath session_path_ GUARDED_BY_CONTEXT(sequence_checker_);
diff --git a/remoting/protocol/session_authz_reauthorizer.cc b/remoting/protocol/session_authz_reauthorizer.cc index a01f3f2..91838c1 100644 --- a/remoting/protocol/session_authz_reauthorizer.cc +++ b/remoting/protocol/session_authz_reauthorizer.cc
@@ -4,6 +4,7 @@ #include "remoting/protocol/session_authz_reauthorizer.h" +#include <algorithm> #include <memory> #include "base/check.h" @@ -20,6 +21,32 @@ namespace remoting::protocol { +namespace { + +constexpr base::TimeDelta kMinReauthTokenLifetime = base::Minutes(5); +constexpr base::TimeDelta kMinReauthRetryDuration = kMinReauthTokenLifetime / 2; +constexpr base::TimeDelta kMaxReauthRetryDuration = base::Minutes(10); + +base::TimeDelta ClampSessionReauthTokenLifetime( + base::TimeDelta session_reauth_token_lifetime) { + if (session_reauth_token_lifetime < kMinReauthTokenLifetime) { + LOG(WARNING) << session_reauth_token_lifetime << " is below the minimum " + << "session reauth token threshold, reauthorization is likely " + << "to fail"; + return kMinReauthTokenLifetime; + } + return session_reauth_token_lifetime; +} + +base::TimeDelta GetReauthInterval(base::TimeTicks reauth_token_expire_time) { + auto token_lifetime = reauth_token_expire_time - base::TimeTicks::Now(); + return token_lifetime - std::clamp(token_lifetime / 2, + kMinReauthRetryDuration, + kMaxReauthRetryDuration); +} + +} // namespace + SessionAuthzReauthorizer::SessionAuthzReauthorizer( SessionAuthzServiceClient* service_client, std::string_view session_id, @@ -29,9 +56,11 @@ : service_client_(service_client), session_id_(session_id), session_reauth_token_(session_reauth_token), - token_expire_time_(base::TimeTicks::Now() + - session_reauth_token_lifetime), - on_reauthorization_failed_(std::move(on_reauthorization_failed)) {} + on_reauthorization_failed_(std::move(on_reauthorization_failed)) { + token_expire_time_ = + base::TimeTicks::Now() + + ClampSessionReauthTokenLifetime(session_reauth_token_lifetime); +} SessionAuthzReauthorizer::~SessionAuthzReauthorizer() = default; @@ -41,8 +70,7 @@ } void SessionAuthzReauthorizer::ScheduleNextReauth() { - base::TimeDelta next_reauth_interval = - (token_expire_time_ - base::TimeTicks::Now()) / 2; + base::TimeDelta next_reauth_interval = GetReauthInterval(token_expire_time_); reauthorize_timer_.Start(FROM_HERE, next_reauth_interval, this, &SessionAuthzReauthorizer::Reauthorize); HOST_LOG << "Next reauthorization scheduled in " << next_reauth_interval; @@ -70,7 +98,8 @@ DCHECK(response->session_reauth_token_lifetime.is_positive()); session_reauth_token_ = response->session_reauth_token; token_expire_time_ = - base::TimeTicks::Now() + response->session_reauth_token_lifetime; + base::TimeTicks::Now() + + ClampSessionReauthTokenLifetime(response->session_reauth_token_lifetime); VLOG(1) << "SessionAuthz reauthorization succeeded."; ScheduleNextReauth(); }
diff --git a/remoting/protocol/session_authz_reauthorizer_unittest.cc b/remoting/protocol/session_authz_reauthorizer_unittest.cc index 3383ef2..2de5352 100644 --- a/remoting/protocol/session_authz_reauthorizer_unittest.cc +++ b/remoting/protocol/session_authz_reauthorizer_unittest.cc
@@ -54,6 +54,8 @@ protected: auto ResetReauthorizer(); + void InitializeReauthorizer(base::TimeDelta session_reauth_token_lifetime); + base::test::TaskEnvironment task_environment_{ base::test::TaskEnvironment::TimeSource::MOCK_TIME}; base::MockCallback<SessionAuthzReauthorizer::OnReauthorizationFailedCallback> @@ -64,11 +66,7 @@ }; SessionAuthzReauthorizerTest::SessionAuthzReauthorizerTest() { - reauthorizer_ = std::make_unique<SessionAuthzReauthorizer>( - &service_client_, kSessionId, kInitialReauthToken, kInitialTokenLifetime, - on_reauthorization_failed_callback_.Get()); - initial_token_expire_time_ = base::TimeTicks::Now() + kInitialTokenLifetime; - reauthorizer_->Start(); + InitializeReauthorizer(kInitialTokenLifetime); } SessionAuthzReauthorizerTest::~SessionAuthzReauthorizerTest() = default; @@ -77,8 +75,19 @@ return [&]() { reauthorizer_.reset(); }; } +void SessionAuthzReauthorizerTest::InitializeReauthorizer( + base::TimeDelta session_reauth_token_lifetime) { + reauthorizer_ = std::make_unique<SessionAuthzReauthorizer>( + &service_client_, kSessionId, kInitialReauthToken, + session_reauth_token_lifetime, on_reauthorization_failed_callback_.Get()); + initial_token_expire_time_ = + base::TimeTicks::Now() + session_reauth_token_lifetime; + reauthorizer_->Start(); +} + TEST_F(SessionAuthzReauthorizerTest, MultipleSuccessfulReauths) { - // Reauth is not triggered before half of the token lifetime has passed. + // Reauth is not triggered before half of the token lifetime has passed for a + // token with a lifetime of 10 minutes. EXPECT_CALL(service_client_, ReauthorizeHost(_, _, _, _)).Times(0); task_environment_.FastForwardBy(kInitialTokenLifetime / 2 - base::Seconds(10)); @@ -109,4 +118,47 @@ task_environment_.FastForwardBy(kInitialTokenLifetime / 2); } +TEST_F(SessionAuthzReauthorizerTest, MaxReauthRetryDurationApplied) { + // Initialize the Reauthorizer with a token lifetime of 1 hour. + InitializeReauthorizer(base::Minutes(60)); + + // Reauth is not triggered before the maximum reauth refresh duration. + EXPECT_CALL(service_client_, ReauthorizeHost(_, _, _, _)).Times(0); + task_environment_.FastForwardBy(base::Minutes(50) - base::Seconds(10)); + + // Reauth is triggered and includes a 30 minute token. + EXPECT_CALL(service_client_, ReauthorizeHost(kInitialReauthToken, kSessionId, + initial_token_expire_time_, _)) + .WillOnce(Respond("fake_second_reauth_token", base::Minutes(30))); + task_environment_.FastForwardBy(base::Seconds(10)); + + EXPECT_CALL(service_client_, ReauthorizeHost(_, _, _, _)).Times(0); + task_environment_.FastForwardBy(base::Minutes(20) - base::Seconds(10)); + + EXPECT_CALL(service_client_, + ReauthorizeHost("fake_second_reauth_token", kSessionId, _, _)) + .WillOnce(Respond("fake_third_reauth_token", base::Minutes(60))); + task_environment_.FastForwardBy(base::Seconds(10)); +} + +TEST_F(SessionAuthzReauthorizerTest, MinReauthRetryDurationApplied) { + // Initialize the Reauthorizer with a token lifetime of 4 minutes (below the + // minimum of 5 minutes). + InitializeReauthorizer(base::Minutes(4)); + + // Minimum expire time is actually 2 1/2 minutes. + auto actual_token_expire_time = base::Minutes(2) + base::Seconds(30); + + // Reauth is not triggered after 1/2 of the token lifetime and just before the + // minimum reauth refresh duration. + EXPECT_CALL(service_client_, ReauthorizeHost(_, _, _, _)).Times(0); + task_environment_.FastForwardBy(actual_token_expire_time - base::Seconds(10)); + + // Reauth is triggered and the second response includes a 30 minute token. + EXPECT_CALL(service_client_, + ReauthorizeHost(kInitialReauthToken, kSessionId, _, _)) + .WillOnce(Respond("fake_second_reauth_token", base::Minutes(30))); + task_environment_.FastForwardBy(base::Seconds(10)); +} + } // namespace remoting::protocol
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc index 8b8ad19..de2f8693 100644 --- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc +++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -247,6 +247,13 @@ return If((flags & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS()); } +SANDBOX_EXPORT ResultExpr RestrictMremapFlagsForODML() { + // No flags are allowed. + const uint64_t kAllowedMask = 0; + const Arg<int> flags(3); + return If((flags & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS()); +} + ResultExpr RestrictMprotectFlags() { // The flags you see are actually the allowed ones, and the variable is a // "denied" mask because of the negation operator.
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h index 915786b..b17b4e04 100644 --- a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h +++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
@@ -40,6 +40,10 @@ // Crash if any other flag is used. SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictMmapFlags(); +// Restrict the flags argument in mremap(2). +// Crash if any flags are used. +SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictMremapFlagsForODML(); + // Restrict the prot argument in mprotect(2). // Only allow: PROT_READ | PROT_WRITE | PROT_EXEC. // PROT_BTI | PROT_MTE is additionally allowed on 64-bit Arm.
diff --git a/sandbox/policy/linux/bpf_cros_amd_gpu_policy_linux.cc b/sandbox/policy/linux/bpf_cros_amd_gpu_policy_linux.cc index df2567f..4fd379c 100644 --- a/sandbox/policy/linux/bpf_cros_amd_gpu_policy_linux.cc +++ b/sandbox/policy/linux/bpf_cros_amd_gpu_policy_linux.cc
@@ -28,7 +28,8 @@ namespace sandbox { namespace policy { -CrosAmdGpuProcessPolicy::CrosAmdGpuProcessPolicy() {} +CrosAmdGpuProcessPolicy::CrosAmdGpuProcessPolicy(bool allow_mremap) + : GpuProcessPolicy(allow_mremap) {} CrosAmdGpuProcessPolicy::~CrosAmdGpuProcessPolicy() {}
diff --git a/sandbox/policy/linux/bpf_cros_amd_gpu_policy_linux.h b/sandbox/policy/linux/bpf_cros_amd_gpu_policy_linux.h index 5711affb..b8b1d9b 100644 --- a/sandbox/policy/linux/bpf_cros_amd_gpu_policy_linux.h +++ b/sandbox/policy/linux/bpf_cros_amd_gpu_policy_linux.h
@@ -15,7 +15,7 @@ // This policy is for AMD GPUs running on Chrome OS. class SANDBOX_POLICY_EXPORT CrosAmdGpuProcessPolicy : public GpuProcessPolicy { public: - CrosAmdGpuProcessPolicy(); + explicit CrosAmdGpuProcessPolicy(bool allow_mremap); CrosAmdGpuProcessPolicy(const CrosAmdGpuProcessPolicy&) = delete; CrosAmdGpuProcessPolicy& operator=(const CrosAmdGpuProcessPolicy&) = delete;
diff --git a/sandbox/policy/linux/bpf_cros_arm_gpu_policy_linux.cc b/sandbox/policy/linux/bpf_cros_arm_gpu_policy_linux.cc index 29798bc62..4d447ebb 100644 --- a/sandbox/policy/linux/bpf_cros_arm_gpu_policy_linux.cc +++ b/sandbox/policy/linux/bpf_cros_arm_gpu_policy_linux.cc
@@ -26,9 +26,12 @@ namespace sandbox { namespace policy { -CrosArmGpuProcessPolicy::CrosArmGpuProcessPolicy(bool allow_shmat) +CrosArmGpuProcessPolicy::CrosArmGpuProcessPolicy(bool allow_mremap, + bool allow_shmat) + : GpuProcessPolicy(allow_mremap) #if defined(__arm__) || defined(__aarch64__) - : allow_shmat_(allow_shmat) + , + allow_shmat_(allow_shmat) #endif { }
diff --git a/sandbox/policy/linux/bpf_cros_arm_gpu_policy_linux.h b/sandbox/policy/linux/bpf_cros_arm_gpu_policy_linux.h index f1d6f7e..1448897 100644 --- a/sandbox/policy/linux/bpf_cros_arm_gpu_policy_linux.h +++ b/sandbox/policy/linux/bpf_cros_arm_gpu_policy_linux.h
@@ -15,7 +15,7 @@ // This policy is for Chrome OS ARM. class SANDBOX_POLICY_EXPORT CrosArmGpuProcessPolicy : public GpuProcessPolicy { public: - explicit CrosArmGpuProcessPolicy(bool allow_shmat); + CrosArmGpuProcessPolicy(bool allow_mremap, bool allow_shmat); CrosArmGpuProcessPolicy(const CrosArmGpuProcessPolicy&) = delete; CrosArmGpuProcessPolicy& operator=(const CrosArmGpuProcessPolicy&) = delete;
diff --git a/sandbox/policy/linux/bpf_cros_intel_gpu_policy_linux.cc b/sandbox/policy/linux/bpf_cros_intel_gpu_policy_linux.cc index dae49bdb..095c645 100644 --- a/sandbox/policy/linux/bpf_cros_intel_gpu_policy_linux.cc +++ b/sandbox/policy/linux/bpf_cros_intel_gpu_policy_linux.cc
@@ -20,7 +20,8 @@ namespace sandbox { namespace policy { -CrosIntelGpuProcessPolicy::CrosIntelGpuProcessPolicy() {} +CrosIntelGpuProcessPolicy::CrosIntelGpuProcessPolicy(bool allow_mremap) + : GpuProcessPolicy(allow_mremap) {} CrosIntelGpuProcessPolicy::~CrosIntelGpuProcessPolicy() {}
diff --git a/sandbox/policy/linux/bpf_cros_intel_gpu_policy_linux.h b/sandbox/policy/linux/bpf_cros_intel_gpu_policy_linux.h index 10205d5..324eae6 100644 --- a/sandbox/policy/linux/bpf_cros_intel_gpu_policy_linux.h +++ b/sandbox/policy/linux/bpf_cros_intel_gpu_policy_linux.h
@@ -15,7 +15,7 @@ // This policy is for ChromeOS running on Intel GPUs. class SANDBOX_POLICY_EXPORT CrosIntelGpuProcessPolicy : public GpuProcessPolicy { public: - CrosIntelGpuProcessPolicy(); + explicit CrosIntelGpuProcessPolicy(bool allow_mremap); CrosIntelGpuProcessPolicy(const CrosIntelGpuProcessPolicy&) = delete; CrosIntelGpuProcessPolicy& operator=(const CrosIntelGpuProcessPolicy&) = delete;
diff --git a/sandbox/policy/linux/bpf_cros_nvidia_gpu_policy_linux.cc b/sandbox/policy/linux/bpf_cros_nvidia_gpu_policy_linux.cc index d68c7e14..f5104bba 100644 --- a/sandbox/policy/linux/bpf_cros_nvidia_gpu_policy_linux.cc +++ b/sandbox/policy/linux/bpf_cros_nvidia_gpu_policy_linux.cc
@@ -20,7 +20,8 @@ namespace sandbox::policy { -CrosNvidiaGpuProcessPolicy::CrosNvidiaGpuProcessPolicy() = default; +CrosNvidiaGpuProcessPolicy::CrosNvidiaGpuProcessPolicy(bool allow_mremap) + : GpuProcessPolicy(allow_mremap) {} CrosNvidiaGpuProcessPolicy::~CrosNvidiaGpuProcessPolicy() = default;
diff --git a/sandbox/policy/linux/bpf_cros_nvidia_gpu_policy_linux.h b/sandbox/policy/linux/bpf_cros_nvidia_gpu_policy_linux.h index 8c9a968..2b61449 100644 --- a/sandbox/policy/linux/bpf_cros_nvidia_gpu_policy_linux.h +++ b/sandbox/policy/linux/bpf_cros_nvidia_gpu_policy_linux.h
@@ -15,7 +15,7 @@ class SANDBOX_POLICY_EXPORT CrosNvidiaGpuProcessPolicy : public GpuProcessPolicy { public: - CrosNvidiaGpuProcessPolicy(); + explicit CrosNvidiaGpuProcessPolicy(bool allow_mremap); CrosNvidiaGpuProcessPolicy(const CrosNvidiaGpuProcessPolicy&) = delete; CrosNvidiaGpuProcessPolicy& operator=(const CrosNvidiaGpuProcessPolicy&) =
diff --git a/sandbox/policy/linux/bpf_cros_virtio_gpu_policy_linux.cc b/sandbox/policy/linux/bpf_cros_virtio_gpu_policy_linux.cc index b3c8a802..1f385d90 100644 --- a/sandbox/policy/linux/bpf_cros_virtio_gpu_policy_linux.cc +++ b/sandbox/policy/linux/bpf_cros_virtio_gpu_policy_linux.cc
@@ -20,7 +20,8 @@ namespace sandbox::policy { -CrosVirtIoGpuProcessPolicy::CrosVirtIoGpuProcessPolicy() = default; +CrosVirtIoGpuProcessPolicy::CrosVirtIoGpuProcessPolicy(bool allow_mremap) + : GpuProcessPolicy(allow_mremap) {} CrosVirtIoGpuProcessPolicy::~CrosVirtIoGpuProcessPolicy() = default;
diff --git a/sandbox/policy/linux/bpf_cros_virtio_gpu_policy_linux.h b/sandbox/policy/linux/bpf_cros_virtio_gpu_policy_linux.h index fb36ea63..76cdab0 100644 --- a/sandbox/policy/linux/bpf_cros_virtio_gpu_policy_linux.h +++ b/sandbox/policy/linux/bpf_cros_virtio_gpu_policy_linux.h
@@ -15,7 +15,7 @@ class SANDBOX_POLICY_EXPORT CrosVirtIoGpuProcessPolicy : public GpuProcessPolicy { public: - CrosVirtIoGpuProcessPolicy(); + explicit CrosVirtIoGpuProcessPolicy(bool allow_mremap); CrosVirtIoGpuProcessPolicy(const CrosVirtIoGpuProcessPolicy&) = delete; CrosVirtIoGpuProcessPolicy& operator=(const CrosVirtIoGpuProcessPolicy&) =
diff --git a/sandbox/policy/linux/bpf_gpu_policy_linux.cc b/sandbox/policy/linux/bpf_gpu_policy_linux.cc index 793545d..c6caf2e9 100644 --- a/sandbox/policy/linux/bpf_gpu_policy_linux.cc +++ b/sandbox/policy/linux/bpf_gpu_policy_linux.cc
@@ -34,7 +34,8 @@ namespace sandbox { namespace policy { -GpuProcessPolicy::GpuProcessPolicy() {} +GpuProcessPolicy::GpuProcessPolicy(bool allow_mremap) + : allow_mremap_(allow_mremap) {} GpuProcessPolicy::~GpuProcessPolicy() {} @@ -87,6 +88,13 @@ // We also hit this on the linux_chromeos bot but don't yet know what // weird flags were involved. case __NR_mprotect: + return Allow(); + // XNNPACK needs mremap when building weight caches. + case __NR_mremap: + if (allow_mremap_) { + return RestrictMremapFlagsForODML(); + } + break; // TODO(jln): restrict prctl. case __NR_prctl: case __NR_sysinfo:
diff --git a/sandbox/policy/linux/bpf_gpu_policy_linux.h b/sandbox/policy/linux/bpf_gpu_policy_linux.h index 4ef6054..472abb8 100644 --- a/sandbox/policy/linux/bpf_gpu_policy_linux.h +++ b/sandbox/policy/linux/bpf_gpu_policy_linux.h
@@ -14,7 +14,7 @@ class SANDBOX_POLICY_EXPORT GpuProcessPolicy : public BPFBasePolicy { public: - GpuProcessPolicy(); + explicit GpuProcessPolicy(bool allow_mremap); GpuProcessPolicy(const GpuProcessPolicy&) = delete; GpuProcessPolicy& operator=(const GpuProcessPolicy&) = delete; @@ -22,6 +22,9 @@ ~GpuProcessPolicy() override; bpf_dsl::ResultExpr EvaluateSyscall(int system_call_number) const override; + + private: + bool allow_mremap_; }; } // namespace policy
diff --git a/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc b/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc index 29749d9..0a62972 100644 --- a/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc +++ b/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc
@@ -123,27 +123,28 @@ } std::unique_ptr<BPFBasePolicy> GetGpuProcessSandbox( - const SandboxSeccompBPF::Options& options) { + const SandboxSeccompBPF::Options& options, + bool allow_mremap) { if (IsChromeOS() || UseChromecastSandboxAllowlist()) { if (IsArchitectureArm()) { return std::make_unique<CrosArmGpuProcessPolicy>( - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kGpuSandboxAllowSysVShm)); + allow_mremap, base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kGpuSandboxAllowSysVShm)); } if (options.use_amd_specific_policies) { - return std::make_unique<CrosAmdGpuProcessPolicy>(); + return std::make_unique<CrosAmdGpuProcessPolicy>(allow_mremap); } if (options.use_intel_specific_policies) { - return std::make_unique<CrosIntelGpuProcessPolicy>(); + return std::make_unique<CrosIntelGpuProcessPolicy>(allow_mremap); } if (options.use_nvidia_specific_policies) { - return std::make_unique<CrosNvidiaGpuProcessPolicy>(); + return std::make_unique<CrosNvidiaGpuProcessPolicy>(allow_mremap); } if (options.use_virtio_specific_policies) { - return std::make_unique<CrosVirtIoGpuProcessPolicy>(); + return std::make_unique<CrosVirtIoGpuProcessPolicy>(allow_mremap); } } - return std::make_unique<GpuProcessPolicy>(); + return std::make_unique<GpuProcessPolicy>(allow_mremap); } #endif // !defined(IN_NACL_HELPER) @@ -186,11 +187,11 @@ const SandboxSeccompBPF::Options& options) { switch (sandbox_type) { case sandbox::mojom::Sandbox::kGpu: - return GetGpuProcessSandbox(options); + return GetGpuProcessSandbox(options, /*allow_mremap=*/false); case sandbox::mojom::Sandbox::kRenderer: return std::make_unique<RendererProcessPolicy>(); case sandbox::mojom::Sandbox::kOnDeviceModelExecution: - return GetGpuProcessSandbox(options); + return GetGpuProcessSandbox(options, /*allow_mremap=*/true); case sandbox::mojom::Sandbox::kUtility: return std::make_unique<UtilityProcessPolicy>(); case sandbox::mojom::Sandbox::kCdm: @@ -227,7 +228,7 @@ // TODO(b/255554267): we're using the GPU process sandbox policy for now // as a transition step. However, we should create a policy that's tighter // just for hardware video encoding. - return GetGpuProcessSandbox(options); + return GetGpuProcessSandbox(options, /*allow_mremap=*/false); #endif // BUILDFLAG(USE_LINUX_VIDEO_ACCELERATION) #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) #if BUILDFLAG(IS_LINUX)
diff --git a/services/network/public/cpp/simple_url_loader_unittest.cc b/services/network/public/cpp/simple_url_loader_unittest.cc index 6648eec..6acb193 100644 --- a/services/network/public/cpp/simple_url_loader_unittest.cc +++ b/services/network/public/cpp/simple_url_loader_unittest.cc
@@ -38,6 +38,7 @@ #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/test/test_timeouts.h" +#include "base/threading/thread_restrictions.h" #include "base/time/time.h" #include "build/build_config.h" #include "mojo/public/c/system/types.h"
diff --git a/services/on_device_model/BUILD.gn b/services/on_device_model/BUILD.gn index 29bf2c7..5d56e28 100644 --- a/services/on_device_model/BUILD.gn +++ b/services/on_device_model/BUILD.gn
@@ -28,6 +28,7 @@ ] deps = [ ":backend_interfaces", + "//build:chromecast_buildflags", "//components/optimization_guide/core:features", "//services/on_device_model/fake", "//services/on_device_model/ml:ml_no_internal", @@ -62,7 +63,7 @@ if (is_android) { deps += [ "//services/on_device_model/android" ] } - if (!is_fuchsia) { + if (!is_fuchsia && !(is_linux && enable_cast_receiver)) { deps += [ "//third_party/dawn/include/dawn:cpp_headers", "//third_party/dawn/src/dawn:proc",
diff --git a/services/on_device_model/android/BUILD.gn b/services/on_device_model/android/BUILD.gn index 942c4c0..ecaf395 100644 --- a/services/on_device_model/android/BUILD.gn +++ b/services/on_device_model/android/BUILD.gn
@@ -46,7 +46,7 @@ "java/src/org/chromium/on_device_model/AiCoreSession.java", "java/src/org/chromium/on_device_model/AiCoreSessionFactory.java", "java/src/org/chromium/on_device_model/AiCoreSessionUpstreamImpl.java", - "java/src/org/chromium/on_device_model/InputPiece.java", + "java/src/org/chromium/on_device_model/InputPieceHelper.java", "java/src/org/chromium/on_device_model/OnDeviceModelBridge.java", ] deps = [ @@ -77,7 +77,7 @@ generate_jni("jni_headers") { sources = [ "java/src/org/chromium/on_device_model/AiCoreSession.java", - "java/src/org/chromium/on_device_model/InputPiece.java", + "java/src/org/chromium/on_device_model/InputPieceHelper.java", "java/src/org/chromium/on_device_model/OnDeviceModelBridge.java", ] }
diff --git a/services/on_device_model/android/backend_session_impl_android.cc b/services/on_device_model/android/backend_session_impl_android.cc index ed525345..094b5c4 100644 --- a/services/on_device_model/android/backend_session_impl_android.cc +++ b/services/on_device_model/android/backend_session_impl_android.cc
@@ -22,42 +22,10 @@ // Must come after all headers that specialize FromJniType() / ToJniType(). #include "services/on_device_model/android/jni_headers/AiCoreSession_jni.h" -#include "services/on_device_model/android/jni_headers/InputPiece_jni.h" +#include "services/on_device_model/android/jni_headers/InputPieceHelper_jni.h" namespace on_device_model { -namespace { - -base::android::ScopedJavaLocalRef<jobject> ToJavaInputPiece( - JNIEnv* env, - const ml::InputPiece& input) { - if (std::holds_alternative<std::string>(input)) { - return Java_InputPiece_createText(env, - base::android::ConvertUTF8ToJavaString( - env, std::get<std::string>(input))); - } else if (std::holds_alternative<ml::Token>(input)) { - return Java_InputPiece_createToken( - env, static_cast<int>(std::get<ml::Token>(input))); - } - // TODO(crbug.com/425408635): Support bitmap and audio. - NOTREACHED(); -} - -base::android::ScopedJavaLocalRef<jobjectArray> ToJavaInputPieceArray( - JNIEnv* env, - const std::vector<ml::InputPiece>& inputs) { - std::vector<base::android::ScopedJavaLocalRef<jobject>> java_inputs( - inputs.size()); - std::transform(inputs.begin(), inputs.end(), java_inputs.begin(), - [&](const ml::InputPiece& input) { - return ToJavaInputPiece(env, input); - }); - return base::android::ToTypedJavaArrayOfObjects( - env, java_inputs, org_chromium_on_1device_1model_InputPiece_clazz(env)); -} - -} // namespace - BackendSessionImplAndroid::BackendSessionImplAndroid( on_device_model::mojom::SessionParamsPtr params) : java_session_(OnDeviceModelBridge::CreateSession(std::move(params))) {} @@ -86,9 +54,24 @@ responder_.Bind(std::move(response)); JNIEnv* env = base::android::AttachCurrentThread(); + std::vector<base::android::ScopedJavaLocalRef<jobject>> java_inputs; + for (const auto& piece : context_input_pieces_) { + if (std::holds_alternative<ml::Token>(piece)) { + java_inputs.push_back(Java_InputPieceHelper_fromToken( + env, static_cast<int>(std::get<ml::Token>(piece)))); + } else if (std::holds_alternative<std::string>(piece)) { + java_inputs.push_back(Java_InputPieceHelper_fromText( + env, base::android::ConvertUTF8ToJavaString( + env, std::get<std::string>(piece)))); + } else { + // TODO(crbug.com/425408635): Support image and audio input. + NOTREACHED(); + } + } + Java_AiCoreSession_generate( env, java_session_, reinterpret_cast<intptr_t>(this), - ToJavaInputPieceArray(env, context_input_pieces_)); + base::android::ToJavaArrayOfObjects(env, java_inputs)); std::move(on_complete).Run(); }
diff --git a/services/on_device_model/android/java/src/org/chromium/on_device_model/AiCoreSession.java b/services/on_device_model/android/java/src/org/chromium/on_device_model/AiCoreSession.java index c8124d3..1a9f91b 100644 --- a/services/on_device_model/android/java/src/org/chromium/on_device_model/AiCoreSession.java +++ b/services/on_device_model/android/java/src/org/chromium/on_device_model/AiCoreSession.java
@@ -20,10 +20,11 @@ * * @param nativeBackendSession The pointer to the native BackendSession. Used to deliver the * result back to the native side. - * @param inputPieces The input pieces to generate the response. + * @param inputPieces The input pieces to generate the response. Should always be an instance of + * mojom::InputPiece. */ @CalledByNative - void generate(long nativeBackendSession, InputPiece[] inputPieces); + void generate(long nativeBackendSession, Object[] inputPieces); /** * Called when the native session is destroyed. The implementation class should not call native
diff --git a/services/on_device_model/android/java/src/org/chromium/on_device_model/AiCoreSessionUpstreamImpl.java b/services/on_device_model/android/java/src/org/chromium/on_device_model/AiCoreSessionUpstreamImpl.java index d1e7910..f20a91d 100644 --- a/services/on_device_model/android/java/src/org/chromium/on_device_model/AiCoreSessionUpstreamImpl.java +++ b/services/on_device_model/android/java/src/org/chromium/on_device_model/AiCoreSessionUpstreamImpl.java
@@ -10,7 +10,7 @@ @NullMarked class AiCoreSessionUpstreamImpl implements AiCoreSession { @Override - public void generate(long nativeBackendSession, InputPiece[] inputPieces) { + public void generate(long nativeBackendSession, Object[] inputPieces) { // TODO(crbug.com/425408635): Return an error instead. AiCoreSessionJni.get().onResponse(nativeBackendSession, "AiCore response"); AiCoreSessionJni.get().onComplete(nativeBackendSession);
diff --git a/services/on_device_model/android/java/src/org/chromium/on_device_model/InputPiece.java b/services/on_device_model/android/java/src/org/chromium/on_device_model/InputPiece.java deleted file mode 100644 index 36e62d0..0000000 --- a/services/on_device_model/android/java/src/org/chromium/on_device_model/InputPiece.java +++ /dev/null
@@ -1,85 +0,0 @@ -// Copyright 2025 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.on_device_model; - -import androidx.annotation.IntDef; -import androidx.annotation.Nullable; - -import org.jni_zero.CalledByNative; -import org.jni_zero.JNINamespace; - -import org.chromium.build.annotations.NullMarked; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * This is the Java representation of the C++ ml::InputPiece. It implements the equivalent - * functionality of std::variant. - */ -@JNINamespace("on_device_model") -@NullMarked -public final class InputPiece { - // LINT.IfChange(Token) - @IntDef({Token.SYSTEM, Token.MODEL, Token.USER, Token.END}) - @Retention(RetentionPolicy.SOURCE) - public @interface Token { - int SYSTEM = 0; - int MODEL = 1; - int USER = 2; - int END = 3; - } - - // LINT.ThenChange(//services/on_device_model/ml/chrome_ml_types.h:Token) - - @IntDef({InputPieceType.TEXT, InputPieceType.TOKEN}) - @Retention(RetentionPolicy.SOURCE) - // LINT.IfChange(InputPieceType) - private @interface InputPieceType { - int TEXT = 0; - int TOKEN = 1; - } - - // LINT.ThenChange(//services/on_device_model/ml/chrome_ml_types.h:InputPiece) - - @InputPieceType private final int mType; - @Nullable private final String mText; - private final int mTokenId; - - private InputPiece(@InputPieceType int type, @Nullable String text, int tokenId) { - mType = type; - mText = text; - mTokenId = tokenId; - } - - @CalledByNative - private static InputPiece createText(String text) { - return new InputPiece(InputPieceType.TEXT, text, -1); - } - - @CalledByNative - private static InputPiece createToken(@Token int tokenId) { - return new InputPiece(InputPieceType.TOKEN, null, tokenId); - } - - public boolean isText() { - return mType == InputPieceType.TEXT; - } - - public boolean isToken() { - return mType == InputPieceType.TOKEN; - } - - public String getText() { - assert isText() : "Cannot call getText() for a token InputPiece."; - assert mText != null; - return mText; - } - - public @Token int getTokenId() { - assert isToken() : "Cannot call getTokenId() for a text InputPiece."; - return mTokenId; - } -}
diff --git a/services/on_device_model/android/java/src/org/chromium/on_device_model/InputPieceHelper.java b/services/on_device_model/android/java/src/org/chromium/on_device_model/InputPieceHelper.java new file mode 100644 index 0000000..64d9787 --- /dev/null +++ b/services/on_device_model/android/java/src/org/chromium/on_device_model/InputPieceHelper.java
@@ -0,0 +1,29 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.on_device_model; + +import org.jni_zero.CalledByNative; +import org.jni_zero.JNINamespace; + +import org.chromium.build.annotations.NullMarked; +import org.chromium.on_device_model.mojom.InputPiece; + +@JNINamespace("on_device_model") +@NullMarked +class InputPieceHelper { + @CalledByNative + private static InputPiece fromToken(int token) { + InputPiece inputPiece = new InputPiece(); + inputPiece.setToken(token); + return inputPiece; + } + + @CalledByNative + private static InputPiece fromText(String text) { + InputPiece inputPiece = new InputPiece(); + inputPiece.setText(text); + return inputPiece; + } +}
diff --git a/services/on_device_model/android/native_java_unittests/src/org/chromium/on_device_model/OnDeviceModelBridgeNativeUnitTestHelper.java b/services/on_device_model/android/native_java_unittests/src/org/chromium/on_device_model/OnDeviceModelBridgeNativeUnitTestHelper.java index fa45e45..0e95acd6 100644 --- a/services/on_device_model/android/native_java_unittests/src/org/chromium/on_device_model/OnDeviceModelBridgeNativeUnitTestHelper.java +++ b/services/on_device_model/android/native_java_unittests/src/org/chromium/on_device_model/OnDeviceModelBridgeNativeUnitTestHelper.java
@@ -8,7 +8,9 @@ import org.jni_zero.CalledByNative; import org.chromium.base.ServiceLoaderUtil; +import org.chromium.on_device_model.mojom.InputPiece; import org.chromium.on_device_model.mojom.SessionParams; +import org.chromium.on_device_model.mojom.Token; /** * Helper class to verify the JNI bridge. Invoked by native unit tests: @@ -32,26 +34,31 @@ } @Override - public void generate(long nativeBackendSession, InputPiece[] inputPieces) { + public void generate(long nativeBackendSession, Object[] inputPieces) { StringBuilder sb = new StringBuilder(); - for (InputPiece piece : inputPieces) { - if (piece.isText()) { - sb.append(piece.getText()); - } else if (piece.isToken()) { - switch (piece.getTokenId()) { - case InputPiece.Token.SYSTEM: - sb.append("<system>"); - break; - case InputPiece.Token.MODEL: - sb.append("<model>"); - break; - case InputPiece.Token.USER: - sb.append("<user>"); - break; - case InputPiece.Token.END: - sb.append("<end>"); - break; - } + for (Object piece : inputPieces) { + assert piece instanceof InputPiece; + InputPiece inputPiece = (InputPiece) piece; + switch (inputPiece.which()) { + case InputPiece.Tag.Token: + switch (inputPiece.getToken()) { + case Token.SYSTEM: + sb.append("<system>"); + break; + case Token.MODEL: + sb.append("<model>"); + break; + case Token.USER: + sb.append("<user>"); + break; + case Token.END: + sb.append("<end>"); + break; + } + break; + case InputPiece.Tag.Text: + sb.append(inputPiece.getText()); + break; } } AiCoreSessionJni.get().onResponse(nativeBackendSession, sb.toString());
diff --git a/services/on_device_model/ml/chrome_ml_types.h b/services/on_device_model/ml/chrome_ml_types.h index 2307487..c222bad 100644 --- a/services/on_device_model/ml/chrome_ml_types.h +++ b/services/on_device_model/ml/chrome_ml_types.h
@@ -18,7 +18,6 @@ inline constexpr uint32_t kMinTopK = 1; inline constexpr float kMinTemperature = 0.0f; -// LINT.IfChange(Token) enum class Token { // Prefix for system text. kSystem, @@ -29,15 +28,12 @@ // End a system/model/user section. kEnd, }; -// LINT.ThenChange(//services/on_device_model/android/java/src/org/chromium/on_device_model/InputPiece.java:Token) // If an InputPiece holds a `bool`, then the operation should fail. This means // the input came from a future client version and can't be handled in the // current library version. -// LINT.IfChange(InputPiece) using InputPiece = std::variant<Token, std::string, SkBitmap, AudioBuffer, bool>; -// LINT.ThenChange(//services/on_device_model/android/java/src/org/chromium/on_device_model/InputPiece.java:InputPiece) // Options for specifying the performance characteristics of the model to load. enum class ModelPerformanceHint {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 56bfb9e..552884f 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -523,21 +523,6 @@ ] } ], - "AllowTabClosingUponMinimization": [ - { - "platforms": [ - "android" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "AllowTabClosingUponMinimization" - ] - } - ] - } - ], "AndroidAnimateSuggestionsListAppearance": [ { "platforms": [ @@ -15145,6 +15130,27 @@ ] } ], + "MojoIpcChannelReceive": [ + { + "platforms": [ + "android", + "chromeos", + "fuchsia", + "ios", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "Disabled", + "disable_features": [ + "MojoIpcChannelReceive" + ] + } + ] + } + ], "MojoPredictiveAllocation": [ { "platforms": [ @@ -16649,6 +16655,7 @@ "intent_picker": "true", "lens_overlay": "true", "manage_passwords": "true", + "mandatory_reauth": "true", "memory_saver": "true", "offer_notification": "true", "price_insights": "true",
diff --git a/third_party/android_deps/autorolled/BUILD.gn b/third_party/android_deps/autorolled/BUILD.gn index 265e813..ff25aefd 100644 --- a/third_party/android_deps/autorolled/BUILD.gn +++ b/third_party/android_deps/autorolled/BUILD.gn
@@ -594,7 +594,7 @@ # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. java_prebuilt("io_grpc_grpc_api_java") { - jar_path = "autorolled/cipd/libs/io_grpc_grpc_api/grpc-api-1.73.0.jar" + jar_path = "autorolled/cipd/libs/io_grpc_grpc_api/grpc-api-1.74.0.jar" output_name = "io_grpc_grpc_api" supports_android = true requires_android = true @@ -609,7 +609,7 @@ # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. android_aar_prebuilt("io_grpc_grpc_binder_java") { aar_path = - "autorolled/cipd/libs/io_grpc_grpc_binder/grpc-binder-1.73.0.aar" + "autorolled/cipd/libs/io_grpc_grpc_binder/grpc-binder-1.74.0.aar" info_path = "autorolled/committed/libs/io_grpc_grpc_binder/io_grpc_grpc_binder.info" enable_bytecode_checks = false deps = [ @@ -624,7 +624,7 @@ # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. java_prebuilt("io_grpc_grpc_protobuf_lite_java") { - jar_path = "autorolled/cipd/libs/io_grpc_grpc_protobuf_lite/grpc-protobuf-lite-1.73.0.jar" + jar_path = "autorolled/cipd/libs/io_grpc_grpc_protobuf_lite/grpc-protobuf-lite-1.74.0.jar" output_name = "io_grpc_grpc_protobuf_lite" supports_android = true requires_android = true @@ -639,7 +639,7 @@ # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. java_prebuilt("io_grpc_grpc_stub_java") { - jar_path = "autorolled/cipd/libs/io_grpc_grpc_stub/grpc-stub-1.73.0.jar" + jar_path = "autorolled/cipd/libs/io_grpc_grpc_stub/grpc-stub-1.74.0.jar" output_name = "io_grpc_grpc_stub" supports_android = true requires_android = true @@ -1319,7 +1319,7 @@ # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. java_prebuilt("io_grpc_grpc_context_java") { jar_path = - "autorolled/cipd/libs/io_grpc_grpc_context/grpc-context-1.73.0.jar" + "autorolled/cipd/libs/io_grpc_grpc_context/grpc-context-1.74.0.jar" output_name = "io_grpc_grpc_context" supports_android = true requires_android = true @@ -1336,7 +1336,7 @@ # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. java_prebuilt("io_grpc_grpc_core_java") { - jar_path = "autorolled/cipd/libs/io_grpc_grpc_core/grpc-core-1.73.0.jar" + jar_path = "autorolled/cipd/libs/io_grpc_grpc_core/grpc-core-1.74.0.jar" output_name = "io_grpc_grpc_core" supports_android = true requires_android = true
diff --git a/third_party/android_deps/autorolled/VERSION.txt b/third_party/android_deps/autorolled/VERSION.txt index ed35e36..4a6a87c8 100644 --- a/third_party/android_deps/autorolled/VERSION.txt +++ b/third_party/android_deps/autorolled/VERSION.txt
@@ -1 +1 @@ -e977bc82797d5a2.95938065338129e \ No newline at end of file +66a0cfee2624f6d.64d2548efa5ab3a \ No newline at end of file
diff --git a/third_party/android_deps/autorolled/bill_of_materials.json b/third_party/android_deps/autorolled/bill_of_materials.json index 4fb3f130..147da87c 100644 --- a/third_party/android_deps/autorolled/bill_of_materials.json +++ b/third_party/android_deps/autorolled/bill_of_materials.json
@@ -527,7 +527,7 @@ { "name": "graphics-path", "group": "androidx.graphics", - "version": "1.0.1" + "version": "1.1.0-SNAPSHOT" }, { "name": "graphics-shapes", @@ -1522,32 +1522,32 @@ { "name": "grpc-api", "group": "io.grpc", - "version": "1.73.0" + "version": "1.74.0" }, { "name": "grpc-binder", "group": "io.grpc", - "version": "1.73.0" + "version": "1.74.0" }, { "name": "grpc-context", "group": "io.grpc", - "version": "1.73.0" + "version": "1.74.0" }, { "name": "grpc-core", "group": "io.grpc", - "version": "1.73.0" + "version": "1.74.0" }, { "name": "grpc-protobuf-lite", "group": "io.grpc", - "version": "1.73.0" + "version": "1.74.0" }, { "name": "grpc-stub", "group": "io.grpc", - "version": "1.73.0" + "version": "1.74.0" }, { "name": "perfmark-api",
diff --git a/third_party/android_deps/autorolled/build.gradle b/third_party/android_deps/autorolled/build.gradle index 3909bb1..642dded 100644 --- a/third_party/android_deps/autorolled/build.gradle +++ b/third_party/android_deps/autorolled/build.gradle
@@ -119,7 +119,7 @@ versionCache['androidx.fragment:fragment-ktx'] = '1.9.0-SNAPSHOT' versionCache['androidx.fragment:fragment-testing'] = '1.9.0-SNAPSHOT' versionCache['androidx.fragment:fragment-testing-manifest'] = '1.9.0-SNAPSHOT' -versionCache['androidx.graphics:graphics-path'] = '1.0.1' +versionCache['androidx.graphics:graphics-path'] = '1.1.0-SNAPSHOT' versionCache['androidx.graphics:graphics-shapes'] = '1.1.0-alpha01' versionCache['androidx.graphics:graphics-shapes-android'] = '1.1.0-alpha01' versionCache['androidx.gridlayout:gridlayout'] = '1.1.0' @@ -318,12 +318,12 @@ versionCache['com.squareup.wire:wire-runtime-jvm'] = '5.2.1' versionCache['com.squareup:javapoet'] = '1.13.0' versionCache['com.squareup:javawriter'] = '2.1.1' -versionCache['io.grpc:grpc-api'] = '1.73.0' -versionCache['io.grpc:grpc-binder'] = '1.73.0' -versionCache['io.grpc:grpc-context'] = '1.73.0' -versionCache['io.grpc:grpc-core'] = '1.73.0' -versionCache['io.grpc:grpc-protobuf-lite'] = '1.73.0' -versionCache['io.grpc:grpc-stub'] = '1.73.0' +versionCache['io.grpc:grpc-api'] = '1.74.0' +versionCache['io.grpc:grpc-binder'] = '1.74.0' +versionCache['io.grpc:grpc-context'] = '1.74.0' +versionCache['io.grpc:grpc-core'] = '1.74.0' +versionCache['io.grpc:grpc-protobuf-lite'] = '1.74.0' +versionCache['io.grpc:grpc-stub'] = '1.74.0' versionCache['io.perfmark:perfmark-api'] = '0.27.0' versionCache['io.reactivex.rxjava2:rxandroid'] = '2.1.1' versionCache['io.reactivex.rxjava2:rxjava'] = '2.2.6'
diff --git a/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_api/README.chromium b/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_api/README.chromium index 27864b1..6d09b19 100644 --- a/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_api/README.chromium +++ b/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_api/README.chromium
@@ -1,7 +1,7 @@ Name: io.grpc:grpc-api Short Name: grpc-api URL: https://github.com/grpc/grpc-java -Version: 1.73.0 +Version: 1.74.0 Update Mechanism: Autoroll License: Apache-2.0 License File: LICENSE
diff --git a/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_binder/README.chromium b/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_binder/README.chromium index 5877043..a6c0630b 100644 --- a/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_binder/README.chromium +++ b/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_binder/README.chromium
@@ -1,7 +1,7 @@ Name: io.grpc:grpc-binder Short Name: grpc-binder URL: https://github.com/grpc/grpc-java -Version: 1.73.0 +Version: 1.74.0 Update Mechanism: Autoroll License: Apache-2.0 License File: LICENSE
diff --git a/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_context/README.chromium b/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_context/README.chromium index d80b7df..709516a 100644 --- a/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_context/README.chromium +++ b/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_context/README.chromium
@@ -1,7 +1,7 @@ Name: io.grpc:grpc-context Short Name: grpc-context URL: https://github.com/grpc/grpc-java -Version: 1.73.0 +Version: 1.74.0 Update Mechanism: Autoroll License: Apache-2.0 License File: LICENSE
diff --git a/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_core/README.chromium b/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_core/README.chromium index 8dbdbaa5..f0fd9b0 100644 --- a/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_core/README.chromium +++ b/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_core/README.chromium
@@ -1,7 +1,7 @@ Name: io.grpc:grpc-core Short Name: grpc-core URL: https://github.com/grpc/grpc-java -Version: 1.73.0 +Version: 1.74.0 Update Mechanism: Autoroll License: Apache-2.0 License File: LICENSE
diff --git a/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_protobuf_lite/README.chromium b/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_protobuf_lite/README.chromium index fb3bf3b..9160586 100644 --- a/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_protobuf_lite/README.chromium +++ b/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_protobuf_lite/README.chromium
@@ -1,7 +1,7 @@ Name: io.grpc:grpc-protobuf-lite Short Name: grpc-protobuf-lite URL: https://github.com/grpc/grpc-java -Version: 1.73.0 +Version: 1.74.0 Update Mechanism: Autoroll License: Apache-2.0 License File: LICENSE
diff --git a/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_stub/README.chromium b/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_stub/README.chromium index 7c3872f..ec84b24 100644 --- a/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_stub/README.chromium +++ b/third_party/android_deps/autorolled/committed/libs/io_grpc_grpc_stub/README.chromium
@@ -1,7 +1,7 @@ Name: io.grpc:grpc-stub Short Name: grpc-stub URL: https://github.com/grpc/grpc-java -Version: 1.73.0 +Version: 1.74.0 Update Mechanism: Autoroll License: Apache-2.0 License File: LICENSE
diff --git a/third_party/androidx/customizations.gni b/third_party/androidx/customizations.gni index 757a029d..ef2e8d4 100644 --- a/third_party/androidx/customizations.gni +++ b/third_party/androidx/customizations.gni
@@ -165,6 +165,11 @@ # Target has AIDL, which we do not need (and don't support). ignore_aidl = true } + + if (target_name == "androidx_pdf_pdf_viewer_fragment_java") { + jar_excluded_patterns = + [ "androidx/pdf/viewer/fragment/PdfViewerFragment*" ] + } } }
diff --git a/third_party/androidx/local_modifications/pdf/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt b/third_party/androidx/local_modifications/pdf/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt new file mode 100644 index 0000000..6dd266a --- /dev/null +++ b/third_party/androidx/local_modifications/pdf/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt
@@ -0,0 +1,1060 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.pdf.viewer.fragment + +import android.content.ActivityNotFoundException +import android.content.ContentResolver +import android.content.Context +import android.content.res.Configuration +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import android.widget.FrameLayout +import androidx.annotation.RequiresExtension +import androidx.annotation.RestrictTo +import androidx.core.os.BundleCompat +import androidx.core.view.ViewCompat +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import androidx.pdf.R +import androidx.pdf.ViewState +import androidx.pdf.data.DisplayData +import androidx.pdf.data.FutureValue +import androidx.pdf.data.Openable +import androidx.pdf.fetcher.Fetcher +import androidx.pdf.find.FindInFileView +import androidx.pdf.metrics.EventCallback +import androidx.pdf.models.PageSelection +import androidx.pdf.select.SelectionActionMode +import androidx.pdf.util.AnnotationUtils +import androidx.pdf.util.ObservableValue.ValueObserver +import androidx.pdf.util.Observables +import androidx.pdf.util.Observables.ExposedValue +import androidx.pdf.util.Preconditions +import androidx.pdf.util.Uris +import androidx.pdf.viewer.ImmersiveModeRequester +import androidx.pdf.viewer.LayoutHandler +import androidx.pdf.viewer.LoadingView +import androidx.pdf.viewer.PageSelectionValueObserver +import androidx.pdf.viewer.PageViewFactory +import androidx.pdf.viewer.PaginatedView +import androidx.pdf.viewer.PaginationModel +import androidx.pdf.viewer.PdfSelectionHandles +import androidx.pdf.viewer.PdfSelectionModel +import androidx.pdf.viewer.SearchQueryObserver +import androidx.pdf.viewer.SelectedMatch +import androidx.pdf.viewer.SelectedMatchValueObserver +import androidx.pdf.viewer.SingleTapHandler +import androidx.pdf.viewer.ZoomScrollValueObserver +import androidx.pdf.viewer.fragment.insets.TranslateInsetsAnimationCallback +import androidx.pdf.viewer.loader.PdfLoader +import androidx.pdf.viewer.loader.PdfLoaderCallbacksImpl +import androidx.pdf.viewmodel.PdfLoaderViewModel +import androidx.pdf.widget.FastScrollView +import androidx.pdf.widget.ZoomView +import androidx.pdf.widget.ZoomView.ZoomScroll +import com.google.android.material.floatingactionbutton.FloatingActionButton +import java.io.IOException +import kotlinx.coroutines.launch + +/** + * A Fragment that renders a PDF document. + * + * <p>A [PdfViewerFragment] that can display paginated PDFs. The viewer includes a FAB for + * annotation support and a search menu. Each page is rendered in its own View. Upon creation, this + * fragment displays a loading spinner. + * + * <p>Rendering is done in 2 passes: + * <ol> + * <li>Layout: Request the page data, get the dimensions and set them as measure for the image view. + * <li>Render: Create bitmap(s) at adequate dimensions and attach them to the page view. + * </ol> + * + * <p>The layout pass is progressive: starts with a few first pages of the document, then reach + * further as the user scrolls down (and ultimately spans the whole document). The rendering pass is + * tightly limited to the currently visible pages. Pages that are scrolled past (become not visible) + * have their bitmaps released to free up memory. + * + * <p>Note that every activity/fragment that uses this class has to be themed with Theme.AppCompat + * or a theme that extends that theme. + * + * @see documentUri + */ +@RequiresExtension(extension = Build.VERSION_CODES.S, version = 13) +public open class PdfViewerFragment : Fragment() { + + // ViewModel to manage PdfLoader state + private val viewModel: PdfLoaderViewModel by viewModels() + + /** Single access to the PDF document: loads contents asynchronously (bitmaps, text,...) */ + private var pdfLoader: PdfLoader? = null + + /** True when this Fragment's life-cycle is between [.onStart] and [.onStop]. */ + private var started = false + + /** + * True when this Viewer is on-screen (but independent on whether it is actually started, so it + * could be invisible, because obscured by another app). This value is controlled by [postEnter] + * and [.exit]. + */ + private var onScreen = false + + /** Marks that [onEnter] must be run after [onCreateView]. */ + private var delayedEnter = false + private var hasContents = false + + private var container: ViewGroup? = null + private var viewState: ExposedValue<ViewState> = + Observables.newExposedValueWithInitialValue(ViewState.NO_VIEW) + private var zoomView: ZoomView? = null + private var paginatedView: PaginatedView? = null + private var fastScrollView: FastScrollView? = null + private var selectionObserver: ValueObserver<PageSelection>? = null + private var selectionActionMode: SelectionActionMode? = null + private var localUri: Uri? = null + + private var fetcher: Fetcher? = null + private var zoomScrollObserver: ValueObserver<ZoomScroll>? = null + private var searchQueryObserver: ValueObserver<String>? = null + private var selectedMatchObserver: ValueObserver<SelectedMatch>? = null + private var pdfViewer: FrameLayout? = null + private var findInFileView: FindInFileView? = null + private var singleTapHandler: SingleTapHandler? = null + + /** Callbacks of PDF loading asynchronous tasks. */ + private var pdfLoaderCallbacks: PdfLoaderCallbacksImpl? = null + + /** A saved [.onContentsAvailable] runnable to be run after [.onCreateView]. */ + private var delayedContentsAvailable: Runnable? = null + private var loadingView: LoadingView? = null + private var paginationModel: PaginationModel? = null + private var layoutHandler: LayoutHandler? = null + private var pageViewFactory: PageViewFactory? = null + private var selectionHandles: PdfSelectionHandles? = null + private var annotationButton: FloatingActionButton? = null + private var fileData: DisplayData? = null + private var isFileRestoring: Boolean = false + private var isAnnotationIntentResolvable = false + private var documentLoaded = false + + /** + * Specify whether [documentUri] is updated before fragment went in STARTED state. + * + * If true, we'll trigger a loadFile() operation as soon as fragment reaches STARTED state. + */ + private var pendingDocumentLoad: Boolean = false + + private var mEventCallback: EventCallback? = null + + private val mImmersiveModeRequester: ImmersiveModeRequester = + object : ImmersiveModeRequester { + override fun requestImmersiveModeChange(enterImmersive: Boolean) { + onRequestImmersiveMode(enterImmersive) + } + } + + /** + * The URI of the PDF document to display defaulting to `null`. + * + * When this property is set, the fragment begins loading the PDF document. A visual indicator + * is displayed while the document is being loaded. Once the loading is fully completed, the + * [onLoadDocumentSuccess] callback is invoked. If an error occurs during the loading phase, the + * [onLoadDocumentError] callback is invoked with the exception. + * + * <p>Note: This property is recommended to be set when the fragment is in the started state. + */ + public var documentUri: Uri? = null + set(value) { + field = value + + // Check if the uri is different from the previous one or restoring the same one + isFileRestoring = + arguments?.let { + val savedUri = BundleCompat.getParcelable(it, KEY_DOCUMENT_URI, Uri::class.java) + savedUri?.equals(value) ?: false + } ?: false + + // Load file if it's a new URI or it's not loaded + if (value != null && (!isFileRestoring || !documentLoaded)) { + loadFile(value) + } + } + + /** + * Controls whether text search mode is active. Defaults to false. + * + * When text search mode is activated, the search menu becomes visible, and search functionality + * is enabled. Deactivating text search mode hides the search menu, clears search results, and + * removes any search-related highlights. + * + * <p>Note: This property should only be set once fragment is in the started state. Updating it + * before will trigger an [IllegalStateException] which will be delivered through + * [onLoadDocumentError] to host. + */ + public var isTextSearchActive: Boolean = false + set(value) { + if (!isFileRestoring && !lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { + onLoadDocumentError( + IllegalStateException( + "Property can only be toggled after fragment's STARTED state" + ) + ) + return + } + field = value + + // Clear selection + pdfLoaderCallbacks?.selectionModel?.setSelection(null) + + arguments?.putBoolean(KEY_TEXT_SEARCH_ACTIVE, value) + findInFileView?.setFindInFileView(value) + } + + @RestrictTo(RestrictTo.Scope.LIBRARY) + public fun setEventCallback(eventCallback: EventCallback) { + this.mEventCallback = eventCallback + } + + /** + * Indicates whether the toolbox should be visible. + * + * The host app can control this property to show/hide the toolbox based on its state and the + * `onRequestImmersiveMode` callback. The setter updates the UI elements within the fragment + * accordingly. + */ + public var isToolboxVisible: Boolean + get() = arguments?.getBoolean(KEY_TOOLBOX_VISIBILITY) ?: true + set(value) { + (arguments ?: Bundle()).apply { putBoolean(KEY_TOOLBOX_VISIBILITY, value) } + if (value) annotationButton?.show() else annotationButton?.hide() + } + + /** + * Called when the PDF view wants to enter or exit immersive mode based on user's interaction + * with the content. Apps would typically hide their top bar or other navigational interface + * when in immersive mode. The default implementation keeps toolbox visibility in sync with the + * enterImmersive mode. It is recommended that apps keep this behaviour by calling + * super.onRequestImmersiveMode while overriding this method. + * + * @param enterImmersive true to enter immersive mode, false to exit. + */ + public open fun onRequestImmersiveMode(enterImmersive: Boolean) { + // Update toolbox visibility + isToolboxVisible = !enterImmersive + } + + /** + * Invoked when the document has been fully loaded, processed, and the initial pages are + * displayed within the viewing area. This callback signifies that the document is ready for + * user interaction. + * + * <p>Note that this callback is dispatched only when the fragment is fully created and not yet + * destroyed, i.e., after [onCreate] has fully run and before [onDestroy] runs, and only on the + * main thread. + */ + public open fun onLoadDocumentSuccess() {} + + /** + * Invoked when a problem arises during the loading process of the PDF document. This callback + * provides details about the encountered error, allowing for appropriate error handling and + * user notification. + * + * <p>Note that this callback is dispatched only when the fragment is fully created and not yet + * destroyed, i.e., after [onCreate] has fully run and before [onDestroy] runs, and only on the + * main thread. + * + * @param error [Throwable] that occurred during document loading. + */ + @Suppress("UNUSED_PARAMETER") public open fun onLoadDocumentError(error: Throwable) {} + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + fetcher = Fetcher.build(requireContext(), 1) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + super.onCreateView(inflater, container, savedInstanceState) + this.container = container + + pdfViewer = inflater.inflate(R.layout.pdf_viewer_container, container, false) as FrameLayout + pdfViewer?.isScrollContainer = true + fastScrollView = pdfViewer?.findViewById(R.id.fast_scroll_view) + loadingView = pdfViewer?.findViewById(R.id.loadingView) + paginatedView = fastScrollView?.findViewById(R.id.pdf_view) + zoomView = pdfViewer?.findViewById(R.id.zoom_view) + findInFileView = pdfViewer?.findViewById(R.id.search) + findInFileView!!.setPaginatedView(paginatedView!!) + findInFileView!!.setOnClosedButtonCallback { isTextSearchActive = false } + annotationButton = pdfViewer?.findViewById(R.id.edit_fab) + + zoomView?.setMetricEventCallback(mEventCallback) + findInFileView?.setOnVisibilityChangedListener { isVisible -> + fastScrollView?.setScrubberVisibility(!isVisible) + } + + // All views are inflated, update the view state. + if (viewState.get() == ViewState.NO_VIEW || viewState.get() == ViewState.ERROR) { + viewState.set(ViewState.VIEW_CREATED) + // View Inflated, show loading view + loadingView?.showLoadingView() + } + + // Restore documentLoaded state to determine if the document was successfully loaded + // before a potential configuration change or fragment replacement. + documentLoaded = savedInstanceState?.getBoolean(KEY_DOCUMENT_LOADED) ?: false + + arguments?.let { args -> + documentUri = BundleCompat.getParcelable(args, KEY_DOCUMENT_URI, Uri::class.java) + isTextSearchActive = args.getBoolean(KEY_TEXT_SEARCH_ACTIVE) + isToolboxVisible = args.getBoolean(KEY_TOOLBOX_VISIBILITY) + } + + pdfLoaderCallbacks = + PdfLoaderCallbacksImpl( + context = requireContext(), + fragmentManager = requireActivity().supportFragmentManager, + fastScrollView = fastScrollView!!, + zoomView = zoomView!!, + paginatedView = paginatedView!!, + findInFileView = findInFileView!!, + isTextSearchActive = isTextSearchActive, + viewState = viewState, + onRequestPassword = { onScreen -> + if (!(isResumed && onScreen)) { + // This would happen if the service decides to start while we're in + // the background. The dialog code below would then crash. We can't just + // bypass it because then we'd have a started service with no loaded PDF + // and no means to load it. The best way is to just kill the service which + // will restart on the next onStart. + pdfLoader?.disconnect() + return@PdfLoaderCallbacksImpl true + } + return@PdfLoaderCallbacksImpl false + }, + onDocumentLoaded = { + documentLoaded = true + onLoadDocumentSuccess() + annotationButton?.let { + if ((savedInstanceState == null) && isAnnotationIntentResolvable) { + onRequestImmersiveMode(false) + } + } + + hideSpinner() + showPdfView() + }, + onDocumentLoadFailure = { exception, showErrorView -> + // Update state to reflect document load failure. + documentLoaded = false + handleError(exception, showErrorView) + }, + eventCallback = mEventCallback + ) + + setUpEditFab() + if (savedInstanceState != null) { + paginatedView?.isConfigurationChanged = true + } + + if (!hasContents && delayedContentsAvailable == null) { + if (savedInstanceState != null) { + restoreContents(savedInstanceState) + } + } + + return pdfViewer + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + // Using lifecycleScope to collect the flow + viewLifecycleOwner.lifecycleScope.launch { + viewModel.pdfLoaderStateFlow.collect { loader -> + loader?.let { + pdfLoader = loader + setContents(savedInstanceState) + } + } + } + // Add listener to adjust bottom margin for [FindInFile] view + findInFileView?.let { + val windowManager = activity?.getSystemService(Context.WINDOW_SERVICE) as WindowManager + + ViewCompat.setWindowInsetsAnimationCallback( + it, + TranslateInsetsAnimationCallback(it, windowManager, container) + ) + } + + loadPendingDocumentIfRequired() + } + + private fun loadPendingDocumentIfRequired() { + lifecycle.addObserver( + object : DefaultLifecycleObserver { + override fun onStart(owner: LifecycleOwner) { + super.onStart(owner) + // Check if we're pending on loading a document + if (pendingDocumentLoad) { + // Trigger load file + documentUri?.let { loadFile(it) } + } + } + } + ) + } + + override fun onStart() { + delayedContentsAvailable?.run() + super.onStart() + started = true + + // Check if the document file exists, return early if not + documentUri?.let { + val fileExist = checkAndFetchFile(it, false) + if (!fileExist) { + return + } + } + if (delayedEnter || onScreen) { + onEnter() + delayedEnter = false + } + } + + override fun onStop() { + if (onScreen) { + onExit() + } + started = false + super.onStop() + } + + /** Called after this viewer enters the screen and becomes visible. */ + private fun onEnter() { + participateInAccessibility(true) + + // This is necessary for password protected PDF documents. If the user failed to produce the + // correct password, we want to prompt for the correct password every time the film strip + // comes back to this viewer. + if (!documentLoaded) { + pdfLoader?.reconnect() + } + + // Start Recording First Page Load Latency. + mEventCallback?.onViewerVisible() + + if (paginatedView != null && paginatedView?.childCount!! > 0) { + zoomView?.let { layoutHandler?.let { it1 -> it.loadPageAssets(it1, viewState) } } + } + } + + /** Called after this viewer exits the screen and becomes invisible to the user. */ + private fun onExit() { + participateInAccessibility(false) + if (!documentLoaded) { + // e.g. a password-protected pdf that wasn't loaded. + pdfLoader?.disconnect() + } + } + + /** + * Notifies this Viewer goes on-screen. Guarantees that [.onEnter] will be called now or when + * the Viewer is started. + */ + private fun postEnter() { + pdfLoaderCallbacks?.onScreen = true + onScreen = true + if (started) { + onEnter() + } else { + delayedEnter = true + } + } + + private fun isStarted(): Boolean { + return started + } + + /** + * Posts a [.onContentsAvailable] method to be run as soon as permitted (when this Viewer has + * its view hierarchy built up and [onCreateView] has finished). It might run right now if the + * Viewer is currently started. + */ + private fun postContentsAvailable(contents: DisplayData) { + Preconditions.checkState(delayedContentsAvailable == null, "Already waits for contents") + + if (isStarted()) { + onContentsAvailable(contents) + hasContents = true + } else { + delayedContentsAvailable = Runnable { + Preconditions.checkState( + !hasContents, + "Received contents while restoring another copy" + ) + onContentsAvailable(contents) + delayedContentsAvailable = null + hasContents = true + } + } + } + + private fun onContentsAvailable(contents: DisplayData) { + fileData = contents + + // Update the PdfLoader in the ViewModel with the new DisplayData + viewModel.updatePdfLoader( + requireActivity().applicationContext, + contents, + pdfLoaderCallbacks!! + ) { + zoomView?.setDocumentLoaded(/* documentLoaded= */ false) + } + setAnnotationIntentResolvability() + } + + private fun setAnnotationIntentResolvability() { + isAnnotationIntentResolvable = + AnnotationUtils.resolveAnnotationIntent(requireContext(), documentUri!!) + singleTapHandler?.setAnnotationIntentResolvable(isAnnotationIntentResolvable) + findInFileView!!.setAnnotationIntentResolvable(isAnnotationIntentResolvable) + (zoomScrollObserver as? ZoomScrollValueObserver)?.setAnnotationIntentResolvable( + isAnnotationIntentResolvable + ) + } + + /** + * Sets PDF viewer content. Initializes/configures components based on provided data and saved + * state. + * + * @param savedState Saved state (e.g., layout) or null. + */ + private fun setContents(savedState: Bundle?) { + savedState?.let { state -> + if (isFileRestoring) { + val showAnnotationButton = state.getBoolean(KEY_SHOW_ANNOTATION) + isAnnotationIntentResolvable = + showAnnotationButton && findInFileView!!.visibility != View.VISIBLE + if ( + isAnnotationIntentResolvable && + state.getBoolean(KEY_ANNOTATION_BUTTON_VISIBILITY) + ) { + onRequestImmersiveMode(false) + } + } + } + + refreshContentAndModels(pdfLoader!!) + + savedState?.let { state -> + if (isFileRestoring) { + state.containsKey(KEY_LAYOUT_REACH).let { + val layoutReach = state.getInt(KEY_LAYOUT_REACH, -1) + if (layoutReach != -1) { + layoutHandler?.pageLayoutReach = layoutReach + layoutHandler?.setInitialPageLayoutReachWithMax(layoutReach) + } + } + + // Restore page selection from saved state if it exists + val savedSelection = + BundleCompat.getParcelable(state, KEY_PAGE_SELECTION, PageSelection::class.java) + savedSelection?.let { pdfLoaderCallbacks?.selectionModel?.setSelection(it) } + } + } + } + + private fun updateSelectionModel(updatedSelectionModel: PdfSelectionModel) { + pdfLoaderCallbacks?.selectionModel = updatedSelectionModel + zoomView?.setPdfSelectionModel(updatedSelectionModel) + paginatedView?.selectionModel = updatedSelectionModel + + selectionActionMode = + SelectionActionMode(requireActivity(), paginatedView!!, updatedSelectionModel) + selectionHandles = + PdfSelectionHandles( + updatedSelectionModel, + zoomView!!, + paginatedView!!, + selectionActionMode!! + ) + paginatedView?.selectionHandles = selectionHandles!! + } + + private fun updatePageViewFactory(updatedPageViewFactory: PageViewFactory) { + pageViewFactory = updatedPageViewFactory + pdfLoaderCallbacks?.pageViewFactory = updatedPageViewFactory + paginatedView?.pageViewFactory = updatedPageViewFactory + paginatedView?.setMetricEventCallback(mEventCallback) + + selectionObserver = + PageSelectionValueObserver(paginatedView!!, pageViewFactory!!, requireContext()) + pdfLoaderCallbacks?.selectionModel?.selection()?.addObserver(selectionObserver) + } + + private fun updateSearchModel() { + findInFileView?.searchModel?.let { model -> + pdfLoaderCallbacks?.searchModel = model + paginatedView?.searchModel = model + searchQueryObserver = SearchQueryObserver(paginatedView!!) + model.query().addObserver(searchQueryObserver) + } + } + + private fun updateSingleTapHandler(pdfLoader: PdfLoader, selectionModel: PdfSelectionModel) { + singleTapHandler = + SingleTapHandler( + requireContext(), + annotationButton!!, + paginatedView!!, + findInFileView!!, + zoomView!!, + selectionModel, + paginationModel!!, + layoutHandler!!, + mImmersiveModeRequester + ) + singleTapHandler!!.setAnnotationIntentResolvable(isAnnotationIntentResolvable) + + pageViewFactory = + PageViewFactory( + requireContext(), + pdfLoader, + paginatedView!!, + zoomView!!, + singleTapHandler!!, + findInFileView!!, + mEventCallback + ) + updatePageViewFactory(pageViewFactory!!) + } + + private fun refreshContentAndModels(pdfLoader: PdfLoader) { + paginationModel = paginatedView!!.model + + paginatedView?.setPdfLoader(pdfLoader) + findInFileView?.setPdfLoader(pdfLoader) + pdfLoaderCallbacks?.pdfLoader = pdfLoader + + layoutHandler = LayoutHandler(pdfLoader) + paginatedView?.model?.size?.let { layoutHandler!!.pageLayoutReach = it } + + val updatedSelectionModel = PdfSelectionModel(pdfLoader) + updateSelectionModel(updatedSelectionModel) + updateSingleTapHandler(pdfLoader, updatedSelectionModel) + updateSearchModel() + + pdfLoaderCallbacks!!.layoutHandler = layoutHandler + zoomScrollObserver = + ZoomScrollValueObserver( + zoomView!!, + paginatedView!!, + layoutHandler!!, + annotationButton!!, + findInFileView!!, + isAnnotationIntentResolvable, + selectionActionMode!!, + viewState, + mImmersiveModeRequester + ) + zoomView?.zoomScroll()?.addObserver(zoomScrollObserver) + + selectedMatchObserver = + SelectedMatchValueObserver( + paginatedView!!, + pageViewFactory!!, + zoomView!!, + layoutHandler!!, + requireContext() + ) + findInFileView!!.searchModel.selectedMatch().addObserver(selectedMatchObserver) + + annotationButton?.let { findInFileView!!.setAnnotationButton(it, mImmersiveModeRequester) } + + fastScrollView?.setOnFastScrollActiveListener { + annotationButton?.let { button -> + if (button.visibility == View.VISIBLE) { + onRequestImmersiveMode(true) + } + } + } + } + + /** Restores the contents of this Viewer when it is automatically restored by android. */ + private fun restoreContents(savedState: Bundle?) { + pendingDocumentLoad = savedState?.getBoolean(KEY_PENDING_DOCUMENT_LOAD) ?: false + val dataBundle = savedState?.getBundle(KEY_DATA) + if (dataBundle != null) { + try { + fileData = DisplayData.fromBundle(dataBundle) + fileData?.let { + localUri = it.uri + postContentsAvailable(it) + postEnter() + } + } catch (e: Exception) { + // This can happen if the data is an instance of StreamOpenable, and the client + // app that owns it has been killed by the system. We will still recover, + // but log this. + viewState.set(ViewState.ERROR) + handleError(e) + } + } + } + + override fun onResume() { + super.onResume() + if (!documentLoaded || paginatedView?.isConfigurationChanged == true) { + return + } + setAnnotationIntentResolvability() + if (!isAnnotationIntentResolvable && annotationButton?.visibility == View.VISIBLE) { + annotationButton?.post { onRequestImmersiveMode(true) } + } + if ( + isAnnotationIntentResolvable && + annotationButton?.visibility != View.VISIBLE && + findInFileView?.visibility != View.VISIBLE + ) { + annotationButton?.post { onRequestImmersiveMode(false) } + } + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + paginatedView?.isConfigurationChanged = true + } + + private fun destroyContentModel() { + pdfLoader?.cancelAll() + paginationModel = null + + selectionHandles?.destroy() + selectionHandles = null + + pdfLoaderCallbacks?.selectionModel = null + selectionActionMode?.destroy() + + findInFileView?.searchModel?.let { + it.selectedMatch().removeObserver(selectedMatchObserver!!) + it.query().removeObserver(searchQueryObserver!!) + } + fastScrollView?.setOnFastScrollActiveListener(null) + + pdfLoaderCallbacks?.searchModel = null + + pdfLoader = null + documentLoaded = false + } + + private fun destroyView() { + detachViewsAndObservers() + zoomView = null + paginatedView = null + + pdfLoader?.cancelAll() + documentLoaded = false + if (viewState.get() !== ViewState.NO_VIEW) { + viewState.set(ViewState.NO_VIEW) + } + if (container != null && view != null && container === requireView().parent) { + // Some viewers add extra views to their container, e.g. toasts. Remove them all. + // Do not remove what's under it though. + val count = container?.childCount + var child: View + if (count != null) { + for (i in count - 1 downTo 1) { + child = container!!.getChildAt(i) + container?.removeView(child) + if (child === view) { + break + } + } + } + } + } + + private fun detachViewsAndObservers() { + zoomScrollObserver?.let { zoomView?.zoomScroll()?.removeObserver(it) } + paginatedView?.let { view -> view.removeAllViews() } + } + + override fun onDestroyView() { + destroyView() + container = null + pdfLoaderCallbacks = null + super.onDestroyView() + (zoomScrollObserver as? ZoomScrollValueObserver)?.clearAnnotationHandler() + } + + override fun onDestroy() { + super.onDestroy() + if (pdfLoader != null) { + destroyContentModel() + } + mEventCallback?.onViewerReset() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.apply { + putBundle(KEY_DATA, fileData?.asBundle()) + layoutHandler?.let { putInt(KEY_LAYOUT_REACH, it.pageLayoutReach) } + putBoolean(KEY_SHOW_ANNOTATION, isAnnotationIntentResolvable) + pdfLoaderCallbacks?.selectionModel?.let { + putParcelable(KEY_PAGE_SELECTION, it.selection().get()) + } + putBoolean( + KEY_ANNOTATION_BUTTON_VISIBILITY, + (annotationButton?.visibility == View.VISIBLE) + ) + putBoolean(KEY_PENDING_DOCUMENT_LOAD, pendingDocumentLoad) + putBoolean(KEY_DOCUMENT_LOADED, documentLoaded) + } + } + + private fun handleError(error: Throwable, showErrorView: Boolean = true) { + // Report exception occurred while processing PDF to host + onLoadDocumentError(error) + // Show a generic error message on pdf container, if required + if (showErrorView) + context?.resources?.getString(R.string.error_cannot_open_pdf)?.let { + loadingView?.showErrorView(it) + } + } + + private fun resetViewsAndModels(fileUri: Uri) { + if (pdfLoader != null) { + pdfLoaderCallbacks?.uri = fileUri + destroyContentModel() + } + paginatedView?.resetModels() + fastScrollView?.resetContents() + findInFileView?.resetFindInFile() + } + + private fun loadFile(fileUri: Uri) { + // Early return if fragment is not in STARTED state + if (!lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { + // Update state to mark an early return + pendingDocumentLoad = true + return + } + // Update state as loadFile is triggered after in-or-after STARTED state + pendingDocumentLoad = false + + arguments = + Bundle().apply { + putParcelable(KEY_DOCUMENT_URI, fileUri) + putBoolean(KEY_TEXT_SEARCH_ACTIVE, false) + } + + // Reset UI components and models before loading the file + resetViewsAndModels(fileUri) + detachViewsAndObservers() + + // Validate the file URI and attempt to load the file contents + checkAndFetchFile(fileUri, true) + + if (localUri != null && localUri != fileUri) { + onRequestImmersiveMode(true) + } + localUri = fileUri + } + + private fun checkAndFetchFile(fileUri: Uri, performLoad: Boolean): Boolean { + try { + validateFileUri(fileUri) + fetchFile(fileUri, performLoad) + return true + } catch (error: Exception) { + when (error) { + is IOException, + is SecurityException, + is NullPointerException -> handleFileNotAvailable(fileUri, error) + else -> { + throw error + } + } + return false + } + } + + private fun handleFileNotAvailable(fileUri: Uri, error: Throwable) { + // Reset views and models when file error occurs + resetViewsAndModels(fileUri) + + // Hide fast scroll and show loading view to display error message + fastScrollView?.visibility = View.GONE + loadingView?.visibility = View.VISIBLE + + handleError(error) + } + + private fun validateFileUri(fileUri: Uri) { + if (!Uris.isContentUri(fileUri) && !Uris.isFileUri(fileUri)) { + throw IllegalArgumentException("Only content and file uri is supported") + } + } + + private fun fetchFile(fileUri: Uri, performLoad: Boolean) { + Preconditions.checkNotNull(fileUri) + val fileName: String = getFileName(fileUri) + val openable: FutureValue<Openable> = fetcher?.loadLocal(fileUri)!! + + openable[ + object : FutureValue.Callback<Openable> { + override fun available(value: Openable) { + // If loading is required, notify the viewer with the available content. + if (performLoad) { + viewerAvailable(fileUri, fileName, value) + } + } + + override fun failed(thrown: Throwable) { + handleError(thrown) + } + + override fun progress(progress: Float) {} + }] + } + + private fun getFileName(fileUri: Uri): String { + val resolver: ContentResolver? = getResolver() + return if (resolver != null) Uris.extractName(fileUri, resolver) + else Uris.extractFileName(fileUri) + } + + private fun getResolver(): ContentResolver? { + if (activity != null) { + return requireActivity().contentResolver + } + return null + } + + private fun viewerAvailable(fileUri: Uri, fileName: String, openable: Openable) { + val contents = DisplayData(fileUri, fileName, openable) + + startViewer(contents) + } + + private fun startViewer(contents: DisplayData) { + Preconditions.checkNotNull(contents) + try { + feed(contents) + postEnter() + } catch (exception: Exception) { + onLoadDocumentError(exception) + } + } + + /** Feed this Viewer with contents to be displayed. */ + private fun feed(contents: DisplayData?): PdfViewerFragment { + if (contents != null) { + postContentsAvailable(contents) + } + return this + } + + /** Makes the views of this Viewer visible to TalkBack (in the swipe gesture circus) or not. */ + private fun participateInAccessibility(participate: Boolean) { + view?.importantForAccessibility = + if (participate) View.IMPORTANT_FOR_ACCESSIBILITY_YES + else View.IMPORTANT_FOR_ACCESSIBILITY_NO + } + + private fun setUpEditFab() { + annotationButton?.setOnClickListener(View.OnClickListener { performEdit() }) + } + + private fun performEdit() { + setAnnotationIntentResolvability() + // TODO: Fix the behavior of immersiveMode to be independent of isAnnotationIntentResolvable + if (!isAnnotationIntentResolvable) { + annotationButton?.hide() + return + } + try { + val intent = AnnotationUtils.getAnnotationIntent(localUri!!) + intent.setData(localUri) + intent.putExtra(EXTRA_PDF_FILE_NAME, getFileName(localUri!!)) + intent.putExtra(EXTRA_STARTING_PAGE, getStartingPageNumber()) + startActivity(intent) + } catch (error: Exception) { + when (error) { + is ActivityNotFoundException, + is NullPointerException -> hideAnnotationButtonAndShowToast() + else -> throw error + } + } + } + + private fun hideAnnotationButtonAndShowToast() { + annotationButton?.hide() + // TODO: Uncomment the Toast after translation for string is completed. + /* Toast.makeText( + context, + context?.resources?.getString(R.string.cannot_edit_pdf), + Toast.LENGTH_SHORT + ) + .show() */ + } + + private fun getStartingPageNumber(): Int { + // Return the page that is centered in the view. + return paginationModel?.midPage ?: 0 + } + + private fun hideSpinner() { + loadingView?.visibility = View.GONE + } + + private fun showPdfView() { + fastScrollView?.visibility = View.VISIBLE + } + + private companion object { + /** Key for saving page layout reach in bundles. */ + private const val KEY_LAYOUT_REACH: String = "plr" + private const val KEY_DATA: String = "data" + private const val KEY_TEXT_SEARCH_ACTIVE: String = "isTextSearchActive" + private const val KEY_SHOW_ANNOTATION: String = "showEditFab" + private const val KEY_PAGE_SELECTION: String = "currentPageSelection" + private const val KEY_DOCUMENT_URI: String = "documentUri" + private const val KEY_ANNOTATION_BUTTON_VISIBILITY = "isAnnotationVisible" + private const val KEY_PENDING_DOCUMENT_LOAD = "pendingDocumentLoad" + private const val KEY_TOOLBOX_VISIBILITY = "isToolboxVisible" + private const val KEY_DOCUMENT_LOADED = "isDocumentLoaded" + private const val EXTRA_PDF_FILE_NAME = "androidx.pdf.viewer.fragment.extra.PDF_FILE_NAME" + private const val EXTRA_STARTING_PAGE: String = + "androidx.pdf.viewer.fragment.extra.STARTING_PAGE" + } +}
diff --git a/third_party/androidx/local_modifications/pdf/java/androidx/pdf/viewer/fragment/insets/TranslateInsetsAnimationCallback.kt b/third_party/androidx/local_modifications/pdf/java/androidx/pdf/viewer/fragment/insets/TranslateInsetsAnimationCallback.kt new file mode 100644 index 0000000..2cd4f33 --- /dev/null +++ b/third_party/androidx/local_modifications/pdf/java/androidx/pdf/viewer/fragment/insets/TranslateInsetsAnimationCallback.kt
@@ -0,0 +1,84 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.pdf.viewer.fragment.insets + +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import androidx.core.view.WindowInsetsAnimationCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updateLayoutParams + +/** + * A callback that will update bottom margin for provided view as per keyboard visibility. + * + * @param view: A view whose bottom margin needs to be updated with keyboard. + * @param windowManager: An interface to interact with window params, here helps to fetch screen + * height. + * @param pdfContainer: Container view where PDF is hosted. + * @param dispatchMode: Specifies whether children view should get callback, by default callback + * will be propagated. + */ +internal class TranslateInsetsAnimationCallback( + private val view: View, + private val windowManager: WindowManager?, + private val pdfContainer: View?, + dispatchMode: Int = DISPATCH_MODE_CONTINUE_ON_SUBTREE +) : WindowInsetsAnimationCompat.Callback(dispatchMode) { + + override fun onProgress( + insets: WindowInsetsCompat, + runningAnimations: List<WindowInsetsAnimationCompat> + ): WindowInsetsCompat { + // onProgress() is called when any of the running animations progress... + + var absoluteContainerBottom = 0 + /* + Calculate absolute pdfContainer bottom on screen + This is necessary as our fragment may not span the complete screen + */ + pdfContainer?.let { + val containerLocation = IntArray(2) + pdfContainer.getLocationInWindow(containerLocation) + absoluteContainerBottom = pdfContainer.height + containerLocation[1] + } + + // Extract keyboard insets + val keyboardInsets = insets.getInsets(WindowInsetsCompat.Type.ime()) + + /* + By default the keyboard top should be aligned with container bottom; + This is same as keyboard is in closed state + */ + var keyboardTop = absoluteContainerBottom + + // Calculate keyboard top wrt screen height + windowManager?.let { + val screenHeight = windowManager.currentWindowMetrics.bounds.height() + keyboardTop = screenHeight - keyboardInsets.bottom + } + + // Net margin wrt pdf container bottom + val margin = + if (absoluteContainerBottom >= keyboardTop) absoluteContainerBottom - keyboardTop else 0 + + // Update bottom margin for view + view.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin = margin } + + return insets + } +}
diff --git a/third_party/androidx/overrides.gni b/third_party/androidx/overrides.gni index 01abd6d..9fbb570 100644 --- a/third_party/androidx/overrides.gni +++ b/third_party/androidx/overrides.gni
@@ -71,6 +71,32 @@ ] } + android_library("pdf_cherry_pick_crbug_394147799_java") { + resources_package = "androidx.pdf" + sources = [ + "local_modifications/pdf/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt", + + # Need to include this due to "internal" visibilty. + "local_modifications/pdf/java/androidx/pdf/viewer/fragment/insets/TranslateInsetsAnimationCallback.kt", + ] + + jar_excluded_patterns = [ "*TranslateInsetsAnimationCallback*" ] + + deps = [ + ":androidx_annotation_annotation_jvm_java", + ":androidx_core_core_java", + ":androidx_fragment_fragment_ktx_java", + ":androidx_pdf_pdf_viewer_fragment_java", + ":androidx_pdf_pdf_viewer_java", + "//third_party/android_deps:material_design_java", + "//third_party/android_deps:org_jetbrains_kotlinx_kotlinx_coroutines_core_jvm_java", + "//third_party/androidx:androidx_fragment_fragment_java", + "//third_party/androidx:androidx_lifecycle_lifecycle_common_jvm_java", + "//third_party/androidx:androidx_lifecycle_lifecycle_viewmodel_android_java", + "//third_party/kotlin_stdlib:kotlin_stdlib_java", + ] + } + android_library("androidx_recyclerview_recyclerview_overrides_java") { resources_package = "androidx.recyclerview" sources = [
diff --git a/third_party/angle b/third_party/angle index 2b12571..214a48c 160000 --- a/third_party/angle +++ b/third_party/angle
@@ -1 +1 @@ -Subproject commit 2b12571c828100adca9b721ed5df96a0c94cd083 +Subproject commit 214a48c41a84799cdf7cf821fcb0e3a0fccd2b11
diff --git a/third_party/blink/common/loader/throttling_url_loader.cc b/third_party/blink/common/loader/throttling_url_loader.cc index 7a0d897..c9e83bc 100644 --- a/third_party/blink/common/loader/throttling_url_loader.cc +++ b/third_party/blink/common/loader/throttling_url_loader.cc
@@ -259,6 +259,17 @@ : priority(in_priority), intra_priority_value(in_intra_priority_value) {} // static +std::unique_ptr<ThrottlingURLLoader> ThrottlingURLLoader::CreateLoader( + std::vector<std::unique_ptr<URLLoaderThrottle>> throttles, + network::mojom::URLLoaderClient* client, + const net::NetworkTrafficAnnotationTag& traffic_annotation, + ClientReceiverDelegate* client_receiver_delegate) { + return std::unique_ptr<ThrottlingURLLoader>( + new ThrottlingURLLoader(std::move(throttles), client, traffic_annotation, + client_receiver_delegate)); +} + +// static std::unique_ptr<ThrottlingURLLoader> ThrottlingURLLoader::CreateLoaderAndStart( scoped_refptr<network::SharedURLLoaderFactory> factory, std::vector<std::unique_ptr<URLLoaderThrottle>> throttles, @@ -272,9 +283,9 @@ ClientReceiverDelegate* client_receiver_delegate, const std::vector<int>* initiator_origin_trial_features) { DCHECK(url_request); - std::unique_ptr<ThrottlingURLLoader> loader( - new ThrottlingURLLoader(std::move(throttles), client, traffic_annotation, - client_receiver_delegate)); + std::unique_ptr<ThrottlingURLLoader> loader = + CreateLoader(std::move(throttles), client, traffic_annotation, + client_receiver_delegate); loader->Start(std::move(factory), request_id, options, url_request, std::move(task_runner), std::move(cors_exempt_header_list), initiator_origin_trial_features);
diff --git a/third_party/blink/public/common/loader/throttling_url_loader.h b/third_party/blink/public/common/loader/throttling_url_loader.h index a26a04e..23af7a89 100644 --- a/third_party/blink/public/common/loader/throttling_url_loader.h +++ b/third_party/blink/public/common/loader/throttling_url_loader.h
@@ -103,6 +103,15 @@ // Note that once |client_receiver_delegate| is set, the relevant throttle // callbacks like BeforeWillProcessResponse(), WillProcessResponse(), and // WillOnCompleteWithError(), will not be triggered by the returned object. + // + // Note that `CreateLoaderAndStart()` (or equivalently `Start()`) can notify + // `client` immediately & synchronously (e.g. via + // `URLLoaderThrottle::Delegate::CancelWithError()` inside + // `WillStartRequest()`) even before returning the + // `std::unique_ptr<ThrottlingURLLoader>`. To avoid this for the short-term, + // call `CreateLoader()` then `Start()`. + // + // TODO(https://crbug.com/433324863): Figure out a longer-term solution. static std::unique_ptr<ThrottlingURLLoader> CreateLoaderAndStart( scoped_refptr<network::SharedURLLoaderFactory> factory, std::vector<std::unique_ptr<URLLoaderThrottle>> throttles, @@ -117,6 +126,20 @@ ClientReceiverDelegate* client_receiver_delegate = nullptr, const std::vector<int>* initiator_origin_trial_features = nullptr); + // See the comments at `CreateLoaderAndStart()` above for parameters. + static std::unique_ptr<ThrottlingURLLoader> CreateLoader( + std::vector<std::unique_ptr<URLLoaderThrottle>> throttles, + network::mojom::URLLoaderClient* client, + const net::NetworkTrafficAnnotationTag& traffic_annotation, + ClientReceiverDelegate* client_receiver_delegate); + void Start(scoped_refptr<network::SharedURLLoaderFactory> factory, + int32_t request_id, + uint32_t options, + network::ResourceRequest* url_request, + scoped_refptr<base::SequencedTaskRunner> task_runner, + std::optional<std::vector<std::string>> cors_exempt_header_list, + const std::vector<int>* initiator_origin_trial_features); + ThrottlingURLLoader(const ThrottlingURLLoader&) = delete; ThrottlingURLLoader& operator=(const ThrottlingURLLoader&) = delete; ~ThrottlingURLLoader() override; @@ -170,14 +193,6 @@ const net::NetworkTrafficAnnotationTag& traffic_annotation, ClientReceiverDelegate* client_receiver_delegate); - void Start(scoped_refptr<network::SharedURLLoaderFactory> factory, - int32_t request_id, - uint32_t options, - network::ResourceRequest* url_request, - scoped_refptr<base::SequencedTaskRunner> task_runner, - std::optional<std::vector<std::string>> cors_exempt_header_list, - const std::vector<int>* initiator_origin_trial_features); - void StartNow(); void RestartWithURLResetNow();
diff --git a/third_party/blink/public/mojom/content_extraction/frame_metadata_observer_registry.mojom b/third_party/blink/public/mojom/content_extraction/frame_metadata_observer_registry.mojom index 997019c0..5fe6ef5 100644 --- a/third_party/blink/public/mojom/content_extraction/frame_metadata_observer_registry.mojom +++ b/third_party/blink/public/mojom/content_extraction/frame_metadata_observer_registry.mojom
@@ -4,19 +4,34 @@ module blink.mojom; +import "third_party/blink/public/mojom/content_extraction/ai_page_content_metadata.mojom"; +import "url/mojom/url.mojom"; + // An observer to be notified whenever metadata derived from the associated // Document changes. interface PaidContentMetadataObserver { // Called when the paid content metadata changes. Clients should consider the // value to be unspecified (eg. document not loaded yet) until this method // is called. + // This value is based solely on this frame's Document. OnPaidContentMetadataChanged(bool has_paid_content); }; +// An observer to be notified when meta tags change. +interface MetaTagsObserver { + // Called when meta tags change. + OnMetaTagsChanged(PageMetadata page_metadata); +}; + // Allows observers in the browser to register to be notified whenever metadata // derived from the associated Document changes. interface FrameMetadataObserverRegistry { // Adds an observer to be notified whenever metadata derived from the // associated Document changes. - AddPaidContentMetadataObserver(pending_remote<PaidContentMetadataObserver> observer); + AddPaidContentMetadataObserver( + pending_remote<PaidContentMetadataObserver> observer); + + // Adds an observer to be notified whenever meta tags change. + AddMetaTagsObserver( + array<string> names, pending_remote<MetaTagsObserver> observer); };
diff --git a/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom index 59559b2..66ff402 100644 --- a/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom +++ b/third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom
@@ -434,6 +434,7 @@ kWasmSignExtensionOperators = 372, kDRAFT_IncomingCallNotifications = 373, kPopover = 374, + kDRAFT_WebInstallAPI = 375, // Add new features immediately above this line. Don't change assigned // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/core/frame/ad_tracker.cc b/third_party/blink/renderer/core/frame/ad_tracker.cc index bce7cf7..8decc025 100644 --- a/third_party/blink/renderer/core/frame/ad_tracker.cc +++ b/third_party/blink/renderer/core/frame/ad_tracker.cc
@@ -104,34 +104,9 @@ local_root_ = nullptr; } -String AdTracker::ScriptAtTopOfStack( - std::optional<AdScriptIdentifier>* out_top_script) { - // CurrentStackTrace is 10x faster than CaptureStackTrace if all that you need - // is the url of the script at the top of the stack. See crbug.com/1057211 for - // more detail. +int AdTracker::ScriptAtTopOfStack() { v8::Isolate* isolate = v8::Isolate::TryGetCurrent(); - if (!isolate) [[unlikely]] { - return String(); - } - - v8::Local<v8::StackTrace> stack_trace = - v8::StackTrace::CurrentStackTrace(isolate, /*frame_limit=*/1); - if (stack_trace.IsEmpty() || stack_trace->GetFrameCount() < 1) - return String(); - - v8::Local<v8::StackFrame> frame = stack_trace->GetFrame(isolate, 0); - v8::Local<v8::String> script_name = frame->GetScriptName(); - - if (out_top_script) { - *out_top_script = AdScriptIdentifier( - GetDebuggerIdForContext(isolate->GetCurrentContext()), - frame->GetScriptId()); - } - - if (script_name.IsEmpty() || !script_name->Length()) - return GenerateFakeUrlFromScriptId(frame->GetScriptId()); - - return ToCoreString(isolate, script_name); + return v8::StackTrace::CurrentScriptId(isolate); } ExecutionContext* AdTracker::GetCurrentExecutionContext() { @@ -408,10 +383,15 @@ if (IsKnownAdExecutionContext(execution_context)) return true; - // We don't care about the `out_ad_script` param here because that only gets - // filled when `url` is empty, but we have a url to pass in this case. - return IsKnownAdScriptForCheckedContext(*execution_context, url, - /*out_ad_script=*/nullptr); + if (url.empty()) { + return false; + } + + auto it = context_known_ad_scripts_.find(execution_context); + if (it == context_known_ad_scripts_.end()) { + return false; + } + return it->value.Contains(url); } bool AdTracker::IsKnownAdScriptForCheckedContext( @@ -428,23 +408,20 @@ return false; } - std::optional<AdScriptIdentifier> top_of_stack_script; - // Delay calling ScriptAtTopOfStack() as much as possible due to its cost. - String script_url = - url.IsNull() - ? ScriptAtTopOfStack(out_ad_script ? &top_of_stack_script : nullptr) - : url; - - if (script_url.empty()) { + int top_script_id = ScriptAtTopOfStack(); + if (top_script_id <= 0) { return false; } - bool found = it->value.Contains(script_url); - if (found && out_ad_script) { - *out_ad_script = std::move(top_of_stack_script); + bool is_ad_script = ad_script_ids_.Contains(top_script_id); + if (is_ad_script && out_ad_script) { + v8::Isolate* isolate = v8::Isolate::TryGetCurrent(); + + *out_ad_script = AdScriptIdentifier( + GetDebuggerIdForContext(isolate->GetCurrentContext()), top_script_id); } - return found; + return is_ad_script; } // This is a separate function for testing purposes. @@ -482,6 +459,8 @@ return; } + ad_script_ids_.insert(script_id); + const HashMap<String, std::unique_ptr<AdProvenance>>& known_ad_scripts_and_provenance = it->value;
diff --git a/third_party/blink/renderer/core/frame/ad_tracker.h b/third_party/blink/renderer/core/frame/ad_tracker.h index 4fd5fdc..a871995 100644 --- a/third_party/blink/renderer/core/frame/ad_tracker.h +++ b/third_party/blink/renderer/core/frame/ad_tracker.h
@@ -191,10 +191,20 @@ protected: // Protected for testing. // Note that this outputs the `out_top_script` even when it's not an ad. - virtual String ScriptAtTopOfStack( - std::optional<AdScriptIdentifier>* out_top_script); + virtual int ScriptAtTopOfStack(); virtual ExecutionContext* GetCurrentExecutionContext(); + // `script_name` will be empty in the case of a dynamically added script with + // no src attribute set. `script_id` won't be set for module scripts in an + // errored state or for non-source text modules. `top_level_execution` should + // be true if the top-level script is being run, as opposed to a function + // being called. + virtual void WillExecuteScript(ExecutionContext*, + const v8::Local<v8::Context>& v8_context, + const String& script_name, + int script_id, + bool top_level_execution); + private: friend class FrameFetchContextSubresourceFilterTest; friend class AdTrackerSimTest; @@ -206,16 +216,6 @@ StackType stack_type, std::optional<AdScriptIdentifier>* out_ad_script); - // `script_name` will be empty in the case of a dynamically added script with - // no src attribute set. `script_id` won't be set for module scripts in an - // errored state or for non-source text modules. `top_level_execution` should - // be true if the top-level script is being run, as opposed to a function - // being called. - void WillExecuteScript(ExecutionContext*, - const v8::Local<v8::Context>& v8_context, - const String& script_name, - int script_id, - bool top_level_execution); void DidExecuteScript(); bool IsKnownAdScript(ExecutionContext*, const String& url); bool IsKnownAdScriptForCheckedContext( @@ -280,6 +280,9 @@ // The number of ad-related async tasks currently running in the stack. int running_ad_async_tasks_ = 0; + + // The known ad-related script ids. + HashSet<int> ad_script_ids_; }; template <>
diff --git a/third_party/blink/renderer/core/frame/ad_tracker_test.cc b/third_party/blink/renderer/core/frame/ad_tracker_test.cc index 56018ae..b3004c5af 100644 --- a/third_party/blink/renderer/core/frame/ad_tracker_test.cc +++ b/third_party/blink/renderer/core/frame/ad_tracker_test.cc
@@ -137,7 +137,13 @@ class TestAdTracker : public AdTracker { public: explicit TestAdTracker(LocalFrame* frame) : AdTracker(frame) {} - void SetScriptAtTopOfStack(const String& url) { script_at_top_ = url; } + void SetScriptAtTopOfStack(const String& url) { + script_at_top_ = url; + // For a given script, we generate a unique "script id" for it. + if (!script_ids_.Contains(url)) { + script_ids_.insert(url, script_id_count_++); + } + } void SetExecutionContext(ExecutionContext* execution_context) { execution_context_ = execution_context; } @@ -187,6 +193,27 @@ return result; } + // Since ScriptAtTopOfStack is id based instead of string based, we need to + // provide a mocked script id to the AdTracker. + void WillExecuteScript(ExecutionContext* execution_context, + const v8::Local<v8::Context>& v8_context, + const String& script_name, + int script_id, + bool top_level_execution) override { + if (script_id == v8::Message::kNoScriptIdInfo) { + auto it = script_ids_.find(script_name); + if (it != script_ids_.end()) { + script_id = it->value; + } else { + script_id = script_id_count_; + script_ids_.insert(script_name, script_id_count_++); + } + } + + AdTracker::WillExecuteScript(execution_context, v8_context, script_name, + script_id, top_level_execution); + } + const AdScriptAncestry& last_ad_script_ancestry() const { return last_ad_script_ancestry_; } @@ -194,16 +221,15 @@ protected: // Override ScriptAtTopofStack to allow us to mock out the returned script // (via `SetScriptAtTopOfStack`). - String ScriptAtTopOfStack( - std::optional<AdScriptIdentifier>* out_ad_script = nullptr) override { + int ScriptAtTopOfStack() override { if (script_at_top_) { - return script_at_top_; + return script_ids_.find(script_at_top_)->value; } if (!sim_test_) { - return ""; + return -1; } - return AdTracker::ScriptAtTopOfStack(out_ad_script); + return AdTracker::ScriptAtTopOfStack(); } ExecutionContext* GetCurrentExecutionContext() override { @@ -235,6 +261,10 @@ private: HashMap<String, bool> is_ad_; + HashMap<String, int> script_ids_; + + // Valid script ids are >= 1. + size_t script_id_count_ = 1; String script_at_top_; Member<ExecutionContext> execution_context_; bool sim_test_ = false; @@ -317,11 +347,16 @@ ad_tracker_->AppendToKnownAdScripts( *GetExecutionContext(), url, std::make_unique<AdTracker::NoAdProvenance>()); + // Calling WilExecuteScript will give the AdTracker a chance to associate + // the script url with a script id. + WillExecuteScript(url); + DidExecuteScript(); } void AppendToKnownAdScripts(int script_id) { // Matches AdTracker's inline script encoding - AppendToKnownAdScripts(String::Format("{ id %d }", script_id)); + String url = String::Format("{ id %d }", script_id); + AppendToKnownAdScripts(url); } test::TaskEnvironment task_environment_; @@ -401,9 +436,6 @@ ad_tracker_->SetScriptAtTopOfStack(""); EXPECT_FALSE(AnyExecutingScriptsTaggedAsAdResource()); - ad_tracker_->SetScriptAtTopOfStack(String()); - EXPECT_FALSE(AnyExecutingScriptsTaggedAsAdResource()); - WillExecuteScript(ad_script_url); EXPECT_TRUE(AnyExecutingScriptsTaggedAsAdResource()); } @@ -1054,11 +1086,6 @@ // Image loaded by ad script is tagged as ad. TEST_F(AdTrackerSimTest, DataURLImageLoadedWhileExecutingAdScriptAsyncEnabled) { - // Reset the AdTracker so that it gets the latest base::Feature value on - // construction. - ad_tracker_ = MakeGarbageCollected<TestAdTracker>(GetDocument().GetFrame()); - GetDocument().GetFrame()->SetAdTrackerForTesting(ad_tracker_); - const char kAdUrl[] = "https://example.com/ad_script.js"; SimSubresourceRequest ad_resource(kAdUrl, "text/javascript");
diff --git a/third_party/blink/renderer/core/html/media/html_video_element.cc b/third_party/blink/renderer/core/html/media/html_video_element.cc index 667a699..320df9a8 100644 --- a/third_party/blink/renderer/core/html/media/html_video_element.cc +++ b/third_party/blink/renderer/core/html/media/html_video_element.cc
@@ -470,7 +470,7 @@ video_timing->SetTimingAllowPassed( GetWebMediaPlayer()->PassedTimingAllowOriginCheck()); - PaintTimingDetector::NotifyImagePaint( + PaintTimingDetector::NotifyFirstVideoFrame( *layout_object, videoVisibleSize(), *video_timing, layout_object->FirstFragment().LocalBorderBoxProperties(), layout_object->AbsoluteBoundingBoxRect());
diff --git a/third_party/blink/renderer/core/layout/grid/grid_track_collection.cc b/third_party/blink/renderer/core/layout/grid/grid_track_collection.cc index 37ae88ca..ae9ac61 100644 --- a/third_party/blink/renderer/core/layout/grid/grid_track_collection.cc +++ b/third_party/blink/renderer/core/layout/grid/grid_track_collection.cc
@@ -110,7 +110,8 @@ end_lines_.emplace_back(start_line + span_length, grid_item_end_range_index); } -GridRangeVector GridRangeBuilder::FinalizeRanges() { +GridRangeVector GridRangeBuilder::FinalizeRanges( + Vector<wtf_size_t>* collapsed_track_indexes) { DCHECK_EQ(start_lines_.size(), end_lines_.size()); // Sort start and ending tracks from low to high. @@ -291,12 +292,16 @@ *end_lines_[line_index].grid_item_range_index_to_cache = ranges.size(); } - // TODO(almaher): Handle special auto-fit behavior for Masonry. - // - // https://drafts.csswg.org/css-grid-3/#repeat-auto-fit if (is_in_auto_fit_range && open_items_or_repeaters == 1) { range.SetIsCollapsed(); range.set_count = 0; + if (collapsed_track_indexes) { + wtf_size_t start_line = range.start_line; + for (wtf_size_t i = start_line; i < start_line + range.track_count; + ++i) { + collapsed_track_indexes->emplace_back(i); + } + } } else { // If this is a non-collapsed range, the number of sets in this range is // the number of track definitions in the current repeater clamped by the
diff --git a/third_party/blink/renderer/core/layout/grid/grid_track_collection.h b/third_party/blink/renderer/core/layout/grid/grid_track_collection.h index 7bb22f6..0811855 100644 --- a/third_party/blink/renderer/core/layout/grid/grid_track_collection.h +++ b/third_party/blink/renderer/core/layout/grid/grid_track_collection.h
@@ -100,8 +100,11 @@ wtf_size_t* grid_item_end_range_index); // Build the collection of ranges based on information provided through the - // specified tracks and |EnsureTrackCoverage|. - GridRangeVector FinalizeRanges(); + // specified tracks and `EnsureTrackCoverage`. If `collapsed_track_indexes` is + // not nullptr, this method with populate it with the track indexes of all + // collapsed tracks. + GridRangeVector FinalizeRanges( + Vector<wtf_size_t>* collapsed_track_indexes = nullptr); private: friend class GridTrackCollectionTest;
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc index 4c0517a..155942b 100644 --- a/third_party/blink/renderer/core/layout/layout_box.cc +++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -506,6 +506,19 @@ return kNoPaintLayer; } +bool LayoutBox::TransformsChangeMayRequireLayout() const { + if (!RuntimeEnabledFeatures::CSSAnchorWithTransformsEnabled()) { + return false; + } + + for (const PhysicalBoxFragment& fragment : PhysicalFragments()) { + if (fragment.HasAnchorQueryToPropagate()) { + return true; + } + } + return false; +} + void LayoutBox::WillBeDestroyed() { NOT_DESTROYED(); ClearOverrideContainingBlockContentSize(); @@ -733,6 +746,11 @@ } } + if (diff.TransformChanged() && TransformsChangeMayRequireLayout()) { + SetNeedsLayoutAndFullPaintInvalidation( + layout_invalidation_reason::kStyleChange); + } + // Update the script style map, from the new computed style. if (IsCustomItem()) GetCustomLayoutChild()->styleMap()->UpdateStyle(GetDocument(), StyleRef());
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h index fabe052..dd9b99f9 100644 --- a/third_party/blink/renderer/core/layout/layout_box.h +++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -213,6 +213,12 @@ return false; } + // Return true if changes to transforms may require layout. + // + // This is the case for anchors that are affected by transforms, as that may + // affect anything that is anchored to it. + bool TransformsChangeMayRequireLayout() const; + // Use this with caution! No type checking is done! LayoutBox* FirstChildBox() const; LayoutBox* LastChildBox() const;
diff --git a/third_party/blink/renderer/core/layout/masonry/masonry_layout_algorithm.cc b/third_party/blink/renderer/core/layout/masonry/masonry_layout_algorithm.cc index 0171c0b..3d369ad 100644 --- a/third_party/blink/renderer/core/layout/masonry/masonry_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/masonry/masonry_layout_algorithm.cc
@@ -41,12 +41,13 @@ std::optional<LayoutUnit> auto_repeat_track_size = std::nullopt; wtf_size_t start_offset; GridItems masonry_items; + Vector<wtf_size_t> collapsed_track_indexes; const bool is_for_columns = Style().MasonryTrackSizingDirection() == kForColumns; GridSizingTrackCollection track_collection = ComputeGridAxisTracks( - sizing_constraint, auto_repeat_track_size, masonry_items, start_offset, - needs_auto_track_size); + sizing_constraint, auto_repeat_track_size, masonry_items, + collapsed_track_indexes, start_offset, needs_auto_track_size); // We have a repeat() track definition with an auto sized track(s). The // previous track sizing pass was used to find the track size to apply @@ -57,6 +58,7 @@ // https://www.w3.org/TR/css-grid-3/#masonry-intrinsic-repeat if (needs_auto_track_size) { CHECK_NE(track_collection.GetAutoSizedRepeaterTrackIndex(), kNotFound); + collapsed_track_indexes.clear(); // Note that when `needs_auto_track_size` is true, we skip the steps to // distribute free space during track sizing. This means that the base // track size at this point represents the size of the intrinsic track @@ -68,7 +70,7 @@ track_collection = ComputeGridAxisTracks( sizing_constraint, auto_repeat_track_size, masonry_items, - start_offset, needs_auto_track_size); + collapsed_track_indexes, start_offset, needs_auto_track_size); } if (is_for_columns) { @@ -85,7 +87,8 @@ MasonryRunningPositions running_positions( track_collection.EndLineOfImplicitGrid(), LayoutUnit(), - ResolveItemToleranceForMasonry(Style(), masonry_available_size_)); + ResolveItemToleranceForMasonry(Style(), masonry_available_size_), + collapsed_track_indexes); PlaceMasonryItems(track_collection, masonry_items, start_offset, running_positions, sizing_constraint); // `stacking_axis_gap` represents the space between each of the items @@ -119,10 +122,12 @@ wtf_size_t start_offset; GridItems masonry_items; HeapVector<Member<LayoutBox>> oof_children; + Vector<wtf_size_t> collapsed_track_indexes; - GridSizingTrackCollection track_collection = ComputeGridAxisTracks( - SizingConstraint::kLayout, auto_repeat_track_size, masonry_items, - start_offset, needs_auto_track_size, &oof_children); + GridSizingTrackCollection track_collection = + ComputeGridAxisTracks(SizingConstraint::kLayout, auto_repeat_track_size, + masonry_items, collapsed_track_indexes, + start_offset, needs_auto_track_size, &oof_children); // We have a repeat() track definition with an auto sized track(s). The // previous track sizing pass was used to find the track size to apply @@ -133,6 +138,7 @@ // https://www.w3.org/TR/css-grid-3/#masonry-intrinsic-repeat if (needs_auto_track_size) { CHECK_NE(track_collection.GetAutoSizedRepeaterTrackIndex(), kNotFound); + collapsed_track_indexes.clear(); // Note that when `needs_auto_track_size` is true, we skip the steps to // distribute free space during track sizing. This means that the base track // size at this point represents the size of the intrinsic track without @@ -144,14 +150,15 @@ track_collection = ComputeGridAxisTracks( SizingConstraint::kLayout, auto_repeat_track_size, masonry_items, - start_offset, needs_auto_track_size); + collapsed_track_indexes, start_offset, needs_auto_track_size); } if (!masonry_items.IsEmpty()) { MasonryRunningPositions running_positions( /*track_count=*/track_collection.EndLineOfImplicitGrid(), /*initial_running_position=*/LayoutUnit(), - ResolveItemToleranceForMasonry(Style(), masonry_available_size_)); + ResolveItemToleranceForMasonry(Style(), masonry_available_size_), + collapsed_track_indexes); PlaceMasonryItems(track_collection, masonry_items, start_offset, running_positions, SizingConstraint::kLayout); } @@ -366,6 +373,7 @@ const GridItems& masonry_items, const bool needs_auto_track_size, SizingConstraint sizing_constraint, + const wtf_size_t auto_repetition_count, wtf_size_t& start_offset) const { const auto& style = Style(); const auto grid_axis_direction = style.MasonryTrackSizingDirection(); @@ -379,8 +387,24 @@ wtf_size_t max_end_line; GridItems virtual_items; - for (const auto& [group_items, group_properties] : Node().CollectItemGroups( - line_resolver, masonry_items, max_end_line, start_offset)) { + // If there is an auto-fit track definition, store what tracks it spans. + const GridTrackList& track_list = + is_for_columns ? style.GridTemplateColumns().GetTrackList() + : style.GridTemplateRows().GetTrackList(); + GridSpan auto_fit_span = GridSpan::IndefiniteGridSpan(); + if (!needs_auto_track_size && track_list.HasAutoRepeater() && + track_list.RepeatType(track_list.AutoRepeatTrackIndex()) == + GridTrackRepeater::RepeatType::kAutoFit) { + auto_fit_span = GridSpan::TranslatedDefiniteGridSpan( + track_list.TrackCountBeforeAutoRepeat(), + track_list.TrackCountBeforeAutoRepeat() + auto_repetition_count); + } + + wtf_size_t unplaced_item_span_count = 0; + + for (const auto& [group_items, group_properties] : + Node().CollectItemGroups(line_resolver, masonry_items, max_end_line, + start_offset, unplaced_item_span_count)) { auto* virtual_item = MakeGarbageCollected<GridItemData>(); GridSpan span = group_properties.Span(); @@ -476,12 +500,26 @@ // `Translate` will move the span to the start and end of the next line, // allowing us to "slide" over the entire implicit grid. span.Translate(1); + + // Per the auto-fit heuristic, don't add auto placed items to tracks + // within the auto-fit range that are greater than the total span count + // of auto placed items. + // + // https://drafts.csswg.org/css-grid-3/#repeat-auto-fit + if (!auto_fit_span.IsIndefinite()) { + while (span.Intersects(auto_fit_span) && + span.EndLine() > unplaced_item_span_count) { + span.Translate(1); + } + } } } DCHECK(span.IsTranslatedDefinite()); - virtual_item->resolved_position.SetSpan(span, grid_axis_direction); - virtual_items.Append(virtual_item); + if (span.EndLine() <= max_end_line) { + virtual_item->resolved_position.SetSpan(span, grid_axis_direction); + virtual_items.Append(virtual_item); + } } return virtual_items; } @@ -573,6 +611,7 @@ const SizingConstraint sizing_constraint, std::optional<LayoutUnit> auto_repeat_track_size, GridItems& masonry_items, + Vector<wtf_size_t>& collapsed_track_indexes, wtf_size_t& start_offset, bool& needs_auto_track_size, HeapVector<Member<LayoutBox>>* opt_oof_children) const { @@ -595,7 +634,8 @@ } return BuildGridAxisTracks(line_resolver, masonry_items, sizing_constraint, - needs_auto_track_size, start_offset); + needs_auto_track_size, collapsed_track_indexes, + start_offset); } GridSizingTrackCollection MasonryLayoutAlgorithm::BuildGridAxisTracks( @@ -603,12 +643,13 @@ const GridItems& masonry_items, SizingConstraint sizing_constraint, bool& needs_auto_track_size, + Vector<wtf_size_t>& collapsed_track_indexes, wtf_size_t& start_offset) const { const auto& style = Style(); const auto grid_axis_direction = style.MasonryTrackSizingDirection(); - auto virtual_items = BuildVirtualMasonryItems( + GridItems virtual_items = BuildVirtualMasonryItems( line_resolver, masonry_items, needs_auto_track_size, sizing_constraint, - start_offset); + line_resolver.AutoRepetitions(grid_axis_direction), start_offset); auto BuildRanges = [&]() { GridRangeBuilder range_builder( @@ -623,7 +664,7 @@ &range_indices.begin, &range_indices.end); } - return range_builder.FinalizeRanges(); + return range_builder.FinalizeRanges(&collapsed_track_indexes); }; GridSizingTrackCollection track_collection(BuildRanges(),
diff --git a/third_party/blink/renderer/core/layout/masonry/masonry_layout_algorithm.h b/third_party/blink/renderer/core/layout/masonry/masonry_layout_algorithm.h index 28c7be8..ea6a1b9c 100644 --- a/third_party/blink/renderer/core/layout/masonry/masonry_layout_algorithm.h +++ b/third_party/blink/renderer/core/layout/masonry/masonry_layout_algorithm.h
@@ -58,11 +58,13 @@ // indicating that another track sizing pass will be required once we've // computed the auto track size. `opt_oof_children` is an optional vector of // out-of-flow direct children of the masonry container that this method will - // populate. + // populate. `collapsed_track_indexes` will be populated with all the grid + // track indexes that were collapsed as a result of auto-fit. GridSizingTrackCollection ComputeGridAxisTracks( const SizingConstraint sizing_constraint, std::optional<LayoutUnit> auto_repeat_track_size, GridItems& masonry_items, + Vector<wtf_size_t>& collapsed_track_indexes, wtf_size_t& start_offset, bool& needs_auto_track_size, HeapVector<Member<LayoutBox>>* opt_oof_children = nullptr) const; @@ -72,6 +74,7 @@ const GridItems& masonry_items, SizingConstraint sizing_constraint, bool& needs_auto_track_size, + Vector<wtf_size_t>& collapsed_track_indexes, wtf_size_t& start_offset) const; // If `auto_repeat_track_size` is non-null, this indicates the track @@ -97,6 +100,7 @@ const GridItems& masonry_items, const bool needs_auto_track_size, SizingConstraint sizing_constraint, + const wtf_size_t auto_repetition_count, wtf_size_t& start_offset) const; LayoutUnit ComputeMasonryItemBlockContribution(
diff --git a/third_party/blink/renderer/core/layout/masonry/masonry_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/masonry/masonry_layout_algorithm_test.cc index 5fc27d4..915e5234 100644 --- a/third_party/blink/renderer/core/layout/masonry/masonry_layout_algorithm_test.cc +++ b/third_party/blink/renderer/core/layout/masonry/masonry_layout_algorithm_test.cc
@@ -25,20 +25,23 @@ wtf_size_t start_offset; const auto& style = algorithm.Style(); const GridLineResolver line_resolver(style, /*auto_repetitions=*/0); + collapsed_track_indexes_.clear(); - const auto masonry_items = - algorithm.Node().ConstructMasonryItems(line_resolver); + auto masonry_items = algorithm.Node().ConstructMasonryItems(line_resolver); bool needs_auto_track_size = false; - grid_axis_tracks_ = algorithm.BuildGridAxisTracks( - line_resolver, masonry_items, SizingConstraint::kLayout, - needs_auto_track_size, start_offset); + grid_axis_tracks_ = algorithm.ComputeGridAxisTracks( + SizingConstraint::kLayout, /*auto_repeat_track_size=*/std::nullopt, + masonry_items, collapsed_track_indexes_, start_offset, + needs_auto_track_size); const auto grid_axis_direction = grid_axis_tracks_->Direction(); ASSERT_EQ(grid_axis_direction, style.MasonryTrackSizingDirection()); for (const auto& masonry_item : algorithm.BuildVirtualMasonryItems( line_resolver, masonry_items, needs_auto_track_size, - SizingConstraint::kLayout, start_offset)) { + SizingConstraint::kLayout, + line_resolver.AutoRepetitions(grid_axis_direction), + start_offset)) { MasonryItemCachedData item_data; item_data.resolved_span = @@ -88,7 +91,8 @@ MasonryRunningPositions InitializeMasonryRunningPositions( const Vector<LayoutUnit>& running_positions, LayoutUnit tie_threshold) { - return MasonryRunningPositions(running_positions, tie_threshold); + return MasonryRunningPositions(running_positions, tie_threshold, + collapsed_track_indexes_); } void SetAutoPlacementCursor(wtf_size_t cursor, @@ -112,6 +116,9 @@ // Virtual items represent the contributions of item groups in track sizing // and are not directly related to any children of the container. Vector<MasonryItemCachedData> virtual_items_data_; + + // List of track indexes that have been collapsed. + Vector<wtf_size_t> collapsed_track_indexes_; }; TEST_F(MasonryLayoutAlgorithmTest, ConstructMasonryItems) { @@ -254,8 +261,10 @@ wtf_size_t max_end_line, start_offset; const GridLineResolver line_resolver(node.Style(), /*auto_repetitions=*/0); const auto masonry_items = node.ConstructMasonryItems(line_resolver); - const auto item_groups = node.CollectItemGroups(line_resolver, masonry_items, - max_end_line, start_offset); + wtf_size_t unplaced_item_span_count = 0; + const auto item_groups = + node.CollectItemGroups(line_resolver, masonry_items, max_end_line, + start_offset, unplaced_item_span_count); EXPECT_EQ(item_groups.size(), 4u); @@ -511,11 +520,350 @@ LayoutUnit(30)})); } +TEST_F(MasonryLayoutAlgorithmTest, ColumnAutoFitAutoPlacement) { + SetBodyInnerHTML(R"HTML( + <style> + #masonry { + display: masonry; + grid-template-columns: repeat(auto-fit, 100px); + } + #masonry > div { + width: 100%; + height: 100px; + } + </style> + <div id="masonry"> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + </div> + )HTML"); + + BlockNode node(GetLayoutBoxByElementId("masonry")); + + const auto space = ConstructBlockLayoutTestConstraintSpace( + {WritingMode::kHorizontalTb, TextDirection::kLtr}, + LogicalSize(LayoutUnit(1000), LayoutUnit(200)), + /*stretch_inline_size_if_auto=*/true, + /*is_new_formatting_context=*/true); + + const auto fragment_geometry = + CalculateInitialFragmentGeometry(space, node, /*break_token=*/nullptr); + + MasonryLayoutAlgorithm algorithm({node, fragment_geometry, space}); + ComputeGeometry(algorithm); + + EXPECT_EQ(TrackSizes(), Vector<LayoutUnit>({LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100)})); +} + +TEST_F(MasonryLayoutAlgorithmTest, ColumnAutoFitAutoAndExplicitPlacement) { + SetBodyInnerHTML(R"HTML( + <style> + #masonry { + display: masonry; + grid-template-columns: repeat(auto-fit, 100px); + } + #masonry > div { + width: 100%; + height: 100px; + } + </style> + <div id="masonry"> + <div></div> + <div></div> + <div style="grid-column: 4"></div> + <div style="grid-column: 6"></div> + <div></div> + </div> + )HTML"); + + BlockNode node(GetLayoutBoxByElementId("masonry")); + + const auto space = ConstructBlockLayoutTestConstraintSpace( + {WritingMode::kHorizontalTb, TextDirection::kLtr}, + LogicalSize(LayoutUnit(1000), LayoutUnit(200)), + /*stretch_inline_size_if_auto=*/true, + /*is_new_formatting_context=*/true); + + const auto fragment_geometry = + CalculateInitialFragmentGeometry(space, node, /*break_token=*/nullptr); + + MasonryLayoutAlgorithm algorithm({node, fragment_geometry, space}); + ComputeGeometry(algorithm); + + EXPECT_EQ(TrackSizes(), Vector<LayoutUnit>({LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100)})); +} + +TEST_F(MasonryLayoutAlgorithmTest, ColumnAutoFillAutoFitAutoPlacement) { + SetBodyInnerHTML(R"HTML( + <style> + #masonry { + display: masonry; + grid-template-columns: repeat(5, 100px) repeat(auto-fit, 100px); + } + #masonry > div { + width: 100%; + height: 100px; + } + </style> + <div id="masonry"> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + </div> + )HTML"); + + BlockNode node(GetLayoutBoxByElementId("masonry")); + + const auto space = ConstructBlockLayoutTestConstraintSpace( + {WritingMode::kHorizontalTb, TextDirection::kLtr}, + LogicalSize(LayoutUnit(1000), LayoutUnit(200)), + /*stretch_inline_size_if_auto=*/true, + /*is_new_formatting_context=*/true); + + const auto fragment_geometry = + CalculateInitialFragmentGeometry(space, node, /*break_token=*/nullptr); + + MasonryLayoutAlgorithm algorithm({node, fragment_geometry, space}); + ComputeGeometry(algorithm); + + EXPECT_EQ( + TrackSizes(), + Vector<LayoutUnit>({LayoutUnit(100), LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100), LayoutUnit(100), LayoutUnit(100)})); +} + +TEST_F(MasonryLayoutAlgorithmTest, ColumnAutoFillAutoFitNoCollapse) { + SetBodyInnerHTML(R"HTML( + <style> + #masonry { + display: masonry; + grid-template-columns: repeat(auto-fit, 100px) repeat(5, 100px); + } + #masonry > div { + width: 100%; + height: 100px; + } + </style> + <div id="masonry"> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + </div> + )HTML"); + + BlockNode node(GetLayoutBoxByElementId("masonry")); + + const auto space = ConstructBlockLayoutTestConstraintSpace( + {WritingMode::kHorizontalTb, TextDirection::kLtr}, + LogicalSize(LayoutUnit(1000), LayoutUnit(200)), + /*stretch_inline_size_if_auto=*/true, + /*is_new_formatting_context=*/true); + + const auto fragment_geometry = + CalculateInitialFragmentGeometry(space, node, /*break_token=*/nullptr); + + MasonryLayoutAlgorithm algorithm({node, fragment_geometry, space}); + ComputeGeometry(algorithm); + + EXPECT_EQ( + TrackSizes(), + Vector<LayoutUnit>({LayoutUnit(100), LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100), LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100), LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100)})); +} + +TEST_F(MasonryLayoutAlgorithmTest, RowAutoFitAutoPlacement) { + SetBodyInnerHTML(R"HTML( + <style> + #masonry { + display: masonry; + masonry-direction: row; + grid-template-rows: repeat(auto-fit, 100px); + height: 1000px; + } + #masonry > div { + height: 100%; + width: 100px; + } + </style> + <div id="masonry"> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + </div> + )HTML"); + + BlockNode node(GetLayoutBoxByElementId("masonry")); + + const auto space = ConstructBlockLayoutTestConstraintSpace( + {WritingMode::kHorizontalTb, TextDirection::kLtr}, + LogicalSize(LayoutUnit(200), LayoutUnit(1000)), + /*stretch_inline_size_if_auto=*/true, + /*is_new_formatting_context=*/true); + + const auto fragment_geometry = + CalculateInitialFragmentGeometry(space, node, /*break_token=*/nullptr); + + MasonryLayoutAlgorithm algorithm({node, fragment_geometry, space}); + ComputeGeometry(algorithm); + + EXPECT_EQ(TrackSizes(), Vector<LayoutUnit>({LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100)})); +} + +TEST_F(MasonryLayoutAlgorithmTest, RowAutoFitAutoAndExplicitPlacement) { + SetBodyInnerHTML(R"HTML( + <style> + #masonry { + display: masonry; + masonry-direction: row; + grid-template-rows: repeat(auto-fit, 100px); + height: 1000px; + } + #masonry > div { + height: 100%; + width: 100px; + } + </style> + <div id="masonry"> + <div></div> + <div></div> + <div style="grid-row: 4"></div> + <div style="grid-row: 6"></div> + <div></div> + </div> + )HTML"); + + BlockNode node(GetLayoutBoxByElementId("masonry")); + + const auto space = ConstructBlockLayoutTestConstraintSpace( + {WritingMode::kHorizontalTb, TextDirection::kLtr}, + LogicalSize(LayoutUnit(200), LayoutUnit(1000)), + /*stretch_inline_size_if_auto=*/true, + /*is_new_formatting_context=*/true); + + const auto fragment_geometry = + CalculateInitialFragmentGeometry(space, node, /*break_token=*/nullptr); + + MasonryLayoutAlgorithm algorithm({node, fragment_geometry, space}); + ComputeGeometry(algorithm); + + EXPECT_EQ(TrackSizes(), Vector<LayoutUnit>({LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100)})); +} + +TEST_F(MasonryLayoutAlgorithmTest, RowAutoFillAutoFitAutoPlacement) { + SetBodyInnerHTML(R"HTML( + <style> + #masonry { + display: masonry; + masonry-direction: row; + grid-template-rows: repeat(5, 100px) repeat(auto-fit, 100px); + height: 1000px; + } + #masonry > div { + height: 100%; + width: 100px; + } + </style> + <div id="masonry"> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + </div> + )HTML"); + + BlockNode node(GetLayoutBoxByElementId("masonry")); + + const auto space = ConstructBlockLayoutTestConstraintSpace( + {WritingMode::kHorizontalTb, TextDirection::kLtr}, + LogicalSize(LayoutUnit(200), LayoutUnit(1000)), + /*stretch_inline_size_if_auto=*/true, + /*is_new_formatting_context=*/true); + + const auto fragment_geometry = + CalculateInitialFragmentGeometry(space, node, /*break_token=*/nullptr); + + MasonryLayoutAlgorithm algorithm({node, fragment_geometry, space}); + ComputeGeometry(algorithm); + + EXPECT_EQ( + TrackSizes(), + Vector<LayoutUnit>({LayoutUnit(100), LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100), LayoutUnit(100), LayoutUnit(100)})); +} + +TEST_F(MasonryLayoutAlgorithmTest, RowAutoFillAutoFitNoCollapse) { + SetBodyInnerHTML(R"HTML( + <style> + #masonry { + display: masonry; + masonry-direction: row; + grid-template-rows: repeat(auto-fit, 100px) repeat(5, 100px); + height: 1000px; + } + #masonry > div { + height: 100%; + width: 100px; + } + </style> + <div id="masonry"> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + </div> + )HTML"); + + BlockNode node(GetLayoutBoxByElementId("masonry")); + + const auto space = ConstructBlockLayoutTestConstraintSpace( + {WritingMode::kHorizontalTb, TextDirection::kLtr}, + LogicalSize(LayoutUnit(200), LayoutUnit(1000)), + /*stretch_inline_size_if_auto=*/true, + /*is_new_formatting_context=*/true); + + const auto fragment_geometry = + CalculateInitialFragmentGeometry(space, node, /*break_token=*/nullptr); + + MasonryLayoutAlgorithm algorithm({node, fragment_geometry, space}); + ComputeGeometry(algorithm); + + EXPECT_EQ( + TrackSizes(), + Vector<LayoutUnit>({LayoutUnit(100), LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100), LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100), LayoutUnit(100), LayoutUnit(100), + LayoutUnit(100)})); +} + TEST_F(MasonryLayoutAlgorithmTest, UpdateRunningPositionsForSpan) { + Vector<wtf_size_t> collapsed_track_indexes; MasonryRunningPositions running_positions( /*track_count=*/4, /*initial_running_position=*/LayoutUnit(), - /*tie_threshold=*/LayoutUnit()); + /*tie_threshold=*/LayoutUnit(), collapsed_track_indexes); Vector<LayoutUnit> expected_running_positions = { LayoutUnit(0), LayoutUnit(3), LayoutUnit(3), LayoutUnit(0)};
diff --git a/third_party/blink/renderer/core/layout/masonry/masonry_node.cc b/third_party/blink/renderer/core/layout/masonry/masonry_node.cc index 2010def3..520e4a4b 100644 --- a/third_party/blink/renderer/core/layout/masonry/masonry_node.cc +++ b/third_party/blink/renderer/core/layout/masonry/masonry_node.cc
@@ -30,7 +30,8 @@ const GridLineResolver& line_resolver, const GridItems& masonry_items, wtf_size_t& max_end_line, - wtf_size_t& start_offset) const { + wtf_size_t& start_offset, + wtf_size_t& unplaced_item_span_count) const { const auto grid_axis_direction = Style().MasonryTrackSizingDirection(); start_offset = 0; @@ -50,6 +51,13 @@ child.Style(), grid_axis_direction)); const auto& item_span = item_properties.Span(); + // Keep a running sum of unplaced item spans to determine where to + // place auto placed virtual items per the auto-fit masonry heuristic. + // + // https://drafts.csswg.org/css-grid-3/#repeat-auto-fit + if (item_span.IsIndefinite()) { + unplaced_item_span_count += item_span.SpanSize(); + } if (item_span.IsUntranslatedDefinite()) { start_offset = std::max<int>(start_offset, -item_span.UntranslatedStartLine());
diff --git a/third_party/blink/renderer/core/layout/masonry/masonry_node.h b/third_party/blink/renderer/core/layout/masonry/masonry_node.h index feababc..55a7df37 100644 --- a/third_party/blink/renderer/core/layout/masonry/masonry_node.h +++ b/third_party/blink/renderer/core/layout/masonry/masonry_node.h
@@ -26,11 +26,14 @@ // provided by `masonry_items`) into item groups based on their placement, // span size, and baseline-sharing group. `start_offset` calculates the offset // of the first grid line in the implicit grid, which is used to translate - // definite grid spans to a 0-indexed format. - MasonryItemGroups CollectItemGroups(const GridLineResolver& line_resolver, - const GridItems& masonry_items, - wtf_size_t& max_end_line, - wtf_size_t& start_offset) const; + // definite grid spans to a 0-indexed format. `unplaced_item_span_count` is + // an ouput param that is the sum of all auto placed item span sizes. + MasonryItemGroups CollectItemGroups( + const GridLineResolver& line_resolver, + const GridItems& masonry_items, + wtf_size_t& max_end_line, + wtf_size_t& start_offset, + wtf_size_t& unplaced_item_span_count) const; // Collects the children of this node, sorts by order property if needed, and // resolves the grid line positions of the items based on style.
diff --git a/third_party/blink/renderer/core/layout/masonry/masonry_running_positions.h b/third_party/blink/renderer/core/layout/masonry/masonry_running_positions.h index cd5cb38..47556355 100644 --- a/third_party/blink/renderer/core/layout/masonry/masonry_running_positions.h +++ b/third_party/blink/renderer/core/layout/masonry/masonry_running_positions.h
@@ -20,9 +20,16 @@ public: MasonryRunningPositions(wtf_size_t track_count, LayoutUnit initial_running_position, - LayoutUnit tie_threshold) + LayoutUnit tie_threshold, + const Vector<wtf_size_t>& collapsed_track_indexes) : running_positions_(track_count, initial_running_position), - tie_threshold_(tie_threshold) {} + tie_threshold_(tie_threshold) { + // To avoid placing items in collapsed tracks, set such tracks to the max + // size. + for (wtf_size_t index : collapsed_track_indexes) { + running_positions_[index] = LayoutUnit::Max(); + } + } // Return the first span within `tie_threshold_` of the minimum max-position // that comes after the auto-placement cursor in masonry's flow. @@ -60,8 +67,15 @@ // For testing only. MasonryRunningPositions(const Vector<LayoutUnit>& running_positions, - LayoutUnit tie_threshold) - : running_positions_(running_positions), tie_threshold_(tie_threshold) {} + LayoutUnit tie_threshold, + const Vector<wtf_size_t>& collapsed_track_indexes) + : running_positions_(running_positions), tie_threshold_(tie_threshold) { + // To avoid placing items in collapsed tracks, set such tracks to the max + // size. + for (wtf_size_t index : collapsed_track_indexes) { + running_positions_[index] = LayoutUnit::Max(); + } + } void SetAutoPlacementCursorForTesting(wtf_size_t cursor) { auto_placement_cursor_ = cursor;
diff --git a/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.h b/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.h index 418dd7f..219b616 100644 --- a/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.h +++ b/third_party/blink/renderer/core/paint/timing/image_paint_timing_detector.h
@@ -290,8 +290,8 @@ std::optional<uint64_t> viewport_size_; // Whether the viewport size used is the page viewport. bool uses_page_viewport_; - // Are we recording an LCP candidate? True after a navigation (including soft - // navigations) until the next user interaction. + // Are we recording an LCP candidate? True after a hard navigation until the + // next user interaction. bool recording_largest_image_paint_ = true; ImageRecordsManager records_manager_;
diff --git a/third_party/blink/renderer/core/paint/timing/paint_timing_detector.cc b/third_party/blink/renderer/core/paint/timing/paint_timing_detector.cc index 49ab2bea..b0d3acf 100644 --- a/third_party/blink/renderer/core/paint/timing/paint_timing_detector.cc +++ b/third_party/blink/renderer/core/paint/timing/paint_timing_detector.cc
@@ -223,6 +223,32 @@ current_paint_chunk_properties, nullptr, image_border); } +// static +void PaintTimingDetector::NotifyFirstVideoFrame( + const LayoutObject& object, + const gfx::Size& intrinsic_size, + const MediaTiming& media_timing, + const PropertyTreeStateOrAlias& current_paint_chunk_properties, + const gfx::Rect& image_border) { + if (NotifyImagePaint(object, intrinsic_size, media_timing, + current_paint_chunk_properties, image_border)) { + LocalFrameView* frame_view = object.GetFrameView(); + CHECK(frame_view); + // crbug.com/434659231: Recording this as an LCP candidate and setting the + // presentation time (without ReportFirstFrameTimeAsRenderTime) depends on + // the next main frame, which we request here. This is flag-guarded for hard + // LCP, since it might move metrics; for soft navs, do this unconditionally + // since this is still experimental and we want accurate behavior for origin + // trial along with attributing video src changes (crbug.com/434215966). + if (RuntimeEnabledFeatures::RequestMainFrameAfterFirstVideoFrameEnabled() || + !frame_view->GetPaintTimingDetector() + .GetImagePaintTimingDetector() + .IsRecordingLargestImagePaint()) { + frame_view->ScheduleAnimation(); + } + } +} + void PaintTimingDetector::NotifyImageFinished(const LayoutObject& object, const MediaTiming* media_timing) { if (IgnorePaintTimingScope::ShouldIgnore()) {
diff --git a/third_party/blink/renderer/core/paint/timing/paint_timing_detector.h b/third_party/blink/renderer/core/paint/timing/paint_timing_detector.h index 056d3ae..a16f882 100644 --- a/third_party/blink/renderer/core/paint/timing/paint_timing_detector.h +++ b/third_party/blink/renderer/core/paint/timing/paint_timing_detector.h
@@ -69,6 +69,12 @@ const MediaTiming& media_timing, const PropertyTreeStateOrAlias& current_paint_chunk_properties, const gfx::Rect& image_border); + static void NotifyFirstVideoFrame( + const LayoutObject&, + const gfx::Size& intrinsic_size, + const MediaTiming& media_timing, + const PropertyTreeStateOrAlias& current_paint_chunk_properties, + const gfx::Rect& image_border); inline static void NotifyTextPaint(const gfx::Rect& text_visual_rect); void NotifyImageFinished(const LayoutObject&, const MediaTiming*);
diff --git a/third_party/blink/renderer/core/style/grid_track_list.cc b/third_party/blink/renderer/core/style/grid_track_list.cc index 2ffdfed..fb287f03 100644 --- a/third_party/blink/renderer/core/style/grid_track_list.cc +++ b/third_party/blink/renderer/core/style/grid_track_list.cc
@@ -144,6 +144,7 @@ break; case GridTrackRepeater::RepeatType::kAutoFill: case GridTrackRepeater::RepeatType::kAutoFit: // Intentional Fallthrough. + track_count_before_auto_repeat_ = track_count_without_auto_repeat_; has_auto_sized_repeater_ = std::find(repeater_track_sizes.begin(), repeater_track_sizes.end(), Length::Auto()) != repeater_track_sizes.end(); @@ -198,6 +199,7 @@ repeater_track_sizes_ = other.repeater_track_sizes_; auto_repeater_index_ = other.auto_repeater_index_; track_count_without_auto_repeat_ = other.track_count_without_auto_repeat_; + track_count_before_auto_repeat_ = other.track_count_before_auto_repeat_; non_auto_repeat_line_count_ = other.non_auto_repeat_line_count_; axis_type_ = other.axis_type_; has_auto_sized_repeater_ = other.has_auto_sized_repeater_;
diff --git a/third_party/blink/renderer/core/style/grid_track_list.h b/third_party/blink/renderer/core/style/grid_track_list.h index 982b2da4..72e865a 100644 --- a/third_party/blink/renderer/core/style/grid_track_list.h +++ b/third_party/blink/renderer/core/style/grid_track_list.h
@@ -73,8 +73,16 @@ wtf_size_t RepeaterCount() const; // Returns the count of all tracks, ignoring those within an auto repeater. wtf_size_t TrackCountWithoutAutoRepeat() const; + // Returns the count of tracks up to an auto repeater. If there is no auto + // repeater, returns 0. + wtf_size_t TrackCountBeforeAutoRepeat() const { + return track_count_before_auto_repeat_; + } // Returns the number of tracks in the auto repeater, or 0 if there is none. wtf_size_t AutoRepeatTrackCount() const; + // Returns the start index of the auto repeater, or kNotFound if there is + // none. + wtf_size_t AutoRepeatTrackIndex() const { return auto_repeater_index_; } // Returns the count of line names not including auto repeaters. Note that // this is subtly different than `TrackCountWithoutAutoRepeat`, as it is // specifically line names (not sizes), and includes empty line names. @@ -124,6 +132,10 @@ // Count of tracks ignoring those within an auto repeater. wtf_size_t track_count_without_auto_repeat_{0}; + // Count of tracks up to an auto repeater. If there is no auto repeater, it is + // 0. + wtf_size_t track_count_before_auto_repeat_{0}; + // Count of line names outside of auto-repeaters. This is subtly different // than `track_count_without_auto_repeat_`, as that is track definitions, // while this tracks line names (including empty lines).
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn index 7d666bcb..fa98a4e 100644 --- a/third_party/blink/renderer/modules/BUILD.gn +++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -735,6 +735,7 @@ "//third_party/opus", "//third_party/webrtc_overrides:logging_test", "//third_party/webrtc_overrides:webrtc_component", + "//ui/accessibility", "//ui/strings:ax_strings_grit", "//v8", ]
diff --git a/third_party/blink/renderer/modules/DEPS b/third_party/blink/renderer/modules/DEPS index 66a62d0..781f92c 100644 --- a/third_party/blink/renderer/modules/DEPS +++ b/third_party/blink/renderer/modules/DEPS
@@ -25,6 +25,7 @@ "+third_party/blink/renderer/modules", "+ui/accessibility/accessibility_features.h", "+ui/accessibility/ax_mode.h", + "+ui/accessibility/ax_action_data.h", "+ui/display/screen_info.h", "+ui/display/screen_infos.h", "+ui/display/mojom",
diff --git a/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent.cc b/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent.cc index 74dcf6b..619eb90 100644 --- a/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent.cc +++ b/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent.cc
@@ -1235,7 +1235,10 @@ void AIPageContentAgent::ContentBuilder::AddNodeGeometry( const LayoutObject& object, mojom::blink::AIPageContentAttributes& attributes) const { - if (!actionable_mode()) { + // When in non-actionable mode, we only want to add geometry for the + // accessibility focused node. + if (!actionable_mode() && + attributes.dom_node_id != accessibility_focused_node_id_) { return; } @@ -1320,8 +1323,9 @@ // Accessibility focus if (AXObjectCache* ax_object_cache = document.ExistingAXObjectCache()) { if (Node* ax_focused_node = ax_object_cache->GetAccessibilityFocus()) { + accessibility_focused_node_id_ = DOMNodeIds::IdForNode(ax_focused_node); page_interaction_info.accessibility_focused_dom_node_id = - DOMNodeIds::IdForNode(ax_focused_node); + accessibility_focused_node_id_; AddInteractiveNode( *page_interaction_info.accessibility_focused_dom_node_id); }
diff --git a/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent.h b/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent.h index 096d0dd..b3669f6 100644 --- a/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent.h +++ b/third_party/blink/renderer/modules/content_extraction/ai_page_content_agent.h
@@ -149,6 +149,10 @@ // produce a ContentNode. base::flat_set<DOMNodeId> interactive_dom_node_ids_; + // If present, the node which is accessibility focused. This is used to + // determine which node to add geometry for in non-actionable mode. + DOMNodeId accessibility_focused_node_id_ = kInvalidDOMNodeId; + const raw_ref<const mojom::blink::AIPageContentOptions> options_; base::flat_map<DOMNodeId, int32_t> dom_node_to_z_order_;
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 fc7b13e17..0d4b6d9 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
@@ -10,6 +10,8 @@ #include "mojo/public/cpp/test_support/test_utils.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/input/web_mouse_event.h" +#include "third_party/blink/renderer/core/accessibility/ax_context.h" +#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/dom_node_ids.h" #include "third_party/blink/renderer/core/frame/frame_test_helpers.h" @@ -18,10 +20,13 @@ #include "third_party/blink/renderer/core/input/event_handler.h" #include "third_party/blink/renderer/core/layout/layout_object.h" #include "third_party/blink/renderer/core/layout/layout_view.h" +#include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h" #include "third_party/blink/renderer/platform/testing/task_environment.h" #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" #include "third_party/blink/renderer/platform/testing/url_test_helpers.h" #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h" +#include "ui/accessibility/ax_action_data.h" +#include "ui/accessibility/ax_mode.h" namespace blink { using ClickabilityReason = mojom::blink::AIPageContentClickabilityReason; @@ -2286,6 +2291,55 @@ button.content_attributes->dom_node_id); } +TEST_F(AIPageContentAgentTest, AccessibilityFocus) { + frame_test_helpers::LoadHTMLString( + helper_.LocalMainFrame(), + "<body>" + " <style>" + " #button1 {" + " position: absolute;" + " top: -10px;" + " left: -20px;" + " width: 30px;" + " height: 40px;" + " }" + " </style>" + " <button id='button1'>button1</button>" + " <div id='div2'>div2</div>" + "</body>", + url_test_helpers::ToKURL("http://foobar.com")); + + // Enable accessibility. + ui::AXMode ax_mode = ui::kAXModeComplete; + Document* document = helper_.LocalMainFrame()->GetFrame()->GetDocument(); + auto context = std::make_unique<AXContext>(*document, ax_mode); + EXPECT_TRUE(document->ExistingAXObjectCache()); + auto* ax_object_cache = + To<AXObjectCacheImpl>(document->ExistingAXObjectCache()); + EXPECT_EQ(ax_mode, ax_object_cache->GetAXMode()); + ax_object_cache->UpdateAXForAllDocuments(); + + // Set accessibility focus to the button. + auto* button_element = document->getElementById(AtomicString("button1")); + auto* button_ax_object = ax_object_cache->Get(button_element); + ui::AXActionData action_data; + action_data.action = ax::mojom::blink::Action::kSetAccessibilityFocus; + button_ax_object->PerformAction(action_data); + + GetAIPageContent(); + + const auto& root = ContentRootNode(); + EXPECT_EQ(root.children_nodes.size(), 2u); + + const auto& button = *root.children_nodes[0]; + const auto& div2 = *root.children_nodes[1]; + const auto& page_interaction_info = Content()->page_interaction_info; + EXPECT_EQ(page_interaction_info->accessibility_focused_dom_node_id, + button.content_attributes->dom_node_id); + CheckGeometry(button, gfx::Rect(-20, -10, 30, 40), gfx::Rect(0, 0, 10, 30)); + EXPECT_FALSE(div2.content_attributes->geometry); +} + TEST_F(AIPageContentAgentTest, MousePosition) { frame_test_helpers::LoadHTMLString( helper_.LocalMainFrame(),
diff --git a/third_party/blink/renderer/modules/content_extraction/frame_metadata_observer_registry.cc b/third_party/blink/renderer/modules/content_extraction/frame_metadata_observer_registry.cc index 521a01976..0598ceb4 100644 --- a/third_party/blink/renderer/modules/content_extraction/frame_metadata_observer_registry.cc +++ b/third_party/blink/renderer/modules/content_extraction/frame_metadata_observer_registry.cc
@@ -4,19 +4,56 @@ #include "third_party/blink/renderer/modules/content_extraction/frame_metadata_observer_registry.h" -#include "third_party/blink/renderer/bindings/core/v8/v8_mutation_observer_init.h" +#include "mojo/public/cpp/bindings/remote_set.h" +#include "third_party/blink/public/mojom/content_extraction/ai_page_content_metadata.mojom-blink.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/events/event.h" -#include "third_party/blink/renderer/core/dom/mutation_observer.h" -#include "third_party/blink/renderer/core/dom/mutation_record.h" +#include "third_party/blink/renderer/core/dom/tree_scope.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/core/frame/remote_frame.h" #include "third_party/blink/renderer/core/html/html_head_element.h" +#include "third_party/blink/renderer/core/html/html_meta_element.h" +#include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/modules/content_extraction/paid_content.h" -#include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" namespace blink { +namespace { + +void CollectMetaTagsFromFrame(LocalFrame* frame, + const HeapVector<String>& names_to_find, + mojom::blink::PageMetadata& page_metadata) { + if (!frame) { + return; + } + + auto* local_frame = To<LocalFrame>(frame); + Document* document = local_frame->GetDocument(); + if (document && document->head()) { + Vector<mojom::blink::MetaTagPtr> found_tags; + for (HTMLMetaElement& meta : + Traversal<HTMLMetaElement>::ChildrenOf(*document->head())) { + const String& name = meta.GetName(); + if (names_to_find.Contains(name)) { + auto meta_tag = mojom::blink::MetaTag::New(); + meta_tag->name = name; + meta_tag->content = meta.Content(); + found_tags.push_back(std::move(meta_tag)); + } + } + if (!found_tags.empty()) { + auto frame_metadata = mojom::blink::FrameMetadata::New(); + frame_metadata->url = document->Url(); + frame_metadata->meta_tags = std::move(found_tags); + page_metadata.frame_metadata.push_back(std::move(frame_metadata)); + } + } +} + +} // namespace + // static const char FrameMetadataObserverRegistry::kSupplementName[] = "FrameMetadataObserverRegistry"; @@ -49,7 +86,11 @@ LocalFrame& frame) : Supplement<Document>(*frame.GetDocument()), receiver_set_(this, frame.DomWindow()), - paid_content_metadata_observers_(frame.DomWindow()) {} + paid_content_metadata_observers_(frame.DomWindow()), + metatags_observers_(frame.DomWindow()) { + metatags_observers_.set_disconnect_handler(blink::BindRepeating( + &FrameMetadataObserverRegistry::DisconnectHandler, WrapPersistent(this))); +} FrameMetadataObserverRegistry::~FrameMetadataObserverRegistry() = default; @@ -66,6 +107,8 @@ visitor->Trace(receiver_set_); visitor->Trace(dom_content_loaded_observer_); visitor->Trace(paid_content_metadata_observers_); + visitor->Trace(metatags_observers_); + visitor->Trace(metatags_observer_names_); } class FrameMetadataObserverRegistry::DomContentLoadedListener final @@ -83,19 +126,13 @@ auto* registry = Supplement<Document>::From<FrameMetadataObserverRegistry>(document); - if (!registry) { - // There is no registry. Just abort. - return; + if (registry) { + registry->OnDomContentLoaded(); } - registry->OnDomContentLoaded(); } }; -void FrameMetadataObserverRegistry::AddPaidContentMetadataObserver( - mojo::PendingRemote<mojom::blink::PaidContentMetadataObserver> observer) { - paid_content_metadata_observers_.Add( - std::move(observer), - GetSupplementable()->GetTaskRunner(TaskType::kInternalUserInteraction)); +void FrameMetadataObserverRegistry::ListenForDomContentLoaded() { if (GetSupplementable()->HasFinishedParsing()) { OnDomContentLoaded(); } else { @@ -109,8 +146,28 @@ } } +void FrameMetadataObserverRegistry::AddPaidContentMetadataObserver( + mojo::PendingRemote<mojom::blink::PaidContentMetadataObserver> observer) { + paid_content_metadata_observers_.Add( + std::move(observer), + GetSupplementable()->GetTaskRunner(TaskType::kInternalUserInteraction)); + ListenForDomContentLoaded(); +} + +void FrameMetadataObserverRegistry::AddMetaTagsObserver( + const Vector<String>& names, + mojo::PendingRemote<mojom::blink::MetaTagsObserver> observer) { + const mojo::RemoteSetElementId& remote_id = metatags_observers_.Add( + std::move(observer), + GetSupplementable()->GetTaskRunner(TaskType::kInternalUserInteraction)); + + metatags_observer_names_.Set(remote_id.value(), HeapVector<String>(names)); + ListenForDomContentLoaded(); +} + void FrameMetadataObserverRegistry::OnDomContentLoaded() { OnPaidContentMetadataChanged(); + OnMetaTagsChanged(); if (dom_content_loaded_observer_) { GetSupplementable()->removeEventListener( @@ -121,14 +178,42 @@ } void FrameMetadataObserverRegistry::OnPaidContentMetadataChanged() { - bool has_paid_content = PaidContent::HasPaidContent(*GetSupplementable()); + if (paid_content_metadata_observers_.empty()) { + return; + } + PaidContent paid_content; + bool paid_content_exists = + paid_content.QueryPaidElements(*GetSupplementable()); - // TODO(gklassen): Add a MuationObserver to monitor for changes during the - // lifetime of the page. + // TODO(gklassen): Add a MutationObserver to monitor for changes during + // the lifetime of the page. for (auto& observer : paid_content_metadata_observers_) { - observer->OnPaidContentMetadataChanged(has_paid_content); + observer->OnPaidContentMetadataChanged(paid_content_exists); } } +void FrameMetadataObserverRegistry::OnMetaTagsChanged() { + if (metatags_observers_.empty()) { + return; + } + + LocalFrame* current_frame = GetSupplementable()->GetFrame(); + if (!current_frame) { + return; + } + + for (auto& it : metatags_observer_names_) { + auto page_metadata = mojom::blink::PageMetadata::New(); + CollectMetaTagsFromFrame(current_frame, it.value, *page_metadata); + metatags_observers_.Get(mojo::RemoteSetElementId(it.key)) + ->OnMetaTagsChanged(std::move(page_metadata)); + } +} + +void FrameMetadataObserverRegistry::DisconnectHandler( + mojo::RemoteSetElementId id) { + metatags_observer_names_.erase(id.value()); +} + } // namespace blink
diff --git a/third_party/blink/renderer/modules/content_extraction/frame_metadata_observer_registry.h b/third_party/blink/renderer/modules/content_extraction/frame_metadata_observer_registry.h index 76db706..c6fd25a 100644 --- a/third_party/blink/renderer/modules/content_extraction/frame_metadata_observer_registry.h +++ b/third_party/blink/renderer/modules/content_extraction/frame_metadata_observer_registry.h
@@ -7,16 +7,20 @@ #include "base/memory/raw_ptr.h" #include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/remote_set.h" #include "third_party/blink/public/mojom/content_extraction/frame_metadata_observer_registry.mojom-blink.h" #include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" #include "third_party/blink/renderer/modules/modules_export.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h" +#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h" +#include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/mojo/heap_mojo_receiver_set.h" #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote_set.h" -#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" #include "third_party/blink/renderer/platform/supplementable.h" namespace blink { + class LocalFrame; // Registry used to Add Observers for when frame metadata changes. @@ -41,18 +45,29 @@ void Trace(Visitor* visitor) const override; - private: - class DomContentLoadedListener; - - void Bind(mojo::PendingReceiver<mojom::blink::FrameMetadataObserverRegistry> - receiver); - + // mojom::blink::FrameMetadataObserverRegistry: void AddPaidContentMetadataObserver( mojo::PendingRemote<mojom::blink::PaidContentMetadataObserver> observer) override; + void AddMetaTagsObserver( + const Vector<String>& names, + mojo::PendingRemote<mojom::blink::MetaTagsObserver> observer) override; + + private: + class DomContentLoadedListener; + friend class DomContentLoadedListener; + + void Bind(mojo::PendingReceiver<mojom::blink::FrameMetadataObserverRegistry> + receiver); + void OnDomContentLoaded(); void OnPaidContentMetadataChanged(); + void OnMetaTagsChanged(); + + void ListenForDomContentLoaded(); + + void DisconnectHandler(mojo::RemoteSetElementId id); HeapMojoReceiverSet<mojom::blink::FrameMetadataObserverRegistry, FrameMetadataObserverRegistry> @@ -61,6 +76,10 @@ HeapMojoRemoteSet<mojom::blink::PaidContentMetadataObserver> paid_content_metadata_observers_; + HeapMojoRemoteSet<mojom::blink::MetaTagsObserver> metatags_observers_; + + HeapHashMap<uint32_t, HeapVector<String>> metatags_observer_names_; + Member<DomContentLoadedListener> dom_content_loaded_observer_; };
diff --git a/third_party/blink/renderer/modules/web_install/navigator_web_install.idl b/third_party/blink/renderer/modules/web_install/navigator_web_install.idl index 3485ec4..113131c 100644 --- a/third_party/blink/renderer/modules/web_install/navigator_web_install.idl +++ b/third_party/blink/renderer/modules/web_install/navigator_web_install.idl
@@ -8,7 +8,7 @@ RuntimeEnabled=WebAppInstallation, ImplementedAs=NavigatorWebInstall ] partial interface Navigator { - [CallWith=ScriptState, RaisesException] Promise<WebInstallResult> install(); - [CallWith=ScriptState, RaisesException] Promise<WebInstallResult> install(USVString install_url); - [CallWith=ScriptState, RaisesException] Promise<WebInstallResult> install(USVString install_url, USVString manifest_id); + [CallWith=ScriptState, RaisesException, MeasureAs="WebDXFeature::kDRAFT_WebInstallAPI"] Promise<WebInstallResult> install(); + [CallWith=ScriptState, RaisesException, MeasureAs="WebDXFeature::kDRAFT_WebInstallAPI"] Promise<WebInstallResult> install(USVString install_url); + [CallWith=ScriptState, RaisesException, MeasureAs="WebDXFeature::kDRAFT_WebInstallAPI"] Promise<WebInstallResult> install(USVString install_url, USVString manifest_id); }; \ No newline at end of file
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc index 3e1657cf..372ae8a 100644 --- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc +++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -407,7 +407,9 @@ if (!shared_image_interface) { return SoftwareResource(); } - // ReadFramebufferIntoBitmapPixels always produced bottom-Left origin. + // glReadPixels always read with bottom-Left origin regardless of framebuffer + // flip extension, so keep shared image the same so we don't need to flip + // here. auto shared_image = shared_image_interface->CreateSharedImageForSoftwareCompositor( {format, size_, color_space, kBottomLeft_GrSurfaceOrigin, @@ -455,8 +457,11 @@ } auto mapping = resource.shared_image->Map(); - ReadFramebufferIntoBitmapPixels( - static_cast<uint8_t*>(mapping->GetMemoryForPlane(0).data())); + + // Readback in Skia native byte order (RGBA or BGRA) with kN32_SkColorType. + ReadBackFramebuffer(mapping->GetMemoryForPlane(0), kN32_SkColorType, + kPremul_SkAlphaType, kBottomLeft_GrSurfaceOrigin, + kBackBuffer); *out_resource = viz::TransferableResource::Make( resource.shared_image, @@ -529,7 +534,14 @@ SkBitmap bitmap; if (!bitmap.tryAllocN32Pixels(size_.width(), size_.height())) return nullptr; - ReadFramebufferIntoBitmapPixels(static_cast<uint8_t*>(bitmap.getPixels())); + const size_t buffer_size = viz::ResourceSizes::CheckedSizeInBytes<size_t>( + size_, viz::SinglePlaneFormat::kRGBA_8888); + ReadBackFramebuffer( + base::span<uint8_t>(reinterpret_cast<uint8_t*>(bitmap.getPixels()), + buffer_size), + kN32_SkColorType, kPremul_SkAlphaType, kBottomLeft_GrSurfaceOrigin, + kBackBuffer); + auto sk_image = SkImages::RasterFromBitmap(bitmap); // GL Framebuffer is bottom-left origin by default and the @@ -540,23 +552,6 @@ : nullptr; } -void DrawingBuffer::ReadFramebufferIntoBitmapPixels(uint8_t* pixels) { - DCHECK(pixels); - DCHECK(state_restorer_); - bool need_premultiply = requested_alpha_type_ == kUnpremul_SkAlphaType; - WebGLImageConversion::AlphaOp op = - need_premultiply ? WebGLImageConversion::kAlphaDoPremultiply - : WebGLImageConversion::kAlphaDoNothing; - state_restorer_->SetFramebufferBindingDirty(); - gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_); - - // Readback in Skia native byte order (RGBA or BGRA) with kN32_SkColorType. - const size_t buffer_size = viz::ResourceSizes::CheckedSizeInBytes<size_t>( - size_, viz::SinglePlaneFormat::kRGBA_8888); - ReadBackFramebuffer(base::span<uint8_t>(pixels, buffer_size), - kN32_SkColorType, op); -} - scoped_refptr<gpu::ClientSharedImage> DrawingBuffer::ExportSharedImageFromBackBuffer( gpu::SyncToken& sync_token, @@ -1791,8 +1786,29 @@ if (!dst_buffer) return nullptr; + auto pixels = base::span<uint8_t>( + static_cast<uint8_t*>(dst_buffer->writable_data()), dst_buffer->size()); + ReadBackFramebuffer(pixels, color_type, requested_alpha_type_, + kTopLeft_GrSurfaceOrigin, source_buffer); + + return StaticBitmapImage::Create( + std::move(dst_buffer), + SkImageInfo::Make(SkISize::Make(Size().width(), Size().height()), + color_type, kUnpremul_SkAlphaType, + color_space_.ToSkColorSpace())); +} + +void DrawingBuffer::ReadBackFramebuffer(base::span<uint8_t> pixels, + SkColorType color_type, + SkAlphaType destination_alpha_type, + GrSurfaceOrigin destination_origin, + SourceDrawingBuffer source_buffer) { + DCHECK(state_restorer_); + GLuint fbo = 0; + state_restorer_->SetFramebufferBindingDirty(); + // Generate new fbo for front buffer if needed. if (source_buffer == kFrontBuffer && front_color_buffer_) { gl_->GenFramebuffers(1, &fbo); gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo); @@ -1805,34 +1821,6 @@ gl_->BindFramebuffer(GL_FRAMEBUFFER, fbo_); } - auto pixels = base::span<uint8_t>( - static_cast<uint8_t*>(dst_buffer->writable_data()), dst_buffer->size()); - ReadBackFramebuffer(pixels, color_type, - WebGLImageConversion::kAlphaDoNothing); - FlipVertically(pixels, num_rows.ValueOrDie(), row_bytes.ValueOrDie()); - - if (fbo) { - // The front buffer was used as the source of the pixels via |fbo|; clean up - // |fbo| and release access to the front buffer's SharedImage now that the - // readback is finished. - gl_->FramebufferTexture2D( - GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - front_color_buffer_->shared_image->GetTextureTarget(), 0, 0); - gl_->DeleteFramebuffers(1, &fbo); - front_color_buffer_->EndAccess(); - } - - return StaticBitmapImage::Create( - std::move(dst_buffer), - SkImageInfo::Make(SkISize::Make(Size().width(), Size().height()), - color_type, kUnpremul_SkAlphaType, - color_space_.ToSkColorSpace())); -} - -void DrawingBuffer::ReadBackFramebuffer(base::span<uint8_t> pixels, - SkColorType color_type, - WebGLImageConversion::AlphaOp op) { - DCHECK(state_restorer_); state_restorer_->SetPixelPackParametersDirty(); gl_->PixelStorei(GL_PACK_ALIGNMENT, 1); if (webgl_version_ > kWebGL1) { @@ -1846,15 +1834,16 @@ GLenum data_type = GL_UNSIGNED_BYTE; - base::CheckedNumeric<size_t> expected_data_size = 4; - expected_data_size *= Size().width(); - expected_data_size *= Size().height(); - + base::CheckedNumeric<size_t> row_bytes = 4; if (RuntimeEnabledFeatures::WebGLDrawingBufferStorageEnabled() && color_type == kRGBA_F16_SkColorType) { data_type = (webgl_version_ > kWebGL1) ? GL_HALF_FLOAT : GL_HALF_FLOAT_OES; - expected_data_size *= 2; + row_bytes *= 2; } + row_bytes *= Size().width(); + + base::CheckedNumeric<size_t> num_rows = Size().height(); + base::CheckedNumeric<size_t> expected_data_size = num_rows * row_bytes; DCHECK_EQ(expected_data_size.ValueOrDie(), pixels.size()); @@ -1870,6 +1859,16 @@ } } + WebGLImageConversion::AlphaOp op = WebGLImageConversion::kAlphaDoNothing; + if (requested_alpha_type_ == kUnpremul_SkAlphaType && + destination_alpha_type == kPremul_SkAlphaType) { + op = WebGLImageConversion::kAlphaDoPremultiply; + } else if (requested_alpha_type_ != kUnpremul_SkAlphaType && + destination_alpha_type == kUnpremul_SkAlphaType) { + // We don't support unpremultiplication. + NOTREACHED(); + } + if (op == WebGLImageConversion::kAlphaDoPremultiply) { for (size_t i = 0; i < pixels.size(); i += 4) { uint8_t alpha = pixels[i + 3]; @@ -1879,6 +1878,23 @@ } else if (op != WebGLImageConversion::kAlphaDoNothing) { NOTREACHED(); } + + // ReadPixels always reads with bottom-left origin regardless of the + // `opengl_flip_y_extension_` + if (destination_origin != kBottomLeft_GrSurfaceOrigin) { + FlipVertically(pixels, num_rows.ValueOrDie(), row_bytes.ValueOrDie()); + } + + if (fbo) { + // The front buffer was used as the source of the pixels via |fbo|; clean up + // |fbo| and release access to the front buffer's SharedImage now that the + // readback is finished. + gl_->FramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + front_color_buffer_->shared_image->GetTextureTarget(), 0, 0); + gl_->DeleteFramebuffers(1, &fbo); + front_color_buffer_->EndAccess(); + } } void DrawingBuffer::ResolveAndPresentSwapChainIfNeeded() {
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h index 02fa7e6..19dc453 100644 --- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h +++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
@@ -552,8 +552,10 @@ // Helper function which does a readback from the currently-bound // framebuffer into a buffer of a certain size with 4-byte pixels. void ReadBackFramebuffer(base::span<uint8_t> pixels, - SkColorType, - WebGLImageConversion::AlphaOp); + SkColorType color_type, + SkAlphaType destination_alpha_type, + GrSurfaceOrigin destination_origin, + SourceDrawingBuffer source_buffer); // If RGB emulation is required, then the CHROMIUM image's alpha channel // must be immediately cleared after it is bound to a texture. Nothing
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc b/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc index 4df9a75..bfbb5d8a 100644 --- a/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc +++ b/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc
@@ -5,6 +5,7 @@ #include "third_party/blink/renderer/platform/graphics/paint/paint_chunker.h" #include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h" +#include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h" #include "third_party/blink/renderer/platform/graphics/paint/scrollbar_display_item.h" #include "ui/gfx/color_utils.h" @@ -120,8 +121,17 @@ DCHECK(chunk.has_text); } } else if (const auto* scrollbar = DynamicTo<ScrollbarDisplayItem>(item)) { - if (scrollbar->IsOpaque()) + if (scrollbar->IsOpaque()) { chunk.rect_known_to_be_opaque = item.VisualRect(); + } + } else if (const auto* foreign_item = + DynamicTo<ForeignLayerDisplayItem>(item)) { + // Assume all OOP iframes contain text to prevent applying + // 2DScaleTransformWithCompositedDescendants on 2D-transformed ancestors, + // which can cause text blurriness in iframes. + if (foreign_item->GetId().type == DisplayItem::kForeignLayerRemoteFrame) { + chunk.has_text = true; + } } chunk.raster_effect_outset =
diff --git a/third_party/blink/renderer/platform/mojo/heap_mojo_remote_set.h b/third_party/blink/renderer/platform/mojo/heap_mojo_remote_set.h index cd056e80..3a331c4 100644 --- a/third_party/blink/renderer/platform/mojo/heap_mojo_remote_set.h +++ b/third_party/blink/renderer/platform/mojo/heap_mojo_remote_set.h
@@ -79,6 +79,10 @@ Iterator end() { return wrapper_->remote_set().end(); } Iterator end() const { return wrapper_->remote_set().end(); } + Interface* Get(mojo::RemoteSetElementId id) { + return wrapper_->remote_set().Get(id); + } + void Trace(Visitor* visitor) const { visitor->Trace(wrapper_); } private:
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index 6310874..e5526a9 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -4019,6 +4019,10 @@ status: "test", }, { + name: "RequestMainFrameAfterFirstVideoFrame", + status: "experimental", + }, + { name: "ResolveVarStylesOnCopy", status: "stable", }, @@ -4676,6 +4680,10 @@ origin_trial_allows_third_party: true, }, { + name: "StickyUserActivationAcrossSameOriginNavigation", + status: "experimental", + }, + { name: "StorageBuckets", status: "stable", },
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 1fbe956e..c18bfc8 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -1126,14 +1126,12 @@ crbug.com/1076027 external/wpt/css/css-grid/masonry/tentative/intrinsic-sizing/* [ Failure Skip ] crbug.com/1076027 external/wpt/css/css-grid/masonry/tentative/order/masonry-order-001.html [ Failure ] crbug.com/1076027 wpt_internal/css/css-masonry/column-auto-repeat-015.html [ Failure ] -crbug.com/1076027 wpt_internal/css/css-masonry/column-auto-repeat-auto-012.html [ Failure ] crbug.com/1076027 wpt_internal/css/css-masonry/column-explicit-placement-002.html [ Failure ] crbug.com/1076027 wpt_internal/css/css-masonry/row-auto-repeat-003.html [ Failure ] crbug.com/1076027 wpt_internal/css/css-masonry/row-auto-repeat-006.html [ Failure ] crbug.com/1076027 wpt_internal/css/css-masonry/row-auto-repeat-012.html [ Failure ] crbug.com/1076027 wpt_internal/css/css-masonry/row-auto-repeat-014.html [ Failure ] crbug.com/1076027 wpt_internal/css/css-masonry/row-auto-repeat-auto-006.html [ Failure ] -crbug.com/1076027 wpt_internal/css/css-masonry/row-auto-repeat-auto-012.html [ Failure ] # Masonry named line failures crbug.com/1076027 external/wpt/css/css-grid/masonry/tentative/grid-placement/masonry-grid-placement-named-lines-001.html [ Failure ] crbug.com/1076027 external/wpt/css/css-grid/masonry/tentative/grid-placement/masonry-grid-placement-named-lines-002.html [ Failure ] @@ -1152,6 +1150,15 @@ # Failing because of the divergence in default track definition crbug.com/1076027 external/wpt/css/css-grid/masonry/tentative/item-placement/masonry-item-placement-002.html [ Failure ] crbug.com/1076027 external/wpt/css/css-grid/masonry/tentative/item-placement/masonry-item-placement-003.html [ Failure ] +# Failing because computed style for template tracks aren't being expanded in masonry +crbug.com/1076027 wpt_internal/css/css-masonry/column-auto-repeat-019.html [ Failure ] +crbug.com/1076027 wpt_internal/css/css-masonry/column-auto-repeat-020.html [ Failure ] +crbug.com/1076027 wpt_internal/css/css-masonry/column-auto-repeat-024.html [ Failure ] +crbug.com/1076027 wpt_internal/css/css-masonry/column-auto-repeat-025.html [ Failure ] +crbug.com/1076027 wpt_internal/css/css-masonry/row-auto-repeat-017.html [ Failure ] +crbug.com/1076027 wpt_internal/css/css-masonry/row-auto-repeat-018.html [ Failure ] +crbug.com/1076027 wpt_internal/css/css-masonry/row-auto-repeat-022.html [ Failure ] +crbug.com/1076027 wpt_internal/css/css-masonry/row-auto-repeat-023.html [ Failure ] # external/wpt/css/css-fonts/... tests triaged away from the default WPT bug ID crbug.com/1211460 external/wpt/css/css-fonts/alternates-order.html [ Failure ] @@ -3904,13 +3911,6 @@ crbug.com/356955226 external/wpt/fedcm/lfedcm-identity.discovery.tentative.sub.https.html [ Timeout ] crbug.com/356428750 external/wpt/css/css-backgrounds/background-clip/clip-text-animated-text.html [ Failure ] crbug.com/356428750 virtual/threaded/external/wpt/css/css-backgrounds/background-clip/clip-text-animated-text.html [ Failure ] -crbug.com/356436572 external/wpt/css/css-box/margin-trim/block-container-block-end-collapsed-margins.html [ Failure ] -crbug.com/356436572 external/wpt/css/css-box/margin-trim/block-container-block-end-self-collapsing-item-has-larger-block-end.html [ Failure ] -crbug.com/356436572 external/wpt/css/css-box/margin-trim/block-container-block-end-self-collapsing-item-has-larger-block-start.html [ Failure ] -crbug.com/356436572 external/wpt/css/css-box/margin-trim/block-container-block-start-collapsed-margins.html [ Failure ] -crbug.com/356436572 external/wpt/css/css-box/margin-trim/block-container-block-start-self-collapsing-item-has-larger-block-end.html [ Failure ] -crbug.com/356436572 external/wpt/css/css-box/margin-trim/block-container-block-start-self-collapsing-item-larger-block-start.html [ Failure ] -crbug.com/356436572 [ Win11-arm64 ] external/wpt/css/css-box/margin-trim/computed-margin-values/grid-inline-end-items-in-last-column-trimmed.html [ Failure Timeout ] crbug.com/356436618 [ Mac12 ] virtual/threaded/external/wpt/css/css-backgrounds/border-image-slice-005.htm [ Failure ] crbug.com/355683658 external/wpt/css/css-align/self-alignment/block-justify-self.html [ Failure ] crbug.com/353583104 [ Win11-arm64 ] external/wpt/html/semantics/embedded-content/media-elements/preserves-pitch.html [ Timeout ] @@ -4250,12 +4250,6 @@ # TODO(crbug.com/1476931): Deflake and re-enable. crbug.com/626703 [ Linux ] external/wpt/mediacapture-record/MediaRecorder-mimetype.html [ Timeout ] crbug.com/626703 external/wpt/svg/text/reftests/opacity.svg [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-column-style-change-triggers-layout-block-end.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-column-style-change-triggers-layout-block-start.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-column-style-change-triggers-layout-block.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-row-style-change-triggers-layout-inline-end.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-row-style-change-triggers-layout-inline-start.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-row-style-change-triggers-layout-inline.html [ Failure ] crbug.com/626703 external/wpt/css/css-transitions/transition-events-with-document-change.html [ Timeout ] crbug.com/626703 external/wpt/css/css-counter-styles/japanese-formal/counter-japanese-formal.html [ Failure ] crbug.com/626703 external/wpt/css/css-counter-styles/japanese-informal/counter-japanese-informal.html [ Failure ] @@ -4276,31 +4270,6 @@ crbug.com/626703 external/wpt/css/css-text/white-space/white-space-vs-joiners-002.html [ Failure ] crbug.com/626703 [ Linux ] virtual/fenced-frame-mparch/external/wpt/fenced-frame/background-fetch.https.html [ Timeout ] crbug.com/626703 [ Mac12 ] virtual/fenced-frame-mparch/external/wpt/fenced-frame/background-fetch.https.html [ Timeout ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/block-container-non-adjoining-item.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-block-end-trimmed-only.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-block-start-trimmed-only.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-block-trimmed-only.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-column-block-multiline.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-column-grow.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-column-inline-multiline.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-column-orthogonal-item.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-column-shrink.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-inline-end-trimmed-only.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-inline-start-trimmed-only.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-inline-trimmed-only.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-row-block-multiline.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-row-grow.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-row-inline-multiline.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-row-orthogonal-item.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-row-shrink.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/flex-trim-all-margins.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/grid-block-end.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/grid-block-start.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/grid-block.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/grid-inline-end.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/grid-inline-start.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/grid-inline.html [ Failure ] -crbug.com/626703 external/wpt/css/css-box/margin-trim/grid-trim-ignores-collapsed-tracks.html [ Failure ] crbug.com/626703 external/wpt/css/css-transforms/animation/translate-animation-on-svg.html [ Failure ] crbug.com/626703 virtual/backface-visibility-interop/external/wpt/css/css-transforms/animation/translate-animation-on-svg.html [ Failure ] crbug.com/626703 [ Win10.20h2 ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/dedicated-worker/mediasource-worker-detach-element.html [ Timeout ] @@ -4623,15 +4592,53 @@ crbug.com/1405830 external/wpt/css/css-text/text-group-align/text-group-align-start.html [ Failure ] # Implement margin-trim -crbug.com/1405835 external/wpt/css/css-box/margin-trim/block-container-block-001.html [ Failure ] -crbug.com/1405835 external/wpt/css/css-box/margin-trim/block-container-block-002.html [ Failure ] -crbug.com/1405835 external/wpt/css/css-box/margin-trim/block-container-block-end-001.html [ Failure ] -crbug.com/1405835 external/wpt/css/css-box/margin-trim/block-container-block-end-002.html [ Failure ] -crbug.com/1405835 external/wpt/css/css-box/margin-trim/block-container-block-start-001.html [ Failure ] -crbug.com/1405835 external/wpt/css/css-box/margin-trim/block-container-block-start-002.html [ Failure ] -crbug.com/1405835 external/wpt/css/css-box/margin-trim/block-container-replaced-block-end.html [ Failure ] -crbug.com/1405835 external/wpt/css/css-box/margin-trim/block-container-replaced-block-start.html [ Failure ] -crbug.com/1405835 external/wpt/css/css-box/margin-trim/block-container-replaced-block.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-block-001.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-block-002.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-block-end-001.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-block-end-002.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-block-end-collapsed-margins.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-block-end-self-collapsing-item-has-larger-block-end.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-block-end-self-collapsing-item-has-larger-block-start.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-block-start-001.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-block-start-002.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-block-start-collapsed-margins.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-block-start-self-collapsing-item-has-larger-block-end.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-block-start-self-collapsing-item-larger-block-start.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-non-adjoining-item.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-replaced-block-end.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-replaced-block-start.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/block-container-replaced-block.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/computed-margin-values/grid-inline-end-items-in-last-column-trimmed.html [ Failure Timeout ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-block-end-trimmed-only.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-block-start-trimmed-only.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-block-trimmed-only.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-column-block-multiline.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-column-grow.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-column-inline-multiline.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-column-orthogonal-item.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-column-shrink.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-column-style-change-triggers-layout-block-end.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-column-style-change-triggers-layout-block-start.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-column-style-change-triggers-layout-block.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-inline-end-trimmed-only.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-inline-start-trimmed-only.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-inline-trimmed-only.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-row-block-multiline.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-row-grow.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-row-inline-multiline.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-row-orthogonal-item.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-row-shrink.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-row-style-change-triggers-layout-inline-end.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-row-style-change-triggers-layout-inline-start.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-row-style-change-triggers-layout-inline.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/flex-trim-all-margins.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/grid-block-end.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/grid-block-start.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/grid-block.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/grid-inline-end.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/grid-inline-start.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/grid-inline.html [ Failure ] +crbug.com/40886857 external/wpt/css/css-box/margin-trim/grid-trim-ignores-collapsed-tracks.html [ Failure ] crbug.com/1367912 external/wpt/css/css-break/flexbox/multi-line-row-flex-fragmentation-058.html [ Failure ] crbug.com/1367912 external/wpt/css/css-break/flexbox/single-line-column-flex-fragmentation-043.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/transform-007.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/transform-007.tentative.html new file mode 100644 index 0000000..77216bfe2 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/transform-007.tentative.html
@@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html class="reftest-wait"> + <title>Anchor with changing transform</title> + <link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> + <link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#determining-position"> + <style> + .anchored { + position: absolute; + position-anchor: --a; + background: green; + } + #anchor { + width: 20px; + height: 20px; + margin-left: 20px; + transform: scale(3); + transform-origin: top left; + anchor-name: --a; + background: green; + } + </style> + <link rel="match" href="../reference/ref-filled-green-100px-square.xht"> + <p>Test passes if there is a filled green square and <strong>no red</strong>.</p> + <div style="position:relative; width:100px; height:100px; background:red;"> + <div style="height:30px;"></div> + <div id="anchor"></div> + <div class="anchored" style="position-area:left span-all; width:100%; height:100px;"></div> + <div class="anchored" style="position-area:right span-all; width:40px; height:100%;"></div> + <div class="anchored" style="position-area:top center; width:40px; height:100%;"></div> + <div class="anchored" style="position-area:bottom center; width:100%; height:100%;"></div> + </div> + <script> + requestAnimationFrame(()=> { + requestAnimationFrame(()=> { + anchor.style.transform = "scale(2)"; + document.documentElement.classList.remove("reftest-wait"); + }); + }); + </script> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/transform-008.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/transform-008.tentative.html new file mode 100644 index 0000000..ffbedaab --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/transform-008.tentative.html
@@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html class="reftest-wait"> + <title>Ancestor of anchor with changing transform</title> + <link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> + <link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#determining-position"> + <style> + .anchored { + position: absolute; + position-anchor: --a; + background: green; + } + #anchor { + width: 20px; + height: 20px; + margin-left: 10px; + anchor-name: --a; + background: green; + } + #transformed { + transform: scale(3); + transform-origin: top left; + } + </style> + <link rel="match" href="../reference/ref-filled-green-100px-square.xht"> + <p>Test passes if there is a filled green square and <strong>no red</strong>.</p> + <div style="position:relative; width:100px; height:100px; background:red;"> + <div style="height:30px;"></div> + <div id="transformed"> + <div id="anchor"></div> + </div> + <div class="anchored" style="position-area:left span-all; width:100%; height:100px;"></div> + <div class="anchored" style="position-area:right span-all; width:40px; height:100%;"></div> + <div class="anchored" style="position-area:top center; width:40px; height:100%;"></div> + <div class="anchored" style="position-area:bottom center; width:100%; height:100%;"></div> + </div> + <script> + requestAnimationFrame(()=> { + requestAnimationFrame(()=> { + transformed.style.transform = "scale(2)"; + document.documentElement.classList.remove("reftest-wait"); + }); + }); + </script> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/fedcm/fedcm-disconnect.sub.https.html b/third_party/blink/web_tests/external/wpt/fedcm/fedcm-disconnect.sub.https.html index 04fcd272..a80ed48 100644 --- a/third_party/blink/web_tests/external/wpt/fedcm/fedcm-disconnect.sub.https.html +++ b/third_party/blink/web_tests/external/wpt/fedcm/fedcm-disconnect.sub.https.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <title>Federated Credential Management API disconnect() tests.</title> +<meta name="timeout" content="long"> <link rel="help" href="https://fedidcg.github.io/FedCM"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script>
diff --git a/third_party/blink/web_tests/http/tests/html/composited-descendant-scaled-oopif-with-text-expected.html b/third_party/blink/web_tests/http/tests/html/composited-descendant-scaled-oopif-with-text-expected.html new file mode 100644 index 0000000..6a2fb86 --- /dev/null +++ b/third_party/blink/web_tests/http/tests/html/composited-descendant-scaled-oopif-with-text-expected.html
@@ -0,0 +1,19 @@ +<!DOCTYPE html> +<head> + <style> + * { + margin: 0; + } + </style> +</head> +<html> + <body> + <h1>Composited 2D scaled transform with descended iframe.</h1> + This test verifies that the text within the OOP iframe is not blurry. + <div style="transform:scale(1.8); transform-origin: 0 0; height: 50px; width: 150px; background-color:pink"> + <div style="border: none; height: 50px; width: 150px"> + This is some text + </div> + </div> + </body> +</html>
diff --git a/third_party/blink/web_tests/http/tests/html/composited-descendant-scaled-oopif-with-text.html b/third_party/blink/web_tests/http/tests/html/composited-descendant-scaled-oopif-with-text.html new file mode 100644 index 0000000..4143d26 --- /dev/null +++ b/third_party/blink/web_tests/http/tests/html/composited-descendant-scaled-oopif-with-text.html
@@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<head> + <style> + * { + margin: 0; + } + </style> +</head> + <link rel=match href="composited-descendant-scaled-oopif-with-text-expected.html"> + <body> + <h1>Composited 2D scaled transform with descended iframe.</h1> + This test verifies that the text within the OOP iframe is not blurry. + <div style="transform:scale(1.8); transform-origin: 0 0; height: 50px; width: 150px; background-color:pink"> + <!-- Use of localhost below will make the iframe an OOPIF --> + <iframe src="http://localhost:8080/html/resources/scaled-oopif-text-no-margin.html" + style="border: none; height: 50px; width: 150px;" + onload="document.documentElement.className = ''"></iframe> + </div> + </body> +</html>
diff --git a/third_party/blink/web_tests/http/tests/html/resources/scaled-oopif-text-no-margin.html b/third_party/blink/web_tests/http/tests/html/resources/scaled-oopif-text-no-margin.html new file mode 100644 index 0000000..4975abe --- /dev/null +++ b/third_party/blink/web_tests/http/tests/html/resources/scaled-oopif-text-no-margin.html
@@ -0,0 +1,9 @@ +<!DOCTYPE html> +<head> + <style> + * { + margin: 0; + } + </style> +</head> +This is some text
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-017-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-017-ref.html new file mode 100644 index 0000000..d691a26b --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-017-ref.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> +<style> +.grid { + display: grid; + grid-template-columns: 100px 100px; + grid-template-rows: 100px 100px 100px; + background: gray; + height: 300px; + width: 300px +} +</style> +<body> + <div class="grid"> + <div style="background: lightskyblue;"> + Number 1 + </div> + <div style="background: lightcoral;"> + Number 2 + </div> + <div style="background: lightgreen;"> + Number 3 + </div> + <div style="background: lightpink;"> + Number 4 + </div> + <div style="background: orange;"> + Number 5 + </div> + </div> +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-017.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-017.html new file mode 100644 index 0000000..07fb075a --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-017.html
@@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and explicit item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="match" href="column-auto-repeat-017-ref.html"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<style> +.masonry { + display: masonry; + background: gray; + item-tolerance: 0; + grid-template-columns: repeat(auto-fit, 100px); + height: 300px; + width: 300px; +} + +.masonry > div { + width: 100%; + height: 100px; +} +</style> +<body> + <div class="masonry"> + <div style="background: lightskyblue; grid-column: 1;"> + Number 1 + </div> + <div style="background: lightcoral; grid-column: 3;"> + Number 2 + </div> + <div style="background: lightgreen; grid-column: 1;"> + Number 3 + </div> + <div style="background: lightpink; grid-column: 3;"> + Number 4 + </div> + <div style="background: orange; grid-column: 1;"> + Number 5 + </div> + </div> +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-018-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-018-ref.html new file mode 100644 index 0000000..c2a974b8 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-018-ref.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> +<style> +.grid { + display: grid; + grid-template-columns: 100px 100px 100px; + grid-template-rows: 100px 100px; + background: gray; + height: 200px; + width: 500px +} +</style> +<body> + <div class="grid"> + <div style="background: lightskyblue;"> + Number 1 + </div> + <div style="background: lightcoral;"> + Number 2 + </div> + <div style="background: lightgreen;"> + Number 3 + </div> + <div style="background: lightpink;"> + Number 4 + </div> + <div style="background: orange;"> + Number 5 + </div> + </div> +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-018.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-018.html new file mode 100644 index 0000000..14c62eb --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-018.html
@@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and explicit item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="match" href="column-auto-repeat-018-ref.html"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<style> +.masonry { + display: masonry; + background: gray; + item-tolerance: 0; + grid-template-columns: repeat(auto-fit, 100px); + height: 200px; + width: 500px; +} + +.masonry > div { + width: 100%; + height: 100px; +} +</style> +<body> + <div class="masonry"> + <div style="background: lightskyblue; grid-column: 1;"> + Number 1 + </div> + <div style="background: lightcoral; grid-column: 3;"> + Number 2 + </div> + <div style="background: lightgreen; grid-column: 5;"> + Number 3 + </div> + <div style="background: lightpink; grid-column: 1;"> + Number 4 + </div> + <div style="background: orange; grid-column: 3;"> + Number 5 + </div> + </div> +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-019.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-019.html new file mode 100644 index 0000000..91e0c3e5 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-019.html
@@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and auto item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +.masonry { + display: masonry; + item-tolerance: 0; + grid-template-columns: repeat(auto-fit, 100px); + height: 200px; + width: 1000px; +} + +.masonry > div { + width: 100%; + height: 100px; +} +</style> +<div class="masonry"> + <div style="grid-column: 1;"></div> + <div style="grid-column: 3;"></div> + <div></div> + <div></div> + <div></div> +</div> +<script> + test(function() { + const container = document.querySelector('.masonry'); + const computedStyle = window.getComputedStyle(container); + assert_equals(computedStyle.getPropertyValue('grid-template-columns'), + "100px 100px 100px 100px 100px 0px 0px 0px 0px 0px"); + }); +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-020.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-020.html new file mode 100644 index 0000000..ed496ed --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-020.html
@@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and auto/explicit item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +.masonry { + display: masonry; + item-tolerance: 0; + grid-template-columns: repeat(auto-fit, 100px); + height: 200px; + width: 1000px; +} + +.masonry > div { + width: 100%; + height: 100px; +} +</style> +<div class="masonry"> + <div></div> + <div></div> + <div style="grid-column: 4"></div> + <div style="grid-column: 6"></div> + <div></div> +</div> +<script> + test(function() { + const container = document.querySelector('.masonry'); + const computedStyle = window.getComputedStyle(container); + assert_equals(computedStyle.getPropertyValue('grid-template-columns'), + "100px 100px 100px 100px 0px 100px 0px 0px 0px 0px"); + }); +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-021-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-021-ref.html new file mode 100644 index 0000000..df8e67b27 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-021-ref.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html> +<style> +.grid { + display: grid; + grid-template-columns: repeat(auto-fit, 50px); + height: 200px; + width: 500px; + gap: 10px; +} + +.grid > div { + width: 100%; + height: 100px; + background-color: orange; +} +</style> +<div class="grid"> + <div style="grid-column: span 4;">1</div> + <div style="grid-column: 4 / span 2;">2</div> + <div style="grid-column: 9 / span 2; grid-row: 1;">3</div> + <div style="grid-column: 7; grid-row: 1;">4</div> + <div style="grid-column: 9 / span 2;">5</div> + <div style="grid-column: 6; grid-row: 1;">6</div> +</div> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-021.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-021.html new file mode 100644 index 0000000..24bb78e --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-021.html
@@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and auto/explicit spanning item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<link rel="match" href="column-auto-repeat-021-ref.html"> +<style> +.masonry { + display: masonry; + item-tolerance: 0; + grid-template-columns: repeat(auto-fit, 50px); + height: 200px; + width: 500px; + gap: 10px; +} + +.masonry > div { + width: 100%; + height: 100px; + background-color: orange; +} +</style> +<div class="masonry"> + <div style="grid-column: span 4;">1</div> + <div style="grid-column: 4 / span 2;">2</div> + <div style="grid-column: 9 / span 2;">3</div> + <div style="grid-column: 7;">4</div> + <div style="grid-column: span 2;">5</div> + <div>6</div> +</div> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-022.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-022.html new file mode 100644 index 0000000..081f1a6 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-022.html
@@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and auto/explicit spanning item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<link rel="match" href="column-auto-repeat-021-ref.html"> +<style> +.masonry { + display: masonry; + item-tolerance: 0; + grid-template-columns: 50px repeat(5, 50px) repeat(auto-fit, 50px); + height: 200px; + width: 500px; + gap: 10px; +} + +.masonry > div { + width: 100%; + height: 100px; + background-color: orange; +} +</style> +<div class="masonry"> + <div style="grid-column: span 4;">1</div> + <div style="grid-column: 4 / span 2;">2</div> + <div style="grid-column: 9 / span 2;">3</div> + <div style="grid-column: 7;">4</div> + <div style="grid-column: span 2;">5</div> + <div>6</div> +</div> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-023-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-023-ref.html new file mode 100644 index 0000000..04d8e26 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-023-ref.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> +<style> +.grid { + display: grid; + grid-template-columns: 100px 100px; + grid-template-rows: 100px 100px 100px; + background: gray; + height: 500px; + width: 300px +} +</style> +<body> + <div class="grid"> + <div style="background: lightskyblue; grid-column: 2;"> + Number 1 + </div> + <div style="background: lightcoral; grid-column: 2;"> + Number 2 + </div> + <div style="background: lightgreen; grid-column: 2;"> + Number 3 + </div> + <div style="background: lightpink; grid-column: 2;"> + Number 4 + </div> + <div style="background: orange; grid-column: 2;"> + Number 5 + </div> + </div> +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-023.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-023.html new file mode 100644 index 0000000..979fc21 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-023.html
@@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and explicit item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="match" href="column-auto-repeat-023-ref.html"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<style> +.masonry { + display: masonry; + background: gray; + item-tolerance: 0; + grid-template-columns: 100px repeat(auto-fit, 100px); + height: 500px; + width: 300px; +} + +.masonry > div { + width: 100%; + height: 100px; +} +</style> +<body> + <div class="masonry"> + <!-- Only column 2 should get collapsed --> + <div style="background: lightskyblue; grid-column: 3;"> + Number 1 + </div> + <div style="background: lightcoral; grid-column: 3;"> + Number 2 + </div> + <div style="background: lightgreen; grid-column: 3;"> + Number 3 + </div> + <div style="background: lightpink; grid-column: 3;"> + Number 4 + </div> + <div style="background: orange; grid-column: 3;"> + Number 5 + </div> + </div> +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-024.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-024.html new file mode 100644 index 0000000..4d9496a --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-024.html
@@ -0,0 +1,38 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and auto item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +.masonry { + display: masonry; + item-tolerance: 0; + grid-template-columns: repeat(5, 100px) repeat(auto-fit, 100px); + height: 200px; + width: 1000px; +} + +.masonry > div { + width: 100%; + height: 100px; +} +</style> +<div class="masonry"> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> +</div> +<script> + test(function() { + const container = document.querySelector('.masonry'); + const computedStyle = window.getComputedStyle(container); + assert_equals(computedStyle.getPropertyValue('grid-template-columns'), + "100px 100px 100px 100px 100px 100px 0px 0px 0px 0px"); + }); +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-025.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-025.html new file mode 100644 index 0000000..76fc6a9df --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-025.html
@@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and auto item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +.masonry { + display: masonry; + item-tolerance: 0; + grid-template-columns: repeat(auto-fit, 100px) repeat(5, 100px); + height: 200px; + width: 1000px; +} + +.masonry > div { + width: 100%; + height: 100px; +} +</style> +<div class="masonry"> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> +</div> +<script> + test(function() { + const container = document.querySelector('.masonry'); + const computedStyle = window.getComputedStyle(container); + assert_equals(computedStyle.getPropertyValue('grid-template-columns'), + "100px 100px 100px 100px 100px 100px 100px 100px 100px 100px"); + }); +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-026-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-026-ref.html new file mode 100644 index 0000000..d491afc --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-026-ref.html
@@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> +<style> +.grid { + display: grid; + grid-template-columns: repeat(8, 50px); + height: 200px; + width: 500px; + gap: 10px; +} + +.grid > div { + width: 100%; + height: 100px; + background-color: orange; +} +</style> +<div class="grid"> + <div style="grid-column: span 3;"></div> + <div style="grid-column: -1;"></div> +</div> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-026.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-026.html new file mode 100644 index 0000000..a8bb0f55 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/column-auto-repeat-026.html
@@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and auto/explicit spanning item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<link rel="match" href="column-auto-repeat-026-ref.html"> +<style> +.masonry { + display: masonry; + item-tolerance: 0; + grid-template-columns: repeat(4, 50px) repeat(auto-fit, 50px) repeat(4, 50px); + height: 200px; + width: 500px; + gap: 10px; +} + +.masonry > div { + width: 100%; + height: 100px; + background-color: orange; +} +</style> +<div class="masonry"> + <div style="grid-column: span 3;"></div> + <div style="grid-column: -1;"></div> +</div> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-015-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-015-ref.html new file mode 100644 index 0000000..4fa74f2 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-015-ref.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> +<style> +.grid { + display: grid; + grid-template-rows: 100px 100px; + grid-template-columns: 100px 100px 100px; + background: gray; + height: 300px; + width: 300px +} +</style> +<body> + <div class="grid"> + <div style="background: lightskyblue; grid-row: 1;"> + Number 1 + </div> + <div style="background: lightcoral; grid-row: 2;"> + Number 2 + </div> + <div style="background: lightgreen; grid-row: 1;"> + Number 3 + </div> + <div style="background: lightpink; grid-row: 2;"> + Number 4 + </div> + <div style="background: orange; grid-row: 1;"> + Number 5 + </div> + </div> +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-015.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-015.html new file mode 100644 index 0000000..8a21f4f --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-015.html
@@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and explicit item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="match" href="row-auto-repeat-015-ref.html"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<style> +.masonry { + display: masonry; + background: gray; + item-tolerance: 0; + masonry-direction: row; + grid-template-rows: repeat(auto-fit, 100px); + height: 300px; + width: 300px; +} + +.masonry > div { + height: 100%; + width: 100px; +} +</style> +<body> + <div class="masonry"> + <div style="background: lightskyblue; grid-row: 1;"> + Number 1 + </div> + <div style="background: lightcoral; grid-row: 3;"> + Number 2 + </div> + <div style="background: lightgreen; grid-row: 1;"> + Number 3 + </div> + <div style="background: lightpink; grid-row: 3;"> + Number 4 + </div> + <div style="background: orange; grid-row: 1;"> + Number 5 + </div> + </div> +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-016-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-016-ref.html new file mode 100644 index 0000000..6d74025 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-016-ref.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> +<style> +.grid { + display: grid; + grid-template-rows: 100px 100px 100px; + grid-template-columns: 100px 100px; + background: gray; + width: 200px; + height: 500px +} +</style> +<body> + <div class="grid"> + <div style="background: lightskyblue;"> + Number 1 + </div> + <div style="background: lightcoral; grid-column: 1;"> + Number 2 + </div> + <div style="background: lightgreen; grid-column: 1;"> + Number 3 + </div> + <div style="background: lightpink; grid-row: 1; grid-column: 2;"> + Number 4 + </div> + <div style="background: orange; grid-row: 2; grid-column: 2;"> + Number 5 + </div> + </div> +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-016.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-016.html new file mode 100644 index 0000000..0313ec5 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-016.html
@@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and explicit item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="match" href="row-auto-repeat-016-ref.html"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<style> +.masonry { + display: masonry; + background: gray; + item-tolerance: 0; + masonry-direction: row; + grid-template-rows: repeat(auto-fit, 100px); + width: 200px; + height: 500px; +} + +.masonry > div { + height: 100%; + width: 100px; +} +</style> +<body> + <div class="masonry"> + <div style="background: lightskyblue; grid-row: 1;"> + Number 1 + </div> + <div style="background: lightcoral; grid-row: 3;"> + Number 2 + </div> + <div style="background: lightgreen; grid-row: 5;"> + Number 3 + </div> + <div style="background: lightpink; grid-row: 1;"> + Number 4 + </div> + <div style="background: orange; grid-row: 3;"> + Number 5 + </div> + </div> +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-017.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-017.html new file mode 100644 index 0000000..7322624 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-017.html
@@ -0,0 +1,38 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and auto item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +.masonry { + display: masonry; + item-tolerance: 0; + masonry-direction: row; + grid-template-rows: repeat(auto-fit, 100px); + width: 200px; + height: 1000px; +} + +.masonry > div { + height: 100%; + width: 100px; +} +</style> +<div class="masonry"> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> +</div> +<script> + test(function() { + const container = document.querySelector('.masonry'); + const computedStyle = window.getComputedStyle(container); + assert_equals(computedStyle.getPropertyValue('grid-template-rows'), + "100px 100px 100px 100px 100px 0px 0px 0px 0px 0px"); + }); +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-018.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-018.html new file mode 100644 index 0000000..e153e2d --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-018.html
@@ -0,0 +1,38 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and auto/explicit item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +.masonry { + display: masonry; + item-tolerance: 0; + masonry-direction: row; + grid-template-rows: repeat(auto-fit, 100px); + width: 200px; + height: 1000px; +} + +.masonry > div { + height: 100%; + width: 100px; +} +</style> +<div class="masonry"> + <div></div> + <div></div> + <div style="grid-row: 4"></div> + <div style="grid-row: 6"></div> + <div></div> +</div> +<script> + test(function() { + const container = document.querySelector('.masonry'); + const computedStyle = window.getComputedStyle(container); + assert_equals(computedStyle.getPropertyValue('grid-template-rows'), + "100px 100px 100px 100px 0px 100px 0px 0px 0px 0px"); + }); +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-019-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-019-ref.html new file mode 100644 index 0000000..0384945 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-019-ref.html
@@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> +<style> +.grid { + display: grid; + grid-template-rows: repeat(auto-fit, 50px); + grid-template-columns: 50px 50px; + width: 200px; + height: 500px; + gap: 10px; +} + +.grid > div { + height: 100%; + width: 50px; + background-color: orange; +} +</style> +<div class="grid"> + <div style="grid-row: 1 / span 4;">1</div> + <div style="grid-row: 4 / span 2;">2</div> + <div style="grid-row: 9 / span 2; grid-column: 1;">3</div> + <div style="grid-row: 7; grid-column: 1;">4</div> + <div style="grid-row: 9 / span 2;">5</div> + <div style="grid-row: 6; grid-column: 1;">6</div> +</div> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-019.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-019.html new file mode 100644 index 0000000..a609db60 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-019.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and auto/explicit spanning item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<link rel="match" href="row-auto-repeat-019-ref.html"> +<style> +.masonry { + display: masonry; + item-tolerance: 0; + masonry-direction: row; + grid-template-rows: repeat(auto-fit, 50px); + width: 200px; + height: 500px; + gap: 10px; +} + +.masonry > div { + height: 100%; + width: 50px; + background-color: orange; +} +</style> +<div class="masonry"> + <div style="grid-row: span 4;">1</div> + <div style="grid-row: 4 / span 2;">2</div> + <div style="grid-row: 9 / span 2;">3</div> + <div style="grid-row: 7;">4</div> + <div style="grid-row: span 2;">5</div> + <div>6</div> +</div> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-020.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-020.html new file mode 100644 index 0000000..d6ad238 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-020.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and auto/explicit spanning item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<link rel="match" href="row-auto-repeat-019-ref.html"> +<style> +.masonry { + display: masonry; + item-tolerance: 0; + masonry-direction: row; + grid-template-rows: 50px repeat(5, 50px) repeat(auto-fit, 50px); + width: 200px; + height: 500px; + gap: 10px; +} + +.masonry > div { + height: 100%; + width: 50px; + background-color: orange; +} +</style> +<div class="masonry"> + <div style="grid-row: span 4;">1</div> + <div style="grid-row: 4 / span 2;">2</div> + <div style="grid-row: 9 / span 2;">3</div> + <div style="grid-row: 7;">4</div> + <div style="grid-row: span 2;">5</div> + <div>6</div> +</div> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-021-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-021-ref.html new file mode 100644 index 0000000..e212b16 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-021-ref.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> +<style> +.grid { + display: grid; + grid-template-rows: 100px 100px; + grid-template-columns: 100px 100px 100px; + background: gray; + width: 500px; + height: 300px +} +</style> +<body> + <div class="grid"> + <div style="background: lightskyblue; grid-row: 2;"> + Number 1 + </div> + <div style="background: lightcoral; grid-row: 2;"> + Number 2 + </div> + <div style="background: lightgreen; grid-row: 2;"> + Number 3 + </div> + <div style="background: lightpink; grid-row: 2;"> + Number 4 + </div> + <div style="background: orange; grid-row: 2;"> + Number 5 + </div> + </div> +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-021.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-021.html new file mode 100644 index 0000000..8c0eadf --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-021.html
@@ -0,0 +1,43 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and explicit item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="match" href="row-auto-repeat-021-ref.html"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<style> +.masonry { + display: masonry; + background: gray; + item-tolerance: 0; + masonry-direction: row; + grid-template-rows: 100px repeat(auto-fit, 100px); + width: 500px; + height: 300px; +} + +.masonry > div { + height: 100%; + width: 100px; +} +</style> +<body> + <div class="masonry"> + <!-- Only row 2 should get collapsed --> + <div style="background: lightskyblue; grid-row: 3;"> + Number 1 + </div> + <div style="background: lightcoral; grid-row: 3;"> + Number 2 + </div> + <div style="background: lightgreen; grid-row: 3;"> + Number 3 + </div> + <div style="background: lightpink; grid-row: 3;"> + Number 4 + </div> + <div style="background: orange; grid-row: 3;"> + Number 5 + </div> + </div> +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-022.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-022.html new file mode 100644 index 0000000..8fde652 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-022.html
@@ -0,0 +1,39 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and auto item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +.masonry { + display: masonry; + item-tolerance: 0; + masonry-direction: row; + grid-template-rows: repeat(5, 100px) repeat(auto-fit, 100px); + width: 200px; + height: 1000px; +} + +.masonry > div { + height: 100%; + width: 100px; +} +</style> +<div class="masonry"> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> +</div> +<script> + test(function() { + const container = document.querySelector('.masonry'); + const computedStyle = window.getComputedStyle(container); + assert_equals(computedStyle.getPropertyValue('grid-template-rows'), + "100px 100px 100px 100px 100px 100px 0px 0px 0px 0px"); + }); +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-023.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-023.html new file mode 100644 index 0000000..e904b5d --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-023.html
@@ -0,0 +1,38 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and auto item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +.masonry { + display: masonry; + item-tolerance: 0; + masonry-direction: row; + grid-template-rows: repeat(auto-fit, 100px) repeat(5, 100px); + width: 200px; + height: 1000px; +} + +.masonry > div { + height: 100%; + width: 100px; +} +</style> +<div class="masonry"> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> +</div> +<script> + test(function() { + const container = document.querySelector('.masonry'); + const computedStyle = window.getComputedStyle(container); + assert_equals(computedStyle.getPropertyValue('grid-template-rows'), + "100px 100px 100px 100px 100px 100px 100px 100px 100px 100px"); + }); +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-024-ref.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-024-ref.html new file mode 100644 index 0000000..1cfb614 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-024-ref.html
@@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> +<style> +.grid { + display: grid; + grid-template-rows: repeat(8, 50px); + width: 200px; + height: 500px; + gap: 10px; +} + +.grid > div { + height: 100%; + width: 100px; + background-color: orange; +} +</style> +<div class="grid"> + <div style="grid-row: span 3;"></div> + <div style="grid-row: -1;"></div> +</div> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-024.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-024.html new file mode 100644 index 0000000..f65d957 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/row-auto-repeat-024.html
@@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html> +<title>Auto-fit repeat tracks with fixed size and auto/explicit spanning item placement</title> +<link rel="help" href="https://drafts.csswg.org/css-grid-3"> +<link rel="author" title="Alison Maher" href="mailto:almaher@microsoft.com"> +<link rel="match" href="row-auto-repeat-024-ref.html"> +<style> +.masonry { + display: masonry; + item-tolerance: 0; + masonry-direction: row; + grid-template-rows: repeat(4, 50px) repeat(auto-fit, 50px) repeat(4, 50px); + width: 200px; + height: 500px; + gap: 10px; +} + +.masonry > div { + height: 100%; + width: 100px; + background-color: orange; +} +</style> +<div class="masonry"> + <div style="grid-row: span 3;"></div> + <div style="grid-row: -1;"></div> +</div> +</html>
diff --git a/third_party/boringssl/src b/third_party/boringssl/src index 1fecca9..5903cfaf 160000 --- a/third_party/boringssl/src +++ b/third_party/boringssl/src
@@ -1 +1 @@ -Subproject commit 1fecca988bd10687c9f2b6b9e6328f03f526e8d7 +Subproject commit 5903cfafaf9cd4f1ef436b6e5717ba511f096e83
diff --git a/third_party/crossbench-web-tests b/third_party/crossbench-web-tests new file mode 160000 index 0000000..3c76c82 --- /dev/null +++ b/third_party/crossbench-web-tests
@@ -0,0 +1 @@ +Subproject commit 3c76c8201f0732fe9781742229ab8ac43bf90cbf
diff --git a/third_party/depot_tools b/third_party/depot_tools index 435e3b3..1b44e27 160000 --- a/third_party/depot_tools +++ b/third_party/depot_tools
@@ -1 +1 @@ -Subproject commit 435e3b303e996904365d5e96579bfc793e113482 +Subproject commit 1b44e27e7d4c63dfe83d10ed01d23824536e9811
diff --git a/third_party/inspector_protocol/crdtp/chromium/protocol_traits.h b/third_party/inspector_protocol/crdtp/chromium/protocol_traits.h index fb6be45..69a824ac 100644 --- a/third_party/inspector_protocol/crdtp/chromium/protocol_traits.h +++ b/third_party/inspector_protocol/crdtp/chromium/protocol_traits.h
@@ -15,10 +15,6 @@ #include "third_party/inspector_protocol/crdtp/protocol_core.h" #include "third_party/inspector_protocol/crdtp/serializable.h" -namespace base { -class Value; -} - namespace crdtp { class Serializable;
diff --git a/third_party/libc++/src b/third_party/libc++/src index be9dd89..c105b13 160000 --- a/third_party/libc++/src +++ b/third_party/libc++/src
@@ -1 +1 @@ -Subproject commit be9dd89ffbfdf2b6cf6b9c5ec076d6adbc5d25a6 +Subproject commit c105b13e377d3fa5fdf033957f31abc1894a9968
diff --git a/third_party/pdfium b/third_party/pdfium index 7f80ad7..864375a 160000 --- a/third_party/pdfium +++ b/third_party/pdfium
@@ -1 +1 @@ -Subproject commit 7f80ad7406e307600914d4ba6ac429cb556df9a3 +Subproject commit 864375abbcd387f3d3153ce810cc8fd9c395a15e
diff --git a/third_party/skia b/third_party/skia index f262427..cdd8212 160000 --- a/third_party/skia +++ b/third_party/skia
@@ -1 +1 @@ -Subproject commit f2624278cd4bc2a5d4cc211124a6b60bdb4621b0 +Subproject commit cdd82125aabea8e9970f42b0fb55d104bc371520
diff --git a/third_party/wpt_tools/README.chromium b/third_party/wpt_tools/README.chromium index 3c4dcbb..c47a051 100644 --- a/third_party/wpt_tools/README.chromium +++ b/third_party/wpt_tools/README.chromium
@@ -2,7 +2,7 @@ Short Name: wpt URL: https://github.com/web-platform-tests/wpt/ Version: N/A -Revision: 22a46fcb70f13da6a62278446f305dfba6e57b9b +Revision: 8074c2b53ba903862d75234c53b70ffbf2285ca5 Update Mechanism: Autoroll License: BSD-3-Clause Security Critical: no
diff --git a/third_party/wpt_tools/wpt/resources/testdriver.js b/third_party/wpt_tools/wpt/resources/testdriver.js index 5a4ccf2a..5b390de 100644 --- a/third_party/wpt_tools/wpt/resources/testdriver.js +++ b/third_party/wpt_tools/wpt/resources/testdriver.js
@@ -526,8 +526,8 @@ * event will be subscribed to globally. If omitted, the * event will be subscribed to on the current browsing * context. - * @returns {Promise<void>} Resolves when the subscription - * is successfully done. + * @returns {Promise<(function(): Promise<void>)>} Callback + * for unsubscribing from the created subscription. */ subscribe: async function(params = {}) { assertBidiIsEnabled(); @@ -593,8 +593,8 @@ * event will be subscribed to globally. If omitted, the * event will be subscribed to on the current browsing * context. - * @returns {Promise<void>} Resolves when the subscription - * is successfully done. + * @returns {Promise<(function(): Promise<void>)>} Callback + * for unsubscribing from the created subscription. */ subscribe: async function(params = {}) { assertBidiIsEnabled(); @@ -660,8 +660,8 @@ * event will be subscribed to globally. If omitted, the * event will be subscribed to on the current browsing * context. - * @returns {Promise<void>} Resolves when the subscription - * is successfully done. + * @returns {Promise<(function(): Promise<void>)>} Callback + * for unsubscribing from the created subscription. */ subscribe: async function(params = {}) { assertBidiIsEnabled(); @@ -727,8 +727,8 @@ * event will be subscribed to globally. If omitted, the * event will be subscribed to on the current browsing * context. - * @returns {Promise<void>} Resolves when the subscription - * is successfully done. + * @returns {Promise<(function(): Promise<void>)>} Callback + * for unsubscribing from the created subscription. */ subscribe: async function(params = {}) { assertBidiIsEnabled(); @@ -819,6 +819,69 @@ return window.test_driver_internal.bidi.emulation.set_geolocation_override( params); }, + /** + * Overrides the locale for the specified browsing contexts. + * Matches the `emulation.setLocaleOverride + * <https://www.w3.org/TR/webdriver-bidi/#commands-emulationsetlocaleoverride>`_ + * WebDriver BiDi command. + * + * @example + * await test_driver.bidi.emulation.set_locale_override({ + * locale: 'de-DE' + * }); + * + * @param {object} params - Parameters for the command. + * @param {null|string} params.locale - The optional + * locale to set. + * @param {null|Array.<(Context)>} [params.contexts] The + * optional contexts parameter specifies which browsing contexts + * to set the locale override on. It should be either an array + * of Context objects (window or browsing context id), or null. + * If null or omitted, the override will be set on the current + * browsing context. + * @returns {Promise<void>} Resolves when the locale override + * is successfully set. + */ + set_locale_override: function (params) { + assertBidiIsEnabled(); + return window.test_driver_internal.bidi.emulation.set_locale_override( + params); + }, + /** + * Overrides the screen orientation for the specified browsing + * contexts. + * Matches the `emulation.setScreenOrientationOverride + * <https://www.w3.org/TR/webdriver-bidi/#commands-emulationsetscreenorientationoverride>`_ + * WebDriver BiDi command. + * + * @example + * await test_driver.bidi.emulation.set_screen_orientation_override({ + * screenOrientation: { + * natural: 'portrait', + * type: 'landscape-secondary' + * } + * }); + * + * @param {object} params - Parameters for the command. + * @param {null|object} params.screenOrientation - The optional + * screen orientation. Matches the + * `emulation.ScreenOrientation <https://www.w3.org/TR/webdriver-bidi/#cddl-type-emulationscreenorientation>`_ + * type. If null or omitted, the override will be removed. + * @param {null|Array.<(Context)>} [params.contexts] The + * optional contexts parameter specifies which browsing contexts + * to set the screen orientation override on. It should be + * either an array of Context objects (window or browsing + * context id), or null. If null or omitted, the override will + * be set on the current browsing context. + * @returns {Promise<void>} Resolves when the screen orientation + * override is successfully set. + */ + set_screen_orientation_override: function (params) { + // Ensure the bidi feature is enabled before calling the internal method + assertBidiIsEnabled(); + return window.test_driver_internal.bidi.emulation.set_screen_orientation_override( + params); + }, }, /** * `log <https://www.w3.org/TR/webdriver-bidi/#module-log>`_ module. @@ -844,8 +907,8 @@ * event will be subscribed to globally. If omitted, the * event will be subscribed to on the current browsing * context. - * @returns {Promise<void>} Resolves when the subscription - * is successfully done. + * @returns {Promise<(function(): Promise<void>)>} Callback + * for unsubscribing from the created subscription. */ subscribe: async function (params = {}) { assertBidiIsEnabled(); @@ -2181,6 +2244,14 @@ set_geolocation_override: function (params) { throw new Error( "bidi.emulation.set_geolocation_override is not implemented by testdriver-vendor.js"); + }, + set_locale_override: function (params) { + throw new Error( + "bidi.emulation.set_locale_override is not implemented by testdriver-vendor.js"); + }, + set_screen_orientation_override: function (params) { + throw new Error( + "bidi.emulation.set_screen_orientation_override is not implemented by testdriver-vendor.js"); } }, log: {
diff --git a/third_party/wpt_tools/wpt/tools/lint/lint.py b/third_party/wpt_tools/wpt/tools/lint/lint.py index 1228d546..f6b0f1ad 100644 --- a/third_party/wpt_tools/wpt/tools/lint/lint.py +++ b/third_party/wpt_tools/wpt/tools/lint/lint.py
@@ -356,7 +356,8 @@ rules.AssertThrowsRegexp, rules.PromiseRejectsRegexp, rules.AssertPreconditionRegexp, - rules.HTMLInvalidSyntaxRegexp]] + rules.HTMLInvalidSyntaxRegexp, + rules.TestDriverInternalRegexp]] def check_regexp_line(repo_root: Text, path: Text, f: IO[bytes]) -> List[rules.Error]:
diff --git a/third_party/wpt_tools/wpt/tools/lint/rules.py b/third_party/wpt_tools/wpt/tools/lint/rules.py index 6f3bacf..c3ca5be 100644 --- a/third_party/wpt_tools/wpt/tools/lint/rules.py +++ b/third_party/wpt_tools/wpt/tools/lint/rules.py
@@ -379,6 +379,17 @@ """) +EXTENSIONS = { + "html": [".html", ".htm"], + "xhtml": [".xht", ".xhtml"], + "svg": [".svg"], + "js": [".js", ".mjs"], + "python": [".py"] +} +EXTENSIONS["markup"] = EXTENSIONS["html"] + EXTENSIONS["xhtml"] + EXTENSIONS["svg"] +EXTENSIONS["js_all"] = EXTENSIONS["markup"] + EXTENSIONS["js"] + + class Regexp(metaclass=abc.ABCMeta): @abc.abstractproperty def pattern(self) -> bytes: @@ -425,7 +436,7 @@ class SetTimeoutRegexp(Regexp): pattern = br"setTimeout\s*\(" name = "SET TIMEOUT" - file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"] + file_extensions = EXTENSIONS["js_all"] description = "setTimeout used" to_fix = """ replace all `setTimeout(...)` calls with `step_timeout(...)` calls @@ -462,7 +473,7 @@ class ConsoleRegexp(Regexp): pattern = br"console\.[a-zA-Z]+\s*\(" name = "CONSOLE" - file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"] + file_extensions = EXTENSIONS["js_all"] description = "Test-file line has a `console.*(...)` call" to_fix = """ remove the `console.*(...)` call (and in some cases, consider adding an @@ -473,7 +484,7 @@ class GenerateTestsRegexp(Regexp): pattern = br"generate_tests\s*\(" name = "GENERATE_TESTS" - file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"] + file_extensions = EXTENSIONS["js_all"] description = "Test file line has a generate_tests call" to_fix = "remove the call and call `test()` a number of times instead" @@ -481,7 +492,7 @@ class PrintRegexp(Regexp): pattern = br"print(?:\s|\s*\()" name = "PRINT STATEMENT" - file_extensions = [".py"] + file_extensions = EXTENSIONS["python"] description = collapse(""" A server-side python support file contains a `print` statement """) @@ -494,14 +505,14 @@ class LayoutTestsRegexp(Regexp): pattern = br"(eventSender|testRunner|internals)\." name = "LAYOUTTESTS APIS" - file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"] + file_extensions = EXTENSIONS["js_all"] description = "eventSender/testRunner/internals used; these are LayoutTests-specific APIs (WebKit/Blink)" class MissingDepsRegexp(Regexp): pattern = br"[^\w]/gen/" name = "MISSING DEPENDENCY" - file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"] + file_extensions = EXTENSIONS["js_all"] description = "Chromium-specific content referenced" to_fix = "Reimplement the test to use well-documented testing interfaces" @@ -509,7 +520,7 @@ class SpecialPowersRegexp(Regexp): pattern = b"SpecialPowers" name = "SPECIALPOWERS API" - file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"] + file_extensions = EXTENSIONS["js_all"] description = "SpecialPowers used; this is gecko-specific and not supported in wpt" @@ -523,7 +534,7 @@ class AssertThrowsRegexp(Regexp): pattern = br"[^.]assert_throws\(" name = "ASSERT_THROWS" - file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"] + file_extensions = EXTENSIONS["js_all"] description = "Test-file line has an `assert_throws(...)` call" to_fix = """Replace with `assert_throws_dom` or `assert_throws_js` or `assert_throws_exactly`""" @@ -531,7 +542,7 @@ class PromiseRejectsRegexp(Regexp): pattern = br"promise_rejects\(" name = "PROMISE_REJECTS" - file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"] + file_extensions = EXTENSIONS["js_all"] description = "Test-file line has a `promise_rejects(...)` call" to_fix = """Replace with promise_rejects_dom or promise_rejects_js or `promise_rejects_exactly`""" @@ -539,7 +550,7 @@ class AssertPreconditionRegexp(Regexp): pattern = br"[^.]assert_precondition\(" name = "ASSERT-PRECONDITION" - file_extensions = [".html", ".htm", ".js", ".xht", ".xhtml", ".svg"] + file_extensions = EXTENSIONS["js_all"] description = "Test-file line has an `assert_precondition(...)` call" to_fix = """Replace with `assert_implements` or `assert_implements_optional`""" @@ -551,6 +562,14 @@ br"search|section|select|slot|small|span|strong|style|sub|summary|sup|table|tbody|td|template|textarea|tfoot|th|thead|time|" br"title|tr|u|ul|var|video)(\s+[^>]+)?\s*/>") name = "HTML INVALID SYNTAX" - file_extensions = [".html", ".htm"] + file_extensions = EXTENSIONS["html"] description = "Test-file line has a non-void HTML tag with /> syntax" to_fix = """Replace with start tag and end tag""" + + +class TestDriverInternalRegexp(Regexp): + pattern = br"test_driver_internal" + name = "TEST DRIVER INTERNAL" + file_extensions = EXTENSIONS["js_all"] + description = "Test-file uses test_driver_internal API" + to_fix = """Only use test_driver public API"""
diff --git a/third_party/wpt_tools/wpt/tools/serve/serve.py b/third_party/wpt_tools/wpt/tools/serve/serve.py index d6b09eb..41469447f 100644 --- a/third_party/wpt_tools/wpt/tools/serve/serve.py +++ b/third_party/wpt_tools/wpt/tools/serve/serve.py
@@ -212,7 +212,7 @@ class HtmlWrapperHandler(WrapperHandler): global_type: ClassVar[Optional[str]] = None - headers = [('Content-Type', 'text/html')] + headers = [("Content-Type", "text/html")] def check_exposure(self, request): if self.global_type is not None: @@ -232,7 +232,7 @@ return '<meta name="timeout" content="long">' if key == "title": value = value.replace("&", "&").replace("<", "<") - return '<title>%s</title>' % value + return "<title>%s</title>" % value return None def _script_replacement(self, key, value): @@ -560,7 +560,7 @@ class BaseWorkerHandler(WrapperHandler): - headers = [('Content-Type', 'text/javascript')] + headers = [("Content-Type", "text/javascript")] def _meta_replacement(self, key, value): return None @@ -738,7 +738,7 @@ self.extra = [] self.inject_script_data = None if inject_script is not None: - with open(inject_script, 'rb') as f: + with open(inject_script, "rb") as f: self.inject_script_data = f.read() self.mountpoint_routes = OrderedDict() @@ -848,7 +848,7 @@ self.proc = self.mp_context.Process(target=self.create_daemon, args=(init_func, host, port, paths, routes, bind_address, config, log_handlers, dict(**os.environ)), - name='%s on port %s' % (self.scheme, port), + name="%s on port %s" % (self.scheme, port), kwargs=kwargs) self.proc.daemon = True self.proc.start() @@ -1429,10 +1429,10 @@ if kwargs.get("alias_file"): with open(kwargs["alias_file"]) as alias_file: for line in alias_file: - alias, doc_root = (x.strip() for x in line.split(',')) + alias, doc_root = (x.strip() for x in line.split(",")) config["aliases"].append({ - 'url-path': alias, - 'local-dir': doc_root, + "url-path": alias, + "local-dir": doc_root, }) if route_builder is None:
diff --git a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/client.py b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/client.py index 1f4647cf..193a027 100644 --- a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/client.py +++ b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/client.py
@@ -7,7 +7,7 @@ from . import modules from .error import from_error_details -from .transport import get_running_loop, Transport +from .transport import Transport class BidiSession: @@ -144,11 +144,10 @@ async def start_transport(self, loop: Optional[asyncio.AbstractEventLoop] = None) -> None: if self.transport is None: - if loop is None: - loop = get_running_loop() - self.transport = Transport(self.websocket_url, self.on_message, loop=loop) await self.transport.start() + elif loop is not None and loop is not self.event_loop: + raise ValueError("Transport with a different event loop already exists") async def start(self, loop: Optional[asyncio.AbstractEventLoop] = None) -> None:
diff --git a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/error.py b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/error.py index 7ee1e5c..0a74251 100644 --- a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/error.py +++ b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/error.py
@@ -75,6 +75,14 @@ error_code = "no such history entry" +class NoSuchNetworkCollectorException(BidiException): + error_code = "no such network collector" + + +class NoSuchNetworkDataException(BidiException): + error_code = "no such network data" + + class NoSuchNodeException(BidiException): error_code = "no such node" @@ -107,6 +115,10 @@ error_code = "unable to set file input" +class UnavailableNetworkDataException(BidiException): + error_code = "unavailable network data" + + class UnderspecifiedStoragePartitionException(BidiException): error_code = "underspecified storage partition"
diff --git a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/modules/browser.py b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/modules/browser.py index da39c3b..aa0b28cc 100644 --- a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/modules/browser.py +++ b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/modules/browser.py
@@ -29,7 +29,8 @@ @command def create_user_context( self, accept_insecure_certs: Optional[bool] = None, - proxy: Optional[Mapping[str, Any]] = None + proxy: Optional[Mapping[str, Any]] = None, + unhandled_prompt_behavior: Optional[Mapping[str, str]] = None, ) -> Mapping[str, Any]: params: MutableMapping[str, Any] = {} @@ -39,6 +40,9 @@ if proxy is not None: params["proxy"] = proxy + if unhandled_prompt_behavior is not None: + params["unhandledPromptBehavior"] = unhandled_prompt_behavior + return params @create_user_context.result
diff --git a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/modules/emulation.py b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/modules/emulation.py index fd5cf73..1704c05e 100644 --- a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/modules/emulation.py +++ b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/modules/emulation.py
@@ -51,3 +51,57 @@ params["userContexts"] = user_contexts return params + + @command + def set_locale_override( + self, + locale: Union[str, None], + contexts: Optional[List[str]] = None, + user_contexts: Optional[List[str]] = None, + ) -> Mapping[str, Any]: + params: MutableMapping[str, Any] = { + "locale": locale + } + + if contexts is not None: + params["contexts"] = contexts + if user_contexts is not None: + params["userContexts"] = user_contexts + + return params + + @command + def set_screen_orientation_override( + self, + screen_orientation:Dict[str, Any], + contexts: Optional[List[str]] = None, + user_contexts: Optional[List[str]] = None, + ) -> Mapping[str, Any]: + params: MutableMapping[str, Any] = { + "screenOrientation": screen_orientation + } + + if contexts is not None: + params["contexts"] = contexts + if user_contexts is not None: + params["userContexts"] = user_contexts + + return params + + @command + def set_timezone_override( + self, + timezone: Union[str, None], + contexts: Optional[List[str]] = None, + user_contexts: Optional[List[str]] = None, + ) -> Mapping[str, Any]: + params: MutableMapping[str, Any] = { + "timezone": timezone + } + + if contexts is not None: + params["contexts"] = contexts + if user_contexts is not None: + params["userContexts"] = user_contexts + + return params
diff --git a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/modules/network.py b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/modules/network.py index dc895d9..0339d96 100644 --- a/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/modules/network.py +++ b/third_party/wpt_tools/wpt/tools/webdriver/webdriver/bidi/modules/network.py
@@ -249,6 +249,35 @@ return params @command + def add_data_collector( + self, + data_types: List[str], + max_encoded_data_size: int, + collector_type: Optional[str] = None, + contexts: Optional[List[str]] = None, + user_contexts: Optional[List[str]] = None) -> Mapping[str, Any]: + params: MutableMapping[str, Any] = { + "dataTypes": data_types, + "maxEncodedDataSize": max_encoded_data_size, + } + + if collector_type is not None: + params["collectorType"] = collector_type + + if contexts is not None: + params["contexts"] = contexts + + if user_contexts is not None: + params["userContexts"] = user_contexts + + return params + + @add_data_collector.result + def _add_data_collector(self, result: Mapping[str, Any]) -> Any: + assert result["collector"] is not None + return result["collector"] + + @command def set_cache_behavior( self, cache_behavior: CacheBehavior,
diff --git a/third_party/wpt_tools/wpt/tools/wpt/browser.py b/third_party/wpt_tools/wpt/tools/wpt/browser.py index ac1aff3..2334942 100644 --- a/third_party/wpt_tools/wpt/tools/wpt/browser.py +++ b/third_party/wpt_tools/wpt/tools/wpt/browser.py
@@ -630,8 +630,14 @@ if dest is None: dest = os.pwd + branches = { + "stable": "mozilla-release", + "beta": "mozilla-beta", + } + branch = branches.get(channel, "mozilla-central") + resp = get_taskcluster_artifact( - "gecko.v2.mozilla-central.shippable.latest.mobile.android-x86_64-opt", + f"gecko.v2.{branch}.shippable.latest.mobile.android-x86_64-opt", "public/build/geckoview-test_runner.apk") filename = "geckoview-test_runner.apk" @@ -1781,7 +1787,7 @@ try: # MojoJS version url must match the browser binary version exactly. - url = ("https://msedgedriver.azureedge.net/wpt-mojom/" + url = ("https://msedgedriver.microsoft.com/wpt-mojom/" f"{edge_version}/linux64/mojojs.zip") # Check the status without downloading the content (this is a # streaming request). @@ -1882,7 +1888,7 @@ elif self.platform == "win": bits = "win64" if uname[4] == "x86_64" else "win32" - url = f"https://msedgedriver.azureedge.net/{version}/edgedriver_{bits}.zip" + url = f"https://msedgedriver.microsoft.com/{version}/edgedriver_{bits}.zip" self.logger.info(f"Downloading MSEdgeDriver from {url}") unzip(get(url).raw, dest) edgedriver_path = which("msedgedriver", path=dest)
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/requirements.txt b/third_party/wpt_tools/wpt/tools/wptrunner/requirements.txt index 6605de2..bdb6820c 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/requirements.txt +++ b/third_party/wpt_tools/wpt/tools/wptrunner/requirements.txt
@@ -5,9 +5,7 @@ mozprocess==1.3.1 packaging==25.0 pillow==10.4.0; python_version < '3.9' -pillow==11.1.0; python_version >= '3.9' +pillow==11.3.0; python_version >= '3.9' requests==2.32.3 types-requests==2.32.0.20241016 -six==1.16.0 -types-six==1.17.0.20241205 urllib3==2.2.2
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/firefox.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/firefox.py index 475c75d..29f415a 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/firefox.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/firefox.py
@@ -259,6 +259,7 @@ "debug", "display", "fission", + "isolated_process", "processor", "swgl", "asan", @@ -768,6 +769,9 @@ profile.set_preferences({ "geo.provider.network.url": "https://web-platform.test:8444/webdriver/tests/support/http_handlers/geolocation_override.py" }) + else: + # Except for wdspec dispatch wheel scroll as widget event by default. + profile.set_preferences({"remote.events.async.wheel.enabled": True}) if self.debug_test: profile.set_preferences({"devtools.console.stdout.content": True})
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/firefox_android.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/firefox_android.py index d0cd7411a..050a96477 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/firefox_android.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/browsers/firefox_android.py
@@ -197,6 +197,12 @@ if self.test_type == "wdspec": profile.set_preferences({"remote.prefs.recommended": True}) + profile.set_preferences({ + "geo.provider.network.url": "https://web-platform.test:8444/webdriver/tests/support/http_handlers/geolocation_override.py" + }) + else: + # Except for wdspec dispatch wheel scroll as widget event by default. + profile.set_preferences({"remote.events.async.wheel.enabled": True}) profile.set_preferences({"fission.autostart": True}) if self.disable_fission:
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/asyncactions.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/asyncactions.py index b2bee624ea..b5192dcb 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/asyncactions.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/asyncactions.py
@@ -197,6 +197,54 @@ coordinates, error, contexts) +class BidiEmulationSetLocaleOverrideAction: + name = "bidi.emulation.set_locale_override" + + def __init__(self, logger, protocol): + do_delayed_imports() + self.logger = logger + self.protocol = protocol + + async def __call__(self, payload): + locale = payload['locale'] if 'locale' in payload else None + + if "contexts" not in payload: + raise ValueError("Missing required parameter: contexts") + contexts = [] + for context in payload["contexts"]: + contexts.append(get_browsing_context_id(context)) + if len(contexts) == 0: + raise ValueError("At least one context must be provided") + + return await self.protocol.bidi_emulation.set_locale_override(locale, + contexts) + + +class BidiEmulationSetScreenOrientationOverrideAction: + name = "bidi.emulation.set_screen_orientation_override" + + def __init__(self, logger, protocol): + do_delayed_imports() + self.logger = logger + self.protocol = protocol + + async def __call__(self, payload): + screen_orientation = payload['screenOrientation'] \ + if 'screenOrientation' in payload \ + else None + + if "contexts" not in payload: + raise ValueError("Missing required parameter: contexts") + contexts = [] + for context in payload["contexts"]: + contexts.append(get_browsing_context_id(context)) + if len(contexts) == 0: + raise ValueError("At least one context must be provided") + + return await self.protocol.bidi_emulation.set_screen_orientation_override( + screen_orientation, contexts) + + class BidiSessionSubscribeAction: name = "bidi.session.subscribe" @@ -215,6 +263,23 @@ return await self.protocol.bidi_events.subscribe(events, contexts) +class BidiSessionUnsubscribeAction: + name = "bidi.session.unsubscribe" + + def __init__(self, logger, protocol): + do_delayed_imports() + self.logger = logger + self.protocol = protocol + + async def __call__(self, payload): + subscriptions = payload["subscriptions"] + if len(subscriptions) == 0: + raise ValueError("At least one subscription ID should be provided") + + return await self.protocol.bidi_events.unsubscribe( + subscriptions=subscriptions) + + class BidiPermissionsSetPermissionAction: name = "bidi.permissions.set_permission" @@ -245,5 +310,8 @@ BidiBluetoothSimulateDescriptorAction, BidiBluetoothSimulateDescriptorResponseAction, BidiEmulationSetGeolocationOverrideAction, + BidiEmulationSetLocaleOverrideAction, + BidiEmulationSetScreenOrientationOverrideAction, BidiPermissionsSetPermissionAction, - BidiSessionSubscribeAction] + BidiSessionSubscribeAction, + BidiSessionUnsubscribeAction]
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorwebdriver.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorwebdriver.py index 88ab4c30..dbf985cf 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorwebdriver.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/executorwebdriver.py
@@ -323,6 +323,11 @@ self._subscriptions.append((events, top_contexts)) return result + async def unsubscribe(self, subscriptions): + self.logger.info("Unsubscribing from subscriptions %s" % subscriptions) + await self.webdriver.bidi_session.session.unsubscribe( + subscriptions=subscriptions) + async def unsubscribe_all(self): self.logger.info("Unsubscribing from all the events") while self._subscriptions: @@ -333,6 +338,12 @@ except webdriver_bidi_error.NoSuchFrameException: # The browsing context is already removed. Nothing to do. pass + except webdriver_bidi_error.InvalidArgumentException as e: + if e.message == "No subscription found": + # The subscription is already removed, nothing to do. + pass + else: + raise e except Exception as e: self.logger.error("Failed to unsubscribe from events %s in %s: %s" % (events, contexts, e)) # Re-raise the exception to identify regressions. @@ -372,6 +383,15 @@ return await self.webdriver.bidi_session.emulation.set_geolocation_override( coordinates=coordinates, error=error, contexts=contexts) + async def set_locale_override(self, locale, contexts): + return await self.webdriver.bidi_session.emulation.set_locale_override( + locale=locale, contexts=contexts) + + async def set_screen_orientation_override(self, screen_orientation, + contexts): + return await self.webdriver.bidi_session.emulation.set_screen_orientation_override( + screen_orientation=screen_orientation, contexts=contexts) + class WebDriverBidiPermissionsProtocolPart(BidiPermissionsProtocolPart): def __init__(self, parent):
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/protocol.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/protocol.py index 2a1db88..d745b31 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/protocol.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/executors/protocol.py
@@ -565,6 +565,14 @@ pass @abstractmethod + async def unsubscribe(self, subscriptions: List[str]) -> Mapping[str, Any]: + """ + Unsubscribes from the subscriptions with the given IDs. + :param subscriptions: The list of subscription ids to unsubscribe from. + """ + pass + + @abstractmethod async def unsubscribe_all(self): """Cleans up the subscription state. Removes all the previously added subscriptions.""" pass @@ -605,6 +613,17 @@ contexts: List[str]) -> None: pass + @abstractmethod + async def set_locale_override(self, locale: Optional[str], + contexts: List[str]) -> None: + pass + + @abstractmethod + async def set_screen_orientation_override(self, + screen_orientation: Optional[Mapping[str, Any]], + contexts: List[str]) -> None: + pass + class BidiScriptProtocolPart(ProtocolPart): """Protocol part for executing BiDi scripts"""
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/metadata.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/metadata.py index 5d7ea2b..c1868a0c 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/metadata.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/metadata.py
@@ -6,7 +6,6 @@ from typing import Dict, List, Tuple from mozlog import structuredlog -from six import ensure_str, ensure_text from sys import intern from . import manifestupdate @@ -486,7 +485,7 @@ self.run_info_by_subsuite[name] = run_info_intern.store(run_info) def test_start(self, data): - test_id = intern(ensure_str(data["test"])) + test_id = intern(data["test"]) try: self.id_test_map[test_id] except KeyError: @@ -496,8 +495,8 @@ self.tests_visited[test_id] = set() def test_status(self, data): - test_id = intern(ensure_str(data["test"])) - subtest = intern(ensure_str(data["subtest"])) + test_id = intern(data["test"]) + subtest = intern(data["subtest"]) test_data = self.id_test_map.get(test_id) if test_data is None: return @@ -516,7 +515,7 @@ if data["status"] == "SKIP": return - test_id = intern(ensure_str(data["test"])) + test_id = intern(data["test"]) test_data = self.id_test_map.get(test_id) if test_data is None: return @@ -531,7 +530,7 @@ del self.tests_visited[test_id] def assertion_count(self, data): - test_id = intern(ensure_str(data["test"])) + test_id = intern(data["test"]) test_data = self.id_test_map.get(test_id) if test_data is None: return @@ -542,7 +541,7 @@ def test_for_scope(self, data): dir_path = data.get("scope", "/") - dir_id = intern(ensure_str(os.path.join(dir_path, "__dir__").replace(os.path.sep, "/"))) + dir_id = intern(os.path.join(dir_path, "__dir__").replace(os.path.sep, "/")) if dir_id.startswith("/"): dir_id = dir_id[1:] return dir_id, self.id_test_map[dir_id] @@ -590,13 +589,13 @@ assert all_types > exclude_types include_types = all_types - exclude_types for item_type, test_path, tests in test_manifest.itertypes(*include_types): - test_file_data = TestFileData(intern(ensure_str(test_manifest.url_base)), - intern(ensure_str(item_type)), + test_file_data = TestFileData(intern(test_manifest.url_base), + intern(item_type), metadata_path, test_path, tests) for test in tests: - id_test_map[intern(ensure_str(test.id))] = test_file_data + id_test_map[intern(test.id)] = test_file_data dir_path = os.path.dirname(test_path) while True: @@ -605,7 +604,7 @@ if dir_id in id_test_map: break - test_file_data = TestFileData(intern(ensure_str(test_manifest.url_base)), + test_file_data = TestFileData(intern(test_manifest.url_base), None, metadata_path, dir_meta_path, @@ -675,7 +674,7 @@ self.item_type = item_type self.test_path = test_path self.metadata_path = metadata_path - self.tests = {intern(ensure_str(item.id)) for item in tests} + self.tests = {intern(item.id) for item in tests} self._requires_update = False self.data = defaultdict(lambda: defaultdict(PackedResultList)) @@ -717,10 +716,10 @@ rv = [] for test_id, subtests in self.data.items(): - test = expected.get_test(ensure_text(test_id)) + test = expected.get_test(test_id) if not test: continue - seen_subtests = {ensure_text(item) for item in subtests.keys() if item is not None} + seen_subtests = {item for item in subtests.keys() if item is not None} missing_subtests = set(test.subtests.keys()) - seen_subtests for item in missing_subtests: expected_subtest = test.get_subtest(item) @@ -792,7 +791,6 @@ expected_by_test[test_id] = test_expected for test_id, test_data in self.data.items(): - test_id = ensure_str(test_id) for subtest_id, results_list in test_data.items(): for prop, run_info, value in results_list: # Special case directory metadata @@ -809,7 +807,6 @@ if subtest_id is None: item_expected = test_expected else: - subtest_id = ensure_text(subtest_id) item_expected = test_expected.get_subtest(subtest_id) if prop == "status":
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testdriver-extra.js b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testdriver-extra.js index ea7719cf..4d26a14 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testdriver-extra.js +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testdriver-extra.js
@@ -207,12 +207,25 @@ return create_action(name, context_params); }; - const subscribe = function (params) { - return create_action("bidi.session.subscribe", { + /** + * @returns {Promise<(function(): Promise<void>)>}: callback for + * unsubscribing from the created subscription. + */ + const subscribe = async function (params) { + const action_result = await create_action("bidi.session.subscribe", { // Default to subscribing to the window's events. contexts: [window], ...params }); + const subscription_id = action_result["subscription"]; + + return async ()=>{ + console.log("!!@@## unsubscribing") + await create_action("bidi.session.unsubscribe", { + // Default to subscribing to the window's events. + subscriptions: [subscription_id] + }); + } }; window.test_driver_internal.in_automation = true; @@ -319,7 +332,7 @@ window.test_driver_internal.bidi.bluetooth.request_device_prompt_updated.subscribe = function(params) { return subscribe( - {params, events: ['bluetooth.requestDevicePromptUpdated']}) + {...params, events: ['bluetooth.requestDevicePromptUpdated']}) }; window.test_driver_internal.bidi.bluetooth.request_device_prompt_updated.on = @@ -336,7 +349,7 @@ window.test_driver_internal.bidi.bluetooth.gatt_connection_attempted.subscribe = function(params) { return subscribe( - {params, events: ['bluetooth.gattConnectionAttempted']}) + {...params, events: ['bluetooth.gattConnectionAttempted']}) }; window.test_driver_internal.bidi.bluetooth.gatt_connection_attempted.on = @@ -353,7 +366,7 @@ window.test_driver_internal.bidi.bluetooth.characteristic_event_generated.subscribe = function(params) { return subscribe( - {params, events: ['bluetooth.characteristicEventGenerated']}) + {...params, events: ['bluetooth.characteristicEventGenerated']}) }; window.test_driver_internal.bidi.bluetooth.characteristic_event_generated.on = @@ -370,7 +383,7 @@ window.test_driver_internal.bidi.bluetooth.descriptor_event_generated.subscribe = function(params) { return subscribe( - {params, events: ['bluetooth.descriptorEventGenerated']}); + {...params, events: ['bluetooth.descriptorEventGenerated']}); }; window.test_driver_internal.bidi.bluetooth.descriptor_event_generated.on = @@ -398,10 +411,28 @@ }); } + window.test_driver_internal.bidi.emulation.set_locale_override = function (params) { + return create_action("bidi.emulation.set_locale_override", { + // Default to the current window. + contexts: [window], + ...(params ?? {}) + }); + }; + + window.test_driver_internal.bidi.emulation.set_screen_orientation_override = + function (params) { + return create_action( + "bidi.emulation.set_screen_orientation_override", { + // Default to the current window. + contexts: [window], + ...(params ?? {}) + }); + } + window.test_driver_internal.bidi.log.entry_added.subscribe = function (params) { return subscribe({ - params, + ...params, events: ["log.entryAdded"] }) };
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testharnessreport-servo.js b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testharnessreport-servo.js index d661673..4439393a 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testharnessreport-servo.js +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testharnessreport-servo.js
@@ -1,22 +1,24 @@ -var props = { - output:%(output)d, - timeout_multiplier: %(timeout_multiplier)s, - explicit_timeout: %(explicit_timeout)s, - debug: %(debug)s -}; -var start_loc = document.createElement('a'); -start_loc.href = location.href; -setup(props); +(function() { + var props = { + output:%(output)d, + timeout_multiplier: %(timeout_multiplier)s, + explicit_timeout: %(explicit_timeout)s, + debug: %(debug)s + }; + var start_loc = document.createElement('a'); + start_loc.href = location.href; + setup(props); -add_completion_callback(function (tests, harness_status) { - var id = decodeURIComponent(start_loc.pathname) + decodeURIComponent(start_loc.search) + decodeURIComponent(start_loc.hash); - console.log("ALERT: RESULT: " + JSON.stringify([ - id, - harness_status.status, - harness_status.message, - harness_status.stack, - tests.map(function(t) { - return [t.name, t.status, t.message, t.stack] - }), - ])); -}); + add_completion_callback(function (tests, harness_status) { + var id = decodeURIComponent(start_loc.pathname) + decodeURIComponent(start_loc.search) + decodeURIComponent(start_loc.hash); + console.log("ALERT: RESULT: " + JSON.stringify([ + id, + harness_status.status, + harness_status.message, + harness_status.stack, + tests.map(function(t) { + return [t.name, t.status, t.message, t.stack] + }), + ])); + }); +})();
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptcommandline.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptcommandline.py index 813af1e..b602c00 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptcommandline.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptcommandline.py
@@ -92,7 +92,9 @@ mode_group.add_argument("--list-disabled", action="store_true", help="List the tests that are disabled on the current platform") mode_group.add_argument("--list-tests", action="store_true", - help="List all tests that will run") + help="List all tests included in the given test_list (whether or not they would be executed)") + mode_group.add_argument("--list-tests-json", action="store_true", + help="List details of all tests included in the given test_list in JSON format") stability_group = mode_group.add_mutually_exclusive_group() stability_group.add_argument("--verify", action="store_true", help="Run a stability check on the selected tests")
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/serializer.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/serializer.py index e749add7..b4bd58d 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/serializer.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptmanifest/serializer.py
@@ -1,7 +1,5 @@ # mypy: allow-untyped-defs -from six import ensure_text - from .node import NodeVisitor, ValueNode, ListNode, BinaryExpressionNode from .parser import atoms, precedence, token_types @@ -23,7 +21,7 @@ rv += "\\" + c else: rv += c - return ensure_text(rv) + return rv class ManifestSerializer(NodeVisitor): @@ -89,7 +87,7 @@ return ["".join(rv)] def visit_ValueNode(self, node): - data = ensure_text(node.data) + data = node.data if ("#" in data or data.startswith("if ") or (isinstance(node.parent, ListNode) and @@ -115,7 +113,7 @@ return rv def visit_NumberNode(self, node): - return [ensure_text(node.data)] + return [node.data] def visit_VariableNode(self, node): rv = escape(node.data) @@ -149,10 +147,10 @@ return [" ".join(children)] def visit_UnaryOperatorNode(self, node): - return [ensure_text(node.data)] + return [node.data] def visit_BinaryOperatorNode(self, node): - return [ensure_text(node.data)] + return [node.data] def serialize(tree, *args, **kwargs):
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptrunner.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptrunner.py index 15f017d8..92bb861 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptrunner.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wptrunner.py
@@ -166,6 +166,25 @@ print(test) +def list_tests_json(test_paths, product, **kwargs): + env.do_delayed_imports(logger, test_paths) + + _, test_loader = get_loader(test_paths, product, **kwargs) + + tests = {} + targets = [(test_loader.tests, False), + (test_loader.disabled_tests, True)] + for subsuite in test_loader.subsuites: + tests[subsuite] = {} + for test_type in test_loader.test_types: + tests[subsuite][test_type] = {} + for target, disabled in targets: + for test in target[subsuite][test_type]: + tests[subsuite][test_type][test.id] = {"disabled": disabled, + "expected": test.expected()} + print(json.dumps(tests, indent=2)) + + def get_pause_after_test(test_loader, **kwargs): if kwargs["pause_after_test"] is not None: return kwargs["pause_after_test"] @@ -563,6 +582,8 @@ list_disabled(**kwargs) elif kwargs["list_tests"]: list_tests(**kwargs) + elif kwargs["list_tests_json"]: + list_tests_json(**kwargs) elif kwargs["verify"] or kwargs["stability"]: rv = check_stability(**kwargs) or logged_critical.has_log else:
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wpttest.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wpttest.py index 9f1e88d..9616f9c 100644 --- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wpttest.py +++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/wpttest.py
@@ -131,13 +131,14 @@ if adb_binary: self["adb_binary"] = adb_binary + if device_serials: # Assume all emulators are identical, so query an arbitrary one. self._update_with_emulator_info(device_serials[0]) self.pop("linux_distro", None) def _adb_run(self, device_serial, args, **kwargs): - adb_binary = self.get("adb_binary", "adb") + adb_binary = self.get("adb_binary", os.environ.get("ADB_PATH", "adb")) cmd = [adb_binary, "-s", device_serial, *args] return subprocess.check_output(cmd, **kwargs) @@ -158,6 +159,11 @@ "ro.build.version.release", encoding="utf-8", ), + "android_version": self._adb_get_property( + device_serial, + "ro.build.version.sdk", + encoding="utf-8", + ) } emulator_info["version"] = emulator_info["os_version"]
diff --git a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/server.py b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/server.py index ba68339..49319ec2 100644 --- a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/server.py +++ b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/server.py
@@ -3,8 +3,11 @@ import errno import http import http.server +import ipaddress import os +import platform import socket +import socketserver import ssl import sys import threading @@ -227,6 +230,25 @@ do_handshake_on_connect=False, server_side=True) + def server_bind(self): + if platform.system() != "Darwin": + super().server_bind() + else: + # We override this on macOS to workaround gethostbyaddr triggering the local + # network alert even when passed "localhost" (rdar://153097791); this should + # be the same as the superclass implementation except for the addition of + # our check. + socketserver.TCPServer.server_bind(self) + host, port = self.server_address[:2] + if ( + ipaddress.ip_address(host).is_loopback and + ipaddress.ip_address(socket.gethostbyname("localhost")).is_loopback + ): + self.server_name = "localhost" + else: + self.server_name = socket.getfqdn(host) + self.server_port = port + def finish_request(self, request, client_address): if isinstance(self.socket, ssl.SSLSocket): request.do_handshake() @@ -377,10 +399,10 @@ def handle_one_request(self): """ - This is the main HTTP/2.0 Handler. + This is the main HTTP/2 Handler. When a browser opens a connection to the server - on the HTTP/2.0 port, the server enters this which will initiate the h2 connection + on the HTTP/2 port, the server enters this which will initiate the h2 connection and keep running throughout the duration of the interaction, and will read/write directly from the socket.
diff --git a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/utils.py b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/utils.py index 403c359..ee4955cd 100644 --- a/third_party/wpt_tools/wpt/tools/wptserve/wptserve/utils.py +++ b/third_party/wpt_tools/wpt/tools/wptserve/wptserve/utils.py
@@ -169,7 +169,7 @@ return port def http2_compatible() -> bool: - # The HTTP/2.0 server requires OpenSSL 1.0.2+. + # The HTTP/2 server requires OpenSSL 1.0.2+. # # For systems using other SSL libraries (e.g. LibreSSL), we assume they # have the necessary support. @@ -177,7 +177,7 @@ if not ssl.OPENSSL_VERSION.startswith("OpenSSL"): logger = get_logger() logger.warning( - 'Skipping HTTP/2.0 compatibility check as system is not using ' + 'Skipping HTTP/2 compatibility check as system is not using ' 'OpenSSL (found: %s)' % ssl.OPENSSL_VERSION) return True
diff --git a/tools/clang/blink_gc_plugin/BadPatternFinder.cpp b/tools/clang/blink_gc_plugin/BadPatternFinder.cpp index 79eda52..e669a4b 100644 --- a/tools/clang/blink_gc_plugin/BadPatternFinder.cpp +++ b/tools/clang/blink_gc_plugin/BadPatternFinder.cpp
@@ -234,14 +234,15 @@ hasName("::std::pair"), hasAnyTemplateArgument(refersToType(member_ptr_or_ref))))))); auto gced_or_member = anyOf(gced_ptr_ref_or_pair, member_ptr_ref_or_pair); - auto has_wtf_collection_name = hasAnyName( - "::WTF::Vector", "::WTF::Deque", "::WTF::HashSet", - "::WTF::LinkedHashSet", "::WTF::HashCountedSet", "::WTF::HashMap"); + auto has_wtf_collection_name = + hasAnyName("::blink::Vector", "::blink::Deque", "::blink::HashSet", + "::blink::LinkedHashSet", "::blink::HashCountedSet", + "::blink::HashMap"); auto has_std_collection_name = hasAnyName("::std::vector", "::std::map", "::std::unordered_map", "::std::set", "::std::unordered_set", "::std::array"); auto partition_allocator = hasCanonicalType( - hasDeclaration(cxxRecordDecl(hasName("::WTF::PartitionAllocator")))); + hasDeclaration(cxxRecordDecl(hasName("::blink::PartitionAllocator")))); auto wtf_collection_decl = classTemplateSpecializationDecl( has_wtf_collection_name,
diff --git a/tools/clang/blink_gc_plugin/process-graph.py b/tools/clang/blink_gc_plugin/process-graph.py index 4f5f4ce..ddead63 100755 --- a/tools/clang/blink_gc_plugin/process-graph.py +++ b/tools/clang/blink_gc_plugin/process-graph.py
@@ -282,7 +282,7 @@ dst = graph.get(root_edge.dst) if src.visited: continue - if root_edge.dst == "WTF::String": + if root_edge.dst == "blink::String": continue if dst is None: print("\nPersistent root to incomplete destination object:") @@ -366,8 +366,8 @@ 'cppgc::GarbageCollectedMixin', ) ref_bases = ( - 'WTF::RefCounted', - 'WTF::ThreadSafeRefCounted', + 'blink::RefCounted', + 'blink::ThreadSafeRefCounted', ) gcref_bases = ( 'blink::RefCountedGarbageCollected',
diff --git a/tools/clang/blink_gc_plugin/tests/heap/stubs.h b/tools/clang/blink_gc_plugin/tests/heap/stubs.h index 227ebb91..e7398c6 100644 --- a/tools/clang/blink_gc_plugin/tests/heap/stubs.h +++ b/tools/clang/blink_gc_plugin/tests/heap/stubs.h
@@ -58,7 +58,7 @@ class raw_ref {}; } // namespace base -namespace WTF { +namespace blink { template<typename T> class RefCounted { }; @@ -157,7 +157,7 @@ ~HashMap() {} }; -} +} // namespace blink // Empty namespace declaration to exercise internal // handling of namespace equality. @@ -418,8 +418,6 @@ template <typename T> using TraceWrapperV8Reference = v8::TracedReference<T>; -using namespace WTF; - #define DISALLOW_NEW() \ public: \ void* operator new(size_t, void* location) { return location; } \
diff --git a/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.cpp b/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.cpp index fb7149d..6690046b 100644 --- a/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.cpp +++ b/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.cpp
@@ -52,13 +52,13 @@ std::array<HeapVector<Base>, 4> array_of_vectors_; // Bad WTF collections: - WTF::HashSet<Base> wtf_hash_set_; - WTF::Deque<Derived> wtf_deque_; - WTF::Vector<Mixin> wtf_vector_; - WTF::LinkedHashSet<Base*> wtf_linked_hash_set_; - WTF::HashCountedSet<Derived&> wtf_hash_counted_set_; - WTF::HashMap<Mixin, bool> wtf_hash_map_key_; - WTF::HashMap<double, const Base> wtf_hash_map_value_; + HashSet<Base> wtf_hash_set_; + Deque<Derived> wtf_deque_; + Vector<Mixin> wtf_vector_; + LinkedHashSet<Base*> wtf_linked_hash_set_; + HashCountedSet<Derived&> wtf_hash_counted_set_; + HashMap<Mixin, bool> wtf_hash_map_key_; + HashMap<double, const Base> wtf_hash_map_value_; // Good collections: blink::HeapHashSet<Base> heap_hash_set_; @@ -107,13 +107,13 @@ (void)vector_pair; // Bad WTF collections: - WTF::HashSet<Base> wtf_hash_set; - WTF::Deque<Derived> wtf_deque; - WTF::Vector<Mixin> wtf_vector; - WTF::LinkedHashSet<Base*> wtf_linked_hash_set; - WTF::HashCountedSet<Derived&> wtf_hash_counted_set; - WTF::HashMap<Mixin, bool> wtf_hash_map_key; - WTF::HashMap<double, const Base> wtf_hash_map_value; + HashSet<Base> wtf_hash_set; + Deque<Derived> wtf_deque; + Vector<Mixin> wtf_vector; + LinkedHashSet<Base*> wtf_linked_hash_set; + HashCountedSet<Derived&> wtf_hash_counted_set; + HashMap<Mixin, bool> wtf_hash_map_key; + HashMap<double, const Base> wtf_hash_map_value; // Good collections: blink::HeapHashSet<Base> heap_hash_set;
diff --git a/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.txt b/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.txt index d50681c..2ed5c8e1 100644 --- a/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.txt +++ b/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-gced.txt
@@ -8,10 +8,10 @@ std::vector<Derived&> vector_ref_; ^ off-heap-collections-of-gced.cpp:58:3: note: [blink-gc] Raw pointer field 'wtf_linked_hash_set_' to a GC managed class declared here: - WTF::LinkedHashSet<Base*> wtf_linked_hash_set_; + LinkedHashSet<Base*> wtf_linked_hash_set_; ^ off-heap-collections-of-gced.cpp:59:3: note: [blink-gc] Reference pointer field 'wtf_hash_counted_set_' to a GC managed class declared here: - WTF::HashCountedSet<Derived&> wtf_hash_counted_set_; + HashCountedSet<Derived&> wtf_hash_counted_set_; ^ off-heap-collections-of-gced.cpp:43:3: warning: [blink-gc] Disallowed collection 'set<blink::Base>' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. std::set<Base> set_; @@ -41,26 +41,26 @@ std::array<Base, 4> array_; ^~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:55:3: warning: [blink-gc] Disallowed collection 'HashSet<blink::Base>' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. - WTF::HashSet<Base> wtf_hash_set_; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashSet<Base> wtf_hash_set_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:56:3: warning: [blink-gc] Disallowed collection 'Deque<blink::Derived>' found; 'Derived' is a garbage-collected type. Use heap collections to hold garbage-collected objects. - WTF::Deque<Derived> wtf_deque_; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Deque<Derived> wtf_deque_; + ^~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:57:3: warning: [blink-gc] Disallowed collection 'Vector<blink::Mixin>' found; 'Mixin' is a garbage-collected type. Use heap collections to hold garbage-collected objects. - WTF::Vector<Mixin> wtf_vector_; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Vector<Mixin> wtf_vector_; + ^~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:58:3: warning: [blink-gc] Disallowed collection 'LinkedHashSet<blink::Base *>' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. - WTF::LinkedHashSet<Base*> wtf_linked_hash_set_; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + LinkedHashSet<Base*> wtf_linked_hash_set_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:59:3: warning: [blink-gc] Disallowed collection 'HashCountedSet<blink::Derived &>' found; 'Derived' is a garbage-collected type. Use heap collections to hold garbage-collected objects. - WTF::HashCountedSet<Derived&> wtf_hash_counted_set_; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashCountedSet<Derived&> wtf_hash_counted_set_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:60:3: warning: [blink-gc] Disallowed collection 'HashMap<blink::Mixin, bool>' found; 'Mixin' is a garbage-collected type. Use heap collections to hold garbage-collected objects. - WTF::HashMap<Mixin, bool> wtf_hash_map_key_; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashMap<Mixin, bool> wtf_hash_map_key_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:61:3: warning: [blink-gc] Disallowed collection 'HashMap<double, const blink::Base>' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. - WTF::HashMap<double, const Base> wtf_hash_map_value_; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashMap<double, const Base> wtf_hash_map_value_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:86:3: warning: [blink-gc] Disallowed collection 'array<blink::Base, 4>' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. std::array<Base, 4> array_; ^~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -89,26 +89,26 @@ std::vector<std::pair<Base, int>> vector_pair; ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:110:3: warning: [blink-gc] Disallowed collection 'HashSet<blink::Base>' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. - WTF::HashSet<Base> wtf_hash_set; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashSet<Base> wtf_hash_set; + ^~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:111:3: warning: [blink-gc] Disallowed collection 'Deque<blink::Derived>' found; 'Derived' is a garbage-collected type. Use heap collections to hold garbage-collected objects. - WTF::Deque<Derived> wtf_deque; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Deque<Derived> wtf_deque; + ^~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:112:3: warning: [blink-gc] Disallowed collection 'Vector<blink::Mixin>' found; 'Mixin' is a garbage-collected type. Use heap collections to hold garbage-collected objects. - WTF::Vector<Mixin> wtf_vector; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Vector<Mixin> wtf_vector; + ^~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:113:3: warning: [blink-gc] Disallowed collection 'LinkedHashSet<blink::Base *>' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. - WTF::LinkedHashSet<Base*> wtf_linked_hash_set; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + LinkedHashSet<Base*> wtf_linked_hash_set; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:114:3: warning: [blink-gc] Disallowed collection 'HashCountedSet<blink::Derived &>' found; 'Derived' is a garbage-collected type. Use heap collections to hold garbage-collected objects. - WTF::HashCountedSet<Derived&> wtf_hash_counted_set; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashCountedSet<Derived&> wtf_hash_counted_set; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:115:3: warning: [blink-gc] Disallowed collection 'HashMap<blink::Mixin, bool>' found; 'Mixin' is a garbage-collected type. Use heap collections to hold garbage-collected objects. - WTF::HashMap<Mixin, bool> wtf_hash_map_key; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashMap<Mixin, bool> wtf_hash_map_key; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:116:3: warning: [blink-gc] Disallowed collection 'HashMap<double, const blink::Base>' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. - WTF::HashMap<double, const Base> wtf_hash_map_value; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashMap<double, const Base> wtf_hash_map_value; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-gced.cpp:127:3: warning: [blink-gc] Disallowed collection 'array<blink::Base, 4>' found; 'Base' is a garbage-collected type. Use heap collections to hold garbage-collected objects. std::array<Base, 4> array; ^~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-members.cpp b/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-members.cpp index 894d0b90..6105c630 100644 --- a/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-members.cpp +++ b/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-members.cpp
@@ -57,13 +57,13 @@ std::array<Member<Base>, 4> array_; // Bad WTF collections: - WTF::HashSet<Member<Base>> wtf_hash_set_; - WTF::Deque<WeakMember<Base>> wtf_deque_; - WTF::Vector<Member<Base>> wtf_vector_; - WTF::LinkedHashSet<Member<Base>*> wtf_linked_hash_set_; - WTF::HashCountedSet<WeakMember<Base>&> wtf_hash_counted_set_; - WTF::HashMap<Member<Base>, bool> wtf_hash_map_key_; - WTF::HashMap<double, const Member<Base>> wtf_hash_map_value_; + HashSet<Member<Base>> wtf_hash_set_; + Deque<WeakMember<Base>> wtf_deque_; + Vector<Member<Base>> wtf_vector_; + LinkedHashSet<Member<Base>*> wtf_linked_hash_set_; + HashCountedSet<WeakMember<Base>&> wtf_hash_counted_set_; + HashMap<Member<Base>, bool> wtf_hash_map_key_; + HashMap<double, const Member<Base>> wtf_hash_map_value_; // Good collections: blink::HeapHashSet<Member<Base>> heap_hash_set_; @@ -112,13 +112,13 @@ (void)vector_pair; // Bad WTF collections: - WTF::HashSet<Member<Base>> wtf_hash_set; - WTF::Deque<WeakMember<Base>> wtf_deque; - WTF::Vector<Member<Base>> wtf_vector; - WTF::LinkedHashSet<Member<Base>*> wtf_linked_hash_set; - WTF::HashCountedSet<WeakMember<Base>&> wtf_hash_counted_set; - WTF::HashMap<Member<Base>, bool> wtf_hash_map_key; - WTF::HashMap<double, const Member<Base>> wtf_hash_map_value; + HashSet<Member<Base>> wtf_hash_set; + Deque<WeakMember<Base>> wtf_deque; + Vector<Member<Base>> wtf_vector; + LinkedHashSet<Member<Base>*> wtf_linked_hash_set; + HashCountedSet<WeakMember<Base>&> wtf_hash_counted_set; + HashMap<Member<Base>, bool> wtf_hash_map_key; + HashMap<double, const Member<Base>> wtf_hash_map_value; // Good collections: blink::HeapHashSet<Member<Base>> heap_hash_set;
diff --git a/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-members.txt b/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-members.txt index 58b860f3..d5c535b0 100644 --- a/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-members.txt +++ b/tools/clang/blink_gc_plugin/tests/off-heap-collections-of-members.txt
@@ -23,26 +23,26 @@ std::vector<std::pair<Member<Base>, int>> vector_pair_; ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-members.cpp:60:3: warning: [blink-gc] Disallowed collection 'HashSet<cppgc::internal::BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>>' found; 'BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>' is a Member type. Use heap collections to hold Members. - WTF::HashSet<Member<Base>> wtf_hash_set_; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashSet<Member<Base>> wtf_hash_set_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-members.cpp:61:3: warning: [blink-gc] Disallowed collection 'Deque<cppgc::internal::BasicMember<blink::Base, cppgc::internal::WeakMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>>' found; 'BasicMember<blink::Base, cppgc::internal::WeakMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>' is a Member type. Use heap collections to hold Members. - WTF::Deque<WeakMember<Base>> wtf_deque_; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Deque<WeakMember<Base>> wtf_deque_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-members.cpp:62:3: warning: [blink-gc] Disallowed collection 'Vector<cppgc::internal::BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>>' found; 'BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>' is a Member type. Use heap collections to hold Members. - WTF::Vector<Member<Base>> wtf_vector_; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Vector<Member<Base>> wtf_vector_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-members.cpp:63:3: warning: [blink-gc] Disallowed collection 'LinkedHashSet<cppgc::internal::BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl> *>' found; 'BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>' is a Member type. Use heap collections to hold Members. - WTF::LinkedHashSet<Member<Base>*> wtf_linked_hash_set_; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + LinkedHashSet<Member<Base>*> wtf_linked_hash_set_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-members.cpp:64:3: warning: [blink-gc] Disallowed collection 'HashCountedSet<cppgc::internal::BasicMember<blink::Base, cppgc::internal::WeakMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl> &>' found; 'BasicMember<blink::Base, cppgc::internal::WeakMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>' is a Member type. Use heap collections to hold Members. - WTF::HashCountedSet<WeakMember<Base>&> wtf_hash_counted_set_; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashCountedSet<WeakMember<Base>&> wtf_hash_counted_set_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-members.cpp:65:3: warning: [blink-gc] Disallowed collection 'HashMap<cppgc::internal::BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>, bool>' found; 'BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>' is a Member type. Use heap collections to hold Members. - WTF::HashMap<Member<Base>, bool> wtf_hash_map_key_; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashMap<Member<Base>, bool> wtf_hash_map_key_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-members.cpp:66:3: warning: [blink-gc] Disallowed collection 'HashMap<double, const cppgc::internal::BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>>' found; 'BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>' is a Member type. Use heap collections to hold Members. - WTF::HashMap<double, const Member<Base>> wtf_hash_map_value_; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashMap<double, const Member<Base>> wtf_hash_map_value_; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-members.cpp:97:3: warning: [blink-gc] Disallowed collection 'set<cppgc::internal::BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>>' found; 'BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>' is a Member type. Use heap collections to hold Members. std::set<Member<Base>> set; ^~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -68,24 +68,24 @@ std::vector<std::pair<Member<Base>, int>> vector_pair; ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-members.cpp:115:3: warning: [blink-gc] Disallowed collection 'HashSet<cppgc::internal::BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>>' found; 'BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>' is a Member type. Use heap collections to hold Members. - WTF::HashSet<Member<Base>> wtf_hash_set; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashSet<Member<Base>> wtf_hash_set; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-members.cpp:116:3: warning: [blink-gc] Disallowed collection 'Deque<cppgc::internal::BasicMember<blink::Base, cppgc::internal::WeakMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>>' found; 'BasicMember<blink::Base, cppgc::internal::WeakMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>' is a Member type. Use heap collections to hold Members. - WTF::Deque<WeakMember<Base>> wtf_deque; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Deque<WeakMember<Base>> wtf_deque; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-members.cpp:117:3: warning: [blink-gc] Disallowed collection 'Vector<cppgc::internal::BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>>' found; 'BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>' is a Member type. Use heap collections to hold Members. - WTF::Vector<Member<Base>> wtf_vector; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Vector<Member<Base>> wtf_vector; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-members.cpp:118:3: warning: [blink-gc] Disallowed collection 'LinkedHashSet<cppgc::internal::BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl> *>' found; 'BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>' is a Member type. Use heap collections to hold Members. - WTF::LinkedHashSet<Member<Base>*> wtf_linked_hash_set; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + LinkedHashSet<Member<Base>*> wtf_linked_hash_set; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-members.cpp:119:3: warning: [blink-gc] Disallowed collection 'HashCountedSet<cppgc::internal::BasicMember<blink::Base, cppgc::internal::WeakMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl> &>' found; 'BasicMember<blink::Base, cppgc::internal::WeakMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>' is a Member type. Use heap collections to hold Members. - WTF::HashCountedSet<WeakMember<Base>&> wtf_hash_counted_set; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashCountedSet<WeakMember<Base>&> wtf_hash_counted_set; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-members.cpp:120:3: warning: [blink-gc] Disallowed collection 'HashMap<cppgc::internal::BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>, bool>' found; 'BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>' is a Member type. Use heap collections to hold Members. - WTF::HashMap<Member<Base>, bool> wtf_hash_map_key; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashMap<Member<Base>, bool> wtf_hash_map_key; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ off-heap-collections-of-members.cpp:121:3: warning: [blink-gc] Disallowed collection 'HashMap<double, const cppgc::internal::BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>>' found; 'BasicMember<blink::Base, cppgc::internal::StrongMemberTag, cppgc::internal::WriteBarrierPolicyImpl, cppgc::internal::CheckingPolicyImpl, cppgc::internal::StorateTypeImpl>' is a Member type. Use heap collections to hold Members. - WTF::HashMap<double, const Member<Base>> wtf_hash_map_value; - ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + HashMap<double, const Member<Base>> wtf_hash_map_value; + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 30 warnings generated.
diff --git a/tools/clang/iterator_checker/IteratorChecker.cpp b/tools/clang/iterator_checker/IteratorChecker.cpp index bc88f42..1da446e3 100644 --- a/tools/clang/iterator_checker/IteratorChecker.cpp +++ b/tools/clang/iterator_checker/IteratorChecker.cpp
@@ -428,7 +428,7 @@ }, }, { - "WTF::Vector", + "blink::Vector", { { "begin",
diff --git a/tools/clang/plugins/BlinkDataMemberTypeChecker.cpp b/tools/clang/plugins/BlinkDataMemberTypeChecker.cpp index b2dc4e1b..6942ddd 100644 --- a/tools/clang/plugins/BlinkDataMemberTypeChecker.cpp +++ b/tools/clang/plugins/BlinkDataMemberTypeChecker.cpp
@@ -20,15 +20,15 @@ diagnostic_(instance.getDiagnostics()), discouraged_types_({ {"GURL", "KURL"}, - {"std::deque", "WTF::Deque"}, - {"std::map", "WTF::HashMap or WTF::LinkedHashSet"}, + {"std::deque", "blink::Deque"}, + {"std::map", "blink::HashMap or blink::LinkedHashSet"}, {"std::multimap", - "WTF::HashMap<K, WTF::Vector<V>> or WTF::HashCountedSet<T>"}, - {"std::multiset", "WTF::HashCountedSet<T>"}, - {"std::set", "WTF::HashSet or WTF::LinkedHashSet"}, - {"std::unordered_set", "WTF::HashSet"}, - {"std::unordered_map", "WTF::HashMap"}, - {"std::vector", "WTF::Vector"}, + "blink::HashMap<K, blink::Vector<V>> or blink::HashCountedSet<T>"}, + {"std::multiset", "blink::HashCountedSet<T>"}, + {"std::set", "blink::HashSet or blink::LinkedHashSet"}, + {"std::unordered_set", "blink::HashSet"}, + {"std::unordered_map", "blink::HashMap"}, + {"std::vector", "blink::Vector"}, }), included_filenames_regex_("/third_party/blink/renderer/"), excluded_filenames_regex_(
diff --git a/tools/clang/plugins/tests/blink_discouraged_type.txt b/tools/clang/plugins/tests/blink_discouraged_type.txt index ea18db36..106e339 100644 --- a/tools/clang/plugins/tests/blink_discouraged_type.txt +++ b/tools/clang/plugins/tests/blink_discouraged_type.txt
@@ -1,23 +1,23 @@ In file included from blink_discouraged_type.cpp:5: -./third_party/blink/renderer/discouraged_type.h:31:20: warning: [blink-style] 'std::vector' is discouraged for data members in blink renderer. Use WTF::Vector if possible. If the usage is necessary, add ALLOW_DISCOURAGED_TYPE(reason) to the data member or the type alias to suppress this message. +./third_party/blink/renderer/discouraged_type.h:31:20: warning: [blink-style] 'std::vector' is discouraged for data members in blink renderer. Use blink::Vector if possible. If the usage is necessary, add ALLOW_DISCOURAGED_TYPE(reason) to the data member or the type alias to suppress this message. std::vector<int> v1; ^ -./third_party/blink/renderer/discouraged_type.h:36:21: warning: [blink-style] 'std::vector' is discouraged for data members in blink renderer. Use WTF::Vector if possible. If the usage is necessary, add ALLOW_DISCOURAGED_TYPE(reason) to the data member or the type alias to suppress this message. +./third_party/blink/renderer/discouraged_type.h:36:21: warning: [blink-style] 'std::vector' is discouraged for data members in blink renderer. Use blink::Vector if possible. If the usage is necessary, add ALLOW_DISCOURAGED_TYPE(reason) to the data member or the type alias to suppress this message. nested::IntVector v2a; ^ -./third_party/blink/renderer/discouraged_type.h:37:15: warning: [blink-style] 'std::vector' is discouraged for data members in blink renderer. Use WTF::Vector if possible. If the usage is necessary, add ALLOW_DISCOURAGED_TYPE(reason) to the data member or the type alias to suppress this message. +./third_party/blink/renderer/discouraged_type.h:37:15: warning: [blink-style] 'std::vector' is discouraged for data members in blink renderer. Use blink::Vector if possible. If the usage is necessary, add ALLOW_DISCOURAGED_TYPE(reason) to the data member or the type alias to suppress this message. FloatVector v2b; ^ -./third_party/blink/renderer/discouraged_type.h:38:16: warning: [blink-style] 'std::vector' is discouraged for data members in blink renderer. Use WTF::Vector if possible. If the usage is necessary, add ALLOW_DISCOURAGED_TYPE(reason) to the data member or the type alias to suppress this message. +./third_party/blink/renderer/discouraged_type.h:38:16: warning: [blink-style] 'std::vector' is discouraged for data members in blink renderer. Use blink::Vector if possible. If the usage is necessary, add ALLOW_DISCOURAGED_TYPE(reason) to the data member or the type alias to suppress this message. FloatVector2 v2c; ^ -./third_party/blink/renderer/discouraged_type.h:41:21: warning: [blink-style] 'std::vector' is discouraged for data members in blink renderer. Use WTF::Vector if possible. If the usage is necessary, add ALLOW_DISCOURAGED_TYPE(reason) to the data member or the type alias to suppress this message. +./third_party/blink/renderer/discouraged_type.h:41:21: warning: [blink-style] 'std::vector' is discouraged for data members in blink renderer. Use blink::Vector if possible. If the usage is necessary, add ALLOW_DISCOURAGED_TYPE(reason) to the data member or the type alias to suppress this message. std::vector<char> v_array[4][4]; ^ -./third_party/blink/renderer/discouraged_type.h:73:22: warning: [blink-style] 'std::vector' is discouraged for data members in blink renderer. Use WTF::Vector if possible. If the usage is necessary, add ALLOW_DISCOURAGED_TYPE(reason) to the data member or the type alias to suppress this message. +./third_party/blink/renderer/discouraged_type.h:73:22: warning: [blink-style] 'std::vector' is discouraged for data members in blink renderer. Use blink::Vector if possible. If the usage is necessary, add ALLOW_DISCOURAGED_TYPE(reason) to the data member or the type alias to suppress this message. std::vector<int> v; ^ -./third_party/blink/renderer/discouraged_type.h:82:18: warning: [blink-style] 'std::vector' is discouraged for data members in blink renderer. Use WTF::Vector if possible. If the usage is necessary, add ALLOW_DISCOURAGED_TYPE(reason) to the data member or the type alias to suppress this message. +./third_party/blink/renderer/discouraged_type.h:82:18: warning: [blink-style] 'std::vector' is discouraged for data members in blink renderer. Use blink::Vector if possible. If the usage is necessary, add ALLOW_DISCOURAGED_TYPE(reason) to the data member or the type alias to suppress this message. std::vector<T> v1; ^ 7 warnings generated.
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py index a0ca433..652df01 100755 --- a/tools/clang/scripts/update.py +++ b/tools/clang/scripts/update.py
@@ -40,7 +40,7 @@ # should not be changed manually. # They are also read by build/config/compiler/BUILD.gn. CLANG_REVISION = 'llvmorg-21-init-16348-gbd809ffb' -CLANG_SUB_REVISION = 13 +CLANG_SUB_REVISION = 14 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION) # TODO(crbug.com/432036065): Bump to 22 in next Clang roll.
diff --git a/tools/jj/README.md b/tools/jj/README.md index a4294ee..70250d3f 100644 --- a/tools/jj/README.md +++ b/tools/jj/README.md
@@ -24,7 +24,7 @@ when you switch between submitted commits), you will need to run `gclient sync`. ### Syncing code -Currently manual and nontrivial +`jj sync` (using the config in `tools/jj/config.md`) ### Uploading code Currently manual (`git cl upload`) @@ -33,4 +33,4 @@ Currently manual (`git cl presubmit`) ### Running formatters -Currently manual (`git cl format`) +`jj fix` (using the config in `tools/jj/config.md`)
diff --git a/tools/jj/config.toml b/tools/jj/config.toml index c19a0f9..db149e8a 100644 --- a/tools/jj/config.toml +++ b/tools/jj/config.toml
@@ -1,5 +1,13 @@ # See https://jj-vcs.github.io/jj/latest/config/ for the schema. +[aliases] +# cr-* aliases are the equivalent of "porcelain" commands and should not +# be overridden. +# All cr-foo commands will have a corresponding foo command that you can +# override with your preference (eg. sync -> ["cr-sync", "-a"]) +"cr-sync" = ["util", "exec", "tools/jj/sync.py", "--"] +"sync" = ["cr-sync"] + [revset-aliases] "trunk()" = "main@origin" "immutable_heads()" = "remote_bookmarks() | tags()"
diff --git a/tools/jj/sync.py b/tools/jj/sync.py new file mode 100755 index 0000000..38a2ed69 --- /dev/null +++ b/tools/jj/sync.py
@@ -0,0 +1,77 @@ +#!/usr/bin/env python + +# 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 argparse +import logging +from util import jj_log +from util import run_command + + +def _fetch(shallow: bool) -> None: + args = ['git', 'fetch', 'origin', 'main'] + if shallow: + # Do something similar to a shallow clone with depth 2 + # For rationale, see: + # https://stackoverflow.com/questions/66431436/pushing-to-github-after-a-shallow-clone-is-horribly-slow + history_limit = jj_log(revisions='parents(fork_point(parents(mutable())))', + templates={'commit_id': 'commit_id'}, + ignore_working_copy=True) + assert len(history_limit) == 1 + history_limit = history_limit[0]['commit_id'] + args.append(f'--shallow_exclude={history_limit}') + run_command(args) + + +def main(args): + logging.basicConfig(level=logging.getLevelNamesMapping()[args.verbosity]) + + _fetch(args.shallow) + + logging.info('Rebasing onto main@origin') + rebase_source = 'mutable()' if args.all else '@' + run_command( + ['jj', 'rebase', '-b', rebase_source, '-d', 'trunk()', '--skip-emptied']) + # Skip-emptied with merge commits can produce weird shapes. + run_command( + ['jj', '--ignore-working-copy', 'simplify-parents', '-r', 'mutable()']) + + while True: + # This can fail if you've changed third-party repos. Since git fetch can be + # quite slow, we make this step able to retry on failure. + logging.info('Running gclient sync') + ps = run_command(['gclient', 'sync', '-D'], check=False) + if ps.returncode == 0: + break + else: + input('press control-C to exit, or enter to retry gclient sync') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument( + '--verbosity', + help='Verbosity of logging', + default='INFO', + choices=logging.getLevelNamesMapping().keys(), + type=lambda x: x.upper(), + ) + + parser.add_argument( + '-a', + '--all', + help='Rebases all local changes onto head', + action='store_true', + ) + + parser.add_argument( + '-s', + '--shallow', + help= + 'Garbage-collects all commits before the common ancestor of all mutable commits', + action='store_true', + ) + + main(parser.parse_args())
diff --git a/tools/jj/util.py b/tools/jj/util.py new file mode 100644 index 0000000..1a32cba --- /dev/null +++ b/tools/jj/util.py
@@ -0,0 +1,72 @@ +# 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 logging +import subprocess + +# CSV-file like separators. The templating language doesn't support escaping, +# so we use +# https://en.wikipedia.org/wiki/C0_and_C1_control_codes#Field_separators +_NEWLINE = '\x1e' +_COMMA = '\x1f' + + +def run_command(args: list[str], + check=True, + **kwargs) -> subprocess.CompletedProcess: + logging.debug('Running command %s', ' '.join(map(str, args))) + ps = subprocess.run(args, **kwargs, check=False) + if check and ps.returncode: + # Don't create a stack trace. + exit(ps.returncode) + return ps + + +def _log(args: list[str], templates: dict[str, str], + ignore_working_copy: bool) -> list[dict[str, str]]: + """Log acts akin to a database query on a table. + + The user will provide templates such as { + 'change_id': 'change_id', + 'parents': 'parents.map(|c| c.change_id())', + } + + And a set of revisions to lookup (eg. 'a|b'). + + And it would then return [ + {'change_id': 'a', 'parents': '<parent of a's id>'} + {'change_id': 'b', 'parents': '<parent of b's id>'} + ] + """ + # Start by assigning indexes based on the field name. + fields, templates = zip(*sorted(templates.items())) + # We're just creating a jj template that outputs CSV files. + template = f' ++ "{_COMMA}" ++ '.join(templates) + template += f' ++ "{_NEWLINE}"' + if ignore_working_copy: + args.append('--ignore-working-copy') + + stdout = run_command( + ['jj', *args, '--no-pager', '--no-graph', '-T', template], + stdout=subprocess.PIPE, + text=True, + ).stdout + + # Now we parse our CSV file. + return [{ + field: value + for field, value in zip(fields, change.split(_COMMA)) + } for change in stdout.rstrip(_NEWLINE).split(_NEWLINE)] + + +def jj_log(*, + templates: dict[str, str], + revisions='@', + ignore_working_copy=False) -> list[dict[str, str]]: + """Retrieves information about jj revisions. + + See _log for details.""" + return _log(['log', '-r', revisions], + templates, + ignore_working_copy=ignore_working_copy)
diff --git a/tools/mac/icons/compile_car.py b/tools/mac/icons/compile_car.py index e2ff598..b5fe5c93 100755 --- a/tools/mac/icons/compile_car.py +++ b/tools/mac/icons/compile_car.py
@@ -5,12 +5,12 @@ # found in the LICENSE file. """A script to compile asset catalogs into .car files for Chromium Mac. -The Chromium build system has the ability to automatically compile -`.xcassets` files into `.car` files and include them into outputs; see +The Chromium build system has the ability to automatically compile `.xcassets` +and `.icon` files into `.car` files and include them into outputs; see //build/toolchain/apple/compile_xcassets.py and https://gn.googlesource.com/gn/+/main/docs/reference.md#func_bundle_data. -However, `actool` is Mac-only, and has a wild amount of dependencies on the -rest of the Xcode package (`ibtoold` specifically, which has 70+ different +However, `actool` is Mac-only, and has a wild amount of dependencies on the rest +of the Xcode package (`ibtoold` specifically, which has 70+ different dependencies, mostly frameworks), so pre-compiled `.car` files are checked in instead. @@ -44,26 +44,9 @@ return '.'.join((str(x) for x in version)) -_REQUIRED_OS_VERSION = _split_version('15.5') _REQUIRED_ACTOOL_VERSION = _split_version('26.0') -def _verify_os_version() -> None: - """Verifies that the OS being used is suitably recent. - - Raises: - AssetCatalogException: If the OS is too old - """ - version = _split_version(platform.mac_ver()[0]) - - if version < _REQUIRED_OS_VERSION: - raise AssetCatalogException( - 'OS is too old; it is version ' - f'{_unsplit_version(version)} but at least version ' - f'{_unsplit_version(_REQUIRED_OS_VERSION)} is ' - 'required') - - def _verify_actool_version() -> None: """Verifies that the `actool` being used is suitably recent. @@ -105,13 +88,13 @@ def _process_path(path: pathlib.Path, min_deployment_target: str, verbose: bool) -> None: - """Compiles a single .xcassets directory into a .car file. + """Compiles a single `.xcassets` directory and `.icon` into a .car file. - This function invokes `actool` to compile the given asset catalog. It - handles the output from `actool`, checks for errors and unexpected - warnings/notices, and copies the resulting `Assets.car` file to the same - directory as the input `.xcassets` directory, with a name derived from the - input path. + This function invokes `actool` to compile the given `.xcassets` directory + and parallel .icon file. It handles the output from `actool`, checks for + errors and unexpected warnings/notices, and copies the resulting `app.icns` + and `Assets.car` files to the same directory as the input `.xcassets` + directory, with names derived from the input path. Args: path: A pathlib.Path object to the .xcassets directory to process. @@ -120,7 +103,7 @@ Raises: ValueError: If the asset catalog's path is incorrect in format. - AssetCatalogException: If `actool` reports errors, or behaved in a way + AssetCatalogException: If `actool` reported errors, or behaved in a way that was unexpected. """ with tempfile.TemporaryDirectory() as tmp_dir: @@ -137,6 +120,14 @@ name_tag = f'_{name_parts[1]}' if len(name_parts) == 2 else '' source_dir = path.joinpath(os.pardir) + # The app icon is copied into the .car file under the name that it has + # when given to actool, so make a copy in the temp directory to ensure + # it has the correct name. `shutil.copytree` is used as .icon "files" + # are really packages. + appicon_original_path = source_dir.joinpath(f'AppIcon{name_tag}.icon') + appicon_tmp_path = tmp_dir.joinpath('AppIcon.icon') + shutil.copytree(appicon_original_path, appicon_tmp_path) + command = [ # The binary. 'xcrun', @@ -160,6 +151,18 @@ # file found in various places inside the Xcode package. '--lightweight-asset-runtime-mode=enabled', + # Correctness. This command-line argument is undocumented. By + # default, if an `.icon` file is provided to `actool`, then `actool` + # will ignore any corresponding fallback bitmaps in the provided + # `.xcassets` directory, and generate its own. However, `actool` + # only generates 1x fallback bitmaps, which causes blurry icons to + # appear on 2x screens on macOS releases prior to macOS 26, which is + # undesirable (FB19028379). Given that there already are hand- + # crafted icon bitmaps available in the `.xcassets` directory, stop + # `actool` from generating its own, and have it use the available + # ones instead. + '--enable-icon-stack-fallback-generation=disabled', + # Target information. '--app-icon=AppIcon', f'--minimum-deployment-target={min_deployment_target}', @@ -169,7 +172,8 @@ f'--compile={tmp_dir}', # What to compile. - path + path, + appicon_tmp_path, ] if verbose: print(f' Invoking: {" ".join((str(item) for item in command))}') @@ -203,7 +207,7 @@ 'warnings') # Some warnings are expected, so swallow those. Raise all others. # TODO(avi): Remove the "ambiguous content" warning exception when - # moving to .icon files. + # switching to `actool`-generated fallback bitmaps. collect_failures( output_dict, 'com.apple.actool.document.warnings', failures, 'document warnings', lambda warnings: [ @@ -244,15 +248,10 @@ # the required information. pass elif output_file.name == 'AppIcon.icns': - # For now, ignore the generated icon file in favor of keeping - # the existing hand-crafted icns file. - # - # TODO(avi): When moving to .icon files, uncomment. - # - # destination_path = source_dir.joinpath( f'app{name_tag}.icns') - # if verbose: - # print(f' Copying output to: {destination_path}') - # shutil.copyfile(output_file, destination_path) + destination_path = source_dir.joinpath(f'app{name_tag}.icns') + if verbose: + print(f' Copying output to: {destination_path}') + shutil.copyfile(output_file, destination_path) pass elif output_file.name == 'Assets.car': destination_path = source_dir.joinpath(f'Assets{name_tag}.car') @@ -265,7 +264,6 @@ def main(args: list[str]): - _verify_os_version() _verify_actool_version() parser = argparse.ArgumentParser()
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index d6b69721..307ef31 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -8460,6 +8460,7 @@ label="AutofillSaveCreditCardUsesStrikeSystemV2:disabled"/> <int value="-2060484800" label="AutofillRequireCvcForPossibleCardUpdate:enabled"/> + <int value="-2060145957" label="DrawChromePagesEdgeToEdge:disabled"/> <int value="-2059771509" label="NTPTilesLowerResolutionFavicons:disabled"/> <int value="-2059529213" label="ConchSystemAudioFromMic:enabled"/> <int value="-2059331227" label="DiceWebSigninInterception:disabled"/> @@ -11564,6 +11565,7 @@ <int value="-940390151" label="RoundedDisplay:disabled"/> <int value="-939676447" label="CrostiniResetLxdDb:enabled"/> <int value="-939519091" label="TabStateFlatBuffer:disabled"/> + <int value="-938463322" label="DrawChromePagesEdgeToEdge:enabled"/> <int value="-938457958" label="CaptionsOnBrailleDisplay:disabled"/> <int value="-938178614" label="enable-suggestions-with-substring-match"/> <int value="-937430451" label="IntensiveWakeUpThrottling:disabled"/>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml index 02a3580..4ccd5fd9 100644 --- a/tools/metrics/histograms/metadata/android/histograms.xml +++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -4358,7 +4358,7 @@ </histogram> <histogram name="Android.Pdf.AssistContent.IsEnterpriseInfoCached" - enum="Boolean" expires_after="2025-09-07"> + enum="Boolean" expires_after="2026-01-11"> <owner>shuyng@google.com</owner> <owner>clank-large-form-factors@google.com</owner> <summary> @@ -4368,7 +4368,7 @@ </histogram> <histogram name="Android.Pdf.AssistContent.IsWorkProfile" enum="Boolean" - expires_after="2025-09-07"> + expires_after="2026-01-11"> <owner>shuyng@google.com</owner> <owner>clank-large-form-factors@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml index 21d0cfd..01965f0 100644 --- a/tools/metrics/histograms/metadata/arc/histograms.xml +++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -830,7 +830,7 @@ </histogram> <histogram name="Arc.Auth.RequestAccountInfoResult.Primary" - enum="ArcAuthCodeStatus" expires_after="2025-09-11"> + enum="ArcAuthCodeStatus" expires_after="2026-07-28"> <owner>jhorwich@google.com</owner> <owner>arc-core@google.com</owner> <summary> @@ -841,7 +841,7 @@ </histogram> <histogram name="Arc.Auth.RequestAccountInfoResult.Secondary" - enum="ArcAuthCodeStatus" expires_after="2025-09-11"> + enum="ArcAuthCodeStatus" expires_after="2026-07-28"> <owner>jhorwich@google.com</owner> <owner>arc-core@google.com</owner> <summary> @@ -899,7 +899,7 @@ </histogram> <histogram name="Arc.ContainerLifetimeEvent" enum="ArcContainerLifetimeEvent" - expires_after="2025-09-01"> + expires_after="2026-09-01"> <owner>khmel@google.com</owner> <owner>arc-core@google.com</owner> <summary> @@ -1007,7 +1007,7 @@ </histogram> <histogram name="Arc.DataRemoved.Success" enum="Boolean" - expires_after="2025-09-01"> + expires_after="2026-09-01"> <owner>khmel@google.com</owner> <owner>arc-core@google.com</owner> <summary> @@ -1019,7 +1019,7 @@ </histogram> <histogram name="Arc.DataRestore.Duration" units="ms" - expires_after="2025-09-01"> + expires_after="2026-09-01"> <owner>khmel@google.com</owner> <owner>arc-core@google.com</owner> <summary> @@ -1029,7 +1029,7 @@ </histogram> <histogram name="Arc.DataRestore.Status" enum="ArcDataRestoreStatus" - expires_after="2025-09-01"> + expires_after="2026-09-01"> <owner>khmel@google.com</owner> <owner>arc-core@google.com</owner> <summary> @@ -1767,7 +1767,7 @@ </histogram> <histogram name="Arc.PlayAutoInstallRequest.State{ArcUserTypes}" - enum="ArcPlayAutoInstallRequestState" expires_after="2025-09-01"> + enum="ArcPlayAutoInstallRequestState" expires_after="2026-09-01"> <owner>jhorwich@google.com</owner> <owner>khmel@google.com</owner> <owner>arc-core@google.com</owner> @@ -1783,7 +1783,7 @@ </histogram> <histogram name="Arc.PlayAutoInstallRequest.TimeDelta{ArcUserTypes}" units="ms" - expires_after="2025-09-01"> + expires_after="2026-09-01"> <owner>jhorwich@google.com</owner> <owner>khmel@google.com</owner> <owner>arc-core@google.com</owner> @@ -2077,7 +2077,7 @@ </histogram> <histogram name="Arc.Reauthorization.Result{ArcUserTypes}" - enum="ArcProvisioningStatus" expires_after="2025-09-11"> + enum="ArcProvisioningStatus" expires_after="2026-09-11"> <owner>jhorwich@google.com</owner> <owner>arc-core@gmail.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml index 1c00094..32906e6 100644 --- a/tools/metrics/histograms/metadata/ash/histograms.xml +++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -8257,7 +8257,7 @@ </histogram> <histogram name="Ash.ShortcutCustomization.CustomizationsBeforeResetAll" - units="count" expires_after="2025-08-10"> + units="count" expires_after="2026-08-10"> <owner>jimmyxgong@chromium.org</owner> <owner>cros-device-enablement@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/autofill/enums.xml b/tools/metrics/histograms/metadata/autofill/enums.xml index 5876880..819422c 100644 --- a/tools/metrics/histograms/metadata/autofill/enums.xml +++ b/tools/metrics/histograms/metadata/autofill/enums.xml
@@ -522,11 +522,6 @@ <int value="3840" label="COMPANY_NAME: Not autofilled or autofilled value edited"/> <int value="3841" label="COMPANY_NAME: Autofilled value accepted"/> - <int value="3904" - label="FIELD_WITH_DEFAULT_VALUE: Not autofilled or autofilled value - edited"/> - <int value="3905" - label="FIELD_WITH_DEFAULT_VALUE: Autofilled value accepted"/> <int value="4672" label="MERCHANT_EMAIL_SIGNUP: Not autofilled or autofilled value edited"/> <int value="4673" label="MERCHANT_EMAIL_SIGNUP: Autofilled value accepted"/> @@ -1026,8 +1021,6 @@ <int value="945" label="CREDIT_CARD_VERIFICATION_CODE: accepted"/> <int value="960" label="COMPANY_NAME: edited"/> <int value="961" label="COMPANY_NAME: accepted"/> - <int value="976" label="FIELD_WITH_DEFAULT_VALUE: edited"/> - <int value="977" label="FIELD_WITH_DEFAULT_VALUE: accepted"/> <int value="1168" label="MERCHANT_EMAIL_SIGNUP: edited"/> <int value="1169" label="MERCHANT_EMAIL_SIGNUP: accepted"/> <int value="1184" label="MERCHANT_PROMO_CODE: edited"/> @@ -1995,7 +1988,6 @@ <int value="58" label="CREDIT_CARD_TYPE"/> <int value="59" label="CREDIT_CARD_VERIFICATION_CODE"/> <int value="60" label="COMPANY_NAME"/> - <int value="61" label="FIELD_WITH_DEFAULT_VALUE"/> <int value="73" label="MERCHANT_EMAIL_SIGNUP"/> <int value="74" label="MERCHANT_PROMO_CODE"/> <int value="75" label="PASSWORD"/> @@ -3570,8 +3562,6 @@ <int value="237" label="CREDIT_CARD_VERIFICATION_CODE: Accepted"/> <int value="240" label="COMPANY_NAME: Ignored"/> <int value="241" label="COMPANY_NAME: Accepted"/> - <int value="244" label="FIELD_WITH_DEFAULT_VALUE: Ignored"/> - <int value="245" label="FIELD_WITH_DEFAULT_VALUE: Accepted"/> <int value="292" label="MERCHANT_EMAIL_SIGNUP: Ignored"/> <int value="293" label="MERCHANT_EMAIL_SIGNUP: Accepted"/> <int value="296" label="MERCHANT_PROMO_CODE: Ignored"/>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml index 91d29761..76f9598 100644 --- a/tools/metrics/histograms/metadata/autofill/histograms.xml +++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -475,7 +475,6 @@ <variant name="EMAIL_ADDRESS"/> <variant name="EMAIL_OR_LOYALTY_MEMBERSHIP_ID"/> <variant name="EMPTY_TYPE"/> - <variant name="FIELD_WITH_DEFAULT_VALUE"/> <variant name="IBAN_VALUE"/> <variant name="LOYALTY_MEMBERSHIP_ID"/> <variant name="LOYALTY_MEMBERSHIP_PROGRAM"/>
diff --git a/tools/metrics/histograms/metadata/blink/enums.xml b/tools/metrics/histograms/metadata/blink/enums.xml index 2bdfc626..e380bf94 100644 --- a/tools/metrics/histograms/metadata/blink/enums.xml +++ b/tools/metrics/histograms/metadata/blink/enums.xml
@@ -8363,6 +8363,7 @@ <int value="372" label="WasmSignExtensionOperators"/> <int value="373" label="DRAFT_IncomingCallNotifications"/> <int value="374" label="Popover"/> + <int value="375" label="DRAFT_WebInstallAPI"/> </enum> <!-- LINT.ThenChange(//third_party/blink/public/mojom/use_counter/metrics/webdx_feature.mojom:WebDXFeature) -->
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml index 49f016d..a60faa5 100644 --- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml +++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -131,7 +131,7 @@ </histogram> <histogram name="CustomTabs.AuthTab.ExternalNavigationScheme" - enum="CustomTabsAuthScheme" expires_after="2025-08-24"> + enum="CustomTabsAuthScheme" expires_after="2026-06-22"> <owner>sinansahin@google.com</owner> <owner>chrome-connective-tissue@google.com</owner> <summary> @@ -142,7 +142,7 @@ </histogram> <histogram name="CustomTabs.AuthTab.RedirectUriScheme" - enum="CustomTabsAuthScheme" expires_after="2025-08-24"> + enum="CustomTabsAuthScheme" expires_after="2026-06-22"> <owner>sinansahin@google.com</owner> <owner>chrome-connective-tissue@google.com</owner> <summary> @@ -153,7 +153,7 @@ </histogram> <histogram name="CustomTabs.AuthTab.TimeToDalVerification.SinceFlowCompletion" - units="ms" expires_after="2025-06-22"> + units="ms" expires_after="2026-06-22"> <owner>sinansahin@google.com</owner> <owner>chrome-connective-tissue@google.com</owner> <summary> @@ -166,7 +166,7 @@ </histogram> <histogram name="CustomTabs.AuthTab.TimeToDalVerification.SinceStart" - units="ms" expires_after="2025-06-22"> + units="ms" expires_after="2026-06-22"> <owner>sinansahin@google.com</owner> <owner>chrome-connective-tissue@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/extensions/enums.xml b/tools/metrics/histograms/metadata/extensions/enums.xml index f37f98aa..d3e7b6a 100644 --- a/tools/metrics/histograms/metadata/extensions/enums.xml +++ b/tools/metrics/histograms/metadata/extensions/enums.xml
@@ -2830,6 +2830,7 @@ <int value="1942" label="EXPERIMENTALACTOR_PERFORMACTIONS"/> <int value="1943" label="EXPERIMENTALACTOR_CREATETASK"/> <int value="1944" label="EXPERIMENTALACTOR_REQUESTTABOBSERVATION"/> + <int value="1945" label="PDFVIEWERPRIVATE_SAVETODRIVE"/> </enum> <!-- LINT.ThenChange(//extensions/browser/extension_function_histogram_value.h:HistogramValue) -->
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml index 6e71052..4435e61 100644 --- a/tools/metrics/histograms/metadata/ios/histograms.xml +++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -2093,6 +2093,25 @@ </summary> </histogram> +<histogram name="IOS.Gemini.StartupTime.{FirstRunBool}" units="ms" + expires_after="2026-07-21"> + <owner>nicolasmacbeth@google.com</owner> + <owner>bling-alchemy-eng@google.com</owner> + <summary> + The latency between starting the preparation of presenting the Gemini + overlay and actually presenting it. Includes the time to extract + PageContext. Logged after the overlay is presented. + </summary> + <token key="FirstRunBool"> + <variant name="FirstRun" + summary="User went from entrypoint, to BWG FRE, to BWG overlay. + Includes the time it took for the user to go through the FRE"/> + <variant name="NotFirstRun" + summary="User went directly from the entrypoint to the BWG overlay. + Does not include FRE time"/> + </token> +</histogram> + <histogram name="IOS.GoogleOne.Outcome.{EntryPoint}" enum="GoogleOneOutcome" expires_after="2026-03-12"> <owner>olivierrobin@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/privacy/histograms.xml b/tools/metrics/histograms/metadata/privacy/histograms.xml index 590d2061..908f958 100644 --- a/tools/metrics/histograms/metadata/privacy/histograms.xml +++ b/tools/metrics/histograms/metadata/privacy/histograms.xml
@@ -819,7 +819,7 @@ <histogram name="PrivacySandbox.AggregationService.KeyFetcher.HttpResponseOrNetErrorCode" - enum="CombinedHttpResponseAndNetErrorCode" expires_after="2025-07-13"> + enum="CombinedHttpResponseAndNetErrorCode" expires_after="2026-08-01"> <owner>alexmt@chromium.org</owner> <owner>linnan@chromium.org</owner> <summary> @@ -1543,9 +1543,9 @@ <histogram name="PrivacySandbox.PrivateAggregation.Budgeter.EnoughBudgetForAnyValueIfNotEnoughOverall" - enum="Boolean" expires_after="2025-08-10"> + enum="Boolean" expires_after="2026-08-01"> <owner>alexmt@chromium.org</owner> - <owner>dmcardle@chromium.org</owner> + <owner>linnan@chromium.org</owner> <summary> When the result of a request to consume budget for the Private Aggregation API fails due to insufficient budget, records whether there was still @@ -1561,9 +1561,9 @@ </histogram> <histogram name="PrivacySandbox.PrivateAggregation.Budgeter.LockHoldDuration" - units="ms" expires_after="2025-08-25"> + units="ms" expires_after="2026-08-01"> <owner>alexmt@chromium.org</owner> - <owner>dmcardle@chromium.org</owner> + <owner>linnan@chromium.org</owner> <owner>measurement-api-dev+metrics@google.com</owner> <summary> Records the duration that the budgeter was locked. When the budgeter is
diff --git a/tools/metrics/histograms/metadata/tab/histograms.xml b/tools/metrics/histograms/metadata/tab/histograms.xml index 40efa573..bbf1bca5 100644 --- a/tools/metrics/histograms/metadata/tab/histograms.xml +++ b/tools/metrics/histograms/metadata/tab/histograms.xml
@@ -2090,7 +2090,7 @@ </histogram> <histogram name="Tabs.ArchivedTabs.MaxLimitReachedAt" units="count" - expires_after="2025-09-07"> + expires_after="2026-01-11"> <owner>wylieb@google.com</owner> <owner>clank-tab-dev@google.com</owner> <summary> @@ -2124,7 +2124,7 @@ </histogram> <histogram name="Tabs.ArchiveSettings.AutoDeleteEnabled" enum="Boolean" - expires_after="2025-09-07"> + expires_after="2026-01-11"> <owner>wylieb@google.com</owner> <owner>clank-tab-dev@google.com</owner> <summary> @@ -2204,7 +2204,7 @@ </histogram> <histogram name="Tabs.DeleteWithPTD.DurationMs" units="ms" - expires_after="2025-09-07"> + expires_after="2026-01-11"> <owner>wylieb@google.com</owner> <owner>clank-tab-dev@google.com</owner> <summary> @@ -2302,7 +2302,7 @@ </histogram> <histogram name="Tabs.InitializePTD.DurationMs" units="ms" - expires_after="2025-09-07"> + expires_after="2026-01-11"> <owner>wylieb@google.com</owner> <owner>clank-tab-dev@google.com</owner> <summary> @@ -2374,6 +2374,19 @@ <token key="BatteryState" variants="BatteryState"/> </histogram> +<histogram name="Tabs.Metadata.FileSizeOnRead.{TabModelSelectorType}" + units="KB" expires_after="2026-07-28"> + <owner>skym@chromium.org</owner> + <owner>clank-tab-dev@google.com</owner> + <summary> + Records the size of the tab list metadata file that's loaded on tab model + creation split out by client type. This is for the regular tab model, not + the incognito. Additionally this file may be loaded more times for various + things such as clean up and merging, these loads are all ignored. Clients + such as headless will still report with their selector type. + </summary> +</histogram> + <histogram name="Tabs.MissingWebStateStorageFileOnSessionRestore" enum="Boolean" expires_after="2026-03-20"> <owner>justincohen@google.com</owner> @@ -2947,7 +2960,7 @@ </histogram> <histogram name="Tabs.TabArchived.FoundDuplicateInRegularModel" units="count" - expires_after="2025-09-07"> + expires_after="2026-01-11"> <owner>wylieb@google.com</owner> <owner>clank-tab-dev@google.com</owner> <summary> @@ -3670,7 +3683,7 @@ </histogram> <histogram name="Tabs.TabStateCleanupAbortedByArchive" enum="Boolean" - expires_after="2025-09-07"> + expires_after="2026-01-11"> <owner>wylieb@google.com</owner> <owner>clank-tab-dev@google.com</owner> <summary> @@ -3681,7 +3694,7 @@ </histogram> <histogram name="Tabs.TabThumbnailCleanupAbortedByArchive" enum="Boolean" - expires_after="2025-09-07"> + expires_after="2026-01-11"> <owner>wylieb@google.com</owner> <owner>clank-tab-dev@google.com</owner> <summary>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml index 4d4173d5..2e6dcf8 100644 --- a/tools/metrics/ukm/ukm.xml +++ b/tools/metrics/ukm/ukm.xml
@@ -22156,6 +22156,13 @@ or sum of a UMA/UKM metric. </summary> </metric> + <metric name="ActualResult10"> + <summary> + The 10th UMA or UKM metrics related to the features affected by the ML + model, with or without aggregation, after running the features as + instructed by the ML model. No encoding is used. + </summary> + </metric> <metric name="ActualResult2"> <summary> The 2nd UMA or UKM metrics related to the features affected by the ML @@ -22191,6 +22198,27 @@ instructed by the ML model. No encoding is used. </summary> </metric> + <metric name="ActualResult7"> + <summary> + The 7th UMA or UKM metrics related to the features affected by the ML + model, with or without aggregation, after running the features as + instructed by the ML model. No encoding is used. + </summary> + </metric> + <metric name="ActualResult8"> + <summary> + The 8th UMA or UKM metrics related to the features affected by the ML + model, with or without aggregation, after running the features as + instructed by the ML model. No encoding is used. + </summary> + </metric> + <metric name="ActualResult9"> + <summary> + The 9th UMA or UKM metrics related to the features affected by the ML + model, with or without aggregation, after running the features as + instructed by the ML model. No encoding is used. + </summary> + </metric> <metric name="Input0"> <summary> The input tensor at index 0 represented in IEEE 754 double precision.
diff --git a/tools/perf/contrib/vr_benchmarks/shared_vr_page_state.py b/tools/perf/contrib/vr_benchmarks/shared_vr_page_state.py index 5eb1bcb..0d7564b3 100644 --- a/tools/perf/contrib/vr_benchmarks/shared_vr_page_state.py +++ b/tools/perf/contrib/vr_benchmarks/shared_vr_page_state.py
@@ -2,11 +2,9 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import os from core import path_util path_util.AddAndroidPylibToPath() -from telemetry.core import android_platform -from telemetry.core import util +from devil.android.sdk import keyevent # pylint: disable=import-error from telemetry.page import shared_page_state from contrib.vr_benchmarks.desktop_runtimes import openxr_runtimes @@ -31,7 +29,7 @@ super(SharedVrPageStateFactory, self).__init__( test, finder_options, story_set, possible_browser) - if isinstance(self.platform, android_platform.AndroidPlatform): + if self.platform.GetOSName().lower() == 'android': self.__class__ = AndroidSharedVrPageState elif self.platform.GetOSName().lower() == 'win': self.__class__ = WindowsSharedVrPageState @@ -79,46 +77,32 @@ def __init__(self, test, finder_options, story_set, possible_browser=None): super(AndroidSharedVrPageState, self).__init__( test, finder_options, story_set, possible_browser) - self._InstallNfcApk() - - def _InstallNfcApk(self): - """Installs the APK that allows VR tests to simulate a headset NFC scan.""" - chromium_root = path_util.GetChromiumSrcDir() - # Find the most recently build APK - candidate_apks = [] - for build_path in util.GetBuildDirectories(chromium_root): - apk_path = os.path.join(build_path, 'apks', 'VrNfcSimulator.apk') - if os.path.exists(apk_path): - last_changed = os.path.getmtime(apk_path) - candidate_apks.append((last_changed, apk_path)) - - if not candidate_apks: - raise RuntimeError( - 'Could not find VrNfcSimulator.apk in a build output directory') - newest_apk_path = sorted(candidate_apks)[-1][1] - self.platform.InstallApplication( - os.path.join(chromium_root, newest_apk_path)) + self._orig_doff_screen_timeout_ms = None def WillRunStory(self, story): super(AndroidSharedVrPageState, self).WillRunStory(story) - if not self._finder_options.disable_screen_reset: - self._CycleScreen() - - def _CycleScreen(self): - """Cycles the screen off then on. - - This is because VR test devices are set to have normal screen brightness and - automatically turn off after several minutes instead of the usual approach - of having the screen always on at minimum brightness. This is due to the - motion-to-photon latency test being sensitive to screen brightness, and min - brightness does not work well for it. - - Simply using TurnScreenOn does not actually reset the timer for turning off - the screen, so instead cycle the screen to refresh it periodically. - """ - self.platform.android_action_runner.TurnScreenOff() + if self.platform.android_action_runner.IsXrDevice(): + # XR devices may keep screen off when not weared. + self._orig_doff_screen_timeout_ms = self.platform.android_action_runner.RunCommand( + 'settings get system doff_screen_timeout_ms').strip() + # The value should never be empty, and we don't know how to restore the + # empty value. + assert self._orig_doff_screen_timeout_ms + self.platform.android_action_runner.RunCommand( + 'settings put system doff_screen_timeout_ms 0') + self.platform.android_action_runner.InputKeyEvent(keyevent.KEYCODE_WAKEUP) self.platform.android_action_runner.TurnScreenOn() + def DidRunStory(self, results): + super(AndroidSharedVrPageState, self).DidRunStory(results) + if self.platform.android_action_runner.IsXrDevice(): + if self._orig_doff_screen_timeout_ms == 'null': + # This means the setting wasn't set. + cmd = 'settings delete system doff_screen_timeout_ms' + elif self._orig_doff_screen_timeout_ms: + cmd = f'settings put system doff_screen_timeout_ms {self._orig_doff_screen_timeout_ms}' + self.platform.android_action_runner.RunCommand(cmd) + def ShouldNavigateToBlankPageBeforeFinishing(self): # Android devices generate a lot of heat while in VR, so navigate away from # the VR page after we're done collecting data so that we aren't in VR while
diff --git a/tools/perf/contrib/vr_benchmarks/webxr_sample_pages.py b/tools/perf/contrib/vr_benchmarks/webxr_sample_pages.py index d89afc1..72f83a8 100644 --- a/tools/perf/contrib/vr_benchmarks/webxr_sample_pages.py +++ b/tools/perf/contrib/vr_benchmarks/webxr_sample_pages.py
@@ -5,6 +5,13 @@ from contrib.vr_benchmarks.vr_sample_page import XrSamplePage from contrib.vr_benchmarks.vr_story_set import VrStorySet +from core import path_util + +path_util.AddAndroidPylibToPath() +from devil.android import device_errors # pylint: disable=import-error + +import time + class WebXrSamplePage(XrSamplePage): @@ -21,7 +28,26 @@ # element's "title" attribute is currently always "Enter XR". action_runner.WaitForElement(selector='button[title="Enter XR"]') action_runner.TapElement(selector='button[title="Enter XR"]') + + if (self.platform.GetOSName().lower() == 'android' + and '--deny-permission-prompts' + not in action_runner.tab.browser.startup_args): + # Grant the Chrome permission for the site. + try: + app_ui = action_runner.tab.browser.GetAppUi() + allow_this_time_button = app_ui.WaitForUiNode( + timeout=10, retries=1, content_desc='Allow this time') + allow_this_time_button.Tap() + except device_errors.CommandTimeoutError: + # It is possible that the permission has been granted in this browser + # session. + pass + action_runner.MeasureMemory(True) + + # Keep the page in immersive mode for a while. + time.sleep(5) + # We don't want to be in VR or on a page with a WebGL canvas at the end of # the test, as this generates unnecessary heat while the trace data is being # processed, so navigate to a blank page if we're on a platform that cares
diff --git a/tools/typescript/definitions/autofill_private.d.ts b/tools/typescript/definitions/autofill_private.d.ts index 02363d2..cc782f46 100644 --- a/tools/typescript/definitions/autofill_private.d.ts +++ b/tools/typescript/definitions/autofill_private.d.ts
@@ -59,7 +59,6 @@ CREDIT_CARD_TYPE, CREDIT_CARD_VERIFICATION_CODE, COMPANY_NAME, - FIELD_WITH_DEFAULT_VALUE, MERCHANT_EMAIL_SIGNUP, MERCHANT_PROMO_CODE, PASSWORD,
diff --git a/tools/typescript/definitions/pdf_viewer_private.d.ts b/tools/typescript/definitions/pdf_viewer_private.d.ts index cc361e5..9a5c25d 100644 --- a/tools/typescript/definitions/pdf_viewer_private.d.ts +++ b/tools/typescript/definitions/pdf_viewer_private.d.ts
@@ -10,6 +10,16 @@ declare global { export namespace chrome { export namespace pdfViewerPrivate { + // Keep in sync with the values for enum `SaveRequestType` in + // `pdf/pdf_view_web_plugin.h` and + // `chrome/common/extensions/api/pdf_viewer_private.idl`. + export enum SaveRequestType { + ANNOTATION = 'ANNOTATION', + ORIGINAL = 'ORIGINAL', + EDITED = 'EDITED', + SEARCHIFIED = 'SEARCHIFIED', + } + // `mimeType` and `responseHeaders` are unused fields, but they are // necessary to be able to cast to chrome.mimeHandlerPrivate.StreamInfo. // TODO(crbug.com/40268279): Remove `mimeType` and `responseHeaders` after @@ -31,6 +41,9 @@ export function getStreamInfo(callback: (info: StreamInfo) => void): void; export function isAllowedLocalFileAccess( url: string, callback: (isAllowed: boolean) => void): void; + // <if expr="enable_pdf_save_to_drive"> + export function saveToDrive(saveRequestType: SaveRequestType): void; + // </if> export function setPdfDocumentTitle(title: string): void; export function setPdfPluginAttributes(attributes: PdfPluginAttributes): void;
diff --git a/ui/gfx/color_space.cc b/ui/gfx/color_space.cc index 02892e604..bfb59ff 100644 --- a/ui/gfx/color_space.cc +++ b/ui/gfx/color_space.cc
@@ -93,7 +93,10 @@ SetCustomPrimaries(*custom_primary_matrix); } if (custom_transfer_fn) { - SetCustomTransferFunction(*custom_transfer_fn); + DCHECK(transfer_ == TransferID::CUSTOM || + transfer_ == TransferID::CUSTOM_HDR); + SetCustomTransferFunction(*custom_transfer_fn, + transfer_ == TransferID::CUSTOM_HDR); } } @@ -104,8 +107,7 @@ RangeID::FULL) { skcms_TransferFunction fn; if (sk_color_space.isNumericalTransferFn(&fn)) { - transfer_ = is_hdr ? TransferID::CUSTOM_HDR : TransferID::CUSTOM; - SetCustomTransferFunction(fn); + SetCustomTransferFunction(fn, is_hdr); } else if (skcms_TransferFunction_isHLGish(&fn)) { transfer_ = TransferID::HLG; } else if (skcms_TransferFunction_isPQish(&fn)) { @@ -194,10 +196,8 @@ primaries_ = PrimaryID::CUSTOM; } -void ColorSpace::SetCustomTransferFunction(const skcms_TransferFunction& fn) { - DCHECK(transfer_ == TransferID::CUSTOM || - transfer_ == TransferID::CUSTOM_HDR); - +void ColorSpace::SetCustomTransferFunction(const skcms_TransferFunction& fn, + bool is_hdr) { auto check_transfer_fn = [this, &fn](TransferID id) { skcms_TransferFunction id_fn; GetTransferFunction(id, &id_fn); @@ -208,6 +208,8 @@ return true; }; + transfer_ = is_hdr ? TransferID::CUSTOM_HDR : TransferID::CUSTOM; + if (transfer_ == TransferID::CUSTOM) { // These are all TransferIDs that will return a transfer function from // GetTransferFunction. When multiple ids map to the same function, this @@ -595,6 +597,30 @@ return result; } +ColorSpace ColorSpace::GetAsHDR() const { + ColorSpace result = *this; + skcms_TransferFunction fn; + if (result.GetTransferFunction(&fn)) { + result.SetCustomTransferFunction(fn, /*is_hdr=*/true); + } + return result; +} + +ColorSpace ColorSpace::GetWithTransferFunction(TransferID transfer) const { + DCHECK_NE(transfer, TransferID::CUSTOM); + DCHECK_NE(transfer, TransferID::CUSTOM_HDR); + ColorSpace result(*this); + result.transfer_ = transfer; + return result; +} + +ColorSpace ColorSpace::GetWithTransferFunction(const skcms_TransferFunction& fn, + bool is_hdr) const { + ColorSpace result(*this); + result.SetCustomTransferFunction(fn, is_hdr); + return result; +} + ColorSpace ColorSpace::GetWithSdrWhiteLevel(float sdr_white_level) const { if (!IsAffectedBySDRWhiteLevel()) return *this;
diff --git a/ui/gfx/color_space.h b/ui/gfx/color_space.h index 13b68553..c5eceb8 100644 --- a/ui/gfx/color_space.h +++ b/ui/gfx/color_space.h
@@ -314,6 +314,15 @@ // the caller but replacing the matrix and range with the given values. ColorSpace GetWithMatrixAndRange(MatrixID matrix, RangeID range) const; + // Return this color space with an HDR version of its transfer function + // (if possible). + ColorSpace GetAsHDR() const; + + // Return this color space with the specified transfer function. + ColorSpace GetWithTransferFunction(TransferID) const; + ColorSpace GetWithTransferFunction(const skcms_TransferFunction&, + bool is_hdr) const; + // This will return nullptr for non-RGB spaces, spaces with non-FULL // range, unspecified spaces, and spaces that require but are not provided // and SDR white level. @@ -396,7 +405,7 @@ static bool GetTransferFunction(TransferID, skcms_TransferFunction* fn); static size_t TransferParamCount(TransferID); - void SetCustomTransferFunction(const skcms_TransferFunction& fn); + void SetCustomTransferFunction(const skcms_TransferFunction& fn, bool is_hdr); void SetCustomPrimaries(const skcms_Matrix3x3& to_XYZD50); PrimaryID primaries_ = PrimaryID::INVALID;
diff --git a/ui/gfx/color_space_unittest.cc b/ui/gfx/color_space_unittest.cc index daaf53a..42c8e28 100644 --- a/ui/gfx/color_space_unittest.cc +++ b/ui/gfx/color_space_unittest.cc
@@ -348,5 +348,34 @@ UNSAFE_TODO(EXPECT_EQ(memcmp(&in_m33, &out_m33, sizeof(in_m33)), 0)); } +TEST(ColorSpace, AsHDR) { + ColorSpace cs; + skcms_TransferFunction fn; + constexpr float kEpsilon = 0.00001f; + + cs = ColorSpace(ColorSpace::PrimaryID::P3, ColorSpace::TransferID::SRGB); + cs = cs.GetAsHDR(); + EXPECT_EQ(cs.GetTransferID(), ColorSpace::TransferID::SRGB_HDR); + + cs = ColorSpace(ColorSpace::PrimaryID::P3, ColorSpace::TransferID::LINEAR); + cs = cs.GetAsHDR(); + EXPECT_EQ(cs.GetTransferID(), ColorSpace::TransferID::LINEAR_HDR); + + cs = cs.GetWithTransferFunction(ColorSpace::TransferID::GAMMA22); + EXPECT_FALSE(cs.IsHDR()); + cs = cs.GetAsHDR(); + EXPECT_EQ(cs.GetTransferID(), ColorSpace::TransferID::CUSTOM_HDR); + EXPECT_TRUE(cs.GetTransferFunction(&fn)); + EXPECT_NEAR(fn.g, 2.2, kEpsilon); + + fn.a = 0.5; + fn.g = 2.5; + cs = cs.GetWithTransferFunction(fn, /*is_hdr=*/true); + EXPECT_EQ(cs.GetTransferID(), ColorSpace::TransferID::CUSTOM_HDR); + EXPECT_TRUE(cs.GetTransferFunction(&fn)); + EXPECT_NEAR(fn.a, 0.5, kEpsilon); + EXPECT_NEAR(fn.g, 2.5, kEpsilon); +} + } // namespace } // namespace gfx
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm index 984d62e0..7aa2d1a 100644 --- a/ui/views/widget/native_widget_mac.mm +++ b/ui/views/widget/native_widget_mac.mm
@@ -1195,6 +1195,7 @@ // parent's focus manager. However, this is not happening for unknown reasons. CHECK_EQ(focus_manager, focus_manager_); focus_manager->RemoveFocusChangeListener(this); + focus_manager_ = nullptr; } ui::EventDispatchDetails NativeWidgetMac::DispatchKeyEventPostIME(
diff --git a/ui/webui/resources/cr_elements/cr_tooltip/cr_tooltip.css b/ui/webui/resources/cr_elements/cr_tooltip/cr_tooltip.css index 86d68d4..34aa69a 100644 --- a/ui/webui/resources/cr_elements/cr_tooltip/cr_tooltip.css +++ b/ui/webui/resources/cr_elements/cr_tooltip/cr_tooltip.css
@@ -66,3 +66,37 @@ animation-duration: var(--paper-tooltip-duration-out, 500ms); animation-fill-mode: forwards; } + +/* Fills in the space created by the offset so that users can move their + * pointer towards the tooltip without having the tooltip disappear. */ +#tooltipOffsetFiller { + position: absolute; + + :host([position="top"]) & { + top: 100%; + } + + :host([position="bottom"]) & { + bottom: 100%; + } + + :host([position="left"]) & { + left: 100%; + } + + :host([position="right"]) & { + right: 100%; + } + + :host(:is([position="top"], [position="bottom"])) & { + left: 0; + height: var(--cr-tooltip-offset); + width: 100%; + } + + :host(:is([position="left"], [position="right"])) & { + top: 0; + height: 100%; + width: var(--cr-tooltip-offset); + } +}
diff --git a/ui/webui/resources/cr_elements/cr_tooltip/cr_tooltip.html.ts b/ui/webui/resources/cr_elements/cr_tooltip/cr_tooltip.html.ts index 8d492a0..601756a 100644 --- a/ui/webui/resources/cr_elements/cr_tooltip/cr_tooltip.html.ts +++ b/ui/webui/resources/cr_elements/cr_tooltip/cr_tooltip.html.ts
@@ -10,5 +10,6 @@ return html` <div id="tooltip" hidden part="tooltip"> <slot></slot> - </div>`; + </div> + <div id="tooltipOffsetFiller"></div>`; }
diff --git a/ui/webui/resources/cr_elements/cr_tooltip/cr_tooltip.ts b/ui/webui/resources/cr_elements/cr_tooltip/cr_tooltip.ts index ea8cf18..bd4eebf 100644 --- a/ui/webui/resources/cr_elements/cr_tooltip/cr_tooltip.ts +++ b/ui/webui/resources/cr_elements/cr_tooltip/cr_tooltip.ts
@@ -58,7 +58,7 @@ /** * Positions the tooltip to the top, right, bottom, left of its content. */ - position: {type: String}, + position: {type: String, reflect: true}, /** * If true, no parts of the tooltip will ever be shown offscreen. @@ -76,11 +76,18 @@ * played when showing the tooltip. */ animationDelay: {type: Number}, + + /** + * The delay before the tooltip hides itself after moving the pointer + * away from the tooltip or target. + */ + hideDelay: {type: Number}, }; } accessor animationDelay: number = 500; accessor fitToVisibleBounds: boolean = false; + accessor hideDelay: number = 600; accessor for: string = ''; accessor manualMode: boolean = false; accessor offset: number = 14; @@ -90,6 +97,7 @@ private manualTarget_?: Element; private target_: Element|null = null; private tracker_: EventTracker = new EventTracker(); + private hideTimeout_: number|null = null; override connectedCallback() { super.connectedCallback(); @@ -100,6 +108,7 @@ if (!this.manualMode) { this.removeListeners_(); } + this.resetHideTimeout_(); } override firstUpdated(changedProperties: PropertyValues<this>) { @@ -130,6 +139,10 @@ this.addListeners_(); } } + + if (changedProperties.has('offset')) { + this.style.setProperty('--cr-tooltip-offset', `${this.offset}px`); + } } /** @@ -167,6 +180,8 @@ * Shows the tooltip programmatically */ show() { + this.resetHideTimeout_(); + // If the tooltip is already showing, there's nothing to do. if (this.showing_) { return; @@ -217,6 +232,21 @@ this.animationPlaying_ = true; } + private queueHide_() { + this.resetHideTimeout_(); + this.hideTimeout_ = setTimeout(() => { + this.hide(); + this.hideTimeout_ = null; + }, this.hideDelay); + } + + private resetHideTimeout_() { + if (this.hideTimeout_ !== null) { + clearTimeout(this.hideTimeout_); + this.hideTimeout_ = null; + } + } + updatePosition() { if (!this.target_) { return; @@ -230,9 +260,9 @@ const offset = this.offset; const parentRect = offsetParent.getBoundingClientRect(); const targetRect = this.target_.getBoundingClientRect(); - const thisRect = this.getBoundingClientRect(); - const horizontalCenterOffset = (targetRect.width - thisRect.width) / 2; - const verticalCenterOffset = (targetRect.height - thisRect.height) / 2; + const tooltipRect = this.$.tooltip.getBoundingClientRect(); + const horizontalCenterOffset = (targetRect.width - tooltipRect.width) / 2; + const verticalCenterOffset = (targetRect.height - tooltipRect.height) / 2; const targetLeft = targetRect.left - parentRect.left; const targetTop = targetRect.top - parentRect.top; let tooltipLeft; @@ -240,14 +270,14 @@ switch (this.position) { case TooltipPosition.TOP: tooltipLeft = targetLeft + horizontalCenterOffset; - tooltipTop = targetTop - thisRect.height - offset; + tooltipTop = targetTop - tooltipRect.height - offset; break; case TooltipPosition.BOTTOM: tooltipLeft = targetLeft + horizontalCenterOffset; tooltipTop = targetTop + targetRect.height + offset; break; case TooltipPosition.LEFT: - tooltipLeft = targetLeft - thisRect.width - offset; + tooltipLeft = targetLeft - tooltipRect.width - offset; tooltipTop = targetTop + verticalCenterOffset; break; case TooltipPosition.RIGHT: @@ -257,7 +287,8 @@ } if (this.fitToVisibleBounds) { // Clip the left/right side - if (parentRect.left + tooltipLeft + thisRect.width > window.innerWidth) { + if (parentRect.left + tooltipLeft + tooltipRect.width > + window.innerWidth) { this.style.right = '0px'; this.style.left = 'auto'; } else { @@ -265,7 +296,8 @@ this.style.right = 'auto'; } // Clip the top/bottom side. - if (parentRect.top + tooltipTop + thisRect.height > window.innerHeight) { + if (parentRect.top + tooltipTop + tooltipRect.height > + window.innerHeight) { this.style.bottom = (parentRect.height - targetTop + offset) + 'px'; this.style.top = 'auto'; } else { @@ -301,13 +333,14 @@ if (this.target_) { this.tracker_.add(this.target_, 'pointerenter', () => this.show()); this.tracker_.add(this.target_, 'focus', () => this.show()); - this.tracker_.add(this.target_, 'pointerleave', () => this.hide()); + this.tracker_.add(this.target_, 'pointerleave', () => this.queueHide_()); this.tracker_.add(this.target_, 'blur', () => this.hide()); this.tracker_.add(this.target_, 'click', () => this.hide()); } this.tracker_.add( this.$.tooltip, 'animationend', () => this.onAnimationEnd_()); - this.tracker_.add(this, 'pointerenter', () => this.hide()); + this.tracker_.add(this, 'pointerenter', () => this.show()); + this.tracker_.add(this, 'pointerleave', () => this.queueHide_()); } private removeListeners_() {