diff --git a/.github/prompts/create_copilot_instructions.prompt.md b/.github/prompts/create_copilot_instructions.prompt.md index 43190d1..6a41677 100644 --- a/.github/prompts/create_copilot_instructions.prompt.md +++ b/.github/prompts/create_copilot_instructions.prompt.md
@@ -27,7 +27,15 @@ Then, introduce yourself, your goals and start by asking the user for the following, in the future you will be able to offer more personalized -instructions: +instructions. Ask the user to answer these questions, you should provide them +in an ordered list to the user. After sharing the list, you can suggest the +quick answer: `yes, debug_x64, no, no`, and invite the user to ask any +questions. + +### If the user does have a `copilot-instructions.md` file +If the user does have a `copilot-instructions.md` file, you will +- offer to update it with the latest instructions if it seems out of date +- offer to update or add `##Developer Prompt Variables` ### If the user does have a `embedder.instructions.md` file - ask if they want to use @@ -38,6 +46,10 @@ [chromium.instructions](../instructions/chromium.instructions.md) ### For both cases +- recommend that they share recommended developer prompt variables for use by + other prompts such as `/autoninja` and `/gtest`. + - You will need to ask for `${out_dir}` this is usually something like + `debug_x64` or `release_x64` but it can be anything. - ask if they want to use [haystack.instructions](../instructions/haystack.instructions.md) - briefly explain it; if they choose to use it, mention it requires an @@ -45,21 +57,15 @@ after creating their instruction file - ask if they want user personalization -### If the user does have a `copilot-instructions.md` file -If the user does have a `copilot-instructions.md` file, you will -- recap if its using the latest version of the instructions per above -- if the file it out of date compared to the other prompts, offer to update it - with the latest instructions -- offer to make any other changes they would like, such as personalization - ## Output Format You will produce [`.github/copilot-instructions.md`](../copilot-instructions.md) with multiple sections, the sections must be ordered as follows if they are to be included: 1. Default chromium or embedder instructions - 2. Haystack - 3. User personalization + 2. Developer Prompt Variables + 3. Haystack + 4. User personalization **Do not** include filepath syntax in the output, such as: `// filepath: ...\.github\instructions\haystack.instructions.md` @@ -70,6 +76,13 @@ - [`chromium.instructions`](../instructions/chromium.instructions.md) - [`embedder.instructions`](../instructions/embedder.instructions.md) +### Developer Prompt Variables +The developer prompt variables should be a version of the following code snippet +```markdown +## Developer Prompt Variables +`${out_dir}` = `out_dir` +``` + ### Chromium Haystack If the user requests Chromium Haystack, you will need to help them set it up.
diff --git a/.github/resources/gtest_discovery.md b/.github/resources/gtest_discovery.md index b903840..0713b75b 100644 --- a/.github/resources/gtest_discovery.md +++ b/.github/resources/gtest_discovery.md
@@ -1,15 +1,13 @@ ## GTest Discovery -1. If the user provided a ${file} you can make the following assumptions: - - If the `${file}` is in the `chrome` folder and ends in `_unittest.cc`, - the ${test_name} `unit_tests`. - - If the `${file}` is in the `chrome` folder and ends in `_browsertest.cc`, - the ${test_name} `browser_tests`. +1. If the user provided a `${file}` you can find a matching `${test_name}` + with the following command: `gn refs out/{out_dir} ${file}`. If the response + is `//chrome/test:browser_tests`, `browser_tests` is the `${test_name}`. 2. If you were able to determine the `${test_name}` from the `${file}`, you can - Read the file to extract a `${test_filter}` that would match all tests in the - file. - - For example if the file has `MyTestSuite.MyTest` and `MyTestSuite.MyTest2`, - the `${test_filter}` can be `MyTestSuite.*`. + Read the file to extract a `${test_filter}` that would match all tests in the + file. + - For example if the file has `MyTestSuite.MyTest` and `MyTestSuite.MyTest2`, + the `${test_filter}` can be `MyTestSuite.*`. 3. If you were able to determine the `${test_name}` and `${test_filter}`, `## GTest Discovery` has passed, otherwise it has failed.
diff --git a/AUTHORS b/AUTHORS index 5509a661..36e16c6 100644 --- a/AUTHORS +++ b/AUTHORS
@@ -590,6 +590,7 @@ Imranur Rahman <ir.shimul@gmail.com> Ion Rosca <rosca@adobe.com> Irmak Kavasoglu <irmakkavasoglu@gmail.com> +Isaac Khor <dev@isaackhor.com> Isaac Murchie <murchieisaac@gmail.com> Isaac Reilly <reillyi@amazon.com> Ivan Naydonov <samogot@gmail.com>
diff --git a/DEPS b/DEPS index d6cdd67..c8564e1 100644 --- a/DEPS +++ b/DEPS
@@ -299,11 +299,11 @@ # 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': '3b2d2d0f73fcc9468a3bced6d6d17a56411a87f4', + 'skia_revision': '18b85aced9b7b381ce184703bdeeae53a2fbe294', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': '7d1326123f37e10d3cf0a8ff6efc2d330e1f2b96', + 'v8_revision': '5f0be56704de0f9fd5ee6bb6a7a780976a5bfcb7', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. @@ -331,7 +331,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling googletest # and whatever else without interference from each other. - 'googletest_revision': '16d4f8eff6d7cefca6975d82a53f8fc995a6feb7', + 'googletest_revision': '6aa03e6774f8cb70da277c56efb24b44ce29d8d7', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling lighttpd # and whatever else without interference from each other. @@ -1508,7 +1508,7 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - '890fb2eabfdafcda1830d1aaf97ba4bf0c52866e', + 'c2910b6472c3849f25d534e1a00fe9691acf9f8d', 'condition': 'checkout_android and checkout_src_internal', }, @@ -1667,7 +1667,7 @@ 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': 'j7iNI3myTyI0EMBVQIEhOLRfEWiicoOLLXtRaela9s0C', + 'version': '35sx6LkLBBzbBXpfWIfXVI8tvIsqtu2T6tWdqcT-g94C', }, ], 'condition': 'checkout_android and non_git_source', @@ -1760,7 +1760,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_build_tools/lint', - 'version': 'PaYB6553MH9GJfamUZLduJESRbN13Clv2N2beHR6IQAC', + 'version': 'Rh_qNy2kyeA9GGIdsjnPMvQa1WpHOEoUBZqV9opGGgIC', }, ], 'condition': 'checkout_android and non_git_source', @@ -1771,7 +1771,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_build_tools/manifest_merger', - 'version': '0L0N3_u2ypIxEEtf0k_l36g_2CykW2BNuZBXm-v5qAUC', + 'version': 'i1CvLtWlkB9QDx0DL_52AZNLpuQc2d6MbpRsCbzgEtEC', }, ], 'condition': 'checkout_android and non_git_source', @@ -2548,7 +2548,7 @@ Var('pdfium_git') + '/pdfium.git' + '@' + Var('pdfium_revision'), 'src/third_party/perfetto': - Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + 'd15aa8fb5bed6a9e04f7f93ebdc3573b6f7a366e', + Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + 'fa368a66f714203d1232ff62ece403bd2f5e7c10', 'src/base/tracing/test/data': { 'bucket': 'perfetto', @@ -2867,16 +2867,16 @@ 'dep_type': 'cipd', }, - 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@12211edbca712355258047ef4f0de13f1da23ac3', - 'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@32f71d72a2643df675684e1795e987ac26e600df', + 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@d8d0687affb24a8069b74b50af9cd9245a4af273', + 'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@93231001597dad1149a5d035af30eda50b9e6b6c', 'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3', 'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@c9aad99f9276817f18f72a4696239237c83cb775', - 'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@736e415ebaa4290d21e42e370db5e933b9dff06d', + 'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@01021466b5e71deaac9054f56082566c782bfd51', 'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@75ad707a587e1469fb53a901b9b68fe9f6fbc11f', 'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@c913466fdc5004584890f89ff91121bdb2ffd4ba', 'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@60b640cb931814fcc6dabe4fc61f4738c56579f6', 'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@ae56bd6e65d9faa731150e931cb35f0d895223bc', - 'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@7760c965ea96a51e7d66433dc9f97b4cac7804f7', + 'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@a44043332c975777a7196406f08601f6099b24f0', 'src/third_party/vulkan_memory_allocator': Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '56300b29fbfcc693ee6609ddad3fdd5b7a449a21', @@ -2921,7 +2921,7 @@ Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'), 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '0f1742e458d2b187a6bf4470352acb6d7387274e', + Var('webrtc_git') + '/src.git' + '@' + '59d578881fea163b8bd63aa056ed38feefd273de', # Wuffs' canonical repository is at github.com/google/wuffs, but we use # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file. @@ -4393,7 +4393,7 @@ # Dependencies from src_internal 'src/chromeos/ash/resources/internal': { 'url': Var('chrome_git') + '/chrome/chromeos/ash/resources/internal.git' + '@' + - 'f6bfe79494ac7266c15b9139f25377ce1e59b22c', + '79861294509d9038645cd39ca828edd93abaf37b', 'condition': 'checkout_src_internal and checkout_chromeos', }, @@ -4439,7 +4439,7 @@ 'src/chrome/app/theme/google_chrome': { 'url': Var('chrome_git') + '/chrome/theme/google_chrome.git' + '@' + - 'd005f846501cdbfcf120e05b86efaa8de3b5b60e', + '61ee81844485e7d5e6f0f598124a93ae62dd07db', 'condition': 'checkout_src_internal', }, @@ -4487,7 +4487,7 @@ 'src/chrome/browser/platform_experience/win': { 'url': Var('chrome_git') + '/chrome/browser/platform_experience/win.git' + '@' + - '1c5aab84b6952ed67196e6a56788e9ef9a6e886e', + 'e0503816507543e3b732a5a46109e9f71d74c3e1', 'condition': 'checkout_src_internal', }, @@ -4641,7 +4641,7 @@ 'src/components/optimization_guide/internal': { 'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' + - '46d95254ba6bdec345073553c6f04b32cfe0a62b', + 'aa7ee4b538e9adb19542ef9b4be4674196aaf859', 'condition': 'checkout_src_internal', },
diff --git a/ash/ambient/test/ambient_ash_test_base.cc b/ash/ambient/test/ambient_ash_test_base.cc index e1c5a0ca..354f113a 100644 --- a/ash/ambient/test/ambient_ash_test_base.cc +++ b/ash/ambient/test/ambient_ash_test_base.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/40285824): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "ash/ambient/test/ambient_ash_test_base.h" #include <map>
diff --git a/ash/assistant/ui/main_stage/assistant_onboarding_suggestion_view.cc b/ash/assistant/ui/main_stage/assistant_onboarding_suggestion_view.cc index 3f1749d..082f6364 100644 --- a/ash/assistant/ui/main_stage/assistant_onboarding_suggestion_view.cc +++ b/ash/assistant/ui/main_stage/assistant_onboarding_suggestion_view.cc
@@ -2,14 +2,11 @@ // 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/40285824): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "ash/assistant/ui/main_stage/assistant_onboarding_suggestion_view.h" +#include <array> #include <string_view> +#include <utility> #include "ash/assistant/ui/assistant_ui_constants.h" #include "ash/assistant/ui/assistant_view_delegate.h" @@ -51,55 +48,53 @@ // Helpers --------------------------------------------------------------------- -struct ColorPalette { - SkColor flag_off; - SkColor dark; - SkColor light; -}; - SkColor GetBackgroundColor(int index) { // Opacity values: // 0x19: 10% // 0x4c: 30% - constexpr ColorPalette kBackgroundColors[] = { - {gfx::kGoogleBlue050, SkColorSetA(gfx::kGoogleBlue300, 0x4c), + constexpr std::array<std::pair<SkColor, SkColor>, 6> kBackgroundColors = {{ + // First: Dark Mode + // Second: Light Mode + {SkColorSetA(gfx::kGoogleBlue300, 0x4c), SkColorSetA(gfx::kGoogleBlue600, 0x19)}, - {gfx::kGoogleRed050, SkColorSetA(gfx::kGoogleRed300, 0x4c), + {SkColorSetA(gfx::kGoogleRed300, 0x4c), SkColorSetA(gfx::kGoogleRed600, 0x19)}, - {gfx::kGoogleYellow050, SkColorSetA(gfx::kGoogleYellow300, 0x4c), + {SkColorSetA(gfx::kGoogleYellow300, 0x4c), SkColorSetA(gfx::kGoogleYellow600, 0x19)}, - {gfx::kGoogleGreen050, SkColorSetA(gfx::kGoogleGreen300, 0x4c), + {SkColorSetA(gfx::kGoogleGreen300, 0x4c), SkColorSetA(gfx::kGoogleGreen600, 0x19)}, - {SkColorSetRGB(0xF6, 0xE9, 0xF8), SkColorSetARGB(0x4c, 0xf8, 0x82, 0xff), + {SkColorSetARGB(0x4c, 0xf8, 0x82, 0xff), SkColorSetARGB(0x19, 0xc6, 0x1a, 0xd9)}, - {gfx::kGoogleBlue050, SkColorSetA(gfx::kGoogleBlue300, 0x4c), - SkColorSetA(gfx::kGoogleBlue600, 0x19)}}; + {SkColorSetA(gfx::kGoogleBlue300, 0x4c), + SkColorSetA(gfx::kGoogleBlue600, 0x19)}, + }}; DCHECK_GE(index, 0); - DCHECK_LT(index, static_cast<int>(std::size(kBackgroundColors))); + DCHECK_LT(index, static_cast<int>(kBackgroundColors.size())); return DarkLightModeControllerImpl::Get()->IsDarkModeEnabled() - ? kBackgroundColors[index].dark - : kBackgroundColors[index].light; + ? kBackgroundColors[index].first + : kBackgroundColors[index].second; } SkColor GetForegroundColor(int index) { - constexpr ColorPalette kForegroundColors[] = { - {gfx::kGoogleBlue800, gfx::kGoogleBlue200, gfx::kGoogleBlue800}, - {gfx::kGoogleRed800, gfx::kGoogleRed200, gfx::kGoogleRed800}, - {SkColorSetRGB(0xBF, 0x50, 0x00), gfx::kGoogleYellow200, - SkColorSetRGB(0xBF, 0x50, 0x00)}, - {gfx::kGoogleGreen800, gfx::kGoogleGreen200, gfx::kGoogleGreen800}, - {SkColorSetRGB(0x8A, 0x0E, 0x9E), SkColorSetRGB(0xf8, 0x82, 0xff), - SkColorSetRGB(0xaa, 0x00, 0xb8)}, - {gfx::kGoogleBlue800, gfx::kGoogleBlue200, gfx::kGoogleBlue800}}; + constexpr std::array<std::pair<SkColor, SkColor>, 6> kForegroundColors = {{ + // First: Dark Mode + // Second: Light Mode + {gfx::kGoogleBlue200, gfx::kGoogleBlue800}, + {gfx::kGoogleRed200, gfx::kGoogleRed800}, + {gfx::kGoogleYellow200, SkColorSetRGB(0xBF, 0x50, 0x00)}, + {gfx::kGoogleGreen200, gfx::kGoogleGreen800}, + {SkColorSetRGB(0xf8, 0x82, 0xff), SkColorSetRGB(0xaa, 0x00, 0xb8)}, + {gfx::kGoogleBlue200, gfx::kGoogleBlue800}, + }}; DCHECK_GE(index, 0); - DCHECK_LT(index, static_cast<int>(std::size(kForegroundColors))); + DCHECK_LT(index, static_cast<int>(kForegroundColors.size())); return DarkLightModeControllerImpl::Get()->IsDarkModeEnabled() - ? kForegroundColors[index].dark - : kForegroundColors[index].light; + ? kForegroundColors[index].first + : kForegroundColors[index].second; } } // namespace
diff --git a/ash/assistant/ui/main_stage/assistant_onboarding_view_unittest.cc b/ash/assistant/ui/main_stage/assistant_onboarding_view_unittest.cc index 04c2308..b2a7c86 100644 --- a/ash/assistant/ui/main_stage/assistant_onboarding_view_unittest.cc +++ b/ash/assistant/ui/main_stage/assistant_onboarding_view_unittest.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/40285824): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "ash/assistant/ui/main_stage/assistant_onboarding_view.h" +#include <array> #include <memory> #include <queue> #include <string> @@ -300,7 +296,8 @@ }; auto get_color = [](int index) { - constexpr SkColor kForegroundColors[6][3] = { + constexpr std::array<std::array<SkColor, 3>, 6> kForegroundColors = {{ + // Colors of dark/light mode is disabled, dark mode, light mode. {gfx::kGoogleBlue800, gfx::kGoogleBlue200, gfx::kGoogleBlue800}, {gfx::kGoogleRed800, gfx::kGoogleRed200, gfx::kGoogleRed800}, @@ -309,7 +306,7 @@ {gfx::kGoogleGreen800, gfx::kGoogleGreen200, gfx::kGoogleGreen800}, {SkColorSetRGB(0x8A, 0x0E, 0x9E), SkColorSetRGB(0xf8, 0x82, 0xff), SkColorSetRGB(0xaa, 0x00, 0xb8)}, - {gfx::kGoogleBlue800, gfx::kGoogleBlue200, gfx::kGoogleBlue800}}; + {gfx::kGoogleBlue800, gfx::kGoogleBlue200, gfx::kGoogleBlue800}}}; const int color_index = DarkLightModeControllerImpl::Get()->IsDarkModeEnabled() ? 1 : 2; return kForegroundColors[index][color_index];
diff --git a/ash/capture_mode/camera_video_frame_renderer.cc b/ash/capture_mode/camera_video_frame_renderer.cc index b67199bb..a7d4917 100644 --- a/ash/capture_mode/camera_video_frame_renderer.cc +++ b/ash/capture_mode/camera_video_frame_renderer.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/40285824): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "ash/capture_mode/camera_video_frame_renderer.h" #include <cmath>
diff --git a/ash/clipboard/test_support/clipboard_history_item_builder.cc b/ash/clipboard/test_support/clipboard_history_item_builder.cc index 5c2614a..4df686c9 100644 --- a/ash/clipboard/test_support/clipboard_history_item_builder.cc +++ b/ash/clipboard/test_support/clipboard_history_item_builder.cc
@@ -2,12 +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/40285824): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "ash/clipboard/test_support/clipboard_history_item_builder.h" + #include <vector> #include "ash/clipboard/clipboard_history_item.h" @@ -182,7 +178,8 @@ ClipboardHistoryItemBuilder& ClipboardHistoryItemBuilder::SetPng( const scoped_refptr<base::RefCountedMemory>& png) { - std::vector<uint8_t> data(png->data(), png->data() + png->size()); + std::vector<uint8_t> data; + data.assign(png->begin(), png->end()); return SetPng(std::move(data)); }
diff --git a/ash/color_enhancement/color_enhancement_controller.cc b/ash/color_enhancement/color_enhancement_controller.cc index 0801840..6d7552f 100644 --- a/ash/color_enhancement/color_enhancement_controller.cc +++ b/ash/color_enhancement/color_enhancement_controller.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/40285824): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "ash/color_enhancement/color_enhancement_controller.h" +#include <array> #include <memory> #include "ash/shell.h" @@ -32,37 +28,38 @@ // // The first index is ColorVisionCorrectionType enum, so this must be kept in // that order. -const float kSimulationParams[3][9][3] = { +constexpr std::array<std::array<float, 3>, 27> kSimulationParams = {{ + // ColorVisionCorrectionType::kProtanomaly: - {{0.4720, -1.2946, 0.9857}, - {-0.6128, 1.6326, 0.0187}, - {0.1407, -0.3380, -0.0044}, - {-0.1420, 0.2488, 0.0044}, - {0.1872, -0.3908, 0.9942}, - {-0.0451, 0.1420, 0.0013}, - {0.0222, -0.0253, -0.0004}, - {-0.0290, -0.0201, 0.0006}, - {0.0068, 0.0454, 0.9990}}, + {{0.4720, -1.2946, 0.9857}}, + {{-0.6128, 1.6326, 0.0187}}, + {{0.1407, -0.3380, -0.0044}}, + {{-0.1420, 0.2488, 0.0044}}, + {{0.1872, -0.3908, 0.9942}}, + {{-0.0451, 0.1420, 0.0013}}, + {{0.0222, -0.0253, -0.0004}}, + {{-0.0290, -0.0201, 0.0006}}, + {{0.0068, 0.0454, 0.9990}}, // ColorVisionCorrectionType::kDeuteranomaly: - {{0.5442, -1.1454, 0.9818}, - {-0.7091, 1.5287, 0.0238}, - {0.1650, -0.3833, -0.0055}, - {-0.1664, 0.4368, 0.0056}, - {0.2178, -0.5327, 0.9927}, - {-0.0514, 0.0958, 0.0017}, - {0.0180, -0.0288, -0.0006}, - {-0.0232, -0.0649, 0.0007}, - {0.0052, 0.0360, 0.9998}}, + {{0.5442, -1.1454, 0.9818}}, + {{-0.7091, 1.5287, 0.0238}}, + {{0.1650, -0.3833, -0.0055}}, + {{-0.1664, 0.4368, 0.0056}}, + {{0.2178, -0.5327, 0.9927}}, + {{-0.0514, 0.0958, 0.0017}}, + {{0.0180, -0.0288, -0.0006}}, + {{-0.0232, -0.0649, 0.0007}}, + {{0.0052, 0.0360, 0.9998}}, // ColorVisionCorrectionType::kTritanomaly: - {{0.4275, -0.0181, 0.9307}, - {-0.2454, 0.0013, 0.0827}, - {-0.1821, 0.0168, -0.0134}, - {-0.1280, 0.0047, 0.0202}, - {0.0233, -0.0398, 0.9728}, - {0.1048, 0.0352, 0.0070}, - {-0.0156, 0.0061, 0.0071}, - {0.3841, 0.2947, 0.0151}, - {-0.3685, -0.3008, 0.9778}}}; + {{0.4275, -0.0181, 0.9307}}, + {{-0.2454, 0.0013, 0.0827}}, + {{-0.1821, 0.0168, -0.0134}}, + {{-0.1280, 0.0047, 0.0202}}, + {{0.0233, -0.0398, 0.9728}}, + {{0.1048, 0.0352, 0.0070}}, + {{-0.0156, 0.0061, 0.0071}}, + {{0.3841, 0.2947, 0.0151}}, + {{-0.3685, -0.3008, 0.9778}}}}; // Returns a 3x3 matrix for simulating the given type of CVD with the given // severity. @@ -74,11 +71,13 @@ gfx::Matrix3F result = gfx::Matrix3F::Zeros(); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { + int type_start_row = static_cast<int>(type) * 9; int param_row = i * 3 + j; - result.set(i, j, - kSimulationParams[type][param_row][0] * severity_squared + - kSimulationParams[type][param_row][1] * severity + - kSimulationParams[type][param_row][2]); + result.set( + i, j, + kSimulationParams[type_start_row + param_row][0] * severity_squared + + kSimulationParams[type_start_row + param_row][1] * severity + + kSimulationParams[type_start_row + param_row][2]); } } return result;
diff --git a/ash/constants/ash_switches.cc b/ash/constants/ash_switches.cc index f97ecb1..66165f3 100644 --- a/ash/constants/ash_switches.cc +++ b/ash/constants/ash_switches.cc
@@ -868,6 +868,9 @@ // If set, the overview button will be visible. const char kOverviewButtonForTests[] = "overview-button-for-tests"; +// If set, the overrrides the overscan settings on all displays. +const char kOverscanInsetsOverride[] = "overscan-insets-override"; + // Controls how often the HiddenNetworkHandler class checks for wrongly hidden // networks. The interval should be provided in seconds, should follow the // format "--hidden-network-migration-interval=#", and should be >= 1.
diff --git a/ash/constants/ash_switches.h b/ash/constants/ash_switches.h index d52e084..1e0d570 100644 --- a/ash/constants/ash_switches.h +++ b/ash/constants/ash_switches.h
@@ -288,6 +288,7 @@ COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kOobeTriggerSyncTimeoutForTests[]; COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kOverviewButtonForTests[]; +COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kOverscanInsetsOverride[]; COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kHiddenNetworkMigrationInterval[]; COMPONENT_EXPORT(ASH_CONSTANTS)
diff --git a/ash/display/cros_display_config.cc b/ash/display/cros_display_config.cc index f56b25e8b..dd0356a 100644 --- a/ash/display/cros_display_config.cc +++ b/ash/display/cros_display_config.cc
@@ -889,8 +889,7 @@ case crosapi::mojom::DisplayConfigOperation::kStart: { DVLOG(1) << "OverscanCalibrationStart: " << display_id; gfx::Insets insets = - Shell::Get()->window_tree_host_manager()->GetOverscanInsets( - display.id()); + Shell::Get()->display_manager()->GetOverscanInsets(display.id()); if (calibrator) { DVLOG(1) << "Replacing existing calibrator for id: " << display_id; }
diff --git a/ash/display/display_color_manager_unittest.cc b/ash/display/display_color_manager_unittest.cc index 2d7d35b..6c4a6eb 100644 --- a/ash/display/display_color_manager_unittest.cc +++ b/ash/display/display_color_manager_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/40285824): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "ash/display/display_color_manager.h" #include <memory>
diff --git a/ash/display/display_prefs.cc b/ash/display/display_prefs.cc index cd15308..d7aa841 100644 --- a/ash/display/display_prefs.cc +++ b/ash/display/display_prefs.cc
@@ -28,7 +28,6 @@ #include "components/prefs/scoped_user_pref_update.h" #include "third_party/cros_system_api/dbus/service_constants.h" #include "ui/display/display_features.h" -#include "ui/display/display_switches.h" #include "ui/display/manager/display_layout_store.h" #include "ui/display/manager/display_manager.h" #include "ui/display/manager/json_converter.h" @@ -218,7 +217,7 @@ if (base::Contains(it.first, ",")) { std::vector<std::string> ids_str = base::SplitString( - it.first, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + it.first, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); std::vector<int64_t> ids; for (std::string id_str : ids_str) { int64_t id; @@ -275,7 +274,25 @@ } gfx::Insets insets; - if (ValueToInsets(*dict_value, &insets)) { + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kOverscanInsetsOverride)) { + std::string value = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kOverscanInsetsOverride); + auto values = base::SplitString(value, ",", + base::WhitespaceHandling::TRIM_WHITESPACE, + base::SplitResult::SPLIT_WANT_ALL); + int top, left, bottom, right; + if (values.size() == 4 && base::StringToInt(values[0], &top) && + base::StringToInt(values[1], &left) && + base::StringToInt(values[2], &bottom) && + base::StringToInt(values[3], &right)) { + insets = gfx::Insets::TLBR(top, left, bottom, right); + insets_to_set = &insets; + } else { + LOG(ERROR) << "Failed to parse overscan insets:" << value; + } + } else if (ValueToInsets(*dict_value, &insets) && !insets.IsEmpty()) { insets_to_set = &insets; }
diff --git a/ash/display/display_prefs_unittest.cc b/ash/display/display_prefs_unittest.cc index 12156a0..13de930 100644 --- a/ash/display/display_prefs_unittest.cc +++ b/ash/display/display_prefs_unittest.cc
@@ -402,8 +402,7 @@ window_tree_host_manager->SetPrimaryDisplayId(dummy_id); EXPECT_NE(dummy_id, display::Screen::GetScreen()->GetPrimaryDisplay().id()); - window_tree_host_manager->SetOverscanInsets( - id1, gfx::Insets::TLBR(10, 11, 12, 13)); + display_manager()->SetOverscanInsets(id1, gfx::Insets::TLBR(10, 11, 12, 13)); display_manager()->SetDisplayRotation(id1, display::Display::ROTATE_90, display::Display::RotationSource::USER); @@ -826,8 +825,8 @@ display_manager()->UpdateZoomFactor(id1, 1.f / scale); window_tree_host_manager->SetPrimaryDisplayId(id2); int64_t new_primary = display::Screen::GetScreen()->GetPrimaryDisplay().id(); - window_tree_host_manager->SetOverscanInsets( - new_primary, gfx::Insets::TLBR(10, 11, 12, 13)); + display_manager()->SetOverscanInsets(new_primary, + gfx::Insets::TLBR(10, 11, 12, 13)); display_manager()->SetDisplayRotation(new_primary, display::Display::ROTATE_90, display::Display::RotationSource::USER); @@ -899,8 +898,8 @@ window_tree_host_manager->SetPrimaryDisplayId(id2); const int64_t new_primary = display::Screen::GetScreen()->GetPrimaryDisplay().id(); - window_tree_host_manager->SetOverscanInsets( - new_primary, gfx::Insets::TLBR(10, 11, 12, 13)); + display_manager()->SetOverscanInsets(new_primary, + gfx::Insets::TLBR(10, 11, 12, 13)); display_manager()->SetDisplayRotation(new_primary, display::Display::ROTATE_90, display::Display::RotationSource::USER);
diff --git a/ash/display/overscan_calibrator.cc b/ash/display/overscan_calibrator.cc index a079c540..9c470f7 100644 --- a/ash/display/overscan_calibrator.cc +++ b/ash/display/overscan_calibrator.cc
@@ -122,8 +122,8 @@ committed_(false) { // Undo the overscan calibration temporarily so that the user can see // dark boundary and current overscan region. - Shell::Get()->window_tree_host_manager()->SetOverscanInsets(display_.id(), - gfx::Insets()); + Shell::Get()->display_manager()->SetOverscanInsets(display_.id(), + gfx::Insets()); UpdateUILayer(); } @@ -131,13 +131,13 @@ // Overscan calibration has finished without commit, so the display has to // be the original offset. if (!committed_) { - Shell::Get()->window_tree_host_manager()->SetOverscanInsets( - display_.id(), initial_insets_); + Shell::Get()->display_manager()->SetOverscanInsets(display_.id(), + initial_insets_); } } void OverscanCalibrator::Commit() { - Shell::Get()->window_tree_host_manager()->SetOverscanInsets( + Shell::Get()->display_manager()->SetOverscanInsets( display_.id(), ConvertToHost(display_, insets_)); committed_ = true; }
diff --git a/ash/display/window_tree_host_manager.cc b/ash/display/window_tree_host_manager.cc index 051560f..59f27d1 100644 --- a/ash/display/window_tree_host_manager.cc +++ b/ash/display/window_tree_host_manager.cc
@@ -451,16 +451,6 @@ return windows; } -gfx::Insets WindowTreeHostManager::GetOverscanInsets(int64_t display_id) const { - return GetDisplayManager()->GetOverscanInsets(display_id); -} - -void WindowTreeHostManager::SetOverscanInsets( - int64_t display_id, - const gfx::Insets& insets_in_dip) { - GetDisplayManager()->SetOverscanInsets(display_id, insets_in_dip); -} - std::vector<RootWindowController*> WindowTreeHostManager::GetAllRootWindowControllers() { std::vector<RootWindowController*> controllers;
diff --git a/ash/display/window_tree_host_manager.h b/ash/display/window_tree_host_manager.h index ff97532..1edab84 100644 --- a/ash/display/window_tree_host_manager.h +++ b/ash/display/window_tree_host_manager.h
@@ -103,11 +103,6 @@ // mode, this return a RootWindowController for the primary root window only. std::vector<RootWindowController*> GetAllRootWindowControllers(); - // Gets/Sets/Clears the overscan insets for the specified |display_id|. See - // display_manager.h for the details. - gfx::Insets GetOverscanInsets(int64_t display_id) const; - void SetOverscanInsets(int64_t display_id, const gfx::Insets& insets_in_dip); - // Checks if the mouse pointer is on one of displays, and moves to // the center of the nearest display if it's outside of all displays. void UpdateMouseLocationAfterDisplayChange();
diff --git a/ash/display/window_tree_host_manager_unittest.cc b/ash/display/window_tree_host_manager_unittest.cc index 4b45a4b..242553c5 100644 --- a/ash/display/window_tree_host_manager_unittest.cc +++ b/ash/display/window_tree_host_manager_unittest.cc
@@ -1512,8 +1512,6 @@ } TEST_F(WindowTreeHostManagerTest, OverscanInsets) { - WindowTreeHostManager* window_tree_host_manager = - Shell::Get()->window_tree_host_manager(); TestEventHandler event_handler; Shell::Get()->AddPreTargetHandler(&event_handler); @@ -1521,8 +1519,8 @@ display::Display display1 = display::Screen::GetScreen()->GetPrimaryDisplay(); aura::Window::Windows root_windows = Shell::GetAllRootWindows(); - window_tree_host_manager->SetOverscanInsets( - display1.id(), gfx::Insets::TLBR(10, 15, 20, 25)); + display_manager()->SetOverscanInsets(display1.id(), + gfx::Insets::TLBR(10, 15, 20, 25)); display::test::DisplayManagerTestApi display_manager_test(display_manager()); EXPECT_EQ(gfx::Rect(0, 0, 80, 170), root_windows[0]->bounds()); EXPECT_EQ(gfx::Size(150, 200), root_windows[1]->bounds().size()); @@ -1533,7 +1531,7 @@ generator.MoveMouseToInHost(20, 25); EXPECT_EQ(gfx::Point(5, 15), event_handler.GetLocationAndReset()); - window_tree_host_manager->SetOverscanInsets(display1.id(), gfx::Insets()); + display_manager()->SetOverscanInsets(display1.id(), gfx::Insets()); EXPECT_EQ(gfx::Rect(0, 0, 120, 200), root_windows[0]->bounds()); EXPECT_EQ(gfx::Rect(120, 0, 150, 200), display_manager_test.GetSecondaryDisplay().bounds());
diff --git a/ash/events/keyboard_driven_event_rewriter_unittest.cc b/ash/events/keyboard_driven_event_rewriter_unittest.cc index 1300699..30bad5d 100644 --- a/ash/events/keyboard_driven_event_rewriter_unittest.cc +++ b/ash/events/keyboard_driven_event_rewriter_unittest.cc
@@ -2,15 +2,11 @@ // 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/40285824): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "ash/events/keyboard_driven_event_rewriter.h" #include <stddef.h> +#include <array> #include <string> #include "base/compiler_specific.h" @@ -87,39 +83,41 @@ }; TEST_F(KeyboardDrivenEventRewriterTest, PassThrough) { - struct { + struct TestData { ui::KeyboardCode ui_keycode; int ui_flags; - } kTests[] = { - { ui::VKEY_A, ui::EF_NONE }, - { ui::VKEY_A, ui::EF_CONTROL_DOWN }, - { ui::VKEY_A, ui::EF_ALT_DOWN }, - { ui::VKEY_A, ui::EF_SHIFT_DOWN }, - { ui::VKEY_A, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN }, - { ui::VKEY_A, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN }, - - { ui::VKEY_LEFT, ui::EF_NONE }, - { ui::VKEY_LEFT, ui::EF_CONTROL_DOWN }, - { ui::VKEY_LEFT, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN }, - - { ui::VKEY_RIGHT, ui::EF_NONE }, - { ui::VKEY_RIGHT, ui::EF_CONTROL_DOWN }, - { ui::VKEY_RIGHT, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN }, - - { ui::VKEY_UP, ui::EF_NONE }, - { ui::VKEY_UP, ui::EF_CONTROL_DOWN }, - { ui::VKEY_UP, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN }, - - { ui::VKEY_DOWN, ui::EF_NONE }, - { ui::VKEY_DOWN, ui::EF_CONTROL_DOWN }, - { ui::VKEY_DOWN, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN }, - - { ui::VKEY_RETURN, ui::EF_NONE }, - { ui::VKEY_RETURN, ui::EF_CONTROL_DOWN }, - { ui::VKEY_RETURN, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN }, }; - for (size_t i = 0; i < std::size(kTests); ++i) { + constexpr std::array<TestData, 21> kTests = {{ + {ui::VKEY_A, ui::EF_NONE}, + {ui::VKEY_A, ui::EF_CONTROL_DOWN}, + {ui::VKEY_A, ui::EF_ALT_DOWN}, + {ui::VKEY_A, ui::EF_SHIFT_DOWN}, + {ui::VKEY_A, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN}, + {ui::VKEY_A, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN}, + + {ui::VKEY_LEFT, ui::EF_NONE}, + {ui::VKEY_LEFT, ui::EF_CONTROL_DOWN}, + {ui::VKEY_LEFT, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN}, + + {ui::VKEY_RIGHT, ui::EF_NONE}, + {ui::VKEY_RIGHT, ui::EF_CONTROL_DOWN}, + {ui::VKEY_RIGHT, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN}, + + {ui::VKEY_UP, ui::EF_NONE}, + {ui::VKEY_UP, ui::EF_CONTROL_DOWN}, + {ui::VKEY_UP, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN}, + + {ui::VKEY_DOWN, ui::EF_NONE}, + {ui::VKEY_DOWN, ui::EF_CONTROL_DOWN}, + {ui::VKEY_DOWN, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN}, + + {ui::VKEY_RETURN, ui::EF_NONE}, + {ui::VKEY_RETURN, ui::EF_CONTROL_DOWN}, + {ui::VKEY_RETURN, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN}, + }}; + + for (size_t i = 0; i < kTests.size(); ++i) { EXPECT_EQ( base::StringPrintf("PassThrough ui_flags=%d", kTests[i].ui_flags), GetRewrittenEventAsString(kTests[i].ui_keycode, kTests[i].ui_flags, @@ -131,19 +129,21 @@ TEST_F(KeyboardDrivenEventRewriterTest, Rewrite) { const int kModifierMask = ui::EF_SHIFT_DOWN; - struct { + struct TestCase { ui::KeyboardCode ui_keycode; int ui_flags; - } kTests[] = { - { ui::VKEY_LEFT, kModifierMask }, - { ui::VKEY_RIGHT, kModifierMask }, - { ui::VKEY_UP, kModifierMask }, - { ui::VKEY_DOWN, kModifierMask }, - { ui::VKEY_RETURN, kModifierMask }, - { ui::VKEY_F6, kModifierMask }, }; - for (size_t i = 0; i < std::size(kTests); ++i) { + constexpr std::array<TestCase, 6> kTests{{ + {ui::VKEY_LEFT, kModifierMask}, + {ui::VKEY_RIGHT, kModifierMask}, + {ui::VKEY_UP, kModifierMask}, + {ui::VKEY_DOWN, kModifierMask}, + {ui::VKEY_RETURN, kModifierMask}, + {ui::VKEY_F6, kModifierMask}, + }}; + + for (size_t i = 0; i < kTests.size(); ++i) { EXPECT_EQ( "Rewritten ui_flags=0", GetRewrittenEventAsString(kTests[i].ui_keycode, kTests[i].ui_flags,
diff --git a/ash/examples/test_app_window.cc b/ash/examples/test_app_window.cc index 8c1e2dab..236ef8f 100644 --- a/ash/examples/test_app_window.cc +++ b/ash/examples/test_app_window.cc
@@ -12,6 +12,7 @@ #include "base/strings/string_number_conversions.h" #include "chromeos/ui/base/app_types.h" #include "chromeos/ui/base/window_properties.h" +#include "ui/aura/client/aura_constants.h" #include "ui/base/models/combobox_model.h" #include "ui/color/color_variant.h" #include "ui/gfx/geometry/size.h" @@ -121,6 +122,17 @@ views::Checkbox* cb = static_cast<views::Checkbox*>(event.target()); cb->GetWidget()->widget_delegate()->SetCanMaximize(cb->GetChecked()); })); + add_checkbox(this, u"Always On Top", u"always on top", + /*initial_state=*/false, + base::BindRepeating([](const ui::Event& event) { + views::Checkbox* cb = + static_cast<views::Checkbox*>(event.target()); + auto* window = cb->GetWidget()->GetNativeWindow(); + window->SetProperty(aura::client::kZOrderingKey, + cb->GetChecked() + ? ui::ZOrderLevel::kFloatingWindow + : ui::ZOrderLevel::kNormal); + })); AddChildView(std::make_unique<views::LabelButton>( base::BindRepeating(&TestView::UpdateMinimumSize,
diff --git a/base/process/process.h b/base/process/process.h index eacea062..aa41efa 100644 --- a/base/process/process.h +++ b/base/process/process.h
@@ -304,7 +304,9 @@ #endif // BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_IOS) && BUILDFLAG(USE_BLINK) - using TerminateCallback = bool (*)(ProcessHandle handle); + using TerminateCallback = bool (*)(ProcessHandle handle, + int exit_code, + bool wait); using WaitForExitCallback = bool (*)(ProcessHandle handle, int* exit_code, base::TimeDelta timeout);
diff --git a/base/process/process_ios.cc b/base/process/process_ios.cc index edf2e492..f3d9c3a 100644 --- a/base/process/process_ios.cc +++ b/base/process/process_ios.cc
@@ -49,7 +49,7 @@ } #endif CHECK(g_terminate_callback); - return (*g_terminate_callback)(process_); + return (*g_terminate_callback)(process_, exit_code, wait); } bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const {
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1 index 1fe719b..c3ea0c8 100644 --- a/build/fuchsia/linux_internal.sdk.sha1 +++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@ -28.20250515.102.1 +28.20250522.103.1
diff --git a/cc/input/input_handler.cc b/cc/input/input_handler.cc index 79c340d..f2aa254 100644 --- a/cc/input/input_handler.cc +++ b/cc/input/input_handler.cc
@@ -1019,7 +1019,7 @@ // CC side always uses fractional scroll deltas. bool use_fractional_offsets = true; std::unique_ptr<SnapSelectionStrategy> strategy = - SnapSelectionStrategy::CreateForEndAndDirection( + SnapSelectionStrategy::CreateForDisplacement( current_offset, snap_displacement, use_fractional_offsets); double snapport_height_adjustment =
diff --git a/cc/input/scroll_snap_data.cc b/cc/input/scroll_snap_data.cc index ef9e165..dc38a90 100644 --- a/cc/input/scroll_snap_data.cc +++ b/cc/input/scroll_snap_data.cc
@@ -530,7 +530,11 @@ float base_position = horiz ? strategy.base_position().x() : strategy.base_position().y(); + // True if we have found a "preferred" candidate. + bool preferred_candidate = false; float smallest_distance = horiz ? proximity_range_.x() : proximity_range_.y(); + float proximity_distance = + horiz ? proximity_range_.x() : proximity_range_.y(); auto evaluate = [&](const SnapSearchResult& candidate, const SnapAreaData& area) { @@ -541,20 +545,38 @@ return; } float distance = std::abs(candidate.snap_offset() - base_position); - if (distance > smallest_distance) { + if (distance > proximity_distance) { + return; + } + + bool is_preferred_candidate = + strategy.IsPreferredSnapPosition(axis, candidate.snap_offset()); + // If we have a preferred candidate, skip those which are not preferred. + if (preferred_candidate && !is_preferred_candidate) { + return; + } + // If this snap area is further away from the best candidate, and + // we either already have a preferred candidate or this candidate is not + // preferred, then skip it. + if (distance > smallest_distance && + (preferred_candidate || !is_preferred_candidate)) { return; } // Aligned snap areas that have focus should be given preference when // selecting snap targets. - if (distance < smallest_distance || candidate.has_focus_within()) { + if (distance < smallest_distance || + (is_preferred_candidate && + (!preferred_candidate || candidate.has_focus_within()))) { smallest_distance = distance; closest = candidate; + preferred_candidate = is_preferred_candidate; } else if (closest && !closest->has_focus_within()) { if (closest->element_id() == targeted_area_id_) { return; } if (candidate.element_id() == targeted_area_id_) { closest = candidate; + preferred_candidate = is_preferred_candidate; return; } const auto candidate_rect = candidate.rect(); @@ -568,11 +590,13 @@ closest_rect != candidate_rect) { smallest_distance = distance; closest = candidate; + preferred_candidate = is_preferred_candidate; } else if ((scroll_snap_type_.axis == SnapAxis::kBoth) && (area.scroll_snap_align.alignment_block != SnapAlignment::kNone) && (area.scroll_snap_align.alignment_inline != - SnapAlignment::kNone)) { + SnapAlignment::kNone) && + is_preferred_candidate == preferred_candidate) { // This candidate is equally aligned with the current closest. Since it // can be snapped to in both axes, designate it a potential alternative // if we don't already have a potential alternative or it is a better
diff --git a/cc/input/scroll_snap_data_unittest.cc b/cc/input/scroll_snap_data_unittest.cc index 8821ef2..f37c833f3 100644 --- a/cc/input/scroll_snap_data_unittest.cc +++ b/cc/input/scroll_snap_data_unittest.cc
@@ -25,7 +25,7 @@ float expected_covered_end = std::numeric_limits<float>::max()) { float invalid = std::numeric_limits<float>::max(); std::unique_ptr<SnapSelectionStrategy> strategy = - SnapSelectionStrategy::CreateForEndAndDirection( + SnapSelectionStrategy::CreateForDisplacement( gfx::PointF(cur_pos, 0), gfx::Vector2dF(delta, 0), false /* use_fractional_deltas */); @@ -50,7 +50,7 @@ float expected_covered_end = std::numeric_limits<float>::max()) { float invalid = std::numeric_limits<float>::max(); std::unique_ptr<SnapSelectionStrategy> strategy = - SnapSelectionStrategy::CreateForEndAndDirection( + SnapSelectionStrategy::CreateForDisplacement( gfx::PointF(0, cur_pos), gfx::Vector2dF(0, delta), false /* use_fractional_deltas */); @@ -334,7 +334,7 @@ result.target_element_ids); std::unique_ptr<SnapSelectionStrategy> end_direction_strategy = - SnapSelectionStrategy::CreateForEndAndDirection( + SnapSelectionStrategy::CreateForDisplacement( gfx::PointF(600, 0), gfx::Vector2dF(15, 15), false /* use_fractional_deltas */); result = container.FindSnapPosition(*end_direction_strategy); @@ -381,7 +381,7 @@ result.target_element_ids); std::unique_ptr<SnapSelectionStrategy> end_direction_strategy = - SnapSelectionStrategy::CreateForEndAndDirection( + SnapSelectionStrategy::CreateForDisplacement( gfx::PointF(650, 10), gfx::Vector2d(15, 15), false /* use_fractional_deltas */); result = container.FindSnapPosition(*end_direction_strategy); @@ -423,7 +423,7 @@ container.AddSnapAreaData(closer_to_target); std::unique_ptr<SnapSelectionStrategy> end_direction_strategy = - SnapSelectionStrategy::CreateForEndAndDirection( + SnapSelectionStrategy::CreateForDisplacement( gfx::PointF(0, 0), gfx::Vector2d(600, 0), false /* use_fractional_deltas */); @@ -453,7 +453,7 @@ container.AddSnapAreaData(covering_area); std::unique_ptr<SnapSelectionStrategy> strategy = - SnapSelectionStrategy::CreateForEndAndDirection( + SnapSelectionStrategy::CreateForDisplacement( gfx::PointF(0, 0), gfx::Vector2d(300, 0), false /* use_fractional_deltas */); @@ -479,7 +479,7 @@ container.AddSnapAreaData(stop_area); std::unique_ptr<SnapSelectionStrategy> strategy = - SnapSelectionStrategy::CreateForEndAndDirection( + SnapSelectionStrategy::CreateForDisplacement( gfx::PointF(150, 0), gfx::Vector2d(200, 0), false /* use_fractional_deltas */); @@ -773,7 +773,7 @@ container.AddSnapAreaData(area); std::unique_ptr<SnapSelectionStrategy> end_direction_strategy = - SnapSelectionStrategy::CreateForEndAndDirection( + SnapSelectionStrategy::CreateForDisplacement( gfx::PointF(0, 100), gfx::Vector2dF(0, 300), false /* use_fractional_deltas */); @@ -785,7 +785,7 @@ EXPECT_EQ(50, result.covered_range_y->start()); EXPECT_EQ(850, result.covered_range_y->end()); - end_direction_strategy = SnapSelectionStrategy::CreateForEndAndDirection( + end_direction_strategy = SnapSelectionStrategy::CreateForDisplacement( gfx::PointF(0, 100), gfx::Vector2dF(0, -100), false /* use_fractional_deltas */); result = container.FindSnapPosition(*end_direction_strategy);
diff --git a/cc/input/scroll_utils.cc b/cc/input/scroll_utils.cc index 8dd1a6c..3526c0c6 100644 --- a/cc/input/scroll_utils.cc +++ b/cc/input/scroll_utils.cc
@@ -37,6 +37,17 @@ } // static +int ScrollUtils::CalculateMinPageSnap(int length) { + const int min_page_step = length * kMinFractionToStepWhenSnapPaging; + return std::max(min_page_step, 1); +} + +// static +int ScrollUtils::CalculateMaxPageSnap(int length) { + return std::max(length, 1); +} + +// static int ScrollUtils::CalculatePageStep(int length) { const int min_page_step = length * kMinFractionToStepWhenPaging; const int page_step =
diff --git a/cc/input/scroll_utils.h b/cc/input/scroll_utils.h index 947156e..e2b0b56 100644 --- a/cc/input/scroll_utils.h +++ b/cc/input/scroll_utils.h
@@ -18,6 +18,7 @@ namespace cc { static constexpr int kPixelsPerLineStep = 40; +static constexpr float kMinFractionToStepWhenSnapPaging = 0.4f; static constexpr float kMinFractionToStepWhenPaging = 0.875f; #if BUILDFLAG(IS_MAC) static constexpr int kMaxOverlapBetweenPages = 40; @@ -38,6 +39,8 @@ const gfx::SizeF& scroller_size, const gfx::SizeF& viewport_size); + static int CalculateMinPageSnap(int length); + static int CalculateMaxPageSnap(int length); static int CalculatePageStep(int length); };
diff --git a/cc/input/snap_selection_strategy.cc b/cc/input/snap_selection_strategy.cc index c62e5c5..025d525 100644 --- a/cc/input/snap_selection_strategy.cc +++ b/cc/input/snap_selection_strategy.cc
@@ -5,6 +5,9 @@ #include "cc/input/snap_selection_strategy.h" #include <cmath> +#include <limits> + +#include "ui/gfx/geometry/vector2d_f.h" namespace cc { @@ -21,16 +24,36 @@ gfx::Vector2dF step, bool use_fractional_offsets, SnapStopAlwaysFilter filter) { - return std::make_unique<DirectionStrategy>(current_position, step, filter, - use_fractional_offsets); + return std::make_unique<DirectionStrategy>( + current_position, step, DirectionStrategy::StepPreference::kDirection, + gfx::Vector2dF(), gfx::Vector2dF(), filter, use_fractional_offsets); } std::unique_ptr<SnapSelectionStrategy> -SnapSelectionStrategy::CreateForEndAndDirection(gfx::PointF current_position, - gfx::Vector2dF displacement, - bool use_fractional_offsets) { - return std::make_unique<EndAndDirectionStrategy>( - current_position, displacement, use_fractional_offsets); +SnapSelectionStrategy::CreateForDisplacement(gfx::PointF current_position, + gfx::Vector2dF displacement, + bool use_fractional_offsets, + SnapStopAlwaysFilter filter) { + return std::make_unique<DirectionStrategy>( + current_position, displacement, + DirectionStrategy::StepPreference::kDistance, gfx::Vector2dF(), + gfx::Vector2dF(std::numeric_limits<float>::max(), + std::numeric_limits<float>::max()), + filter, use_fractional_offsets); +} + +std::unique_ptr<SnapSelectionStrategy> +SnapSelectionStrategy::CreateForPreferredDisplacement( + gfx::PointF current_position, + gfx::Vector2dF displacement, + gfx::Vector2dF min_displacement, + gfx::Vector2dF max_displacement, + bool use_fractional_offsets, + SnapStopAlwaysFilter filter) { + return std::make_unique<DirectionStrategy>( + current_position, displacement, + DirectionStrategy::StepPreference::kDistance, min_displacement, + max_displacement, filter, use_fractional_offsets); } std::unique_ptr<SnapSelectionStrategy> @@ -79,6 +102,11 @@ return current_position_; } +bool EndPositionStrategy::IsPreferredSnapPosition(SearchAxis axis, + float position) const { + return true; +} + // |position| is unused in this method. bool EndPositionStrategy::IsValidSnapPosition(SearchAxis axis, float position) const { @@ -117,7 +145,22 @@ } gfx::PointF DirectionStrategy::base_position() const { - return current_position_; + return preferred_step_ == StepPreference::kDirection + ? current_position_ + : current_position_ + step_; +} + +bool DirectionStrategy::IsPreferredSnapPosition(SearchAxis axis, + float position) const { + if (axis == SearchAxis::kX) { + float delta = position - current_position_.x(); + return std::abs(delta) >= std::abs(preferred_min_displacement_.x()) && + std::abs(delta) <= std::abs(preferred_max_displacement_.x()); + } else { + float delta = position - current_position_.y(); + return std::abs(delta) >= std::abs(preferred_min_displacement_.y()) && + std::abs(delta) <= std::abs(preferred_max_displacement_.y()); + } } bool DirectionStrategy::IsValidSnapPosition(SearchAxis axis, @@ -148,6 +191,10 @@ area.must_snap); } +bool DirectionStrategy::ShouldRespectSnapStop() const { + return true; +} + const std::optional<SnapSearchResult>& DirectionStrategy::PickBestResult( const std::optional<SnapSearchResult>& closest, const std::optional<SnapSearchResult>& covering) const { @@ -160,15 +207,25 @@ if (!covering.has_value()) return closest; - // "Right" or "Down" arrow. - if ((step_.x() > 0 || step_.y() > 0) && - closest.value().snap_offset() < covering.value().snap_offset()) { - return closest; + // If covering and closest represent the same snap area, covering best + // preserves the intended scroll position. + if (covering->element_id() == closest->element_id()) { + return covering; } - // "Left" or "Up" arrow. - if ((step_.x() < 0 || step_.y() < 0) && - closest.value().snap_offset() > covering.value().snap_offset()) { - return closest; + + // If we only intend to scroll in the given direction, prefer the closer + // snap position. + if (preferred_step_ == StepPreference::kDirection) { + // Scroll right or down. + if ((step_.x() > 0 || step_.y() > 0) && + closest.value().snap_offset() < covering.value().snap_offset()) { + return closest; + } + // Scroll left or up. + if ((step_.x() < 0 || step_.y() < 0) && + closest.value().snap_offset() > covering.value().snap_offset()) { + return closest; + } } return covering; @@ -182,59 +239,4 @@ return std::make_unique<DirectionStrategy>(*this); } -bool EndAndDirectionStrategy::ShouldSnapOnX() const { - return displacement_.x() != 0; -} - -bool EndAndDirectionStrategy::ShouldSnapOnY() const { - return displacement_.y() != 0; -} - -gfx::PointF EndAndDirectionStrategy::intended_position() const { - return current_position_ + displacement_; -} - -gfx::PointF EndAndDirectionStrategy::base_position() const { - return current_position_ + displacement_; -} - -bool EndAndDirectionStrategy::IsValidSnapPosition(SearchAxis axis, - float position) const { - // If not using fractional offsets then it is possible for the currently - // snapped area's offset, which is fractional, to not be equal to the current - // scroll offset, which is not fractional. Therefore we round the offsets so - // that any position within 0.5 of the current position is ignored. - if (axis == SearchAxis::kX) { - float delta = position - current_position_.x(); - if (!use_fractional_offsets_) - delta = std::round(delta); - return (displacement_.x() > 0 && delta > 0) || // Right - (displacement_.x() < 0 && delta < 0); // Left - } else { - float delta = position - current_position_.y(); - if (!use_fractional_offsets_) - delta = std::round(delta); - return (displacement_.y() > 0 && delta > 0) || // Down - (displacement_.y() < 0 && delta < 0); // Up - } -} - -bool EndAndDirectionStrategy::ShouldRespectSnapStop() const { - return true; -} - -const std::optional<SnapSearchResult>& EndAndDirectionStrategy::PickBestResult( - const std::optional<SnapSearchResult>& closest, - const std::optional<SnapSearchResult>& covering) const { - return covering.has_value() ? covering : closest; -} - -bool EndAndDirectionStrategy::UsingFractionalOffsets() const { - return use_fractional_offsets_; -} - -std::unique_ptr<SnapSelectionStrategy> EndAndDirectionStrategy::Clone() const { - return std::make_unique<EndAndDirectionStrategy>(*this); -} - } // namespace cc
diff --git a/cc/input/snap_selection_strategy.h b/cc/input/snap_selection_strategy.h index 5e7462d..5e5afeb 100644 --- a/cc/input/snap_selection_strategy.h +++ b/cc/input/snap_selection_strategy.h
@@ -22,11 +22,15 @@ public: SnapSelectionStrategy() = default; virtual ~SnapSelectionStrategy() = default; + // Strategy for scrolling to a particular position in a non-directional + // manner. static std::unique_ptr<SnapSelectionStrategy> CreateForEndPosition( const gfx::PointF& current_position, bool scrolled_x, bool scrolled_y); + // Strategy for scrolling in a direction by some small amount, + // giving preference to stop at snap areas. // |use_fractional_offsets| should be true when the current position is // provided in fractional pixels. static std::unique_ptr<SnapSelectionStrategy> CreateForDirection( @@ -34,10 +38,30 @@ gfx::Vector2dF step, bool use_fractional_offsets, SnapStopAlwaysFilter filter = SnapStopAlwaysFilter::kIgnore); - static std::unique_ptr<SnapSelectionStrategy> CreateForEndAndDirection( + + // Strategy for scrolling by some large offset in a particular direction. + // Unlike CreateForDirection, prefers scrolling by the given displacement + // over snapping to nearby points. + // |use_fractional_offsets| should be true when the current position is + // provided in fractional pixels. + static std::unique_ptr<SnapSelectionStrategy> CreateForDisplacement( gfx::PointF current_position, gfx::Vector2dF displacement, - bool use_fractional_offsets); + bool use_fractional_offsets, + SnapStopAlwaysFilter filter = SnapStopAlwaysFilter::kIgnore); + + // Similar to the previous strategy, this prefers scrolling by the given + // displacement. However, it additionally prefers snap points that scroll + // within the given range. + // |use_fractional_offsets| should be true when the current position is + // provided in fractional pixels. + static std::unique_ptr<SnapSelectionStrategy> CreateForPreferredDisplacement( + gfx::PointF current_position, + gfx::Vector2dF displacement, + gfx::Vector2dF min_displacement, + gfx::Vector2dF max_displacement, + bool use_fractional_offsets, + SnapStopAlwaysFilter filter = SnapStopAlwaysFilter::kIgnore); // Creates a selection strategy that attempts to snap to previously snapped // targets if possible, but defaults to finding the closest snap point if @@ -61,6 +85,10 @@ // Returns the current scroll position of the snap container. const gfx::PointF& current_position() const { return current_position_; } + // Returns true if the given snap offset matches the strategy's preference. + virtual bool IsPreferredSnapPosition(SearchAxis axis, + float position) const = 0; + // Returns true if the selection strategy considers the given snap offset // valid for the current axis. virtual bool IsValidSnapPosition(SearchAxis axis, float position) const = 0; @@ -124,6 +152,7 @@ gfx::PointF intended_position() const override; gfx::PointF base_position() const override; + bool IsPreferredSnapPosition(SearchAxis axis, float position) const override; bool IsValidSnapPosition(SearchAxis axis, float position) const override; bool HasIntendedDirection() const override; bool ShouldPrioritizeSnapTargets() const override; @@ -143,21 +172,41 @@ // Examples for intended direction scrolls include // - pressing an arrow key on the keyboard // - a swiping gesture interpreted as a fixed (rather than inertial) scroll -// For this type of scrolls, we want to -// * Minimize the distance between the snap position and the starting position, +// - a “fling” gesture, interpreted with momentum +// - programmatically scrolling via APIs such as scrollBy() +// - paging operations such as the PgUp/PgDn keys (or equivalent operations on +// the scrollbar) +// For this type of scroll, we want to +// * Minimize the distance between the snap position and +// the starting position if we only prefer the direction // so that we stop at the first snap position in that direction. +// * When the step distance is preferred, prefer skipping snap positions +// to scroll closer to the step distance. // * Return the default intended position (using the default step) if that makes // a snap area covers the snapport. class DirectionStrategy : public SnapSelectionStrategy { public: + enum class StepPreference { + // Prefer only the direction, but otherwise choose a closer snap position. + kDirection, + // Prefer snap areas close to the specified step distance. + kDistance + }; + // |use_fractional_offsets| should be true when the current position is // provided in fractional pixels. DirectionStrategy(const gfx::PointF& current_position, const gfx::Vector2dF& step, + StepPreference preferred_step, + const gfx::Vector2dF preferred_min_displacement, + const gfx::Vector2dF preferred_max_displacement, SnapStopAlwaysFilter filter, bool use_fractional_offsets) : SnapSelectionStrategy(current_position), step_(step), + preferred_step_(preferred_step), + preferred_min_displacement_(preferred_min_displacement), + preferred_max_displacement_(preferred_max_displacement), snap_stop_always_filter_(filter), use_fractional_offsets_(use_fractional_offsets) {} DirectionStrategy(const DirectionStrategy& other) = default; @@ -169,54 +218,11 @@ gfx::PointF intended_position() const override; gfx::PointF base_position() const override; + bool IsPreferredSnapPosition(SearchAxis axis, float position) const override; bool IsValidSnapPosition(SearchAxis axis, float position) const override; bool IsValidSnapArea(SearchAxis axis, const SnapAreaData& area) const override; - const std::optional<SnapSearchResult>& PickBestResult( - const std::optional<SnapSearchResult>& closest, - const std::optional<SnapSearchResult>& covering) const override; - - bool UsingFractionalOffsets() const override; - - std::unique_ptr<SnapSelectionStrategy> Clone() const override; - - private: - // The default step for this DirectionStrategy. - const gfx::Vector2dF step_; - SnapStopAlwaysFilter snap_stop_always_filter_; - bool use_fractional_offsets_; -}; - -// Examples for intended direction and end position scrolls include -// - a “fling” gesture, interpreted with momentum -// - programmatically scrolling via APIs such as scrollBy() -// - paging operations such as the PgUp/PgDn keys (or equivalent operations on -// the scrollbar) -// For this type of scrolls, we want to -// * Minimize the distance between the snap position and the end position. -// * Return the end position if that makes a snap area covers the snapport. -class EndAndDirectionStrategy : public SnapSelectionStrategy { - public: - // |use_fractional_offsets| should be true when the current position is - // provided in fractional pixels. - EndAndDirectionStrategy(const gfx::PointF& current_position, - const gfx::Vector2dF& displacement, - bool use_fractional_offsets) - : SnapSelectionStrategy(current_position), - displacement_(displacement), - use_fractional_offsets_(use_fractional_offsets) {} - EndAndDirectionStrategy(const EndAndDirectionStrategy& other) = default; - ~EndAndDirectionStrategy() override = default; - - bool ShouldSnapOnX() const override; - bool ShouldSnapOnY() const override; - - gfx::PointF intended_position() const override; - gfx::PointF base_position() const override; - - bool IsValidSnapPosition(SearchAxis axis, float position) const override; - bool ShouldRespectSnapStop() const override; const std::optional<SnapSearchResult>& PickBestResult( @@ -228,7 +234,18 @@ std::unique_ptr<SnapSelectionStrategy> Clone() const override; private: - const gfx::Vector2dF displacement_; + // The default step for this DirectionStrategy. + const gfx::Vector2dF step_; + // How strictly to prefer the step's magnitude. + const StepPreference preferred_step_; + + // Some operations, e.g. scrolling by a page, prefer snap areas that + // scroll no more than a certain amount and at least a certain amount. + // 0 represents unrestricted displacement. + const gfx::Vector2dF preferred_min_displacement_; + const gfx::Vector2dF preferred_max_displacement_; + + SnapStopAlwaysFilter snap_stop_always_filter_; bool use_fractional_offsets_; };
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc index 3d56810..a2d09d2 100644 --- a/cc/layers/picture_layer_impl.cc +++ b/cc/layers/picture_layer_impl.cc
@@ -1076,9 +1076,18 @@ if (raster_source_) { const ScrollTree& scroll_tree = layer_tree_impl()->property_trees()->scroll_tree(); + const TransformTree& transform_tree = + layer_tree_impl()->property_trees()->transform_tree(); for (auto [element_id, _] : raster_source_->GetDisplayItemList()->raster_inducing_scrolls()) { - map[element_id] = scroll_tree.current_scroll_offset(element_id); + // The transform node has the realized scroll offset and snap amount, + // and should be used for rendering. + const auto* scroll_node = scroll_tree.FindNodeFromElementId(element_id); + CHECK(scroll_node); + const auto* transform = transform_tree.Node(scroll_node->transform_id); + CHECK(transform); + map[element_id] = + gfx::PointAtOffsetFromOrigin(-transform->to_parent.To2dTranslation()); } } return map;
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc index 2722c16..be679766 100644 --- a/cc/layers/picture_layer_impl_unittest.cc +++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -2301,13 +2301,14 @@ RecordingSource recording; Region invalidation; recording.Update(layer_bounds, 1, client, invalidation); - SetupPendingTreeWithFixedTileSize( - FakeRasterSource::CreateFromRecordingSource(recording), tile_size, - Region()); + SetupPendingTreeWithFixedTileSize(nullptr, tile_size, Region()); + pending_layer()->SetBounds(layer_bounds); CreateScrollNodeForNonCompositedScroller( host_impl()->pending_tree()->property_trees(), pending_layer()->scroll_tree_index(), scroll_element_id, scroll_contents_bounds, scroll_container_bounds, scroll_container_origin); + pending_layer()->SetRasterSource( + FakeRasterSource::CreateFromRecordingSource(recording), invalidation); ScrollTree& pending_scroll_tree = host_impl()->pending_tree()->property_trees()->scroll_tree_mutable(); pending_scroll_tree.SetScrollingContentsCullRect(scroll_element_id, @@ -6702,14 +6703,36 @@ EXPECT_EQ(gfx::Rect(100, 300, 200, 200), info2.visual_rect); EXPECT_FALSE(info2.has_discardable_images); + gfx::Size layer_bounds(500, 500); + SetupPendingTree(); + pending_layer()->SetBounds(layer_bounds); + auto* property_trees = host_impl()->pending_tree()->property_trees(); + auto& scroll_tree = property_trees->scroll_tree_mutable(); + int scroll_node_id1 = + CreateScrollNodeForNonCompositedScroller( + property_trees, pending_layer()->scroll_tree_index(), + scroll_element_id1, gfx::Size(200, 200), gfx::Size(300, 200), + gfx::Point()) + .id; + ASSERT_TRUE(scroll_tree.CanRealizeScrollsOnPendingTree( + *scroll_tree.Node(scroll_node_id1))); + int scroll_node_id2 = + CreateScrollNodeForNonCompositedScroller( + property_trees, scroll_node_id1, scroll_element_id2, + gfx::Size(200, 200), gfx::Size(1000, 1000), gfx::Point(100, 300)) + .id; + ASSERT_TRUE(scroll_tree.CanRealizeScrollsOnPendingTree( + *scroll_tree.Node(scroll_node_id2))); + FakeContentLayerClient client; client.set_display_item_list(display_list); - gfx::Size layer_bounds(500, 500); RecordingSource recording; Region invalidation; recording.Update(layer_bounds, 1, client, invalidation); auto raster = FakeRasterSource::CreateFromRecordingSource(recording); - SetupTreesWithInvalidation(raster, raster, invalidation); + pending_layer()->SetRasterSource(raster, Region()); + ActivateTree(); + SetupPendingTree(raster, layer_bounds, invalidation); pending_layer()->set_invalidation(Region()); auto pending_image_map = pending_layer()->discardable_image_map(); @@ -6720,12 +6743,12 @@ ElementsAre(gfx::Rect(-1, -1, 202, 202))); // Simulate a raster-inducing scroll. - host_impl() - ->pending_tree() - ->property_trees() - ->scroll_tree_mutable() - .GetOrCreateSyncedScrollOffsetForTesting(scroll_element_id1) - ->SetCurrent(gfx::PointF(0, 100)); + scroll_tree.GetOrCreateSyncedScrollOffsetForTesting(scroll_element_id1) + ->SetCurrent(gfx::PointF(0, 100.25f)); + host_impl()->pending_tree()->DidUpdateScrollOffset(scroll_element_id1, false); + property_trees->transform_tree_mutable().UpdateTransforms( + scroll_tree.Node(scroll_node_id1)->transform_id); + // Invalidating scroll_element_id1 will invalidate scroll visual rect. pending_layer()->InvalidateRasterInducingScrolls({scroll_element_id1}); EXPECT_EQ(info1.visual_rect, pending_layer()->invalidation().bounds());
diff --git a/cc/mojom/BUILD.gn b/cc/mojom/BUILD.gn index a4235bb2..cd3fbc16 100644 --- a/cc/mojom/BUILD.gn +++ b/cc/mojom/BUILD.gn
@@ -170,8 +170,8 @@ copyable_pass_by_value = true }, ] - traits_headers = [ "//cc/mojom/synced_scroll_offset_type_traits.h" ] - traits_sources = [ "//cc/mojom/synced_scroll_offset_type_traits.cc" ] + traits_headers = [ "//cc/mojom/synced_scroll_offset_mojom_traits.h" ] + traits_sources = [ "//cc/mojom/synced_scroll_offset_mojom_traits.cc" ] traits_public_deps = [ "//ui/gfx/geometry/mojom" ] }, {
diff --git a/cc/mojom/synced_scroll_offset_type_traits.cc b/cc/mojom/synced_scroll_offset_mojom_traits.cc similarity index 92% rename from cc/mojom/synced_scroll_offset_type_traits.cc rename to cc/mojom/synced_scroll_offset_mojom_traits.cc index dd43bc4..f232572 100644 --- a/cc/mojom/synced_scroll_offset_type_traits.cc +++ b/cc/mojom/synced_scroll_offset_mojom_traits.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "cc/mojom/synced_scroll_offset_type_traits.h" +#include "cc/mojom/synced_scroll_offset_mojom_traits.h" #include <utility>
diff --git a/cc/mojom/synced_scroll_offset_type_traits.h b/cc/mojom/synced_scroll_offset_mojom_traits.h similarity index 84% rename from cc/mojom/synced_scroll_offset_type_traits.h rename to cc/mojom/synced_scroll_offset_mojom_traits.h index 7ffba6f8..6be7cb6 100644 --- a/cc/mojom/synced_scroll_offset_type_traits.h +++ b/cc/mojom/synced_scroll_offset_mojom_traits.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CC_MOJOM_SYNCED_SCROLL_OFFSET_TYPE_TRAITS_H_ -#define CC_MOJOM_SYNCED_SCROLL_OFFSET_TYPE_TRAITS_H_ +#ifndef CC_MOJOM_SYNCED_SCROLL_OFFSET_MOJOM_TRAITS_H_ +#define CC_MOJOM_SYNCED_SCROLL_OFFSET_MOJOM_TRAITS_H_ #include "base/memory/scoped_refptr.h" #include "cc/mojom/synced_scroll_offset.mojom-shared.h" @@ -33,4 +33,4 @@ } // namespace mojo -#endif // CC_MOJOM_SYNCED_SCROLL_OFFSET_TYPE_TRAITS_H_ +#endif // CC_MOJOM_SYNCED_SCROLL_OFFSET_MOJOM_TRAITS_H_
diff --git a/chrome/android/java/res/xml/bookmark_widget_info.xml b/chrome/android/java/res/xml/bookmark_widget_info.xml index 1773346..e05c5ec 100644 --- a/chrome/android/java/res/xml/bookmark_widget_info.xml +++ b/chrome/android/java/res/xml/bookmark_widget_info.xml
@@ -9,7 +9,7 @@ <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="180dp" - android:minHeight="180dp" + android:minHeight="150dp" android:minResizeWidth="@dimen/bookmark_widget_min_width" android:minResizeHeight="@dimen/bookmark_widget_min_height" android:previewImage="@drawable/bookmark_widget_preview"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/BrowsingDataCounterBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/BrowsingDataCounterBridge.java index 7780e41..13f49fd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/BrowsingDataCounterBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/BrowsingDataCounterBridge.java
@@ -8,11 +8,13 @@ import org.jni_zero.JniType; import org.jni_zero.NativeMethods; +import org.chromium.build.annotations.NullMarked; import org.chromium.chrome.browser.profiles.Profile; /** * Communicates between BrowsingDataCounter (C++ backend) and ClearBrowsingDataFragment (Java UI). */ +@NullMarked public class BrowsingDataCounterBridge { /** Can receive a callback from a BrowsingDataCounter. */ public interface BrowsingDataCounterCallback {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataCheckBoxPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataCheckBoxPreference.java index 4072fa9c..5c32d34 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataCheckBoxPreference.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataCheckBoxPreference.java
@@ -16,18 +16,23 @@ import androidx.preference.PreferenceViewHolder; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.util.ChromeAccessibilityUtil; import org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference; import org.chromium.ui.text.ChromeClickableSpan; import org.chromium.ui.text.SpanApplier; +import java.util.Objects; + /** * A preference representing one browsing data type in ClearBrowsingDataFragment. This class allows * clickable links inside the checkbox summary. */ +@NullMarked public class ClearBrowsingDataCheckBoxPreference extends ChromeBaseCheckBoxPreference { - private View mView; - private Runnable mLinkClickDelegate; + private @Nullable View mView; + private @Nullable Runnable mLinkClickDelegate; private boolean mHasClickableSpans; /** Constructor for inflating from XML. */ @@ -94,9 +99,9 @@ } @Override - public void setSummary(CharSequence summary) { + public void setSummary(@Nullable CharSequence summary) { // If there is no link in the summary invoke the default behavior. - String summaryString = summary.toString(); + String summaryString = Objects.requireNonNullElse(summary, "").toString(); if (!summaryString.contains("<link>") || !summaryString.contains("</link>")) { super.setSummary(summary); return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/UrlFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/UrlFilter.java index 659625be..86439f1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/UrlFilter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/UrlFilter.java
@@ -4,7 +4,10 @@ package org.chromium.chrome.browser.browsing_data; +import org.chromium.build.annotations.NullMarked; + /** A URL->boolean filter used in browsing data deletions. */ +@NullMarked public interface UrlFilter { /** * @param url The url to be matched.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/AuthTabColorProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/AuthTabColorProvider.java index 622883e..0de27aad 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/AuthTabColorProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/AuthTabColorProvider.java
@@ -12,29 +12,29 @@ import android.graphics.Color; import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.browser.auth.AuthTabColorSchemeParams; import androidx.browser.auth.AuthTabIntent; import androidx.browser.customtabs.CustomTabsIntent; import org.chromium.base.Log; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.browserservices.intents.ColorProvider; import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils; import org.chromium.ui.util.ColorUtils; /** {@link ColorProvider} implementation used for Auth Tab. */ +@NullMarked public class AuthTabColorProvider implements ColorProvider { private static final String TAG = "AuthTabColorProvider"; private final @ColorInt int mToolbarColor; private final @ColorInt int mBottomBarColor; private final boolean mHasCustomToolbarColor; - @Nullable private final Integer mNavigationBarColor; - @Nullable private final Integer mNavigationBarDividerColor; + private final @Nullable Integer mNavigationBarColor; + private final @Nullable Integer mNavigationBarDividerColor; - private static @NonNull AuthTabColorSchemeParams getColorSchemeParams( - Intent intent, int colorScheme) { + private static AuthTabColorSchemeParams getColorSchemeParams(Intent intent, int colorScheme) { if (colorScheme == COLOR_SCHEME_SYSTEM) { assert false : "Color scheme passed to IntentDataProvider should not be " @@ -51,12 +51,10 @@ } public AuthTabColorProvider( - @NonNull Intent intent, - @NonNull Context context, - @CustomTabsIntent.ColorScheme int colorScheme) { + Intent intent, Context context, @CustomTabsIntent.ColorScheme int colorScheme) { AuthTabColorSchemeParams params = getColorSchemeParams(intent, colorScheme); mHasCustomToolbarColor = params.getToolbarColor() != null; - mToolbarColor = retrieveToolbarColor(params, context, mHasCustomToolbarColor); + mToolbarColor = retrieveToolbarColor(params, context); mBottomBarColor = mToolbarColor; mNavigationBarColor = params.getNavigationBarColor() == null @@ -65,9 +63,8 @@ mNavigationBarDividerColor = params.getNavigationBarDividerColor(); } - private static int retrieveToolbarColor( - AuthTabColorSchemeParams params, Context context, boolean hasCustomToolbarColor) { - if (hasCustomToolbarColor) { + private static int retrieveToolbarColor(AuthTabColorSchemeParams params, Context context) { + if (params.getToolbarColor() != null) { return ColorUtils.getOpaqueColor(params.getToolbarColor()); } return SurfaceColorUpdateUtils.getDefaultThemeColor(context, /* isIncognito= */ false); @@ -84,12 +81,12 @@ } @Override - public Integer getNavigationBarColor() { + public @Nullable Integer getNavigationBarColor() { return mNavigationBarColor; } @Override - public Integer getNavigationBarDividerColor() { + public @Nullable Integer getNavigationBarDividerColor() { return mNavigationBarDividerColor; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAuthUrlHeuristics.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAuthUrlHeuristics.java index cfbf8de..8fee327f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAuthUrlHeuristics.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAuthUrlHeuristics.java
@@ -12,6 +12,8 @@ import org.jni_zero.NativeMethods; import org.chromium.base.metrics.RecordHistogram; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider; import org.chromium.chrome.browser.tab.Tab; import org.chromium.components.embedder_support.util.UrlConstants; @@ -23,6 +25,7 @@ * Helper class to record histogram to determine whether the Custom Tab was launched with what looks * like an OAuth URL. */ +@NullMarked public class CustomTabAuthUrlHeuristics { // This should be kept in sync with the definition |CustomTabsAuthScheme| in // tools/metrics/histograms/metadata/custom_tabs/enums.xml. @@ -112,7 +115,7 @@ CustomTabAuthUrlHeuristics.AuthScheme.COUNT); } - static @CustomTabAuthUrlHeuristics.AuthScheme int getAuthSchemeEnum(String scheme) { + static @CustomTabAuthUrlHeuristics.AuthScheme int getAuthSchemeEnum(@Nullable String scheme) { if (UrlConstants.HTTP_SCHEME.equalsIgnoreCase(scheme)) { return CustomTabAuthUrlHeuristics.AuthScheme.HTTP; } else if (UrlConstants.HTTPS_SCHEME.equalsIgnoreCase(scheme)) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarView.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarView.java index 74e65dd..3c039e795 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabBottomBarView.java
@@ -9,13 +9,14 @@ import android.util.AttributeSet; import android.view.MotionEvent; -import androidx.annotation.Nullable; - +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.components.browser_ui.widget.BoundedLinearLayout; import org.chromium.components.browser_ui.widget.gesture.SwipeGestureListener; import org.chromium.components.browser_ui.widget.gesture.SwipeGestureListener.SwipeHandler; /** A custom container view for the Custom Tab bottom bar that supports swipe gesture handling. */ +@NullMarked public class CustomTabBottomBarView extends BoundedLinearLayout { private final Context mContext; private @Nullable BottomBarSwipeGestureListener mSwipeGestureListener;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabFileUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabFileUtils.java index 867070b..10861a7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabFileUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabFileUtils.java
@@ -4,6 +4,8 @@ package org.chromium.chrome.browser.customtabs; +import org.chromium.build.annotations.NullMarked; + import java.io.File; import java.util.ArrayList; import java.util.Collections; @@ -11,6 +13,7 @@ import java.util.List; /** File utilities for Custom Tabs. */ +@NullMarked public class CustomTabFileUtils { /** Threshold where old state files should be deleted (30 days). */ protected static final long STATE_EXPIRY_THRESHOLD = 30L * 24 * 60 * 60 * 1000;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java index aeaa6106..e791cec 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
@@ -6,13 +6,15 @@ import android.content.Intent; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatDelegate; import androidx.browser.customtabs.CustomTabsIntent; import org.chromium.base.IntentUtils; import org.chromium.base.ObserverList; +import org.chromium.build.annotations.MonotonicNonNull; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; +import org.chromium.build.annotations.RequiresNonNull; import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher; import org.chromium.chrome.browser.lifecycle.DestroyObserver; import org.chromium.chrome.browser.night_mode.NightModeStateProvider; @@ -21,6 +23,7 @@ import org.chromium.chrome.browser.night_mode.SystemNightModeMonitor; /** Maintains and provides the night mode state for {@link CustomTabActivity}. */ +@NullMarked public class CustomTabNightModeStateController implements DestroyObserver, NightModeStateProvider { private final ObserverList<Observer> mObservers = new ObserverList<>(); private final PowerSavingModeMonitor mPowerSavingModeMonitor; @@ -34,7 +37,7 @@ */ private int mRequestedColorScheme; - private AppCompatDelegate mAppCompatDelegate; + private @MonotonicNonNull AppCompatDelegate mAppCompatDelegate; @Nullable // Null initially, so that the first update is always applied (see updateNightMode()). private Boolean mIsInNightMode; @@ -90,12 +93,12 @@ } @Override - public void addObserver(@NonNull Observer observer) { + public void addObserver(Observer observer) { mObservers.addObserver(observer); } @Override - public void removeObserver(@NonNull Observer observer) { + public void removeObserver(Observer observer) { mObservers.removeObserver(observer); } @@ -106,6 +109,7 @@ return false; } + @RequiresNonNull({"mAppCompatDelegate"}) private void updateNightMode() { boolean shouldBeInNightMode = shouldBeInNightMode(); if (mIsInNightMode != null && mIsInNightMode == shouldBeInNightMode) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTaskDescriptionIconGenerator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTaskDescriptionIconGenerator.java index f0ef8f94..6e11a6b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTaskDescriptionIconGenerator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTaskDescriptionIconGenerator.java
@@ -7,10 +7,13 @@ import android.content.Context; import android.graphics.Bitmap; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.components.browser_ui.widget.RoundedIconGenerator; import org.chromium.url.GURL; /** Generates icons suitable for Custom Tabs in the recent tasks list. */ +@NullMarked public class CustomTabTaskDescriptionIconGenerator { private static final int APP_ICON_MIN_SIZE_DP = 32; private static final int APP_ICON_SIZE_DP = 64; @@ -22,13 +25,13 @@ private final int mMinSizePx; /** The page URL for which {@link #mGeneratedIcon} was generated. */ - private GURL mGeneratedPageUrl; + private @Nullable GURL mGeneratedPageUrl; /** The most recently generated icon. */ - private Bitmap mGeneratedIcon; + private @Nullable Bitmap mGeneratedIcon; /** Generates the icon if there is no adequate favicon. */ - private RoundedIconGenerator mGenerator; + private @Nullable RoundedIconGenerator mGenerator; public CustomTabTaskDescriptionIconGenerator(Context context) { mContext = context; @@ -45,7 +48,7 @@ * @param largestFavicon The largest favicon available at the page URL. * @return The icon to use in the recent tasks list. */ - public Bitmap getBitmap(GURL pageUrl, Bitmap largestFavicon) { + public @Nullable Bitmap getBitmap(GURL pageUrl, Bitmap largestFavicon) { if (largestFavicon != null && largestFavicon.getWidth() >= mMinSizePx && largestFavicon.getHeight() >= mMinSizePx) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTrustedCdnPublisherUrlVisibility.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTrustedCdnPublisherUrlVisibility.java index 7b9ef3d..64d6063 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTrustedCdnPublisherUrlVisibility.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabTrustedCdnPublisherUrlVisibility.java
@@ -5,6 +5,8 @@ package org.chromium.chrome.browser.customtabs; import org.chromium.base.UnownedUserData; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher; import org.chromium.chrome.browser.lifecycle.DestroyObserver; import org.chromium.chrome.browser.tab.Tab; @@ -17,9 +19,10 @@ * Implementation of {@link TrustedCdn.PublisherUrlVisibility} to provide Tab with * the availability of publisher URL of trusted CDN when attached to a custom tab activity. */ +@NullMarked class CustomTabTrustedCdnPublisherUrlVisibility implements PublisherUrlVisibility, DestroyObserver, UnownedUserData { - private WindowAndroid mWindowAndroid; + private @Nullable WindowAndroid mWindowAndroid; private BooleanSupplier mIsPublisherPackageForSession; CustomTabTrustedCdnPublisherUrlVisibility( @@ -37,6 +40,7 @@ return mIsPublisherPackageForSession.getAsBoolean(); } + @SuppressWarnings("NullAway") @Override public void onDestroy() { PublisherUrlVisibility.detach(this);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsFeatureUsage.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsFeatureUsage.java index d2a01db4..2f228f8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsFeatureUsage.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsFeatureUsage.java
@@ -8,12 +8,14 @@ import androidx.annotation.VisibleForTesting; import org.chromium.base.metrics.RecordHistogram; +import org.chromium.build.annotations.NullMarked; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.BitSet; /** Records a histogram that tracks usage of all the CCT features of interest. */ +@NullMarked public class CustomTabsFeatureUsage { @VisibleForTesting public static final String CUSTOM_TABS_FEATURE_USAGE_HISTOGRAM = "CustomTabs.FeatureUsage";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsShareBroadcastReceiver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsShareBroadcastReceiver.java index f3336f4..b1098e7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsShareBroadcastReceiver.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsShareBroadcastReceiver.java
@@ -9,11 +9,13 @@ import android.content.Intent; import org.chromium.base.metrics.RecordUserAction; +import org.chromium.build.annotations.NullMarked; /** * Receives shared content broadcast from Chrome Custom Tabs and shows a share sheet to share the * url. */ +@NullMarked public final class CustomTabsShareBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabColorProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabColorProvider.java index 63d1145..23e9172 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabColorProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabColorProvider.java
@@ -7,12 +7,13 @@ import android.content.Context; import android.graphics.Color; -import androidx.annotation.Nullable; - +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.browserservices.intents.ColorProvider; import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils; /** ColorProvider implementation used for incognito profiles. */ +@NullMarked public final class IncognitoCustomTabColorProvider implements ColorProvider { private final int mToolbarColor; private final int mBottomBarColor;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTrigger.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTrigger.java index b33ea51..0aeae8f0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTrigger.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTrigger.java
@@ -8,6 +8,7 @@ import org.chromium.base.Callback; import org.chromium.base.ThreadUtils; +import org.chromium.build.annotations.NullMarked; import org.chromium.chrome.browser.tab.Tab; import java.util.LinkedList; @@ -23,6 +24,7 @@ * If a capture has not been taken after a long amount of time or when the Tab is hidden, we also * capture. */ +@NullMarked public class NavigationInfoCaptureTrigger { private static final int ONLOAD_DELAY_MS = 1000; private static final int ONLOAD_LONG_DELAY_MS = 15000;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/RequestThrottler.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/RequestThrottler.java index 31c4021..19d29fe 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/RequestThrottler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/RequestThrottler.java
@@ -14,6 +14,8 @@ import org.chromium.base.ContextUtils; 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 java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; @@ -28,6 +30,7 @@ * * This class is *not* thread-safe. */ +@NullMarked class RequestThrottler { // These are for (a). private static final long MIN_DELAY = 100; @@ -47,14 +50,14 @@ private static final String BANNED_UNTIL = "banned_until_"; private static final AtomicBoolean sAccessedSharedPreferences = new AtomicBoolean(); - private static SparseArray<RequestThrottler> sUidToThrottler; + private static @Nullable SparseArray<RequestThrottler> sUidToThrottler; private final SharedPreferences mSharedPreferences; private final int mUid; private float mScore; private long mLastPrerenderRequestMs; private long mBannedUntilMs; - private String mUrl; + private @Nullable String mUrl; /** * Updates the prediction stats and returns whether prediction is allowed.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/TwaOfflineDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/TwaOfflineDataProvider.java index 3c50a56c..e510aca5 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/TwaOfflineDataProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/TwaOfflineDataProvider.java
@@ -5,6 +5,8 @@ package org.chromium.chrome.browser.customtabs; import org.chromium.base.UserData; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.tab.Tab; import java.util.List; @@ -13,6 +15,7 @@ * The lifetime of a tab is complicated and not always associated with an Activity. Offline page * info could be required at any time, so we store it with the tab itself. */ +@NullMarked public class TwaOfflineDataProvider implements UserData { private static final Class<TwaOfflineDataProvider> USER_DATA_KEY = TwaOfflineDataProvider.class; @@ -20,7 +23,7 @@ private final List<String> mAdditionalTwaOrigins; private final String mClientPackageName; - public static TwaOfflineDataProvider from(Tab tab) { + public static @Nullable TwaOfflineDataProvider from(Tab tab) { if (tab == null) return null; return tab.getUserDataHost().getUserData(USER_DATA_KEY); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java index f07152a..5c5376f 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
@@ -24,11 +24,11 @@ import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CriteriaHelper; -import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.EnormousTest; import org.chromium.base.test.util.Feature; import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.omnibox.status.StatusCoordinator; +import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider; import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteController.OnSuggestionsReceivedListener; import org.chromium.chrome.browser.profiles.ProfileManager; import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory; @@ -102,7 +102,6 @@ @Test @MediumTest @Feature({"Omnibox"}) - @DisabledTest(message = "crbug.com/419016470 - likely addressed, but can't trigger bot from CQ") public void testDefaultText() { mActivityTestRule.startOnNtp(); @@ -111,13 +110,17 @@ // Omnibox on NTP shows the hint text. Assert.assertNotNull(urlBar); Assert.assertEquals("Location bar has text.", "", urlBar.getText().toString()); - Assert.assertEquals( - "Location bar has incorrect hint.", - mActivityTestRule - .getActivity() - .getResources() - .getString(R.string.omnibox_empty_hint_with_dse_name, "Google"), - urlBar.getHint().toString()); + + CriteriaHelper.pollUiThread( + () -> { + Assert.assertEquals( + "Location bar has incorrect hint.", + OmniboxResourceProvider.getString( + mActivityTestRule.getActivity(), + R.string.omnibox_empty_hint_with_dse_name, + "Google"), + urlBar.getHint().toString()); + }); // Type something in the omnibox. // Note that the TextView does not provide a way to test if the hint is showing, the API
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/adding_new_tests.md b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/adding_new_tests.md index 61e8efbc..3ee4a441 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/adding_new_tests.md +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/adding_new_tests.md
@@ -154,7 +154,7 @@ If you are adding an AR test and none of the existing datasets work for it, you can create and upload a new dataset that fits your needs. Dataset creation requires some internal tools, see go/arcore-chrome-collect-recordings (internal -link) or contact either bsheedy@ or bialpio@ for instructions. +link) or contact bsheedy@ for instructions. Once you have your playback dataset (.mp4 file), simply place it in `//chrome/test/data/xr/ar_playback_datasets/` and upload it using
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt index 4b8bdecd..ad4b5c4 100644 --- a/chrome/android/profiles/arm.newest.txt +++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@ -chromeos-chrome-arm-138.0.7190.0_rc-r1-merged.afdo.bz2 +chromeos-chrome-arm-138.0.7194.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt index 4f13291..0285343 100644 --- a/chrome/android/profiles/newest.txt +++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@ -chromeos-chrome-amd64-138.0.7190.0_rc-r1-merged.afdo.bz2 +chromeos-chrome-amd64-138.0.7194.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd index ef22aef9..aa807fb6 100644 --- a/chrome/app/chromium_strings.grd +++ b/chrome/app/chromium_strings.grd
@@ -1908,6 +1908,10 @@ desc="The label of the Lens overlay entrypoint in the omnibox"> Image Search </message> + <message name="IDS_CONTENT_LENS_OVERLAY_HOMEWORK_ENTRYPOINT_LABEL" + desc="The label of the Lens overlay entrypoint in the omnibox offering homework help"> + Homework help + </message> <message name="IDS_SIDE_PANEL_LENS_OVERLAY_TOOLBAR_TOOLTIP" desc="The tooltip for the Lens overlay toolbar button."> Search with Image Search
diff --git a/chrome/app/chromium_strings_grd/IDS_CONTENT_LENS_OVERLAY_HOMEWORK_ENTRYPOINT_LABEL.png.sha1 b/chrome/app/chromium_strings_grd/IDS_CONTENT_LENS_OVERLAY_HOMEWORK_ENTRYPOINT_LABEL.png.sha1 new file mode 100644 index 0000000..63f3cd24 --- /dev/null +++ b/chrome/app/chromium_strings_grd/IDS_CONTENT_LENS_OVERLAY_HOMEWORK_ENTRYPOINT_LABEL.png.sha1
@@ -0,0 +1 @@ +48544fec5adba6b87e82aa44a07cd66226d2d172 \ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 2cf24c4..3fc773a 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -7757,7 +7757,7 @@ <message name="IDS_NTP_CUSTOMIZE_CHROME_CHANGE_THEME_LABEL" desc="The label for the action button for changing Chrome theme in the New Tab Page customize chrome side panel."> Change theme </message> - <message name="IDS_NTP_CUSTOMIZE_FOOTER_HEADER" desc="The label for the Footer title in the customization menu on the New Tab Page."> + <message name="IDS_NTP_CUSTOMIZE_FOOTER_HEADER" desc="The toggle for the Footer title in the customization menu on the New Tab Page." meaning="The footer which shows at the bottom of the New Tab page."> Footer </message> <message name="IDS_NTP_CUSTOMIZE_CHROME_MANAGED_NEW_TAB_PAGE" desc="The string that shows on the customize chrome UI when a 3rd party manages the new tab page.">
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_FOOTER_HEADER.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_FOOTER_HEADER.png.sha1 index 0f8a76e..ac6bda1 100644 --- a/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_FOOTER_HEADER.png.sha1 +++ b/chrome/app/generated_resources_grd/IDS_NTP_CUSTOMIZE_FOOTER_HEADER.png.sha1
@@ -1 +1 @@ -0a2f0293525b47823871981fb69bf77a01efd6e5 \ No newline at end of file +f50c51861fbbc0e63ebd24734a52c1e725f8a2a7 \ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd index a0f46d3e..5eab363 100644 --- a/chrome/app/google_chrome_strings.grd +++ b/chrome/app/google_chrome_strings.grd
@@ -1922,6 +1922,10 @@ desc="The label of the Lens overlay entrypoint in the omnibox"> Google Lens </message> + <message name="IDS_CONTENT_LENS_OVERLAY_HOMEWORK_ENTRYPOINT_LABEL" + desc="The label of the Lens overlay entrypoint in the omnibox offering homework help"> + Homework help + </message> <message name="IDS_SIDE_PANEL_LENS_OVERLAY_TOOLBAR_TOOLTIP" desc="The tooltip for the Lens overlay toolbar button."> Search with Google Lens
diff --git a/chrome/app/google_chrome_strings_grd/IDS_CONTENT_LENS_OVERLAY_HOMEWORK_ENTRYPOINT_LABEL.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_CONTENT_LENS_OVERLAY_HOMEWORK_ENTRYPOINT_LABEL.png.sha1 new file mode 100644 index 0000000..63f3cd24 --- /dev/null +++ b/chrome/app/google_chrome_strings_grd/IDS_CONTENT_LENS_OVERLAY_HOMEWORK_ENTRYPOINT_LABEL.png.sha1
@@ -0,0 +1 @@ +48544fec5adba6b87e82aa44a07cd66226d2d172 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp index 6a1c149..b92cef7 100644 --- a/chrome/app/os_settings_strings.grdp +++ b/chrome/app/os_settings_strings.grdp
@@ -884,9 +884,6 @@ <message name="IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_KEYMAP_STYLE" desc="The label for Japanese keyboard settings dropdown within the settings page to set the keymap style for the Japanese keyboard. The available dropdown values are 'Custom keymap', 'ATOK', 'MS-IME', 'Kotoeri', and 'ChromeOS'. In Japanese, this string should be 'キー設定の選択'."> Keymap style </message> - <message name="IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_KEYMAP_STYLE_CUSTOM" desc="The label for Japanese settings page to set the keymap style to use a Custom keymap. This is 'Custom Keymap' in Japanese. In Japanese, this string should be 'カスタム'."> - Custom keymap - </message> <message name="IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_KEYMAP_STYLE_ATOK" translateable="false"> ATOK </message>
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_KEYMAP_STYLE_CUSTOM.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_KEYMAP_STYLE_CUSTOM.png.sha1 deleted file mode 100644 index d0f6d0e9..0000000 --- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_KEYMAP_STYLE_CUSTOM.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -254a6961462989a79fd756fe1d5290abbb44d744 \ No newline at end of file
diff --git a/chrome/app/theme/google_chrome b/chrome/app/theme/google_chrome index d005f846..61ee818 160000 --- a/chrome/app/theme/google_chrome +++ b/chrome/app/theme/google_chrome
@@ -1 +1 @@ -Subproject commit d005f846501cdbfcf120e05b86efaa8de3b5b60e +Subproject commit 61ee81844485e7d5e6f0f598124a93ae62dd07db
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index f638761..1329d876 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -4423,6 +4423,7 @@ "//chrome/browser/ui/toolbar/cast:impl", "//chrome/browser/ui/user_education", "//chrome/browser/ui/views/download", + "//chrome/browser/ui/views/new_tab_footer", "//chrome/browser/ui/views/side_panel", "//chrome/browser/ui/views/toolbar", "//chrome/browser/ui/webui:webui_util", @@ -4589,6 +4590,7 @@ "//chrome/browser/ui/browser_window:impl", "//chrome/browser/ui/exclusive_access", "//chrome/browser/ui/views/toolbar", + "//chrome/browser/ui/views/new_tab_footer", "//chrome/browser/ui/webui/searchbox", "//chrome/browser/ui/webui/top_chrome:impl", "//chrome/browser/ui/content_settings:impl", @@ -6780,7 +6782,10 @@ "enterprise/connectors/reporting/reporting_event_router_factory.cc", "enterprise/connectors/reporting/reporting_event_router_factory.h", ] - deps += [ "//components/enterprise/connectors/core" ] + deps += [ + "//components/enterprise/common/proto:chrome_reporting_entity", + "//components/enterprise/connectors/core", + ] } if (enterprise_cloud_content_analysis) {
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 6358be45..4778a56 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -1368,9 +1368,8 @@ std::size(kOmniboxStarterPackExpansionStagingUrl), nullptr}}; const FeatureEntry::FeatureParam kOmniboxSearchAggregatorProdParams[] = { - {"name", "Agentspace (prod)"}, + {"name", "Agentspace"}, {"shortcut", "agentspace"}, - {"icon_url", "https://gstatic.com/vertexaisearch/favicon.png"}, {"search_url", "https://vertexaisearch.cloud.google.com/home/cid/" "8884f744-aae1-4fbc-8a64-b8bf7cbf270e?q={searchTerms}"}, @@ -1378,41 +1377,18 @@ "https://discoveryengine.googleapis.com/v1alpha/projects/862721868538/" "locations/global/collections/default_collection/engines/" "teamfood-v11_1720671063545/completionConfig:completeQuery"}}; -const FeatureEntry::FeatureParam - kOmniboxSearchAggregatorProdWithFallbackIconParams[] = { - {"name", "Agentspace (prod with fallback icon)"}, - {"shortcut", "agentspace"}, - {"search_url", - "https://vertexaisearch.cloud.google.com/home/cid/" - "8884f744-aae1-4fbc-8a64-b8bf7cbf270e?q={searchTerms}"}, - {"suggest_url", - "https://discoveryengine.googleapis.com/v1alpha/projects/862721868538/" - "locations/global/collections/default_collection/engines/" - "teamfood-v11_1720671063545/completionConfig:completeQuery"}}; const FeatureEntry::FeatureParam kOmniboxSearchAggregatorStagingParams[] = { {"name", "Agentspace (staging)"}, {"shortcut", "agentspace"}, {"icon_url", "https://gstatic.com/vertexaisearch/favicon.png"}, {"search_url", "https://vertexaisearch.cloud.google.com/home/cid/" - "8884f744-aae1-4fbc-8a64-b8bf7cbf270e?e=97710846%2C97750609%2C97760709%" - "2C97711975&q={searchTerms}"}, + "3abd7045-7845-4f83-b204-e39fcbca3494?q={searchTerms}&mods=widget_staging_" + "api_mod"}, {"suggest_url", "https://staging-discoveryengine.sandbox.googleapis.com/v1alpha/projects/" "862721868538/locations/global/collections/default_collection/engines/" "teamfood-v11/completionConfig:completeQuery"}}; -const FeatureEntry::FeatureParam kOmniboxSearchAggregatorAlternateParams[] = { - {"name", "NeuraVibe"}, - {"shortcut", "neura"}, - {"icon_url", "https://gstatic.com/vertexaisearch/favicon.png"}, - {"search_url", - "https://vertexaisearch.cloud.google.com/home/cid/" - "e04a19e6-1fc2-48ba-9d0d-53c6aabcba7b?e=97844069%2C-97770083&" - "q={searchTerms}"}, - {"suggest_url", - "https://discoveryengine.googleapis.com/v1alpha/projects/301214329925/" - "locations/global/collections/default_collection/engines/" - "neuravibeblendedsearch_1727383849310/completionConfig:completeQuery"}}; const FeatureEntry::FeatureParam kOmniboxSearchAggregatorDemoParams[] = { {"name", "Neuravibe"}, {"shortcut", "neura"}, @@ -1427,13 +1403,8 @@ const FeatureEntry::FeatureVariation kOmniboxSearchAggregatorVariations[] = { {"prod", kOmniboxSearchAggregatorProdParams, std::size(kOmniboxSearchAggregatorProdParams), nullptr}, - {"prod (with fallback icon)", - kOmniboxSearchAggregatorProdWithFallbackIconParams, - std::size(kOmniboxSearchAggregatorProdWithFallbackIconParams), nullptr}, {"staging", kOmniboxSearchAggregatorStagingParams, std::size(kOmniboxSearchAggregatorStagingParams), nullptr}, - {"alternate", kOmniboxSearchAggregatorAlternateParams, - std::size(kOmniboxSearchAggregatorAlternateParams), nullptr}, {"demo", kOmniboxSearchAggregatorDemoParams, std::size(kOmniboxSearchAggregatorDemoParams), nullptr}}; @@ -12563,6 +12534,16 @@ #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || // BUILDFLAG(IS_CHROME_OS) + {"autofill-enable-multiple-request-in-virtual-card-downstream-enrollment", + flag_descriptions:: + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollmentName, + flag_descriptions:: + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollmentDescription, + kOsAll, + FEATURE_VALUE_TYPE( + autofill::features:: + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment)}, + // Add new entries above this line. // NOTE: Adding a new flag requires adding a corresponding entry to enum
diff --git a/chrome/browser/actor/actor_coordinator.cc b/chrome/browser/actor/actor_coordinator.cc index 1917679e..125c05d 100644 --- a/chrome/browser/actor/actor_coordinator.cc +++ b/chrome/browser/actor/actor_coordinator.cc
@@ -142,7 +142,8 @@ } void ActorCoordinator::StartTask(const BrowserAction& action, - StartTaskCallback callback) { + StartTaskCallback callback, + std::optional<tabs::TabHandle> tab_handle) { CHECK(base::FeatureList::IsEnabled(features::kGlicActor)); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -150,8 +151,9 @@ // Posts to a sequence to avoid potential races with multiple attempts to // start a task. base::SequencedTaskRunner::GetCurrentDefault()->PostTask( - FROM_HERE, base::BindOnce(&ActorCoordinator::TryStartNewTask, - GetWeakPtr(), action, std::move(callback))); + FROM_HERE, + base::BindOnce(&ActorCoordinator::TryStartNewTask, GetWeakPtr(), action, + std::move(callback), std::move(tab_handle))); } void ActorCoordinator::StopTask() { @@ -227,8 +229,10 @@ web_contents.GetPrimaryMainFrame()->GetLastCommittedOrigin())); } -void ActorCoordinator::TryStartNewTask(const BrowserAction& action, - StartTaskCallback callback) { +void ActorCoordinator::TryStartNewTask( + const BrowserAction& action, + StartTaskCallback callback, + std::optional<tabs::TabHandle> tab_handle) { // Check for a failed attempt to initialize a new task, where there is a new // tab observer but it's navigation handle is no longer valid. The expectation // is that new tab observer will be notified regardless if the navigation @@ -256,7 +260,23 @@ initializing_new_task_ = true; - // Force the task to be performed in a new tab. + // If a tab handle was provided, try to get the tab interface + if (tab_handle) { + if (auto* tab_interface = tab_handle->Get()) { + // Create a new task with the existing tab + task_state_ = std::make_unique<Task>(*tab_interface); + initializing_new_task_ = false; + PostTaskForStartCallback(std::move(callback), task_state_->tab); + return; + } + // If we couldn't get the tab interface, error out + VLOG(1) << "Could not get tab interface for handle"; + PostTaskForStartInitializationFailed(std::move(callback)); + return; + } + + // If no tab handle was provided, create a new tab + VLOG(1) << "No tab handle provided, creating new tab"; CreateNewTab(std::move(callback)); }
diff --git a/chrome/browser/actor/actor_coordinator.h b/chrome/browser/actor/actor_coordinator.h index 873d90b7..18e9e1f 100644 --- a/chrome/browser/actor/actor_coordinator.h +++ b/chrome/browser/actor/actor_coordinator.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_ACTOR_ACTOR_COORDINATOR_H_ #include <memory> +#include <optional> #include "base/functional/callback.h" #include "base/memory/raw_ptr.h" @@ -14,6 +15,7 @@ #include "base/types/id_type.h" #include "chrome/browser/actor/tools/tool_controller.h" #include "chrome/common/actor.mojom-forward.h" +#include "components/tabs/public/tab_interface.h" #include "content/public/browser/web_contents_observer.h" class Profile; @@ -60,17 +62,18 @@ static void RegisterWithProfile(Profile* profile); // Starts a new task. - // Currently, requires a navigate action to start, and always creates a new - // tab. - // If starting the task succeeds, provides the newly-created tab in the - // callback, otherwise null. - // Starting the task may fail for any of: + // Currently, requires a navigate action to start. + // If starting the task succeeds, provides the tab in the callback, otherwise + // null. Starting the task may fail for any of: // - The `action` is not navigate. // - There is already a task started, or attempting to create a new tab to // start a task. - // - Unable to create a new tab. + // - If a tab handle is provided, the tab must exist and be valid. The task + // will fail if the tab cannot be found or is invalid. + // - If no tab handle is provided, a new tab will be created. void StartTask(const optimization_guide::proto::BrowserAction& action, - StartTaskCallback callback); + StartTaskCallback callback, + std::optional<tabs::TabHandle> tab_handle); // Stops the currently running task, if one is active. Callbacks for // in-progress actions are invoked. @@ -99,7 +102,8 @@ // Starts a new task, after validating there isn't already a task being // initialized or in progress. void TryStartNewTask(const optimization_guide::proto::BrowserAction& action, - StartTaskCallback callback); + StartTaskCallback callback, + std::optional<tabs::TabHandle> tab_handle); // Invokes the StartTask callback when initializing a new task failed (e.g. // error creating a new tab). Must be called to reset from the "initializing"
diff --git a/chrome/browser/ai/ai_data_keyed_service.cc b/chrome/browser/ai/ai_data_keyed_service.cc index 753f649f..41a20684 100644 --- a/chrome/browser/ai/ai_data_keyed_service.cc +++ b/chrome/browser/ai/ai_data_keyed_service.cc
@@ -65,6 +65,7 @@ #include "ui/gfx/geometry/rect.h" #if BUILDFLAG(ENABLE_GLIC) +#include "chrome/browser/actor/actor_coordinator.h" #include "chrome/browser/glic/host/context/glic_page_context_fetcher.h" #include "chrome/browser/glic/host/context/glic_tab_data.h" #include "chrome/browser/glic/host/glic.mojom.h" @@ -892,7 +893,9 @@ dummy_navigate_action, base::BindOnce(&AiDataKeyedService::OnTaskCreated, weak_factory_.GetWeakPtr(), std::move(callback), task_id_, - tab_id_)); + tab_id_), + task.has_tab_id() ? std::make_optional(tabs::TabHandle(task.tab_id())) + : std::nullopt); #endif // BUILDFLAG(ENABLE_GLIC) }
diff --git a/chrome/browser/ai/ai_on_device_browsertest.cc b/chrome/browser/ai/ai_on_device_browsertest.cc index b330827..daf0d85c 100644 --- a/chrome/browser/ai/ai_on_device_browsertest.cc +++ b/chrome/browser/ai/ai_on_device_browsertest.cc
@@ -4,6 +4,7 @@ #include <string> +#include "base/strings/stringprintf.h" #include "base/test/scoped_feature_list.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc index 051203e..fa5ee4c 100644 --- a/chrome/browser/apps/guest_view/web_view_browsertest.cc +++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -23,6 +23,7 @@ #include "base/run_loop.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/task/single_thread_task_runner.h" #include "base/test/bind.h" @@ -7205,11 +7206,16 @@ // Helper class to turn off strict site isolation while still using site // isolation paths for <webview>. This forces <webview> to use the default -// SiteInstance paths. The helper also defines one isolated origin at -// isolated.com, which takes precedence over the command-line switch to disable -// site isolation and can be used to test a combination of SiteInstances that -// require and don't require dedicated processes. -class WebViewWithDefaultSiteInstanceTest : public SitePerProcessWebViewTest { +// SiteInstance or default SiteInstanceGroup paths. The helper also defines one +// isolated origin at isolated.com, which takes precedence over the command-line +// switch to disable site isolation and can be used to test a combination of +// SiteInstances that require and don't require dedicated processes. +// This test is parameterized to run in MPArch or InnerWebContents mode, and +// DefaultSiteInstance or DefaultSiteInstanceGroup mode, totaling 4 +// configurations. +class WebViewWithDefaultSiteInstanceTest + : public WebViewTestBase, + public testing::WithParamInterface<testing::tuple<bool, bool>> { public: WebViewWithDefaultSiteInstanceTest() = default; ~WebViewWithDefaultSiteInstanceTest() override = default; @@ -7222,28 +7228,42 @@ command_line->AppendSwitch(switches::kDisableSiteIsolation); command_line->AppendSwitchASCII(switches::kIsolateOrigins, "http://isolated.com"); - SitePerProcessWebViewTest::SetUpCommandLine(command_line); + feature_list_.InitWithFeatureStates( + {{features::kGuestViewMPArch, testing::get<0>(GetParam())}, + {features::kDefaultSiteInstanceGroups, testing::get<1>(GetParam())}}); + + WebViewTestBase::SetUpCommandLine(command_line); } content::test::FencedFrameTestHelper& fenced_frame_test_helper() { return fenced_frame_test_helper_; } + static std::string DescribeParams( + const testing::TestParamInfo<ParamType>& info) { + const auto [mparch, site_instance_group] = info.param; + return base::StringPrintf("%s_%s", mparch ? "MPArch" : "InnerWebContents", + site_instance_group ? "DefaultSiteInstanceGroups" + : "DefaultSiteInstances"); + } + private: content::test::FencedFrameTestHelper fenced_frame_test_helper_; + base::test::ScopedFeatureList feature_list_; }; INSTANTIATE_TEST_SUITE_P(/* no prefix */, WebViewWithDefaultSiteInstanceTest, - testing::Bool(), + testing::Combine(testing::Bool(), testing::Bool()), WebViewWithDefaultSiteInstanceTest::DescribeParams); -// Check that when strict site isolation is turned off (via a command-line flag -// or from chrome://flags), the <webview> site isolation paths still work. In -// particular, <webview> navigations should use a default SiteInstance which -// should still be considered a guest SiteInstance in the guest's -// StoragePartition. Cross-site navigations in the guest should stay in the -// same SiteInstance, and the guest process shouldn't be locked. +// Check that when strict site isolation is turned off (via a command-line +// flag or from chrome://flags), the <webview> site isolation paths still +// work. In particular, <webview> navigations should use a default +// SiteInstance or default SiteInstanceGroup which should still be considered +// a guest SiteInstance in the guest's StoragePartition. Cross-site +// navigations in the guest should stay in the same SiteInstance or +// SiteInstanceGroup, and the guest process shouldn't be locked. IN_PROC_BROWSER_TEST_P(WebViewWithDefaultSiteInstanceTest, SimpleNavigations) { ASSERT_TRUE(StartEmbeddedTestServer()); @@ -7275,27 +7295,40 @@ load_observer.Wait(); } - // Expect that we stayed in the same (default) SiteInstance. + // Expect that we stayed in the same (default) SiteInstance or + // SiteInstanceGroup. main_frame = GetGuestRenderFrameHost(); ASSERT_TRUE(main_frame); - if (!main_frame->ShouldChangeRenderFrameHostOnSameSiteNavigation()) { - // The RenderFrameHost will stay the same when we don't change - // RenderFrameHosts on same-SiteInstance navigations. - EXPECT_EQ(main_frame->GetGlobalId(), original_id); + if (base::FeatureList::IsEnabled(features::kDefaultSiteInstanceGroups)) { + EXPECT_NE(starting_instance, main_frame->GetSiteInstance()); + EXPECT_EQ(starting_instance->GetSiteInstanceGroupId(), + main_frame->GetSiteInstance()->GetSiteInstanceGroupId()); + } else { + EXPECT_EQ(starting_instance, main_frame->GetSiteInstance()); + if (!main_frame->ShouldChangeRenderFrameHostOnSameSiteNavigation()) { + // The RenderFrameHost will stay the same when we don't change + // RenderFrameHosts on same-SiteInstance navigations. + EXPECT_EQ(main_frame->GetGlobalId(), original_id); + } } - EXPECT_EQ(starting_instance, main_frame->GetSiteInstance()); EXPECT_FALSE(main_frame->GetSiteInstance()->RequiresDedicatedProcess()); EXPECT_FALSE(main_frame->GetProcess()->IsProcessLockedToSiteForTesting()); // Navigate <webview> subframe cross-site. Check that it stays in the same - // SiteInstance and process. + // process, and SiteInstance/Group. const GURL frame_url = embedded_test_server()->GetURL("b.test", "/title1.html"); content::RenderFrameHost* subframe = content::ChildFrameAt(main_frame, 0); ASSERT_TRUE(subframe); EXPECT_TRUE(NavigateToURLFromRenderer(subframe, frame_url)); subframe = content::ChildFrameAt(main_frame, 0); - EXPECT_EQ(main_frame->GetSiteInstance(), subframe->GetSiteInstance()); + if (base::FeatureList::IsEnabled(features::kDefaultSiteInstanceGroups)) { + EXPECT_NE(main_frame->GetSiteInstance(), subframe->GetSiteInstance()); + EXPECT_EQ(main_frame->GetSiteInstance()->GetSiteInstanceGroupId(), + subframe->GetSiteInstance()->GetSiteInstanceGroupId()); + } else { + EXPECT_EQ(main_frame->GetSiteInstance(), subframe->GetSiteInstance()); + } EXPECT_EQ(main_frame->GetProcess(), subframe->GetProcess()); EXPECT_TRUE(subframe->GetSiteInstance()->IsGuest()); EXPECT_FALSE(subframe->GetSiteInstance()->RequiresDedicatedProcess()); @@ -7303,10 +7336,11 @@ } // Similar to the test above, but also exercises navigations to an isolated -// origin, which takes precedence over switches::kDisableSiteIsolation. In this -// setup, navigations to the isolated origin should use a normal SiteInstance -// that requires a dedicated process, while all other navigations should use -// the default SiteInstance and an unlocked process. +// origin, which takes precedence over switches::kDisableSiteIsolation. In +// this setup, navigations to the isolated origin should use a normal +// SiteInstance that requires a dedicated process, while all other navigations +// should use the default SiteInstance or default SiteInstanceGroup and an +// unlocked process. IN_PROC_BROWSER_TEST_P(WebViewWithDefaultSiteInstanceTest, IsolatedOrigin) { ASSERT_TRUE(StartEmbeddedTestServer());
diff --git a/chrome/browser/ash/printing/oauth2/http_exchange.cc b/chrome/browser/ash/printing/oauth2/http_exchange.cc index 4868847..9942233 100644 --- a/chrome/browser/ash/printing/oauth2/http_exchange.cc +++ b/chrome/browser/ash/printing/oauth2/http_exchange.cc
@@ -14,6 +14,7 @@ #include "base/strings/escape.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" #include "base/values.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "services/network/public/cpp/resource_request.h"
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc index 022cc878..8f1686b5e 100644 --- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc +++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -559,19 +559,32 @@ #if !BUILDFLAG(IS_ANDROID) if (auto* lens_search_controller = GetLensSearchController(GetWebContents(web_contents_getter_))) { - // Only allow Lens entrypoints if the Lens overlay is enabled and Lens is - // not currently active. Guaranteed to exist if lens_search_controller is - // not null. + // Guaranteed to exist if lens_search_controller is not null. return lens_search_controller->GetTabInterface() ->GetBrowserWindowInterface() ->GetFeatures() .lens_overlay_entry_point_controller() - ->AreVisible(); + ->IsEnabled(); } #endif return false; } +bool ChromeAutocompleteProviderClient::AreLensEntrypointsVisible() const { + #if !BUILDFLAG(IS_ANDROID) + if (auto* lens_search_controller = + GetLensSearchController(GetWebContents(web_contents_getter_))) { + // Guaranteed to exist if lens_search_controller is not null. + return lens_search_controller->GetTabInterface() + ->GetBrowserWindowInterface() + ->GetFeatures() + .lens_overlay_entry_point_controller() + ->AreVisible(); + } + #endif + return false; + } + base::CallbackListSubscription ChromeAutocompleteProviderClient::GetLensSuggestInputsWhenReady( LensOverlaySuggestInputsCallback callback) const { @@ -648,12 +661,12 @@ if (auto* lens_search_controller = GetLensSearchController(GetWebContents(web_contents_getter_))) { if (show) { + lens_search_controller->OpenLensOverlay( + lens::LensOverlayInvocationSource::kOmniboxPageAction); + } else { // TODO(crbug.com/402497756): For prototyping, reusing the existing // omnibox entry point. However, for production, create a new invocation // source for this new entry point. - lens_search_controller->OpenLensOverlay( - lens::LensOverlayInvocationSource::kOmnibox); - } else { lens_search_controller->StartContextualization( lens::LensOverlayInvocationSource::kOmnibox); } @@ -669,6 +682,7 @@ if (auto* lens_search_controller = GetLensSearchController(GetWebContents(web_contents_getter_))) { lens_search_controller->IssueContextualSearchRequest( + lens::LensOverlayInvocationSource::kOmniboxContextualSuggestion, destination_url, match_type, is_zero_prefix_suggestion); } #endif // !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h index cfb587c0..b17c620b 100644 --- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h +++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.h
@@ -123,6 +123,7 @@ bool IsHistoryEmbeddingsEnabled() const override; bool IsHistoryEmbeddingsSettingVisible() const override; bool IsLensEnabled() const override; + bool AreLensEntrypointsVisible() const override; base::CallbackListSubscription GetLensSuggestInputsWhenReady( LensOverlaySuggestInputsCallback callback) const override; base::WeakPtr<AutocompleteProviderClient> GetWeakPtr() override;
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client_browsertest.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client_browsertest.cc index 83c79dd..791acff 100644 --- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client_browsertest.cc +++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client_browsertest.cc
@@ -123,7 +123,7 @@ .Times(1) .WillOnce(testing::Invoke( [](lens::LensOverlayInvocationSource invocation_source) { - EXPECT_EQ(lens::LensOverlayInvocationSource::kOmnibox, + EXPECT_EQ(lens::LensOverlayInvocationSource::kOmniboxPageAction, invocation_source); }));
diff --git a/chrome/browser/autofill/autofill_across_iframes_browsertest.cc b/chrome/browser/autofill/autofill_across_iframes_browsertest.cc index 1cad3e2..91242c6 100644 --- a/chrome/browser/autofill/autofill_across_iframes_browsertest.cc +++ b/chrome/browser/autofill/autofill_across_iframes_browsertest.cc
@@ -13,6 +13,7 @@ #include <vector> #include "base/command_line.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" #include "build/build_config.h"
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index df1b55e5..a3466de 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -1980,14 +1980,15 @@ return true; } -std::optional< - content::ContentBrowserClient::SpareProcessRefusedByEmbedderReason> -ChromeContentBrowserClient::ShouldUseSpareRenderProcessHost( +bool ChromeContentBrowserClient::ShouldUseSpareRenderProcessHost( content::BrowserContext* browser_context, - const GURL& site_url) { + const GURL& site_url, + std::optional<SpareProcessRefusedByEmbedderReason>& refused_reason) { + refused_reason = std::nullopt; Profile* profile = Profile::FromBrowserContext(browser_context); if (!profile) { - return SpareProcessRefusedByEmbedderReason::NoProfile; + refused_reason = SpareProcessRefusedByEmbedderReason::NoProfile; + return false; } #if !BUILDFLAG(IS_ANDROID) @@ -1999,17 +2000,20 @@ // The NTP page chrome://new-tab-page and chrome://new-tab-page-third-party // are using WebUI and will not use instant renderer. // The only usecase is chrome-search:// URLs. - return SpareProcessRefusedByEmbedderReason::InstantRendererForNewTabPage; + refused_reason = + SpareProcessRefusedByEmbedderReason::InstantRendererForNewTabPage; + return false; } #endif #if BUILDFLAG(ENABLE_EXTENSIONS_CORE) if (!ChromeContentBrowserClientExtensionsPart:: ShouldUseSpareRenderProcessHost(profile, site_url)) { - return SpareProcessRefusedByEmbedderReason::ExtensionProcess; + refused_reason = SpareProcessRefusedByEmbedderReason::ExtensionProcess; + return false; } #endif - return std::nullopt; + return true; } bool ChromeContentBrowserClient::DoesSiteRequireDedicatedProcess(
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h index 93129eb1..5e38776 100644 --- a/chrome/browser/chrome_content_browser_client.h +++ b/chrome/browser/chrome_content_browser_client.h
@@ -208,9 +208,11 @@ const GURL& site_instance_original_url) override; bool ShouldAllowProcessPerSiteForMultipleMainFrames( content::BrowserContext* context) override; - std::optional<SpareProcessRefusedByEmbedderReason> - ShouldUseSpareRenderProcessHost(content::BrowserContext* browser_context, - const GURL& site_url) override; + bool ShouldUseSpareRenderProcessHost( + content::BrowserContext* browser_context, + const GURL& site_url, + std::optional<SpareProcessRefusedByEmbedderReason>& refused_reason) + override; bool DoesSiteRequireDedicatedProcess(content::BrowserContext* browser_context, const GURL& effective_site_url) override; bool ShouldAllowCrossProcessSandboxedFrameForPrecursor(
diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc index f57f609..96855bb1 100644 --- a/chrome/browser/chrome_content_browser_client_unittest.cc +++ b/chrome/browser/chrome_content_browser_client_unittest.cc
@@ -1639,27 +1639,31 @@ content::ContentBrowserClient::SpareProcessRefusedByEmbedderReason; ChromeContentBrowserClient browser_client; + std::optional<SpareProcessRefusedByEmbedderReason> refused_reason; // Standard web URL - EXPECT_FALSE(browser_client.ShouldUseSpareRenderProcessHost( - &profile_, GURL("https://www.example.com"))); + EXPECT_TRUE(browser_client.ShouldUseSpareRenderProcessHost( + &profile_, GURL("https://www.example.com"), refused_reason)); + EXPECT_FALSE(refused_reason.has_value()); // No profile - EXPECT_EQ(SpareProcessRefusedByEmbedderReason::NoProfile, - browser_client.ShouldUseSpareRenderProcessHost( - nullptr, GURL("https://www.example.com"))); + EXPECT_FALSE(browser_client.ShouldUseSpareRenderProcessHost( + nullptr, GURL("https://www.example.com"), refused_reason)); + EXPECT_EQ(SpareProcessRefusedByEmbedderReason::NoProfile, refused_reason); #if !BUILDFLAG(IS_ANDROID) // Chrome-search URL + EXPECT_FALSE(browser_client.ShouldUseSpareRenderProcessHost( + &profile_, GURL("chrome-search://test"), refused_reason)); EXPECT_EQ(SpareProcessRefusedByEmbedderReason::InstantRendererForNewTabPage, - browser_client.ShouldUseSpareRenderProcessHost( - &profile_, GURL("chrome-search://test"))); + refused_reason); #endif #if BUILDFLAG(ENABLE_EXTENSIONS_CORE) // Extension URL + EXPECT_FALSE(browser_client.ShouldUseSpareRenderProcessHost( + &profile_, GURL("chrome-extension://test-extension/"), refused_reason)); EXPECT_EQ(SpareProcessRefusedByEmbedderReason::ExtensionProcess, - browser_client.ShouldUseSpareRenderProcessHost( - &profile_, GURL("chrome-extension://test-extension/"))); + refused_reason); #endif }
diff --git a/chrome/browser/enterprise/connectors/reporting/crash_reporting_context.cc b/chrome/browser/enterprise/connectors/reporting/crash_reporting_context.cc index 21a11a7e..368fd5d 100644 --- a/chrome/browser/enterprise/connectors/reporting/crash_reporting_context.cc +++ b/chrome/browser/enterprise/connectors/reporting/crash_reporting_context.cc
@@ -13,8 +13,12 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/common/channel_info.h" #include "components/crash/core/app/crashpad.h" +#include "components/enterprise/common/proto/synced_from_google3/chrome_reporting_entity.pb.h" #include "components/enterprise/connectors/core/connectors_prefs.h" +#include "components/enterprise/connectors/core/realtime_reporting_client_base.h" #include "components/enterprise/connectors/core/reporting_service_settings.h" +#include "components/enterprise/connectors/core/reporting_utils.h" +#include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h" #include "components/prefs/pref_service.h" #include "components/version_info/version_info.h" @@ -40,6 +44,37 @@ ->chrome_browser_cloud_management_controller(); } +base::Value::Dict GetBrowserCrashEventDeprecated(const std::string& channel, + const std::string& version, + const std::string& report_id, + const std::string& platform) { + base::Value::Dict event; + event.Set(kKeyChannel, channel); + event.Set(kKeyVersion, version); + event.Set(kKeyReportId, report_id); + event.Set(kKeyPlatform, platform); + + return event; +} + +::chrome::cros::reporting::proto::Event GetBrowserCrashEvent( + const std::string& channel, + const std::string& version, + const std::string& report_id, + const std::string& platform, + time_t report_creation_time) { + ::chrome::cros::reporting::proto::Event event; + auto* browser_crash_event = event.mutable_browser_crash_event(); + browser_crash_event->set_channel(channel); + browser_crash_event->set_version(version); + browser_crash_event->set_report_id(report_id); + browser_crash_event->set_platform(platform); + *event.mutable_time() = + ToProtoTimestamp(base::Time::FromTimeT(report_creation_time)); + + return event; +} + // Copy new reports (i.e. reports that have not been sent to the // reporting server) from `reports_to_be_copied` to `reports` // based on the `latest_creation_time`. @@ -169,14 +204,19 @@ int64_t latest_creation_time = -1; for (const auto& report : reports) { - base::Value::Dict event; - event.Set(kKeyChannel, channel); - event.Set(kKeyVersion, version); - event.Set(kKeyReportId, report.id); - event.Set(kKeyPlatform, platform); - reporting_client->ReportPastEvent( - kBrowserCrashEvent, settings.value(), std::move(event), - base::Time::FromTimeT(report.creation_time)); + if (base::FeatureList::IsEnabled( + policy::kUploadRealtimeReportingEventsUsingProto)) { + reporting_client->ReportEvent( + GetBrowserCrashEvent(channel, version, report.id, platform, + report.creation_time), + settings.value()); + } else { + reporting_client->ReportPastEvent( + kBrowserCrashEvent, settings.value(), + GetBrowserCrashEventDeprecated(channel, version, report.id, platform), + base::Time::FromTimeT(report.creation_time)); + } + if (report.creation_time > latest_creation_time) { latest_creation_time = report.creation_time; }
diff --git a/chrome/browser/enterprise/connectors/reporting/crash_reporting_context_unittest.cc b/chrome/browser/enterprise/connectors/reporting/crash_reporting_context_unittest.cc index 8865cfb..53f6d42 100644 --- a/chrome/browser/enterprise/connectors/reporting/crash_reporting_context_unittest.cc +++ b/chrome/browser/enterprise/connectors/reporting/crash_reporting_context_unittest.cc
@@ -5,6 +5,8 @@ #include "base/command_line.h" #include "base/files/scoped_temp_dir.h" +#include "base/test/protobuf_matchers.h" +#include "base/test/scoped_feature_list.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/enterprise/connectors/reporting/realtime_reporting_client.h" #include "chrome/browser/enterprise/connectors/reporting/realtime_reporting_client_factory.h" @@ -16,6 +18,8 @@ #include "components/enterprise/connectors/core/connectors_prefs.h" #include "components/enterprise/connectors/core/reporting_service_settings.h" #include "components/enterprise/connectors/core/reporting_test_utils.h" +#include "components/enterprise/connectors/core/reporting_utils.h" +#include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h" #include "components/version_info/version_info.h" #include "content/public/test/browser_task_environment.h" #include "testing/gmock/include/gmock/gmock.h" @@ -26,8 +30,10 @@ #include "chrome/test/base/scoped_channel_override.h" #endif +using base::test::EqualsProto; using ::testing::_; using ::testing::ByMove; +using ::testing::ByRef; using ::testing::Eq; using ::testing::Return; @@ -60,18 +66,31 @@ } // namespace -class CrashReportingContextTest : public testing::Test { +class CrashReportingContextTest : public testing::TestWithParam<bool> { public: CrashReportingContextTest() : profile_manager_(TestingBrowserProcess::GetGlobal()) {} - void SetUp() override { EXPECT_TRUE(profile_manager_.SetUp()); } + void SetUp() override { + EXPECT_TRUE(profile_manager_.SetUp()); + + if (use_proto_format()) { + feature_list_.InitAndEnableFeature( + policy::kUploadRealtimeReportingEventsUsingProto); + } else { + feature_list_.InitAndDisableFeature( + policy::kUploadRealtimeReportingEventsUsingProto); + } + } + + bool use_proto_format() { return GetParam(); } content::BrowserTaskEnvironment task_environment_; TestingProfileManager profile_manager_; + base::test::ScopedFeatureList feature_list_; }; -TEST_F(CrashReportingContextTest, GetNewReportsFromDB) { +TEST_P(CrashReportingContextTest, GetNewReportsFromDB) { base::ScopedTempDir database_dir; ASSERT_TRUE(database_dir.CreateUniqueTempDir()); std::unique_ptr<crashpad::CrashReportDatabase> database = @@ -86,7 +105,7 @@ EXPECT_EQ(reports.size(), 1u); } -TEST_F(CrashReportingContextTest, GetAndSetLatestCrashReportingTime) { +TEST_P(CrashReportingContextTest, GetAndSetLatestCrashReportingTime) { time_t timestamp = base::Time::Now().ToTimeT(); SetLatestCrashReportTime(g_browser_process->local_state(), timestamp); @@ -94,7 +113,7 @@ GetLatestCrashReportTime(g_browser_process->local_state())); } -TEST_F(CrashReportingContextTest, UploadToReportingServer) { +TEST_P(CrashReportingContextTest, UploadToReportingServer) { EXPECT_EQ(static_cast<long>(0u), GetLatestCrashReportTime(g_browser_process->local_state())); @@ -102,6 +121,7 @@ std::vector<crashpad::CrashReportDatabase::Report> reports; crashpad::CrashReportDatabase::Report report; report.creation_time = timestamp; + report.id = "123"; reports.push_back(report); TestingProfile* profile = @@ -121,16 +141,45 @@ static_cast<test::MockRealtimeReportingClient*>( RealtimeReportingClientFactory::GetForProfile(profile)); - EXPECT_CALL(*reporting_client, - ReportPastEvent(kBrowserCrashEvent, _, _, - base::Time::FromTimeT(timestamp))) - .Times(1); + ::chrome::cros::reporting::proto::Event expected_event_proto; + base::Value::Dict expected_event; + + if (use_proto_format()) { + auto* browser_crash_event = + expected_event_proto.mutable_browser_crash_event(); + browser_crash_event->set_channel( + version_info::GetChannelString(chrome::GetChannel())); + browser_crash_event->set_version(version_info::GetVersionNumber()); + browser_crash_event->set_report_id("123"); + browser_crash_event->set_platform(version_info::GetOSType()); + *expected_event_proto.mutable_time() = + ToProtoTimestamp(base::Time::FromTimeT(timestamp)); + + EXPECT_CALL(*reporting_client, + ReportEvent(EqualsProto(expected_event_proto), _)) + .Times(1); + } else { + expected_event.Set("channel", + version_info::GetChannelString(chrome::GetChannel())); + expected_event.Set("version", version_info::GetVersionNumber()); + expected_event.Set("reportId", "123"); + expected_event.Set("platform", version_info::GetOSType()); + + EXPECT_CALL( + *reporting_client, + ReportPastEvent(kBrowserCrashEvent, _, Eq(ByRef(expected_event)), + base::Time::FromTimeT(timestamp))) + .Times(1); + } + UploadToReportingServer(reporting_client->AsWeakPtrImpl(), g_browser_process->local_state(), reports); EXPECT_EQ(timestamp, GetLatestCrashReportTime(g_browser_process->local_state())); } +INSTANTIATE_TEST_SUITE_P(, CrashReportingContextTest, ::testing::Bool()); + #if BUILDFLAG(GOOGLE_CHROME_BRANDING) && !BUILDFLAG(IS_ANDROID) struct PollingIntervalParams { @@ -151,9 +200,9 @@ TEST_P(CrashpadPollingIntervalTest, GetCrashpadPollingInterval) { chrome::ScopedChannelOverride scoped_channel(GetParam().channel); - base::CommandLine* commandLine = base::CommandLine::ForCurrentProcess(); - commandLine->AppendSwitchASCII(kCrashpadPollingIntervalFlag, - GetParam().cmd_flag); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + command_line->AppendSwitchASCII(kCrashpadPollingIntervalFlag, + GetParam().cmd_flag); EXPECT_EQ(GetCrashpadPollingInterval(), base::Seconds(GetParam().expected_interval)); }
diff --git a/chrome/browser/extensions/ai_language_model_browsertest.cc b/chrome/browser/extensions/ai_language_model_browsertest.cc index 8af2296..2624702 100644 --- a/chrome/browser/extensions/ai_language_model_browsertest.cc +++ b/chrome/browser/extensions/ai_language_model_browsertest.cc
@@ -11,7 +11,6 @@ #include "base/test/scoped_feature_list.h" #include "base/version_info/channel.h" #include "chrome/browser/extensions/extension_browsertest.h" -#include "components/embedder_support/switches.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test.h" #include "extensions/test/result_catcher.h" @@ -19,30 +18,10 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/features_generated.h" -// TODO(crbug.com/350642260): the prompt API for extension OT is not affecting -// ChromeOS. We have skipped the logic for ChromeOS so the test will be skipped -// as well. namespace extensions { namespace { -// This is the public key of tools/origin_trials/eftest.key, used to validate -// origin trial tokens generated by tools/origin_trials/generate_token.py. -constexpr char kOriginTrialPublicKeyForTesting[] = - "dRCs+TocuKkocNKa0AtZ4awrt9XKH2SQCI6o4FY6BNA="; - -// The extension origin trial token (expired on 2032-11-26) was generated by -// ``` -// tools/origin_trials/generate_token.py -// chrome-extension://jnapclmfkaejhjkddbmiafekigmcbmma AIPromptAPIForExtension -// --expire-days 3000 -// ``` -constexpr char kLanguageModelOriginTrialTokensField[] = - "\"trial_tokens\":[\"A5nDxhrF7Qe4GiLouR1mgL5XKSk4wXA0B/RV2VyQcZj2IkLALdG/" - "FHrucKbG1TKD8QidNfqBdC07wP8KJaF6EQYAAAB9eyJvcmlnaW4iOiAiY2hyb21lLWV4dGVuc2" - "lvbjovL2puYXBjbG1ma2Flamhqa2RkYm1pYWZla2lnbWNibW1hIiwgImZlYXR1cmUiOiAiQUlQ" - "cm9tcHRBUElGb3JFeHRlbnNpb24iLCAiZXhwaXJ5IjogMTk4NTA2MjMwM30=\"],"; - // The `key` field stores the public key for the extension with id // "jnapclmfkaejhjkddbmiafekigmcbmma". static constexpr char kManifestTemplate[] = @@ -51,7 +30,6 @@ "name": "AI language model test", "version": "0.1", "manifest_version": 3, - %s "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3H6Jc0On6l0H3DJ6bx4aOW3+srCfjSdr+3ukwIEZrL6jDy500XweIwOp9PItpM9sijwu8v1rdyoBPubm/ottp/oz42aKp+2xIxcMTa6/cA2BL2kOWxwv+WP9d01IOFbFpWmQBDQNpp2UmH67OFbie6zHhyrSJKL2o9d05iX0a9Xwv9W48JKYpldo+/2JTP/5en0jxgiN+qkOCZuLag2cS/6Az0LArqsf5D+ReJemIBCNJhVxu3P0naxfEG6B6XczzuuptrX3H2vDr1LxZasLh9bzV88+8BxarjETACebOfqy366QxXluwAjnu/NHPv53edXlXvXrZ0C69RvvlMh1qQIDAQAB", "description": "Extension for testing the AI language model API.", "background": { @@ -72,26 +50,18 @@ )JS"; // The boolean tuple describing: -// 1. if the chrome://flag `kAIPromptAPI` is explicitly enabled; -// 2. if the kAIPromptAPI kill switch is triggered; -// 3. if the chrome://flag `kAIPromptAPIForExtension` is explicitly enabled; -// 4. if the kAIPromptAPIForExtension kill switch is triggered; -// 5. if the extension has an origin trial token. -using Variant = std::tuple<bool, bool, bool, bool, bool>; +// 1. if the `kAIPromptAPI` chrome://flag is explicitly enabled; +// 2. if the `kAIPromptAPI` kill switch is triggered; +// 3. if the `kAIPromptAPIForExtension` kill switch is triggered; +using Variant = std::tuple<bool, bool, bool>; bool IsAPIFlagEnabled(Variant v) { return std::get<0>(v); } bool IsAPIKillSwitchTriggered(Variant v) { return std::get<1>(v); } -bool IsExtensionFlagEnabled(Variant v) { - return std::get<2>(v); -} bool IsExtensionKillSwitchTriggered(Variant v) { - return std::get<3>(v); -} -bool IsExtensionInOriginTrial(Variant v) { - return std::get<4>(v); + return std::get<2>(v); } // Describes the test variants in a meaningful way in the parameterized tests. @@ -101,18 +71,11 @@ std::string api_kill_switch = IsAPIKillSwitchTriggered(info.param) ? "WithAPIKillswitch" : "NoAPIKillswitch"; - std::string extension_flag_enabled = IsExtensionFlagEnabled(info.param) - ? "WithExtensionFlag" - : "NoExtensionFlag"; std::string extension_kill_switch = IsExtensionKillSwitchTriggered(info.param) ? "WithExtensionKillswitch" : "NoExtensionKillswitch"; - std::string origin_trial = - IsExtensionInOriginTrial(info.param) ? "WithOTToken" : "NoOTToken"; return base::JoinString( - {api_flag_enabled, api_kill_switch, extension_flag_enabled, - extension_kill_switch, origin_trial}, - "_"); + {api_flag_enabled, api_kill_switch, extension_kill_switch}, "_"); } } // namespace @@ -127,14 +90,6 @@ command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures, "AIPromptAPI"); } - if (IsExtensionFlagEnabled(GetParam())) { - command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures, - "AIPromptAPIForExtension"); - } - - // Also specify the test public key to make the test token effective. - command_line->AppendSwitchASCII(embedder_support::kOriginTrialPublicKey, - kOriginTrialPublicKeyForTesting); base::flat_map<base::test::FeatureRef, bool> feature_states; if (IsAPIKillSwitchTriggered(GetParam())) { @@ -146,14 +101,6 @@ feature_list_.InitWithFeatureStates(feature_states); } - protected: - std::string GetManifest() { - return base::StringPrintf(kManifestTemplate, - IsExtensionInOriginTrial(GetParam()) - ? kLanguageModelOriginTrialTokensField - : ""); - } - private: base::test::ScopedFeatureList feature_list_; }; @@ -163,11 +110,10 @@ ExtensionAILanguageModelBrowserTest, testing::Combine(testing::Bool(), testing::Bool(), - testing::Bool(), - testing::Bool(), testing::Bool()), &DescribeTestVariant); +// TODO(crbug.com/419321441): Support Built-In AI APIs on ChromeOS. #if BUILDFLAG(IS_CHROMEOS) #define MAYBE_WorkerAccess DISABLED_WorkerAccess #else @@ -176,12 +122,11 @@ IN_PROC_BROWSER_TEST_P(ExtensionAILanguageModelBrowserTest, MAYBE_WorkerAccess) { TestExtensionDir test_dir; - test_dir.WriteManifest(GetManifest()); + test_dir.WriteManifest(kManifestTemplate); + // Extension access is blocked by either kill switch. const bool is_api_exposed = IsAPIFlagEnabled(GetParam()) || - IsExtensionFlagEnabled(GetParam()) || (!IsAPIKillSwitchTriggered(GetParam()) && - !IsExtensionKillSwitchTriggered(GetParam()) && - IsExtensionInOriginTrial(GetParam())); + !IsExtensionKillSwitchTriggered(GetParam())); test_dir.WriteFile( FILE_PATH_LITERAL("sw.js"), base::StringPrintf(kServiceWorkerScript, base::ToString(is_api_exposed)));
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc index 91f5176..0c28b5813 100644 --- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc +++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc
@@ -5,6 +5,7 @@ #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" +#include "base/strings/stringprintf.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "chrome/browser/extensions/extension_apitest.h"
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator_desktop.cc b/chrome/browser/extensions/api/developer_private/extension_info_generator_desktop.cc index c14c1d9..9758eba 100644 --- a/chrome/browser/extensions/api/developer_private/extension_info_generator_desktop.cc +++ b/chrome/browser/extensions/api/developer_private/extension_info_generator_desktop.cc
@@ -48,14 +48,6 @@ api::developer_private::ExtensionInfo info) { Profile* profile = Profile::FromBrowserContext(browser_context_); - if (extension_system_->extension_service()->allowlist()->ShouldDisplayWarning( - extension.id())) { - info.show_safe_browsing_allowlist_warning = true; - } - - ExtensionManagement* extension_management = - ExtensionManagementFactory::GetForBrowserContext(browser_context_); - // ControlledInfo. bool is_policy_location = Manifest::IsPolicyLocation(extension.location()); if (is_policy_location) { @@ -75,55 +67,6 @@ } } - // Dependent extensions. - if (extension.is_shared_module()) { - std::unique_ptr<ExtensionSet> dependent_extensions = - SharedModuleService::Get(browser_context_) - ->GetDependentExtensions(&extension); - for (const scoped_refptr<const Extension>& dependent : - *dependent_extensions) { - developer::DependentExtension dependent_extension; - dependent_extension.id = dependent->id(); - dependent_extension.name = dependent->name(); - info.dependent_extensions.push_back(std::move(dependent_extension)); - } - } - // TODO(crbug.com/413650880): Investigate if `parent_disabled_permissions` - // can be removed. - info.disable_reasons.parent_disabled_permissions = false; - - // Location. - bool updates_from_web_store = - extension_management->UpdatesFromWebstore(extension); - if (extension.location() == mojom::ManifestLocation::kInternal && - updates_from_web_store) { - info.location = developer::Location::kFromStore; - } else if (Manifest::IsUnpackedLocation(extension.location())) { - info.location = developer::Location::kUnpacked; - } else if (extension.was_installed_by_default() && - !extension.was_installed_by_oem() && updates_from_web_store) { - info.location = developer::Location::kInstalledByDefault; - } else if (Manifest::IsExternalLocation(extension.location()) && - updates_from_web_store) { - info.location = developer::Location::kThirdParty; - } else { - info.location = developer::Location::kUnknown; - } - - ManagementPolicy* management_policy = extension_system_->management_policy(); - info.must_remain_installed = - management_policy->MustRemainInstalled(&extension, nullptr); - info.user_may_modify = - management_policy->UserMayModifySettings(&extension, nullptr); - - info.update_url = - extension_management->GetEffectiveUpdateURL(extension).spec(); - - // Show access requests in toolbar. - info.show_access_requests_in_toolbar = - SitePermissionsHelper(profile).ShowAccessRequestsInToolbar( - extension.id()); - // Pinned to toolbar. // TODO(crbug.com/40280426): Currently this information is only shown for // enabled extensions as only enabled extensions can have actions. However,
diff --git a/chrome/browser/extensions/api/developer_private/extension_info_generator_shared.cc b/chrome/browser/extensions/api/developer_private/extension_info_generator_shared.cc index e6c15e15..ed888e7 100644 --- a/chrome/browser/extensions/api/developer_private/extension_info_generator_shared.cc +++ b/chrome/browser/extensions/api/developer_private/extension_info_generator_shared.cc
@@ -23,7 +23,10 @@ #include "chrome/browser/extensions/commands/command_service.h" #include "chrome/browser/extensions/error_console/error_console.h" #include "chrome/browser/extensions/extension_allowlist.h" +#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_util.h" +#include "chrome/browser/extensions/permissions/site_permissions_helper.h" +#include "chrome/browser/extensions/shared_module_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/extensions/extension_icon_source.h" #include "chrome/common/chrome_features.h" @@ -571,10 +574,44 @@ info.blocklist_text = l10n_util::GetStringUTF8(blocklist_text); } + if (extension_system_->extension_service()->allowlist()->ShouldDisplayWarning( + extension.id())) { + info.show_safe_browsing_allowlist_warning = true; + } + ExtensionManagement* extension_management = + ExtensionManagementFactory::GetForBrowserContext(browser_context_); + + // TODO(crbug.com/419419534): Add back ControlledInfo. + Profile* profile = Profile::FromBrowserContext(browser_context_); bool is_enabled = state == developer::ExtensionState::kEnabled; + // Commands. + if (is_enabled) { + ConstructCommands(command_service_, extension.id(), &info.commands); + } +#if BUILDFLAG(ENABLE_EXTENSIONS) + info.is_command_registration_handled_externally = + ui::GlobalAcceleratorListener::GetInstance() && + ui::GlobalAcceleratorListener::GetInstance() + ->IsRegistrationHandledExternally(); +#endif // BUILDFLAG(ENABLE_EXTENSIONS) + + // Dependent extensions. + if (extension.is_shared_module()) { + std::unique_ptr<ExtensionSet> dependent_extensions = + SharedModuleService::Get(browser_context_) + ->GetDependentExtensions(&extension); + for (const scoped_refptr<const Extension>& dependent : + *dependent_extensions) { + developer::DependentExtension dependent_extension; + dependent_extension.id = dependent->id(); + dependent_extension.name = dependent->name(); + info.dependent_extensions.push_back(std::move(dependent_extension)); + } + } + info.description = extension.description(); // Disable reasons. @@ -594,6 +631,9 @@ disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED); info.disable_reasons.custodian_approval_required = custodian_approval_required; + // TODO(crbug.com/413650880): Investigate if `parent_disabled_permissions` + // can be removed. + info.disable_reasons.parent_disabled_permissions = false; info.disable_reasons.published_in_store_required = disable_reasons.contains( disable_reason::DISABLE_PUBLISHED_IN_STORE_REQUIRED_BY_POLICY); info.disable_reasons.unsupported_manifest_version = disable_reasons.contains( @@ -611,6 +651,7 @@ error_console_->IsReportingEnabledForExtension(extension.id()); // File access. + ManagementPolicy* management_policy = extension_system_->management_policy(); info.file_access.is_enabled = (extension.wants_file_access() || Manifest::ShouldAlwaysAllowFileAccess(extension.location())); @@ -682,8 +723,20 @@ } // Location. - // Set it to kUnknown only if the caller didn't set it. - if (info.location == developer::Location::kNone) { + bool updates_from_web_store = + extension_management->UpdatesFromWebstore(extension); + if (extension.location() == mojom::ManifestLocation::kInternal && + updates_from_web_store) { + info.location = developer::Location::kFromStore; + } else if (Manifest::IsUnpackedLocation(extension.location())) { + info.location = developer::Location::kUnpacked; + } else if (extension.was_installed_by_default() && + !extension.was_installed_by_oem() && updates_from_web_store) { + info.location = developer::Location::kInstalledByDefault; + } else if (Manifest::IsExternalLocation(extension.location()) && + updates_from_web_store) { + info.location = developer::Location::kThirdParty; + } else { info.location = developer::Location::kUnknown; } @@ -725,6 +778,9 @@ } } + info.must_remain_installed = + management_policy->MustRemainInstalled(&extension, nullptr); + info.name = extension.name(); info.offline_enabled = OfflineEnabledInfo::IsOfflineEnabled(&extension); @@ -756,6 +812,12 @@ info.type = GetExtensionType(extension.manifest()->type()); + info.update_url = + extension_management->GetEffectiveUpdateURL(extension).spec(); + + info.user_may_modify = + management_policy->UserMayModifySettings(&extension, nullptr); + info.version = extension.GetVersionForDisplay(); if (state != developer::ExtensionState::kTerminated) { @@ -763,16 +825,16 @@ extension, is_enabled); } - // Commands. - if (is_enabled) { - ConstructCommands(command_service_, extension.id(), &info.commands); - } -#if BUILDFLAG(ENABLE_EXTENSIONS) - info.is_command_registration_handled_externally = - ui::GlobalAcceleratorListener::GetInstance() && - ui::GlobalAcceleratorListener::GetInstance() - ->IsRegistrationHandledExternally(); -#endif // BUILDFLAG(ENABLE_EXTENSIONS) + // Show access requests in toolbar. + info.show_access_requests_in_toolbar = + SitePermissionsHelper(profile).ShowAccessRequestsInToolbar( + extension.id()); + // TODO(crbug.com/419419534): Add back pinned_to_toolbar. + + // TODO(crbug.com/419419534): Add back MV2 deprecation if needed, so that + // extension_info_generator_desktop can be removed. + + // TODO(crbug.com/419419534): Add back can_upload_as_account_extension. // The icon. This section must come last as it moves `info`. ExtensionResource icon = IconsInfo::GetIconResource(
diff --git a/chrome/browser/extensions/api/extension_action/BUILD.gn b/chrome/browser/extensions/api/extension_action/BUILD.gn index 524df04..d5441f6 100644 --- a/chrome/browser/extensions/api/extension_action/BUILD.gn +++ b/chrome/browser/extensions/api/extension_action/BUILD.gn
@@ -10,8 +10,6 @@ sources = [ "extension_action_api.cc", "extension_action_api.h", - "extension_page_actions_api_constants.cc", - "extension_page_actions_api_constants.h", ] if (is_android) {
diff --git a/chrome/browser/extensions/api/extension_action/extension_page_actions_api_constants.cc b/chrome/browser/extensions/api/extension_action/extension_page_actions_api_constants.cc deleted file mode 100644 index 9451961..0000000 --- a/chrome/browser/extensions/api/extension_action/extension_page_actions_api_constants.cc +++ /dev/null
@@ -1,15 +0,0 @@ -// Copyright 2012 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/extensions/api/extension_action/extension_page_actions_api_constants.h" - -namespace extension_page_actions_api_constants { - -const char kTabIdKey[] = "tabId"; -const char kTabUrlKey[] = "tabUrl"; -const char kUrlKey[] = "url"; -const char kTitleKey[] = "title"; -const char kButtonKey[] = "button"; - -} // namespace extension_page_actions_api_constants
diff --git a/chrome/browser/extensions/api/extension_action/extension_page_actions_api_constants.h b/chrome/browser/extensions/api/extension_action/extension_page_actions_api_constants.h deleted file mode 100644 index 16cae46..0000000 --- a/chrome/browser/extensions/api/extension_action/extension_page_actions_api_constants.h +++ /dev/null
@@ -1,21 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Constants used for the Page Actions API. - -#ifndef CHROME_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_EXTENSION_PAGE_ACTIONS_API_CONSTANTS_H_ -#define CHROME_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_EXTENSION_PAGE_ACTIONS_API_CONSTANTS_H_ - -namespace extension_page_actions_api_constants { - -// Keys. -extern const char kTabIdKey[]; -extern const char kTabUrlKey[]; -extern const char kUrlKey[]; -extern const char kTitleKey[]; -extern const char kButtonKey[]; - -} // namespace extension_page_actions_api_constants - -#endif // CHROME_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_EXTENSION_PAGE_ACTIONS_API_CONSTANTS_H_
diff --git a/chrome/browser/extensions/api/offscreen/offscreen_apitest.cc b/chrome/browser/extensions/api/offscreen/offscreen_apitest.cc index 0735984..6e196db 100644 --- a/chrome/browser/extensions/api/offscreen/offscreen_apitest.cc +++ b/chrome/browser/extensions/api/offscreen/offscreen_apitest.cc
@@ -6,6 +6,7 @@ #include "base/functional/callback_helpers.h" #include "base/run_loop.h" +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "build/build_config.h" #include "chrome/browser/extensions/extension_apitest.h"
diff --git a/chrome/browser/extensions/api/scripting/scripting_apitest.cc b/chrome/browser/extensions/api/scripting/scripting_apitest.cc index 819fab5..162366a5 100644 --- a/chrome/browser/extensions/api/scripting/scripting_apitest.cc +++ b/chrome/browser/extensions/api/scripting/scripting_apitest.cc
@@ -4,6 +4,7 @@ #include <optional> +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "build/build_config.h" #include "chrome/browser/extensions/api/scripting/scripting_api.h"
diff --git a/chrome/browser/extensions/content_verifier_browsertest.cc b/chrome/browser/extensions/content_verifier_browsertest.cc index b2ada58..c7fb420 100644 --- a/chrome/browser/extensions/content_verifier_browsertest.cc +++ b/chrome/browser/extensions/content_verifier_browsertest.cc
@@ -13,6 +13,7 @@ #include "base/files/file_util.h" #include "base/functional/callback_helpers.h" #include "base/strings/string_split.h" +#include "base/strings/stringprintf.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h"
diff --git a/chrome/browser/extensions/cross_origin_isolation_browsertest.cc b/chrome/browser/extensions/cross_origin_isolation_browsertest.cc index e71f0f9..c8ada56 100644 --- a/chrome/browser/extensions/cross_origin_isolation_browsertest.cc +++ b/chrome/browser/extensions/cross_origin_isolation_browsertest.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/files/file_path.h" +#include "base/strings/stringprintf.h" #include "base/strings/to_string.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/extensions/event_metrics_browsertest.cc b/chrome/browser/extensions/event_metrics_browsertest.cc index 30a8c645..d9a3998e 100644 --- a/chrome/browser/extensions/event_metrics_browsertest.cc +++ b/chrome/browser/extensions/event_metrics_browsertest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/strings/stringprintf.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/extensions/service_worker_installation_browsertest.cc b/chrome/browser/extensions/service_worker_installation_browsertest.cc index 504bd216..3a7d973 100644 --- a/chrome/browser/extensions/service_worker_installation_browsertest.cc +++ b/chrome/browser/extensions/service_worker_installation_browsertest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/strings/stringprintf.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/profiles/profile.h" #include "content/public/test/browser_test.h"
diff --git a/chrome/browser/extensions/user_host_restrictions_browsertest.cc b/chrome/browser/extensions/user_host_restrictions_browsertest.cc index 1ff70f7..dc69d5a 100644 --- a/chrome/browser/extensions/user_host_restrictions_browsertest.cc +++ b/chrome/browser/extensions/user_host_restrictions_browsertest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "chrome/browser/extensions/extension_apitest.h"
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 2aa56d5b..d4a2aeb 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -745,6 +745,14 @@ "expiry_milestone": 140 }, { + "name": "autofill-enable-multiple-request-in-virtual-card-downstream-enrollment", + "owners": [ + "siyua@chromium.org", + "payments-autofill-team@google.com" + ], + "expiry_milestone": 145 + }, + { "name": "autofill-enable-new-fop-display-desktop", "owners": [ "qihuizhao@google.com", "jsaul@google.com"], "expiry_milestone": 145 @@ -3847,17 +3855,17 @@ }, { "name": "enable-openxr-android", - "owners": [ "alcooper@chromium.org", "bajones@chromium.org", "bialpio@chromium.org", "xr-dev@chromium.org" ], + "owners": [ "alcooper@chromium.org", "bajones@chromium.org", "xr-dev@chromium.org" ], "expiry_milestone": 140 }, { "name": "enable-openxr-android-smooth-depth", - "owners": [ "alcooper@chromium.org", "bajones@chromium.org", "bialpio@chromium.org", "xr-dev@chromium.org" ], + "owners": [ "alcooper@chromium.org", "bajones@chromium.org", "xr-dev@chromium.org" ], "expiry_milestone": 150 }, { "name": "enable-openxr-extended", - "owners": [ "alcooper@chromium.org", "bajones@chromium.org", "bialpio@chromium.org", "xr-dev@chromium.org" ], + "owners": [ "alcooper@chromium.org", "bajones@chromium.org", "xr-dev@chromium.org" ], "expiry_milestone": 140 }, { @@ -10220,7 +10228,7 @@ }, { "name": "zero-copy-tab-capture", - "owners": [ "bialpio@chromium.org", "media-capture-dev@chromium.org" ], + "owners": [ "media-capture-dev@chromium.org" ], "expiry_milestone": 140 }, {
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 581594cb..cd729b3 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -772,6 +772,16 @@ "When enabled, Autofill will offer support for filling the user's loyalty " "cards stored in Google Wallet."; +const char + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollmentName[] = + "Enable multiple server request support for virtual card downstream " + "enrollment"; +const char + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollmentDescription + [] = "When enabled, Chrome will be able to send preflight call for " + "enrollment earlier in the flow with the multiple server request " + "support."; + const char kAutofillEnableNewFopDisplayDesktopName[] = "Enable Autofill new FOP display on Desktop"; const char kAutofillEnableNewFopDisplayDesktopDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 8e3b4a3d..1df2dccf 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -462,6 +462,12 @@ extern const char kAutofillEnableLoyaltyCardsFillingName[]; extern const char kAutofillEnableLoyaltyCardsFillingDescription[]; +extern const char + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollmentName[]; +extern const char + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollmentDescription + []; + extern const char kAutofillEnableNewFopDisplayDesktopName[]; extern const char kAutofillEnableNewFopDisplayDesktopDescription[];
diff --git a/chrome/browser/glic/host/glic_actor_controller.cc b/chrome/browser/glic/host/glic_actor_controller.cc index 53892dd..f4f06be 100644 --- a/chrome/browser/glic/host/glic_actor_controller.cc +++ b/chrome/browser/glic/host/glic_actor_controller.cc
@@ -95,7 +95,8 @@ GetActorCoordinator()->StartTask( action, base::BindOnce(&GlicActorController::OnTaskStarted, GetWeakPtr(), - action, options, std::move(callback))); + action, options, std::move(callback)), + /*tab_id=*/std::nullopt); return; }
diff --git a/chrome/browser/loader/keep_alive_category_request_browsertest.cc b/chrome/browser/loader/keep_alive_category_request_browsertest.cc index 9b1de86..52e43e11 100644 --- a/chrome/browser/loader/keep_alive_category_request_browsertest.cc +++ b/chrome/browser/loader/keep_alive_category_request_browsertest.cc
@@ -167,8 +167,10 @@ content::KeepAliveRequestTracker::RequestStageType::kLoaderCompleted, content::KeepAliveRequestTracker::RequestStageType::kResponseReceived, /*keepalive_token=*/std::nullopt, - /*error_code=*/net::OK, - /*extended_error_code=*/0); + /*failed_error_code=*/std::nullopt, + /*failed_extended_error_code=*/std::nullopt, + /*completed_error_code=*/net::OK, + /*completed_extended_error_code=*/0); ExpectTimeSortedTimeDeltaUkm( {"TimeDelta.RequestStarted", "TimeDelta.ResponseReceived", "TimeDelta.LoaderCompleted", "TimeDelta.EventLogged"}); @@ -245,8 +247,10 @@ content::KeepAliveRequestTracker::RequestStageType::kLoaderCompleted, content::KeepAliveRequestTracker::RequestStageType::kResponseReceived, /*keepalive_token=*/std::nullopt, - /*error_code=*/net::OK, - /*extended_error_code=*/0); + /*failed_error_code=*/std::nullopt, + /*failed_extended_error_code=*/std::nullopt, + /*completed_error_code=*/net::OK, + /*completed_extended_error_code=*/0); ExpectTimeSortedTimeDeltaUkm( {"TimeDelta.RequestStarted", "TimeDelta.ResponseReceived", "TimeDelta.LoaderCompleted", "TimeDelta.EventLogged"}); @@ -360,8 +364,10 @@ content::KeepAliveRequestTracker::RequestStageType::kLoaderCompleted, content::KeepAliveRequestTracker::RequestStageType::kResponseReceived, /*keepalive_token=*/std::nullopt, - /*error_code=*/net::OK, - /*extended_error_code=*/0); + /*failed_error_code=*/std::nullopt, + /*failed_extended_error_code=*/std::nullopt, + /*completed_error_code=*/net::OK, + /*completed_extended_error_code=*/0); ExpectTimeSortedTimeDeltaUkm( {"TimeDelta.RequestStarted", "TimeDelta.ResponseReceived", "TimeDelta.LoaderCompleted", "TimeDelta.EventLogged"}); @@ -400,8 +406,10 @@ content::KeepAliveRequestTracker::RequestStageType::kLoaderCompleted, content::KeepAliveRequestTracker::RequestStageType::kResponseReceived, /*keepalive_token=*/std::nullopt, - /*error_code=*/net::OK, - /*extended_error_code=*/0); + /*failed_error_code=*/std::nullopt, + /*failed_extended_error_code=*/std::nullopt, + /*completed_error_code=*/net::OK, + /*completed_extended_error_code=*/0); ExpectTimeSortedTimeDeltaUkm( {"TimeDelta.RequestStarted", "TimeDelta.ResponseReceived", "TimeDelta.LoaderCompleted", "TimeDelta.EventLogged"}); @@ -449,8 +457,10 @@ content::KeepAliveRequestTracker::RequestStageType::kLoaderCompleted, content::KeepAliveRequestTracker::RequestStageType::kResponseReceived, /*keepalive_token=*/std::nullopt, - /*error_code=*/net::OK, - /*extended_error_code=*/0}, + /*failed_error_code=*/std::nullopt, + /*failed_extended_error_code=*/std::nullopt, + /*completed_error_code=*/net::OK, + /*completed_extended_error_code=*/0}, {content::KeepAliveRequestTracker::RequestType::kFetch, /*category_id=*/2, /*num_redirects=*/0, @@ -458,8 +468,10 @@ content::KeepAliveRequestTracker::RequestStageType::kLoaderCompleted, content::KeepAliveRequestTracker::RequestStageType::kResponseReceived, /*keepalive_token=*/std::nullopt, - /*error_code=*/net::OK, - /*extended_error_code=*/0}}); + /*failed_error_code=*/std::nullopt, + /*failed_extended_error_code=*/std::nullopt, + /*completed_error_code=*/net::OK, + /*completed_extended_error_code=*/0}}); // Only request with `category2` should be paired with the navigation // request. ExpectNavigationUkm(/*category_id=*/2, /*navigation_id=*/std::nullopt, @@ -503,8 +515,10 @@ content::KeepAliveRequestTracker::RequestStageType::kLoaderCompleted, content::KeepAliveRequestTracker::RequestStageType::kResponseReceived, /*keepalive_token=*/std::nullopt, - /*error_code=*/net::OK, - /*extended_error_code=*/0}, + /*failed_error_code=*/std::nullopt, + /*failed_extended_error_code=*/std::nullopt, + /*completed_error_code=*/net::OK, + /*completed_extended_error_code=*/0}, {content::KeepAliveRequestTracker::RequestType::kFetch, /*category_id=*/1, /*num_redirects=*/0, @@ -512,8 +526,10 @@ content::KeepAliveRequestTracker::RequestStageType::kLoaderCompleted, content::KeepAliveRequestTracker::RequestStageType::kResponseReceived, /*keepalive_token=*/std::nullopt, - /*error_code=*/net::OK, - /*extended_error_code=*/0}}); + /*failed_error_code=*/std::nullopt, + /*failed_extended_error_code=*/std::nullopt, + /*completed_error_code=*/net::OK, + /*completed_extended_error_code=*/0}}); // Only one request should be paired with the navigation request, event though // both requests have the same category ID. ExpectNavigationUkm(/*category_id=*/1, /*navigation_id=*/std::nullopt, @@ -550,8 +566,10 @@ content::KeepAliveRequestTracker::RequestStageType::kLoaderCompleted, content::KeepAliveRequestTracker::RequestStageType::kResponseReceived, /*keepalive_token=*/std::nullopt, - /*error_code=*/net::OK, - /*extended_error_code=*/0); + /*failed_error_code=*/std::nullopt, + /*failed_extended_error_code=*/std::nullopt, + /*completed_error_code=*/net::OK, + /*completed_extended_error_code=*/0); ExpectTimeSortedTimeDeltaUkm( {"TimeDelta.RequestStarted", "TimeDelta.ResponseReceived", "TimeDelta.LoaderCompleted", "TimeDelta.EventLogged"}); @@ -594,8 +612,10 @@ content::KeepAliveRequestTracker::RequestStageType::kLoaderCompleted, content::KeepAliveRequestTracker::RequestStageType::kResponseReceived, /*keepalive_token=*/std::nullopt, - /*error_code=*/net::OK, - /*extended_error_code=*/0); + /*failed_error_code=*/std::nullopt, + /*failed_extended_error_code=*/std::nullopt, + /*completed_error_code=*/net::OK, + /*completed_extended_error_code=*/0); ExpectTimeSortedTimeDeltaUkm( {"TimeDelta.RequestStarted", "TimeDelta.ResponseReceived", "TimeDelta.LoaderCompleted", "TimeDelta.EventLogged"}); @@ -647,8 +667,10 @@ content::KeepAliveRequestTracker::RequestStageType::kLoaderCompleted, content::KeepAliveRequestTracker::RequestStageType::kResponseReceived, /*keepalive_token=*/std::nullopt, - /*error_code=*/net::OK, - /*extended_error_code=*/0}, + /*failed_error_code=*/std::nullopt, + /*failed_extended_error_code=*/std::nullopt, + /*completed_error_code=*/net::OK, + /*completed_extended_error_code=*/0}, {content::KeepAliveRequestTracker::RequestType::kFetch, /*category_id=*/2, /*num_redirects=*/0, @@ -656,8 +678,10 @@ content::KeepAliveRequestTracker::RequestStageType::kLoaderCompleted, content::KeepAliveRequestTracker::RequestStageType::kResponseReceived, /*keepalive_token=*/std::nullopt, - /*error_code=*/net::OK, - /*extended_error_code=*/0}}); + /*failed_error_code=*/std::nullopt, + /*failed_extended_error_code=*/std::nullopt, + /*completed_error_code=*/net::OK, + /*completed_extended_error_code=*/0}}); // Only one navigation should be paired with the fetch keepalive request. ExpectNavigationUkms({{/*category_id=*/1, /*navigation_id=*/std::nullopt, /*keepalive_token=*/std::nullopt}, @@ -695,8 +719,10 @@ content::KeepAliveRequestTracker::RequestStageType::kLoaderCompleted, content::KeepAliveRequestTracker::RequestStageType::kResponseReceived, /*keepalive_token=*/std::nullopt, - /*error_code=*/net::OK, - /*extended_error_code=*/0); + /*failed_error_code=*/std::nullopt, + /*failed_extended_error_code=*/std::nullopt, + /*completed_error_code=*/net::OK, + /*completed_extended_error_code=*/0); ExpectTimeSortedTimeDeltaUkm( {"TimeDelta.RequestStarted", "TimeDelta.ResponseReceived", "TimeDelta.LoaderCompleted", "TimeDelta.EventLogged"});
diff --git a/chrome/browser/loader/keep_alive_request_browsertest_util.cc b/chrome/browser/loader/keep_alive_request_browsertest_util.cc index a92fbbb..da7e784 100644 --- a/chrome/browser/loader/keep_alive_request_browsertest_util.cc +++ b/chrome/browser/loader/keep_alive_request_browsertest_util.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/loader/keep_alive_request_browsertest_util.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "chrome/browser/ui/browser.h" #include "components/network_session_configurator/common/network_switches.h" #include "content/public/test/back_forward_cache_util.h"
diff --git a/chrome/browser/loader/keep_alive_request_tracker.cc b/chrome/browser/loader/keep_alive_request_tracker.cc index eab5b91..90ddf4d6 100644 --- a/chrome/browser/loader/keep_alive_request_tracker.cc +++ b/chrome/browser/loader/keep_alive_request_tracker.cc
@@ -144,8 +144,8 @@ case RequestStageType::kRequestFailed: ukm_builder_.SetTimeDelta_RequestFailed( relative_to_created_time.InMilliseconds()); - ukm_builder_.SetCompletionStatus_ErrorCode(stage.status->error_code); - ukm_builder_.SetCompletionStatus_ExtendedErrorCode( + ukm_builder_.SetRequestFailed_ErrorCode(stage.status->error_code); + ukm_builder_.SetRequestFailed_ExtendedErrorCode( stage.status->extended_error_code); break; @@ -172,8 +172,8 @@ case RequestStageType::kLoaderCompleted: ukm_builder_.SetTimeDelta_LoaderCompleted( relative_to_created_time.InMilliseconds()); - ukm_builder_.SetCompletionStatus_ErrorCode(stage.status->error_code); - ukm_builder_.SetCompletionStatus_ExtendedErrorCode( + ukm_builder_.SetLoaderCompleted_ErrorCode(stage.status->error_code); + ukm_builder_.SetLoaderCompleted_ExtendedErrorCode( stage.status->extended_error_code); break; }
diff --git a/chrome/browser/loader/keep_alive_request_tracker_unittest.cc b/chrome/browser/loader/keep_alive_request_tracker_unittest.cc index b94743a..d3ebd1c 100644 --- a/chrome/browser/loader/keep_alive_request_tracker_unittest.cc +++ b/chrome/browser/loader/keep_alive_request_tracker_unittest.cc
@@ -438,6 +438,8 @@ /*is_context_detached=*/false, RequestStageType::kLoaderCompleted, RequestStageType::kResponseReceived, *request.keepalive_token, + /*failed_error_code=*/std::nullopt, + /*failed_extended_error_code=*/std::nullopt, status.error_code, status.extended_error_code); ExpectTimeSortedTimeDeltaUkm( @@ -466,6 +468,8 @@ /*is_context_detached=*/false, RequestStageType::kLoaderCompleted, RequestStageType::kRequestStarted, *request.keepalive_token, + /*failed_error_code=*/std::nullopt, + /*failed_extended_error_code=*/std::nullopt, failed_status.error_code, failed_status.extended_error_code); ExpectTimeSortedTimeDeltaUkm(
diff --git a/chrome/browser/media/media_session_browsertest.cc b/chrome/browser/media/media_session_browsertest.cc index ed25d5c..65bd686 100644 --- a/chrome/browser/media/media_session_browsertest.cc +++ b/chrome/browser/media/media_session_browsertest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/ui/browser.h" #include "chrome/test/base/in_process_browser_test.h"
diff --git a/chrome/browser/media/prefs/OWNERS b/chrome/browser/media/prefs/OWNERS index 73795e6..9780be5 100644 --- a/chrome/browser/media/prefs/OWNERS +++ b/chrome/browser/media/prefs/OWNERS
@@ -1,3 +1,2 @@ bryantchandler@chromium.org mfoltz@chromium.org -bialpio@chromium.org
diff --git a/chrome/browser/media_effects/OWNERS b/chrome/browser/media_effects/OWNERS index d4987c9..58c950b 100644 --- a/chrome/browser/media_effects/OWNERS +++ b/chrome/browser/media_effects/OWNERS
@@ -1,4 +1,3 @@ bryantchandler@chromium.org mfoltz@chromium.org -bialpio@chromium.org ahmedmoussa@google.com
diff --git a/chrome/browser/net/websocket_browsertest.cc b/chrome/browser/net/websocket_browsertest.cc index 9e1877b..90ee7c3 100644 --- a/chrome/browser/net/websocket_browsertest.cc +++ b/chrome/browser/net/websocket_browsertest.cc
@@ -17,6 +17,7 @@ #include "base/path_service.h" #include "base/run_loop.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/bind.h" #include "base/test/run_until.h"
diff --git a/chrome/browser/platform_experience/win b/chrome/browser/platform_experience/win index 1c5aab8..e050381 160000 --- a/chrome/browser/platform_experience/win +++ b/chrome/browser/platform_experience/win
@@ -1 +1 @@ -Subproject commit 1c5aab84b6952ed67196e6a56788e9ef9a6e886e +Subproject commit e0503816507543e3b732a5a46109e9f71d74c3e1
diff --git a/chrome/browser/policy/test/ipv6_reachability_override_policy_browsertest.cc b/chrome/browser/policy/test/ipv6_reachability_override_policy_browsertest.cc index a27b10e..abb9768 100644 --- a/chrome/browser/policy/test/ipv6_reachability_override_policy_browsertest.cc +++ b/chrome/browser/policy/test/ipv6_reachability_override_policy_browsertest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/strings/stringprintf.h" #include "chrome/browser/policy/policy_test_utils.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h"
diff --git a/chrome/browser/private_network_access/OWNERS b/chrome/browser/private_network_access/OWNERS index fbd73a5..c8542ff 100644 --- a/chrome/browser/private_network_access/OWNERS +++ b/chrome/browser/private_network_access/OWNERS
@@ -1,6 +1,5 @@ -lyf@chromium.org -titouan@chromium.org -phao@chromium.org clamy@chromium.org +cthomp@chromium.org estark@chromium.org +hchao@chromium.org jdeblasio@chromium.org
diff --git a/chrome/browser/resources/ash/settings/os_languages_page/input_method_types.ts b/chrome/browser/resources/ash/settings/os_languages_page/input_method_types.ts index 624504dc..975ee99b7 100644 --- a/chrome/browser/resources/ash/settings/os_languages_page/input_method_types.ts +++ b/chrome/browser/resources/ash/settings/os_languages_page/input_method_types.ts
@@ -35,11 +35,9 @@ } export enum JapaneseKeymapStyle { - CUSTOM = 'Custom', ATOK = 'Atok', MS_IME = 'MsIme', KOTOERI = 'Kotoeri', - MOBILE = 'Mobile', CHROME_OS = 'ChromeOs', }
diff --git a/chrome/browser/resources/ash/settings/os_languages_page/input_method_util.ts b/chrome/browser/resources/ash/settings/os_languages_page/input_method_util.ts index 72ed196..d00e832 100644 --- a/chrome/browser/resources/ash/settings/os_languages_page/input_method_util.ts +++ b/chrome/browser/resources/ash/settings/os_languages_page/input_method_util.ts
@@ -166,7 +166,7 @@ [OptionType.JAPANESE_SPACE_INPUT_STYLE]: JapaneseSpaceInputStyle.INPUT_MODE, [OptionType.JAPANESE_SECTION_SHORTCUT]: JapaneseSectionShortcut.DIGITS_123456789, - [OptionType.JAPANESE_KEYMAP_STYLE]: JapaneseKeymapStyle.CUSTOM, + [OptionType.JAPANESE_KEYMAP_STYLE]: JapaneseKeymapStyle.CHROME_OS, [OptionType.JAPANESE_DISABLE_PERSONALIZED_SUGGESTIONS]: false, // LINT.ThenChange(/chrome/browser/ash/input_method/japanese/japanese_settings.cc:JpPrefDefaults) @@ -945,10 +945,6 @@ case OptionType.JAPANESE_KEYMAP_STYLE: return [ { - value: JapaneseKeymapStyle.CUSTOM, - name: 'inputMethodOptionsJapaneseKeymapStyleCustom', - }, - { value: JapaneseKeymapStyle.ATOK, name: 'inputMethodOptionsJapaneseKeymapStyleAtok', },
diff --git a/chrome/browser/resources/lens/overlay/post_selection_renderer.html b/chrome/browser/resources/lens/overlay/post_selection_renderer.html index c2c9640..ae16f29d 100644 --- a/chrome/browser/resources/lens/overlay/post_selection_renderer.html +++ b/chrome/browser/resources/lens/overlay/post_selection_renderer.html
@@ -10,18 +10,24 @@ background-color: var(--color-scrim); height: 100%; opacity: 20%; - transition: opacity cubic-bezier(0.2, 0.0, 0, 1.0) 400ms; + transition: opacity cubic-bezier(0.2, 0, 0, 1) 400ms; width: 100%; } + :host([region-selected-glow-enabled]) #postSelectionScrim { + /* Hide this scrim because its superfluous here - the region_selection scrim + * is already active and has transitioned to its post-selection opacity. */ + display: none; + } + /** Render the selected part of the image again so it appears glowing over the scrim */ #backgroundImageCanvas { - clip-path: rect(var(--selection-top) - calc(var(--selection-left) + var(--selection-width)) - calc(var(--selection-top) + var(--selection-height)) - var(--selection-left) - round var(--post-selection-cutout-corner-radius)); + clip-path: rect( + var(--selection-top) calc(var(--selection-left) + var(--selection-width)) + calc(var(--selection-top) + var(--selection-height)) var(--selection-left) round + var(--post-selection-cutout-corner-radius) + ); height: 100%; inset: 0; object-fit: contain; @@ -29,6 +35,12 @@ width: 100%; } + :host([region-selected-glow-enabled]) #backgroundImageCanvas { + /* Do not use z-index once region-selection-glow-enabled is launched. + * Instead, move the backgroundImageCanvas higher in the DOM. */ + z-index: 2; + } + #postSelection { /* Scrim is rendered here too, so that the rendered screenshot is dark * as if it were under the scrim as well. */ @@ -44,6 +56,31 @@ width: var(--selection-width); } + :host([region-selected-glow-enabled]) #postSelection { + background-color: transparent; + opacity: 1; + transition: none; + } + + :host([region-selected-glow-enabled]) #postSelection:before { + background: conic-gradient( + from 90deg at center, + var(--gradient-blue) 0deg, + var(--gradient-blue) 162deg, + var(--gradient-red) 216deg, + var(--gradient-yellow) 274deg, + var(--gradient-green) 331deg + ); + content: ""; + /* Generally blur should be avoided for performance reasons but it's ok here + * because it's only being calculated once */ + filter: blur(40px); + inset: 0; + opacity: 1; + position: absolute; + transition: opacity 166ms cubic-bezier(0.3, 0, 1, 1); + } + :host([should-darken-scrim]) #postSelection { opacity: 20%; } @@ -51,13 +88,11 @@ #selectionCorners { background-image: paint(post-selection); forced-color-adjust: none; - height: calc(var(--selection-height) + - (2 * var(--post-selection-corner-width))); + height: calc(var(--selection-height) + (2 * var(--post-selection-corner-width))); left: calc(var(--selection-left) - var(--post-selection-corner-width)); position: absolute; top: calc(var(--selection-top) - var(--post-selection-corner-width)); - width: calc(var(--selection-width) + - (2 * var(--post-selection-corner-width))); + width: calc(var(--selection-width) + (2 * var(--post-selection-corner-width))); z-index: 5; /* Position above words. */ } @@ -80,28 +115,28 @@ cursor: nw-resize; left: 0; top: 0; - transform: translate(-25%, -25%) + transform: translate(-25%, -25%); } #topRight { cursor: ne-resize; top: 0; right: 0; - transform: translate(25%, -25%) + transform: translate(25%, -25%); } #bottomRight { cursor: se-resize; bottom: 0; right: 0; - transform: translate(25%, 25%) + transform: translate(25%, 25%); } #bottomLeft { cursor: sw-resize; bottom: 0; left: 0; - transform: translate(-25%, 25%) + transform: translate(-25%, 25%); } .slider { @@ -112,24 +147,33 @@ opacity: 0; } </style> -<div id="postSelectionScrim" - style$="[[getScrimStyleProperties(height, width)]]"> -</div> +<div id="postSelectionScrim" style$="[[getScrimStyleProperties(height, width)]]"></div> <div hidden$="[[!hasSelection(height, width)]]"> - <canvas id="backgroundImageCanvas" height="[[canvasPhysicalHeight]]" - width="[[canvasPhysicalWidth]]" - style$="height: [[canvasHeight]]px; width: [[canvasWidth]]px;"> + <canvas + id="backgroundImageCanvas" + height="[[canvasPhysicalHeight]]" + width="[[canvasPhysicalWidth]]" + style$="height: [[canvasHeight]]px; width: [[canvasWidth]]px;" + > </canvas> - <div id="postSelection"></div> + <div style$="[[getHexColorStyles()]]" id="postSelection"></div> <div id="selectionCorners"> <template is="dom-repeat" items="[[cornerIds]]"> <div id="[[item]]" class="corner-hit-box"> <template is="dom-if" if="[[cornerSlidersEnabled]]"> - <input id="[[item]]Slider" type="range" tabindex="0" min="-1" - max="101" step="1" class="slider" on-change="handleSliderChange" - data-corner-id$="[[item]]"> + <input + id="[[item]]Slider" + type="range" + tabindex="0" + min="-1" + max="101" + step="1" + class="slider" + on-change="handleSliderChange" + data-corner-id$="[[item]]" + /> </template> </div> </template> </div> -</div> \ No newline at end of file +</div>
diff --git a/chrome/browser/resources/lens/overlay/post_selection_renderer.ts b/chrome/browser/resources/lens/overlay/post_selection_renderer.ts index 98ca370d..d7e1caa 100644 --- a/chrome/browser/resources/lens/overlay/post_selection_renderer.ts +++ b/chrome/browser/resources/lens/overlay/post_selection_renderer.ts
@@ -10,6 +10,7 @@ import {BrowserProxyImpl} from './browser_proxy.js'; import type {BrowserProxy} from './browser_proxy.js'; +import {GLIF_HEX_COLORS} from './color_utils.js'; import {CenterRotatedBox_CoordinateType} from './geometry.mojom-webui.js'; import type {CenterRotatedBox} from './geometry.mojom-webui.js'; import {UserAction} from './lens.mojom-webui.js'; @@ -119,6 +120,11 @@ canvasWidth: Number, canvasPhysicalHeight: Number, canvasPhysicalWidth: Number, + regionSelectedGlowEnabled: { + type: Boolean, + reflectToAttribute: true, + value: () => loadTimeData.getBoolean('enableRegionSelectedGlow'), + }, selectionOverlayRect: Object, shouldDarkenScrim: { type: Boolean, @@ -149,6 +155,8 @@ declare private canvasPhysicalWidth: number; // The bounds of the parent element. This is updated by the parent to avoid // this class needing to call getBoundingClientRect(). + // Whether the region selected glow is enabled via feature flag. + declare private regionSelectedGlowEnabled: boolean; declare private selectionOverlayRect: DOMRect; private context: CanvasRenderingContext2D; @@ -458,6 +466,16 @@ }, this.sliderChangedTimeout); } + private getHexColorStyles() { + const style: string[] = [ + `--gradient-blue: ${GLIF_HEX_COLORS.blue}`, + `--gradient-red: ${GLIF_HEX_COLORS.red}`, + `--gradient-yellow: ${GLIF_HEX_COLORS.yellow}`, + `--gradient-green: ${GLIF_HEX_COLORS.green}`, + ]; + return style.join('; '); + } + private setDimensions( top: number, left: number, height: number, width: number) { this.top = top;
diff --git a/chrome/browser/resources/lens/overlay/region_selection.html b/chrome/browser/resources/lens/overlay/region_selection.html index 5735a4f..d4262762 100644 --- a/chrome/browser/resources/lens/overlay/region_selection.html +++ b/chrome/browser/resources/lens/overlay/region_selection.html
@@ -25,7 +25,7 @@ :host([border-glow-enabled][has-selected]) #regionSelectionCanvas { /* 20% scrim */ - background: rgba(0, 0, 0, 0.2); + background: rgba(0, 0, 0, 0.1); opacity: 1; } </style>
diff --git a/chrome/browser/resources/lens/overlay/selection_overlay.html b/chrome/browser/resources/lens/overlay/selection_overlay.html index a2e7f89..177d2e3f 100644 --- a/chrome/browser/resources/lens/overlay/selection_overlay.html +++ b/chrome/browser/resources/lens/overlay/selection_overlay.html
@@ -380,6 +380,7 @@ <post-selection-renderer id="postSelectionRenderer" selection-overlay-rect="[[selectionOverlayRect]]" + region-selected-glow-enabled="[[enableRegionSelectedGlow]]" > </post-selection-renderer> <lens-object-layer
diff --git a/chrome/browser/resources/print_preview/BUILD.gn b/chrome/browser/resources/print_preview/BUILD.gn index a382400e..86eefc8 100644 --- a/chrome/browser/resources/print_preview/BUILD.gn +++ b/chrome/browser/resources/print_preview/BUILD.gn
@@ -119,7 +119,6 @@ icons_html_files = [ "ui/icons.html" ] html_to_wrapper_template = "detect" - ts_tsconfig_base = "tsconfig_base.json" ts_composite = true ts_definitions = [ "//tools/typescript/definitions/chrome_event.d.ts",
diff --git a/chrome/browser/resources/print_preview/data/model.ts b/chrome/browser/resources/print_preview/data/model.ts index 45664dc..8caffcb 100644 --- a/chrome/browser/resources/print_preview/data/model.ts +++ b/chrome/browser/resources/print_preview/data/model.ts
@@ -492,7 +492,7 @@ } accessor settingsManaged: boolean = false; - accessor destination: Destination; + accessor destination: Destination|null = null; accessor documentSettings: DocumentSettings = createDocumentSettings(); accessor margins: Margins|null = null; accessor pageSize: Size = new Size(612, 792); @@ -681,6 +681,7 @@ } private updateSettingsAvailabilityFromDestination_() { + assert(this.destination); const caps = this.destination.capabilities ? this.destination.capabilities.printer : null; @@ -710,12 +711,14 @@ this.setSettingPath_( 'vendorItems.available', !!caps && !!caps.vendor_capability); - if (this.documentSettings) { - this.updateSettingsAvailabilityFromDestinationAndDocumentSettings_(); - } + this.updateSettingsAvailabilityFromDestinationAndDocumentSettings_(); } private updateSettingsAvailabilityFromDestinationAndDocumentSettings_() { + if (!this.documentSettings || !this.destination) { + return; + } + const isSaveAsPDF = this.destination.type === PrinterType.PDF_PRINTER; const knownSizeToSaveAsPdf = isSaveAsPDF && (!this.documentSettings.isModifiable || @@ -728,9 +731,7 @@ this.setSettingPath_( 'scalingTypePdf.available', scalingAvailable && !this.documentSettings.isModifiable); - const caps = this.destination && this.destination.capabilities ? - this.destination.capabilities.printer : - null; + const caps = this.destination.capabilities?.printer || null; this.setSettingPath_( 'mediaSize.available', !!caps && !!caps.media_size && !knownSizeToSaveAsPdf); @@ -762,9 +763,7 @@ this.settings_.headerFooter.available || this.settings_.rasterize.available); - if (this.destination) { - this.updateSettingsAvailabilityFromDestinationAndDocumentSettings_(); - } + this.updateSettingsAvailabilityFromDestinationAndDocumentSettings_(); } private updateHeaderFooterAvailable_() { @@ -857,6 +856,7 @@ } private updateSettingsValues_() { + assert(this.destination); const caps = this.destination.capabilities ? this.destination.capabilities.printer : null; @@ -1342,6 +1342,7 @@ return; } + assert(this.destination); const mediaSizePolicy = this.policySettings_['mediaSize'].value; const matchingOption = this.destination.getMediaSize( mediaSizePolicy.width, mediaSizePolicy.height);
diff --git a/chrome/browser/resources/print_preview/tsconfig_base.json b/chrome/browser/resources/print_preview/tsconfig_base.json deleted file mode 100644 index 8bda7687..0000000 --- a/chrome/browser/resources/print_preview/tsconfig_base.json +++ /dev/null
@@ -1,9 +0,0 @@ -{ - "extends": "../../../../tools/typescript/tsconfig_base_polymer.json", - "compilerOptions": { - "target": "ES2024", - "lib": ["ES2024", "DOM", "DOM.Iterable"], - "noUncheckedIndexedAccess": true, - "noUnusedLocals": true - } -}
diff --git a/chrome/browser/resources/print_preview/ui/advanced_options_settings.ts b/chrome/browser/resources/print_preview/ui/advanced_options_settings.ts index 50b61c0..e2cad9b 100644 --- a/chrome/browser/resources/print_preview/ui/advanced_options_settings.ts +++ b/chrome/browser/resources/print_preview/ui/advanced_options_settings.ts
@@ -42,7 +42,7 @@ } accessor disabled: boolean = false; - accessor destination: Destination; + accessor destination: Destination|null = null; protected accessor showAdvancedDialog_: boolean = false; protected onButtonClick_() {
diff --git a/chrome/browser/resources/print_preview/ui/advanced_settings_dialog.html b/chrome/browser/resources/print_preview/ui/advanced_settings_dialog.html index ff5058a..696b041 100644 --- a/chrome/browser/resources/print_preview/ui/advanced_settings_dialog.html +++ b/chrome/browser/resources/print_preview/ui/advanced_settings_dialog.html
@@ -1,6 +1,6 @@ <cr-dialog id="dialog" @close="${this.onCloseOrCancel_}"> <div slot="title"> - ${this.i18n('advancedSettingsDialogTitle', this.destination.displayName)} + ${this.i18n('advancedSettingsDialogTitle', this.destination?.displayName || '')} </div> <div slot="body"> <print-preview-search-box id="searchBox"
diff --git a/chrome/browser/resources/print_preview/ui/advanced_settings_dialog.ts b/chrome/browser/resources/print_preview/ui/advanced_settings_dialog.ts index 3694f3f..423c19aa 100644 --- a/chrome/browser/resources/print_preview/ui/advanced_settings_dialog.ts +++ b/chrome/browser/resources/print_preview/ui/advanced_settings_dialog.ts
@@ -59,7 +59,7 @@ }; } - accessor destination: Destination; + accessor destination: Destination|null = null; protected accessor searchQuery_: RegExp|null = null; private accessor hasMatching_: boolean = false; @@ -117,6 +117,10 @@ * @return Whether there is more than one vendor item to display. */ protected hasMultipleItems_(): boolean { + if (!this.destination) { + return false; + } + return this.destination.capabilities!.printer.vendor_capability!.length > 1; } @@ -187,7 +191,7 @@ } protected getVendorCapabilities_(): VendorCapability[] { - return this.destination.capabilities?.printer.vendor_capability || []; + return this.destination?.capabilities?.printer.vendor_capability || []; } protected onSearchQueryChanged_(e: CustomEvent<{value: RegExp | null}>) {
diff --git a/chrome/browser/resources/print_preview/ui/advanced_settings_item.ts b/chrome/browser/resources/print_preview/ui/advanced_settings_item.ts index 520f040f..87ab6285 100644 --- a/chrome/browser/resources/print_preview/ui/advanced_settings_item.ts +++ b/chrome/browser/resources/print_preview/ui/advanced_settings_item.ts
@@ -42,7 +42,7 @@ }; } - accessor capability: VendorCapability; + accessor capability: VendorCapability = {id: '', type: ''}; private accessor currentValue_: string = ''; override connectedCallback() {
diff --git a/chrome/browser/resources/print_preview/ui/app.ts b/chrome/browser/resources/print_preview/ui/app.ts index 9ad8d6e..8d174f7e 100644 --- a/chrome/browser/resources/print_preview/ui/app.ts +++ b/chrome/browser/resources/print_preview/ui/app.ts
@@ -84,7 +84,7 @@ accessor state: State = State.NOT_READY; protected accessor controlsManaged_: boolean = false; - protected accessor destination_: Destination; + protected accessor destination_: Destination|null = null; private accessor destinationsManaged_: boolean = false; protected accessor documentSettings_: DocumentSettings = createDocumentSettings(); @@ -324,7 +324,7 @@ this.startPreviewWhenReady_ = true; if (this.state === State.NOT_READY && - this.destination_.type !== PrinterType.PDF_PRINTER) { + this.destination_!.type !== PrinterType.PDF_PRINTER) { this.nativeLayer_!.recordBooleanHistogram( 'PrintPreview.TransitionedToReadyState', true); } @@ -333,7 +333,7 @@ break; case DestinationState.ERROR: if (this.state === State.NOT_READY && - this.destination_.type !== PrinterType.PDF_PRINTER) { + this.destination_!.type !== PrinterType.PDF_PRINTER) { this.nativeLayer_!.recordBooleanHistogram( 'PrintPreview.TransitionedToReadyState', false); } @@ -358,8 +358,8 @@ // is synced across print-preview-app, print-preview-model and // print-preview-area. await this.updateComplete; - assert(this.destination_.id === this.$.previewArea.destination.id); - assert(this.destination_.id === this.$.model.destination.id); + assert(this.destination_!.id === this.$.previewArea.destination!.id); + assert(this.destination_!.id === this.$.model.destination!.id); this.$.previewArea.startPreview(false); this.startPreviewWhenReady_ = false; } else { @@ -382,12 +382,14 @@ this.remove(); this.nativeLayer_!.dialogClose(this.cancelled_); } else if (this.state === State.PRINT_PENDING) { + assert(this.destination_); if (this.destination_.type !== PrinterType.PDF_PRINTER) { // Only hide the preview for local, non PDF destinations. this.nativeLayer_!.hidePreview(); this.$.state.transitTo(State.HIDDEN); } } else if (this.state === State.PRINTING) { + assert(this.destination_); const whenPrintDone = this.nativeLayer_!.doPrint(this.$.model.createPrintTicket( this.destination_, this.openPdfInPreview_,
diff --git a/chrome/browser/resources/print_preview/ui/button_strip.ts b/chrome/browser/resources/print_preview/ui/button_strip.ts index 5669f05..de2f8e9 100644 --- a/chrome/browser/resources/print_preview/ui/button_strip.ts +++ b/chrome/browser/resources/print_preview/ui/button_strip.ts
@@ -42,7 +42,7 @@ }; } - accessor destination: Destination; + accessor destination: Destination|null = null; accessor firstLoad: boolean = false; accessor state: State = State.NOT_READY; protected accessor printButtonEnabled_: boolean = false; @@ -72,7 +72,7 @@ } private isPdf_(): boolean { - return this.destination && + return !!this.destination && this.destination.type === PrinterType.PDF_PRINTER; }
diff --git a/chrome/browser/resources/print_preview/ui/destination_dialog.ts b/chrome/browser/resources/print_preview/ui/destination_dialog.ts index 37bcbad..4b53bfcf 100644 --- a/chrome/browser/resources/print_preview/ui/destination_dialog.ts +++ b/chrome/browser/resources/print_preview/ui/destination_dialog.ts
@@ -56,7 +56,7 @@ }; } - accessor destinationStore: DestinationStore; + accessor destinationStore: DestinationStore|null = null; protected accessor loadingDestinations_: boolean = false; protected accessor searchQuery_: RegExp|null = null; @@ -93,6 +93,7 @@ private onDestinationStoreSet_() { assert(!this.initialized_); + assert(this.destinationStore); this.tracker_.add( this.destinationStore, DestinationStoreEventType.DESTINATIONS_INSERTED, this.updateDestinations_.bind(this)); @@ -104,7 +105,7 @@ } private updateDestinations_() { - if (this.destinationStore === undefined || !this.initialized_) { + if (!this.destinationStore || !this.initialized_) { return; } @@ -132,13 +133,14 @@ } private selectDestination_(destination: Destination) { + assert(this.destinationStore); this.destinationStore.selectDestination(destination); this.$.dialog.close(); } show() { this.$.dialog.showModal(); - const loading = this.destinationStore === undefined || + const loading = !this.destinationStore || this.destinationStore.isPrintDestinationSearchInProgress; if (!loading) { // All destinations have already loaded.
diff --git a/chrome/browser/resources/print_preview/ui/destination_select.ts b/chrome/browser/resources/print_preview/ui/destination_select.ts index c6ecb66e..ee627cc 100644 --- a/chrome/browser/resources/print_preview/ui/destination_select.ts +++ b/chrome/browser/resources/print_preview/ui/destination_select.ts
@@ -48,7 +48,7 @@ } accessor dark: boolean = false; - accessor destination: Destination; + accessor destination: Destination|null = null; accessor disabled: boolean = false; accessor loaded: boolean = false; accessor noDestinations: boolean = false; @@ -62,7 +62,7 @@ /** Sets the select to the current value of |destination|. */ updateDestination() { - this.selectedValue = this.destination.key; + this.selectedValue = this.destination?.key || ''; } /**
diff --git a/chrome/browser/resources/print_preview/ui/header.ts b/chrome/browser/resources/print_preview/ui/header.ts index dfd2334..db3d721 100644 --- a/chrome/browser/resources/print_preview/ui/header.ts +++ b/chrome/browser/resources/print_preview/ui/header.ts
@@ -46,7 +46,7 @@ }; } - accessor destination: Destination; + accessor destination: Destination|null = null; accessor error: Error|null = null; accessor state: State = State.NOT_READY; accessor managed: boolean = false; @@ -84,7 +84,7 @@ } private isPdf_(): boolean { - return this.destination && + return !!this.destination && this.destination.type === PrinterType.PDF_PRINTER; }
diff --git a/chrome/browser/resources/print_preview/ui/preview_area.ts b/chrome/browser/resources/print_preview/ui/preview_area.ts index dc3f781..1abcaa3b 100644 --- a/chrome/browser/resources/print_preview/ui/preview_area.ts +++ b/chrome/browser/resources/print_preview/ui/preview_area.ts
@@ -6,6 +6,7 @@ import {I18nMixinLit} from 'chrome://resources/cr_elements/i18n_mixin_lit.js'; import {WebUiListenerMixinLit} from 'chrome://resources/cr_elements/web_ui_listener_mixin_lit.js'; +import {assert} from 'chrome://resources/js/assert.js'; import {hasKeyModifiers} from 'chrome://resources/js/util.js'; import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js'; import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js'; @@ -95,7 +96,7 @@ }; } - accessor destination: Destination; + accessor destination: Destination|null = null; accessor documentModifiable: boolean = false; accessor error: Error|null = null; accessor margins: Margins|null = null; @@ -520,6 +521,8 @@ return true; } + assert(this.destination); + const lastTicket = this.lastTicket_; // Margins @@ -611,6 +614,7 @@ /** @return Native color model of the destination. */ private getColorForTicket_(): number { + assert(this.destination); return this.destination.getNativeColorModel( this.getSettingValue('color') as boolean); } @@ -673,6 +677,7 @@ * generated. */ private getPreview_(): Promise<number> { + assert(this.destination); this.inFlightRequestId_++; const ticket: PreviewTicket = { pageRange: this.getSettingValue('ranges'),
diff --git a/chrome/browser/resources/side_panel/customize_chrome/appearance.html.ts b/chrome/browser/resources/side_panel/customize_chrome/appearance.html.ts index fb592736..6cb2cf7c 100644 --- a/chrome/browser/resources/side_panel/customize_chrome/appearance.html.ts +++ b/chrome/browser/resources/side_panel/customize_chrome/appearance.html.ts
@@ -32,7 +32,7 @@ label="$i18n{yourSearchedImage}" label-description="$i18n{currentTheme}"> </customize-chrome-hover-button> -${(!this.isSourceTabFirstPartyNtp_() && !!this.ntpManagedByName_) ? html` +${this.showManagedButton_ ? html` <customize-chrome-hover-button id="thirdPartyManageLinkButton" aria-button-label="${this.i18n('newTabPageManagedByA11yLabel', this.ntpManagedByName_)}"
diff --git a/chrome/browser/resources/side_panel/customize_chrome/appearance.ts b/chrome/browser/resources/side_panel/customize_chrome/appearance.ts index b7cdc39..a27ff3e 100644 --- a/chrome/browser/resources/side_panel/customize_chrome/appearance.ts +++ b/chrome/browser/resources/side_panel/customize_chrome/appearance.ts
@@ -22,8 +22,8 @@ import {getCss} from './appearance.css.js'; import {getHtml} from './appearance.html.js'; import {CustomizeChromeAction, recordCustomizeChromeAction} from './common.js'; -import type {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerInterface, Theme} from './customize_chrome.mojom-webui.js'; import {NewTabPageType} from './customize_chrome.mojom-webui.js'; +import type {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerInterface, Theme} from './customize_chrome.mojom-webui.js'; import {CustomizeChromeApiProxy} from './customize_chrome_api_proxy.js'; export interface AppearanceElement { @@ -77,6 +77,7 @@ showThemeSnapshot_: {type: Boolean}, showUploadedImageButton_: {type: Boolean}, showSearchedImageButton_: {type: Boolean}, + showManagedButton_: {type: Boolean}, showManagedDialog_: {type: Boolean}, showEditTheme_: {type: Boolean}, newTabPageType_: {type: NewTabPageType}, @@ -87,6 +88,7 @@ }, wallpaperSearchEnabled_: {type: Boolean}, + footerEnabled_: {type: Boolean}, }; } @@ -101,11 +103,14 @@ protected accessor showThemeSnapshot_: boolean = false; protected accessor showUploadedImageButton_: boolean = false; protected accessor showSearchedImageButton_: boolean = false; + protected accessor showManagedButton_: boolean = false; protected accessor showManagedDialog_: boolean = false; protected accessor wallpaperSearchButtonEnabled_: boolean = loadTimeData.getBoolean('wallpaperSearchButtonEnabled'); private accessor wallpaperSearchEnabled_: boolean = loadTimeData.getBoolean('wallpaperSearchEnabled'); + private accessor footerEnabled_: boolean = + loadTimeData.getBoolean('footerEnabled'); protected accessor newTabPageType_: NewTabPageType = NewTabPageType.kFirstPartyWebUI; protected accessor showEditTheme_: boolean = true; @@ -189,6 +194,7 @@ this.showThemeSnapshot_ = this.computeShowThemeSnapshot_(); this.showUploadedImageButton_ = this.computeShowUploadedImageButton_(); this.showSearchedImageButton_ = this.computeShowSearchedImageButton_(); + this.showManagedButton_ = this.computeShowManagedButton_(); } this.showBottomDivider_ = this.computeShowBottomDivider_(); @@ -241,6 +247,12 @@ } private computeShowClassicChromeButton_(): boolean { + if (this.footerEnabled_) { + return !!( + this.theme_ && this.theme_.backgroundImage && + (this.newTabPageType_ === NewTabPageType.kFirstPartyWebUI || + this.newTabPageType_ === NewTabPageType.kThirdPartyWebUI)); + } return !!( this.theme_ && (this.theme_.backgroundImage || this.theme_.thirdPartyThemeInfo)); @@ -261,7 +273,7 @@ this.theme_.backgroundImage.isUploadedImage)) && // TODO(crbug.com/404247286) Enable snapshots for extension NTP with 1P // theme. - this.isSourceTabFirstPartyNtp_(); + this.newTabPageType_ === NewTabPageType.kFirstPartyWebUI; } private computeShowUploadedImageButton_(): boolean { @@ -277,8 +289,9 @@ this.theme_.backgroundImage.localBackgroundId); } - protected isSourceTabFirstPartyNtp_(): boolean { - return this.newTabPageType_ === NewTabPageType.kFirstPartyWebUI; + private computeShowManagedButton_(): boolean { + return this.newTabPageType_ !== NewTabPageType.kFirstPartyWebUI && + !!this.ntpManagedByName_; } protected onEditThemeClicked_() {
diff --git a/chrome/browser/resources/side_panel/read_anything/app.ts b/chrome/browser/resources/side_panel/read_anything/app.ts index ecb3566..a0cd1a3 100644 --- a/chrome/browser/resources/side_panel/read_anything/app.ts +++ b/chrome/browser/resources/side_panel/read_anything/app.ts
@@ -692,7 +692,7 @@ } protected updateImages_() { - if (!this.shadowRoot) { + if (!this.shadowRoot || !chrome.readingMode.imagesFeatureEnabled) { return; }
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_interactive_test.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_interactive_test.cc index 2ca9030..09825157 100644 --- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_interactive_test.cc +++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_interactive_test.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/functional/callback.h" +#include "base/strings/stringprintf.h" #include "build/build_config.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/safe_browsing/test_safe_browsing_navigation_observer_manager.h"
diff --git a/chrome/browser/ssl/https_upgrades_browsertest.cc b/chrome/browser/ssl/https_upgrades_browsertest.cc index 1f6ffc4f..522dcbd 100644 --- a/chrome/browser/ssl/https_upgrades_browsertest.cc +++ b/chrome/browser/ssl/https_upgrades_browsertest.cc
@@ -8,6 +8,7 @@ #include "base/memory/raw_ptr.h" #include "base/strings/strcat.h" +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/simple_test_clock.h"
diff --git a/chrome/browser/supervised_user/kids_profile_interactive_uitest.cc b/chrome/browser/supervised_user/kids_profile_interactive_uitest.cc index 223480e3..a4eec88 100644 --- a/chrome/browser/supervised_user/kids_profile_interactive_uitest.cc +++ b/chrome/browser/supervised_user/kids_profile_interactive_uitest.cc
@@ -45,11 +45,12 @@ std::u16string_view tab_title, std::string_view iframe_name) { content::WebContents* web_contents = nullptr; - for (int i = 0; i < browser.GetTabStripModel()->GetTabCount(); ++i) { - std::u16string wc_title = - TabUIHelper::FromWebContents( - browser.GetTabStripModel()->GetWebContentsAt(i)) - ->GetTitle(); + TabStripModel* const tab_strip_model = browser.tab_strip_model(); + for (int i = 0; i < tab_strip_model->GetTabCount(); ++i) { + const std::u16string wc_title = tab_strip_model->GetTabAtIndex(i) + ->GetTabFeatures() + ->tab_ui_helper() + ->GetTitle(); if (wc_title == tab_title) { web_contents = browser.GetTabStripModel()->GetWebContentsAt(i); break;
diff --git a/chrome/browser/trusted_vault/trusted_vault_encryption_keys_tab_helper_browsertest.cc b/chrome/browser/trusted_vault/trusted_vault_encryption_keys_tab_helper_browsertest.cc index a983798..1cae61c 100644 --- a/chrome/browser/trusted_vault/trusted_vault_encryption_keys_tab_helper_browsertest.cc +++ b/chrome/browser/trusted_vault/trusted_vault_encryption_keys_tab_helper_browsertest.cc
@@ -9,6 +9,7 @@ #include "base/feature_list.h" #include "base/path_service.h" +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" #include "build/build_config.h"
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 2e783f8..cc5375c7 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -1873,6 +1873,7 @@ "//chrome/browser/ui/toasts:impl", "//chrome/browser/ui/views/toolbar", "//chrome/browser/ui/views/page_action", + "//chrome/browser/ui/views/new_tab_footer", "//chrome/browser/ui/webui/searchbox", "//chrome/browser/ui/webui/top_chrome:impl", "//chrome/browser/ui/search:impl", @@ -4150,6 +4151,8 @@ "views/location_bar/intent_chip_button.h", "views/location_bar/intent_picker_view.cc", "views/location_bar/intent_picker_view.h", + "views/location_bar/lens_overlay_homework_page_action_icon_view.cc", + "views/location_bar/lens_overlay_homework_page_action_icon_view.h", "views/location_bar/lens_overlay_page_action_icon_view.cc", "views/location_bar/lens_overlay_page_action_icon_view.h", "views/location_bar/location_bar_bubble_delegate_view.cc",
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 633a77eae..86f199c 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
@@ -90,6 +90,13 @@ android:layout_marginHorizontal="@dimen/incognito_indicator_lateral_margin" android:visibility="gone" /> + <ViewStub + android:id="@+id/extension_toolbar_container_stub" + android:inflatedId="@+id/extension_toolbar_container" + android:layout="@layout/extension_toolbar_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" /> + <org.chromium.chrome.browser.toolbar.top.ToggleTabStackButton android:id="@+id/tab_switcher_button" style="@style/ToolbarHoverableButton" @@ -98,13 +105,6 @@ app:menuMaxWidth="@dimen/tab_switcher_menu_width" app:menuVerticalOverlapAnchor="false" /> - <ViewStub - android:id="@+id/extension_toolbar_container_stub" - android:inflatedId="@+id/extension_toolbar_container" - android:layout="@layout/extension_toolbar_container" - android:layout_width="wrap_content" - android:layout_height="match_parent" /> - <include layout="@layout/menu_button"/> </LinearLayout> </org.chromium.chrome.browser.toolbar.top.ToolbarTablet>
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index 5bcef58..8c5e577e 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc
@@ -1277,10 +1277,6 @@ return window_->GetLensOverlayView(); } -new_tab_footer::NewTabFooterWebView* Browser::NewTabFooterWebView() { - return GetBrowserView().new_tab_footer_web_view(); -} - base::CallbackListSubscription Browser::RegisterActiveTabDidChange( ActiveTabChangeCallback callback) { return did_active_tab_change_callback_list_.Add(std::move(callback));
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h index 553762b..1de7f95 100644 --- a/chrome/browser/ui/browser.h +++ b/chrome/browser/ui/browser.h
@@ -129,10 +129,6 @@ class View; } -namespace new_tab_footer { -class NewTabFooterWebView; -} // namespace new_tab_footer - // This enum is not a member of `Browser` so that it can be forward // declared in `unload_controller.h` to avoid circular includes. enum class BrowserClosingStatus { @@ -875,7 +871,6 @@ bool IsVisible() const override; base::WeakPtr<BrowserWindowInterface> GetWeakPtr() override; views::View* LensOverlayView() override; - new_tab_footer::NewTabFooterWebView* NewTabFooterWebView() override; base::CallbackListSubscription RegisterActiveTabDidChange( ActiveTabChangeCallback callback) override; tabs::TabInterface* GetActiveTabInterface() override;
diff --git a/chrome/browser/ui/browser_element_identifiers.cc b/chrome/browser/ui/browser_element_identifiers.cc index 50a7d10..f8afae0e 100644 --- a/chrome/browser/ui/browser_element_identifiers.cc +++ b/chrome/browser/ui/browser_element_identifiers.cc
@@ -52,6 +52,7 @@ DEFINE_ELEMENT_IDENTIFIER_VALUE(kInstallPwaElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kIntentChipElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kLeftAlignedSidePanelSeparatorViewElementId); +DEFINE_ELEMENT_IDENTIFIER_VALUE(kLensOverlayHomeworkPageActionIconElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kLensOverlayPageActionIconElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kLensOverlayTranslateButtonElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kLensPermissionDialogCancelButtonElementId); @@ -61,6 +62,7 @@ DEFINE_ELEMENT_IDENTIFIER_VALUE(kLensSidePanelSearchBoxElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kLocalWebParentApprovalDialogId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kLocalWebParentApprovalDialogErrorId); +DEFINE_ELEMENT_IDENTIFIER_VALUE(kLocationBarElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kLocationIconElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kNewTabButtonElementId); DEFINE_ELEMENT_IDENTIFIER_VALUE(kOfferNotificationChipElementId);
diff --git a/chrome/browser/ui/browser_element_identifiers.h b/chrome/browser/ui/browser_element_identifiers.h index 64d7524..ecfa5f7 100644 --- a/chrome/browser/ui/browser_element_identifiers.h +++ b/chrome/browser/ui/browser_element_identifiers.h
@@ -61,6 +61,7 @@ DECLARE_ELEMENT_IDENTIFIER_VALUE(kInstallPwaElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kIntentChipElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kLeftAlignedSidePanelSeparatorViewElementId); +DECLARE_ELEMENT_IDENTIFIER_VALUE(kLensOverlayHomeworkPageActionIconElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kLensOverlayPageActionIconElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kLensOverlayTranslateButtonElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kLensPermissionDialogCancelButtonElementId); @@ -70,6 +71,7 @@ DECLARE_ELEMENT_IDENTIFIER_VALUE(kLensSidePanelSearchBoxElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kLocalWebParentApprovalDialogId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kLocalWebParentApprovalDialogErrorId); +DECLARE_ELEMENT_IDENTIFIER_VALUE(kLocationBarElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kLocationIconElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kNewTabButtonElementId); DECLARE_ELEMENT_IDENTIFIER_VALUE(kOfferNotificationChipElementId);
diff --git a/chrome/browser/ui/browser_tabrestore.cc b/chrome/browser/ui/browser_tabrestore.cc index a1ca3719..6ba7198 100644 --- a/chrome/browser/ui/browser_tabrestore.cc +++ b/chrome/browser/ui/browser_tabrestore.cc
@@ -18,12 +18,14 @@ #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/tab_ui_helper.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/browser/ui/tabs/tab_enums.h" #include "chrome/browser/ui/tabs/tab_group_model.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "components/sessions/content/content_serialized_navigation_builder.h" #include "components/tab_groups/tab_group_id.h" #include "components/tabs/public/tab_group.h" +#include "components/tabs/public/tab_interface.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/restore_type.h" @@ -75,15 +77,6 @@ std::unique_ptr<WebContents> web_contents = WebContents::CreateWithSessionStorage(create_params, session_storage_namespace_map); - if (from_session_restore) { - // Indicate that the tab is created by session restore. This is used to hide - // the throbber when a background restored tab is loading. TabUIHelper is - // created by TabHelpers::AttachTabHelpers, but this happens later, so we - // explicitly create it early here. - TabUIHelper::CreateForWebContents(web_contents.get()); - TabUIHelper::FromWebContents(web_contents.get()) - ->set_created_by_session_restore(true); - } apps::SetAppIdForWebContents(browser->profile(), web_contents.get(), extension_app_id); @@ -187,6 +180,15 @@ add_types, group); } + if (from_session_restore) { + // Indicate that the tab is created by session restore. This is used to hide + // the throbber when a background restored tab is loading. + tabs::TabInterface* const tab_interface = + tabs::TabInterface::GetFromContents(raw_web_contents); + tabs::TabFeatures* const tab_features = tab_interface->GetTabFeatures(); + tab_features->tab_ui_helper()->set_created_by_session_restore(true); + } + // We set the size of the view here, before Blink does its initial layout. // If we don't, the initial layout of background tabs will be performed // with a view width of 0, which may cause script outputs and anchor link @@ -303,6 +305,16 @@ insertion_index + 1, std::move(web_contents), AddTabTypes::ADD_ACTIVE | AddTabTypes::ADD_INHERIT_OPENER, tab_strip->GetTabGroupForTab(insertion_index)); + + if (from_session_restore) { + // Indicate that the tab is created by session restore. This is used to hide + // the throbber when a background restored tab is loading. + tabs::TabInterface* const tab_interface = + tabs::TabInterface::GetFromContents(raw_web_contents); + tabs::TabFeatures* const tab_features = tab_interface->GetTabFeatures(); + tab_features->tab_ui_helper()->set_created_by_session_restore(true); + } + tab_strip->CloseWebContentsAt(insertion_index, TabCloseTypes::CLOSE_NONE); LoadRestoredTabIfVisible(browser, raw_web_contents);
diff --git a/chrome/browser/ui/browser_window/BUILD.gn b/chrome/browser/ui/browser_window/BUILD.gn index 422ba98..bcd1ef7 100644 --- a/chrome/browser/ui/browser_window/BUILD.gn +++ b/chrome/browser/ui/browser_window/BUILD.gn
@@ -41,6 +41,7 @@ "//chrome/browser/ui/tabs/tab_strip_api", "//chrome/browser/ui/toasts", "//chrome/browser/ui/views/download", + "//chrome/browser/ui/views/new_tab_footer", "//chrome/browser/ui/views/side_panel", "//chrome/browser/ui/views/toolbar", "//components/collaboration/public:public",
diff --git a/chrome/browser/ui/browser_window/browser_window_features.cc b/chrome/browser/ui/browser_window/browser_window_features.cc index bfd41dbe..4dce6d3 100644 --- a/chrome/browser/ui/browser_window/browser_window_features.cc +++ b/chrome/browser/ui/browser_window/browser_window_features.cc
@@ -48,6 +48,7 @@ #include "chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_coordinator.h" #include "chrome/browser/ui/views/location_bar/location_bar_view.h" #include "chrome/browser/ui/views/media_router/cast_browser_controller.h" +#include "chrome/browser/ui/views/new_tab_footer/footer_controller.h" #include "chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_toolbar_bubble_controller.h" #include "chrome/browser/ui/views/side_panel/bookmarks/bookmarks_side_panel_coordinator.h" #include "chrome/browser/ui/views/side_panel/extensions/extension_side_panel_manager.h" @@ -65,6 +66,7 @@ #include "components/lens/lens_features.h" #include "components/profile_metrics/browser_profile_type.h" #include "components/saved_tab_groups/public/features.h" +#include "components/search/ntp_features.h" #include "components/search/search.h" #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) @@ -324,6 +326,12 @@ std::make_unique<tab_groups::SharedTabGroupFeedbackController>( browser_view); } + + if (base::FeatureList::IsEnabled(ntp_features::kNtpFooter)) { + new_tab_footer_controller_ = + std::make_unique<new_tab_footer::NewTabFooterController>( + browser_view->browser(), browser_view->new_tab_footer_web_view()); + } } void BrowserWindowFeatures::TearDownPreBrowserViewDestruction() { @@ -356,6 +364,10 @@ if (chrome_labs_coordinator_) { chrome_labs_coordinator_->TearDown(); } + + if (new_tab_footer_controller_) { + new_tab_footer_controller_->TearDown(); + } } SidePanelUI* BrowserWindowFeatures::side_panel_ui() {
diff --git a/chrome/browser/ui/browser_window/public/browser_window_features.h b/chrome/browser/ui/browser_window/public/browser_window_features.h index 8f3ab47..f72f878 100644 --- a/chrome/browser/ui/browser_window/public/browser_window_features.h +++ b/chrome/browser/ui/browser_window/public/browser_window_features.h
@@ -71,6 +71,10 @@ class MemorySaverBubbleController; } // namespace memory_saver +namespace new_tab_footer { +class NewTabFooterController; +} // namespace new_tab_footer + namespace tab_groups { class SessionServiceTabGroupSyncObserver; class SharedTabGroupFeedbackController; @@ -234,6 +238,10 @@ return tab_strip_service_.get(); } + new_tab_footer::NewTabFooterController* new_tab_footer_controller() { + return new_tab_footer_controller_.get(); + } + protected: BrowserWindowFeatures(); @@ -321,6 +329,9 @@ std::unique_ptr<TabMenuModelDelegate> tab_menu_model_delegate_; + std::unique_ptr<new_tab_footer::NewTabFooterController> + new_tab_footer_controller_; + // This is an experimental API that interacts with the TabStripModel. std::unique_ptr<TabStripServiceRegister> tab_strip_service_; };
diff --git a/chrome/browser/ui/browser_window/public/browser_window_interface.h b/chrome/browser/ui/browser_window/public/browser_window_interface.h index 8fda121..fbb7152 100644 --- a/chrome/browser/ui/browser_window/public/browser_window_interface.h +++ b/chrome/browser/ui/browser_window/public/browser_window_interface.h
@@ -18,10 +18,6 @@ // your feature needs. This comment will be deleted after there are 10+ features // in BrowserWindowFeatures. -namespace new_tab_footer { -class NewTabFooterWebView; -} // namespace new_tab_footer - namespace tabs { class TabInterface; } // namespace tabs @@ -117,9 +113,6 @@ // Returns the view that houses the Lens overlay. virtual views::View* LensOverlayView() = 0; - // Returns the view that houses the New Tab Footer. - virtual new_tab_footer::NewTabFooterWebView* NewTabFooterWebView() = 0; - using ActiveTabChangeCallback = base::RepeatingCallback<void(BrowserWindowInterface*)>; virtual base::CallbackListSubscription RegisterActiveTabDidChange(
diff --git a/chrome/browser/ui/browser_window/test/mock_browser_window_interface.h b/chrome/browser/ui/browser_window/test/mock_browser_window_interface.h index 1b52a92..0240ee6 100644 --- a/chrome/browser/ui/browser_window/test/mock_browser_window_interface.h +++ b/chrome/browser/ui/browser_window/test/mock_browser_window_interface.h
@@ -37,10 +37,6 @@ (), (override)); MOCK_METHOD(views::View*, LensOverlayView, (), (override)); - MOCK_METHOD(new_tab_footer::NewTabFooterWebView*, - NewTabFooterWebView, - (), - (override)); MOCK_METHOD(base::CallbackListSubscription, RegisterActiveTabDidChange, (ActiveTabChangeCallback callback),
diff --git a/chrome/browser/ui/cocoa/tab_menu_bridge.mm b/chrome/browser/ui/cocoa/tab_menu_bridge.mm index b185534..5660c0f7 100644 --- a/chrome/browser/ui/cocoa/tab_menu_bridge.mm +++ b/chrome/browser/ui/cocoa/tab_menu_bridge.mm
@@ -10,11 +10,14 @@ #include "base/strings/sys_string_conversions.h" #include "chrome/browser/ui/recently_audible_helper.h" #include "chrome/browser/ui/tab_ui_helper.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" #include "chrome/grit/generated_resources.h" +#include "components/tabs/public/tab_interface.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util_mac.h" +#include "ui/base/models/image_model.h" #include "ui/gfx/image/image_skia_util_mac.h" using MenuItemCallback = base::RepeatingCallback<void(NSMenuItem*)>; @@ -23,7 +26,10 @@ void UpdateItemForWebContents(NSMenuItem* item, content::WebContents* web_contents) { - TabUIHelper* tab_ui_helper = TabUIHelper::FromWebContents(web_contents); + tabs::TabInterface* const tab_interface = + tabs::TabInterface::GetFromContents(web_contents); + TabUIHelper* const tab_ui_helper = + tab_interface->GetTabFeatures()->tab_ui_helper(); auto* audio_helper = RecentlyAudibleHelper::FromWebContents(web_contents); if (audio_helper && audio_helper->WasRecentlyAudible()) {
diff --git a/chrome/browser/ui/cocoa/tab_menu_bridge_unittest.mm b/chrome/browser/ui/cocoa/tab_menu_bridge_unittest.mm index c07988d..c62a75f 100644 --- a/chrome/browser/ui/cocoa/tab_menu_bridge_unittest.mm +++ b/chrome/browser/ui/cocoa/tab_menu_bridge_unittest.mm
@@ -6,21 +6,27 @@ #import <Cocoa/Cocoa.h> +#include <memory> + +#include "base/functional/callback_helpers.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/test/task_environment.h" #include "chrome/browser/favicon/favicon_utils.h" #include "chrome/browser/ui/tab_ui_helper.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/browser/ui/tabs/tab_enums.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" #include "chrome/browser/ui/tabs/test_util.h" #include "chrome/test/base/testing_profile.h" +#include "components/tabs/public/tab_interface.h" #include "content/public/test/test_renderer_host.h" #include "content/public/test/web_contents_tester.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest_mac.h" +namespace { constexpr int kStaticItemCount = 4; class TabStripModelUiHelperDelegate : public TestTabStripModelDelegate { @@ -29,10 +35,11 @@ TestTabStripModelDelegate::WillAddWebContents(contents); favicon::CreateContentFaviconDriverForWebContents(contents); - TabUIHelper::CreateForWebContents(contents); } }; +} // namespace + class TabMenuBridgeTest : public ::testing::Test { public: void SetUp() override { @@ -75,8 +82,15 @@ return model()->GetWebContentsAt(index); } - void AddModelTabNamed(const std::string& name) { - model()->AppendWebContents(CreateWebContents(name), true); + void AddModelTabNamed(const std::string& name, + TabStripModel* tab_strip_model) { + std::unique_ptr<tabs::TabModel> tab_model = + std::make_unique<tabs::TabModel>(CreateWebContents(name), + tab_strip_model); + tabs::TabFeatures* const tab_features = tab_model->GetTabFeatures(); + tab_features->SetTabUIHelperForTesting( + std::make_unique<TabUIHelper>(*tab_model)); + tab_strip_model->AppendTab(std::move(tab_model), true); } int ModelIndexForTabNamed(const std::string& name) { @@ -190,18 +204,19 @@ } TEST_F(TabMenuBridgeTest, TracksModelUpdates) { + TabStripModel* const tab_strip_model = model(); TabMenuBridge bridge(model(), menu_root()); bridge.BuildMenu(); - AddModelTabNamed("Tab 1"); - AddModelTabNamed("Tab 2"); - AddModelTabNamed("Tab 3"); + AddModelTabNamed("Tab 1", tab_strip_model); + AddModelTabNamed("Tab 2", tab_strip_model); + AddModelTabNamed("Tab 3", tab_strip_model); ExpectDynamicTabsInMenuAre({"Tab 1", "Tab 2", "Tab 3"}); RemoveModelTabNamed("Tab 2"); ExpectDynamicTabsInMenuAre({"Tab 1", "Tab 3"}); - AddModelTabNamed("Tab 2"); + AddModelTabNamed("Tab 2", tab_strip_model); ExpectDynamicTabsInMenuAre({"Tab 1", "Tab 3", "Tab 2"}); RenameModelTabNamed("Tab 1", "Tab 4"); @@ -216,13 +231,14 @@ // Tests that dynamic menu items added by the bridge are removed on // bridge destruction. TEST_F(TabMenuBridgeTest, RemoveDynamicMenuItemsOnDestruct) { + TabStripModel* const tab_strip_model = model(); std::unique_ptr<TabMenuBridge> bridge = - std::make_unique<TabMenuBridge>(model(), menu_root()); + std::make_unique<TabMenuBridge>(tab_strip_model, menu_root()); bridge->BuildMenu(); - AddModelTabNamed("Tab 1"); - AddModelTabNamed("Tab 2"); - AddModelTabNamed("Tab 3"); + AddModelTabNamed("Tab 1", tab_strip_model); + AddModelTabNamed("Tab 2", tab_strip_model); + AddModelTabNamed("Tab 3", tab_strip_model); ExpectDynamicTabsInMenuAre({"Tab 1", "Tab 2", "Tab 3"}); bridge.reset(); @@ -231,11 +247,12 @@ } TEST_F(TabMenuBridgeTest, ClickingMenuActivatesTab) { - TabMenuBridge bridge(model(), menu_root()); + TabStripModel* const tab_strip_model = model(); + TabMenuBridge bridge(tab_strip_model, menu_root()); bridge.BuildMenu(); - AddModelTabNamed("Tab 1"); - AddModelTabNamed("Tab 2"); + AddModelTabNamed("Tab 1", tab_strip_model); + AddModelTabNamed("Tab 2", tab_strip_model); EXPECT_EQ(ActiveTabName(), "Tab 2"); NSMenuItem* tab1_item = MenuItemForTabNamed("Tab 1"); @@ -262,13 +279,14 @@ // static items, and ended up with incorrect indexes. This test exercises that // behavior. TEST_F(TabMenuBridgeTest, SwappingBridgeRecreatesMenu) { - auto bridge = std::make_unique<TabMenuBridge>(model(), menu_root()); + TabStripModel* const tab_strip_model = model(); + auto bridge = std::make_unique<TabMenuBridge>(tab_strip_model, menu_root()); bridge->BuildMenu(); - AddModelTabNamed("Tab 1"); + AddModelTabNamed("Tab 1", tab_strip_model); auto model2 = std::make_unique<TabStripModel>(delegate(), profile()); - model2->AppendWebContents(CreateWebContents("Tab 2"), true); + AddModelTabNamed("Tab 2", model2.get()); bridge = std::make_unique<TabMenuBridge>(model2.get(), menu_root()); bridge->BuildMenu(); @@ -288,12 +306,13 @@ } TEST_F(TabMenuBridgeTest, ActiveItemTracksChanges) { - TabMenuBridge bridge(model(), menu_root()); + TabStripModel* const tab_strip_model = model(); + TabMenuBridge bridge(tab_strip_model, menu_root()); bridge.BuildMenu(); - AddModelTabNamed("Tab 1"); - AddModelTabNamed("Tab 2"); - AddModelTabNamed("Tab 3"); + AddModelTabNamed("Tab 1", tab_strip_model); + AddModelTabNamed("Tab 2", tab_strip_model); + AddModelTabNamed("Tab 3", tab_strip_model); ExpectActiveMenuItemNameIs("Tab 3"); ActivateModelTabNamed("Tab 2");
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.cc b/chrome/browser/ui/lens/lens_overlay_controller.cc index f231058..bed44d0 100644 --- a/chrome/browser/ui/lens/lens_overlay_controller.cc +++ b/chrome/browser/ui/lens/lens_overlay_controller.cc
@@ -135,11 +135,6 @@ // taking a screenshot. constexpr base::TimeDelta kReflowWaitTimeout = base::Milliseconds(200); -// The amount of change in bytes that is considered a significant change and -// should trigger a page content update request. This provides tolerance in -// case there is slight variation in the retrievied bytes in between calls. -constexpr float kByteChangeTolerancePercent = 0.01; - // The maximum length of the DOM text to consider for OCR similarity. // Currently 50 MB constexpr int kMaxDomTextLengthForOcrSimilarity = 50 * 1000 * 1000; @@ -460,7 +455,6 @@ } lens_selection_type_ = lens::UNKNOWN_SELECTION_TYPE; - should_show_overlay_ = true; is_page_context_eligible_ = true; should_send_screenshot_on_init_ = false; @@ -1024,9 +1018,10 @@ CHECK(lens_overlay_query_controller_); GetContextualizationController()->StartContextualization( invocation_source, - base::BindOnce(&LensOverlayController::OnPageContextUpdated, - weak_factory_.GetWeakPtr(), destination_url, match_type, - is_zero_prefix_suggestion, invocation_source)); + base::BindOnce( + &LensOverlayController::OnPageContextUpdatedForSuggestion, + weak_factory_.GetWeakPtr(), destination_url, match_type, + is_zero_prefix_suggestion, invocation_source)); return; } @@ -1045,9 +1040,10 @@ // search request. CHECK(lens_overlay_query_controller_); GetContextualizationController()->TryUpdatePageContextualization( - base::BindOnce(&LensOverlayController::OnPageContextUpdated, - weak_factory_.GetWeakPtr(), destination_url, match_type, - is_zero_prefix_suggestion, invocation_source)); + base::BindOnce( + &LensOverlayController::OnPageContextUpdatedForSuggestion, + weak_factory_.GetWeakPtr(), destination_url, match_type, + is_zero_prefix_suggestion, invocation_source)); return; } @@ -1058,13 +1054,6 @@ destination_url, match_type, is_zero_prefix_suggestion); } -void LensOverlayController::StartContextualizationWithoutOverlay( - lens::LensOverlayInvocationSource invocation_source, - lens::LensOverlayQueryController* lens_overlay_query_controller) { - should_show_overlay_ = false; - ShowUI(invocation_source, lens_overlay_query_controller); -} - void LensOverlayController::ShowUIWithPendingRegion( lens::LensOverlayQueryController* lens_overlay_query_controller, lens::LensOverlayInvocationSource invocation_source, @@ -1170,7 +1159,10 @@ // If the live page is showing and the searchbox becomes focused, showing // intent to issue a new query, upload the new page content for // contextualization. - TryUpdatePageContextualization(); + // TODO(crbug.com/418856988): Replace this with a call that starts + // contextualization without the unneeded callback. + GetContextualizationController()->TryUpdatePageContextualization( + base::DoNothing()); } } } @@ -1255,13 +1247,10 @@ // If contextual searchbox is enabled, make sure the page bytes are current // prior to issuing the search box request. - GetContextualizationController()->GetPageContextualization( - base::BindOnce(&LensOverlayController::UpdatePageContextualization, - weak_factory_.GetWeakPtr()) - .Then(base::BindOnce( - &LensOverlayController::IssueSearchBoxRequestPart2, - weak_factory_.GetWeakPtr(), query_start_time, search_box_text, - match_type, is_zero_prefix_suggestion, additional_query_params))); + GetContextualizationController()->TryUpdatePageContextualization( + base::BindOnce(&LensOverlayController::IssueSearchBoxRequestPart2, + weak_factory_.GetWeakPtr(), query_start_time, search_box_text, match_type, + is_zero_prefix_suggestion, additional_query_params)); } void LensOverlayController::IssueContextualTextRequest( @@ -1269,6 +1258,7 @@ const std::string& text_query, lens::LensOverlaySelectionType selection_type) { if (is_page_context_eligible_) { + lens_selection_type_ = selection_type; lens_overlay_query_controller_->SendContextualTextQuery( query_start_time, text_query, selection_type, initialization_data_->additional_search_query_params_); @@ -1582,8 +1572,6 @@ initialization_data->primary_content_type_ = primary_content_type; initialization_data->pdf_page_count_ = page_count; InitializeOverlay(std::move(initialization_data)); - - RecordDocumentMetrics(page_count); } std::vector<lens::mojom::CenterRotatedBoxPtr> @@ -1628,195 +1616,6 @@ return significant_region_boxes; } -void LensOverlayController::TryUpdatePageContextualization() { - // If there is already an upload, do not send another request. - // TODO(crbug.com/399154548): Ideally, there could be two uploads in progress - // at a time, however, the current query controller implementation does not - // support this. - if (lens_overlay_query_controller_->IsPageContentUploadInProgress()) { - return; - } - - GetContextualizationController()->GetPageContextualization( - base::BindOnce(&LensOverlayController::UpdatePageContextualization, - weak_factory_.GetWeakPtr())); -} - -void LensOverlayController::UpdatePageContextualization( - std::vector<lens::PageContent> page_contents, - lens::MimeType primary_content_type, - std::optional<uint32_t> page_count) { - if (!lens::features::IsLensOverlayContextualSearchboxEnabled()) { - return; - } - - // If the protected page is showing, then return early as none of the content - // will be sent. - if (results_side_panel_coordinator_->IsShowingProtectedErrorPage()) { - return; - } - - // Do not capture a new screenshot if the feature param is not enabled or if - // the user is not viewing the live page, meaning the viewport cannot have - // changed. - if (!lens::features::UpdateViewportEachQueryEnabled() || - state_ != State::kLivePageAndResults) { - UpdatePageContextualizationPart2(page_contents, primary_content_type, - page_count, SkBitmap()); - return; - } - - // Begin the process of grabbing a screenshot. - content::RenderWidgetHostView* view = tab_->GetContents() - ->GetPrimaryMainFrame() - ->GetRenderViewHost() - ->GetWidget() - ->GetView(); - if (!IsScreenshotPossible(view)) { - UpdatePageContextualizationPart2(page_contents, primary_content_type, - page_count, SkBitmap()); - return; - } - view->CopyFromSurface( - /*src_rect=*/gfx::Rect(), /*output_size=*/gfx::Size(), - base::BindPostTask( - base::SequencedTaskRunner::GetCurrentDefault(), - base::BindOnce( - &LensOverlayController::UpdatePageContextualizationPart2, - weak_factory_.GetWeakPtr(), page_contents, primary_content_type, - page_count))); -} - -void LensOverlayController::UpdatePageContextualizationPart2( - std::vector<lens::PageContent> page_contents, - lens::MimeType primary_content_type, - std::optional<uint32_t> page_count, - const SkBitmap& bitmap) { -#if BUILDFLAG(ENABLE_PDF) - if (lens::features::SendPdfCurrentPageEnabled()) { - pdf::PDFDocumentHelper* pdf_helper = - pdf::PDFDocumentHelper::MaybeGetForWebContents(tab_->GetContents()); - if (pdf_helper) { - pdf_helper->GetMostVisiblePageIndex(base::BindOnce( - &LensOverlayController::UpdatePageContextualizationPart3, - weak_factory_.GetWeakPtr(), page_contents, primary_content_type, - page_count, bitmap)); - return; - } - } -#endif // BUILDFLAG(ENABLE_PDF) - - UpdatePageContextualizationPart3(page_contents, primary_content_type, - page_count, bitmap, - /*most_visible_page=*/std::nullopt); -} - -void LensOverlayController::UpdatePageContextualizationPart3( - std::vector<lens::PageContent> page_contents, - lens::MimeType primary_content_type, - std::optional<uint32_t> page_count, - const SkBitmap& bitmap, - std::optional<uint32_t> most_visible_page) { - bool sending_bitmap = false; - if (!bitmap.drawsNothing() && - (initialization_data_->updated_screenshot_.drawsNothing() || - !lens::AreBitmapsEqual(initialization_data_->updated_screenshot_, - bitmap))) { - initialization_data_->updated_screenshot_ = bitmap; - sending_bitmap = true; - } - initialization_data_->last_retrieved_most_visible_page_ = most_visible_page; - - // TODO(crbug.com/399215935): Ideally, this check should ensure that any of - // the content date has not changed. For now, we only check if the - // primary_content_type bytes have changed. - auto old_page_content_it = std::ranges::find_if( - initialization_data_->page_contents_, - [&primary_content_type](const auto& page_content) { - return page_content.content_type_ == primary_content_type; - }); - auto new_page_content_it = std::ranges::find_if( - page_contents, [&primary_content_type](const auto& page_content) { - return page_content.content_type_ == primary_content_type; - }); - const lens::PageContent* old_page_content = - old_page_content_it != initialization_data_->page_contents_.end() - ? &(*old_page_content_it) - : nullptr; - const lens::PageContent* new_page_content = - new_page_content_it != page_contents.end() ? &(*new_page_content_it) - : nullptr; - - if (initialization_data_->primary_content_type_ == primary_content_type && - old_page_content && new_page_content) { - const float old_size = old_page_content->bytes_.size(); - const float new_size = new_page_content->bytes_.size(); - const float percent_changed = abs((new_size - old_size) / old_size); - if (percent_changed < kByteChangeTolerancePercent) { - if (!sending_bitmap) { - // If the bytes have not changed more than our threshold and the - // screenshot has not changed, exit early. Notify the query controller - // that the user may be issuing a search request, and therefore the - // query should be restarted if TTL expired. If the bytes did change, - // this will happen automatically as a result of the - // SendUpdatedPageContent call below. - lens_overlay_query_controller_->MaybeRestartQueryFlow(); - return; - } - - // If the screenshot has changed but the bytes have not, send only the - // screenshot. - lens_overlay_query_controller_->SendUpdatedPageContent( - std::nullopt, std::nullopt, std::nullopt, std::nullopt, - initialization_data_->last_retrieved_most_visible_page_, - sending_bitmap ? bitmap : SkBitmap()); - return; - } - } - - // Since the page content has changed, let the query controller know to avoid - // dangling pointers. - lens_overlay_query_controller_->ResetPageContentData(); - - initialization_data_->page_contents_ = page_contents; - initialization_data_->primary_content_type_ = primary_content_type; - - // If no bytes were retrieved from the page, the query won't be able to be - // contextualized. Notify the side panel so the ghost loader isn't shown. No - // need to update update the overlay as this update only happens on navigation - // where the side panel will already be open. - if (!new_page_content || new_page_content->bytes_.empty()) { - SuppressGhostLoader(); - } - -#if BUILDFLAG(ENABLE_PDF) - // If the new page is a PDF, fetch the text from the page to be used as early - // suggest signals. - if (new_page_content && - new_page_content->content_type_ == lens::MimeType::kPdf) { - CHECK(lens_overlay_query_controller_); - GetContextualizationController()->FetchVisiblePageIndexAndGetPartialPdfText( - page_count.value_or(0), - base::BindOnce(&LensOverlayController::OnPdfPartialPageTextRetrieved, - weak_factory_.GetWeakPtr())); - } -#endif - - is_upload_progress_bar_shown_ = true; - is_first_upload_handler_event_ = true; - lens_overlay_query_controller_->SendUpdatedPageContent( - initialization_data_->page_contents_, - initialization_data_->primary_content_type_, - lens_search_controller_->GetPageURL(), - lens_search_controller_->GetPageTitle(), - initialization_data_->last_retrieved_most_visible_page_, - sending_bitmap ? bitmap : SkBitmap()); - - GetLensSessionMetricsLogger()->OnFollowUpPageContentRetrieved( - primary_content_type); - RecordDocumentMetrics(page_count); -} - void LensOverlayController::SuppressGhostLoader() { if (page_) { page_->SuppressGhostLoader(); @@ -1841,16 +1640,10 @@ // Grab the tab contents web view and disable mouse and keyboard inputs to it. auto* contents_web_view = tab_->GetBrowserWindowInterface()->GetWebView(); CHECK(contents_web_view); - if (should_show_overlay_) { - contents_web_view->SetEnabled(false); - } + contents_web_view->SetEnabled(false); // If the view already exists, we just need to reshow it. if (overlay_view_) { - // Exit early to avoid reshowing the overlay if it should not be shown. - if (!should_show_overlay_) { - return; - } // Restore the state to show the overlay. overlay_view_->SetVisible(true); preselection_widget_anchor_->SetVisible(true); @@ -1865,11 +1658,9 @@ return; } - // Create the views that will house our UI. The overlay view might not - // actually be shown, as dictated by `should_show_overlay_`. It still needs to - // be created so the initialization process completes. + // Create the views that will house our UI. overlay_view_ = CreateViewForOverlay(); - overlay_view_->SetVisible(should_show_overlay_); + overlay_view_->SetVisible(true); // Sanity check that the overlay view is above the contents web view. auto* parent_view = overlay_view_->parent(); @@ -1895,10 +1686,8 @@ // The overlay needs to be focused on show to immediately begin // receiving key events. - if (should_show_overlay_) { - CHECK(overlay_web_view_); - overlay_web_view_->RequestFocus(); - } + CHECK(overlay_web_view_); + overlay_web_view_->RequestFocus(); // Listen to the render process housing out overlay. overlay_web_view_->GetWebContents() @@ -1971,43 +1760,20 @@ InitializeOverlayUI(*initialization_data_); base::UmaHistogramBoolean("Lens.Overlay.Shown", true); -#if BUILDFLAG(ENABLE_PDF) - // If PDF content was extracted from the page, fetch the text from the PDF to - // be used as early suggest signals. - if (!initialization_data_->page_contents_.empty() && - initialization_data_->page_contents_.front().content_type_ == - lens::MimeType::kPdf) { - CHECK(initialization_data_->pdf_page_count_.has_value()); - CHECK(lens_overlay_query_controller_); - GetContextualizationController()->FetchVisiblePageIndexAndGetPartialPdfText( - initialization_data_->pdf_page_count_.value(), - base::BindOnce(&LensOverlayController::OnPdfPartialPageTextRetrieved, - weak_factory_.GetWeakPtr())); - } -#endif - // If the StartQueryFlow optimization is enabled, the page contents will not // be sent with the initial image request, so we need to send it here. if (lens::features::IsLensOverlayContextualSearchboxEnabled() && lens::features::IsLensOverlayEarlyStartQueryFlowOptimizationEnabled() && is_page_context_eligible_) { - // The screenshot is not sent here unless forced by - // `should_send_screenshot_on_init_` as it should have been sent in the - // original StartQueryFlow call. - lens_overlay_query_controller_->SendUpdatedPageContent( - initialization_data_->page_contents_, - initialization_data_->primary_content_type_, - lens_search_controller_->GetPageURL(), - lens_search_controller_->GetPageTitle(), - initialization_data_->last_retrieved_most_visible_page_, - should_send_screenshot_on_init_ - ? initialization_data_->initial_screenshot_ - : SkBitmap()); + // TODO(crbug.com/418856988): Replace this with a call that starts + // contextualization without the unneeded callback. + GetContextualizationController()->TryUpdatePageContextualization( + base::DoNothing()); } // Show the preselection overlay now that the overlay is initialized and ready // to be shown. - if (!pending_region_ && should_show_overlay_) { + if (!pending_region_) { ShowPreselectionBubble(); } @@ -2592,7 +2358,7 @@ void LensOverlayController::ShowPreselectionBubble() { // Don't show the preselection bubble if the overlay is not being shown. - if (!should_show_overlay_ || state() == State::kOverlayAndResults) { + if (state() == State::kOverlayAndResults) { return; } @@ -3152,7 +2918,7 @@ initialization_data_->pdf_pages_text_ = std::move(pdf_pages_text); } -void LensOverlayController::OnPageContextUpdated( +void LensOverlayController::OnPageContextUpdatedForSuggestion( const GURL& destination_url, AutocompleteMatchType::Type match_type, bool is_zero_prefix_suggestion,
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.h b/chrome/browser/ui/lens/lens_overlay_controller.h index 30bf5c85..6f2e7c2 100644 --- a/chrome/browser/ui/lens/lens_overlay_controller.h +++ b/chrome/browser/ui/lens/lens_overlay_controller.h
@@ -356,6 +356,16 @@ // Gets string for invocation source enum, used for logging metrics. std::string GetInvocationSourceString(); + // Records the UMA for the metrics relating to the document where the + // contextual search box was shown. If this is a webpage, records the size of + // the innerHtml and the innerText. If this is a PDF, records the byte size of + // the PDF and the number of pages. `pdf_page_count` is only used for PDFs. + // TODO(crbug.com/404941800): Move the document metrics to where the page + // content bytes are stored. Temporarily public and should be made a private + // function again after contextualization controller is fully migrated to + // avoid overlay dependencies. + void RecordDocumentMetrics(std::optional<uint32_t> pdf_page_count); + // Gets the WebContents housed in the side panel for testing. content::WebContents* GetSidePanelWebContentsForTesting(); @@ -443,19 +453,6 @@ bool is_zero_prefix_suggestion, lens::LensOverlayInvocationSource invocation_source); - // Starts the contextualization flow without the overlay being shown to the - // user. - // TODO(crbug.com/404941800): This still goes through the entire - // TODO(crbug.com/404941800): This still goes through the entire - // initialization flow for the overlay. This is not efficient, but is being - // done to unblock the contextual searchbox prototype. This should be - // refactored to be done in the LensSearchController to not go through the - // overlay controller. - // Virtual for testing. - virtual void StartContextualizationWithoutOverlay( - lens::LensOverlayInvocationSource invocation_source, - lens::LensOverlayQueryController* lens_overlay_query_controller); - // Sets a region to search after the overlay loads, then calls ShowUI(). // All units are in device pixels. region_bitmap contains the high definition // image bytes to use for the search instead of cropping the region from the @@ -720,34 +717,6 @@ std::vector<lens::mojom::CenterRotatedBoxPtr> ConvertSignificantRegionBoxes( const std::vector<gfx::Rect>& all_bounds); - // Tries to fetch the underlying page content bytes and update the query flow - // with them. - void TryUpdatePageContextualization(); - - // Begin updating page contextualization by potentially taking a new - // screenshot. - void UpdatePageContextualization(std::vector<lens::PageContent> page_contents, - lens::MimeType primary_content_type, - std::optional<uint32_t> pdf_page_count); - - // Continue updating page contextualization by potentially getting the current - // PDF page. - void UpdatePageContextualizationPart2( - std::vector<lens::PageContent> page_contents, - lens::MimeType primary_content_type, - std::optional<uint32_t> pdf_page_count, - const SkBitmap& bitmap); - - // Updates the query flow with the new page content bytes and/or screenshot. A - // request will only be sent if the bytes are different from the previous - // bytes sent or the screenshot is different from the previous screenshot. - void UpdatePageContextualizationPart3( - std::vector<lens::PageContent> page_contents, - lens::MimeType primary_content_type, - std::optional<uint32_t> pdf_page_count, - const SkBitmap& bitmap, - std::optional<uint32_t> pdf_current_page); - // Updates state of the ghost loader. |suppress_ghost_loader| is true when // the page bytes can't be uploaded. void SuppressGhostLoader(); @@ -929,14 +898,6 @@ bool is_zero_prefix_suggestion, std::map<std::string, std::string> additional_query_params); - // Records the UMA for the metrics relating to the document where the - // contextual search box was shown. If this is a webpage, records the size of - // the innerHtml and the innerText. If this is a PDF, records the byte size of - // the PDF and the number of pages. `pdf_page_count` is only used for PDFs. - // TODO(crbug.com/404941800): Move the document metrics to where the page - // content bytes are stored. - void RecordDocumentMetrics(std::optional<uint32_t> pdf_page_count); - // Posts a task to the background thread to calculate the OCR DOM similarity // and then records the result. Only records the similarity once per session. // Only records the similarity if the OCR text and page content are available. @@ -975,8 +936,9 @@ void OnPdfPartialPageTextRetrieved( std::vector<std::u16string> pdf_pages_text); - // Callback to run when the page context has been updated. - void OnPageContextUpdated( + // Callback to run when the page context has been updated and the suggestion + // query should now be issued. + void OnPageContextUpdatedForSuggestion( const GURL& destination_url, AutocompleteMatchType::Type match_type, bool is_zero_prefix_suggestion, @@ -1016,11 +978,6 @@ lens::LensOverlayInvocationSource invocation_source_ = lens::LensOverlayInvocationSource::kAppMenu; - // Whether the overlay should be visible when it is opened. This is a hacky - // approach to start contextual searchbox flow without the overlay UI being - // shown. See StartContextualizationWithoutOverlay todo for more details. - bool should_show_overlay_ = true; - // A contextual search request to be issued once the overlay is initialized. base::OnceClosure pending_contextual_search_request_;
diff --git a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc index 20c675b2..d2eede5 100644 --- a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc +++ b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
@@ -235,6 +235,10 @@ " === $1; return iframeSrcLoaded && stickPresent && " " searchboxInputLoaded;})();"; +// TODO(crbug.com/417812533): Tests are currently failing in branded builds due +// to missing metrics. Will be fixed as part of work to move recording document +// metrics to the contextualization controller. +#if !BUILDFLAG(GOOGLE_CHROME_BRANDING) constexpr char kCheckSidePanelThumbnailShownScript[] = "(function() {const appRoot = " "document.getElementsByTagName('lens-side-panel-app')[0].shadowRoot;" @@ -245,6 +249,7 @@ "const imageSrc = thumbnailRoot.getElementById('image').src;" "return window.getComputedStyle(thumbContainer).display !== 'none' && " " imageSrc.startsWith('data:image/jpeg');})();"; +#endif // !BUILDFLAG(GOOGLE_CHROME_BRANDING) constexpr char kCheckSidePanelToastShownScript[] = "(function() {const appRoot = " @@ -1458,6 +1463,10 @@ ASSERT_TRUE(base::test::RunUntil([&]() { return observer.request_shown(); })); } +// TODO(crbug.com/417812533): Tests are currently failing in branded builds due +// to missing metrics. Will be fixed as part of work to move recording document +// metrics to the contextualization controller. +#if !BUILDFLAG(GOOGLE_CHROME_BRANDING) // TODO(b/335801964): Test flaky on Mac. #if BUILDFLAG(IS_MAC) #define MAYBE_SidePanelInteractionsAfterRegionSelection \ @@ -1519,7 +1528,7 @@ EXPECT_FALSE(controller->get_selected_region_for_testing().is_null()); EXPECT_TRUE(base::StartsWith(controller->GetThumbnailForTesting(), "data:")); EXPECT_EQ(controller->GetPageClassificationForTesting(), - metrics::OmniboxEventProto::LENS_SIDE_PANEL_SEARCHBOX); + metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX); // Verify that after text selection, the controller has a copy of the text, // the thumbnail is no longer shown and the controller's copy of the @@ -1537,7 +1546,7 @@ EXPECT_TRUE(controller->get_selected_region_for_testing().is_null()); EXPECT_TRUE(controller->GetThumbnailForTesting().empty()); EXPECT_EQ(controller->GetPageClassificationForTesting(), - metrics::OmniboxEventProto::SEARCH_SIDE_PANEL_SEARCHBOX); + metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX); // Verify that after a signal from the searchbox that the text was modified, // no text selection is present. @@ -1547,6 +1556,7 @@ fake_controller->FlushForTesting(); EXPECT_TRUE(fake_controller->fake_overlay_page_.did_clear_text_selection_); } +#endif // !BUILDFLAG(GOOGLE_CHROME_BRANDING) // TODO(b/350991033): Test flaky on Mac. #if BUILDFLAG(IS_MAC) @@ -3506,188 +3516,6 @@ } IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, - PopAndLoadTranslateQueryFromHistory) { - 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. - OpenLensOverlayWithPendingRegion( - LensOverlayInvocationSource::kContentAreaContextMenuImage, - kTestRegion->Clone(), CreateNonEmptyBitmap(100, 100)); - ASSERT_EQ(controller->state(), State::kScreenshot); - ASSERT_TRUE(base::test::RunUntil( - [&]() { return controller->state() == State::kOverlayAndResults; })); - EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); - EXPECT_TRUE(content::WaitForLoadStop( - controller->GetSidePanelWebContentsForTesting())); - EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible()); - auto* fake_query_controller = - static_cast<lens::TestLensOverlayQueryController*>( - controller->get_lens_overlay_query_controller_for_testing()); - EXPECT_EQ(fake_query_controller->last_lens_selection_type(), - lens::INJECTED_IMAGE); - - const GURL first_search_url( - "https://www.google.com/" - "search?source=chrome.cr.ctxi&q=&lns_fp=1&lns_mode=un" - "&gsc=2&hl=en-US&cs=0"); - - // The search query history stack should be empty and the currently loaded - // query should be set. - EXPECT_TRUE(GetSearchQueryHistory().empty()); - const auto first_search_query = GetLoadedSearchQuery(); - EXPECT_TRUE(first_search_query); - EXPECT_TRUE(first_search_query->search_query_text_.empty()); - VerifySearchQueryParameters(first_search_query->search_query_url_); - VerifyTextQueriesAreEqual(first_search_query->search_query_url_, - first_search_url); - EXPECT_FALSE(first_search_query->selected_region_thumbnail_uri_.empty()); - EXPECT_EQ(first_search_query->selected_region_, kTestRegion); - EXPECT_FALSE(first_search_query->selected_region_bitmap_.drawsNothing()); - EXPECT_EQ(first_search_query->selected_region_bitmap_.width(), 100); - EXPECT_EQ(first_search_query->selected_region_bitmap_.height(), 100); - EXPECT_FALSE(first_search_query->selected_text_); - EXPECT_EQ(first_search_query->lens_selection_type_, lens::INJECTED_IMAGE); - - // Loading a url in the side panel should show the results page. - const GURL second_search_url( - "https://www.google.com/" - "search?source=chrome.cr.menu&q=oranges&lns_fp=1&lns_mode=text" - "&gsc=2&hl=en-US&cs=0"); - // Do the full image translate query first so it gets attached to the query. - const std::string source_lang = "auto"; - const std::string target_lang = "fi"; - content::TestNavigationObserver second_observer( - controller->GetSidePanelWebContentsForTesting()); - controller->IssueTranslateFullPageRequestForTesting(source_lang, target_lang); - controller->IssueTextSelectionRequestForTesting("oranges", 20, 200); - second_observer.Wait(); - - // The search query history stack should be size 1. - EXPECT_EQ(GetSearchQueryHistory().size(), 1UL); - const auto second_search_query = GetLoadedSearchQuery(); - EXPECT_TRUE(second_search_query); - EXPECT_EQ(second_search_query->search_query_text_, "oranges"); - VerifySearchQueryParameters(second_search_query->search_query_url_); - VerifyTextQueriesAreEqual(second_search_query->search_query_url_, - second_search_url); - EXPECT_TRUE(second_search_query->selected_region_thumbnail_uri_.empty()); - EXPECT_TRUE(second_search_query->selected_region_bitmap_.drawsNothing()); - EXPECT_FALSE(second_search_query->selected_region_); - EXPECT_TRUE(second_search_query->selected_text_); - EXPECT_EQ(second_search_query->selected_text_->first, 20); - EXPECT_EQ(second_search_query->selected_text_->second, 200); - EXPECT_TRUE(second_search_query->translate_options_); - EXPECT_EQ(second_search_query->translate_options_->source_language, - source_lang); - EXPECT_EQ(second_search_query->translate_options_->target_language, - target_lang); - EXPECT_EQ(second_search_query->lens_selection_type_, - lens::SELECT_TEXT_HIGHLIGHT); - VerifySearchQueryParameters(second_observer.last_navigation_url()); - VerifyTextQueriesAreEqual(second_observer.last_navigation_url(), - second_search_url); - - // Loading a second url in the side panel should show the results page. - const GURL third_search_url( - "https://www.google.com/" - "search?source=chrome.cr.menu&q=kiwi&lns_fp=1&lns_mode=text&gsc=2" - "&hl=en-US&cs=0"); - // We can't use content::WaitForLoadStop here since the last navigation is - // successful. - content::TestNavigationObserver third_observer( - controller->GetSidePanelWebContentsForTesting()); - controller->IssueEndTranslateModeRequestForTesting(); - controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( - third_search_url); - third_observer.Wait(); - - // The search query history stack should have 2 entries and the currently - // loaded query should be set to the new query - EXPECT_EQ(GetSearchQueryHistory().size(), 2UL); - const auto third_search_query = GetLoadedSearchQuery(); - EXPECT_TRUE(third_search_query); - EXPECT_EQ(third_search_query->search_query_text_, "kiwi"); - VerifySearchQueryParameters(third_search_query->search_query_url_); - VerifyTextQueriesAreEqual(third_search_query->search_query_url_, - third_search_url); - EXPECT_TRUE(third_search_query->selected_region_thumbnail_uri_.empty()); - EXPECT_FALSE(third_search_query->selected_region_); - EXPECT_FALSE(third_search_query->selected_text_); - EXPECT_FALSE(third_search_query->translate_options_); - EXPECT_EQ(third_search_query->lens_selection_type_, - lens::UNKNOWN_SELECTION_TYPE); - VerifySearchQueryParameters(third_observer.last_navigation_url()); - VerifyTextQueriesAreEqual(third_observer.last_navigation_url(), - third_search_url); - - // Popping the query should load the previous query into the results frame. - content::TestNavigationObserver fourth_observer( - controller->GetSidePanelWebContentsForTesting()); - controller->results_side_panel_coordinator()->PopAndLoadQueryFromHistory(); - fourth_observer.Wait(); - - // The search query history stack should have 1 entry and the currently loaded - // query should be set to the previous query. - EXPECT_EQ(GetSearchQueryHistory().size(), 1UL); - const auto first_popped_query = GetLoadedSearchQuery(); - EXPECT_TRUE(first_popped_query); - EXPECT_EQ(first_popped_query->search_query_text_, "oranges"); - VerifySearchQueryParameters(first_popped_query->search_query_url_); - VerifyTextQueriesAreEqual(first_popped_query->search_query_url_, - second_search_url); - EXPECT_TRUE(first_popped_query->selected_region_thumbnail_uri_.empty()); - EXPECT_FALSE(first_popped_query->selected_region_); - EXPECT_TRUE(first_popped_query->selected_text_); - EXPECT_EQ(first_popped_query->selected_text_->first, 20); - EXPECT_EQ(first_popped_query->selected_text_->second, 200); - EXPECT_TRUE(first_popped_query->translate_options_); - EXPECT_EQ(first_popped_query->translate_options_->source_language, - source_lang); - EXPECT_EQ(first_popped_query->translate_options_->target_language, - target_lang); - EXPECT_EQ(first_popped_query->lens_selection_type_, - lens::SELECT_TEXT_HIGHLIGHT); - VerifySearchQueryParameters(fourth_observer.last_navigation_url()); - VerifyTextQueriesAreEqual(fourth_observer.last_navigation_url(), - second_search_url); - auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); - fake_controller->FlushForTesting(); - EXPECT_EQ(fake_controller->fake_overlay_page_.source_language_, source_lang); - EXPECT_EQ(fake_controller->fake_overlay_page_.target_language_, target_lang); - - // Popping the query should load the previous query into the results frame. - content::TestNavigationObserver fifth_observer( - controller->GetSidePanelWebContentsForTesting()); - controller->results_side_panel_coordinator()->PopAndLoadQueryFromHistory(); - fifth_observer.Wait(); - - // The search query history stack should be empty and the currently loaded - // query should be set. - EXPECT_TRUE(GetSearchQueryHistory().empty()); - const auto second_popped_query = GetLoadedSearchQuery(); - EXPECT_TRUE(second_popped_query); - EXPECT_TRUE(second_popped_query->search_query_text_.empty()); - VerifySearchQueryParameters(second_popped_query->search_query_url_); - VerifyTextQueriesAreEqual(second_popped_query->search_query_url_, - first_search_url); - EXPECT_FALSE(second_popped_query->selected_region_thumbnail_uri_.empty()); - EXPECT_EQ(second_popped_query->selected_region_, kTestRegion); - EXPECT_FALSE(second_popped_query->selected_region_bitmap_.drawsNothing()); - EXPECT_EQ(second_popped_query->selected_region_bitmap_.width(), 100); - EXPECT_EQ(second_popped_query->selected_region_bitmap_.height(), 100); - EXPECT_FALSE(second_popped_query->selected_text_); - EXPECT_EQ(second_popped_query->lens_selection_type_, lens::INJECTED_IMAGE); - EXPECT_FALSE(second_popped_query->translate_options_); - fake_controller->FlushForTesting(); - EXPECT_TRUE(fake_controller->fake_overlay_page_.source_language_.empty()); - EXPECT_TRUE(fake_controller->fake_overlay_page_.target_language_.empty()); -} - -IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, PopAndLoadQueryFromHistoryWithRegionAndTextSelection) { WaitForPaint(); @@ -3739,7 +3567,7 @@ controller->GetSidePanelWebContentsForTesting()); controller->IssueLensRegionRequestForTesting(kTestRegion->Clone(), /*is_click=*/false); - second_search_observer.Wait(); + second_search_observer.WaitForNavigationFinished(); // The search query history stack should have 1 entry and the currently loaded // region should be set. @@ -3761,7 +3589,7 @@ content::TestNavigationObserver third_search_observer( controller->GetSidePanelWebContentsForTesting()); controller->IssueTextSelectionRequestForTesting("kiwi", 1, 100); - third_search_observer.Wait(); + third_search_observer.WaitForNavigationFinished(); // The search query history stack should have 2 entries and the currently // loaded query should be set to the new query @@ -3801,7 +3629,7 @@ EXPECT_EQ(fake_query_controller->last_lens_selection_type(), lens::REGION_SEARCH); - first_pop_observer.Wait(); + first_pop_observer.WaitForNavigationFinished(); // The search query history stack should have 1 entry and the previously // loaded region should be present. @@ -3820,7 +3648,7 @@ content::TestNavigationObserver second_pop_observer( controller->GetSidePanelWebContentsForTesting()); controller->results_side_panel_coordinator()->PopAndLoadQueryFromHistory(); - second_pop_observer.Wait(); + second_pop_observer.WaitForNavigationFinished(); // The search query history stack should be empty and the currently loaded // query should be set to the original query. @@ -3831,7 +3659,9 @@ EXPECT_EQ(loaded_search_query->search_query_text_, "oranges"); url_without_start_time_or_size = RemoveStartTimeAndSizeParams(loaded_search_query->search_query_url_); - EXPECT_EQ(url_without_start_time_or_size, first_search_url); + VerifySearchQueryParameters(loaded_search_query->search_query_url_); + VerifyTextQueriesAreEqual(loaded_search_query->search_query_url_, + first_search_url); EXPECT_TRUE(loaded_search_query->selected_region_thumbnail_uri_.empty()); EXPECT_FALSE(loaded_search_query->selected_region_); EXPECT_TRUE(loaded_search_query->selected_text_); @@ -3839,10 +3669,9 @@ lens::SELECT_TEXT_HIGHLIGHT); EXPECT_EQ(loaded_search_query->selected_text_->first, 20); EXPECT_EQ(loaded_search_query->selected_text_->second, 200); - url_without_start_time_or_size = - RemoveStartTimeAndSizeParams(second_pop_observer.last_navigation_url()); - EXPECT_EQ(url_without_start_time_or_size, first_search_url); - + VerifySearchQueryParameters(second_pop_observer.last_navigation_url()); + VerifyTextQueriesAreEqual(second_pop_observer.last_navigation_url(), + first_search_url); // Verify the text selection was sent back to mojo and any old selections // were cleared. auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); @@ -5737,6 +5566,10 @@ .last_received_should_show_contextual_searchbox_); } +// TODO(crbug.com/417812533): Tests are currently failing in branded builds due +// to missing metrics. Will be fixed as part of work to move recording document +// metrics to the contextualization controller. +#if !BUILDFLAG(GOOGLE_CHROME_BRANDING) IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, PartialPdfIncludedInRequest) { // Open the PDF document and wait for it to finish loading. @@ -5767,6 +5600,7 @@ fake_query_controller->last_sent_partial_content().pages(0).text_segments( 0)); } +#endif // !BUILDFLAG(GOOGLE_CHROME_BRANDING) IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, PageUrlIncludedInRequest) { @@ -5820,6 +5654,10 @@ .pdf_page_number()); } +// TODO(crbug.com/417812533): Tests are currently failing in branded builds due +// to missing metrics. Will be fixed as part of work to move recording document +// metrics to the contextualization controller. +#if !BUILDFLAG(GOOGLE_CHROME_BRANDING) IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, PdfBytesInFollowUpRequest) { base::HistogramTester histogram_tester; @@ -5886,6 +5724,7 @@ "TimeFromNavigationToFirstInteraction", /*expected_count=*/1); } +#endif // !BUILDFLAG(GOOGLE_CHROME_BRANDING) IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, LargePdfNotIncludedInRequest) { @@ -6008,6 +5847,10 @@ })); } +// TODO(crbug.com/417812533): Tests are currently failing in branded builds due +// to missing metrics. Will be fixed as part of work to move recording document +// metrics to the contextualization controller. +#if !BUILDFLAG(GOOGLE_CHROME_BRANDING) IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, Histograms) { ukm::TestAutoSetUkmRecorder test_ukm_recorder; @@ -6145,6 +5988,7 @@ kPageContentTypeName, static_cast<int64_t>(lens::MimeType::kPdf)); } +#endif // !BUILDFLAG(GOOGLE_CHROME_BRANDING) IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, RecordSearchboxFocusedInSessionNavigationHistograms) { @@ -6291,6 +6135,10 @@ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); } +// TODO(crbug.com/417812533): Tests are currently failing in branded builds due +// to missing metrics. Will be fixed as part of work to move recording document +// metrics to the contextualization controller. +#if !BUILDFLAG(GOOGLE_CHROME_BRANDING) class LensOverlayControllerBrowserPDFUpdatedContentFieldsTest : public LensOverlayControllerBrowserPDFContextualizationTest { public: @@ -6409,13 +6257,17 @@ // TODO(crbug.com/40268279): Stop testing both modes after OOPIF PDF viewer // launches. +INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE( + LensOverlayControllerBrowserPDFUpdatedContentFieldsTest); +INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE( + LensOverlayControllerBrowserPDFIncreaseLimitTest); +#endif // !BUILDFLAG(GOOGLE_CHROME_BRANDING) + +// TODO(crbug.com/40268279): Stop testing both modes after OOPIF PDF viewer +// launches. INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(LensOverlayControllerBrowserPDFTest); INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE( LensOverlayControllerBrowserPDFContextualizationTest); -INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE( - LensOverlayControllerBrowserPDFUpdatedContentFieldsTest); -INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE( - LensOverlayControllerBrowserPDFIncreaseLimitTest); // Test with --enable-pixel-output-in-tests enabled, required to actually grab // screenshots for color extraction. @@ -6703,8 +6555,9 @@ auto* fake_query_controller = static_cast<lens::TestLensOverlayQueryController*>( controller->get_lens_overlay_query_controller_for_testing()); - ASSERT_FALSE( - fake_query_controller->last_sent_underlying_content_bytes().empty()); + ASSERT_TRUE(base::test::RunUntil([&]() { + return !fake_query_controller->last_sent_underlying_content_bytes().empty(); + })); ASSERT_EQ(lens::MimeType::kPlainText, fake_query_controller->last_sent_underlying_content_type()); @@ -6716,6 +6569,10 @@ last_sent_underlying_content_bytes.end())); } +// TODO(crbug.com/417812533): Tests are currently failing in branded builds due +// to missing metrics. Will be fixed as part of work to move recording document +// metrics to the contextualization controller. +#if !BUILDFLAG(GOOGLE_CHROME_BRANDING) IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, InnerTextBytesInFollowUpRequest) { base::HistogramTester histogram_tester; @@ -6807,6 +6664,7 @@ // Verify the similarity score was only recorded once for the session. histogram_tester.ExpectTotalCount("Lens.Overlay.OcrDomSimilarity", 1); } +#endif // !BUILDFLAG(GOOGLE_CHROME_BRANDING) IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, UpdateScreenshotOnSearchboxFocus) { @@ -6864,6 +6722,10 @@ .has_image_data()); } +// TODO(crbug.com/417812533): Tests are currently failing in branded builds due +// to missing metrics. Will be fixed as part of work to move recording document +// metrics to the contextualization controller. +#if !BUILDFLAG(GOOGLE_CHROME_BRANDING) IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, WebDocumentTypeHistograms) { ukm::TestAutoSetUkmRecorder test_ukm_recorder; @@ -6940,6 +6802,7 @@ 80) == 1; })); } +#endif // !BUILDFLAG(GOOGLE_CHROME_BRANDING) IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, RecordSearchboxFocusedInSessionHistograms) { @@ -6961,8 +6824,9 @@ auto* fake_query_controller = static_cast<lens::TestLensOverlayQueryController*>( controller->get_lens_overlay_query_controller_for_testing()); - ASSERT_FALSE( - fake_query_controller->last_sent_underlying_content_bytes().empty()); + ASSERT_TRUE(base::test::RunUntil([&]() { + return !fake_query_controller->last_sent_underlying_content_bytes().empty(); + })); // Simulate the searchbox being focused. controller->OnFocusChangedForTesting(true); @@ -7022,8 +6886,9 @@ auto* fake_query_controller = static_cast<lens::TestLensOverlayQueryController*>( controller->get_lens_overlay_query_controller_for_testing()); - ASSERT_FALSE( - fake_query_controller->last_sent_underlying_content_bytes().empty()); + ASSERT_TRUE(base::test::RunUntil([&]() { + return !fake_query_controller->last_sent_underlying_content_bytes().empty(); + })); // Close the overlay and assert that the histogram was recorded once and // that the searchbox was not focused in the session. @@ -7080,8 +6945,10 @@ auto* fake_query_controller = static_cast<lens::TestLensOverlayQueryController*>( controller->get_lens_overlay_query_controller_for_testing()); - ASSERT_FALSE( - fake_query_controller->last_sent_underlying_content_bytes().empty()); + // Verify bytes was updated. + ASSERT_TRUE(base::test::RunUntil([&]() { + return !fake_query_controller->last_sent_underlying_content_bytes().empty(); + })); controller->OnZeroSuggestShownForTesting(); @@ -7217,8 +7084,9 @@ auto* fake_query_controller = static_cast<lens::TestLensOverlayQueryController*>( controller->get_lens_overlay_query_controller_for_testing()); - ASSERT_FALSE( - fake_query_controller->last_sent_underlying_content_bytes().empty()); + ASSERT_TRUE(base::test::RunUntil([&]() { + return !fake_query_controller->last_sent_underlying_content_bytes().empty(); + })); controller->OnZeroSuggestShownForTesting(); @@ -7324,8 +7192,9 @@ auto* fake_query_controller = static_cast<lens::TestLensOverlayQueryController*>( controller->get_lens_overlay_query_controller_for_testing()); - ASSERT_FALSE( - fake_query_controller->last_sent_underlying_content_bytes().empty()); + ASSERT_TRUE(base::test::RunUntil([&]() { + return !fake_query_controller->last_sent_underlying_content_bytes().empty(); + })); // Close the overlay and assert that the histogram was recorded once and // that zps was not shown in the session. @@ -7997,8 +7866,9 @@ auto* fake_query_controller = static_cast<lens::TestLensOverlayQueryController*>( controller->get_lens_overlay_query_controller_for_testing()); - ASSERT_FALSE( - fake_query_controller->last_sent_underlying_content_bytes().empty()); + ASSERT_TRUE(base::test::RunUntil([&]() { + return !fake_query_controller->last_sent_underlying_content_bytes().empty(); + })); ASSERT_EQ(lens::MimeType::kHtml, fake_query_controller->last_sent_underlying_content_type()); @@ -8339,6 +8209,10 @@ .last_received_should_show_contextual_searchbox_); } +// TODO(crbug.com/417812533): Tests are currently failing in branded builds due +// to missing metrics. Will be fixed as part of work to move recording document +// metrics to the contextualization controller. +#if !BUILDFLAG(GOOGLE_CHROME_BRANDING) IN_PROC_BROWSER_TEST_F(LensOverlayControllerInnerHtmlWithInnerTextAndApc, PageContentTypeHistograms) { ukm::TestAutoSetUkmRecorder test_ukm_recorder; @@ -8418,6 +8292,7 @@ 80) == 1; })); } +#endif // !BUILDFLAG(GOOGLE_CHROME_BRANDING) IN_PROC_BROWSER_TEST_F(LensOverlayControllerInnerHtmlWithInnerTextAndApc, PageNotContextEligibleError) {
diff --git a/chrome/browser/ui/lens/lens_overlay_entry_point_controller.cc b/chrome/browser/ui/lens/lens_overlay_entry_point_controller.cc index 3197a16..1b6821c 100644 --- a/chrome/browser/ui/lens/lens_overlay_entry_point_controller.cc +++ b/chrome/browser/ui/lens/lens_overlay_entry_point_controller.cc
@@ -189,7 +189,7 @@ UpdatePageActionState(); } -bool LensOverlayEntryPointController::IsUrlEduEligible(const GURL& url) { +bool LensOverlayEntryPointController::IsUrlEduEligible(const GURL& url) const { if (!IsEnabled()) { return false; }
diff --git a/chrome/browser/ui/lens/lens_overlay_entry_point_controller.h b/chrome/browser/ui/lens/lens_overlay_entry_point_controller.h index 8b29c9b..111b54d 100644 --- a/chrome/browser/ui/lens/lens_overlay_entry_point_controller.h +++ b/chrome/browser/ui/lens/lens_overlay_entry_point_controller.h
@@ -67,7 +67,7 @@ // Returns true if the given URL is eligible for EDU promos present on some // entrypoints. - bool IsUrlEduEligible(const GURL& url); + bool IsUrlEduEligible(const GURL& url) const; // Invokes the entrypoint action. static void InvokeAction(tabs::TabInterface* active_tab,
diff --git a/chrome/browser/ui/lens/lens_overlay_query_controller.cc b/chrome/browser/ui/lens/lens_overlay_query_controller.cc index dce4004..191e6fd 100644 --- a/chrome/browser/ui/lens/lens_overlay_query_controller.cc +++ b/chrome/browser/ui/lens/lens_overlay_query_controller.cc
@@ -330,6 +330,12 @@ return lens::LensOverlayClientLogs::IMAGE_CONTEXT_MENU; case lens::LensOverlayInvocationSource::kOmnibox: return lens::LensOverlayClientLogs::OMNIBOX_BUTTON; + case lens::LensOverlayInvocationSource::kOmniboxContextualSuggestion: + return lens::LensOverlayClientLogs::OMNIBOX_CONTEXTUAL_SUGGESTION; + case lens::LensOverlayInvocationSource::kOmniboxPageAction: + return lens::LensOverlayClientLogs::OMNIBOX_PAGE_ACTION; + case lens::LensOverlayInvocationSource::kHomeworkActionChip: + return lens::LensOverlayClientLogs::HOMEWORK_ACTION_CHIP; case lens::LensOverlayInvocationSource::kToolbar: return lens::LensOverlayClientLogs::TOOLBAR_BUTTON; case lens::LensOverlayInvocationSource::kFindInPage:
diff --git a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc index fbc35eb4..4c0af91 100644 --- a/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc +++ b/chrome/browser/ui/lens/lens_overlay_untrusted_ui.cc
@@ -246,6 +246,9 @@ html_source->AddBoolean( "enableGradientRegionStroke", lens::features::GetVisualSelectionUpdatesEnableGradientRegionStroke()); + html_source->AddBoolean( + "enableRegionSelectedGlow", + lens::features::GetVisualSelectionUpdatesEnableRegionSelectedGlow()); html_source->AddBoolean("autoFocusSearchbox", lens::features::ShouldAutoFocusSearchbox()); html_source->AddBoolean("cornerSlidersEnabled",
diff --git a/chrome/browser/ui/lens/lens_overlay_url_builder.cc b/chrome/browser/ui/lens/lens_overlay_url_builder.cc index ffb774e..48acfb7f 100644 --- a/chrome/browser/ui/lens/lens_overlay_url_builder.cc +++ b/chrome/browser/ui/lens/lens_overlay_url_builder.cc
@@ -80,6 +80,10 @@ inline constexpr char kInvocationSourceFindInPage[] = "chrome.cr.find"; inline constexpr char kInvocationSourceToolbarIcon[] = "chrome.cr.tbic"; inline constexpr char kInvocationSourceOmniboxIcon[] = "chrome.cr.obic"; +inline constexpr char kInvocationSourceOmniboxPageAction[] = "chrome.cr.obpa"; +inline constexpr char kInvocationSourceOmniboxContextualSuggestion[] = + "chrome.cr.obcs"; +inline constexpr char kInvocationSourceHomeworkActionChip[] = "chrome.cr.hwac"; // The url query param for the viewport width and height. inline constexpr char kViewportWidthQueryParamKey[] = "biw"; @@ -254,6 +258,15 @@ case lens::LensOverlayInvocationSource::kOmnibox: param_value = kInvocationSourceOmniboxIcon; break; + case lens::LensOverlayInvocationSource::kOmniboxPageAction: + param_value = kInvocationSourceOmniboxPageAction; + break; + case lens::LensOverlayInvocationSource::kOmniboxContextualSuggestion: + param_value = kInvocationSourceOmniboxContextualSuggestion; + break; + case lens::LensOverlayInvocationSource::kHomeworkActionChip: + param_value = kInvocationSourceHomeworkActionChip; + break; case lens::LensOverlayInvocationSource::kLVFShutterButton: case lens::LensOverlayInvocationSource::kLVFGallery: case lens::LensOverlayInvocationSource::kContextMenu:
diff --git a/chrome/browser/ui/lens/lens_search_contextualization_controller.cc b/chrome/browser/ui/lens/lens_search_contextualization_controller.cc index 3c807881..28556f11 100644 --- a/chrome/browser/ui/lens/lens_search_contextualization_controller.cc +++ b/chrome/browser/ui/lens/lens_search_contextualization_controller.cc
@@ -128,6 +128,10 @@ void LensSearchContextualizationController::TryUpdatePageContextualization( OnPageContextUpdatedCallback callback) { if (state_ == State::kOff) { + // TODO(crbug.com/418825720): The viewport screenshot should be only be set + // in this controller in the future. + viewport_screenshot_ = lens_search_controller_->lens_overlay_controller() + ->initial_screenshot(); state_ = State::kActive; } CHECK(state_ == State::kActive); @@ -226,6 +230,13 @@ lens::MimeType primary_content_type, std::optional<uint32_t> page_count, const SkBitmap& bitmap) { + // It's possible the Lens session could have been closed while updating the + // page context. Return early and do not run the callback as it should have + // been cleared. + if (state_ == State::kOff || !on_page_context_updated_callback_) { + return; + } + #if BUILDFLAG(ENABLE_PDF) if (lens::features::SendPdfCurrentPageEnabled()) { pdf::PDFDocumentHelper* pdf_helper = @@ -253,6 +264,13 @@ std::optional<uint32_t> page_count, const SkBitmap& bitmap, std::optional<uint32_t> most_visible_page) { + // It's possible the Lens session could have been closed while updating the + // page context. Return early and do not run the callback as it should have + // been cleared. + if (state_ == State::kOff || !on_page_context_updated_callback_) { + return; + } + bool sending_bitmap = false; if (!bitmap.drawsNothing() && (viewport_screenshot_.drawsNothing() || @@ -345,7 +363,12 @@ lens_search_controller_->GetPageURL(), lens_search_controller_->GetPageTitle(), last_retrieved_most_visible_page_, sending_bitmap ? bitmap : SkBitmap()); - // TODO(crbug.com/417812533): Record document metrics. + // TODO(crbug.com/417812533): Record document metrics in metrics helper or in + // this controller instead. + if (lens_search_controller_->lens_overlay_controller()->IsOverlayActive()) { + lens_search_controller_->lens_overlay_controller()->RecordDocumentMetrics( + page_count.value_or(0)); + } lens_search_controller_->lens_session_metrics_logger() ->OnFollowUpPageContentRetrieved(primary_content_type);
diff --git a/chrome/browser/ui/lens/lens_search_controller.cc b/chrome/browser/ui/lens/lens_search_controller.cc index 6b7c459e..1e9edf7 100644 --- a/chrome/browser/ui/lens/lens_search_controller.cc +++ b/chrome/browser/ui/lens/lens_search_controller.cc
@@ -187,29 +187,30 @@ // Setup all state necessary for this Lens session. StartLensSession(invocation_source); - // TODO(crbug.com/404941800): This flow should not start the overlay once - // contextualization is separated from the overlay. - lens_overlay_controller_->StartContextualizationWithoutOverlay( - invocation_source, lens_overlay_query_controller_.get()); + // TODO(crbug.com/418856988): Replace this with a call that starts + // contextualization without the unneeded callback. + lens_contextualization_controller_->StartContextualization(invocation_source, + base::DoNothing()); } void LensSearchController::IssueContextualSearchRequest( + lens::LensOverlayInvocationSource invocation_source, const GURL& destination_url, AutocompleteMatchType::Type match_type, bool is_zero_prefix_suggestion) { - // TODO(crbug.com/402497756): For prototyping, reusing the existing - // omnibox entry point. However, for production, create a new invocation - // source for this new entry point. - lens::LensOverlayInvocationSource invocation_source = - lens::LensOverlayInvocationSource::kOmnibox; + // This method should only be used by the omnibox contextual suggestion flow. + // There is no dependency on the omnibox, so this check is solely to ensure a + // new flow is not accidentally added. + CHECK(invocation_source == + lens::LensOverlayInvocationSource::kOmniboxContextualSuggestion); // If the eligibility checks fail, do not procced with opening any UI. if (!RunLensEligibilityChecks( invocation_source, /*permission_granted_callback=*/base::BindRepeating( &LensSearchController::IssueContextualSearchRequest, - weak_ptr_factory_.GetWeakPtr(), destination_url, match_type, - is_zero_prefix_suggestion))) { + weak_ptr_factory_.GetWeakPtr(), invocation_source, + destination_url, match_type, is_zero_prefix_suggestion))) { return; }
diff --git a/chrome/browser/ui/lens/lens_search_controller.h b/chrome/browser/ui/lens/lens_search_controller.h index 39938898..83f1cb0c 100644 --- a/chrome/browser/ui/lens/lens_search_controller.h +++ b/chrome/browser/ui/lens/lens_search_controller.h
@@ -114,9 +114,11 @@ // is fully opened. // TODO(crbug.com/403629222): Revisit if it makes sense to pass the // destination URL instead of the query text directly. - void IssueContextualSearchRequest(const GURL& destination_url, - AutocompleteMatchType::Type match_type, - bool is_zero_prefix_suggestion); + void IssueContextualSearchRequest( + lens::LensOverlayInvocationSource invocation_source, + const GURL& destination_url, + AutocompleteMatchType::Type match_type, + bool is_zero_prefix_suggestion); // Starts the closing process of the overlay. This is an asynchronous process // with the following sequence:
diff --git a/chrome/browser/ui/lens/lens_url_matcher.cc b/chrome/browser/ui/lens/lens_url_matcher.cc index 15a0839..7ad399b 100644 --- a/chrome/browser/ui/lens/lens_url_matcher.cc +++ b/chrome/browser/ui/lens/lens_url_matcher.cc
@@ -88,7 +88,7 @@ void LensUrlMatcher::InitializePathAllowMatcher( std::string path_match_allow_filters, base::MatcherStringPattern::ID* id) { - auto allow_strings = JSONArrayToVector(path_match_allow_filters); + const auto allow_strings = JSONArrayToVector(path_match_allow_filters); std::vector<base::MatcherStringPattern> allow_patterns; std::vector<const base::MatcherStringPattern*> allow_pointers; allow_patterns.reserve(allow_strings.size()); @@ -106,7 +106,7 @@ void LensUrlMatcher::InitializePathBlockMatcher( std::string path_match_block_filters, base::MatcherStringPattern::ID* id) { - auto block_strings = JSONArrayToVector(path_match_block_filters); + const auto block_strings = JSONArrayToVector(path_match_block_filters); std::vector<base::MatcherStringPattern> block_patterns; std::vector<const base::MatcherStringPattern*> block_pointers; block_patterns.reserve(block_strings.size());
diff --git a/chrome/browser/ui/page_action/page_action_icon_type.h b/chrome/browser/ui/page_action/page_action_icon_type.h index f3a06be6..8027e04e 100644 --- a/chrome/browser/ui/page_action/page_action_icon_type.h +++ b/chrome/browser/ui/page_action/page_action_icon_type.h
@@ -47,7 +47,8 @@ kOptimizationGuide = 31, kCollaborationMessaging = 32, kChangePassword = 33, - kMaxValue = kChangePassword, + kLensOverlayHomework = 34, + kMaxValue = kLensOverlayHomework, }; // LINT.ThenChange(//tools/metrics/histograms/metadata/page/enums.xml:PageActionIconType) @@ -83,6 +84,7 @@ static_assert(static_cast<int>(PageActionIconType::kCollaborationMessaging) == 32); static_assert(static_cast<int>(PageActionIconType::kChangePassword) == 33); +static_assert(static_cast<int>(PageActionIconType::kLensOverlayHomework) == 34); // Returns a bool indicating whether the given page action type has been // migrated to the new framework, which is based on ActionItems instead of
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc index 47e03592..697ab224 100644 --- a/chrome/browser/ui/tab_helpers.cc +++ b/chrome/browser/ui/tab_helpers.cc
@@ -94,7 +94,6 @@ #include "chrome/browser/ui/search_engines/search_engine_tab_helper.h" #include "chrome/browser/ui/tab_contents/core_tab_helper.h" #include "chrome/browser/ui/tab_dialogs.h" -#include "chrome/browser/ui/tab_ui_helper.h" #include "chrome/browser/ui/thumbnails/thumbnail_tab_helper.h" #include "chrome/browser/v8_compile_hints/v8_compile_hints_tab_helper.h" #include "chrome/browser/vr/vr_tab_helper.h" @@ -567,7 +566,6 @@ } #endif HttpErrorTabHelper::CreateForWebContents(web_contents); - TabUIHelper::CreateForWebContents(web_contents); tasks::TaskTabHelper::CreateForWebContents(web_contents); tpcd::metadata::TpcdMetadataDevtoolsObserver::CreateForWebContents( web_contents);
diff --git a/chrome/browser/ui/tab_ui_helper.cc b/chrome/browser/ui/tab_ui_helper.cc index 43c21ca8..701bbe8e 100644 --- a/chrome/browser/ui/tab_ui_helper.cc +++ b/chrome/browser/ui/tab_ui_helper.cc
@@ -24,9 +24,8 @@ } // namespace -TabUIHelper::TabUIHelper(content::WebContents* contents) - : WebContentsObserver(contents), - content::WebContentsUserData<TabUIHelper>(*contents) {} +TabUIHelper::TabUIHelper(tabs::TabInterface& tab_interface) + : ContentsObservingTabFeature(tab_interface) {} TabUIHelper::~TabUIHelper() = default; @@ -78,5 +77,3 @@ was_active_at_least_once_ = true; } } - -WEB_CONTENTS_USER_DATA_KEY_IMPL(TabUIHelper);
diff --git a/chrome/browser/ui/tab_ui_helper.h b/chrome/browser/ui/tab_ui_helper.h index b7db8ed1..b581bd0 100644 --- a/chrome/browser/ui/tab_ui_helper.h +++ b/chrome/browser/ui/tab_ui_helper.h
@@ -7,19 +7,22 @@ #include <string> -#include "content/public/browser/web_contents_observer.h" -#include "content/public/browser/web_contents_user_data.h" -#include "ui/base/models/image_model.h" +#include "chrome/browser/ui/tabs/contents_observing_tab_feature.h" + +namespace tabs { +class TabInterface; +} + +namespace ui { +class ImageModel; +} // namespace ui // TabUIHelper is used by UI code to obtain the title and favicon for a // WebContents. The values returned by TabUIHelper differ from the WebContents // when the WebContents hasn't loaded. -class TabUIHelper : public content::WebContentsObserver, - public content::WebContentsUserData<TabUIHelper> { +class TabUIHelper : public tabs::ContentsObservingTabFeature { public: - TabUIHelper(const TabUIHelper&) = delete; - TabUIHelper& operator=(const TabUIHelper&) = delete; - + explicit TabUIHelper(tabs::TabInterface& tab); ~TabUIHelper() override; // Get the title of the tab. When the associated WebContents' title is empty, @@ -35,7 +38,7 @@ void SetWasActiveAtLeastOnce(); - // content::WebContentsObserver implementation + // tabs::ContentsObservingTabFeature override: void DidStopLoading() override; void OnVisibilityChanged(content::Visibility visiblity) override; @@ -47,14 +50,8 @@ } private: - friend class content::WebContentsUserData<TabUIHelper>; - - explicit TabUIHelper(content::WebContents* contents); - bool was_active_at_least_once_ = false; bool created_by_session_restore_ = false; - - WEB_CONTENTS_USER_DATA_KEY_DECL(); }; #endif // CHROME_BROWSER_UI_TAB_UI_HELPER_H_
diff --git a/chrome/browser/ui/tab_ui_helper_browsertest.cc b/chrome/browser/ui/tab_ui_helper_browsertest.cc index 20c00bc..6f266f8 100644 --- a/chrome/browser/ui/tab_ui_helper_browsertest.cc +++ b/chrome/browser/ui/tab_ui_helper_browsertest.cc
@@ -5,8 +5,10 @@ #include "chrome/browser/ui/tab_ui_helper.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" +#include "components/tabs/public/tab_interface.h" #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test.h" #include "content/public/test/prerender_test_util.h" @@ -50,15 +52,19 @@ IN_PROC_BROWSER_TEST_F(TabUIHelperWithPrerenderingTest, ShouldNotAffectTabUIHelperOnPrerendering) { - GURL initial_url = embedded_test_server()->GetURL("/empty.html"); - GURL prerender_url = + const GURL initial_url = embedded_test_server()->GetURL("/empty.html"); + const GURL prerender_url = embedded_test_server()->GetURL("/favicon/title2_with_favicon.html"); ASSERT_NE(ui_test_utils::NavigateToURL(browser(), initial_url), nullptr); - TabUIHelper* tab_ui_helper = TabUIHelper::FromWebContents(GetWebContents()); - std::u16string primary_title = tab_ui_helper->GetTitle(); - ui::ImageModel primary_favicon = tab_ui_helper->GetFavicon(); - bool primary_should_hide_throbber = tab_ui_helper->ShouldHideThrobber(); + TabUIHelper* const tab_ui_helper = browser() + ->tab_strip_model() + ->GetActiveTab() + ->GetTabFeatures() + ->tab_ui_helper(); + const std::u16string primary_title = tab_ui_helper->GetTitle(); + const ui::ImageModel primary_favicon = tab_ui_helper->GetFavicon(); + const bool primary_should_hide_throbber = tab_ui_helper->ShouldHideThrobber(); // Set |create_by_session_restore_| to true to check if the value is changed // after prerendering. It should not be changed because DidStopLoading is not
diff --git a/chrome/browser/ui/tabs/BUILD.gn b/chrome/browser/ui/tabs/BUILD.gn index de83f6ff..fd43655d 100644 --- a/chrome/browser/ui/tabs/BUILD.gn +++ b/chrome/browser/ui/tabs/BUILD.gn
@@ -342,7 +342,6 @@ "//chrome/browser/ui/commerce", "//chrome/browser/ui/lens", "//chrome/browser/ui/views/intent_picker:intent_picker_page_action", - "//chrome/browser/ui/views/new_tab_footer", "//chrome/browser/ui/views/page_action", "//chrome/browser/ui/views/side_panel", "//chrome/browser/ui/webui:webui_util", @@ -361,7 +360,6 @@ "//components/performance_manager", "//components/permissions", "//components/pref_registry", - "//components/search", "//components/security_interstitials/content:security_interstitial_page", "//components/sessions", "//components/sync_sessions",
diff --git a/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.cc b/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.cc index 4e61f6b1..fa42c2c75c 100644 --- a/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.cc +++ b/chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.cc
@@ -47,8 +47,7 @@ IDS_TAB_CXMENU_PLACEHOLDER_GROUP_TITLE, group.tab_count() - 1); std::u16string short_title; gfx::ElideString( - TabUIHelper::FromWebContents(group.GetFirstTab()->GetContents()) - ->GetTitle(), + group.GetFirstTab()->GetTabFeatures()->tab_ui_helper()->GetTitle(), kContextMenuTabTitleMaxLength, &short_title); return base::ReplaceStringPlaceholders(format_string, short_title, nullptr); }
diff --git a/chrome/browser/ui/tabs/public/tab_features.h b/chrome/browser/ui/tabs/public/tab_features.h index 25ab24b..d36afb6 100644 --- a/chrome/browser/ui/tabs/public/tab_features.h +++ b/chrome/browser/ui/tabs/public/tab_features.h
@@ -26,6 +26,7 @@ class ReadAnythingSidePanelController; class SidePanelRegistry; class TabResourceUsageTabHelper; +class TabUIHelper; class TranslatePageActionController; namespace commerce { @@ -101,10 +102,6 @@ class CollaborationMessagingTabData; } // namespace tab_groups -namespace new_tab_footer { -class NewTabFooterController; -} // namespace new_tab_footer - namespace tabs { class TabAlertController; @@ -231,10 +228,6 @@ return inactive_window_mouse_event_controller_.get(); } - new_tab_footer::NewTabFooterController* new_tab_footer_controller() { - return new_tab_footer_controller_.get(); - } - TabResourceUsageTabHelper* resource_usage_helper() { return resource_usage_helper_.get(); } @@ -243,11 +236,16 @@ return memory_saver_chip_helper_.get(); } + TabUIHelper* tab_ui_helper() { return tab_ui_helper_.get(); } + // Note: Temporary until there is a more uniform way to swap out features for // testing. TabResourceUsageTabHelper* SetResourceUsageHelperForTesting( std::unique_ptr<TabResourceUsageTabHelper> resource_usage_helper); + TabUIHelper* SetTabUIHelperForTesting( + std::unique_ptr<TabUIHelper> tab_ui_helper); + #if BUILDFLAG(ENABLE_GLIC) glic::GlicPageContextEligibilityObserver* glic_page_context_eligibility_observer() { @@ -390,15 +388,14 @@ std::unique_ptr<FromGWSNavigationAndKeepAliveRequestObserver> from_gws_navigation_and_keep_alive_request_observer_; - std::unique_ptr<new_tab_footer::NewTabFooterController> - new_tab_footer_controller_; - std::unique_ptr<TabResourceUsageTabHelper> resource_usage_helper_; std::unique_ptr<MemorySaverChipTabHelper> memory_saver_chip_helper_; std::unique_ptr<TabAlertController> tab_alert_controller_; + std::unique_ptr<TabUIHelper> tab_ui_helper_; + // Must be the last member. base::WeakPtrFactory<TabFeatures> weak_factory_{this}; };
diff --git a/chrome/browser/ui/tabs/tab_features.cc b/chrome/browser/ui/tabs/tab_features.cc index 971fcaba..49c1d30 100644 --- a/chrome/browser/ui/tabs/tab_features.cc +++ b/chrome/browser/ui/tabs/tab_features.cc
@@ -40,6 +40,7 @@ #include "chrome/browser/ui/performance_controls/memory_saver_chip_controller.h" #include "chrome/browser/ui/performance_controls/memory_saver_chip_tab_helper.h" #include "chrome/browser/ui/performance_controls/tab_resource_usage_tab_helper.h" +#include "chrome/browser/ui/tab_ui_helper.h" #include "chrome/browser/ui/tabs/alert/tab_alert_controller.h" #include "chrome/browser/ui/tabs/inactive_window_mouse_event_controller.h" #include "chrome/browser/ui/tabs/public/tab_dialog_manager.h" @@ -55,7 +56,6 @@ #include "chrome/browser/ui/views/commerce/price_insights_page_action_view_controller.h" #include "chrome/browser/ui/views/file_system_access/file_system_access_page_action_controller.h" #include "chrome/browser/ui/views/intent_picker/intent_picker_view_page_action_controller.h" -#include "chrome/browser/ui/views/new_tab_footer/footer_controller.h" #include "chrome/browser/ui/views/page_action/action_ids.h" #include "chrome/browser/ui/views/page_action/page_action_controller.h" #include "chrome/browser/ui/views/page_action/page_action_properties_provider.h" @@ -78,7 +78,6 @@ #include "components/metrics/content/dwa_web_contents_observer.h" #include "components/passage_embeddings/passage_embeddings_features.h" #include "components/permissions/permission_indicators_tab_data.h" -#include "components/search/ntp_features.h" #include "components/tabs/public/tab_interface.h" #include "net/base/features.h" @@ -272,11 +271,6 @@ tab.GetContents()); } #endif // BUILDFLAG(ENABLE_GLIC) - - if (base::FeatureList::IsEnabled(ntp_features::kNtpFooter)) { - new_tab_footer_controller_ = - std::make_unique<new_tab_footer::NewTabFooterController>(&tab); - } } // IsInNormalWindow() end. customize_chrome_side_panel_controller_ = @@ -341,6 +335,8 @@ tab_alert_controller_ = std::make_unique<TabAlertController>(tab.GetContents()); + tab_ui_helper_ = std::make_unique<TabUIHelper>(tab); + task_manager::WebContentsTags::CreateForTabContents(tab.GetContents()); #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \ @@ -358,6 +354,12 @@ return resource_usage_helper_.get(); } +TabUIHelper* TabFeatures::SetTabUIHelperForTesting( + std::unique_ptr<TabUIHelper> tab_ui_helper) { + tab_ui_helper_ = std::move(tab_ui_helper); + return tab_ui_helper_.get(); +} + std::unique_ptr<LensSearchController> TabFeatures::CreateLensController( TabInterface* tab) { return std::make_unique<LensSearchController>(tab);
diff --git a/chrome/browser/ui/tabs/tab_renderer_data.cc b/chrome/browser/ui/tabs/tab_renderer_data.cc index db8f64f..6de221d 100644 --- a/chrome/browser/ui/tabs/tab_renderer_data.cc +++ b/chrome/browser/ui/tabs/tab_renderer_data.cc
@@ -76,13 +76,13 @@ !security_interstitial_tab_helper->IsDisplayingInterstitial() || security_interstitial_tab_helper->ShouldDisplayURL(); TabRendererData data; - TabUIHelper* const tab_ui_helper = TabUIHelper::FromWebContents(contents); + tabs::TabFeatures* const features = tab->GetTabFeatures(); + TabUIHelper* const tab_ui_helper = features->tab_ui_helper(); data.favicon = tab_ui_helper->GetFavicon(); data.title = tab_ui_helper->GetTitle(); // If the tab is in a deferred state, override favicon and title data. - const tabs::TabFeatures* features = tab->GetTabFeatures(); if (features) { const tab_groups::SavedTabGroupWebContentsListener* wc_listener = features->saved_tab_group_web_contents_listener();
diff --git a/chrome/browser/ui/tabs/tab_renderer_data_unittest.cc b/chrome/browser/ui/tabs/tab_renderer_data_unittest.cc index c6c11ed..aca4632 100644 --- a/chrome/browser/ui/tabs/tab_renderer_data_unittest.cc +++ b/chrome/browser/ui/tabs/tab_renderer_data_unittest.cc
@@ -13,11 +13,13 @@ #include "chrome/browser/ui/performance_controls/tab_resource_usage_tab_helper.h" #include "chrome/browser/ui/tab_ui_helper.h" #include "chrome/browser/ui/tabs/alert/tab_alert.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" #include "chrome/browser/ui/tabs/test_util.h" #include "chrome/browser/ui/thumbnails/thumbnail_tab_helper.h" #include "chrome/test/base/testing_profile.h" +#include "components/tabs/public/tab_interface.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/web_contents.h" #include "content/public/test/browser_task_environment.h" @@ -71,12 +73,15 @@ if (pinned) { tab_strip_model_.SetTabPinned(index, true); } + TabInterface* const tab_interface = tab_strip_model_.GetTabAtIndex(index); + tab_interface->GetTabFeatures()->SetTabUIHelperForTesting( + std::make_unique<TabUIHelper>(*tab_interface)); return index; } }; TEST_F(TabRendererDataTest, FromTabInModel) { - int index = AddTab(); + const int index = AddTab(); TabRendererData data = TabRendererData::FromTabInModel(&tab_strip_model_, index); @@ -88,8 +93,7 @@ EXPECT_EQ(data.visible_url, GURL()); EXPECT_EQ(data.last_committed_url, GURL()); EXPECT_EQ(data.title, - TabUIHelper::FromWebContents(data.tab_interface->GetContents()) - ->GetTitle()); + data.tab_interface->GetTabFeatures()->tab_ui_helper()->GetTitle()); EXPECT_FALSE(data.incognito); EXPECT_FALSE(data.blocked); EXPECT_FALSE(data.should_hide_throbber); @@ -175,11 +179,12 @@ TEST_F(TabRendererDataTest, FaviconAndIconFlags) { { // Initial favicon data matches default - int default_index = AddTab(); - content::WebContents* wc = tab_strip_model_.GetWebContentsAt(default_index); + const int default_index = AddTab(); TabRendererData data = TabRendererData::FromTabInModel(&tab_strip_model_, default_index); - EXPECT_EQ(data.favicon, TabUIHelper::FromWebContents(wc)->GetFavicon()); + EXPECT_EQ( + data.favicon, + data.tab_interface->GetTabFeatures()->tab_ui_helper()->GetFavicon()); EXPECT_FALSE(data.should_themify_favicon); EXPECT_FALSE(data.is_monochrome_favicon); EXPECT_TRUE(data.show_icon); @@ -293,9 +298,9 @@ } TEST_F(TabRendererDataTest, ShouldHideThrobber) { - int index = AddTab(); - content::WebContents* wc = tab_strip_model_.GetWebContentsAt(index); - TabUIHelper* helper = TabUIHelper::FromWebContents(wc); + const int index = AddTab(); + TabUIHelper* const helper = + tab_strip_model_.GetTabAtIndex(index)->GetTabFeatures()->tab_ui_helper(); ASSERT_NE(nullptr, helper); helper->set_created_by_session_restore(true); TabRendererData data =
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc index 2d467e9..db4d689 100644 --- a/chrome/browser/ui/tabs/tab_strip_model.cc +++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -790,12 +790,9 @@ if (create_historical_tab) { id = delegate_->CreateHistoricalTab(tab->GetContents()); } - if (tab_detach_reason == tabs::TabInterface::DetachReason::kDelete) { - tab->DestroyTabFeatures(); - } std::unique_ptr<tabs::TabModel> old_tab_model = - RemoveTabFromIndexImpl(index_at_time_of_removal); + RemoveTabFromIndexImpl(index_at_time_of_removal, tab_detach_reason); old_tab_model->OnRemovedFromModel(); return std::make_unique<DetachedTab>( @@ -3840,18 +3837,21 @@ } std::unique_ptr<tabs::TabModel> TabStripModel::RemoveTabFromIndexImpl( - int index) { - tabs::TabInterface* tab = GetTabAtIndex(index); + int index, + tabs::TabInterface::DetachReason tab_detach_reason) { + tabs::TabModel* const tab = GetTabModelAtIndex(index); const std::optional<tab_groups::TabGroupId> old_group = tab->GetGroup(); - - std::optional<int> next_selected_index = - DetermineNewSelectedIndex(GetTabAtIndex(index)); + std::optional<int> next_selected_index = DetermineNewSelectedIndex(tab); const bool removed_tab_is_split = tab->IsSplit(); if (removed_tab_is_split) { RemoveSplitImpl(tab->GetSplit().value(), SplitTabChange::SplitTabRemoveReason::kSplitTabRemoved); } + if (tab_detach_reason == tabs::TabInterface::DetachReason::kDelete) { + tab->DestroyTabFeatures(); + } + // Remove the tab. std::unique_ptr<tabs::TabModel> old_data = base::WrapUnique(static_cast<tabs::TabModel*>(
diff --git a/chrome/browser/ui/tabs/tab_strip_model.h b/chrome/browser/ui/tabs/tab_strip_model.h index 5b5bd54b..e560f05c 100644 --- a/chrome/browser/ui/tabs/tab_strip_model.h +++ b/chrome/browser/ui/tabs/tab_strip_model.h
@@ -1150,7 +1150,9 @@ // Updates the `contents_data_` and sends out observer notifications for // removing an existing tab in the tabstrip. - std::unique_ptr<tabs::TabModel> RemoveTabFromIndexImpl(int index); + std::unique_ptr<tabs::TabModel> RemoveTabFromIndexImpl( + int index, + tabs::TabInterface::DetachReason tab_detach_reason); // Updates the `contents_data_` and sends out observer notifications for // updating the index, pinned state or group property.
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index 1d15a64..ffa5ec18 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -3103,10 +3103,6 @@ return true; } - if (GetLocationBarView()->ActivateFirstInactiveBubbleForAccessibility()) { - return true; - } - // TODO: this fixes https://crbug.com/40668249 and https://crbug.com/40674460, // but a more general solution should be desirable to find any bubbles // anchored in the views hierarchy.
diff --git a/chrome/browser/ui/views/frame/browser_view_browsertest.cc b/chrome/browser/ui/views/frame/browser_view_browsertest.cc index 3c0677b..c3a8f1f 100644 --- a/chrome/browser/ui/views/frame/browser_view_browsertest.cc +++ b/chrome/browser/ui/views/frame/browser_view_browsertest.cc
@@ -380,13 +380,14 @@ // Verifies a tab should show its favicon. IN_PROC_BROWSER_TEST_F(BrowserViewTest, ShowFaviconInTab) { // Opens "chrome://version/" page, which uses default favicon. - GURL version_url(chrome::kChromeUIVersionURL); + const GURL version_url(chrome::kChromeUIVersionURL); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), version_url)); - auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); - auto* helper = TabUIHelper::FromWebContents(contents); + auto* const tab_features = + browser()->tab_strip_model()->GetActiveTab()->GetTabFeatures(); + auto* const helper = tab_features->tab_ui_helper(); ASSERT_TRUE(helper); - auto favicon = helper->GetFavicon(); + const auto favicon = helper->GetFavicon(); ASSERT_FALSE(favicon.IsEmpty()); }
diff --git a/chrome/browser/ui/views/frame/browser_view_interactive_uitest.cc b/chrome/browser/ui/views/frame/browser_view_interactive_uitest.cc index 6bf76e0..2375a3427ef 100644 --- a/chrome/browser/ui/views/frame/browser_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/frame/browser_view_interactive_uitest.cc
@@ -13,6 +13,7 @@ #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/location_bar/location_bar_view.h" +#include "chrome/browser/ui/views/page_action/page_action_view.h" #include "chrome/browser/ui/views/tabs/tab_strip.h" #include "chrome/common/chrome_features.h" #include "chrome/common/pref_names.h" @@ -28,8 +29,11 @@ #include "ui/base/ozone_buildflags.h" #include "ui/base/ui_base_features.h" #include "ui/ozone/public/ozone_platform.h" +#include "ui/views/bubble/bubble_dialog_delegate_view.h" +#include "ui/views/bubble/bubble_dialog_model_host.h" #include "ui/views/buildflags.h" #include "ui/views/test/ax_event_counter.h" +#include "ui/views/test/widget_activation_waiter.h" #include "ui/views/widget/widget_interactive_uitest_utils.h" #if BUILDFLAG(IS_MAC) @@ -344,6 +348,46 @@ } #endif +#if BUILDFLAG(IS_MAC) +// TODO(crbug.com/40568702) NativeWidgetMac::Deactivate is not implemented on +// Mac. +#define MAYBE_FocusInactivePopupForAccessibility \ + DISABLED_FocusInactivePopupForAccessibility +#else +#define MAYBE_FocusInactivePopupForAccessibility \ + FocusInactivePopupForAccessibility +#endif +IN_PROC_BROWSER_TEST_F(BrowserViewTest, + MAYBE_FocusInactivePopupForAccessibility) { + std::unique_ptr<ui::DialogModel> dialog_model = + ui::DialogModel::Builder() + .SetTitle(u"test") + .SetIsAlertDialog() + .AddOkButton(base::DoNothing()) + .Build(); + views::View* anchor = browser_view()->GetLocationBarView(); + auto bubble = std::make_unique<views::BubbleDialogModelHost>( + std::move(dialog_model), anchor, views::BubbleBorder::TOP_RIGHT); + bubble->set_close_on_deactivate(false); + views::Widget* widget = + views::BubbleDialogDelegate::CreateBubble(std::move(bubble)); + + widget->Show(); + views::test::WaitForWidgetActive(widget, true); + + widget->Deactivate(); + widget->ShowInactive(); + EXPECT_TRUE(widget->IsVisible()); + EXPECT_FALSE(widget->IsActive()); + + browser_view()->FocusInactivePopupForAccessibility(); + views::test::WaitForWidgetActive(widget, true); + + // Ensure the bubble's widget refreshed appropriately. + EXPECT_TRUE(widget->IsVisible()); + EXPECT_TRUE(widget->IsActive()); +} + class BrowserViewFullscreenTest : public BrowserViewTest { public: BrowserViewFullscreenTest() {
diff --git a/chrome/browser/ui/views/frame/top_container_loading_bar.cc b/chrome/browser/ui/views/frame/top_container_loading_bar.cc index 276fefb3..146f793 100644 --- a/chrome/browser/ui/views/frame/top_container_loading_bar.cc +++ b/chrome/browser/ui/views/frame/top_container_loading_bar.cc
@@ -8,6 +8,8 @@ #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/color/chrome_color_id.h" #include "chrome/browser/ui/tab_ui_helper.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" +#include "components/tabs/public/tab_interface.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/color/color_provider.h" #include "ui/compositor/layer.h" @@ -129,8 +131,10 @@ return; } + tabs::TabInterface* const tab_interface = + tabs::TabInterface::GetFromContents(web_contents()); TabUIHelper* const tab_ui_helper = - TabUIHelper::FromWebContents(web_contents()); + tab_interface->GetTabFeatures()->tab_ui_helper(); if (tab_ui_helper->ShouldHideThrobber()) { HideImmediately(); return;
diff --git a/chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view.cc b/chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view.cc new file mode 100644 index 0000000..c93890f --- /dev/null +++ b/chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view.cc
@@ -0,0 +1,183 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view.h" + +#include "chrome/browser/lens/region_search/lens_region_search_controller.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/search/search.h" +#include "chrome/browser/ui/browser_element_identifiers.h" +#include "chrome/browser/ui/browser_window/public/browser_window_features.h" +#include "chrome/browser/ui/color/chrome_color_id.h" +#include "chrome/browser/ui/lens/lens_overlay_entry_point_controller.h" +#include "chrome/browser/ui/lens/lens_search_controller.h" +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/location_bar/location_bar_view.h" +#include "chrome/browser/ui/views/omnibox/omnibox_view_views.h" +#include "chrome/browser/ui/views/page_action/page_action_icon_view.h" +#include "chrome/browser/user_education/user_education_service.h" +#include "chrome/grit/branded_strings.h" +#include "components/lens/lens_features.h" +#include "components/lens/lens_metrics.h" +#include "components/omnibox/browser/omnibox_prefs.h" +#include "components/vector_icons/vector_icons.h" +#include "content/public/browser/navigation_controller.h" +#include "content/public/browser/navigation_entry.h" +#include "ui/base/metadata/metadata_impl_macros.h" +#include "ui/views/accessibility/view_accessibility.h" +#include "ui/views/interaction/element_tracker_views.h" +#include "ui/views/view_class_properties.h" + +LensOverlayHomeworkPageActionIconView::LensOverlayHomeworkPageActionIconView( + IconLabelBubbleView::Delegate* parent_delegate, + Delegate* delegate, + BrowserWindowInterface* browser) + : PageActionIconView(nullptr, + 0, + parent_delegate, + delegate, + "LensOverlayHomework"), + browser_(browser) { + CHECK(browser_); + image_container_view()->SetFlipCanvasOnPaintForRTLUI(false); + + SetProperty(views::kElementIdentifierKey, + kLensOverlayHomeworkPageActionIconElementId); + + SetLabel(l10n_util::GetStringUTF16( + IDS_CONTENT_LENS_OVERLAY_HOMEWORK_ENTRYPOINT_LABEL)); + SetUseTonalColorsWhenExpanded(true); + SetBackgroundVisibility(BackgroundVisibility::kWithLabel); +} + +LensOverlayHomeworkPageActionIconView:: + ~LensOverlayHomeworkPageActionIconView() = default; + +void LensOverlayHomeworkPageActionIconView::UpdateImpl() { + const bool should_show = ShouldShow(); + + if (should_show) { + // UpdateImpl() can be called multiple times, so make sure we don't call + // ShowCallToAction() more than once while the chip is showing. + if (!scoped_window_call_to_action_ptr_) { + scoped_window_call_to_action_ptr_ = browser_->ShowCallToAction(); + } + } else { + scoped_window_call_to_action_ptr_.reset(); + } + + SetVisible(should_show); + ResetSlideAnimation(true); +} + +bool LensOverlayHomeworkPageActionIconView::ShouldShow() { + if (!lens::features::IsLensOverlayEduActionChipEnabled()) { + return false; + } + + if (browser_->GetProfile()->IsOffTheRecord()) { + return false; + } + + if (!browser_->GetProfile()->GetPrefs()->GetBoolean( + omnibox::kShowGoogleLensShortcut)) { + return false; + } + + // Hide the homework chip if the broader lens feature is disabled. + const auto* controller = + browser_->GetFeatures().lens_overlay_entry_point_controller(); + if (!controller || !controller->AreVisible()) { + return false; + } + + auto* web_contents = GetWebContents(); + if (!web_contents) { + return false; + } + + // Don't show the chip if the location bar isn't visible yet. + View* location_bar_view = + views::ElementTrackerViews::GetInstance()->GetUniqueView( + kLocationBarElementId, + views::ElementTrackerViews::GetContextForView(this)); + if (!location_bar_view) { + return false; + } + + // Hide the homework chip if the location bar is focused. + const views::FocusManager* const focus_manager = GetFocusManager(); + if (!focus_manager || + location_bar_view->Contains(focus_manager->GetFocusedView())) { + return false; + } + + // Treat the chip as a window-level call to action UI; only one such UI is + // allowed to show at a time. Check if scoped_window_call_to_action_ptr_ is + // already set (we are already showing the chip) before checking + // CanShowCallToAction(). + if (!scoped_window_call_to_action_ptr_ && !browser_->CanShowCallToAction()) { + return false; + } + + // Use the committed entry (or the visible entry, if the committed entry is + // the initial NavigationEntry) so the bookmarks bar disappears at the same + // time the page does. + CHECK(web_contents); + content::NavigationEntry* entry = + web_contents->GetController().GetLastCommittedEntry(); + if (entry->IsInitialEntry()) { + entry = web_contents->GetController().GetVisibleEntry(); + } + const GURL& url = entry->GetURL(); + + return controller->IsUrlEduEligible(url); +} + +void LensOverlayHomeworkPageActionIconView::OnExecuting( + PageActionIconView::ExecuteSource source) { + // If the user entered Lens through the keyboard, we want to open Lens Web + // in a new tab. + // TODO(crbug.com/404640455): Clean up after a11y updates. + if (source == PageActionIconView::EXECUTE_SOURCE_KEYBOARD) { + browser_->GetFeatures().lens_region_search_controller()->Start( + GetWebContents(), /*use_fullscreen_capture=*/true, + /*is_google_default_search_provider=*/true, + lens::AmbientSearchEntryPoint:: + LENS_OVERLAY_LOCATION_BAR_ACCESSIBILITY_FALLBACK); + return; + } + + LensSearchController* const controller = + LensSearchController::FromTabWebContents(GetWebContents()); + CHECK(controller); + + controller->OpenLensOverlay( + lens::LensOverlayInvocationSource::kHomeworkActionChip); + UserEducationService::MaybeNotifyNewBadgeFeatureUsed( + GetWebContents()->GetBrowserContext(), lens::features::kLensOverlay); +} + +views::BubbleDialogDelegate* LensOverlayHomeworkPageActionIconView::GetBubble() + const { + return nullptr; +} + +const gfx::VectorIcon& LensOverlayHomeworkPageActionIconView::GetVectorIcon() + const { +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) + return vector_icons::kGoogleLensMonochromeLogoIcon; +#else + return vector_icons::kSearchChromeRefreshIcon; +#endif +} + +void LensOverlayHomeworkPageActionIconView:: + ExecuteWithKeyboardSourceForTesting() { + CHECK(GetVisible()); + OnExecuting(EXECUTE_SOURCE_KEYBOARD); +} + +BEGIN_METADATA(LensOverlayHomeworkPageActionIconView) +END_METADATA
diff --git a/chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view.h b/chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view.h new file mode 100644 index 0000000..e8afadc --- /dev/null +++ b/chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view.h
@@ -0,0 +1,46 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_LENS_OVERLAY_HOMEWORK_PAGE_ACTION_ICON_VIEW_H_ +#define CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_LENS_OVERLAY_HOMEWORK_PAGE_ACTION_ICON_VIEW_H_ + +#include "base/memory/raw_ptr.h" +#include "chrome/browser/lens/region_search/lens_region_search_controller.h" +#include "chrome/browser/ui/views/page_action/page_action_icon_view.h" +#include "ui/base/metadata/metadata_header_macros.h" + +class BrowserWindowInterface; +class ScopedWindowCallToAction; + +class LensOverlayHomeworkPageActionIconView : public PageActionIconView { + METADATA_HEADER(LensOverlayHomeworkPageActionIconView, PageActionIconView) + + public: + LensOverlayHomeworkPageActionIconView( + IconLabelBubbleView::Delegate* parent_delegate, + Delegate* delegate, + BrowserWindowInterface* browser); + ~LensOverlayHomeworkPageActionIconView() override; + + // PageActionIconView: + views::BubbleDialogDelegate* GetBubble() const override; + void OnExecuting(PageActionIconView::ExecuteSource execute_source) override; + const gfx::VectorIcon& GetVectorIcon() const override; + + void ExecuteWithKeyboardSourceForTesting(); + + protected: + // PageActionIconView: + void UpdateImpl() override; + + private: + bool ShouldShow(); + void ShowCallToAction(); + + const raw_ptr<BrowserWindowInterface> browser_; + + std::unique_ptr<ScopedWindowCallToAction> scoped_window_call_to_action_ptr_; +}; + +#endif // CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_LENS_OVERLAY_HOMEWORK_PAGE_ACTION_ICON_VIEW_H_
diff --git a/chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view_interactive_uitest.cc b/chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view_interactive_uitest.cc new file mode 100644 index 0000000..dfc9428c --- /dev/null +++ b/chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view_interactive_uitest.cc
@@ -0,0 +1,274 @@ +// 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. + +// TODO(crbug.com/376283383): This file should be moved closer to the +// `LensOverlayEntryPointController` once the page actions migration is +// complete. + +#include <memory> + +#include "base/functional/callback_forward.h" +#include "base/test/run_until.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/actions/chrome_action_id.h" +#include "chrome/browser/ui/browser_element_identifiers.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" +#include "chrome/browser/ui/ui_features.h" +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view.h" +#include "chrome/browser/ui/views/location_bar/location_bar_view.h" +#include "chrome/browser/ui/views/toolbar/toolbar_view.h" +#include "chrome/common/webui_url_constants.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/lens/lens_features.h" +#include "components/omnibox/browser/omnibox_prefs.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "ui/events/test/test_event.h" +#include "ui/views/interaction/element_tracker_views.h" +#include "ui/views/test/widget_test.h" +#include "url/url_constants.h" + +using ::testing::MatchesRegex; + +namespace { + +constexpr char kDocumentWithNamedElement[] = "/select.html"; +constexpr char kDocument2[] = "/title1.html"; + +class ViewVisibilityWaiter : public views::ViewObserver { + public: + explicit ViewVisibilityWaiter(views::View* observed_view, + bool expected_visible) + : view_(observed_view), expected_visible_(expected_visible) { + observation_.Observe(view_.get()); + } + ViewVisibilityWaiter(const ViewVisibilityWaiter&) = delete; + ViewVisibilityWaiter& operator=(const ViewVisibilityWaiter&) = delete; + + ~ViewVisibilityWaiter() override = default; + + // Wait for changes to occur, or return immediately if view already has + // expected visibility. + void Wait() { + if (expected_visible_ != view_->GetVisible()) { + run_loop_.Run(); + } + } + + private: + // views::ViewObserver: + void OnViewVisibilityChanged(views::View* observed_view, + views::View* starting_view) override { + if (expected_visible_ == observed_view->GetVisible()) { + run_loop_.Quit(); + } + } + + raw_ptr<views::View> view_; + const bool expected_visible_; + base::RunLoop run_loop_; + base::ScopedObservation<views::View, views::ViewObserver> observation_{this}; +}; + +class LensOverlayHomeworkPageActionIconViewTestBase + : public InProcessBrowserTest { + public: + LensOverlayHomeworkPageActionIconViewTestBase() = default; + LensOverlayHomeworkPageActionIconViewTestBase( + const LensOverlayHomeworkPageActionIconViewTestBase&) = delete; + LensOverlayHomeworkPageActionIconViewTestBase& operator=( + const LensOverlayHomeworkPageActionIconViewTestBase&) = delete; + ~LensOverlayHomeworkPageActionIconViewTestBase() override = default; + + void SetUp() override { + ASSERT_TRUE(embedded_test_server()->InitializeAndListen()); + InProcessBrowserTest::SetUp(); + } + + void SetUpOnMainThread() override { + InProcessBrowserTest::SetUpOnMainThread(); + embedded_test_server()->StartAcceptingConnections(); + } + + void TearDownOnMainThread() override { + EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete()); + InProcessBrowserTest::TearDownOnMainThread(); + } + + LensOverlayHomeworkPageActionIconView* lens_overlay_homework_icon_view() { + views::View* const icon_view = + views::ElementTrackerViews::GetInstance()->GetFirstMatchingView( + kLensOverlayHomeworkPageActionIconElementId, + browser()->window()->GetElementContext()); + return icon_view + ? views::AsViewClass<LensOverlayHomeworkPageActionIconView>( + icon_view) + : nullptr; + } + + LocationBarView* location_bar_view() { + views::View* const location_bar_view = + views::ElementTrackerViews::GetInstance()->GetUniqueView( + kLocationBarElementId, browser()->window()->GetElementContext()); + return location_bar_view + ? views::AsViewClass<LocationBarView>(location_bar_view) + : nullptr; + } + + protected: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +class LensOverlayHomeworkPageActionIconViewTest + : public LensOverlayHomeworkPageActionIconViewTestBase { + public: + LensOverlayHomeworkPageActionIconViewTest() { + scoped_feature_list_.InitWithFeaturesAndParameters( + {base::test::FeatureRefAndParams(lens::features::kLensOverlay, {}), + base::test::FeatureRefAndParams( + lens::features::kLensOverlayEduActionChip, + {{"url-allow-filters", "[\"*\"]"}, + {"url-path-match-allow-filters", "[\"select\"]"}})}, + {}); + } +}; + +IN_PROC_BROWSER_TEST_F(LensOverlayHomeworkPageActionIconViewTest, + ShowsOnMatchingPage) { + // Navigate to a matching page. + const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(url))); + + LensOverlayHomeworkPageActionIconView* icon_view = + lens_overlay_homework_icon_view(); + views::FocusManager* focus_manager = icon_view->GetFocusManager(); + focus_manager->ClearFocus(); + EXPECT_FALSE(focus_manager->GetFocusedView()); + EXPECT_TRUE(icon_view->GetVisible()); + + // Focus in the location bar should hide the icon. + location_bar_view()->FocusLocation(false); + ViewVisibilityWaiter(icon_view, false).Wait(); + + EXPECT_TRUE(focus_manager->GetFocusedView()); + EXPECT_FALSE(icon_view->GetVisible()); +} + +IN_PROC_BROWSER_TEST_F(LensOverlayHomeworkPageActionIconViewTest, + HidesOnNonMatchingPage) { + // Navigate to a non-matching page. + const GURL url = embedded_test_server()->GetURL(kDocument2); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(url))); + + LensOverlayHomeworkPageActionIconView* icon_view = + lens_overlay_homework_icon_view(); + views::FocusManager* focus_manager = icon_view->GetFocusManager(); + focus_manager->ClearFocus(); + EXPECT_FALSE(focus_manager->GetFocusedView()); + EXPECT_FALSE(icon_view->GetVisible()); + + // Focus in the location bar should not show the icon. + location_bar_view()->FocusLocation(false); + ViewVisibilityWaiter(icon_view, false).Wait(); + + EXPECT_TRUE(focus_manager->GetFocusedView()); + EXPECT_FALSE(icon_view->GetVisible()); +} + +#if BUILDFLAG(IS_WIN) +#define MAYBE_OpensNewTabWhenEnteredThroughKeyboard \ + DISABLED_OpensNewTabWhenEnteredThroughKeyboard +#else +#define MAYBE_OpensNewTabWhenEnteredThroughKeyboard \ + OpensNewTabWhenEnteredThroughKeyboard +#endif +// Flaky failures on Windows; see https://crbug.com/419308044. +IN_PROC_BROWSER_TEST_F(LensOverlayHomeworkPageActionIconViewTest, + MAYBE_OpensNewTabWhenEnteredThroughKeyboard) { + const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); + // Navigate to a matching page. + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(url))); + // We need to wait for paint in order to take a screenshot of the page. + ASSERT_TRUE(base::test::RunUntil([&]() { + return browser() + ->tab_strip_model() + ->GetActiveTab() + ->GetContents() + ->CompletedFirstVisuallyNonEmptyPaint(); + })); + + LensOverlayHomeworkPageActionIconView* icon_view = + lens_overlay_homework_icon_view(); + views::FocusManager* focus_manager = icon_view->GetFocusManager(); + focus_manager->ClearFocus(); + EXPECT_FALSE(focus_manager->GetFocusedView()); + EXPECT_TRUE(icon_view->GetVisible()); + + // Executing the lens overlay icon view with keyboard source should open a new + // tab. + ui_test_utils::TabAddedWaiter tab_add(browser()); + lens_overlay_homework_icon_view()->ExecuteWithKeyboardSourceForTesting(); + auto* new_tab_contents = tab_add.Wait(); + + EXPECT_TRUE(new_tab_contents); + content::WaitForLoadStop(new_tab_contents); + EXPECT_THAT(new_tab_contents->GetLastCommittedURL().query(), + MatchesRegex("ep=crmntob&re=df&s=4&st=\\d+&lm=.+")); +} + +IN_PROC_BROWSER_TEST_F(LensOverlayHomeworkPageActionIconViewTest, + DoesNotShowWhenSettingDisabled) { + // Disable the setting. + browser()->profile()->GetPrefs()->SetBoolean(omnibox::kShowGoogleLensShortcut, + false); + const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); + // Navigate to a matching page. + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(url))); + + LensOverlayHomeworkPageActionIconView* icon_view = + lens_overlay_homework_icon_view(); + views::FocusManager* focus_manager = icon_view->GetFocusManager(); + focus_manager->ClearFocus(); + EXPECT_FALSE(focus_manager->GetFocusedView()); + EXPECT_FALSE(icon_view->GetVisible()); + + // Focus in the location bar should not show the icon. + location_bar_view()->FocusLocation(false); + ViewVisibilityWaiter(icon_view, false).Wait(); + + EXPECT_TRUE(focus_manager->GetFocusedView()); + EXPECT_FALSE(icon_view->GetVisible()); +} + +IN_PROC_BROWSER_TEST_F(LensOverlayHomeworkPageActionIconViewTest, + RespectsShowShortcutPreference) { + // Ensure the shortcut pref starts enabled. + browser()->profile()->GetPrefs()->SetBoolean(omnibox::kShowGoogleLensShortcut, + true); + + const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); + // Navigate to a matching page. + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(url))); + + views::View* icon_view = lens_overlay_homework_icon_view(); + views::FocusManager* focus_manager = icon_view->GetFocusManager(); + focus_manager->ClearFocus(); + EXPECT_FALSE(focus_manager->GetFocusedView()); + EXPECT_TRUE(icon_view->GetVisible()); + + // Disable the preference, the entrypoint should immediately disappear. + browser()->profile()->GetPrefs()->SetBoolean(omnibox::kShowGoogleLensShortcut, + false); + EXPECT_FALSE(icon_view->GetVisible()); + + // Re-enable the preference, the entrypoint should immediately become + // visible. + browser()->profile()->GetPrefs()->SetBoolean(omnibox::kShowGoogleLensShortcut, + true); + EXPECT_TRUE(icon_view->GetVisible()); +} + +} // namespace
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc index baf90f1..55e2295 100644 --- a/chrome/browser/ui/views/location_bar/location_bar_view.cc +++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -40,6 +40,7 @@ #include "chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_actions.h" +#include "chrome/browser/ui/browser_element_identifiers.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" @@ -166,6 +167,7 @@ #include "ui/views/style/typography.h" #include "ui/views/style/typography_provider.h" #include "ui/views/view.h" +#include "ui/views/view_class_properties.h" #include "ui/views/view_utils.h" #include "ui/views/widget/widget.h" @@ -233,6 +235,7 @@ &LocationBarView::OnAppShimChanged, base::Unretained(this))); #endif GetViewAccessibility().SetRole(ax::mojom::Role::kGroup); + SetProperty(views::kElementIdentifierKey, kLocationBarElementId); } LocationBarView::~LocationBarView() = default; @@ -436,6 +439,14 @@ } } + if (browser_ && lens::features::IsLensOverlayEduActionChipEnabled()) { + // Position in the leading position, like the expanding entrypoint for + // kLensOverlay above. While both chips may be enabled, they will not appear + // at the same time due to different focus behavior. + params.types_enabled.insert(params.types_enabled.begin(), + PageActionIconType::kLensOverlayHomework); + } + if (browser_ && tab_groups::SavedTabGroupUtils::SupportsSharedTabGroups()) { params.types_enabled.push_back(PageActionIconType::kCollaborationMessaging); } @@ -1019,11 +1030,6 @@ omnibox_view_->ResetTabState(contents); } -bool LocationBarView::ActivateFirstInactiveBubbleForAccessibility() { - return page_action_icon_controller_ - ->ActivateFirstInactiveBubbleForAccessibility(); -} - ChipController* LocationBarView::GetChipController() { if (base::FeatureList::IsEnabled( content_settings::features::kLeftHandSideActivityIndicators)) {
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.h b/chrome/browser/ui/views/location_bar/location_bar_view.h index 9d98ffa..87ebe76 100644 --- a/chrome/browser/ui/views/location_bar/location_bar_view.h +++ b/chrome/browser/ui/views/location_bar/location_bar_view.h
@@ -196,10 +196,6 @@ // Clears the location bar's state for |contents|. void ResetTabState(content::WebContents* contents); - // Activates the first visible but inactive PageActionIconView for - // accessibility. - bool ActivateFirstInactiveBubbleForAccessibility(); - // Controls the chip in the LocationBarView. ChipController* GetChipController();
diff --git a/chrome/browser/ui/views/location_bar/location_icon_view_interactive_uitest.cc b/chrome/browser/ui/views/location_bar/location_icon_view_interactive_uitest.cc index 9b54387..f09e7af 100644 --- a/chrome/browser/ui/views/location_bar/location_icon_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/location_bar/location_icon_view_interactive_uitest.cc
@@ -63,59 +63,4 @@ EXPECT_EQ(PageInfoBubbleView::BUBBLE_NONE, PageInfoBubbleView::GetShownBubbleType()); } - -#if BUILDFLAG(IS_MAC) -// TODO(jongkwon.lee): https://crbug.com/825834 NativeWidgetMac::Deactivate is -// not implemented on Mac. -#define MAYBE_ActivateFirstInactiveBubbleForAccessibility \ - DISABLED_ActivateFirstInactiveBubbleForAccessibility -#else -#define MAYBE_ActivateFirstInactiveBubbleForAccessibility \ - ActivateFirstInactiveBubbleForAccessibility -#endif -IN_PROC_BROWSER_TEST_F(LocationIconViewTest, - MAYBE_ActivateFirstInactiveBubbleForAccessibility) { - BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser()); - LocationBarView* location_bar_view = browser_view->GetLocationBarView(); - EXPECT_FALSE( - location_bar_view->ActivateFirstInactiveBubbleForAccessibility()); - - content::WebContents* web_contents = - browser()->tab_strip_model()->GetActiveWebContents(); - browser_view->ShowTranslateBubble( - web_contents, translate::TRANSLATE_STEP_AFTER_TRANSLATE, "en", "fr", - translate::TranslateErrors::NONE, true); - - views::View* icon_view; - if (IsPageActionMigrated(PageActionIconType::kTranslate)) { - icon_view = browser_view->toolbar_button_provider()->GetPageActionView( - kActionShowTranslate); - } else { - icon_view = browser_view->toolbar_button_provider()->GetPageActionIconView( - PageActionIconType::kTranslate); - } - - ASSERT_TRUE(icon_view); - EXPECT_TRUE(icon_view->GetVisible()); - - // Ensure the bubble's widget is visible, but inactive. Active widgets are - // focused by accessibility, so not of concern. - views::Widget* widget = browser() - ->GetFeatures() - .translate_bubble_controller() - ->GetTranslateBubble() - ->GetWidget(); - widget->Deactivate(); - widget->ShowInactive(); - EXPECT_TRUE(widget->IsVisible()); - EXPECT_FALSE(widget->IsActive()); - - EXPECT_TRUE(location_bar_view->ActivateFirstInactiveBubbleForAccessibility()); - - // Ensure the bubble's widget refreshed appropriately. - EXPECT_TRUE(icon_view->GetVisible()); - EXPECT_TRUE(widget->IsVisible()); - EXPECT_TRUE(widget->IsActive()); -} - } // namespace
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_coordinator_unittest.cc b/chrome/browser/ui/views/media_router/cast_dialog_coordinator_browsertest.cc similarity index 87% rename from chrome/browser/ui/views/media_router/cast_dialog_coordinator_unittest.cc rename to chrome/browser/ui/views/media_router/cast_dialog_coordinator_browsertest.cc index 72dbffd..eebf2e8e 100644 --- a/chrome/browser/ui/views/media_router/cast_dialog_coordinator_unittest.cc +++ b/chrome/browser/ui/views/media_router/cast_dialog_coordinator_browsertest.cc
@@ -8,15 +8,17 @@ #include "base/test/scoped_feature_list.h" #include "base/time/time.h" +#include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/media_router/cast_dialog_controller.h" #include "chrome/browser/ui/media_router/media_cast_mode.h" #include "chrome/browser/ui/media_router/media_route_starter.h" #include "chrome/browser/ui/views/frame/browser_view.h" -#include "chrome/browser/ui/views/frame/test_with_browser_view.h" #include "chrome/browser/ui/views/frame/top_container_view.h" #include "chrome/browser/ui/views/media_router/cast_dialog_view.h" #include "chrome/browser/ui/views/toolbar/toolbar_view.h" +#include "chrome/test/base/in_process_browser_test.h" #include "content/public/browser/web_contents.h" +#include "content/public/test/browser_test.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/views/test/widget_test.h" @@ -45,9 +47,9 @@ MOCK_METHOD(void, RegisterDestructor, (base::OnceClosure)); }; -class CastDialogCoordinatorTest : public TestWithBrowserView { +class CastDialogCoordinatorTest : public InProcessBrowserTest { public: - void SetUp() override { TestWithBrowserView::SetUp(); } + void SetUp() override { InProcessBrowserTest::SetUp(); } NiceMock<MockCastDialogController> controller_; CastDialogCoordinator cast_dialog_coordinator_; @@ -56,10 +58,10 @@ // Tests show and hide for ShowDialogCenteredForBrowserWindow. Defers // ShowDialogWithToolbarAction to Media Router tests (already covered) since // additional Media Router services setup is required. -TEST_F(CastDialogCoordinatorTest, ShowAndHideDialog) { +IN_PROC_BROWSER_TEST_F(CastDialogCoordinatorTest, ShowAndHideDialog) { EXPECT_CALL(controller_, AddObserver(_)); cast_dialog_coordinator_.ShowDialogCenteredForBrowserWindow( - &controller_, browser_view()->browser(), base::Time::Now(), + &controller_, browser(), base::Time::Now(), MediaRouterDialogActivationLocation::PAGE); EXPECT_TRUE(cast_dialog_coordinator_.IsShowing()); EXPECT_NE(nullptr, cast_dialog_coordinator_.GetCastDialogWidget());
diff --git a/chrome/browser/ui/views/new_tab_footer/BUILD.gn b/chrome/browser/ui/views/new_tab_footer/BUILD.gn index 7cbb750..4e7ffbc1 100644 --- a/chrome/browser/ui/views/new_tab_footer/BUILD.gn +++ b/chrome/browser/ui/views/new_tab_footer/BUILD.gn
@@ -15,6 +15,7 @@ "//base:base", "//chrome/browser/profiles:profile", "//chrome/browser/resources/new_tab_footer:resources_grit", + "//chrome/browser/search", "//chrome/browser/ui:ui_features", "//chrome/browser/ui/browser_window", "//chrome/browser/ui/webui:webui_util", @@ -27,15 +28,19 @@ "//ui/views", "//ui/views/controls/webview", ] + public_deps = [ "//chrome/browser:browser_public_dependencies" ] } -source_set("unit_tests") { +source_set("browser_tests") { testonly = true - sources = [ "footer_controller_unittest.cc" ] + defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] + sources = [ "footer_controller_browsertest.cc" ] deps = [ ":new_tab_footer", "//base/test:test_support", + "//chrome/browser/search_engines", "//chrome/browser/ui:ui_features", + "//chrome/browser/ui/browser_window", "//chrome/test:test_support", ] }
diff --git a/chrome/browser/ui/views/new_tab_footer/footer_controller.cc b/chrome/browser/ui/views/new_tab_footer/footer_controller.cc index 111c192..b6b92d3b 100644 --- a/chrome/browser/ui/views/new_tab_footer/footer_controller.cc +++ b/chrome/browser/ui/views/new_tab_footer/footer_controller.cc
@@ -4,82 +4,111 @@ #include "chrome/browser/ui/views/new_tab_footer/footer_controller.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/enterprise/util/managed_browser_utils.h" +#include "chrome/browser/search/search.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/ui_features.h" +#include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/new_tab_footer/footer_web_view.h" #include "chrome/browser/ui/webui/new_tab_footer/new_tab_footer_helper.h" +#include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h" +#include "chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.h" +#include "chrome/browser/ui/webui/ntp/new_tab_ui.h" #include "chrome/common/pref_names.h" #include "components/prefs/pref_service.h" +#include "components/tabs/public/tab_interface.h" #include "content/public/browser/navigation_entry.h" namespace new_tab_footer { -// TODO (crbug.com/415116344) add unittest coverage. -NewTabFooterController::NewTabFooterController(tabs::TabInterface* tab) - : tab_(tab) { - // TODO(crbug.com/4438803): Support SideBySide. - if (features::IsNtpFooterEnabledWithoutSideBySide()) { - footer_web_view_ = tab_->GetBrowserWindowInterface()->NewTabFooterWebView(); +namespace { +bool IsNtp(const GURL& url, + content::WebContents* web_contents, + Profile* profile) { + content::NavigationEntry* entry = + web_contents->GetController().GetLastCommittedEntry(); + if (entry->IsInitialEntry()) { + entry = web_contents->GetController().GetVisibleEntry(); } + return NewTabUI::IsNewTab(url) || NewTabPageUI::IsNewTabPageOrigin(url) || + NewTabPageThirdPartyUI::IsNewTabPageOrigin(url) || + search::NavEntryIsInstantNTP(web_contents, entry) || + ntp_footer::IsExtensionNtp(url, profile); +} +} // namespace - profile_ = tab_->GetBrowserWindowInterface()->GetProfile(); +NewTabFooterController::NewTabFooterController(BrowserWindowInterface* browser, + NewTabFooterWebView* footer) + : browser_(browser), footer_(footer) { + profile_ = browser_->GetProfile(); pref_change_registrar_.Init(profile_->GetPrefs()); pref_change_registrar_.Add( prefs::kNtpFooterVisible, base::BindRepeating(&NewTabFooterController::UpdateFooterVisibility, weak_factory_.GetWeakPtr())); - - content::WebContentsObserver::Observe(tab_->GetContents()); - tab_did_activate_callback_subscription_ = tab_->RegisterDidActivate( - base::BindRepeating(&NewTabFooterController::TabForegrounded, + pref_change_registrar_.Add( + prefs::kNTPFooterExtensionAttributionEnabled, + base::BindRepeating(&NewTabFooterController::UpdateFooterVisibility, weak_factory_.GetWeakPtr())); +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) + local_state_pref_change_registrar_.Init(g_browser_process->local_state()); + local_state_pref_change_registrar_.Add( + prefs::kNTPFooterManagementNoticeEnabled, + base::BindRepeating(&NewTabFooterController::UpdateFooterVisibility, + weak_factory_.GetWeakPtr())); +#endif + + tab_activation_subscription_subscription_ = + browser_->RegisterActiveTabDidChange( + base::BindRepeating(&NewTabFooterController::OnActiveTabChanged, + weak_factory_.GetWeakPtr())); } NewTabFooterController::~NewTabFooterController() = default; +void NewTabFooterController::TearDown() { + pref_change_registrar_.Reset(); + tab_activation_subscription_subscription_ = base::CallbackListSubscription(); + footer_ = nullptr; + browser_ = nullptr; + profile_ = nullptr; +} + void NewTabFooterController::DidFinishNavigation( content::NavigationHandle* navigation_handle) { - if (tab_->IsActivated()) { UpdateFooterVisibility(); - } } void NewTabFooterController::UpdateFooterVisibility() { - if (!footer_web_view_) { + // TODO(crbug.com/4438803): Support SideBySide. Currently, when it is enabled, + // footer_ will have no value. + if (!footer_) { return; } - GURL url = - tab_->GetContents()->GetController().GetLastCommittedEntry()->GetURL(); + GURL url = web_contents()->GetController().GetLastCommittedEntry()->GetURL(); if (url.is_empty()) { - url = tab_->GetContents()->GetController().GetVisibleEntry()->GetURL(); + url = web_contents()->GetController().GetVisibleEntry()->GetURL(); } - bool is_footer_visible_pref = - profile_->GetPrefs()->GetBoolean(prefs::kNtpFooterVisible); - bool can_show_footer = - is_footer_visible_pref && ntp_footer::IsExtensionNtp(url, profile_); - if (can_show_footer) { - ShowUI(); + bool managed_ntp = + IsNtp(url, web_contents(), profile_) && + enterprise_util::CanShowEnterpriseBadgingForNTPFooter(profile_); + bool show = managed_ntp || + (profile_->GetPrefs()->GetBoolean(prefs::kNtpFooterVisible) && + ntp_footer::CanShowExtensionFooter(url, profile_)); + if (show) { + footer_->ShowUI(); } else { - CloseUI(); + footer_->CloseUI(); } } -void NewTabFooterController::TabForegrounded(tabs::TabInterface* tab) { +void NewTabFooterController::OnActiveTabChanged( + BrowserWindowInterface* browser) { + Observe(browser->GetActiveTabInterface()->GetContents()); UpdateFooterVisibility(); } -void NewTabFooterController::ShowUI() { - if (footer_web_view_) { - footer_web_view_->ShowUI(); - } -} - -void NewTabFooterController::CloseUI() { - if (footer_web_view_) { - footer_web_view_->CloseUI(); - } -} - } // namespace new_tab_footer
diff --git a/chrome/browser/ui/views/new_tab_footer/footer_controller.h b/chrome/browser/ui/views/new_tab_footer/footer_controller.h index ce32123..2f650be3 100644 --- a/chrome/browser/ui/views/new_tab_footer/footer_controller.h +++ b/chrome/browser/ui/views/new_tab_footer/footer_controller.h
@@ -7,34 +7,39 @@ #include "chrome/browser/ui/views/new_tab_footer/footer_web_view.h" #include "components/prefs/pref_change_registrar.h" -#include "components/tabs/public/tab_interface.h" #include "content/public/browser/web_contents_observer.h" +class BrowserWindowInterface; + namespace new_tab_footer { // Class used to manage the state of the new tab footer. class NewTabFooterController : public content::WebContentsObserver { public: - explicit NewTabFooterController(tabs::TabInterface* tab); + explicit NewTabFooterController(BrowserWindowInterface* browser, + NewTabFooterWebView* footer); NewTabFooterController(const NewTabFooterController&) = delete; NewTabFooterController& operator=(const NewTabFooterController&) = delete; ~NewTabFooterController() override; + void TearDown(); + private: // content::WebContentsObserver: void DidFinishNavigation( content::NavigationHandle* navigation_handle) override; void UpdateFooterVisibility(); - // Called when the associated tab enters the foreground. - void TabForegrounded(tabs::TabInterface* tab); + // Callback for active tab changes from BrowserWindowInterface. + void OnActiveTabChanged(BrowserWindowInterface* browser); void ShowUI(); void CloseUI(); - const raw_ptr<tabs::TabInterface> tab_; - raw_ptr<new_tab_footer::NewTabFooterWebView> footer_web_view_; - base::CallbackListSubscription tab_did_activate_callback_subscription_; + raw_ptr<BrowserWindowInterface> browser_; + raw_ptr<new_tab_footer::NewTabFooterWebView> footer_; + base::CallbackListSubscription tab_activation_subscription_subscription_; PrefChangeRegistrar pref_change_registrar_; + PrefChangeRegistrar local_state_pref_change_registrar_; raw_ptr<Profile> profile_; base::WeakPtrFactory<NewTabFooterController> weak_factory_{this};
diff --git a/chrome/browser/ui/views/new_tab_footer/footer_controller_browsertest.cc b/chrome/browser/ui/views/new_tab_footer/footer_controller_browsertest.cc new file mode 100644 index 0000000..c0c4aeb1 --- /dev/null +++ b/chrome/browser/ui/views/new_tab_footer/footer_controller_browsertest.cc
@@ -0,0 +1,259 @@ +// 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 <memory> + +#include "base/test/scoped_feature_list.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/enterprise/browser_management/management_service_factory.h" +#include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_window/public/browser_window_features.h" +#include "chrome/browser/ui/ui_features.h" +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/new_tab_footer/footer_web_view.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/base/chrome_test_utils.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/policy/core/common/management/scoped_management_service_override_for_testing.h" +#include "components/prefs/pref_service.h" +#include "components/search/ntp_features.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "extensions/common/extension.h" +#include "extensions/test/test_extension_dir.h" +#include "net/base/url_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/window_open_disposition.h" +#include "url/gurl.h" + +namespace { +const char kNonNtpUrl[] = "https://www.google.com"; +} + +class FooterControllerExtensionTestBase + : public extensions::ExtensionBrowserTest { + public: + void SetUpOnMainThread() override { + extensions::ExtensionBrowserTest::SetUpOnMainThread(); + profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, true); + } + + scoped_refptr<const extensions::Extension> LoadNtpExtension() { + extensions::TestExtensionDir extension_dir; + constexpr char kManifest[] = R"( + { + "chrome_url_overrides": { + "newtab": "ext.html" + }, + "name": "Extension-overridden NTP", + "manifest_version": 3, + "version": "0.1" + })"; + extension_dir.WriteManifest(kManifest); + extension_dir.WriteFile(FILE_PATH_LITERAL("ext.html"), + "<body>Extension-overridden NTP</body>"); + scoped_refptr<const extensions::Extension> extension = + LoadExtension(extension_dir.Pack()); + return extension; + } + + void NavigateCurrentTab(const GURL& url) { + ui_test_utils::NavigateToURLWithDisposition( + browser(), url, WindowOpenDisposition::CURRENT_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); + } + + void OpenNewTab(const GURL& url) { + ui_test_utils::NavigateToURLWithDisposition( + browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); + } + + new_tab_footer::NewTabFooterWebView* footer() { + return BrowserView::GetBrowserViewForBrowser(browser()) + ->new_tab_footer_web_view(); + } + + protected: + base::test::ScopedFeatureList feature_list_; +}; + +class FooterControllerExtensionTest : public FooterControllerExtensionTestBase { + public: + FooterControllerExtensionTest() { + feature_list_.InitWithFeatures( + /*enabled_features=*/{ntp_features::kNtpFooter}, + /*disabled_features=*/{features::kSideBySide, + features::kEnterpriseBadgingForNtpFooter}); + } + ~FooterControllerExtensionTest() override = default; +}; + +IN_PROC_BROWSER_TEST_F(FooterControllerExtensionTest, + FooterShown_ExtensionNTP) { + auto extension = LoadNtpExtension(); + ASSERT_FALSE(footer()->GetVisible()); + + OpenNewTab(GURL(extension->url())); + EXPECT_TRUE(footer()->GetVisible()); + + NavigateCurrentTab(extension->url()); + EXPECT_TRUE(footer()->GetVisible()); +} + +IN_PROC_BROWSER_TEST_F(FooterControllerExtensionTest, + FooterHidden_NonExtensionNTP) { + auto extension = LoadNtpExtension(); + ASSERT_FALSE(footer()->GetVisible()); + NavigateCurrentTab(extension->url()); + EXPECT_TRUE(footer()->GetVisible()); + + NavigateCurrentTab(GURL(kNonNtpUrl)); + EXPECT_FALSE(footer()->GetVisible()); + + OpenNewTab(GURL(chrome::kChromeUINewTabPageURL)); + EXPECT_FALSE(footer()->GetVisible()); +} + +IN_PROC_BROWSER_TEST_F(FooterControllerExtensionTest, UserPrefChanged) { + profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, false); + auto extension = LoadNtpExtension(); + NavigateCurrentTab(extension->url()); + ASSERT_FALSE(footer()->GetVisible()); + + profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, true); + EXPECT_TRUE(footer()->GetVisible()); + + profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, false); + EXPECT_FALSE(footer()->GetVisible()); +} + +IN_PROC_BROWSER_TEST_F(FooterControllerExtensionTest, + AttributionPolicyChanged) { + auto extension = LoadNtpExtension(); + ASSERT_FALSE(footer()->GetVisible()); + + NavigateCurrentTab(extension->url()); + EXPECT_TRUE(footer()->GetVisible()); + + profile()->GetPrefs()->SetBoolean( + prefs::kNTPFooterExtensionAttributionEnabled, false); + EXPECT_FALSE(footer()->GetVisible()); + + profile()->GetPrefs()->SetBoolean( + prefs::kNTPFooterExtensionAttributionEnabled, true); + EXPECT_TRUE(footer()->GetVisible()); +} + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) +class FooterControllerEnterpriseTest + : public FooterControllerExtensionTestBase, + public testing::WithParamInterface<bool> { + public: + FooterControllerEnterpriseTest() { + feature_list_.InitWithFeatures( + /*enabled_features=*/{ntp_features::kNtpFooter, + features::kEnterpriseBadgingForNtpFooter}, + /*disabled_features=*/{features::kSideBySide}); + } + ~FooterControllerEnterpriseTest() override = default; + + bool managed() { return GetParam(); } + PrefService* local_state() { return g_browser_process->local_state(); } +}; + +INSTANTIATE_TEST_SUITE_P(, FooterControllerEnterpriseTest, testing::Bool()); + +IN_PROC_BROWSER_TEST_P(FooterControllerEnterpriseTest, + FooterShown_NoticeEnabled) { + policy::ScopedManagementServiceOverrideForTesting + profile_supervised_management( + policy::ManagementServiceFactory::GetForProfile(profile()), + managed() ? policy::EnterpriseManagementAuthority::DOMAIN_LOCAL + : policy::EnterpriseManagementAuthority::NONE); + + // Non-NTP + ASSERT_FALSE(footer()->GetVisible()); + + // Default NTP + NavigateCurrentTab(GURL(chrome::kChromeUINewTabURL)); + EXPECT_EQ(managed(), footer()->GetVisible()); + + // 1P NTP + NavigateCurrentTab(GURL(chrome::kChromeUINewTabPageURL)); + EXPECT_EQ(managed(), footer()->GetVisible()); + + // 3P NTP + NavigateCurrentTab(GURL(chrome::kChromeUINewTabPageThirdPartyURL)); + EXPECT_EQ(managed(), footer()->GetVisible()); + + // Extension NTP + auto extension = LoadNtpExtension(); + NavigateCurrentTab(extension->url()); + EXPECT_TRUE(footer()->GetVisible()); + + // Non-NTP + NavigateCurrentTab(GURL(kNonNtpUrl)); + EXPECT_FALSE(footer()->GetVisible()); +} + +IN_PROC_BROWSER_TEST_P(FooterControllerEnterpriseTest, + FooterHidden_NoticeDisabled) { + policy::ScopedManagementServiceOverrideForTesting + profile_supervised_management( + policy::ManagementServiceFactory::GetForProfile(profile()), + managed() ? policy::EnterpriseManagementAuthority::DOMAIN_LOCAL + : policy::EnterpriseManagementAuthority::NONE); + local_state()->SetBoolean(prefs::kNTPFooterManagementNoticeEnabled, false); + + NavigateCurrentTab(GURL(chrome::kChromeUINewTabPageURL)); + EXPECT_FALSE(footer()->GetVisible()); +} + +IN_PROC_BROWSER_TEST_P(FooterControllerEnterpriseTest, + FooterHidden_NoticePolicyChanged) { + policy::ScopedManagementServiceOverrideForTesting + profile_supervised_management( + policy::ManagementServiceFactory::GetForProfile(profile()), + managed() ? policy::EnterpriseManagementAuthority::DOMAIN_LOCAL + : policy::EnterpriseManagementAuthority::NONE); + ASSERT_FALSE(footer()->GetVisible()); + + NavigateCurrentTab(GURL(chrome::kChromeUINewTabURL)); + EXPECT_EQ(managed(), footer()->GetVisible()); + + local_state()->SetBoolean(prefs::kNTPFooterManagementNoticeEnabled, false); + EXPECT_FALSE(footer()->GetVisible()); + + local_state()->SetBoolean(prefs::kNTPFooterManagementNoticeEnabled, true); + EXPECT_EQ(managed(), footer()->GetVisible()); +} +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) + +// TODO(crbug.com/4438803): Once the controller supports SideBySide enablement, +// refactor `FooterControllerExtensionTest` into a value-parameterized test, +// making `FooterControllerSideBySideTest` and +// `FooterControllerExtensionTestBase` redundant. +class FooterControllerSideBySideTest : public InProcessBrowserTest { + public: + FooterControllerSideBySideTest() { + feature_list_.InitWithFeatures( + /*enabled_features=*/{ntp_features::kNtpFooter, features::kSideBySide, + features::kEnterpriseBadgingForNtpFooter}, + /*disabled_features=*/{}); + } + ~FooterControllerSideBySideTest() override = default; + + private: + base::test::ScopedFeatureList feature_list_; +}; + +IN_PROC_BROWSER_TEST_F(FooterControllerSideBySideTest, FooterNotCreated) { + auto* footer = BrowserView::GetBrowserViewForBrowser(browser()) + ->new_tab_footer_web_view(); + EXPECT_FALSE(footer); +}
diff --git a/chrome/browser/ui/views/new_tab_footer/footer_controller_unittest.cc b/chrome/browser/ui/views/new_tab_footer/footer_controller_unittest.cc deleted file mode 100644 index 8e90ca5..0000000 --- a/chrome/browser/ui/views/new_tab_footer/footer_controller_unittest.cc +++ /dev/null
@@ -1,235 +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/views/new_tab_footer/footer_controller.h" - -#include "base/test/scoped_feature_list.h" -#include "chrome/browser/extensions/chrome_test_extension_loader.h" -#include "chrome/browser/extensions/extension_service_test_base.h" -#include "chrome/browser/extensions/extension_web_ui.h" -#include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h" -#include "chrome/browser/ui/tabs/test/mock_tab_interface.h" -#include "chrome/browser/ui/ui_features.h" -#include "chrome/browser/ui/views/new_tab_footer/footer_web_view.h" -#include "chrome/common/pref_names.h" -#include "chrome/test/base/testing_profile.h" -#include "components/search/ntp_features.h" -#include "content/public/test/web_contents_tester.h" -#include "extensions/browser/extension_registry.h" -#include "extensions/common/extension_builder.h" -#include "extensions/test/test_extension_dir.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { -const char kNonExtensionNtpUrl[] = "https://www.google.com"; -} - -class FakeNewTabFooterWebView : public new_tab_footer::NewTabFooterWebView { - public: - explicit FakeNewTabFooterWebView(BrowserWindowInterface* browser_window) - : NewTabFooterWebView(browser_window) {} - ~FakeNewTabFooterWebView() override {} - - bool did_show_ui() { return did_show_ui_; } - bool did_close_ui() { return did_close_ui_; } - - void Clear() { - did_show_ui_ = false; - did_close_ui_ = false; - } - - protected: - // WebUIContentsWrapper::Host: - void ShowUI() override { did_show_ui_ = true; } - void CloseUI() override { did_close_ui_ = true; } - - bool did_show_ui_ = false; - bool did_close_ui_ = false; -}; - -class FakeBrowserWindowInterface : public MockBrowserWindowInterface { - public: - explicit FakeBrowserWindowInterface(Profile* profile) { profile_ = profile; } - ~FakeBrowserWindowInterface() override = default; - - // MockBrowserWindowInterface: - FakeNewTabFooterWebView* NewTabFooterWebView() override { - return footer_web_view_; - } - Profile* GetProfile() override { return profile_.get(); } - - void SetFooterWebView(FakeNewTabFooterWebView* footer_web_view) { - footer_web_view_ = footer_web_view; - } - - protected: - raw_ptr<Profile> profile_; - raw_ptr<FakeNewTabFooterWebView> footer_web_view_; -}; - -class FakeTabInterface : public tabs::MockTabInterface { - public: - FakeTabInterface(FakeBrowserWindowInterface* browser_window, - content::BrowserContext* browser_context) { - browser_window_ = browser_window; - web_contents_ = content::WebContentsTester::CreateTestWebContents( - browser_context, nullptr); - footer_web_view_ = - std::make_unique<FakeNewTabFooterWebView>(browser_window); - browser_window_->SetFooterWebView(footer_web_view_.get()); - } - ~FakeTabInterface() override { - browser_window_->SetFooterWebView(nullptr); - footer_web_view_.reset(); - } - - // tabs::MockTabInterface: - FakeBrowserWindowInterface* GetBrowserWindowInterface() override { - return browser_window_; - } - content::WebContents* GetContents() const override { - return web_contents_.get(); - } - bool IsActivated() const override { return true; } - - void NavigateTo(const GURL url) { - EXPECT_TRUE(web_contents_.get()); - content::WebContentsTester* web_contents_tester = - content::WebContentsTester::For(web_contents_.get()); - web_contents_tester->NavigateAndCommit(url); - } - - protected: - std::unique_ptr<content::WebContents> web_contents_; - std::unique_ptr<FakeNewTabFooterWebView> footer_web_view_; - raw_ptr<FakeBrowserWindowInterface> browser_window_; -}; - -class FooterControllerExtensionTest - : public extensions::ExtensionServiceTestBase { - public: - void SetUp() override { - feature_list_.InitWithFeatures( - /*enabled_features=*/{ntp_features::kNtpFooter}, - /*disabled_features=*/{features::kSideBySide}); - ExtensionServiceTestBase::SetUp(); - InitializeEmptyExtensionService(); - - browser_window_ = std::make_unique<FakeBrowserWindowInterface>(profile()); - tab_ = std::make_unique<FakeTabInterface>(browser_window_.get(), - browser_context()); - controller_ = - std::make_unique<new_tab_footer::NewTabFooterController>(tab_.get()); - } - - void TearDown() override { - controller_.reset(); - tab_.reset(); - browser_window_.reset(); - ExtensionServiceTestBase::TearDown(); - } - - scoped_refptr<const extensions::Extension> LoadNtpExtension() { - extensions::TestExtensionDir extension_dir; - const std::string kManifest = R"( - { - "chrome_url_overrides": { - "newtab": "ext.html" - }, - "name": "Extension-overridden NTP", - "manifest_version": 3, - "version": "0.1" - })"; - extension_dir.WriteManifest(kManifest); - extension_dir.WriteFile(FILE_PATH_LITERAL("ext.html"), - "<body>Extension-overridden NTP</body>"); - extensions::ChromeTestExtensionLoader extension_loader(profile()); - scoped_refptr<const extensions::Extension> extension = - extension_loader.LoadExtension(extension_dir.Pack()); - return extension; - } - - FakeBrowserWindowInterface* browser_window() { return browser_window_.get(); } - - FakeTabInterface* tab_interface() { return tab_.get(); } - - protected: - base::test::ScopedFeatureList feature_list_; - std::unique_ptr<FakeBrowserWindowInterface> browser_window_; - std::unique_ptr<FakeTabInterface> tab_; - std::unique_ptr<new_tab_footer::NewTabFooterController> controller_; -}; - -TEST_F(FooterControllerExtensionTest, FooterShown_ExtensionNTP) { - profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, true); - auto extension = LoadNtpExtension(); - ASSERT_TRUE(extension); - // Force activation of the URL override. The usual observer for - // extension load isn't created in the unit test. - ExtensionWebUI::RegisterOrActivateChromeURLOverrides( - profile_.get(), - extensions::URLOverrides::GetChromeURLOverrides(extension.get())); - EXPECT_FALSE(browser_window()->NewTabFooterWebView()->did_show_ui()); - tab_interface()->NavigateTo(extension->url()); - - EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_show_ui()); -} - -TEST_F(FooterControllerExtensionTest, FooterHidden_NonExtensionNTP) { - profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, true); - // After a pref change, there's an attempt to show the footer. - EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_close_ui()); - // Clear the value of `FakeNewTabFooterWebView::did_close_ui()` to - // check that the navigation also results in a hidden footer. - browser_window()->NewTabFooterWebView()->Clear(); - - EXPECT_FALSE(browser_window()->NewTabFooterWebView()->did_close_ui()); - tab_interface()->NavigateTo(GURL(kNonExtensionNtpUrl)); - - EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_close_ui()); -} - -// Ensures footer is shown on extension NTPs when -// `prefs::kNtpFooterVisible` is set to true. -TEST_F(FooterControllerExtensionTest, FooterShown_UserPref) { - profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, false); - auto extension = LoadNtpExtension(); - ASSERT_TRUE(extension); - // Force activation of the URL override. The usual observer for - // extension load isn't created in the unit test. - ExtensionWebUI::RegisterOrActivateChromeURLOverrides( - profile_.get(), - extensions::URLOverrides::GetChromeURLOverrides(extension.get())); - - tab_interface()->NavigateTo(extension->url()); - EXPECT_FALSE(browser_window()->NewTabFooterWebView()->did_show_ui()); - - profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, true); - EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_show_ui()); -} - -// Ensures footer is hidden on extension NTPs when -// `prefs::kNtpFooterVisible` is set to false. -TEST_F(FooterControllerExtensionTest, FooterHidden_UserPref) { - profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, true); - // After a pref change, there's an attempt to show the footer. - EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_close_ui()); - // Clear the value of `FakeNewTabFooterWebView::did_close_ui()` to check - // the effect of pref changes on an extension NTP. - browser_window()->NewTabFooterWebView()->Clear(); - EXPECT_FALSE(browser_window()->NewTabFooterWebView()->did_close_ui()); - - auto extension = LoadNtpExtension(); - ASSERT_TRUE(extension); - // Force activation of the URL override. The usual observer for - // extension load isn't created in the unit test. - ExtensionWebUI::RegisterOrActivateChromeURLOverrides( - profile_.get(), - extensions::URLOverrides::GetChromeURLOverrides(extension.get())); - - tab_interface()->NavigateTo(extension->url()); - EXPECT_FALSE(browser_window()->NewTabFooterWebView()->did_close_ui()); - - profile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, false); - EXPECT_TRUE(browser_window()->NewTabFooterWebView()->did_close_ui()); -}
diff --git a/chrome/browser/ui/views/new_tab_footer/footer_interactive_uitest.cc b/chrome/browser/ui/views/new_tab_footer/footer_interactive_uitest.cc index b329e865..85ffbca 100644 --- a/chrome/browser/ui/views/new_tab_footer/footer_interactive_uitest.cc +++ b/chrome/browser/ui/views/new_tab_footer/footer_interactive_uitest.cc
@@ -3,30 +3,33 @@ // found in the LICENSE file. #include "base/test/scoped_feature_list.h" +#include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/enterprise/browser_management/management_service_factory.h" #include "chrome/browser/extensions/chrome_test_extension_loader.h" #include "chrome/browser/extensions/install_verifier.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser_commands.h" +#include "chrome/browser/ui/browser_element_identifiers.h" #include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/views/new_tab_footer/footer_web_view.h" #include "chrome/browser/ui/webui/test_support/webui_interactive_test_mixin.h" +#include "chrome/common/pref_names.h" #include "chrome/test/base/ui_test_utils.h" #include "chrome/test/interaction/interactive_browser_test.h" +#include "components/policy/core/common/management/scoped_management_service_override_for_testing.h" #include "components/search/ntp_features.h" #include "content/public/test/browser_test.h" #include "content/public/test/test_navigation_observer.h" #include "extensions/test/test_extension_dir.h" -namespace { -constexpr char kFooterViewName[] = "footer_view"; -} // namespace - class FooterInteractiveTest : public WebUiInteractiveTestMixin<InteractiveBrowserTest> { public: FooterInteractiveTest() { scoped_feature_list_.InitWithFeatures( - /*enabled_features=*/{ntp_features::kNtpFooter}, + /*enabled_features=*/{ntp_features::kNtpFooter, + features::kEnterpriseBadgingForNtpFooter}, /*disabled_features=*/{features::kSideBySide}); } @@ -71,6 +74,17 @@ nav_observer.Wait(); } + InteractiveTestApi::MultiStep OpenCustomizeChromeSidePanel( + const ui::ElementIdentifier& contents_id) { + return Steps(Do(base::BindLambdaForTesting([=, this]() { + chrome::ExecuteCommand(browser(), + IDC_SHOW_CUSTOMIZE_CHROME_SIDE_PANEL); + })), + WaitForShow(kCustomizeChromeSidePanelWebViewElementId), + InstrumentNonTabWebView( + contents_id, kCustomizeChromeSidePanelWebViewElementId)); + } + new_tab_footer::NewTabFooterWebView* GetFooterView() { return browser()->GetBrowserView().new_tab_footer_web_view(); } @@ -80,33 +94,142 @@ extensions::ScopedInstallVerifierBypassForTest install_verifier_bypass_; }; -IN_PROC_BROWSER_TEST_F(FooterInteractiveTest, FooterVisibleOnExtensionNtp) { +IN_PROC_BROWSER_TEST_F(FooterInteractiveTest, + ConsumerExtensionNtp_FooterVisible) { LoadNtpOverridingExtension(browser()->profile()); RunTestSequence( // Open extension NTP. Do(base::BindLambdaForTesting([&, this]() { OpenNewTabPage(); })), - Steps(NameView(kFooterViewName, GetFooterView()), - // Ensure footer is visible. - CheckView(kFooterViewName, - [](new_tab_footer::NewTabFooterWebView* footer) { - return footer->GetVisible(); - }))); + // Ensure footer is visible. + Steps(WaitForShow(kNtpFooterId))); } IN_PROC_BROWSER_TEST_F(FooterInteractiveTest, - FooterNotVisibleOnNonExtensionNtp) { + ConsumerNonExtensionNtp_FooterNotVisible) { LoadNtpOverridingExtension(browser()->profile()); RunTestSequence( // Open extension NTP. Do(base::BindLambdaForTesting([&, this]() { OpenNewTabPage(); })), - Steps(NameView(kFooterViewName, GetFooterView()), - // Ensure footer is visible. - CheckView(kFooterViewName, - [](new_tab_footer::NewTabFooterWebView* footer) { - return footer->GetVisible(); - })), + // Ensure footer is visible. + Steps(WaitForShow(kNtpFooterId)), // Navigate to non-extension NTP and check that the footer isn't visible. Do(base::BindLambdaForTesting( [&, this]() { NavigateTo(GURL("https://google.com")); })), - WaitForHide(kFooterViewName)); + WaitForHide(kNtpFooterId)); } + +IN_PROC_BROWSER_TEST_F(FooterInteractiveTest, + CustomizeChrome_ToggleHidesFooter) { + browser()->GetProfile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, + true); + + DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kLocalCustomizeChromeElementId); + const DeepQuery kFooterSection = {"customize-chrome-app", "#footer", + "customize-chrome-footer", + "#showToggleContainer", "#showToggle"}; + + LoadNtpOverridingExtension(browser()->profile()); + RunTestSequence( + // Open extension NTP. + Do(base::BindLambdaForTesting([&, this]() { OpenNewTabPage(); })), + // Ensure footer is visible. + WaitForShow(kNtpFooterId), + OpenCustomizeChromeSidePanel(kLocalCustomizeChromeElementId), + Steps( + // Click the footer section toggle. + ScrollIntoView(kLocalCustomizeChromeElementId, kFooterSection), + EnsurePresent(kLocalCustomizeChromeElementId, kFooterSection), + ExecuteJsAt(kLocalCustomizeChromeElementId, kFooterSection, + "(toggle) => toggle.click()"), + // Ensure the footer is no longer visible. + WaitForHide(kNtpFooterId))); +} + +IN_PROC_BROWSER_TEST_F(FooterInteractiveTest, + CustomizeChrome_ToggleShowsFooter) { + browser()->GetProfile()->GetPrefs()->SetBoolean(prefs::kNtpFooterVisible, + false); + + DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kLocalCustomizeChromeElementId); + const DeepQuery kFooterSection = {"customize-chrome-app", "#footer", + "customize-chrome-footer", + "#showToggleContainer", "#showToggle"}; + LoadNtpOverridingExtension(browser()->profile()), + RunTestSequence( + Do(base::BindLambdaForTesting([&, this]() { OpenNewTabPage(); })), + // Ensure footer is not visible. + EnsureNotPresent(kNtpFooterId), + OpenCustomizeChromeSidePanel(kLocalCustomizeChromeElementId), + Steps( + // Click the footer section toggle. + ScrollIntoView(kLocalCustomizeChromeElementId, kFooterSection), + EnsurePresent(kLocalCustomizeChromeElementId, kFooterSection), + ExecuteJsAt(kLocalCustomizeChromeElementId, kFooterSection, + "(toggle) => toggle.click()"), + // Ensure footer is visible. + WaitForShow(kNtpFooterId))); +} + +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) +IN_PROC_BROWSER_TEST_F(FooterInteractiveTest, + EnterpriseNonExtensionNtp_FooterVisible) { + policy::ScopedManagementServiceOverrideForTesting browser_management( + policy::ManagementServiceFactory::GetForProfile(browser()->profile()), + policy::EnterpriseManagementAuthority::DOMAIN_LOCAL); + RunTestSequence( + // Open NTP. + Do(base::BindLambdaForTesting([&, this]() { OpenNewTabPage(); })), + // Ensure footer is visible. + Steps(WaitForShow(kNtpFooterId))); +} + +IN_PROC_BROWSER_TEST_F(FooterInteractiveTest, + EnterpriseExtensionNtp_FooterVisible) { + policy::ScopedManagementServiceOverrideForTesting browser_management( + policy::ManagementServiceFactory::GetForProfile(browser()->profile()), + policy::EnterpriseManagementAuthority::DOMAIN_LOCAL); + LoadNtpOverridingExtension(browser()->profile()); + RunTestSequence( + // Open extension NTP. + Do(base::BindLambdaForTesting([&, this]() { OpenNewTabPage(); })), + // Ensure footer is visible. + Steps(WaitForShow(kNtpFooterId))); +} + +IN_PROC_BROWSER_TEST_F(FooterInteractiveTest, + EnterpriseNonNtp_FooterNotVisible) { + policy::ScopedManagementServiceOverrideForTesting browser_management( + policy::ManagementServiceFactory::GetForProfile(browser()->profile()), + policy::EnterpriseManagementAuthority::DOMAIN_LOCAL); + RunTestSequence( + // Open NTP. + Do(base::BindLambdaForTesting([&, this]() { OpenNewTabPage(); })), + Steps( + // Ensure footer is visible. + Steps(WaitForShow(kNtpFooterId))), + // Navigate to non-extension NTP and check that the footer isn't visible. + Do(base::BindLambdaForTesting( + [&, this]() { NavigateTo(GURL("https://google.com")); })), + WaitForHide(kNtpFooterId)); +} + +IN_PROC_BROWSER_TEST_F(FooterInteractiveTest, + ManagementNoticeDisabledByPolicy_FooterNotVisible) { + g_browser_process->local_state()->SetBoolean( + prefs::kNTPFooterManagementNoticeEnabled, false); + policy::ScopedManagementServiceOverrideForTesting browser_management( + policy::ManagementServiceFactory::GetForProfile(browser()->profile()), + policy::EnterpriseManagementAuthority::DOMAIN_LOCAL); + OpenNewTabPage(); + EXPECT_FALSE(GetFooterView()->GetVisible()); +} + +IN_PROC_BROWSER_TEST_F(FooterInteractiveTest, + ExtensionAttributionDisabledByPolicy_FooterNotVisible) { + browser()->profile()->GetPrefs()->SetBoolean( + prefs::kNTPFooterExtensionAttributionEnabled, false); + LoadNtpOverridingExtension(browser()->profile()); + OpenNewTabPage(); + EXPECT_FALSE(GetFooterView()->GetVisible()); +} +#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
diff --git a/chrome/browser/ui/views/new_tab_footer/footer_web_view.cc b/chrome/browser/ui/views/new_tab_footer/footer_web_view.cc index 27ac67e..1da7454 100644 --- a/chrome/browser/ui/views/new_tab_footer/footer_web_view.cc +++ b/chrome/browser/ui/views/new_tab_footer/footer_web_view.cc
@@ -12,6 +12,9 @@ #include "chrome/common/webui_url_constants.h" #include "chrome/grit/generated_resources.h" #include "ui/base/metadata/metadata_impl_macros.h" +#include "ui/views/view_class_properties.h" + +DEFINE_ELEMENT_IDENTIFIER_VALUE(kNtpFooterId); namespace new_tab_footer { @@ -25,6 +28,7 @@ SetWebContents(contents_wrapper_->web_contents()); webui::SetBrowserWindowInterface(contents_wrapper_->web_contents(), browser_window); + SetProperty(views::kElementIdentifierKey, kNtpFooterId); } NewTabFooterWebView::~NewTabFooterWebView() {
diff --git a/chrome/browser/ui/views/new_tab_footer/footer_web_view.h b/chrome/browser/ui/views/new_tab_footer/footer_web_view.h index 5b012ea..3ee8f3a 100644 --- a/chrome/browser/ui/views/new_tab_footer/footer_web_view.h +++ b/chrome/browser/ui/views/new_tab_footer/footer_web_view.h
@@ -13,6 +13,8 @@ class BrowserWindowInterface; class WebUIContentsWrapper; +DECLARE_ELEMENT_IDENTIFIER_VALUE(kNtpFooterId); + namespace new_tab_footer { // NewTabFooterWebView is used to present the WebContents of the New Tab Footer.
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_controller.cc b/chrome/browser/ui/views/page_action/page_action_icon_controller.cc index e31f3ff..5732e0f 100644 --- a/chrome/browser/ui/views/page_action/page_action_icon_controller.cc +++ b/chrome/browser/ui/views/page_action/page_action_icon_controller.cc
@@ -34,6 +34,7 @@ #include "chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view.h" #include "chrome/browser/ui/views/location_bar/find_bar_icon.h" #include "chrome/browser/ui/views/location_bar/intent_picker_view.h" +#include "chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view.h" #include "chrome/browser/ui/views/location_bar/lens_overlay_page_action_icon_view.h" #include "chrome/browser/ui/views/location_bar/star_view.h" #include "chrome/browser/ui/views/location_bar/zoom_bubble_view.h" @@ -283,6 +284,12 @@ params.browser, params.icon_label_bubble_delegate, params.page_action_icon_delegate)); break; + case PageActionIconType::kLensOverlayHomework: + add_page_action_icon( + type, std::make_unique<LensOverlayHomeworkPageActionIconView>( + params.icon_label_bubble_delegate, + params.page_action_icon_delegate, params.browser)); + break; case PageActionIconType::kOptimizationGuide: add_page_action_icon( type, std::make_unique<OptimizationGuideIconView>( @@ -348,22 +355,6 @@ }); } -bool PageActionIconController::ActivateFirstInactiveBubbleForAccessibility() { - for (auto icon_item : page_action_icon_views_) { - auto* icon = icon_item.second.get(); - if (!icon->GetVisible() || !icon->GetBubble()) { - continue; - } - - views::Widget* widget = icon->GetBubble()->GetWidget(); - if (widget && widget->IsVisible() && !widget->IsActive()) { - widget->Show(); - return true; - } - } - return false; -} - void PageActionIconController::SetIconColor(SkColor icon_color) { for (auto icon_item : page_action_icon_views_) { icon_item.second->SetIconColor(icon_color);
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_controller.h b/chrome/browser/ui/views/page_action/page_action_icon_controller.h index 2052a681..549fba3 100644 --- a/chrome/browser/ui/views/page_action/page_action_icon_controller.h +++ b/chrome/browser/ui/views/page_action/page_action_icon_controller.h
@@ -46,10 +46,6 @@ bool IsAnyIconVisible() const; - // Activates the first visible but inactive icon for accessibility. Returns - // whether any icons were activated. - bool ActivateFirstInactiveBubbleForAccessibility(); - // Update the icons' color. void SetIconColor(SkColor icon_color);
diff --git a/chrome/browser/ui/views/performance_controls/tab_list_row_view.cc b/chrome/browser/ui/views/performance_controls/tab_list_row_view.cc index f4f1f67..c60715c 100644 --- a/chrome/browser/ui/views/performance_controls/tab_list_row_view.cc +++ b/chrome/browser/ui/views/performance_controls/tab_list_row_view.cc
@@ -14,10 +14,12 @@ #include "base/time/time.h" #include "chrome/browser/ui/performance_controls/tab_list_model.h" #include "chrome/browser/ui/tab_ui_helper.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/grit/generated_resources.h" #include "components/performance_manager/public/resource_attribution/page_context.h" #include "components/strings/grit/components_strings.h" +#include "components/tabs/public/tab_interface.h" #include "components/url_formatter/url_formatter.h" #include "components/vector_icons/vector_icons.h" #include "content/public/browser/web_contents.h" @@ -155,7 +157,10 @@ content::WebContents* const web_contents = tab.GetWebContents(); CHECK(web_contents); - TabUIHelper* const tab_ui_helper = TabUIHelper::FromWebContents(web_contents); + tabs::TabInterface* const tab_interface = + tabs::TabInterface::GetFromContents(web_contents); + TabUIHelper* const tab_ui_helper = + tab_interface->GetTabFeatures()->tab_ui_helper(); CHECK(tab_ui_helper); // The container adds all contents of the row as child views to ensure that we
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc index 81b9314..60ad9eb 100644 --- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc +++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
@@ -32,6 +32,7 @@ #include "chrome/browser/ui/browser_tabstrip.h" #include "chrome/browser/ui/browser_window/public/browser_window_features.h" #include "chrome/browser/ui/tab_ui_helper.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h" #include "chrome/browser/ui/tabs/split_tab_util.h" #include "chrome/browser/ui/tabs/tab_enums.h" @@ -706,8 +707,7 @@ IDS_TAB_CXMENU_PLACEHOLDER_GROUP_TITLE, tab_group->tab_count() - 1); std::u16string short_title; gfx::ElideString( - TabUIHelper::FromWebContents(tab_group->GetFirstTab()->GetContents()) - ->GetTitle(), + tab_group->GetFirstTab()->GetTabFeatures()->tab_ui_helper()->GetTitle(), kContextMenuTabTitleMaxLength, &short_title); return base::ReplaceStringPlaceholders(format_string, short_title, nullptr); } @@ -856,10 +856,13 @@ if (selection.active_tab_changed()) { // It's possible for `new_contents` to be null when the final tab in a tab // strip is closed. - content::WebContents* new_contents = selection.new_contents; + content::WebContents* const new_contents = selection.new_contents; + tabs::TabInterface* const new_tab_interface = selection.new_tab; std::optional<size_t> index = selection.new_model.active(); - if (new_contents && index.has_value()) { - TabUIHelper::FromWebContents(new_contents)->SetWasActiveAtLeastOnce(); + if (new_contents && new_tab_interface && index.has_value()) { + new_tab_interface->GetTabFeatures() + ->tab_ui_helper() + ->SetWasActiveAtLeastOnce(); SetTabDataAt(new_contents, index.value()); } }
diff --git a/chrome/browser/ui/views/tabs/compound_tab_container.cc b/chrome/browser/ui/views/tabs/compound_tab_container.cc index 3068beb..e655cb3c 100644 --- a/chrome/browser/ui/views/tabs/compound_tab_container.cc +++ b/chrome/browser/ui/views/tabs/compound_tab_container.cc
@@ -671,11 +671,6 @@ return unpinned_tab_container_->get_group_views_for_testing(); // IN-TEST } -int CompoundTabContainer::GetActiveTabWidth() const { - // Only the unpinned container has variable-width tabs. - return unpinned_tab_container_->GetActiveTabWidth(); -} - gfx::Rect CompoundTabContainer::GetIdealBounds(int model_index) const { // Ideal bounds for pinned tabs are fine as-is. if (model_index < NumPinnedTabs()) {
diff --git a/chrome/browser/ui/views/tabs/compound_tab_container.h b/chrome/browser/ui/views/tabs/compound_tab_container.h index 9bb50d65..924df23 100644 --- a/chrome/browser/ui/views/tabs/compound_tab_container.h +++ b/chrome/browser/ui/views/tabs/compound_tab_container.h
@@ -96,7 +96,6 @@ TabGroupViews* GetGroupViews(tab_groups::TabGroupId group_id) const override; const std::map<tab_groups::TabGroupId, std::unique_ptr<TabGroupViews>>& get_group_views_for_testing() const override; - int GetActiveTabWidth() const override; gfx::Rect GetIdealBounds(int model_index) const override; gfx::Rect GetIdealBounds(tab_groups::TabGroupId group) const override;
diff --git a/chrome/browser/ui/views/tabs/dragging/dragging_tabs_session.cc b/chrome/browser/ui/views/tabs/dragging/dragging_tabs_session.cc index 45e4c4db..2c9860c 100644 --- a/chrome/browser/ui/views/tabs/dragging/dragging_tabs_session.cc +++ b/chrome/browser/ui/views/tabs/dragging/dragging_tabs_session.cc
@@ -236,7 +236,7 @@ } return gfx::Rect(tab_strip_point.x(), tab_strip_point.y(), - attached_context_->GetActiveTabWidth(), + TabStyle::Get()->GetStandardWidth(/*is_split=*/false), GetLayoutConstant(TAB_HEIGHT)); }
diff --git a/chrome/browser/ui/views/tabs/dragging/tab_drag_context.h b/chrome/browser/ui/views/tabs/dragging/tab_drag_context.h index 2c8a6c5a..c4e8352 100644 --- a/chrome/browser/ui/views/tabs/dragging/tab_drag_context.h +++ b/chrome/browser/ui/views/tabs/dragging/tab_drag_context.h
@@ -96,9 +96,6 @@ // Returns true if a tab is being dragged into this tab strip. virtual bool IsActiveDropTarget() const = 0; - // Returns the width of the active tab. - virtual int GetActiveTabWidth() const = 0; - // Returns where the drag region begins and ends; tabs dragged beyond these // points should detach. virtual int TabDragAreaEndX() const = 0;
diff --git a/chrome/browser/ui/views/tabs/tab_container.h b/chrome/browser/ui/views/tabs/tab_container.h index ed2983d..b621711 100644 --- a/chrome/browser/ui/views/tabs/tab_container.h +++ b/chrome/browser/ui/views/tabs/tab_container.h
@@ -184,9 +184,6 @@ std::unique_ptr<TabGroupViews>>& get_group_views_for_testing() const = 0; - // Returns the current width of the active tab. - virtual int GetActiveTabWidth() const = 0; - // Returns ideal bounds for the tab at `model_index` in this TabContainer's // coordinate space. virtual gfx::Rect GetIdealBounds(int model_index) const = 0;
diff --git a/chrome/browser/ui/views/tabs/tab_container_impl.cc b/chrome/browser/ui/views/tabs/tab_container_impl.cc index 75379edb..ee8890d 100644 --- a/chrome/browser/ui/views/tabs/tab_container_impl.cc +++ b/chrome/browser/ui/views/tabs/tab_container_impl.cc
@@ -822,10 +822,6 @@ return group_views_; } -int TabContainerImpl::GetActiveTabWidth() const { - return layout_helper_->active_tab_width(); -} - gfx::Rect TabContainerImpl::GetIdealBounds(int model_index) const { return tabs_view_model_.ideal_bounds(model_index); }
diff --git a/chrome/browser/ui/views/tabs/tab_container_impl.h b/chrome/browser/ui/views/tabs/tab_container_impl.h index b1af740..2513dc0c 100644 --- a/chrome/browser/ui/views/tabs/tab_container_impl.h +++ b/chrome/browser/ui/views/tabs/tab_container_impl.h
@@ -130,8 +130,6 @@ const std::map<tab_groups::TabGroupId, std::unique_ptr<TabGroupViews>>& get_group_views_for_testing() const override; - int GetActiveTabWidth() const override; - gfx::Rect GetIdealBounds(int model_index) const override; gfx::Rect GetIdealBounds(tab_groups::TabGroupId group) const override;
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc index e1ddab79..d4a7ae4 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.cc +++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -478,10 +478,6 @@ return false; } - int GetActiveTabWidth() const override { - return tab_strip_->GetActiveTabWidth(); - } - int GetTabDragAreaWidth() const override { // There are two cases here (with tab scrolling enabled): // 1) If the tab strip is not wider than the tab strip region (and thus @@ -2239,10 +2235,6 @@ #endif // BUILDFLAG(IS_WIN) } -int TabStrip::GetActiveTabWidth() const { - return tab_container_->GetActiveTabWidth(); -} - const Tab* TabStrip::GetLastVisibleTab() const { for (int i = GetTabCount() - 1; i >= 0; --i) { const Tab* tab = tab_at(i); @@ -2556,5 +2548,4 @@ TabSeparatorColor, ui::metadata::SkColorConverter) ADD_READONLY_PROPERTY_METADATA(float, HoverOpacityForRadialHighlight) -ADD_READONLY_PROPERTY_METADATA(int, ActiveTabWidth) END_METADATA
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h index e6d84f5..3f389e4 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.h +++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -402,9 +402,6 @@ // Returns whether the window background behind the tabstrip is transparent. bool TitlebarBackgroundIsTransparent() const; - // Returns the current width of the active tab. - int GetActiveTabWidth() const; - // Returns the last tab in the strip that's actually visible. This will be // the actual last tab unless the strip is in the overflow node_data. const Tab* GetLastVisibleTab() const;
diff --git a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc index 6ed59f6..61c8b89b 100644 --- a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc +++ b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.cc
@@ -47,7 +47,6 @@ GetTabsCallback get_tabs_callback) : controller_(controller), get_tabs_callback_(get_tabs_callback), - active_tab_width_(TabStyle::Get()->GetStandardWidth(/*is_split=*/false)), tab_strip_layout_domain_(LayoutDomain::kInactiveWidthEqualsActiveWidth) {} TabStripLayoutHelper::~TabStripLayoutHelper() = default; @@ -229,14 +228,6 @@ case TabSlotView::ViewType::kTab: if (!slot.state.IsClosed()) { tabs->set_ideal_bounds(current_tab_model_index, bounds[i]); - bool is_active = i == active_tab_slot_index; - - if (active_split_id.has_value() && - slot.view->split() == active_split_id) { - is_active = true; - } - - UpdateCachedTabWidth(i, bounds[i].width(), is_active); ++current_tab_model_index; } break; @@ -424,19 +415,6 @@ return std::nullopt; } -void TabStripLayoutHelper::UpdateCachedTabWidth(int tab_index, - int tab_width, - bool active) { - // If the slot is collapsed, its width should never be reported as the - // current active or inactive tab width - it's not even visible. - if (SlotIsCollapsedTab(tab_index)) { - return; - } - if (active) { - active_tab_width_ = tab_width; - } -} - bool TabStripLayoutHelper::SlotIsCollapsedTab(int i) const { // The slot can only be collapsed if it is a tab and in a collapsed group. // If the slot is indeed a tab and in a group, check the collapsed state of
diff --git a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.h b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.h index 35a9b06..462eccb 100644 --- a/chrome/browser/ui/views/tabs/tab_strip_layout_helper.h +++ b/chrome/browser/ui/views/tabs/tab_strip_layout_helper.h
@@ -49,7 +49,6 @@ // GetTabs() and all tab group headers. std::vector<TabSlotView*> GetTabSlotViews() const; - int active_tab_width() { return active_tab_width_; } LayoutDomain layout_domain() { return tab_strip_layout_domain_; } // Returns the number of pinned tabs in the tabstrip. @@ -102,8 +101,7 @@ int CalculatePreferredWidth(); // Generates and sets the ideal bounds for the views in `tabs` and - // `group_headers`. Updates the cached width in `active_tab_width_`. Returns - // the total width occupied by the new ideal bounds. + // `group_headers`. Returns the total width occupied by the new ideal bounds. int UpdateIdealBounds(int available_width); private: @@ -147,9 +145,6 @@ // tab. Otherwise returns `std::nullopt`. std::optional<int> GetAdjacentSplitTab(int index) const; - // Updates the value of either `active_tab_width_`. - void UpdateCachedTabWidth(int tab_index, int tab_width, bool active); - // True iff the slot at index `i` is a tab that is in a collapsed group. bool SlotIsCollapsedTab(int i) const; @@ -166,10 +161,6 @@ // Contains the ideal bounds of tab group headers. std::map<tab_groups::TabGroupId, gfx::Rect> group_header_ideal_bounds_; - // The current widths of tabs. If the space for tabs is not evenly divisible - // into these widths, the initial tabs in the strip will be 1 px larger. - int active_tab_width_; - LayoutDomain tab_strip_layout_domain_; };
diff --git a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc index 917db15..bdf7b24b 100644 --- a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc +++ b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
@@ -185,8 +185,6 @@ views::test::RunScheduledLayout(tab_strip_parent_.get()); } - int GetActiveTabWidth() { return tab_strip_->GetActiveTabWidth(); } - // End any outstanding drag and animate tabs back to their ideal bounds. void StopDragging() { tab_strip_->GetDragContext()->StoppedDragging(); } @@ -463,30 +461,6 @@ } #endif -// The cached widths are private, but if they give incorrect results it can -// cause subtle errors in other tests. Therefore it's prudent to test them. -TEST_P(TabStripTest, CachedWidthsReportCorrectSize) { - controller_->AddTab(0, TabActive::kInactive); - controller_->AddTab(1, TabActive::kActive); - controller_->AddTab(2, TabActive::kInactive); - - const int standard_width = - TabStyle::Get()->GetStandardWidth(/*is_split*/ false); - - SetMaxTabStripWidth(1000); - - EXPECT_EQ(standard_width, GetActiveTabWidth()); - - SetMaxTabStripWidth(240); - - EXPECT_LT(GetActiveTabWidth(), standard_width); - - SetMaxTabStripWidth(50); - - EXPECT_EQ(TabStyle::Get()->GetMinimumActiveWidth(/*is_split*/ false), - GetActiveTabWidth()); -} - // The active tab should always be at least as wide as its minimum width. // http://crbug.com/587688 TEST_P(TabStripTest, ActiveTabWidthWhenTabsAreTiny) {
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 15839ab..6c3c16e 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
@@ -9,6 +9,7 @@ #include <utility> #include <vector> +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "base/test/gmock_expected_support.h" #include "base/test/scoped_feature_list.h"
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest_base.cc b/chrome/browser/ui/web_applications/web_app_browsertest_base.cc index 5edbe6f4..a6ec0c4 100644 --- a/chrome/browser/ui/web_applications/web_app_browsertest_base.cc +++ b/chrome/browser/ui/web_applications/web_app_browsertest_base.cc
@@ -7,6 +7,7 @@ #include <string> #include <vector> +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/bind.h" #include "base/time/time.h"
diff --git a/chrome/browser/ui/webui/ash/settings/pages/device/inputs_section.cc b/chrome/browser/ui/webui/ash/settings/pages/device/inputs_section.cc index 4bc9aea..8d7d21c 100644 --- a/chrome/browser/ui/webui/ash/settings/pages/device/inputs_section.cc +++ b/chrome/browser/ui/webui/ash/settings/pages/device/inputs_section.cc
@@ -261,8 +261,6 @@ IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_SECTION_SHORTCUT_ASDFGHJKL}, {"inputMethodOptionsJapaneseKeymapStyle", IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_KEYMAP_STYLE}, - {"inputMethodOptionsJapaneseKeymapStyleCustom", - IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_KEYMAP_STYLE_CUSTOM}, {"inputMethodOptionsJapaneseKeymapStyleAtok", IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_KEYMAP_STYLE_ATOK}, {"inputMethodOptionsJapaneseKeymapStyleMsIme",
diff --git a/chrome/browser/ui/webui/new_tab_footer/BUILD.gn b/chrome/browser/ui/webui/new_tab_footer/BUILD.gn index 0c51a8b0..2f5132fa 100644 --- a/chrome/browser/ui/webui/new_tab_footer/BUILD.gn +++ b/chrome/browser/ui/webui/new_tab_footer/BUILD.gn
@@ -34,6 +34,7 @@ "//chrome/browser/extensions", "//chrome/browser/profiles", "//chrome/browser/resources/new_tab_footer:resources_grit", + "//chrome/browser/search", "//chrome/browser/ui/browser_window", "//chrome/browser/ui/webui:webui_util", "//chrome/common",
diff --git a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_helper.cc b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_helper.cc index e9b6320..ee29535 100644 --- a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_helper.cc +++ b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_helper.cc
@@ -4,7 +4,11 @@ #include "chrome/browser/ui/webui/new_tab_footer/new_tab_footer_helper.h" +#include "chrome/browser/enterprise/util/managed_browser_utils.h" #include "chrome/browser/extensions/settings_api_helpers.h" +#include "chrome/common/pref_names.h" +#include "components/prefs/pref_service.h" +#include "components/search/ntp_features.h" #include "extensions/common/constants.h" namespace ntp_footer { @@ -24,4 +28,12 @@ return extension_managing_ntp->id() == url.host(); } +bool CanShowExtensionFooter(const GURL& url, Profile* profile) { + if (!IsExtensionNtp(url, profile)) { + return false; + } + + return profile->GetPrefs()->GetBoolean( + prefs::kNTPFooterExtensionAttributionEnabled); +} } // namespace ntp_footer
diff --git a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_helper.h b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_helper.h index 2a16ae7..1aa328c 100644 --- a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_helper.h +++ b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_helper.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_UI_WEBUI_NEW_TAB_FOOTER_NEW_TAB_FOOTER_HELPER_H_ #include "chrome/browser/profiles/profile.h" +#include "content/public/browser/web_contents.h" #include "url/gurl.h" class Profile; @@ -13,6 +14,8 @@ namespace ntp_footer { // Returns whether `url` belongs to an extension NTP. bool IsExtensionNtp(const GURL& url, Profile* profile); +// Returns whether the extension attribution can be shown. +bool CanShowExtensionFooter(const GURL& url, Profile* profile); } // namespace ntp_footer #endif // CHROME_BROWSER_UI_WEBUI_NEW_TAB_FOOTER_NEW_TAB_FOOTER_HELPER_H_
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc b/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc index 2a14191..00f81e7 100644 --- a/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc +++ b/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc
@@ -22,8 +22,10 @@ #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_window/public/browser_window_interface.h" #include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h" +#include "chrome/browser/ui/tab_ui_helper.h" #include "chrome/browser/ui/tabs/alert/tab_alert.h" #include "chrome/browser/ui/tabs/organization/tab_declutter_controller.h" +#include "chrome/browser/ui/tabs/public/tab_features.h" #include "chrome/browser/ui/tabs/tab_utils.h" #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h" #include "chrome/browser/ui/tabs/test_util.h" @@ -969,6 +971,20 @@ TabStripModel* fake_tab_strip_model() { return tab_strip_model_.get(); } Profile* testing_profile() { return testing_profile_.get(); } + tabs::TabInterface* AppendBackgroundTab() { + std::unique_ptr<tabs::TabModel> tab_model = + std::make_unique<tabs::TabModel>( + content::WebContents::Create( + content::WebContents::CreateParams(testing_profile())), + fake_tab_strip_model()); + tabs::TabFeatures* const tab_features = tab_model->GetTabFeatures(); + tabs::TabInterface* const tab_interface = tab_model.get(); + tab_features->SetTabUIHelperForTesting( + std::make_unique<TabUIHelper>(*tab_interface)); + fake_tab_strip_model()->AppendTab(std::move(tab_model), false); + return tab_interface; + } + private: std::unique_ptr<TestingProfile> testing_profile_; base::test::ScopedFeatureList feature_list_; @@ -985,26 +1001,14 @@ // Create stale tabs. std::vector<tabs::TabInterface*> stale_tabs_raw_ptr; for (int i = 0; i < 4; ++i) { - std::unique_ptr<tabs::TabModel> tab_model = - std::make_unique<tabs::TabModel>( - content::WebContents::Create( - content::WebContents::CreateParams(testing_profile())), - fake_tab_strip_model()); - stale_tabs_raw_ptr.push_back(tab_model.get()); - fake_tab_strip_model()->AppendTab(std::move(tab_model), false); + stale_tabs_raw_ptr.push_back(AppendBackgroundTab()); } // Create duplicate tabs. std::map<GURL, std::vector<tabs::TabInterface*>> duplicate_tabs; GURL duplicate_tabs_url("https://duplicate_url.com"); for (int i = 0; i < 2; ++i) { - std::unique_ptr<tabs::TabModel> tab_model = - std::make_unique<tabs::TabModel>( - content::WebContents::Create( - content::WebContents::CreateParams(testing_profile())), - fake_tab_strip_model()); - duplicate_tabs[duplicate_tabs_url].push_back(tab_model.get()); - fake_tab_strip_model()->AppendTab(std::move(tab_model), false); + duplicate_tabs[duplicate_tabs_url].push_back(AppendBackgroundTab()); } EXPECT_CALL(*tab_declutter_controller(), GetStaleTabs()) @@ -1035,30 +1039,19 @@ // Create stale tabs. std::vector<tabs::TabInterface*> stale_tabs_raw_ptr; for (int i = 0; i < 4; ++i) { - std::unique_ptr<tabs::TabModel> tab_model = - std::make_unique<tabs::TabModel>( - content::WebContents::Create( - content::WebContents::CreateParams(testing_profile())), - fake_tab_strip_model()); - stale_tabs_raw_ptr.push_back(tab_model.get()); - fake_tab_strip_model()->AppendTab(std::move(tab_model), false); + stale_tabs_raw_ptr.push_back(AppendBackgroundTab()); } // Create duplicate tabs. std::map<GURL, std::vector<tabs::TabInterface*>> duplicate_tabs; GURL duplicate_tabs_url("https://duplicate_url.com"); for (int i = 0; i < 2; ++i) { - std::unique_ptr<tabs::TabModel> tab_model = - std::make_unique<tabs::TabModel>( - content::WebContents::Create( - content::WebContents::CreateParams(testing_profile())), - fake_tab_strip_model()); + tabs::TabInterface* const tab_interface = AppendBackgroundTab(); auto navigation_simulator = content::NavigationSimulator::CreateBrowserInitiated( - duplicate_tabs_url, tab_model->GetContents()); + duplicate_tabs_url, tab_interface->GetContents()); navigation_simulator->Commit(); - duplicate_tabs[duplicate_tabs_url].push_back(tab_model.get()); - fake_tab_strip_model()->AppendTab(std::move(tab_model), false); + duplicate_tabs[duplicate_tabs_url].push_back(tab_interface); } EXPECT_CALL(*tab_declutter_controller(), GetStaleTabs()) @@ -1091,25 +1084,13 @@ std::vector<tabs::TabInterface*> stale_tabs_raw_ptr; for (int i = 0; i < 4; ++i) { - std::unique_ptr<tabs::TabModel> tab_model = - std::make_unique<tabs::TabModel>( - content::WebContents::Create( - content::WebContents::CreateParams(testing_profile())), - fake_tab_strip_model()); - stale_tabs_raw_ptr.push_back(tab_model.get()); - fake_tab_strip_model()->AppendTab(std::move(tab_model), false); + stale_tabs_raw_ptr.push_back(AppendBackgroundTab()); } std::map<GURL, std::vector<tabs::TabInterface*>> duplicate_tabs; GURL duplicate_tabs_url("https://duplicate_url.com"); for (int i = 0; i < 2; ++i) { - std::unique_ptr<tabs::TabModel> tab_model = - std::make_unique<tabs::TabModel>( - content::WebContents::Create( - content::WebContents::CreateParams(testing_profile())), - fake_tab_strip_model()); - duplicate_tabs[duplicate_tabs_url].push_back(tab_model.get()); - fake_tab_strip_model()->AppendTab(std::move(tab_model), false); + duplicate_tabs[duplicate_tabs_url].push_back(AppendBackgroundTab()); } EXPECT_CALL(*tab_declutter_controller(), GetStaleTabs()) @@ -1133,31 +1114,19 @@ // Create 10 stale tabs. for (int i = 0; i < 10; ++i) { - std::unique_ptr<tabs::TabModel> tab_model = - std::make_unique<tabs::TabModel>( - content::WebContents::Create( - content::WebContents::CreateParams(testing_profile())), - fake_tab_strip_model()); - stale_tabs_raw_ptr.push_back(tab_model.get()); - fake_tab_strip_model()->AppendTab(std::move(tab_model), false); + stale_tabs_raw_ptr.push_back(AppendBackgroundTab()); } // Create duplicate tabs. std::map<GURL, std::vector<tabs::TabInterface*>> duplicate_tabs; GURL duplicate_tabs_url("https://duplicate_url.com"); for (int i = 0; i < 5; ++i) { - std::unique_ptr<tabs::TabModel> tab_model = - std::make_unique<tabs::TabModel>( - content::WebContents::Create( - content::WebContents::CreateParams(testing_profile())), - fake_tab_strip_model()); - + tabs::TabInterface* const tab_interface = AppendBackgroundTab(); auto navigation_simulator = content::NavigationSimulator::CreateBrowserInitiated( - duplicate_tabs_url, tab_model->GetContents()); + duplicate_tabs_url, tab_interface->GetContents()); navigation_simulator->Commit(); - duplicate_tabs[duplicate_tabs_url].push_back(tab_model.get()); - fake_tab_strip_model()->AppendTab(std::move(tab_model), false); + duplicate_tabs[duplicate_tabs_url].push_back(tab_interface); } EXPECT_CALL(*tab_declutter_controller(), GetStaleTabs()) @@ -1221,31 +1190,20 @@ // Create 10 stale tabs. for (int i = 0; i < 10; ++i) { - std::unique_ptr<tabs::TabModel> tab_model = - std::make_unique<tabs::TabModel>( - content::WebContents::Create( - content::WebContents::CreateParams(testing_profile())), - fake_tab_strip_model()); - stale_tabs_raw_ptr.push_back(tab_model.get()); - fake_tab_strip_model()->AppendTab(std::move(tab_model), false); + stale_tabs_raw_ptr.push_back(AppendBackgroundTab()); } // Create duplicate tabs. std::map<GURL, std::vector<tabs::TabInterface*>> duplicate_tabs; GURL duplicate_tabs_url("https://duplicate_url.com"); for (int i = 0; i < 5; ++i) { - std::unique_ptr<tabs::TabModel> tab_model = - std::make_unique<tabs::TabModel>( - content::WebContents::Create( - content::WebContents::CreateParams(testing_profile())), - fake_tab_strip_model()); + tabs::TabInterface* const tab_interface = AppendBackgroundTab(); auto navigation_simulator = content::NavigationSimulator::CreateBrowserInitiated( - duplicate_tabs_url, tab_model->GetContents()); + duplicate_tabs_url, tab_interface->GetContents()); navigation_simulator->Commit(); - duplicate_tabs[duplicate_tabs_url].push_back(tab_model.get()); - fake_tab_strip_model()->AppendTab(std::move(tab_model), false); + duplicate_tabs[duplicate_tabs_url].push_back(tab_interface); } EXPECT_CALL(*tab_declutter_controller(), GetStaleTabs())
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt index 72c9568..6c77e39 100644 --- a/chrome/build/android-arm32.pgo.txt +++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@ -chrome-android32-main-1747936607-13cfbcc65a4f112c07e4996fe9d478fe4d2bf491-69fb47b82a6b54185ef4d8fb55a4d5986f147002.profdata +chrome-android32-main-1747958116-71d90e08d2fa9a822583b99f4c96ec0d81aced54-27634fa381c6426758aa5ee8e41e07eb20de6241.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt index aab9c892..22fc2e0 100644 --- a/chrome/build/android-arm64.pgo.txt +++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@ -chrome-android64-main-1747939698-5ed854c0d3de178bffe4287d730129210f18d97e-81491c26d7e19d0b6fd34afdd80ed104d6fca73a.profdata +chrome-android64-main-1747953933-2c9850db71463d0ee7218eac1d206dc2de1c3eeb-f016a0d14d0e312bd1b35da74149506ea9d3c849.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 07e7e99..afb6534 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1747943845-4eca17c6187b57e881e4c677c7783458e390e7ae-0bf5f94cccb26bba96f5a138a90554069b08e752.profdata +chrome-mac-arm-main-1747958116-56b29ed03fabeb23985e90cf4cbb8c7dff663295-27634fa381c6426758aa5ee8e41e07eb20de6241.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index 8b13c22..51abc2bc 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1747914995-1a7d603e542c97b9fb3ee8057ff4e1f9c0f97cca-cdc6b2f711718e875f7f4dd35ce9bbf52c3704d5.profdata +chrome-win32-main-1747936607-0f2275ac7ccea9479fe0d01329add34967ac4d9d-69fb47b82a6b54185ef4d8fb55a4d5986f147002.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index d2ec0f5..4991595cd 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1747925918-75b78bae67e7e33a17c7c18ad1ea8f11cde84c1e-13567e7795a503b19f15caf7456ab3235f01670c.profdata +chrome-win64-main-1747936607-5a6137eb5450ca5d3616b870007034a682bdd0d1-69fb47b82a6b54185ef4d8fb55a4d5986f147002.profdata
diff --git a/chrome/common/extensions/api/experimental_actor.idl b/chrome/common/extensions/api/experimental_actor.idl index 1e42bc9..629ea273 100644 --- a/chrome/common/extensions/api/experimental_actor.idl +++ b/chrome/common/extensions/api/experimental_actor.idl
@@ -9,8 +9,10 @@ callback ClosureCallback = void(); interface Functions { - // Creates and starts a new task. By default, opens a new tab to - // about:blank that is ready for actions. + // Creates and starts a new task. By default, opens a new tab to + // about:blank that is ready for actions. The startTaskProto can + // optionally specify a tab_id to use an existing tab instead of creating + // a new one. // startTaskProto: encoded optimization_guide.proto.BrowserStartTask // startTaskcallback: // encoded optimization_guide.proto.BrowserStartTaskResult
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index 3b762714..a051c685 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -1704,11 +1704,16 @@ blink::WebRuntimeFeatures::EnableAdTagging(true); if (IsStandaloneContentExtensionProcess()) { - // These Web APIs are only exposed to workers in extensions. + // These Web API features are exposed in extensions. blink::WebRuntimeFeatures::EnableWebUSBOnServiceWorkers(true); #if !BUILDFLAG(IS_ANDROID) blink::WebRuntimeFeatures::EnableWebHIDOnServiceWorkers(true); #endif // !BUILDFLAG(IS_ANDROID) + if (blink::WebRuntimeFeatures::IsAIPromptAPIForExtensionEnabled() && + base::FeatureList::IsEnabled( + blink::features::kAIPromptAPIForExtension)) { + blink::WebRuntimeFeatures::EnableAIPromptAPI(true); + } blink::WebRuntimeFeatures::EnableAIPromptAPIForWorkers(true); blink::WebRuntimeFeatures::EnableAIRewriterAPIForWorkers(true); blink::WebRuntimeFeatures::EnableAISummarizationAPIForWorkers(true);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 7c56986..bd5d2923 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -2353,6 +2353,7 @@ "//chrome/browser/ui/user_education", "//chrome/browser/ui/views/bubble", "//chrome/browser/ui/views/download", + "//chrome/browser/ui/views/new_tab_footer:browser_tests", "//chrome/browser/ui/views/page_action:page_action", "//chrome/browser/ui/views/privacy_sandbox", "//chrome/browser/ui/views/side_panel", @@ -3484,6 +3485,7 @@ "../browser/ui/views/location_bar/intent_chip_button_browsertest.cc", "../browser/ui/views/location_bar/intent_chip_button_test_base.cc", "../browser/ui/views/location_bar/intent_chip_button_test_base.h", + "../browser/ui/views/media_router/cast_dialog_coordinator_browsertest.cc", "../browser/ui/views/passwords/password_change/password_change_ui_browsertest.cc", "../browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_browsertest.cc", "../browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc", @@ -8100,7 +8102,6 @@ "//chrome/browser/ui/tabs:tab_strip", "//chrome/browser/ui/tabs:test_support", "//chrome/browser/ui/tabs:unit_tests", - "//chrome/browser/ui/views/new_tab_footer:unit_tests", "//chrome/browser/ui/views/page_action:unit_tests", "//chrome/browser/ui/views/webid:test_support", "//chrome/browser/ui/webui:webui_util", @@ -10199,7 +10200,6 @@ "../browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc", "../browser/ui/views/location_bar/location_icon_view_unittest.cc", "../browser/ui/views/media_router/cast_dialog_access_code_cast_button_unittest.cc", - "../browser/ui/views/media_router/cast_dialog_coordinator_unittest.cc", "../browser/ui/views/media_router/cast_dialog_metrics_unittest.cc", "../browser/ui/views/media_router/cast_dialog_no_sinks_view_unittest.cc", "../browser/ui/views/media_router/cast_dialog_sink_button_unittest.cc", @@ -11386,6 +11386,7 @@ "../browser/ui/views/fullscreen_control/fullscreen_control_view_interactive_uitest.cc", "../browser/ui/views/keyboard_access_interactive_uitest.cc", "../browser/ui/views/location_bar/cookie_controls/cookie_controls_interactive_uitest.cc", + "../browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view_interactive_uitest.cc", "../browser/ui/views/location_bar/lens_overlay_page_action_icon_view_interactive_uitest.cc", "../browser/ui/views/location_bar/location_icon_view_interactive_uitest.cc", "../browser/ui/views/location_bar/merchant_trust_chip_button_interactive_uitest.cc",
diff --git a/chrome/test/data/extensions/api_test/runtime/uninstall_url/manifest.json b/chrome/test/data/extensions/api_test/runtime/uninstall_url/manifest.json index ecab261..0012e15 100644 --- a/chrome/test/data/extensions/api_test/runtime/uninstall_url/manifest.json +++ b/chrome/test/data/extensions/api_test/runtime/uninstall_url/manifest.json
@@ -5,8 +5,7 @@ "description": "Browser test for chrome.runtime.setUninstallURL", "permissions": [ "management", - "tabs", - "<all_urls>" + "tabs" ], "background": { "scripts": ["test.js"],
diff --git a/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.ts b/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.ts index d325475..806af17 100644 --- a/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.ts +++ b/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.ts
@@ -53,7 +53,7 @@ const searchInput = dialog.$.searchBox.getSearchInput(); assertTrue(!!searchInput); const whenFocusDone = eventToPromise('focus', searchInput); - dialog.destinationStore.startLoadAllDestinations(); + dialog.destinationStore!.startLoadAllDestinations(); dialog.show(); return whenFocusDone; }); @@ -66,7 +66,7 @@ const searchInput = searchBox.getSearchInput(); assertTrue(!!searchInput); const whenFocusDone = eventToPromise('focus', searchInput); - dialog.destinationStore.startLoadAllDestinations(); + dialog.destinationStore!.startLoadAllDestinations(); dialog.show(); return whenFocusDone .then(() => {
diff --git a/chrome/test/data/webui/print_preview/model_settings_availability_test.ts b/chrome/test/data/webui/print_preview/model_settings_availability_test.ts index 079cf6b..693479f 100644 --- a/chrome/test/data/webui/print_preview/model_settings_availability_test.ts +++ b/chrome/test/data/webui/print_preview/model_settings_availability_test.ts
@@ -15,6 +15,7 @@ let model: PrintPreviewModelElement; function simulateCapabilitiesChange(capabilities: Cdd) { + assertTrue(!!model.destination); model.destination.capabilities = capabilities; // In prod code, capabilities changes are detected by print-preview-app // which then calls updateSettingsFromDestination(). @@ -45,6 +46,7 @@ // These tests verify that the model correctly updates the settings // availability based on the destination and document info. test('copies', function() { + assertTrue(!!model.destination); assertTrue(model.getSetting('copies').available); // Set max copies to 1. @@ -75,6 +77,7 @@ }); test('collate', function() { + assertTrue(!!model.destination); assertTrue(model.getSetting('collate').available); // Remove collate capability. @@ -93,6 +96,8 @@ }); test('layout', async function() { + assertTrue(!!model.destination); + // Layout is available since the printer has the capability and the // document is set to modifiable. assertTrue(model.getSetting('layout').available); @@ -102,7 +107,7 @@ {option: [{type: 'PORTRAIT', is_default: true}]}, {option: [{type: 'LANDSCAPE', is_default: true}]}, ].forEach(layoutCap => { - const capabilities = getCddTemplate(model.destination.id).capabilities!; + const capabilities = getCddTemplate(model.destination!.id).capabilities!; capabilities.printer.page_orientation = layoutCap; // Layout section should now be hidden. simulateCapabilitiesChange(capabilities); @@ -129,6 +134,7 @@ }); test('color', function() { + assertTrue(!!model.destination); // Color is available since the printer has the capability. assertTrue(model.getSetting('color').available); @@ -172,7 +178,7 @@ colorCap: {option: [{type: 'CUSTOM_COLOR', vendor_id: '42'}]}, expectedValue: true, }].forEach(capabilityAndValue => { - const capabilities = getCddTemplate(model.destination.id).capabilities!; + const capabilities = getCddTemplate(model.destination!.id).capabilities!; capabilities.printer.color = capabilityAndValue.colorCap; simulateCapabilitiesChange(capabilities); assertFalse(model.getSetting('color').available); @@ -210,7 +216,7 @@ }, expectedValue: true, }].forEach(capabilityAndValue => { - const capabilities = getCddTemplate(model.destination.id).capabilities!; + const capabilities = getCddTemplate(model.destination!.id).capabilities!; capabilities.printer.color = capabilityAndValue.colorCap; simulateCapabilitiesChange(capabilities); assertEquals( @@ -221,12 +227,13 @@ function setSaveAsPdfDestination(): Promise<void> { const saveAsPdf = getSaveAsPdfDestination(); - saveAsPdf.capabilities = getCddTemplate(model.destination.id).capabilities; + saveAsPdf.capabilities = getCddTemplate(saveAsPdf.id).capabilities; model.destination = saveAsPdf; return microtasksFinished(); } test('media size', async function() { + assertTrue(!!model.destination); // Media size is available since the printer has the capability. assertTrue(model.getSetting('mediaSize').available); @@ -287,6 +294,7 @@ }); test('dpi', function() { + assertTrue(!!model.destination); // The settings are available since the printer has multiple DPI options. assertTrue(model.getSetting('dpi').available); @@ -424,6 +432,7 @@ assertTrue(model.getSetting('headerFooter').available); // Small paper sizes + assertTrue(!!model.destination); const capabilities = getCddTemplate(model.destination.id).capabilities!; capabilities.printer.media_size = { 'option': [ @@ -478,6 +487,7 @@ }); test('duplex', function() { + assertTrue(!!model.destination); assertTrue(model.getSetting('duplex').available); assertTrue(model.getSetting('duplexShortEdge').available);
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts index b2dc801b..685d8a18 100644 --- a/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts +++ b/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts
@@ -19,6 +19,16 @@ import {$$, assertNotStyle, assertStyle, createBackgroundImage, createTheme, createThirdPartyThemeInfo, installMock} from './test_support.js'; +const newTabPageTypes = [ + NewTabPageType.kFirstPartyWebUI, + NewTabPageType.kThirdPartyWebUI, + NewTabPageType.kThirdPartyRemote, + NewTabPageType.kExtension, + NewTabPageType.kIncognito, + NewTabPageType.kGuestMode, + NewTabPageType.kNone, +]; + suite('AppearanceTest', () => { let appearanceElement: AppearanceElement; let callbackRouterRemote: CustomizeChromePageRemote; @@ -619,6 +629,31 @@ }); }); + suite('NtpFooterEnabled', () => { + suiteSetup(() => { + loadTimeData.overrideValues({ + footerEnabled: true, + }); + }); + + newTabPageTypes.forEach((t) => { + test(`classic chrome button NTP type ${t}`, async () => { + // Arrange. + const theme = createTheme(); + theme.backgroundImage = createBackgroundImage('chrome://theme/foo'); + callbackRouterRemote.setTheme(theme); + callbackRouterRemote.attachedTabStateUpdated(t); + await microtasksFinished(); + + // Assert. + assertEquals( + t === NewTabPageType.kFirstPartyWebUI || + t === NewTabPageType.kThirdPartyWebUI, + !appearanceElement.$.setClassicChromeButton.hidden); + }); + }); + }); + test('source tab type should update the content', async () => { const idsControlledByIsSourceTabFirstPartyNtp = [ '#editButtonsContainer', @@ -632,21 +667,10 @@ '#chromeColors', '#followThemeToggle', '#followThemeToggleControl', - '#setClassicChromeButton', '#editThemeButton', '#editThemeIcon', ]; - const newTabPageTypes = [ - NewTabPageType.kFirstPartyWebUI, - NewTabPageType.kThirdPartyWebUI, - NewTabPageType.kThirdPartyRemote, - NewTabPageType.kExtension, - NewTabPageType.kIncognito, - NewTabPageType.kGuestMode, - NewTabPageType.kNone, - ]; - const checkIdsVisibility = (sourceTabType: NewTabPageType) => { idsControlledByIsSourceTabFirstPartyNtp.forEach( id => assertEquals(
diff --git a/chromeos/ash/components/boca/babelorca/fakes/fake_caption_controller_delegate.cc b/chromeos/ash/components/boca/babelorca/fakes/fake_caption_controller_delegate.cc index 154d564e..6a3c7c7 100644 --- a/chromeos/ash/components/boca/babelorca/fakes/fake_caption_controller_delegate.cc +++ b/chromeos/ash/components/boca/babelorca/fakes/fake_caption_controller_delegate.cc
@@ -14,6 +14,7 @@ #include "components/live_caption/caption_bubble_controller.h" #include "components/live_caption/caption_bubble_settings.h" #include "components/live_caption/views/caption_bubble_model.h" +#include "components/live_caption/views/translation_view_wrapper_base.h" #include "components/prefs/pref_service.h" #include "media/mojo/mojom/speech_recognition.mojom.h" #include "media/mojo/mojom/speech_recognition_result.h" @@ -73,7 +74,8 @@ std::unique_ptr<captions::CaptionBubbleController> FakeCaptionControllerDelegate::CreateCaptionBubbleController( captions::CaptionBubbleSettings*, - const std::string&) { + const std::string&, + std::unique_ptr<captions::TranslationViewWrapperBase>) { caption_bubble_alive_ = true; ++create_bubble_controller_count_; return std::make_unique<FakeCaptionBubbleController>(this);
diff --git a/chromeos/ash/components/boca/babelorca/fakes/fake_caption_controller_delegate.h b/chromeos/ash/components/boca/babelorca/fakes/fake_caption_controller_delegate.h index eece7a6..183fb92 100644 --- a/chromeos/ash/components/boca/babelorca/fakes/fake_caption_controller_delegate.h +++ b/chromeos/ash/components/boca/babelorca/fakes/fake_caption_controller_delegate.h
@@ -17,6 +17,7 @@ namespace captions { class CaptionBubbleController; class CaptionBubbleSettings; +class TranslationViewWrapperBase; } // namespace captions namespace media { @@ -38,8 +39,10 @@ ~FakeCaptionControllerDelegate() override; std::unique_ptr<captions::CaptionBubbleController> - CreateCaptionBubbleController(captions::CaptionBubbleSettings*, - const std::string&) override; + CreateCaptionBubbleController( + captions::CaptionBubbleSettings*, + const std::string&, + std::unique_ptr<captions::TranslationViewWrapperBase>) override; void AddCaptionStyleObserver(ui::NativeThemeObserver* observer) override;
diff --git a/chromeos/ash/resources/internal b/chromeos/ash/resources/internal index f6bfe79..7986129 160000 --- a/chromeos/ash/resources/internal +++ b/chromeos/ash/resources/internal
@@ -1 +1 @@ -Subproject commit f6bfe79494ac7266c15b9139f25377ce1e59b22c +Subproject commit 79861294509d9038645cd39ca828edd93abaf37b
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni index 4f786b2..f8c6598 100644 --- a/chromeos/tast_control.gni +++ b/chromeos/tast_control.gni
@@ -105,6 +105,9 @@ # b/409349162 "inputs.PhysicalKeyboardKoreanTyping@jacuzzi", + # b/419598394 + "dlp.DataLeakPreventionRulesListFilesUSB.ash_blocked@brya", + # READ COMMENT AT TOP BEFORE ADDING NEW TESTS HERE. ]
diff --git a/clank b/clank index 890fb2ea..c2910b6 160000 --- a/clank +++ b/clank
@@ -1 +1 @@ -Subproject commit 890fb2eabfdafcda1830d1aaf97ba4bf0c52866e +Subproject commit c2910b6472c3849f25d534e1a00fe9691acf9f8d
diff --git a/components/autofill/core/common/autofill_payments_features.cc b/components/autofill/core/common/autofill_payments_features.cc index 39270fad..de4f002 100644 --- a/components/autofill/core/common/autofill_payments_features.cc +++ b/components/autofill/core/common/autofill_payments_features.cc
@@ -75,7 +75,7 @@ // When enabled, card benefit source will be synced to Chrome clients. BASE_FEATURE(kAutofillEnableCardBenefitsSourceSync, "AutofillEnableCardBenefitsSourceSync", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); // When enabled, Chrome will show metadata along with other card information // when the virtual card is presented to users. @@ -162,6 +162,12 @@ "AutofillEnableLogFormEventsToAllParsedFormTypes", base::FEATURE_DISABLED_BY_DEFAULT); +// When enabled, virtual card downstream enrollment will support multiple +// requests at a time. +BASE_FEATURE(kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment, + "AutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment", + base::FEATURE_DISABLED_BY_DEFAULT); + // When enabled, the card benefits toggle in settings will show updated text. BASE_FEATURE(kAutofillEnableNewCardBenefitsToggleText, "AutofillEnableNewCardBenefitsToggleText",
diff --git a/components/autofill/core/common/autofill_payments_features.h b/components/autofill/core/common/autofill_payments_features.h index 3017c61..07fff5ce 100644 --- a/components/autofill/core/common/autofill_payments_features.h +++ b/components/autofill/core/common/autofill_payments_features.h
@@ -58,6 +58,9 @@ COMPONENT_EXPORT(AUTOFILL) BASE_DECLARE_FEATURE(kAutofillEnableLogFormEventsToAllParsedFormTypes); COMPONENT_EXPORT(AUTOFILL) +BASE_DECLARE_FEATURE( + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment); +COMPONENT_EXPORT(AUTOFILL) BASE_DECLARE_FEATURE(kAutofillEnableNewCardBenefitsToggleText); COMPONENT_EXPORT(AUTOFILL) BASE_DECLARE_FEATURE(kAutofillEnableNewFopDisplayDesktop);
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 b00bdcd..997b6c0 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
@@ -5,9 +5,13 @@ package org.chromium.components.browser_ui.site_settings; import android.content.Context; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import org.chromium.base.ApiCompatibilityUtils; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; +import org.chromium.components.browser_ui.styles.SemanticColorUtils; import org.chromium.components.content_settings.ContentSettingsType; import org.chromium.components.permissions.OsAdditionalSecurityPermissionUtil; import org.chromium.content_public.browser.BrowserContextHandle; @@ -36,4 +40,15 @@ var provider = OsAdditionalSecurityPermissionUtil.getProviderInstance(); return (provider == null) ? null : provider.getJavascriptOptimizerMessage(context); } + + @Override + Drawable getDisabledInAndroidIcon(Context context) { + Drawable icon = + ApiCompatibilityUtils.getDrawable( + context.getResources(), R.drawable.secured_by_brand_shield_24); + icon.mutate(); + int disabledColor = SemanticColorUtils.getDefaultControlColorActive(context); + icon.setColorFilter(disabledColor, PorterDuff.Mode.SRC_IN); + return icon; + } }
diff --git a/components/enterprise/connectors/core/realtime_reporting_client_base.cc b/components/enterprise/connectors/core/realtime_reporting_client_base.cc index 8dba204..47820e42 100644 --- a/components/enterprise/connectors/core/realtime_reporting_client_base.cc +++ b/components/enterprise/connectors/core/realtime_reporting_client_base.cc
@@ -4,6 +4,8 @@ #include "components/enterprise/connectors/core/realtime_reporting_client_base.h" +#include <ctime> + #include "base/containers/contains.h" #include "base/containers/to_value_list.h" #include "base/i18n/time_formatting.h" @@ -186,9 +188,7 @@ // If the timestamp is not set, it's a realtime event so use current time. if (!event.has_time()) { - int64_t timestamp_millis = base::Time::Now().InMillisecondsSinceUnixEpoch(); - event.mutable_time()->set_seconds(timestamp_millis / 1000); - event.mutable_time()->set_nanos((timestamp_millis % 1000) * 1000000); + *event.mutable_time() = ToProtoTimestamp(base::Time::Now()); } #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
diff --git a/components/enterprise/connectors/core/reporting_utils.cc b/components/enterprise/connectors/core/reporting_utils.cc index 71e3985..1122f58 100644 --- a/components/enterprise/connectors/core/reporting_utils.cc +++ b/components/enterprise/connectors/core/reporting_utils.cc
@@ -92,6 +92,14 @@ {kMaskedUsername, base::UTF16ToUTF8(username.substr(pos))}); } +::google3_protos::Timestamp ToProtoTimestamp(base::Time time) { + int64_t millis = time.InMillisecondsFSinceUnixEpoch(); + ::google3_protos::Timestamp timestamp; + timestamp.set_seconds(millis / 1000); + timestamp.set_nanos((millis % 1000) * 1000000); + return timestamp; +} + std::unique_ptr<url_matcher::URLMatcher> CreateURLMatcherForOptInEvent( const enterprise_connectors::ReportingSettings& settings, const char* event_type) {
diff --git a/components/enterprise/connectors/core/reporting_utils.h b/components/enterprise/connectors/core/reporting_utils.h index 748c95f1..c2e0b9cc 100644 --- a/components/enterprise/connectors/core/reporting_utils.h +++ b/components/enterprise/connectors/core/reporting_utils.h
@@ -25,6 +25,9 @@ // username should be masked. std::string MaskUsername(const std::u16string& username); +// Convert base::Time to Timestamp proto. +::google3_protos::Timestamp ToProtoTimestamp(base::Time); + // Verify if the given `matcher` matches the `url`. bool IsUrlMatched(url_matcher::URLMatcher* matcher, const GURL& url);
diff --git a/components/js_injection/browser/navigation_listener_browsertest.cc b/components/js_injection/browser/navigation_listener_browsertest.cc index 3dbc4f1..5c673d2 100644 --- a/components/js_injection/browser/navigation_listener_browsertest.cc +++ b/components/js_injection/browser/navigation_listener_browsertest.cc
@@ -5,6 +5,7 @@ #include <string> #include "base/json/json_writer.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "components/js_injection/browser/js_communication_host.h" #include "components/js_injection/browser/navigation_web_message_sender.h"
diff --git a/components/lens/lens_features.cc b/components/lens/lens_features.cc index 51ea0191..5cd4968 100644 --- a/components/lens/lens_features.cc +++ b/components/lens/lens_features.cc
@@ -484,6 +484,11 @@ true}; constexpr base::FeatureParam<bool> + kLensOverlayVisualSelectionUpdatesEnableRegionSelectedGlow{ + &kLensOverlayVisualSelectionUpdates, "enable-region-selected-glow", + true}; + +constexpr base::FeatureParam<bool> kLensOverlayVisualSelectionUpdatesCsbThumbnail{ &kLensOverlayVisualSelectionUpdates, "enable-csb-thumbnail", true}; @@ -1037,6 +1042,11 @@ kLensOverlayVisualSelectionUpdatesEnableGradientRegionStroke.Get(); } +bool GetVisualSelectionUpdatesEnableRegionSelectedGlow() { + return base::FeatureList::IsEnabled(kLensOverlayVisualSelectionUpdates) && + kLensOverlayVisualSelectionUpdatesEnableRegionSelectedGlow.Get(); +} + bool GetVisualSelectionUpdatesEnableCsbThumbnail() { return base::FeatureList::IsEnabled(kLensOverlayVisualSelectionUpdates) && kLensOverlayVisualSelectionUpdatesCsbThumbnail.Get();
diff --git a/components/lens/lens_features.h b/components/lens/lens_features.h index 1084c8e..9189254 100644 --- a/components/lens/lens_features.h +++ b/components/lens/lens_features.h
@@ -773,6 +773,10 @@ COMPONENT_EXPORT(LENS_FEATURES) extern bool GetVisualSelectionUpdatesEnableGradientRegionStroke(); +// Whether to enable the region selected glow for the visual selection updates. +COMPONENT_EXPORT(LENS_FEATURES) +extern bool GetVisualSelectionUpdatesEnableRegionSelectedGlow(); + // Whether to enable the thumbnail in the contextual searchbox. COMPONENT_EXPORT(LENS_FEATURES) extern bool GetVisualSelectionUpdatesEnableCsbThumbnail();
diff --git a/components/lens/lens_overlay_invocation_source.h b/components/lens/lens_overlay_invocation_source.h index 0b532e2..57f6c1e5 100644 --- a/components/lens/lens_overlay_invocation_source.h +++ b/components/lens/lens_overlay_invocation_source.h
@@ -45,7 +45,17 @@ // The context menu when long pressing a web image. kContextMenu = 8, - kMaxValue = kContextMenu + // The Lens suggestion in the omnibox. + kOmniboxPageAction = 9, + + // The contextual suggestions in the omnibox that take you directly to + // contextual answers in the side panel. + kOmniboxContextualSuggestion = 10, + + // The Lens homework action chip in the omnibox. + kHomeworkActionChip = 11, + + kMaxValue = kHomeworkActionChip }; // LINT.ThenChange(//tools/metrics/histograms/metadata/lens/enums.xml:LensOverlayInvocationSource) // When adding a value here, also update:
diff --git a/components/lens/lens_overlay_metrics.cc b/components/lens/lens_overlay_metrics.cc index 3f10ea9..a2a1da28 100644 --- a/components/lens/lens_overlay_metrics.cc +++ b/components/lens/lens_overlay_metrics.cc
@@ -36,6 +36,12 @@ return "LVFGallery"; case LensOverlayInvocationSource::kContextMenu: return "ContextMenu"; + case LensOverlayInvocationSource::kOmniboxPageAction: + return "OmniboxPageAction"; + case LensOverlayInvocationSource::kOmniboxContextualSuggestion: + return "OmniboxContextualSuggestion"; + case LensOverlayInvocationSource::kHomeworkActionChip: + return "HomeworkActionChip"; } } @@ -388,8 +394,17 @@ event.SetFindInPage(time_to_first_interaction.InMilliseconds()); break; case lens::LensOverlayInvocationSource::kOmnibox: + // TODO(crbug.com/419051875): Add separate UKM for homework action chip. + case lens::LensOverlayInvocationSource::kHomeworkActionChip: event.SetOmnibox(time_to_first_interaction.InMilliseconds()); break; + case lens::LensOverlayInvocationSource::kOmniboxPageAction: + event.SetOmniboxPageAction(time_to_first_interaction.InMilliseconds()); + break; + case lens::LensOverlayInvocationSource::kOmniboxContextualSuggestion: + event.SetOmniboxContextualSuggestion( + time_to_first_interaction.InMilliseconds()); + break; } event.SetFirstInteractionType(static_cast<int64_t>(first_interaction_type)) .Record(ukm::UkmRecorder::Get());
diff --git a/components/live_caption/caption_bubble_controller.h b/components/live_caption/caption_bubble_controller.h index 909c28e0..6a1b482 100644 --- a/components/live_caption/caption_bubble_controller.h +++ b/components/live_caption/caption_bubble_controller.h
@@ -22,6 +22,7 @@ class CaptionBubbleContext; class CaptionBubbleSettings; +class TranslationViewWrapperBase; /////////////////////////////////////////////////////////////////////////////// // Caption Bubble Controller @@ -41,7 +42,8 @@ static std::unique_ptr<CaptionBubbleController> Create( CaptionBubbleSettings* caption_bubble_settings, - const std::string& application_locale); + const std::string& application_locale, + std::unique_ptr<TranslationViewWrapperBase> translation_view_wrapper); // Called when the speech service has an error. This should be part of // `CaptionControllerBase::Listener`, but the callbacks make this tricky.
diff --git a/components/live_caption/caption_controller_base.cc b/components/live_caption/caption_controller_base.cc index 1cb7c6a..f08fb44 100644 --- a/components/live_caption/caption_controller_base.cc +++ b/components/live_caption/caption_controller_base.cc
@@ -12,6 +12,8 @@ #include "components/live_caption/caption_bubble_controller.h" #include "components/live_caption/caption_util.h" #include "components/live_caption/pref_names.h" +#include "components/live_caption/views/translation_view_wrapper.h" +#include "components/live_caption/views/translation_view_wrapper_base.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_service.h" #include "ui/native_theme/native_theme.h" @@ -36,9 +38,12 @@ std::unique_ptr<CaptionBubbleController> CreateCaptionBubbleController( CaptionBubbleSettings* caption_bubble_settings, - const std::string& application_locale) override { + const std::string& application_locale, + std::unique_ptr<TranslationViewWrapperBase> translation_view_wrapper) + override { return CaptionBubbleController::Create(caption_bubble_settings, - application_locale); + application_locale, + std::move(translation_view_wrapper)); } void AddCaptionStyleObserver(ui::NativeThemeObserver* observer) override { @@ -84,7 +89,8 @@ is_ui_constructed_ = true; auto controller = delegate_->CreateCaptionBubbleController( - caption_bubble_settings(), application_locale_); + caption_bubble_settings(), application_locale_, + CreateTranslationViewWrapper()); caption_bubble_controller_ = controller.get(); AddListener(std::move(controller)); @@ -138,6 +144,11 @@ return caption_bubble_controller_.get(); } +std::unique_ptr<TranslationViewWrapperBase> +CaptionControllerBase::CreateTranslationViewWrapper() { + return std::make_unique<TranslationViewWrapper>(caption_bubble_settings()); +} + void CaptionControllerBase::OnCaptionStyleUpdated() { if (!caption_bubble_controller_) { return;
diff --git a/components/live_caption/caption_controller_base.h b/components/live_caption/caption_controller_base.h index a4575958..96f2f907 100644 --- a/components/live_caption/caption_controller_base.h +++ b/components/live_caption/caption_controller_base.h
@@ -27,6 +27,7 @@ class CaptionBubbleContext; class CaptionBubbleController; class CaptionBubbleSettings; +class TranslationViewWrapperBase; class CaptionControllerBase : public ui::NativeThemeObserver { public: @@ -40,7 +41,9 @@ virtual std::unique_ptr<CaptionBubbleController> CreateCaptionBubbleController( CaptionBubbleSettings* caption_bubble_settings, - const std::string& application_locale) = 0; + const std::string& application_locale, + std::unique_ptr<TranslationViewWrapperBase> + translation_view_wrapper) = 0; virtual void AddCaptionStyleObserver(ui::NativeThemeObserver* observer) = 0; @@ -133,6 +136,9 @@ PrefChangeRegistrar* pref_change_registrar() const; CaptionBubbleController* caption_bubble_controller() const; + virtual std::unique_ptr<TranslationViewWrapperBase> + CreateTranslationViewWrapper(); + private: virtual CaptionBubbleSettings* caption_bubble_settings() = 0;
diff --git a/components/live_caption/caption_controller_base_unittest.cc b/components/live_caption/caption_controller_base_unittest.cc index b2a66b017..560afc1 100644 --- a/components/live_caption/caption_controller_base_unittest.cc +++ b/components/live_caption/caption_controller_base_unittest.cc
@@ -10,6 +10,7 @@ #include "components/live_caption/caption_bubble_context.h" #include "components/live_caption/caption_bubble_controller.h" #include "components/live_caption/pref_names.h" +#include "components/live_caption/views/translation_view_wrapper_base.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "components/prefs/testing_pref_service.h" @@ -136,14 +137,16 @@ public: explicit MockCaptionControllerDelegate( std::unique_ptr<CaptionBubbleController> bubble_controller) { - EXPECT_CALL(*this, CreateCaptionBubbleController(_, _)) + EXPECT_CALL(*this, CreateCaptionBubbleController(_, _, _)) .WillOnce(Return(std::move(bubble_controller))); } ~MockCaptionControllerDelegate() override = default; MOCK_METHOD(std::unique_ptr<CaptionBubbleController>, CreateCaptionBubbleController, - (CaptionBubbleSettings*, const std::string&), + (CaptionBubbleSettings*, + const std::string&, + std::unique_ptr<TranslationViewWrapperBase>), (override)); void AddCaptionStyleObserver(ui::NativeThemeObserver*) override {}
diff --git a/components/live_caption/live_caption_controller_unittest.cc b/components/live_caption/live_caption_controller_unittest.cc index ea16d2b..b746717 100644 --- a/components/live_caption/live_caption_controller_unittest.cc +++ b/components/live_caption/live_caption_controller_unittest.cc
@@ -15,6 +15,7 @@ #include "components/live_caption/caption_bubble_context.h" #include "components/live_caption/caption_bubble_controller.h" #include "components/live_caption/pref_names.h" +#include "components/live_caption/views/translation_view_wrapper_base.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_service.h" #include "components/prefs/testing_pref_service.h" @@ -37,7 +38,9 @@ MOCK_METHOD(std::unique_ptr<CaptionBubbleController>, CreateCaptionBubbleController, - (CaptionBubbleSettings*, const std::string&), + (CaptionBubbleSettings*, + const std::string&, + std::unique_ptr<TranslationViewWrapperBase>), (override)); void AddCaptionStyleObserver(ui::NativeThemeObserver*) override {}
diff --git a/components/live_caption/views/caption_bubble_controller_views.cc b/components/live_caption/views/caption_bubble_controller_views.cc index 5cf15f0..b7216b8 100644 --- a/components/live_caption/views/caption_bubble_controller_views.cc +++ b/components/live_caption/views/caption_bubble_controller_views.cc
@@ -29,10 +29,11 @@ // Static std::unique_ptr<CaptionBubbleController> CaptionBubbleController::Create( CaptionBubbleSettings* caption_bubble_settings, - const std::string& application_locale) { + const std::string& application_locale, + std::unique_ptr<TranslationViewWrapperBase> translation_view_wrapper) { return std::make_unique<CaptionBubbleControllerViews>( caption_bubble_settings, application_locale, - std::make_unique<TranslationViewWrapper>(caption_bubble_settings)); + std::move(translation_view_wrapper)); } CaptionBubbleControllerViews::CaptionBubbleControllerViews(
diff --git a/components/media_effects/OWNERS b/components/media_effects/OWNERS index d4987c9..58c950b 100644 --- a/components/media_effects/OWNERS +++ b/components/media_effects/OWNERS
@@ -1,4 +1,3 @@ bryantchandler@chromium.org mfoltz@chromium.org -bialpio@chromium.org ahmedmoussa@google.com
diff --git a/components/messages/android/DEPS b/components/messages/android/DEPS index 3c1bde2..32a168c 100644 --- a/components/messages/android/DEPS +++ b/components/messages/android/DEPS
@@ -8,7 +8,7 @@ "+components/browser_ui/widget/android", "+components/browser_ui/styles/android", "+third_party/skia/include", - "+ui/accessibility", + "+ui/accessibility/android", "+ui/android", "+ui/gfx/android", ]
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java index 2427d78..b0ed817f 100644 --- a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java +++ b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java
@@ -5,15 +5,20 @@ package org.chromium.components.messages; import android.animation.Animator; +import android.content.Context; import android.content.res.Resources; import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import androidx.annotation.VisibleForTesting; import androidx.core.view.ViewCompat; +import androidx.core.view.accessibility.AccessibilityEventCompat; import org.chromium.base.supplier.Supplier; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; +import org.chromium.components.messages.MessageFeatureMap.AccessibilityEventInvestigationGroup; import org.chromium.components.messages.MessageStateHandler.Position; import org.chromium.ui.accessibility.AccessibilityState; import org.chromium.ui.listmenu.ListMenuHost.PopupMenuShownListener; @@ -193,6 +198,7 @@ () -> { setOnTouchRunnable(null); setOnTitleChanged(null); + sendPaneChangeAccessibilityEvent(/* isShowing= */ false); messageHidden.run(); }); } @@ -220,6 +226,82 @@ msg = mView.getResources().getString(R.string.message_new_actions_available); } ViewCompat.setAccessibilityPaneTitle(mParentView, msg); + sendPaneChangeAccessibilityEvent(/* isShowing= */ true); + } + + /** + * Sends accessibility events for pane appearance/disappearance when the message is shown/hidden + * respectively. This should ideally move accessibility focus automatically to/out of the + * message view as applicable. + * + * @param isShowing Whether the message is visible. {@code true} if shown, {@code false} if + * hidden. + */ + @SuppressWarnings("WrongConstant") + private void sendPaneChangeAccessibilityEvent(boolean isShowing) { + // We will only send an AccessibilityEvent when running the + // MessagesAccessibilityEventInvestigations experiment since this is known to crash in some + // cases. The experiment will provide more hints to crash root cause and possible solutions. + if (!MessageFeatureList.isMessagesAccessibilityEventInvestigationsEnabled()) { + return; + } + + AccessibilityEvent event = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + if (isShowing) { + event.setContentChangeTypes(AccessibilityEventCompat.CONTENT_CHANGE_TYPE_PANE_APPEARED); + } else { + event.setContentChangeTypes( + AccessibilityEventCompat.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED); + } + + // The MessagesAccessibilityEventInvestigations has various arms to try potential solutions. + int approach = MessageFeatureList.getMessagesAccessibilityEventInvestigationsParam(); + switch (approach) { + case AccessibilityEventInvestigationGroup.ENABLED_BASELINE: + // Case 1 = "EnabledBaseline" group. This is our baseline group, which acts similar + // to a control group for the experiment. This is the ideal implementation that we + // would expect not to crash, and we want to compare the other implementations + // against this one. + if (AccessibilityState.isAnyAccessibilityServiceEnabled()) { + mView.requestSendAccessibilityEvent(mView, event); + } + break; + case AccessibilityEventInvestigationGroup.ENABLED_WITH_ACCESSIBILITY_STATE: + // Case 2 = "EnabledWithAccessibilityState" group. For this group, we will send the + // event through the AccessibilityState instead. + AccessibilityState.sendAccessibilityEvent(event); + break; + case AccessibilityEventInvestigationGroup.ENABLED_WITH_RESTRICTIVE_SERVICE_CHECK: + // Case 3 = "EnabledWithRestrictiveServiceCheck" group. For this group, we will send + // the event as we normally would, but for a more restrictive group of services. + if (AccessibilityState.isComplexUserInteractionServiceEnabled()) { + mView.requestSendAccessibilityEvent(mView, event); + } + break; + case AccessibilityEventInvestigationGroup.ENABLED_WITH_MASK_CHECK: + // Case 4 = "EnabledWithMaskCheck" group. For this group, we will not check the + // enabled services, but instead look at the requested event types, and only send + // the event if some service has requested its type. + if (AccessibilityState.relevantEventTypesForCurrentServices() + .contains(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)) { + mView.requestSendAccessibilityEvent(mView, event); + } + break; + case AccessibilityEventInvestigationGroup.ENABLED_WITH_DIRECT_QUERY: + // Case 5 = "EnabledWithDirectQuery" group. For this group, we will directly check + // the current accessibility state against the framework before sending the event. + AccessibilityManager manager = + (AccessibilityManager) + mView.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); + if (manager.isEnabled()) { + mView.requestSendAccessibilityEvent(mView, event); + } + break; + default: + // For any other value (default or bad param), we do not want to send any event. + break; + } } private void setOnTitleChanged(@Nullable Runnable runnable) {
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageFeatureList.java b/components/messages/android/java/src/org/chromium/components/messages/MessageFeatureList.java index 0b34805..038b339 100644 --- a/components/messages/android/java/src/org/chromium/components/messages/MessageFeatureList.java +++ b/components/messages/android/java/src/org/chromium/components/messages/MessageFeatureList.java
@@ -5,6 +5,7 @@ package org.chromium.components.messages; import org.chromium.build.annotations.NullMarked; +import org.chromium.components.messages.MessageFeatureMap.AccessibilityEventInvestigationGroup; /** * Lists base::Features that can be accessed through {@link MessageFeatureMap}. @@ -21,11 +22,12 @@ public static final String MESSAGES_ANDROID_EXTRA_HISTOGRAMS = "MessagesAndroidExtraHistograms"; public static final String MESSAGES_CLOSE_BUTTON = "MessagesCloseButton"; - public static int getMessagesAccessibilityEventInvestigationsParam() { + public static @AccessibilityEventInvestigationGroup int + getMessagesAccessibilityEventInvestigationsParam() { return getFieldTrialParamByFeatureAsInt( MESSAGES_ACCESSIBILITY_EVENT_INVESTIGATIONS, "messages_accessibility_events_investigations_param", - 0); + AccessibilityEventInvestigationGroup.DEFAULT); } public static boolean isMessagesAccessibilityEventInvestigationsEnabled() {
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageFeatureMap.java b/components/messages/android/java/src/org/chromium/components/messages/MessageFeatureMap.java index 987d29e..3059f49f 100644 --- a/components/messages/android/java/src/org/chromium/components/messages/MessageFeatureMap.java +++ b/components/messages/android/java/src/org/chromium/components/messages/MessageFeatureMap.java
@@ -4,18 +4,42 @@ package org.chromium.components.messages; +import androidx.annotation.IntDef; + import org.jni_zero.JNINamespace; import org.jni_zero.NativeMethods; import org.chromium.base.FeatureMap; import org.chromium.build.annotations.NullMarked; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** Java accessor for base::Features listed in {@link MessageFeatureList} */ @JNINamespace("messages") @NullMarked public final class MessageFeatureMap extends FeatureMap { private static final MessageFeatureMap sInstance = new MessageFeatureMap(); + /** Int values for the param of the MessagesAccessibilityEventInvestigations experiment. */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + AccessibilityEventInvestigationGroup.DEFAULT, + AccessibilityEventInvestigationGroup.ENABLED_BASELINE, + AccessibilityEventInvestigationGroup.ENABLED_WITH_ACCESSIBILITY_STATE, + AccessibilityEventInvestigationGroup.ENABLED_WITH_RESTRICTIVE_SERVICE_CHECK, + AccessibilityEventInvestigationGroup.ENABLED_WITH_MASK_CHECK, + AccessibilityEventInvestigationGroup.ENABLED_WITH_DIRECT_QUERY, + }) + public @interface AccessibilityEventInvestigationGroup { + int DEFAULT = 0; + int ENABLED_BASELINE = 1; + int ENABLED_WITH_ACCESSIBILITY_STATE = 2; + int ENABLED_WITH_RESTRICTIVE_SERVICE_CHECK = 3; + int ENABLED_WITH_MASK_CHECK = 4; + int ENABLED_WITH_DIRECT_QUERY = 5; + } + // Do not instantiate this class. private MessageFeatureMap() {}
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc index f9ee3617..a34b48c 100644 --- a/components/omnibox/browser/autocomplete_controller.cc +++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -1438,20 +1438,22 @@ MlRerank(old_result); + // If the entrypoints aren't visible, then Lens is active and contextual + // suggestions shouldn't be shown. + const bool is_lens_active = + !autocomplete_provider_client()->AreLensEntrypointsVisible(); if (update_type == UpdateType::kSyncPass || update_type == UpdateType::kAsyncPass || update_type == UpdateType::kLastAsyncPassExceptDoc) { - internal_result_.SortAndCull( - input_, template_url_service_, triggered_feature_service_, - autocomplete_provider_client()->IsLensEnabled(), - old_result.default_match_to_preserve); + internal_result_.SortAndCull(input_, template_url_service_, + triggered_feature_service_, is_lens_active, + old_result.default_match_to_preserve); internal_result_.TransferOldMatches(input_, &old_result.matches_to_transfer); } internal_result_.SortAndCull(input_, template_url_service_, - triggered_feature_service_, - autocomplete_provider_client()->IsLensEnabled(), + triggered_feature_service_, is_lens_active, old_result.default_match_to_preserve); if (update_type == UpdateType::kSyncPass) {
diff --git a/components/omnibox/browser/autocomplete_provider_client.cc b/components/omnibox/browser/autocomplete_provider_client.cc index a0c5423..02af4c0 100644 --- a/components/omnibox/browser/autocomplete_provider_client.cc +++ b/components/omnibox/browser/autocomplete_provider_client.cc
@@ -49,6 +49,10 @@ return false; } +bool AutocompleteProviderClient::AreLensEntrypointsVisible() const { + return false; +} + bool AutocompleteProviderClient::in_background_state() const { return false; }
diff --git a/components/omnibox/browser/autocomplete_provider_client.h b/components/omnibox/browser/autocomplete_provider_client.h index d3fe88ad..d4bfca71 100644 --- a/components/omnibox/browser/autocomplete_provider_client.h +++ b/components/omnibox/browser/autocomplete_provider_client.h
@@ -228,6 +228,10 @@ // Returns true if the current profile is eligible for Lens. virtual bool IsLensEnabled() const; + // Returns true if the Lens entrypoints can be shown to the user. That is if + // Lens is not already active. + virtual bool AreLensEntrypointsVisible() const; + // Returns whether the app is currently in the background state (Mobile only). virtual bool in_background_state() const;
diff --git a/components/omnibox/browser/mock_autocomplete_provider_client.h b/components/omnibox/browser/mock_autocomplete_provider_client.h index afcf96f4..f6ba07ac 100644 --- a/components/omnibox/browser/mock_autocomplete_provider_client.h +++ b/components/omnibox/browser/mock_autocomplete_provider_client.h
@@ -153,6 +153,7 @@ MOCK_CONST_METHOD0(IsHistoryEmbeddingsEnabled, bool()); MOCK_CONST_METHOD0(IsHistoryEmbeddingsSettingVisible, bool()); MOCK_CONST_METHOD0(IsLensEnabled, bool()); + MOCK_CONST_METHOD0(AreLensEntrypointsVisible, bool()); MOCK_CONST_METHOD1(GetLensSuggestInputsWhenReady, base::CallbackListSubscription( LensOverlaySuggestInputsCallback callback));
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc index 6627d26..e1017e5b 100644 --- a/components/omnibox/browser/zero_suggest_provider.cc +++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -560,9 +560,7 @@ } // Do not start a request if async requests are disallowed. - if (input.omit_asynchronous_matches() || - omnibox_feature_configs::ContextualSearch::Get() - .IsEnabledWithPrefetch()) { + if (input.omit_asynchronous_matches()) { return; } @@ -704,6 +702,13 @@ loader_.reset(); done_ = true; + // The contextual search experience intentionally updates the cache with + // latest received results, but does not publish the matches asynchronously. + if (omnibox_feature_configs::ContextualSearch::Get() + .IsEnabledWithPrefetch()) { + return; + } + // For display stability reasons, update the displayed results with the remote // response only if they are empty or if an empty result set is received. In // the latter case, the displayed results may no longer be valid to be shown.
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller.h b/components/optimization_guide/core/model_execution/on_device_model_service_controller.h index c0f4e29..b1aeafd 100644 --- a/components/optimization_guide/core/model_execution/on_device_model_service_controller.h +++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller.h
@@ -158,10 +158,6 @@ return safety_client_.language_detection_model_path(); } - mojo::Remote<on_device_model::mojom::OnDeviceModel>& base_model_remote() { - return base_model_controller_->remote(); - } - private: // A set of (references to) compatible, versioned dependencies that implement // a ModelBasedCapability. @@ -234,11 +230,6 @@ on_device_model::ModelAssetPaths PopulateModelPaths(); - // TODO(holte): Eliminate this accessor - mojo::Remote<on_device_model::mojom::OnDeviceModel>& remote() { - return remote_; - } - OnDeviceModelMetadata* model_metadata() { return model_metadata_.get(); } base::WeakPtr<BaseModelController> GetWeakPtr() {
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal index 46d95254..aa7ee4b5 160000 --- a/components/optimization_guide/internal +++ b/components/optimization_guide/internal
@@ -1 +1 @@ -Subproject commit 46d95254ba6bdec345073553c6f04b32cfe0a62b +Subproject commit aa7ee4b538e9adb19542ef9b4be4674196aaf859
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/WebXRImmersiveArEnabled.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/WebXRImmersiveArEnabled.yaml index 1039da2..bdf8fa3 100644 --- a/components/policy/resources/templates/policy_definitions/Miscellaneous/WebXRImmersiveArEnabled.yaml +++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/WebXRImmersiveArEnabled.yaml
@@ -21,7 +21,7 @@ sessions value: false owners: -- bialpio@chromium.org +- alcooper@chromium.org - xr-dev@chromium.org schema: type: boolean
diff --git a/components/saved_tab_groups/internal/saved_tab_group_model.cc b/components/saved_tab_groups/internal/saved_tab_group_model.cc index 8348fdf..a8cd65b 100644 --- a/components/saved_tab_groups/internal/saved_tab_group_model.cc +++ b/components/saved_tab_groups/internal/saved_tab_group_model.cc
@@ -543,6 +543,30 @@ } } +void SavedTabGroupModel::UpdatePositionForSharedGroupFromSync( + const base::Uuid& group_id, + std::optional<size_t> position) { + const SavedTabGroup* group = Get(group_id); + if (!group || !group->is_shared_tab_group() || + group->position() == position) { + return; + } + + // Remove the tab group, set position and reinsert. + const int index = GetIndexOf(group_id).value(); + SavedTabGroup saved_group = RemoveImpl(index); + if (position.has_value()) { + saved_group.SetPosition(position.value()); + } else { + saved_group.SetPinned(false); + } + InsertGroupImpl(std::move(saved_group)); + + for (SavedTabGroupModelObserver& observer : observers_) { + observer.SavedTabGroupUpdatedFromSync(group_id, /*tab_guid=*/std::nullopt); + } +} + void SavedTabGroupModel::UpdateLastUpdaterCacheGuidForGroup( const std::optional<std::string>& cache_guid, const LocalTabGroupID& group_id,
diff --git a/components/saved_tab_groups/internal/saved_tab_group_model.h b/components/saved_tab_groups/internal/saved_tab_group_model.h index 98312f0..37bc3a37 100644 --- a/components/saved_tab_groups/internal/saved_tab_group_model.h +++ b/components/saved_tab_groups/internal/saved_tab_group_model.h
@@ -207,6 +207,11 @@ base::Time time, TriggerSource source); + // Update the position for a share group from sync. If the position is + // nullopt, the group will be moved to the end of the list. + void UpdatePositionForSharedGroupFromSync(const base::Uuid& group_id, + std::optional<size_t> position); + // Update the last updater cache guid for a give group and optionally a tab. void UpdateLastUpdaterCacheGuidForGroup( const std::optional<std::string>& cache_guid,
diff --git a/components/saved_tab_groups/internal/saved_tab_group_model_unittest.cc b/components/saved_tab_groups/internal/saved_tab_group_model_unittest.cc index 4a696fe..f844f7d3 100644 --- a/components/saved_tab_groups/internal/saved_tab_group_model_unittest.cc +++ b/components/saved_tab_groups/internal/saved_tab_group_model_unittest.cc
@@ -1382,6 +1382,49 @@ .last_seen_time()); } +TEST_F(SavedTabGroupModelTest, UpdatePositionForSharedGroupFromSyncFromSync) { + RemoveTestData(); + + // Create some tab groups with initial orders. + SavedTabGroup group_1(u"Group 1", tab_groups::TabGroupColorId::kRed, {}, 0); + SavedTabGroup group_2(u"Group 2", tab_groups::TabGroupColorId::kOrange, {}, + 1); + SavedTabGroup group_3(u"Group 3", tab_groups::TabGroupColorId::kYellow, {}, + 2); + SavedTabGroup group_4(u"Group 4", tab_groups::TabGroupColorId::kGreen, {}, + std::nullopt); + + group_1.SetCollaborationId(CollaborationId("collaboration")); + group_2.SetCollaborationId(CollaborationId("collaboration")); + group_3.SetCollaborationId(CollaborationId("collaboration")); + group_4.SetCollaborationId(CollaborationId("collaboration")); + + // This is the order we expect the groups in the model to be. + std::vector<SavedTabGroup> groups = {group_3, group_2, group_4, group_1}; + + // Add the groups into the model in an arbitrary order. + saved_tab_group_model_->AddedLocally(group_1); + saved_tab_group_model_->AddedLocally(group_2); + saved_tab_group_model_->AddedLocally(group_3); + saved_tab_group_model_->AddedLocally(group_4); + + // Change the order of groups. + saved_tab_group_model_->UpdatePositionForSharedGroupFromSync( + group_1.saved_guid(), std::nullopt); + saved_tab_group_model_->UpdatePositionForSharedGroupFromSync( + group_2.saved_guid(), 1); + saved_tab_group_model_->UpdatePositionForSharedGroupFromSync( + group_3.saved_guid(), 0); + saved_tab_group_model_->UpdatePositionForSharedGroupFromSync( + group_4.saved_guid(), 2); + + EXPECT_EQ(saved_tab_group_model_->saved_tab_groups().size(), groups.size()); + for (size_t i = 0; i < groups.size(); ++i) { + EXPECT_EQ(groups[i].saved_guid(), + saved_tab_group_model_->saved_tab_groups()[i].saved_guid()); + } +} + } // namespace } // namespace tab_groups
diff --git a/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.cc b/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.cc index 2839986a..e161e95 100644 --- a/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.cc +++ b/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.cc
@@ -46,6 +46,11 @@ return tab_guid.AsLowercaseString() + "|" + collaboration_id.value(); } +std::string CreateClientTagForSharedGroup(const SavedTabGroup& group) { + return group.saved_guid().AsLowercaseString() + "|" + + group.collaboration_id().value().value(); +} + // Returns the client tag for this specifics object. Note that // SharedTabGroupAccountDataSpecifics uses the client tag as a storage key. std::string GetClientTagFromSpecifics( @@ -98,6 +103,24 @@ return entity_data; } +std::unique_ptr<syncer::EntityData> CreateEntityDataFromSharedTabGroup( + const SavedTabGroupModel& model, + const SavedTabGroup& tab_group) { + CHECK(tab_group.is_shared_tab_group()); + + sync_pb::SharedTabGroupAccountDataSpecifics specifics; + specifics.set_guid(tab_group.saved_guid().AsLowercaseString()); + specifics.set_collaboration_id(tab_group.collaboration_id()->value()); + + sync_pb::SharedTabGroupDetails* tab_group_details = + specifics.mutable_shared_tab_group_details(); + if (tab_group.position().has_value()) { + tab_group_details->set_pinned_position(tab_group.position().value()); + } + + return CreateEntityDataFromSpecifics(specifics); +} + bool SharedTabExistsForSpecifics( const SavedTabGroupModel& model, const sync_pb::SharedTabGroupAccountDataSpecifics& specifics) { @@ -115,6 +138,28 @@ return group->GetTab(tab_id) != nullptr; } +bool IsTabDetailsValid( + const sync_pb::SharedTabGroupAccountDataSpecifics& specifics) { + if (!specifics.has_shared_tab_details()) { + // Non-tab account specifics should be handled here. + return false; + } + + const sync_pb::SharedTabDetails& tab_details = specifics.shared_tab_details(); + if (!base::Uuid::ParseCaseInsensitive(tab_details.shared_tab_group_guid()) + .is_valid() || + !tab_details.has_last_seen_timestamp_windows_epoch()) { + return false; + } + + return true; +} + +bool IsTabGroupDetailsValid( + const sync_pb::SharedTabGroupAccountDataSpecifics& specifics) { + return specifics.has_shared_tab_group_details(); +} + } // namespace SharedTabGroupAccountDataSyncBridge::SharedTabGroupAccountDataSyncBridge( @@ -182,6 +227,8 @@ // find the specifics in-memory. if (specifics.has_shared_tab_details()) { UpdateTabDetailsModel(specifics); + } else if (specifics.has_shared_tab_group_details()) { + UpdateTabGroupDetailsModel(specifics); } break; @@ -276,19 +323,7 @@ return false; } - if (!specifics.has_shared_tab_details()) { - // Non-tab account specifics should be handled here. - return false; - } - - const sync_pb::SharedTabDetails& tab_details = specifics.shared_tab_details(); - if (!base::Uuid::ParseCaseInsensitive(tab_details.shared_tab_group_guid()) - .is_valid() || - !tab_details.has_last_seen_timestamp_windows_epoch()) { - return false; - } - - return true; + return IsTabDetailsValid(specifics) || IsTabGroupDetailsValid(specifics); } sync_pb::EntitySpecifics @@ -354,6 +389,7 @@ void SharedTabGroupAccountDataSyncBridge::SavedTabGroupModelLoaded() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ApplyMissingTabData(); + ApplyMissingTabGroupData(); } void SharedTabGroupAccountDataSyncBridge::SavedTabGroupTabLastSeenTimeUpdated( @@ -417,32 +453,42 @@ const SavedTabGroup* group = model_->Get(group_guid); CHECK(group); - if (!group->is_shared_tab_group() || !tab_guid) { + if (!group->is_shared_tab_group()) { return; } - const SavedTabGroupTab* tab = group->GetTab(tab_guid.value()); - if (tab) { - return; + if (tab_guid) { + MaybeRemoveTabDetailsOnGroupUpdate(*group, tab_guid); + } else { + // Handle shared tab group details. + WriteTabGroupDetailToSyncIfPositionChanged(*group); } - - // This is an update for a shared tab deletion from local. Remove the - // corresponding entity from sync. - const std::string storage_key = CreateClientTagForSharedTab( - group->collaboration_id().value(), tab_guid.value()); - RemoveEntitySpecifics(storage_key); } void SharedTabGroupAccountDataSyncBridge::SavedTabGroupUpdatedFromSync( const base::Uuid& group_guid, const std::optional<base::Uuid>& tab_guid) { - // Regardless of update source, we need to detect tab deletions and clean them - // up from sync. - SavedTabGroupUpdatedLocally(group_guid, tab_guid); + if (!is_initialized_) { + // Ignore any changes before the model is successfully initialized. + return; + } + + const SavedTabGroup* group = model_->Get(group_guid); + CHECK(group); + if (!group->is_shared_tab_group()) { + return; + } + + MaybeRemoveTabDetailsOnGroupUpdate(*group, tab_guid); } void SharedTabGroupAccountDataSyncBridge::SavedTabGroupRemovedLocally( const SavedTabGroup& removed_group) { + if (!is_initialized_) { + // Ignore any changes before the model is successfully initialized. + return; + } + if (!removed_group.is_shared_tab_group()) { return; } @@ -451,6 +497,9 @@ for (const SavedTabGroupTab& tab : removed_group.saved_tabs()) { RemoveEntitySpecifics(CreateClientTagForSharedTab(removed_group, tab)); } + + // Remove tab group details entity. + RemoveEntitySpecifics(CreateClientTagForSharedGroup(removed_group)); } void SharedTabGroupAccountDataSyncBridge::SavedTabGroupRemovedFromSync( @@ -458,6 +507,54 @@ SavedTabGroupRemovedLocally(removed_group); } +void SharedTabGroupAccountDataSyncBridge::SavedTabGroupAddedLocally( + const base::Uuid& guid) { + if (!is_initialized_) { + // Ignore any changes before the model is successfully initialized. + return; + } + + const SavedTabGroup* group = model_->Get(guid); + + if (!group || !group->is_shared_tab_group()) { + return; + } + + WriteTabGroupDetailToSyncIfPositionChanged(*group); +} + +void SharedTabGroupAccountDataSyncBridge::SavedTabGroupAddedFromSync( + const base::Uuid& guid) { + if (!is_initialized_) { + // Ignore any changes before the model is successfully initialized. + return; + } + + const SavedTabGroup* group = model_->Get(guid); + + if (!group || !group->is_shared_tab_group()) { + return; + } + + std::string client_tag = CreateClientTagForSharedGroup(*group); + std::optional<sync_pb::SharedTabGroupAccountDataSpecifics> specifics = + GetSpecificsForStorageKey(client_tag); + if (specifics.has_value()) { + UpdateTabGroupDetailsModel(specifics.value()); + } +} + +void SharedTabGroupAccountDataSyncBridge::SavedTabGroupReorderedLocally() { + if (!is_initialized_) { + // Ignore any changes before the model is successfully initialized. + return; + } + + for (const SavedTabGroup* group : model_->GetSharedTabGroupsOnly()) { + WriteTabGroupDetailToSyncIfPositionChanged(*group); + } +} + bool SharedTabGroupAccountDataSyncBridge::IsInitialized() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return is_initialized_; @@ -516,9 +613,12 @@ // GUID is used as a storage key, so it should always match. continue; } + specifics_[storage_key] = specifics; if (specifics.has_shared_tab_details()) { UpdateTabDetailsModel(specifics); + } else if (specifics.has_shared_tab_group_details()) { + UpdateTabGroupDetailsModel(specifics); } } @@ -565,6 +665,29 @@ storage_keys_for_missing_tabs_.erase(storage_key); } +void SharedTabGroupAccountDataSyncBridge::UpdateTabGroupDetailsModel( + const sync_pb::SharedTabGroupAccountDataSpecifics& specifics) { + CHECK(specifics.has_shared_tab_group_details()); + + const std::string storage_key = GetClientTagFromSpecifics(specifics); + const base::Uuid tab_group_id = + base::Uuid::ParseCaseInsensitive(specifics.guid()); + const SavedTabGroup* group = model_->Get(tab_group_id); + if (!group) { + storage_keys_for_missing_tab_groups_.insert(storage_key); + return; + } + + // If the group is in the model, update its position based on the specifics. + std::optional<size_t> position; + if (specifics.shared_tab_group_details().has_pinned_position()) { + position = specifics.shared_tab_group_details().pinned_position(); + } + model_->UpdatePositionForSharedGroupFromSync(tab_group_id, position); + + storage_keys_for_missing_tab_groups_.erase(storage_key); +} + void SharedTabGroupAccountDataSyncBridge::ApplyMissingTabData() { // Find previously missing tabs have now been added. Create a copy of // the strings from `storage_keys_for_missing_tabs_` so this set can @@ -585,6 +708,28 @@ } } +void SharedTabGroupAccountDataSyncBridge::ApplyMissingTabGroupData() { + // Find previously missing tab groups have now been added. Create a copy of + // the strings from `storage_keys_for_missing_tab_groups_` so this set can + // be mutated in `UpdateTabGroupDetailsModel`. + std::vector<std::string> groups_in_model; + groups_in_model.reserve(storage_keys_for_missing_tab_groups_.size()); + + for (const std::string& storage_key : storage_keys_for_missing_tab_groups_) { + const sync_pb::SharedTabGroupAccountDataSpecifics& specifics = + specifics_.at(storage_key); + const base::Uuid tab_group_id = + base::Uuid::ParseCaseInsensitive(specifics.guid()); + if (model_->Get(tab_group_id)) { + groups_in_model.emplace_back(storage_key); + } + } + + for (const std::string& storage_key : groups_in_model) { + UpdateTabGroupDetailsModel(specifics_.at(storage_key)); + } +} + void SharedTabGroupAccountDataSyncBridge::WriteEntityToSync( std::unique_ptr<syncer::EntityData> entity) { std::unique_ptr<syncer::DataTypeStore::WriteBatch> batch = @@ -598,7 +743,10 @@ if (specifics.has_shared_tab_details()) { // For tab details, remove them from the missing tabs. storage_keys_for_missing_tabs_.erase(storage_key); + } else if (specifics.has_shared_tab_group_details()) { + storage_keys_for_missing_tab_groups_.erase(storage_key); } + batch->WriteData(storage_key, specifics.SerializeAsString()); change_processor()->Put(storage_key, std::move(entity), @@ -664,4 +812,49 @@ return CreateEntityDataFromSpecifics(specifics); } +void SharedTabGroupAccountDataSyncBridge::MaybeRemoveTabDetailsOnGroupUpdate( + const SavedTabGroup& group, + const std::optional<base::Uuid>& tab_guid) { + if (!tab_guid) { + return; + } + + const SavedTabGroupTab* tab = group.GetTab(tab_guid.value()); + if (tab) { + return; + } + + // This is an update for a shared tab deletion from local. Remove the + // corresponding entity from sync. + const std::string storage_key = CreateClientTagForSharedTab( + group.collaboration_id().value(), tab_guid.value()); + RemoveEntitySpecifics(storage_key); +} + +void SharedTabGroupAccountDataSyncBridge:: + WriteTabGroupDetailToSyncIfPositionChanged(const SavedTabGroup& group) { + std::string client_tag = CreateClientTagForSharedGroup(group); + std::optional<sync_pb::SharedTabGroupAccountDataSpecifics> specifics = + GetSpecificsForStorageKey(client_tag); + bool has_changed = false; + if (specifics.has_value()) { + std::optional<size_t> specifics_pinned_position; + if (specifics->has_shared_tab_group_details()) { + if (specifics->shared_tab_group_details().has_pinned_position()) { + specifics_pinned_position = + specifics->shared_tab_group_details().pinned_position(); + } + } + if (group.position() != specifics_pinned_position) { + has_changed = true; + } + } else { + has_changed = true; + } + + if (has_changed) { + WriteEntityToSync(CreateEntityDataFromSharedTabGroup(*model_, group)); + } +} + } // namespace tab_groups
diff --git a/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.h b/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.h index 645ff11a..d1da9dc 100644 --- a/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.h +++ b/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge.h
@@ -68,6 +68,8 @@ void SavedTabGroupModelLoaded() override; void SavedTabGroupTabLastSeenTimeUpdated(const base::Uuid& saved_tab_id, TriggerSource source) override; + void SavedTabGroupAddedLocally(const base::Uuid& guid) override; + void SavedTabGroupAddedFromSync(const base::Uuid& guid) override; void SavedTabGroupUpdatedLocally( const base::Uuid& group_guid, const std::optional<base::Uuid>& tab_guid) override; @@ -77,6 +79,7 @@ void SavedTabGroupRemovedLocally(const SavedTabGroup& removed_group) override; void SavedTabGroupRemovedFromSync( const SavedTabGroup& removed_group) override; + void SavedTabGroupReorderedLocally() override; // Returns whether the sync bridge has initialized by reading data // from the on-disk store. @@ -107,10 +110,17 @@ void UpdateTabDetailsModel( const sync_pb::SharedTabGroupAccountDataSpecifics& specifics); + void UpdateTabGroupDetailsModel( + const sync_pb::SharedTabGroupAccountDataSpecifics& specifics); + // Look for tabs specified in `storage_keys_for_missing_tabs_` and // apply their corresponding model updates. void ApplyMissingTabData(); + // Look for tab groups specified in `storage_keys_for_missing_tab_groups_` and + // apply their corresponding model updates. + void ApplyMissingTabGroupData(); + // Write a new entity to sync. This is used when the model is updated // with a new value and sync needs to be triggered. void WriteEntityToSync(std::unique_ptr<syncer::EntityData> entity); @@ -126,6 +136,14 @@ const SavedTabGroupModel& model, const SavedTabGroupTab& tab); + // Remove tab details on tab group update locally or from sync if available. + void MaybeRemoveTabDetailsOnGroupUpdate( + const SavedTabGroup& group, + const std::optional<base::Uuid>& tab_guid); + + // Write tab group detail to sync only if the tab group details has changed. + void WriteTabGroupDetailToSyncIfPositionChanged(const SavedTabGroup& group); + SEQUENCE_CHECKER(sequence_checker_); // In charge of actually persisting changes to disk, or loading previous data. @@ -146,6 +164,8 @@ // still stored in sync bridge cache as well as written to disk. std::set<std::string> storage_keys_for_missing_tabs_; + std::set<std::string> storage_keys_for_missing_tab_groups_; + // Observes the SavedTabGroupModel. base::ScopedObservation<SavedTabGroupModel, SavedTabGroupModelObserver> saved_tab_group_model_observation_{this};
diff --git a/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge_unittest.cc b/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge_unittest.cc index 792fe667..a9829a9 100644 --- a/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge_unittest.cc +++ b/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge_unittest.cc
@@ -72,6 +72,11 @@ return tab_guid.AsLowercaseString() + "|" + collaboration_id.value(); } +std::string CreateClientTagForSharedGroup(const SavedTabGroup& group) { + return group.saved_guid().AsLowercaseString() + "|" + + group.collaboration_id().value().value(); +} + sync_pb::SharedTabGroupAccountDataSpecifics CreateTabGroupAccountSpecifics( const CollaborationId& collaboration_id, const SavedTabGroupTab& tab, @@ -120,6 +125,21 @@ return extended_specifics.extra_field_for_testing(); } +sync_pb::SharedTabGroupAccountDataSpecifics +CreateTabGroupAccountSpecificsForGroup(const CollaborationId& collaboration_id, + const SavedTabGroup& group, + std::optional<size_t> position) { + sync_pb::SharedTabGroupAccountDataSpecifics specifics; + specifics.set_guid(group.saved_guid().AsLowercaseString()); + specifics.set_collaboration_id(collaboration_id.value()); + sync_pb::SharedTabGroupDetails* shared_tab_group_details = + specifics.mutable_shared_tab_group_details(); + if (position.has_value()) { + shared_tab_group_details->set_pinned_position(position.value()); + } + return specifics; +} + syncer::EntityData CreateEntityData( const sync_pb::SharedTabGroupAccountDataSpecifics& specifics, base::Time creation_time = base::Time::Now()) { @@ -229,30 +249,43 @@ return group; } - size_t GetNumEntriesInStore(bool is_tab_details) { - std::map<std::string, sync_pb::SharedTabGroupAccountDataSpecifics> data = - syncer::DataTypeStoreTestUtil::ReadAllDataAsProtoAndWait< - sync_pb::SharedTabGroupAccountDataSpecifics>(*store_); + std::map<std::string, sync_pb::SharedTabGroupAccountDataSpecifics> + GetEntriesInStore(bool is_tab_details) { + std::unique_ptr<syncer::DataTypeStore::RecordList> entries; + base::RunLoop run_loop; + store_->ReadAllData(base::BindLambdaForTesting( + [&run_loop, &entries]( + const std::optional<syncer::ModelError>& error, + std::unique_ptr<syncer::DataTypeStore::RecordList> data) { + entries = std::move(data); + run_loop.Quit(); + })); + run_loop.Run(); - size_t size = 0; - for (const auto& [storage_key, specifics] : data) { + std::map<std::string, sync_pb::SharedTabGroupAccountDataSpecifics> result; + for (const auto& record : *entries) { + sync_pb::SharedTabGroupAccountDataSpecifics specifics; + if (!specifics.ParseFromString(record.value)) { + CHECK(false); + } + if (is_tab_details && specifics.has_shared_tab_details()) { - ++size; + result[record.id] = specifics; } if (!is_tab_details && specifics.has_shared_tab_group_details()) { - ++size; + result[record.id] = specifics; } } - return size; + return result; } size_t GetNumTabDetailsInStore() { - return GetNumEntriesInStore(/*is_tab_details=*/true); + return GetEntriesInStore(/*is_tab_details=*/true).size(); } size_t GetNumTabGroupDetailsInStore() { - return GetNumEntriesInStore(/*is_tab_details=*/false); + return GetEntriesInStore(/*is_tab_details=*/false).size(); } // Cleans up the bridge and the model, used to simulate browser restart. @@ -331,6 +364,9 @@ CreateTabGroupAccountSpecifics(kCollaborationId, group.saved_tabs().at(0), last_seen_time), base::Time::Now()))); + + EXPECT_TRUE(bridge().IsEntityDataValid(CreateEntityData( + CreateTabGroupAccountSpecificsForGroup(kCollaborationId, group, 0)))); } TEST_F(SharedTabGroupAccountDataSyncBridgeTest, ShouldResolveConflicts) { @@ -669,6 +705,8 @@ const base::Uuid& tab_id2 = created_tab2.saved_tab_guid(); InitializeBridgeAndModel(); + const std::string storage_key = CreateClientTagForSharedGroup(created_group); + EXPECT_CALL(mock_processor(), Put(Eq(storage_key), _, _)).Times(1); model().AddedLocally(created_group); EXPECT_EQ(model().Count(), 1); @@ -854,6 +892,8 @@ const base::Uuid& tab_id2 = created_group.saved_tabs()[1].saved_tab_guid(); InitializeBridgeAndModel(); + const std::string storage_key = CreateClientTagForSharedGroup(created_group); + EXPECT_CALL(mock_processor(), Put(Eq(storage_key), _, _)).Times(1); model().AddedLocally(created_group); ASSERT_EQ(model().Count(), 1); @@ -879,10 +919,12 @@ // should be deleted. EXPECT_CALL(mock_processor(), Delete(Eq(storage_key1), _, _)).Times(1); EXPECT_CALL(mock_processor(), Delete(Eq(storage_key2), _, _)).Times(1); + EXPECT_CALL(mock_processor(), Delete(Eq(storage_key), _, _)).Times(1); model().RemovedLocally(group_id); EXPECT_EQ(GetNumTabDetailsInStore(), 0u); EXPECT_FALSE(bridge().GetSpecificsForStorageKey(storage_key1)); EXPECT_FALSE(bridge().GetSpecificsForStorageKey(storage_key2)); + EXPECT_FALSE(bridge().GetSpecificsForStorageKey(storage_key)); } TEST_F(SharedTabGroupAccountDataSyncBridgeTest, @@ -895,6 +937,8 @@ const base::Uuid& tab_id2 = created_group.saved_tabs()[1].saved_tab_guid(); InitializeBridgeAndModel(); + const std::string storage_key = CreateClientTagForSharedGroup(created_group); + EXPECT_CALL(mock_processor(), Put(Eq(storage_key), _, _)).Times(1); model().AddedLocally(created_group); ASSERT_EQ(model().Count(), 1); @@ -920,10 +964,12 @@ // tabs should be deleted. EXPECT_CALL(mock_processor(), Delete(Eq(storage_key1), _, _)).Times(1); EXPECT_CALL(mock_processor(), Delete(Eq(storage_key2), _, _)).Times(1); + EXPECT_CALL(mock_processor(), Delete(Eq(storage_key), _, _)).Times(1); model().RemovedFromSync(group_id); EXPECT_EQ(GetNumTabDetailsInStore(), 0u); EXPECT_FALSE(bridge().GetSpecificsForStorageKey(storage_key1)); EXPECT_FALSE(bridge().GetSpecificsForStorageKey(storage_key2)); + EXPECT_FALSE(bridge().GetSpecificsForStorageKey(storage_key)); } TEST_F(SharedTabGroupAccountDataSyncBridgeTest, @@ -1076,5 +1122,195 @@ "extra_field_for_testing"); } +TEST_F(SharedTabGroupAccountDataSyncBridgeTest, + IncrementalUpdateShouldSetPosition) { + const CollaborationId kCollaborationId("collaboration"); + const SavedTabGroup created_group = CreateGroupWithLocalIds(kCollaborationId); + const base::Uuid& created_group_id = created_group.saved_guid(); + + // Add group locally. + InitializeBridgeAndModel(); + model().AddedLocally(created_group); + + // Receive update from sync. + syncer::EntityChangeList change_list; + std::optional<size_t> position = 5; + change_list.push_back( + CreateAddEntityChange(CreateTabGroupAccountSpecificsForGroup( + kCollaborationId, created_group, position))); + EXPECT_EQ(GetNumTabGroupDetailsInStore(), 1u); + bridge().ApplyIncrementalSyncChanges(bridge().CreateMetadataChangeList(), + std::move(change_list)); + + // Verify position is set correctly. + const SavedTabGroup* group = model().Get(created_group_id); + EXPECT_EQ(position, group->position()); + EXPECT_EQ(GetNumTabGroupDetailsInStore(), 1u); +} + +TEST_F(SharedTabGroupAccountDataSyncBridgeTest, + TabGroupAddedFromSyncShouldSetPosition) { + const CollaborationId kCollaborationId("collaboration"); + const SavedTabGroup created_group = CreateGroupWithLocalIds(kCollaborationId); + const base::Uuid& created_group_id = created_group.saved_guid(); + + InitializeBridgeAndModel(); + + // Receive update from sync. + syncer::EntityChangeList change_list; + std::optional<size_t> position = 5; + change_list.push_back( + CreateAddEntityChange(CreateTabGroupAccountSpecificsForGroup( + kCollaborationId, created_group, position))); + bridge().ApplyIncrementalSyncChanges(bridge().CreateMetadataChangeList(), + std::move(change_list)); + EXPECT_EQ(GetNumTabGroupDetailsInStore(), 1u); + + // Add group from sync. + model().AddedFromSync(created_group); + + // Verify position is set correctly. + const SavedTabGroup* group = model().Get(created_group_id); + EXPECT_EQ(position, group->position()); + EXPECT_EQ(GetNumTabGroupDetailsInStore(), 1u); +} + +TEST_F(SharedTabGroupAccountDataSyncBridgeTest, + TabGroupAddedLocallyShouldSavePosition) { + const CollaborationId kCollaborationId("collaboration"); + const int kPosition = 5; + SavedTabGroup created_group = CreateGroupWithLocalIds(kCollaborationId); + created_group.SetPosition(kPosition); + std::string client_tag = CreateClientTagForSharedGroup(created_group); + + InitializeBridgeAndModel(); + model().AddedLocally(created_group); + + // Verify the position is updated. + EXPECT_EQ(GetNumTabGroupDetailsInStore(), 1u); + auto entries = GetEntriesInStore(/*is_tab_details=*/false); + ASSERT_TRUE(entries.contains(client_tag)); + ASSERT_EQ(kPosition, + entries[client_tag].shared_tab_group_details().pinned_position()); +} + +TEST_F(SharedTabGroupAccountDataSyncBridgeTest, + TabGroupTogglePinStateShouldSavePosition) { + const CollaborationId kCollaborationId("collaboration"); + SavedTabGroup created_group = CreateGroupWithLocalIds(kCollaborationId); + base::Uuid guid = created_group.saved_guid(); + std::string client_tag = CreateClientTagForSharedGroup(created_group); + + InitializeBridgeAndModel(); + model().AddedLocally(created_group); + + // Verify unpinned position. + EXPECT_EQ(GetNumTabGroupDetailsInStore(), 1u); + auto entries = GetEntriesInStore(/*is_tab_details=*/false); + ASSERT_TRUE(entries.contains(client_tag)); + ASSERT_FALSE( + entries[client_tag].shared_tab_group_details().has_pinned_position()); + + // Pin the tab group. + model().TogglePinState(guid); + + // Verify pinned position. + entries = GetEntriesInStore(/*is_tab_details=*/false); + ASSERT_TRUE(entries.contains(client_tag)); + ASSERT_EQ(0, + entries[client_tag].shared_tab_group_details().pinned_position()); +} + +TEST_F(SharedTabGroupAccountDataSyncBridgeTest, + TabGroupRemovedLocallyShouldRemovePosition) { + const CollaborationId kCollaborationId("collaboration"); + SavedTabGroup created_group = CreateGroupWithLocalIds(kCollaborationId); + base::Uuid guid = created_group.saved_guid(); + std::string client_tag = CreateClientTagForSharedGroup(created_group); + + InitializeBridgeAndModel(); + model().AddedLocally(created_group); + + EXPECT_EQ(GetNumTabGroupDetailsInStore(), 1u); + + model().RemovedLocally(guid); + + EXPECT_EQ(GetNumTabGroupDetailsInStore(), 0u); +} + +TEST_F(SharedTabGroupAccountDataSyncBridgeTest, + TabGroupRemovedFromSyncShouldRemovePosition) { + const CollaborationId kCollaborationId("collaboration"); + SavedTabGroup created_group = CreateGroupWithLocalIds(kCollaborationId); + base::Uuid guid = created_group.saved_guid(); + std::string client_tag = CreateClientTagForSharedGroup(created_group); + + InitializeBridgeAndModel(); + model().AddedLocally(created_group); + + EXPECT_EQ(GetNumTabGroupDetailsInStore(), 1u); + + model().RemovedFromSync(guid); + + EXPECT_EQ(GetNumTabGroupDetailsInStore(), 0u); +} + +TEST_F(SharedTabGroupAccountDataSyncBridgeTest, + TabGroupReorderLocallyShouldSavePosition) { + const CollaborationId kCollaborationId1("collaboration1"); + SavedTabGroup created_group1 = CreateGroupWithLocalIds(kCollaborationId1); + created_group1.SetPosition(0); + base::Uuid guid1 = created_group1.saved_guid(); + std::string client_tag1 = CreateClientTagForSharedGroup(created_group1); + + const CollaborationId kCollaborationId2("collaboration2"); + SavedTabGroup created_group2 = CreateGroupWithLocalIds(kCollaborationId2); + created_group2.SetPosition(1); + std::string client_tag2 = CreateClientTagForSharedGroup(created_group2); + + // Add 2 groups. + InitializeBridgeAndModel(); + model().AddedLocally(created_group1); + model().AddedLocally(created_group2); + + // Verify initial positions. + EXPECT_EQ(GetNumTabGroupDetailsInStore(), 2u); + auto entries = GetEntriesInStore(/*is_tab_details=*/false); + ASSERT_TRUE(entries.contains(client_tag1)); + ASSERT_EQ(0, + entries[client_tag1].shared_tab_group_details().pinned_position()); + ASSERT_TRUE(entries.contains(client_tag2)); + ASSERT_EQ(1, + entries[client_tag2].shared_tab_group_details().pinned_position()); + + // Reorder group. + model().ReorderGroupLocally(guid1, 1); + + // Verify modified positions. + EXPECT_EQ(GetNumTabGroupDetailsInStore(), 2u); + entries = GetEntriesInStore(/*is_tab_details=*/false); + ASSERT_TRUE(entries.contains(client_tag1)); + ASSERT_EQ(1, + entries[client_tag1].shared_tab_group_details().pinned_position()); + ASSERT_TRUE(entries.contains(client_tag2)); + ASSERT_EQ(0, + entries[client_tag2].shared_tab_group_details().pinned_position()); +} + +TEST_F(SharedTabGroupAccountDataSyncBridgeTest, + TabGroupShouldOnlySaveIfPositionChanged) { + const CollaborationId kCollaborationId("collaboration"); + SavedTabGroup created_group = CreateGroupWithLocalIds(kCollaborationId); + base::Uuid guid = created_group.saved_guid(); + std::string client_tag = CreateClientTagForSharedGroup(created_group); + + InitializeBridgeAndModel(); + + EXPECT_CALL(mock_processor(), Put(Eq(client_tag), _, _)).Times(1); + model().AddedLocally(created_group); + model().UpdateArchivalStatus(guid, true); + model().UpdateArchivalStatus(guid, false); +} + } // namespace } // namespace tab_groups
diff --git a/components/saved_tab_groups/public/saved_tab_group.cc b/components/saved_tab_groups/public/saved_tab_group.cc index a6d97c2..762ebbd 100644 --- a/components/saved_tab_groups/public/saved_tab_group.cc +++ b/components/saved_tab_groups/public/saved_tab_group.cc
@@ -429,10 +429,15 @@ base::Time update_time) { SetTitle(title); SetColor(color); - if (position.has_value()) { - SetPosition(position.value()); - } else { - SetPinned(false); + + // Do not merge position for shared tab group since the position is saved from + // elsewhere. + if (!is_shared_tab_group()) { + if (position.has_value()) { + SetPosition(position.value()); + } else { + SetPinned(false); + } } SetCreatorCacheGuid(creator_cache_guid);
diff --git a/components/saved_tab_groups/public/saved_tab_group_unittest.cc b/components/saved_tab_groups/public/saved_tab_group_unittest.cc index 800e56b2..a9b6d9c 100644 --- a/components/saved_tab_groups/public/saved_tab_group_unittest.cc +++ b/components/saved_tab_groups/public/saved_tab_group_unittest.cc
@@ -378,4 +378,24 @@ kOriginatingTabGroupGuid); } +TEST(SavedTabGroupTest, MergeRemoteGroupPosition) { + std::u16string title = u"title"; + tab_groups::TabGroupColorId color = tab_groups::TabGroupColorId::kBlue; + std::optional<size_t> position = 0; + + // Saved group should merge position. + SavedTabGroup saved_group = CreateDefaultEmptySavedTabGroup(); + saved_group.MergeRemoteGroupMetadata(title, color, position, std::nullopt, + std::nullopt, base::Time::Now()); + EXPECT_EQ(position, saved_group.position()); + + // Shared group should not merge position. + SavedTabGroup shared_group = + CreateDefaultEmptySavedTabGroup().CloneAsSharedTabGroup( + CollaborationId("collaboration")); + shared_group.MergeRemoteGroupMetadata(title, color, position, std::nullopt, + std::nullopt, base::Time::Now()); + EXPECT_EQ(std::nullopt, shared_group.position()); +} + } // namespace tab_groups
diff --git a/components/services/storage/dom_storage/local_storage_impl.cc b/components/services/storage/dom_storage/local_storage_impl.cc index 0c14486..9c8e490 100644 --- a/components/services/storage/dom_storage/local_storage_impl.cc +++ b/components/services/storage/dom_storage/local_storage_impl.cc
@@ -603,6 +603,8 @@ LocalStorageImpl::~LocalStorageImpl() { DCHECK_EQ(connection_state_, CONNECTION_SHUTDOWN); + // ShutDown() should run before this destructor and clear `areas_`. + CHECK(areas_.empty()); base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( this); }
diff --git a/components/services/storage/partition_impl.cc b/components/services/storage/partition_impl.cc index a30aff5..15e641a 100644 --- a/components/services/storage/partition_impl.cc +++ b/components/services/storage/partition_impl.cc
@@ -7,6 +7,7 @@ #include <memory> #include <utility> +#include "base/debug/crash_logging.h" #include "base/functional/bind.h" #include "base/task/sequenced_task_runner.h" #include "base/task/thread_pool.h" @@ -90,6 +91,18 @@ base::SequencedTaskRunner::GetCurrentDefault(), std::move(receiver)); } +#if BUILDFLAG(IS_MAC) +void PartitionImpl::BindLocalStorageControlAndReportLifecycle( + mojom::LocalStorageLifecycle lifecycle, + mojo::PendingReceiver<mojom::LocalStorageControl> receiver) { + SCOPED_CRASH_KEY_NUMBER("396030877", "local_storage_lifecycle", + static_cast<int>(lifecycle)); + local_storage_ = std::make_unique<LocalStorageImpl>( + path_.value_or(base::FilePath()), + base::SequencedTaskRunner::GetCurrentDefault(), std::move(receiver)); +} +#endif // BUILDFLAG(IS_MAC) + void PartitionImpl::OnDisconnect() { if (receivers_.empty()) { // Deletes |this|.
diff --git a/components/services/storage/partition_impl.h b/components/services/storage/partition_impl.h index 6f3443e2..ba21d6f 100644 --- a/components/services/storage/partition_impl.h +++ b/components/services/storage/partition_impl.h
@@ -48,6 +48,11 @@ mojo::PendingReceiver<mojom::SessionStorageControl> receiver) override; void BindLocalStorageControl( mojo::PendingReceiver<mojom::LocalStorageControl> receiver) override; +#if BUILDFLAG(IS_MAC) + void BindLocalStorageControlAndReportLifecycle( + mojom::LocalStorageLifecycle lifecycle, + mojo::PendingReceiver<mojom::LocalStorageControl> receiver) override; +#endif // BUILDFLAG(IS_MAC) private: void OnDisconnect();
diff --git a/components/services/storage/public/mojom/partition.mojom b/components/services/storage/public/mojom/partition.mojom index f6d650f..0959952 100644 --- a/components/services/storage/public/mojom/partition.mojom +++ b/components/services/storage/public/mojom/partition.mojom
@@ -7,12 +7,33 @@ import "components/services/storage/public/mojom/local_storage_control.mojom"; import "components/services/storage/public/mojom/session_storage_control.mojom"; +// Mac only enum used as a crash key to help investigate Storage process +// crashes. +// TODO(crbug.com/396030877): Fix the bug and remove this enum. +[EnableIf=is_mac] +enum LocalStorageLifecycle { + kInitializing, + kInitializingWithUnboundStorageService, + kRecovering, + kRecoveringWithUnboundStorageService, +}; + // Partition controls an isolated storage partition owned by the Storage // Service. This is analogous to the browser's own storage partition concept. interface Partition { // Binds the main control interface for Session Storage in this partition. - BindSessionStorageControl(pending_receiver<SessionStorageControl> receiver); + BindSessionStorageControl( + pending_receiver<SessionStorageControl> receiver); // Binds the main control interface for Local Storage in this partition. BindLocalStorageControl(pending_receiver<LocalStorageControl> receiver); + + // Similar to the above method but with a `lifecycle` param. This is included + // as a crash key to help root cause a Storage process crash in LocalStorage + // code on macOS. + // TODO(crbug.com/396030877): Fix the bug and remove this param. + [EnableIf=is_mac] + BindLocalStorageControlAndReportLifecycle( + LocalStorageLifecycle lifecycle, + pending_receiver<LocalStorageControl> receiver); };
diff --git a/components/viz/common/quads/selection.h b/components/viz/common/quads/selection.h index b0ff04122..3713462d 100644 --- a/components/viz/common/quads/selection.h +++ b/components/viz/common/quads/selection.h
@@ -5,8 +5,6 @@ #ifndef COMPONENTS_VIZ_COMMON_QUADS_SELECTION_H_ #define COMPONENTS_VIZ_COMMON_QUADS_SELECTION_H_ -#include "base/strings/stringprintf.h" - namespace viz { template <typename BoundType> @@ -14,12 +12,8 @@ Selection() = default; ~Selection() = default; - BoundType start, end; - - std::string ToString() const { - return base::StringPrintf("Selection(%s, %s)", start.ToString().c_str(), - end.ToString().c_str()); - } + BoundType start; + BoundType end; friend bool operator==(const Selection<BoundType>&, const Selection<BoundType>&) = default;
diff --git a/components/webxr/OWNERS b/components/webxr/OWNERS index 3bd7798..fba6307f 100644 --- a/components/webxr/OWNERS +++ b/components/webxr/OWNERS
@@ -1,6 +1,5 @@ alcooper@chromium.org bajones@chromium.org -bialpio@chromium.org # WebXR Test related bsheedy@chromium.org
diff --git a/content/browser/accessibility/browser_accessibility_state_impl_win.cc b/content/browser/accessibility/browser_accessibility_state_impl_win.cc index c2ecee72..1b69200 100644 --- a/content/browser/accessibility/browser_accessibility_state_impl_win.cc +++ b/content/browser/accessibility/browser_accessibility_state_impl_win.cc
@@ -21,6 +21,7 @@ #include "base/files/file_path.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "base/task/thread_pool.h" #include "base/win/registry.h" #include "content/browser/web_contents/web_contents_impl.h"
diff --git a/content/browser/back_forward_cache_basics_browsertest.cc b/content/browser/back_forward_cache_basics_browsertest.cc index 0cb2693..1d04ba9 100644 --- a/content/browser/back_forward_cache_basics_browsertest.cc +++ b/content/browser/back_forward_cache_basics_browsertest.cc
@@ -4,6 +4,7 @@ #include <array> +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "content/browser/back_forward_cache_browsertest.h" #include "content/browser/web_contents/web_contents_impl.h" @@ -788,8 +789,7 @@ // RelatedActiveContents metrics because the related active contents // count is > 1. if (ShouldCreateNewHostForAllFrames() || - (!NavigateSameSite() && - SiteIsolationPolicy::UseDedicatedProcessesForAllSites())) { + (!NavigateSameSite() && AreStrictSiteInstancesEnabled())) { ExpectNotRestored( {NotRestoredReason::kRelatedActiveContentsExist, NotRestoredReason::kBlocklistedFeatures,
diff --git a/content/browser/back_forward_cache_features_browsertest.cc b/content/browser/back_forward_cache_features_browsertest.cc index d8b85be..38f54aa 100644 --- a/content/browser/back_forward_cache_features_browsertest.cc +++ b/content/browser/back_forward_cache_features_browsertest.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/containers/contains.h" +#include "base/strings/stringprintf.h" #include "base/threading/platform_thread.h" #include "base/time/time.h" #include "build/build_config.h"
diff --git a/content/browser/browsing_instance.cc b/content/browser/browsing_instance.cc index 3fd633d..8a6358d 100644 --- a/content/browser/browsing_instance.cc +++ b/content/browser/browsing_instance.cc
@@ -138,8 +138,8 @@ // Check to see if we can use the default SiteInstance for sites that don't // need to be isolated in their own process. - if (allow_default_instance && - SiteInstanceImpl::CanBePlacedInDefaultSiteInstance( + if (!ShouldUseDefaultSiteInstanceGroup() && allow_default_instance && + SiteInstanceImpl::CanBePlacedInDefaultSiteInstanceOrGroup( isolation_context_, url_info.url, site_info)) { scoped_refptr<SiteInstanceImpl> site_instance = default_site_instance_.get(); @@ -183,6 +183,7 @@ // Explicitly prevent the default SiteInstance from being added since // the map is only supposed to contain instances that map to a single site. if (site_instance->IsDefaultSiteInstance()) { + DCHECK(!ShouldUseDefaultSiteInstanceGroup()); CHECK(!default_site_instance_); default_site_instance_ = site_instance; return;
diff --git a/content/browser/btm/btm_bounce_detector_unittest.cc b/content/browser/btm/btm_bounce_detector_unittest.cc index 0d70cff4..2f17336 100644 --- a/content/browser/btm/btm_bounce_detector_unittest.cc +++ b/content/browser/btm/btm_bounce_detector_unittest.cc
@@ -11,6 +11,7 @@ #include "base/functional/callback_forward.h" #include "base/functional/callback_helpers.h" #include "base/strings/strcat.h" +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h"
diff --git a/content/browser/btm/btm_navigation_flow_detector_browsertest.cc b/content/browser/btm/btm_navigation_flow_detector_browsertest.cc index e08d3ec..f3d0b7da1 100644 --- a/content/browser/btm/btm_navigation_flow_detector_browsertest.cc +++ b/content/browser/btm/btm_navigation_flow_detector_browsertest.cc
@@ -5,6 +5,7 @@ #include "content/browser/btm/btm_navigation_flow_detector.h" #include "base/base64.h" +#include "base/strings/stringprintf.h" #include "base/strings/to_string.h" #include "base/test/bind.h" #include "base/test/gmock_expected_support.h"
diff --git a/content/browser/child_process_launcher_helper.h b/content/browser/child_process_launcher_helper.h index bee6bc2..620a337 100644 --- a/content/browser/child_process_launcher_helper.h +++ b/content/browser/child_process_launcher_helper.h
@@ -242,7 +242,8 @@ void OnChildProcessStarted(pid_t process_id, std::unique_ptr<LaunchResult> launch_result); void ClearProcessStorage(); - void SetNormalTermination(); + void SetExitCode(int exit_code); + std::optional<int> GetExitCode(); #if defined(__OBJC__) NSObject* GetProcess(); @@ -356,7 +357,7 @@ #if BUILDFLAG(IS_IOS) std::unique_ptr<base::ScopedTempDir> scoped_temp_dir_; - bool normal_termination_ = false; + std::optional<int> exit_code_; #endif // Histogram shared memory region. Ownership of the memory region object is
diff --git a/content/browser/child_process_launcher_helper_android.cc b/content/browser/child_process_launcher_helper_android.cc index 30e3fe9e..3459186a 100644 --- a/content/browser/child_process_launcher_helper_android.cc +++ b/content/browser/child_process_launcher_helper_android.cc
@@ -249,11 +249,19 @@ static jboolean JNI_ChildProcessLauncherHelperImpl_ServiceGroupImportanceEnabled(JNIEnv* env) { // Not this is called on the launcher thread, not UI thread. + // + // Note that service grouping is mandatory for site isolation on pre-U devices + // to avoid cached process limit. By service grouping, cached chrome renderer + // processes in a group are counted as one. On pre-U devices the cached + // process limit is usually 32 or such. U+ devices has a larger limit 1024 or + // such. return (SiteIsolationPolicy::AreIsolatedOriginsEnabled() || SiteIsolationPolicy::UseDedicatedProcessesForAllSites() || SiteIsolationPolicy::AreDynamicIsolatedOriginsEnabled() || SiteIsolationPolicy::ArePreloadedIsolatedOriginsEnabled()) && - base::FeatureList::IsEnabled(kServiceGroupImportance); + (base::android::android_info::sdk_int() < + base::android::android_info::SDK_VERSION_U || + base::FeatureList::IsEnabled(kServiceGroupImportance)); } // static
diff --git a/content/browser/child_process_launcher_helper_ios.mm b/content/browser/child_process_launcher_helper_ios.mm index 109f1df..221c428 100644 --- a/content/browser/child_process_launcher_helper_ios.mm +++ b/content/browser/child_process_launcher_helper_ios.mm
@@ -53,24 +53,6 @@ } } -bool TerminateNow(pid_t process_id) { - NSObject* process = nullptr; - { - base::AutoLock guard(*g_process_table_lock_); - auto it = g_process_table_->find(process_id); - if (it != g_process_table_->end()) { - it->second->SetNormalTermination(); - process = it->second->GetProcess(); - } - } - - if (!process) { - return false; - } - InvalidateProcess(process); - return true; -} - bool WaitForExit(pid_t process_id, int* exit_code, base::TimeDelta timeout) { base::TimeTicks wakeup_time = base::TimeTicks::Now() + timeout; constexpr uint32_t kMaxSleepInMicroseconds = 1 << 18; // ~256 ms. @@ -84,7 +66,7 @@ if (it != g_process_table_->end()) { if (it->second->GetProcess() == nullptr) { if (exit_code) { - *exit_code = 0; + *exit_code = it->second->GetExitCode().value_or(0); } return true; } @@ -108,6 +90,27 @@ } } +bool TerminateNow(pid_t process_id, int exit_code, bool wait) { + NSObject* process = nullptr; + { + base::AutoLock guard(*g_process_table_lock_); + auto it = g_process_table_->find(process_id); + if (it != g_process_table_->end()) { + it->second->SetExitCode(exit_code); + process = it->second->GetProcess(); + } + } + + if (!process) { + return false; + } + InvalidateProcess(process); + if (wait) { + return WaitForExit(process_id, nullptr, base::Seconds(60)); + } + return true; +} + // Object used to pass the result of the launch from the async // dispatch_queue to the LauncherThread. class LaunchResult { @@ -415,9 +418,15 @@ info.status = base::TERMINATION_STATUS_LAUNCH_FAILED; } else if (static_cast<ProcessStorage*>(process_storage_.get())->Process() == nullptr) { - info.status = normal_termination_ - ? base::TERMINATION_STATUS_NORMAL_TERMINATION - : base::TERMINATION_STATUS_PROCESS_CRASHED; + if (exit_code_.has_value()) { + if (exit_code_.value() == RESULT_CODE_NORMAL_EXIT) { + info.status = base::TERMINATION_STATUS_NORMAL_TERMINATION; + } else { + info.status = base::TERMINATION_STATUS_PROCESS_WAS_KILLED; + } + } else { + info.status = base::TERMINATION_STATUS_PROCESS_CRASHED; + } } else { info.status = base::TERMINATION_STATUS_STILL_RUNNING; } @@ -430,8 +439,12 @@ } } -void ChildProcessLauncherHelper::SetNormalTermination() { - normal_termination_ = true; +void ChildProcessLauncherHelper::SetExitCode(int exit_code) { + exit_code_ = exit_code; +} + +std::optional<int> ChildProcessLauncherHelper::GetExitCode() { + return exit_code_; } NSObject* ChildProcessLauncherHelper::GetProcess() {
diff --git a/content/browser/code_cache/generated_code_cache_browsertest.cc b/content/browser/code_cache/generated_code_cache_browsertest.cc index f4d0fc89..8faec12 100644 --- a/content/browser/code_cache/generated_code_cache_browsertest.cc +++ b/content/browser/code_cache/generated_code_cache_browsertest.cc
@@ -5,6 +5,7 @@ #include "content/browser/code_cache/generated_code_cache.h" #include "base/run_loop.h" +#include "base/strings/stringprintf.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/test_future.h"
diff --git a/content/browser/content_security_policy_browsertest.cc b/content/browser/content_security_policy_browsertest.cc index 3a5119b..559cd05 100644 --- a/content/browser/content_security_policy_browsertest.cc +++ b/content/browser/content_security_policy_browsertest.cc
@@ -10,6 +10,7 @@ #include "base/memory/raw_ref.h" #include "base/notreached.h" #include "base/path_service.h" +#include "base/strings/stringprintf.h" #include "base/test/scoped_feature_list.h" #include "base/threading/thread_restrictions.h" #include "content/browser/renderer_host/render_frame_host_impl.h"
diff --git a/content/browser/dom_storage/dom_storage_context_wrapper.cc b/content/browser/dom_storage/dom_storage_context_wrapper.cc index 83e16b0..d7a3599b 100644 --- a/content/browser/dom_storage/dom_storage_context_wrapper.cc +++ b/content/browser/dom_storage/dom_storage_context_wrapper.cc
@@ -22,7 +22,6 @@ #include "build/build_config.h" #include "components/services/storage/dom_storage/local_storage_impl.h" #include "components/services/storage/dom_storage/session_storage_impl.h" -#include "components/services/storage/public/mojom/partition.mojom.h" #include "components/services/storage/public/mojom/storage_policy_update.mojom.h" #include "components/services/storage/public/mojom/storage_usage_info.mojom.h" #include "content/browser/dom_storage/session_storage_namespace_impl.h" @@ -44,6 +43,9 @@ #include "third_party/blink/public/common/storage_key/storage_key.h" namespace content { +#if BUILDFLAG(IS_MAC) +using LocalStorageLifecycle = storage::mojom::LocalStorageLifecycle; +#endif // BUILDFLAG(IS_MAC) namespace { void AdaptSessionStorageUsageInfo( @@ -72,6 +74,22 @@ std::move(callback).Run(result); } +#if BUILDFLAG(IS_MAC) +LocalStorageLifecycle GetLocalStorageLifecycle( + bool recovering, + bool storage_service_remote_was_bound) { + if (recovering) { + return storage_service_remote_was_bound + ? LocalStorageLifecycle::kRecovering + : LocalStorageLifecycle::kRecoveringWithUnboundStorageService; + } else { + return storage_service_remote_was_bound + ? LocalStorageLifecycle::kInitializing + : LocalStorageLifecycle::kInitializingWithUnboundStorageService; + } +} +#endif // BUILDFLAG(IS_MAC) + } // namespace scoped_refptr<DOMStorageContextWrapper> DOMStorageContextWrapper::Create( @@ -100,8 +118,18 @@ base::BindRepeating(&DOMStorageContextWrapper::OnMemoryPressure, base::Unretained(this))); +#if BUILDFLAG(IS_MAC) + // Binding Session or Local storage will result in the storage service getting + // bound. So, we capture this state before those calls. + LocalStorageLifecycle lifecycle = GetLocalStorageLifecycle( + /*recovering=*/false, partition_->IsStorageServiceRemoteValid()); +#endif // BUILDFLAG(IS_MAC) MaybeBindSessionStorageControl(); +#if BUILDFLAG(IS_MAC) + MaybeBindLocalStorageControlAndReportLifecycle(lifecycle); +#else MaybeBindLocalStorageControl(); +#endif // BUILDFLAG(IS_MAC) } DOMStorageContextWrapper::~DOMStorageContextWrapper() { @@ -331,8 +359,18 @@ void DOMStorageContextWrapper::RecoverFromStorageServiceCrash() { DCHECK(partition_); +#if BUILDFLAG(IS_MAC) + // Binding Session or Local storage will result in the storage service getting + // bound. So, we capture this state before those calls. + LocalStorageLifecycle lifecycle = GetLocalStorageLifecycle( + /*recovering=*/true, partition_->IsStorageServiceRemoteValid()); +#endif // BUILDFLAG(IS_MAC) MaybeBindSessionStorageControl(); +#if BUILDFLAG(IS_MAC) + MaybeBindLocalStorageControlAndReportLifecycle(lifecycle); +#else MaybeBindLocalStorageControl(); +#endif // BUILDFLAG(IS_MAC) // Make sure the service is aware of namespaces we asked a previous instance // to create, so it can properly service renderers trying to manipulate those @@ -359,6 +397,19 @@ local_storage_control_.BindNewPipeAndPassReceiver()); } +#if BUILDFLAG(IS_MAC) +void DOMStorageContextWrapper::MaybeBindLocalStorageControlAndReportLifecycle( + LocalStorageLifecycle lifecycle) { + if (!partition_) { + return; + } + local_storage_control_.reset(); + partition_->GetStorageServicePartition() + ->BindLocalStorageControlAndReportLifecycle( + lifecycle, local_storage_control_.BindNewPipeAndPassReceiver()); +} +#endif // BUILDFLAG(IS_MAC) + scoped_refptr<SessionStorageNamespaceImpl> DOMStorageContextWrapper::MaybeGetExistingNamespace( const std::string& namespace_id) const {
diff --git a/content/browser/dom_storage/dom_storage_context_wrapper.h b/content/browser/dom_storage/dom_storage_context_wrapper.h index 63ca08b..6289ed4 100644 --- a/content/browser/dom_storage/dom_storage_context_wrapper.h +++ b/content/browser/dom_storage/dom_storage_context_wrapper.h
@@ -17,6 +17,7 @@ #include "base/thread_annotations.h" #include "base/threading/sequence_bound.h" #include "components/services/storage/public/mojom/local_storage_control.mojom.h" +#include "components/services/storage/public/mojom/partition.mojom.h" #include "components/services/storage/public/mojom/session_storage_control.mojom.h" #include "components/services/storage/public/mojom/storage_usage_info.mojom.h" #include "content/browser/child_process_security_policy_impl.h" @@ -141,6 +142,10 @@ void MaybeBindSessionStorageControl(); void MaybeBindLocalStorageControl(); +#if BUILDFLAG(IS_MAC) + void MaybeBindLocalStorageControlAndReportLifecycle( + storage::mojom::LocalStorageLifecycle lifecycle); +#endif // BUILDFLAG(IS_MAC) scoped_refptr<SessionStorageNamespaceImpl> MaybeGetExistingNamespace( const std::string& namespace_id) const;
diff --git a/content/browser/interest_group/auction_nonce_manager_unittest.cc b/content/browser/interest_group/auction_nonce_manager_unittest.cc index ceb49e6..55909dd 100644 --- a/content/browser/interest_group/auction_nonce_manager_unittest.cc +++ b/content/browser/interest_group/auction_nonce_manager_unittest.cc
@@ -3,10 +3,12 @@ // found in the LICENSE file. #include "content/browser/interest_group/auction_nonce_manager.h" + #include <memory> #include <string> #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "base/uuid.h" #include "content/public/test/browser_test_utils.h"
diff --git a/content/browser/interest_group/tools/adjustable_auction.cc b/content/browser/interest_group/tools/adjustable_auction.cc index 09fc0b1..3dbc565a 100644 --- a/content/browser/interest_group/tools/adjustable_auction.cc +++ b/content/browser/interest_group/tools/adjustable_auction.cc
@@ -13,6 +13,7 @@ #include "base/functional/bind.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" #include "base/task/sequenced_task_runner.h" #include "base/task/single_thread_task_runner.h" #include "base/test/gtest_util.h"
diff --git a/content/browser/isolated_origin_browsertest.cc b/content/browser/isolated_origin_browsertest.cc index a43efed..7019df9a 100644 --- a/content/browser/isolated_origin_browsertest.cc +++ b/content/browser/isolated_origin_browsertest.cc
@@ -3815,7 +3815,7 @@ const SiteInstanceImpl* const newshell_site_instance_impl = static_cast<SiteInstanceImpl*>( new_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance()); - if (AreAllSitesIsolatedForTesting()) { + if (AreStrictSiteInstancesEnabled()) { // At this point, the popup and the opener should still be in separate // SiteInstances. EXPECT_NE(newshell_site_instance_impl, root_site_instance_impl); @@ -4976,9 +4976,15 @@ } namespace { -bool HasDefaultSiteInstance(RenderFrameHost* rfh) { - return static_cast<SiteInstanceImpl*>(rfh->GetSiteInstance()) - ->IsDefaultSiteInstance(); +bool HasDefaultSiteInstanceOrGroup(RenderFrameHost* rfh) { + SiteInstanceImpl* site_instance = + static_cast<SiteInstanceImpl*>(rfh->GetSiteInstance()); + if (ShouldUseDefaultSiteInstanceGroup()) { + return site_instance->group() == + site_instance->DefaultSiteInstanceGroupForBrowsingInstance(); + } else { + return site_instance->IsDefaultSiteInstance(); + } } } // namespace @@ -5052,9 +5058,9 @@ EXPECT_EQ(a->GetSiteInstance(), d->GetSiteInstance()); EXPECT_NE(b->GetSiteInstance(), c->GetSiteInstance()); - EXPECT_FALSE(HasDefaultSiteInstance(a)); - EXPECT_FALSE(HasDefaultSiteInstance(b)); - EXPECT_FALSE(HasDefaultSiteInstance(c)); + EXPECT_FALSE(HasDefaultSiteInstanceOrGroup(a)); + EXPECT_FALSE(HasDefaultSiteInstanceOrGroup(b)); + EXPECT_FALSE(HasDefaultSiteInstanceOrGroup(c)); } else { // All sites that are not isolated should be in the same default // SiteInstance process. @@ -5064,12 +5070,18 @@ c->GetProcess()->GetDeprecatedID()); EXPECT_NE(a->GetSiteInstance(), b->GetSiteInstance()); - EXPECT_EQ(a->GetSiteInstance(), c->GetSiteInstance()); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_NE(a->GetSiteInstance(), c->GetSiteInstance()); + EXPECT_EQ(a->GetSiteInstance()->GetSiteInstanceGroupId(), + c->GetSiteInstance()->GetSiteInstanceGroupId()); + } else { + EXPECT_EQ(a->GetSiteInstance(), c->GetSiteInstance()); + } EXPECT_EQ(a->GetSiteInstance(), d->GetSiteInstance()); EXPECT_NE(b->GetSiteInstance(), c->GetSiteInstance()); - EXPECT_TRUE(HasDefaultSiteInstance(a)); - EXPECT_FALSE(HasDefaultSiteInstance(b)); + EXPECT_TRUE(HasDefaultSiteInstanceOrGroup(a)); + EXPECT_FALSE(HasDefaultSiteInstanceOrGroup(b)); } } @@ -6133,18 +6145,36 @@ EXPECT_TRUE(first_instance->GetProcess()->GetProcessLock().allows_any_site()); } -// TODO(crbug.com/390571607, yangsharon): Enable these tests once default -// SiteInstanceGroups has been implemented. -class IsolatedOriginTestWithStrictSiteInstances : public IsolatedOriginTest { +class IsolatedOriginTestWithDefaultSiteInstanceGroups + : public IsolatedOriginTest, + public ::testing::WithParamInterface<bool> { + public: void SetUpCommandLine(base::CommandLine* command_line) override { IsolatedOriginTest::SetUpCommandLine(command_line); command_line->AppendSwitch(switches::kDisableSiteIsolation); command_line->RemoveSwitch(switches::kSitePerProcess); + + if (IsDefaultSiteInstanceGroupEnabled()) { + feature_list_.InitAndEnableFeature(features::kDefaultSiteInstanceGroups); + } else { + feature_list_.InitAndDisableFeature(features::kDefaultSiteInstanceGroups); + } } + + static std::string DescribeParams( + const testing::TestParamInfo<ParamType>& info) { + return info.param ? "UseDefaultSiteInstanceGroups" + : "UseDefaultSiteInstances"; + } + + private: + bool IsDefaultSiteInstanceGroupEnabled() const { return GetParam(); } + + base::test::ScopedFeatureList feature_list_; }; -IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances, - DISABLED_NonIsolatedFramesCanShareDefaultProcess) { +IN_PROC_BROWSER_TEST_P(IsolatedOriginTestWithDefaultSiteInstanceGroups, + NonIsolatedFramesCanShareDefaultProcess) { GURL top_url( embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html")); ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(top_url))); @@ -6172,22 +6202,42 @@ observer.Wait(); } - // All 3 frames are from different sites, so each should have its own - // SiteInstance. - EXPECT_NE(root->current_frame_host()->GetSiteInstance(), - child1->current_frame_host()->GetSiteInstance()); - EXPECT_NE(root->current_frame_host()->GetSiteInstance(), - child2->current_frame_host()->GetSiteInstance()); - EXPECT_NE(child1->current_frame_host()->GetSiteInstance(), - child2->current_frame_host()->GetSiteInstance()); - EXPECT_EQ( - " Site A ------------ proxies for B C\n" - " |--Site B ------- proxies for A C\n" - " +--Site C ------- proxies for A B\n" - "Where A = http://127.0.0.1/\n" - " B = http://bar.com/\n" - " C = http://baz.com/", - DepictFrameTree(*root)); + if (ShouldUseDefaultSiteInstanceGroup()) { + // All 3 frames are different sites, so each should have its own + // SiteInstance. + EXPECT_NE(root->current_frame_host()->GetSiteInstance(), + child1->current_frame_host()->GetSiteInstance()); + EXPECT_NE(root->current_frame_host()->GetSiteInstance(), + child2->current_frame_host()->GetSiteInstance()); + EXPECT_NE(child1->current_frame_host()->GetSiteInstance(), + child2->current_frame_host()->GetSiteInstance()); + + // All 3 sites should share the default SiteInstanceGroup and be in the same + // process, so no proxies are needed. + EXPECT_EQ( + " Site A\n" + " |--Site B\n" + " +--Site C\n" + "Where A = http://127.0.0.1/\n" + " B = http://bar.com/\n" + " C = http://baz.com/", + DepictFrameTree(*root)); + } else { + // All 3 frames are in the default SiteInstance. + EXPECT_EQ(root->current_frame_host()->GetSiteInstance(), + child1->current_frame_host()->GetSiteInstance()); + EXPECT_EQ(root->current_frame_host()->GetSiteInstance(), + child2->current_frame_host()->GetSiteInstance()); + EXPECT_EQ(child1->current_frame_host()->GetSiteInstance(), + child2->current_frame_host()->GetSiteInstance()); + + EXPECT_EQ( + " Site A\n" + " |--Site A\n" + " +--Site A\n" + "Where A = http://unisolated.invalid/", + DepictFrameTree(*root)); + } // But none are isolated, so all should share the default process for their // BrowsingInstance. @@ -6199,11 +6249,10 @@ // Creates a non-isolated main frame with an isolated child and non-isolated // grandchild. With strict site isolation disabled and -// kProcessSharingWithStrictSiteInstances enabled, the main frame and the -// grandchild should be in the same process even though they have different -// SiteInstances. -IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances, - DISABLED_IsolatedChildWithNonIsolatedGrandchild) { +// default SiteInstanceGroups enabled, the main frame and the grandchild should +// be in the default SiteInstanceGroup with different SiteInstances. +IN_PROC_BROWSER_TEST_P(IsolatedOriginTestWithDefaultSiteInstanceGroups, + IsolatedChildWithNonIsolatedGrandchild) { GURL top_url( embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html")); ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(top_url))); @@ -6243,76 +6292,112 @@ observer.Wait(); EXPECT_EQ(non_isolated_url, grandchild->current_url()); - EXPECT_NE(root->current_frame_host()->GetSiteInstance(), - grandchild->current_frame_host()->GetSiteInstance()); EXPECT_NE(child->current_frame_host()->GetSiteInstance(), grandchild->current_frame_host()->GetSiteInstance()); EXPECT_EQ(root->current_frame_host()->GetProcess(), grandchild->current_frame_host()->GetProcess()); - EXPECT_EQ( - " Site A ------------ proxies for B C\n" - " +--Site B ------- proxies for A C\n" - " +--Site C -- proxies for A B\n" - "Where A = http://foo.com/\n" - " B = http://isolated.foo.com/\n" - " C = http://bar.com/", - DepictFrameTree(*root)); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_NE(root->current_frame_host()->GetSiteInstance(), + grandchild->current_frame_host()->GetSiteInstance()); + EXPECT_EQ( + " Site A ------------ proxies for B\n" + " +--Site B ------- proxies for {A,C}\n" + " +--Site C -- proxies for B\n" + "Where A = http://foo.com/\n" + " B = http://isolated.foo.com/\n" + " C = http://bar.com/", + DepictFrameTree(*root)); + } else { + EXPECT_EQ(root->current_frame_host()->GetSiteInstance(), + grandchild->current_frame_host()->GetSiteInstance()); + EXPECT_EQ( + " Site A ------------ proxies for B\n" + " +--Site B ------- proxies for A\n" + " +--Site A -- proxies for B\n" + "Where A = http://unisolated.invalid/\n" + " B = http://isolated.foo.com/", + DepictFrameTree(*root)); + } } // Navigate a frame into and out of an isolated origin. This should not -// confuse BrowsingInstance into holding onto a stale default_process_. -IN_PROC_BROWSER_TEST_F( - IsolatedOriginTestWithStrictSiteInstances, - DISABLED_SubframeNavigatesOutofIsolationThenToIsolation) { +// confuse BrowsingInstance into holding onto a stale default SiteInstance or +// default SiteInstanceGroup +IN_PROC_BROWSER_TEST_P(IsolatedOriginTestWithDefaultSiteInstanceGroups, + SubframeNavigatesOutOfIsolationThenToIsolation) { + // Navigate to an isolated site, with a same-site subframe. GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com", "/page_with_iframe.html")); ASSERT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url))); EXPECT_TRUE(NavigateToURL(shell(), isolated_url)); FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root(); + SiteInstanceImpl* root_instance = + root->current_frame_host()->GetSiteInstance(); FrameTreeNode* child = root->child_at(0); - EXPECT_EQ(web_contents()->GetSiteInstance(), - child->current_frame_host()->GetSiteInstance()); + SiteInstanceImpl* child_instance = + child->current_frame_host()->GetSiteInstance(); + EXPECT_EQ(web_contents()->GetSiteInstance(), child_instance); + EXPECT_EQ(root_instance->group(), child_instance->group()); EXPECT_FALSE(child->current_frame_host()->IsCrossProcessSubframe()); + EXPECT_FALSE(HasDefaultSiteInstanceOrGroup(child->current_frame_host())); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_NE(child_instance->group(), + child_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + } else { + EXPECT_FALSE(child_instance->IsDefaultSiteInstance()); + } + // Navigate the child to a non-isolated page. GURL non_isolated_url( - embedded_test_server()->GetURL("www.foo.com", "/title3.html")); + embedded_test_server()->GetURL("www.bar.com", "/title3.html")); ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(non_isolated_url))); NavigateIframeToURL(web_contents(), "test_iframe", non_isolated_url); + child_instance = child->current_frame_host()->GetSiteInstance(); + EXPECT_EQ(child->current_url(), non_isolated_url); + EXPECT_TRUE(HasDefaultSiteInstanceOrGroup(child->current_frame_host())); + EXPECT_NE(root_instance->group(), child_instance->group()); + // Keep this value for comparing later. + SiteInstanceGroup* first_default_group = child_instance->group(); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_EQ(child_instance->group(), + child_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + } else { + EXPECT_TRUE(child_instance->IsDefaultSiteInstance()); + } - // Verify that the child frame is an OOPIF with a different SiteInstance. - EXPECT_NE(web_contents()->GetSiteInstance(), - child->current_frame_host()->GetSiteInstance()); - EXPECT_NE(root->current_frame_host()->GetProcess(), - child->current_frame_host()->GetProcess()); + // Navigate to an isolated page without an iframe. This should cause the + // default SiteInstance/Group to be deleted. + GURL isolated_url2( + embedded_test_server()->GetURL("isolated.foo.com", "/title1.html")); + ASSERT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url2))); + EXPECT_TRUE(NavigateToURL(shell(), isolated_url2)); + EXPECT_FALSE( + HasDefaultSiteInstanceOrGroup(web_contents()->GetPrimaryMainFrame())); - // Navigating the child to the isolated origin again. - NavigateIframeToURL(web_contents(), "test_iframe", isolated_url); - EXPECT_EQ(child->current_url(), isolated_url); - EXPECT_EQ(web_contents()->GetSiteInstance(), - child->current_frame_host()->GetSiteInstance()); - - // And navigate out of the isolated origin one last time. - NavigateIframeToURL(web_contents(), "test_iframe", non_isolated_url); - EXPECT_EQ(child->current_url(), non_isolated_url); - EXPECT_NE(web_contents()->GetSiteInstance(), - child->current_frame_host()->GetSiteInstance()); - EXPECT_NE(root->current_frame_host()->GetProcess(), - child->current_frame_host()->GetProcess()); - EXPECT_EQ( - " Site A ------------ proxies for B\n" - " +--Site B ------- proxies for A\n" - "Where A = http://isolated.foo.com/\n" - " B = http://foo.com/", - DepictFrameTree(*root)); + // Navigate the main frame to bar.com. We expect bar's SiteInstance to be in + // the default SiteInstance/Group, but a different one from the subframe + // navigation, since that should have been destroyed when we navigated away, + // as it was the last and only SiteInstance in the default SiteInstance/Group. + EXPECT_TRUE(NavigateToURL(shell(), non_isolated_url)); + FrameTreeNode* bar = web_contents()->GetPrimaryFrameTree().root(); + SiteInstanceImpl* bar_instance = bar->current_frame_host()->GetSiteInstance(); + // The SiteInstanceGroup and process from the first time we navigated to + // bar.com should not be reused. + EXPECT_NE(bar_instance->group(), first_default_group); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_EQ(bar_instance->group(), + bar_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + } else { + EXPECT_TRUE(bar_instance->IsDefaultSiteInstance()); + } } // Ensure a popup and its opener can go in the same process, even though -// they have different SiteInstances with kProcessSharingWithStrictSiteInstances -// enabled. -IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances, - DISABLED_NonIsolatedPopup) { +// they have different SiteInstances with default SiteInstanceGroups enabled. +IN_PROC_BROWSER_TEST_P(IsolatedOriginTestWithDefaultSiteInstanceGroups, + NonIsolatedPopup) { GURL foo_url( embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html")); EXPECT_TRUE(NavigateToURL(shell(), foo_url)); @@ -6333,32 +6418,53 @@ ASSERT_TRUE(manager.WaitForNavigationFinished()); } - // The popup and the opener should not share a SiteInstance, but should - // end up in the same process. - EXPECT_NE(new_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance(), - root->current_frame_host()->GetSiteInstance()); + // The popup and the opener should not share a SiteInstance in default + // SiteInstanceGroup mode, but should end up in the same process in either + // mode. EXPECT_EQ(root->current_frame_host()->GetProcess(), new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess()); - EXPECT_EQ( - " Site A ------------ proxies for B\n" - " +--Site A ------- proxies for B\n" - "Where A = http://foo.com/\n" - " B = http://bar.com/", - DepictFrameTree(*root)); - EXPECT_EQ( - " Site A ------------ proxies for B\n" - "Where A = http://bar.com/\n" - " B = http://foo.com/", - DepictFrameTree(*static_cast<WebContentsImpl*>(new_shell->web_contents()) - ->GetPrimaryFrameTree() - .root())); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_NE( + new_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance(), + root->current_frame_host()->GetSiteInstance()); + // There should be no proxies between the popup and opener since they share + // a SiteInstanceGroup. + EXPECT_EQ( + " Site A\n" + " +--Site A\n" + "Where A = http://foo.com/", + DepictFrameTree(*root)); + EXPECT_EQ( + " Site A\n" + "Where A = http://bar.com/", + DepictFrameTree( + *static_cast<WebContentsImpl*>(new_shell->web_contents()) + ->GetPrimaryFrameTree() + .root())); + } else { + EXPECT_EQ( + new_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance(), + root->current_frame_host()->GetSiteInstance()); + EXPECT_EQ( + " Site A\n" + " +--Site A\n" + "Where A = http://unisolated.invalid/", + DepictFrameTree(*root)); + EXPECT_EQ( + " Site A\n" + "Where A = http://unisolated.invalid/", + DepictFrameTree( + *static_cast<WebContentsImpl*>(new_shell->web_contents()) + ->GetPrimaryFrameTree() + .root())); + } } // Check that when a cross-site, non-isolated-origin iframe opens a popup, // navigates it to an isolated origin, and then the popup navigates back to its // opener iframe's site, the popup and the opener iframe end up in the same // process and can script each other. See https://crbug.com/796912. -IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances, +IN_PROC_BROWSER_TEST_P(IsolatedOriginTestWithDefaultSiteInstanceGroups, PopupNavigatesToIsolatedOriginAndBack) { // Start on a page with same-site iframe. GURL foo_url( @@ -6415,6 +6521,64 @@ EXPECT_EQ(bar_url2.spec(), EvalJs(child, "window.w.location.href;")); } +// Make sure a navigation from about:blank in the default SiteInstanceGroup to +// an isolated site correctly gets a different process. Also makes sure opening +// a popup is same-SiteInstanceGroup. +IN_PROC_BROWSER_TEST_P(IsolatedOriginTestWithDefaultSiteInstanceGroups, + InitialEmptyDocumentToIsolatedSite) { + GURL foo_url(embedded_test_server()->GetURL("www.bar.com", "/title1.html")); + EXPECT_TRUE(NavigateToURL(shell(), foo_url)); + + ShellAddedObserver new_shell1_observer; + EXPECT_TRUE(ExecJs(shell(), "window.w = window.open();")); + Shell* new_shell1 = new_shell1_observer.GetShell(); + + ShellAddedObserver new_shell2_observer; + EXPECT_TRUE(ExecJs(shell(), "window.w = window.open();")); + Shell* new_shell2 = new_shell2_observer.GetShell(); + + // Everything should be in the same default SiteInstanceGroup, and thus same + // process, so far. + SiteInstanceImpl* root_site_instance = + web_contents()->GetPrimaryMainFrame()->GetSiteInstance(); + SiteInstanceImpl* popup1_site_instance = static_cast<SiteInstanceImpl*>( + new_shell1->web_contents()->GetPrimaryMainFrame()->GetSiteInstance()); + SiteInstanceImpl* popup2_site_instance = static_cast<SiteInstanceImpl*>( + new_shell2->web_contents()->GetPrimaryMainFrame()->GetSiteInstance()); + EXPECT_EQ(root_site_instance->group(), popup1_site_instance->group()); + EXPECT_EQ(popup2_site_instance->group(), popup1_site_instance->group()); + + // Navigate popup1 to an isolated site. + GURL isolated_url( + embedded_test_server()->GetURL("isolated.foo.com", "/title1.html")); + EXPECT_TRUE(IsIsolatedOrigin(isolated_url)); + { + TestNavigationManager manager(new_shell1->web_contents(), isolated_url); + EXPECT_TRUE( + ExecJs(new_shell1, "location.href = '" + isolated_url.spec() + "';")); + ASSERT_TRUE(manager.WaitForNavigationFinished()); + } + + SiteInstanceImpl* isolated_site_instance = static_cast<SiteInstanceImpl*>( + new_shell1->web_contents()->GetPrimaryMainFrame()->GetSiteInstance()); + EXPECT_NE(isolated_site_instance->group(), popup2_site_instance->group()); + EXPECT_NE(isolated_site_instance->group(), root_site_instance->group()); + EXPECT_NE(isolated_site_instance->group(), popup2_site_instance->group()); + EXPECT_EQ(popup2_site_instance->group(), root_site_instance->group()); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_EQ( + popup2_site_instance->group(), + popup2_site_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + EXPECT_NE( + isolated_site_instance->group(), + isolated_site_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + } else { + EXPECT_TRUE(popup2_site_instance->IsDefaultSiteInstance()); + EXPECT_TRUE(root_site_instance->IsDefaultSiteInstance()); + EXPECT_FALSE(isolated_site_instance->IsDefaultSiteInstance()); + } +} + class WildcardOriginIsolationTest : public IsolatedOriginTestBase { public: WildcardOriginIsolationTest() = default; @@ -7679,4 +7843,9 @@ } } +INSTANTIATE_TEST_SUITE_P( + All, + IsolatedOriginTestWithDefaultSiteInstanceGroups, + ::testing::Bool(), + &IsolatedOriginTestWithDefaultSiteInstanceGroups::DescribeParams); } // namespace content
diff --git a/content/browser/media/cdm_registry_impl.cc b/content/browser/media/cdm_registry_impl.cc index 3425512..ccd989d6 100644 --- a/content/browser/media/cdm_registry_impl.cc +++ b/content/browser/media/cdm_registry_impl.cc
@@ -106,16 +106,6 @@ base::UmaHistogramBoolean( uma_prefix + ".Support." + media::GetCodecNameForUMA(video_codec), is_supported); - - // When the codec is supported for hardware security, report whether - // clear lead is supported or not. - if (is_supported) { - bool is_clear_lead_supported = - video_codecs.at(video_codec).supports_clear_lead; - base::UmaHistogramBoolean(uma_prefix + ".ClearLeadSupport." + - media::GetCodecNameForUMA(video_codec), - is_clear_lead_supported); - } } } }
diff --git a/content/browser/media/media_capabilities_browsertest.cc b/content/browser/media/media_capabilities_browsertest.cc index 123088e..a5436de 100644 --- a/content/browser/media/media_capabilities_browsertest.cc +++ b/content/browser/media/media_capabilities_browsertest.cc
@@ -8,6 +8,7 @@ #include "base/command_line.h" #include "base/files/file_util.h" #include "base/strings/string_split.h" +#include "base/strings/stringprintf.h" #include "base/strings/to_string.h" #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h"
diff --git a/content/browser/mojo_sandbox_browsertest.cc b/content/browser/mojo_sandbox_browsertest.cc index bac07e6..60e1b63 100644 --- a/content/browser/mojo_sandbox_browsertest.cc +++ b/content/browser/mojo_sandbox_browsertest.cc
@@ -43,34 +43,32 @@ MojoSandboxTest(const MojoSandboxTest&) = delete; MojoSandboxTest& operator=(const MojoSandboxTest&) = delete; - using BeforeStartCallback = base::OnceCallback<void(UtilityProcessHost*)>; + using BeforeStartCallback = base::OnceCallback<void(UtilityProcessHost&)>; - void StartProcess(BeforeStartCallback callback = BeforeStartCallback()) { - host_ = std::make_unique<UtilityProcessHost>(); - host_->SetMetricsName("mojo_sandbox_test_process"); - if (callback) - std::move(callback).Run(host_.get()); - ASSERT_TRUE(host_->Start()); - } + mojo::Remote<mojom::TestService> StartProcessAndBindTestInterface( + bool unsandboxed = false) { + UtilityProcessHost::Options options; - mojo::Remote<mojom::TestService> BindTestService() { + options.WithMetricsName("mojo_sandbox_test_process"); + + if (unsandboxed) { + options.WithSandboxType(sandbox::mojom::Sandbox::kNoSandbox); + } mojo::Remote<mojom::TestService> test_service; - host_->GetChildProcess()->BindServiceInterface( + options.WithBoundServiceInterfaceOnChildProcess( test_service.BindNewPipeAndPassReceiver()); + + UtilityProcessHost::Start(std::move(options)); + return test_service; } - - void TearDownOnMainThread() override { host_.reset(); } - - protected: - std::unique_ptr<UtilityProcessHost> host_; }; // Ensures that a read-only shared memory region can be created within a // sandboxed process. IN_PROC_BROWSER_TEST_F(MojoSandboxTest, SubprocessReadOnlySharedMemoryRegion) { - StartProcess(); - mojo::Remote<mojom::TestService> test_service = BindTestService(); + mojo::Remote<mojom::TestService> test_service = + StartProcessAndBindTestInterface(); bool got_response = false; base::RunLoop run_loop; @@ -93,8 +91,8 @@ // Ensures that a writable shared memory region can be created within a // sandboxed process. IN_PROC_BROWSER_TEST_F(MojoSandboxTest, SubprocessWritableSharedMemoryRegion) { - StartProcess(); - mojo::Remote<mojom::TestService> test_service = BindTestService(); + mojo::Remote<mojom::TestService> test_service = + StartProcessAndBindTestInterface(); bool got_response = false; base::RunLoop run_loop; @@ -117,8 +115,8 @@ // Ensures that an unsafe shared memory region can be created within a // sandboxed process. IN_PROC_BROWSER_TEST_F(MojoSandboxTest, SubprocessUnsafeSharedMemoryRegion) { - StartProcess(); - mojo::Remote<mojom::TestService> test_service = BindTestService(); + mojo::Remote<mojom::TestService> test_service = + StartProcessAndBindTestInterface(); bool got_response = false; base::RunLoop run_loop; @@ -140,8 +138,8 @@ // Test for sandbox::policy::IsProcessSandboxed(). IN_PROC_BROWSER_TEST_F(MojoSandboxTest, IsProcessSandboxed) { - StartProcess(); - mojo::Remote<mojom::TestService> test_service = BindTestService(); + mojo::Remote<mojom::TestService> test_service = + StartProcessAndBindTestInterface(); // The browser should not be considered sandboxed. EXPECT_FALSE(sandbox::policy::Sandbox::IsProcessSandboxed()); @@ -167,10 +165,8 @@ #define MAYBE_NotIsProcessSandboxed NotIsProcessSandboxed #endif IN_PROC_BROWSER_TEST_F(MojoSandboxTest, MAYBE_NotIsProcessSandboxed) { - StartProcess(base::BindOnce([](UtilityProcessHost* host) { - host->SetSandboxType(sandbox::mojom::Sandbox::kNoSandbox); - })); - mojo::Remote<mojom::TestService> test_service = BindTestService(); + mojo::Remote<mojom::TestService> test_service = + StartProcessAndBindTestInterface(/*unsandboxed=*/true); // The browser should not be considered sandboxed. EXPECT_FALSE(sandbox::policy::Sandbox::IsProcessSandboxed());
diff --git a/content/browser/power_monitor_browsertest.cc b/content/browser/power_monitor_browsertest.cc index 2658c0d5..8de54d1 100644 --- a/content/browser/power_monitor_browsertest.cc +++ b/content/browser/power_monitor_browsertest.cc
@@ -147,13 +147,13 @@ base::OnceClosure utility_bound_closure) { utility_bound_closure_ = std::move(utility_bound_closure); - UtilityProcessHost* host = new UtilityProcessHost(); - host->SetMetricsName("test_process"); - host->SetName(u"TestProcess"); - EXPECT_TRUE(host->Start()); - - host->GetChildProcess()->BindReceiver( - power_monitor_test->BindNewPipeAndPassReceiver()); + UtilityProcessHost::Start( + UtilityProcessHost::Options() + .WithMetricsName("test_process") + .WithName(u"TestProcess") + .WithBoundReceiverOnChildProcessForTesting( + power_monitor_test->BindNewPipeAndPassReceiver()) + .Pass()); } void set_renderer_bound_closure(base::OnceClosure closure) {
diff --git a/content/browser/preloading/prerender/prerender_browsertest.cc b/content/browser/preloading/prerender/prerender_browsertest.cc index 01349d33..1b65121 100644 --- a/content/browser/preloading/prerender/prerender_browsertest.cc +++ b/content/browser/preloading/prerender/prerender_browsertest.cc
@@ -23,6 +23,7 @@ #include "base/scoped_observation.h" #include "base/strings/escape.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/lock.h" #include "base/task/single_thread_task_runner.h"
diff --git a/content/browser/preloading/prerender/prerender_host.cc b/content/browser/preloading/prerender/prerender_host.cc index 8e4d2198..5185918 100644 --- a/content/browser/preloading/prerender/prerender_host.cc +++ b/content/browser/preloading/prerender/prerender_host.cc
@@ -46,6 +46,10 @@ #include "third_party/blink/public/common/navigation/preloading_headers.h" #include "url/origin.h" +#if BUILDFLAG(IS_ANDROID) +#include "base/strings/stringprintf.h" +#endif + namespace content { namespace {
diff --git a/content/browser/process_internals/process_internals_handler_impl.cc b/content/browser/process_internals/process_internals_handler_impl.cc index 8b22cb0..88d4079 100644 --- a/content/browser/process_internals/process_internals_handler_impl.cc +++ b/content/browser/process_internals/process_internals_handler_impl.cc
@@ -10,6 +10,7 @@ #include <vector> #include "base/strings/strcat.h" +#include "base/strings/stringprintf.h" #include "content/browser/child_process_security_policy_impl.h" #include "content/browser/process_internals/process_internals.mojom.h" #include "content/browser/process_lock.h"
diff --git a/content/browser/renderer_host/browsing_context_group_swap_browsertest.cc b/content/browser/renderer_host/browsing_context_group_swap_browsertest.cc index 269b3f3..0b255470 100644 --- a/content/browser/renderer_host/browsing_context_group_swap_browsertest.cc +++ b/content/browser/renderer_host/browsing_context_group_swap_browsertest.cc
@@ -6,6 +6,7 @@ #include <optional> +#include "base/strings/stringprintf.h" #include "base/test/scoped_feature_list.h" #include "content/browser/renderer_host/navigation_request.h" #include "content/public/browser/web_contents_observer.h"
diff --git a/content/browser/renderer_host/input/composited_scrolling_browsertest.cc b/content/browser/renderer_host/input/composited_scrolling_browsertest.cc index 2b9da97..93ca5937 100644 --- a/content/browser/renderer_host/input/composited_scrolling_browsertest.cc +++ b/content/browser/renderer_host/input/composited_scrolling_browsertest.cc
@@ -9,6 +9,7 @@ #include "base/functional/bind.h" #include "base/numerics/angle_conversions.h" #include "base/run_loop.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h"
diff --git a/content/browser/renderer_host/navigation_controller_history_intervention_browsertest.cc b/content/browser/renderer_host/navigation_controller_history_intervention_browsertest.cc index ced39b47..ca0c127 100644 --- a/content/browser/renderer_host/navigation_controller_history_intervention_browsertest.cc +++ b/content/browser/renderer_host/navigation_controller_history_intervention_browsertest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/strings/stringprintf.h" #include "base/test/scoped_feature_list.h" #include "content/browser/renderer_host/frame_tree.h" #include "content/browser/renderer_host/navigation_controller_impl.h"
diff --git a/content/browser/renderer_host/navigation_transitions/navigation_entry_screenshot_browsertest.cc b/content/browser/renderer_host/navigation_transitions/navigation_entry_screenshot_browsertest.cc index 6b7d7d2..e9f48ae 100644 --- a/content/browser/renderer_host/navigation_transitions/navigation_entry_screenshot_browsertest.cc +++ b/content/browser/renderer_host/navigation_transitions/navigation_entry_screenshot_browsertest.cc
@@ -1201,6 +1201,12 @@ // Regression test for https://crbug.com/368289857. IN_PROC_BROWSER_TEST_P(NavigationEntryScreenshotBrowserTest, NavigateWhileHidden_NotCaptured) { + // TODO(crbug.com/390571607): Update this test to support default + // SiteInstanceGroup in all parameterization modes. + if (ShouldUseDefaultSiteInstanceGroup()) { + GTEST_SKIP(); + } + const size_t page_size = GetUncompressedScreenshotSizeInBytes(); const size_t memory_budget = 10 * page_size; auto* manager = GetManagerForTab(web_contents());
diff --git a/content/browser/renderer_host/proactively_swap_browsing_instances_browsertest.cc b/content/browser/renderer_host/proactively_swap_browsing_instances_browsertest.cc index af7d50e..601f925 100644 --- a/content/browser/renderer_host/proactively_swap_browsing_instances_browsertest.cc +++ b/content/browser/renderer_host/proactively_swap_browsing_instances_browsertest.cc
@@ -274,14 +274,20 @@ web_contents->GetPrimaryMainFrame()->GetSiteInstance()); // Check that A and B are in different BrowsingInstances and renderer - // process. Without full site isolation, A and B are both default - // SiteInstances of different BrowsingInstances. + // processes. Without full site isolation, A and B are either both default + // SiteInstances or in the default SiteInstanceGroup of different + // BrowsingInstances. EXPECT_FALSE(a_site_instance->IsRelatedSiteInstance(b_site_instance.get())); EXPECT_NE(a_site_instance->GetProcess(), b_site_instance->GetProcess()); - EXPECT_EQ(!AreAllSitesIsolatedForTesting(), - a_site_instance->IsDefaultSiteInstance()); - EXPECT_EQ(!AreAllSitesIsolatedForTesting(), - b_site_instance->IsDefaultSiteInstance()); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_EQ(a_site_instance->group(), + a_site_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + EXPECT_EQ(b_site_instance->group(), + b_site_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + } else { + EXPECT_TRUE(a_site_instance->IsDefaultSiteInstance()); + EXPECT_TRUE(b_site_instance->IsDefaultSiteInstance()); + } } // Different from renderer-initiated cross-site navigations, browser-initiated @@ -311,14 +317,20 @@ web_contents->GetPrimaryMainFrame()->GetSiteInstance()); // Check that A and B are in different BrowsingInstances and renderer - // processes. Without full site isolation, A and B are both default - // SiteInstances of different BrowsingInstances. + // processes. Without full site isolation, A and B are either both default + // SiteInstances or in the default SiteInstanceGroup of different + // BrowsingInstances. EXPECT_FALSE(a_site_instance->IsRelatedSiteInstance(b_site_instance.get())); EXPECT_NE(a_site_instance->GetProcess(), b_site_instance->GetProcess()); - EXPECT_EQ(!AreAllSitesIsolatedForTesting(), - a_site_instance->IsDefaultSiteInstance()); - EXPECT_EQ(!AreAllSitesIsolatedForTesting(), - b_site_instance->IsDefaultSiteInstance()); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_EQ(a_site_instance->group(), + a_site_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + EXPECT_EQ(b_site_instance->group(), + b_site_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + } else { + EXPECT_TRUE(a_site_instance->IsDefaultSiteInstance()); + EXPECT_TRUE(b_site_instance->IsDefaultSiteInstance()); + } } // A test ContentBrowserClient implementation that enforce process-per-site mode @@ -380,10 +392,15 @@ // processes. EXPECT_FALSE(a_site_instance->IsRelatedSiteInstance(b_site_instance.get())); EXPECT_NE(b_site_instance->GetProcess(), original_process); - EXPECT_EQ(!AreAllSitesIsolatedForTesting(), - a_site_instance->IsDefaultSiteInstance()); - EXPECT_EQ(!AreAllSitesIsolatedForTesting(), - b_site_instance->IsDefaultSiteInstance()); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_EQ(a_site_instance->group(), + a_site_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + EXPECT_EQ(b_site_instance->group(), + b_site_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + } else { + EXPECT_TRUE(a_site_instance->IsDefaultSiteInstance()); + EXPECT_TRUE(b_site_instance->IsDefaultSiteInstance()); + } // Make sure we will use process-per-site for C. // Note this is enforcing process-per-site for all sites, which is why we turn @@ -400,8 +417,12 @@ // Check that B and C are in different BrowsingInstances and renderer // processes. EXPECT_FALSE(b_site_instance->IsRelatedSiteInstance(c_site_instance.get())); - EXPECT_EQ(!AreAllSitesIsolatedForTesting(), - c_site_instance->IsDefaultSiteInstance()); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_EQ(c_site_instance->group(), + c_site_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + } else { + EXPECT_TRUE(c_site_instance->IsDefaultSiteInstance()); + } EXPECT_NE(c_site_instance->GetProcess(), original_process); // C is using the process for C's site. EXPECT_EQ(c_site_instance->GetProcess(), @@ -419,8 +440,12 @@ web_contents->GetPrimaryMainFrame()->GetSiteInstance()); EXPECT_FALSE(b2_site_instance->IsRelatedSiteInstance(c_site_instance.get())); EXPECT_FALSE(b2_site_instance->IsRelatedSiteInstance(b_site_instance.get())); - EXPECT_EQ(!AreAllSitesIsolatedForTesting(), - b2_site_instance->IsDefaultSiteInstance()); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_EQ(b2_site_instance->group(), + b2_site_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + } else { + EXPECT_TRUE(b2_site_instance->IsDefaultSiteInstance()); + } EXPECT_NE(b2_site_instance->GetProcess(), original_process); // Check that B and C are in different renderer processes. EXPECT_NE(b2_site_instance->GetProcess(), c_site_instance->GetProcess()); @@ -473,10 +498,15 @@ // Check that A and B are in different BrowsingInstances but B should use the // sole process assigned to site B. EXPECT_FALSE(a_site_instance->IsRelatedSiteInstance(b_site_instance.get())); - EXPECT_EQ(!AreAllSitesIsolatedForTesting(), - a_site_instance->IsDefaultSiteInstance()); - EXPECT_EQ(!AreAllSitesIsolatedForTesting(), - b_site_instance->IsDefaultSiteInstance()); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_EQ(a_site_instance->group(), + a_site_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + EXPECT_EQ(b_site_instance->group(), + b_site_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + } else { + EXPECT_TRUE(a_site_instance->IsDefaultSiteInstance()); + EXPECT_TRUE(b_site_instance->IsDefaultSiteInstance()); + } EXPECT_NE(b_site_instance->GetProcess(), original_process); EXPECT_EQ(b_site_instance->GetProcess(), process_for_b); EXPECT_EQ(b_site_instance->GetProcess(), @@ -1302,7 +1332,7 @@ // same renderer process. // If site isolation is turned off, it will hit the case at crbug.com/1094147. EXPECT_FALSE(site_instance_2->IsRelatedSiteInstance(site_instance_3.get())); - if (AreAllSitesIsolatedForTesting()) { + if (AreStrictSiteInstancesEnabled()) { EXPECT_EQ(site_instance_2->GetProcess(), site_instance_3->GetProcess()); } else { EXPECT_NE(site_instance_2->GetProcess(), site_instance_3->GetProcess());
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc index 6319452..03abd477 100644 --- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc +++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -8604,7 +8604,9 @@ ->RequiresDedicatedProcess(); // `shell()` and `second_shell` opened different sites. - if (requires_dedicated_process) { + // TODO(crbug.com/419469455): Make sure metrics are updated correctly with the + // introduction of default SiteInstanceGroup. + if (requires_dedicated_process || ShouldUseDefaultSiteInstanceGroup()) { EXPECT_THAT(histogram.GetAllSamples( "SiteIsolation." "NewProcessUsedForNavigationWhenSameSiteProcessExists"), @@ -8618,7 +8620,7 @@ ASSERT_TRUE(NavigateToURL(second_shell, url)); // Now `shell()` and `second_shell` opened the same site. - if (requires_dedicated_process) { + if (requires_dedicated_process || ShouldUseDefaultSiteInstanceGroup()) { EXPECT_THAT( histogram.GetAllSamples( "SiteIsolation."
diff --git a/content/browser/renderer_host/render_frame_host_manager.cc b/content/browser/renderer_host/render_frame_host_manager.cc index 68bc5be..a194fbb 100644 --- a/content/browser/renderer_host/render_frame_host_manager.cc +++ b/content/browser/renderer_host/render_frame_host_manager.cc
@@ -3602,7 +3602,10 @@ // does not yet support OOPIFs (https://crbug.com/1101214). // TODO(crbug.com/40704573): Remove this block when default // SiteInstances support file: URLs. - if (!frame_tree_node_->IsMainFrame()) { + // TODO(crbug.com/419595581): Make sure default SiteInstanceGroup is safe for + // Android WebView before enabling experiments on that platform. + if (!frame_tree_node_->IsMainFrame() && + !ShouldUseDefaultSiteInstanceGroup()) { RenderFrameHostImpl* parent = frame_tree_node_->parent(); auto& parent_isolation_context = parent->GetSiteInstance()->GetIsolationContext();
diff --git a/content/browser/renderer_host/render_process_host_unittest.cc b/content/browser/renderer_host/render_process_host_unittest.cc index 041ec18..61ff38dc 100644 --- a/content/browser/renderer_host/render_process_host_unittest.cc +++ b/content/browser/renderer_host/render_process_host_unittest.cc
@@ -1391,10 +1391,13 @@ refuse_reason_ = reason; } - std::optional<SpareProcessRefusedByEmbedderReason> - ShouldUseSpareRenderProcessHost(BrowserContext* browser_context, - const GURL& site_url) override { - return refuse_reason_; + bool ShouldUseSpareRenderProcessHost( + BrowserContext* browser_context, + const GURL& site_url, + std::optional<SpareProcessRefusedByEmbedderReason>& refused_reason) + override { + refused_reason = refuse_reason_; + return false; } private:
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_aura_browsertest.cc index 1d741f0..6453ca21 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura_browsertest.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura_browsertest.cc
@@ -265,6 +265,7 @@ "</body>" "</html>"); EXPECT_TRUE(NavigateToURL(shell(), page)); + SimulateEndOfPaintHoldingOnPrimaryMainFrame(shell()->web_contents()); auto* wc = shell()->web_contents(); ASSERT_TRUE(ExecJs(wc, "focusSelectMenu();")); @@ -465,6 +466,8 @@ "</html>"); EXPECT_TRUE(NavigateToURL(shell(), page)); + SimulateEndOfPaintHoldingOnPrimaryMainFrame(shell()->web_contents()); + auto* wc = shell()->web_contents(); Attach(); SendCommandSync("Debugger.enable");
diff --git a/content/browser/renderer_host/scroll_into_view_browsertest.cc b/content/browser/renderer_host/scroll_into_view_browsertest.cc index b9bb46d..f12cc12c 100644 --- a/content/browser/renderer_host/scroll_into_view_browsertest.cc +++ b/content/browser/renderer_host/scroll_into_view_browsertest.cc
@@ -7,6 +7,7 @@ #include "base/json/json_reader.h" #include "base/strings/strcat.h" +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "base/values.h"
diff --git a/content/browser/renderer_host/spare_render_process_host_manager_browsertest.cc b/content/browser/renderer_host/spare_render_process_host_manager_browsertest.cc index 4010947..67a1250 100644 --- a/content/browser/renderer_host/spare_render_process_host_manager_browsertest.cc +++ b/content/browser/renderer_host/spare_render_process_host_manager_browsertest.cc
@@ -536,10 +536,13 @@ return true; } - std::optional<SpareProcessRefusedByEmbedderReason> - ShouldUseSpareRenderProcessHost(BrowserContext* browser_context, - const GURL& site_url) override { - return SpareProcessRefusedByEmbedderReason::DefaultDisabled; + bool ShouldUseSpareRenderProcessHost( + BrowserContext* browser_context, + const GURL& site_url, + std::optional<SpareProcessRefusedByEmbedderReason>& refused_reason) + override { + refused_reason = std::nullopt; + return false; } };
diff --git a/content/browser/renderer_host/spare_render_process_host_manager_impl.cc b/content/browser/renderer_host/spare_render_process_host_manager_impl.cc index 3e3c20a2..0591c576 100644 --- a/content/browser/renderer_host/spare_render_process_host_manager_impl.cc +++ b/content/browser/renderer_host/spare_render_process_host_manager_impl.cc
@@ -659,10 +659,11 @@ // ShouldUseSpareRenderProcessHost starts covering non-process-per-site // scenarios). std::optional<ContentBrowserClient::SpareProcessRefusedByEmbedderReason> - refuse_reason = - GetContentClient()->browser()->ShouldUseSpareRenderProcessHost( - browser_context, site_instance->GetSiteInfo().site_url()); - if (refuse_reason.has_value()) { + refuse_reason; + if (!GetContentClient()->browser()->ShouldUseSpareRenderProcessHost( + browser_context, site_instance->GetSiteInfo().site_url(), + refuse_reason)) { + CHECK(refuse_reason.has_value()); return refuse_reason; }
diff --git a/content/browser/renderer_host/unassigned_site_instance_browsertest.cc b/content/browser/renderer_host/unassigned_site_instance_browsertest.cc index a7ef8cfe..863b1ac2 100644 --- a/content/browser/renderer_host/unassigned_site_instance_browsertest.cc +++ b/content/browser/renderer_host/unassigned_site_instance_browsertest.cc
@@ -7,6 +7,7 @@ #include "base/memory/raw_ptr.h" #include "base/memory/scoped_refptr.h" +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "content/browser/process_lock.h"
diff --git a/content/browser/security/coop/cross_origin_opener_policy_browsertest.cc b/content/browser/security/coop/cross_origin_opener_policy_browsertest.cc index a743620..beec338d 100644 --- a/content/browser/security/coop/cross_origin_opener_policy_browsertest.cc +++ b/content/browser/security/coop/cross_origin_opener_policy_browsertest.cc
@@ -4,6 +4,7 @@ #include "base/command_line.h" #include "base/strings/escape.h" +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "base/test/gtest_util.h" #include "base/test/scoped_feature_list.h" @@ -284,14 +285,20 @@ // Android to limit the number of processes. Testing these particularities of // the process model and their interaction with cross-origin isolation requires // to disable SiteIsolation. +// TODO(crbug.com/390571607): Remove this version of the test in favour of the +// SiteInstanceGroup version below once default SiteInstanceGroups are +// enabled by default. class NoSiteIsolationCrossOriginIsolationBrowserTest : public CrossOriginOpenerPolicyBrowserTest { public: NoSiteIsolationCrossOriginIsolationBrowserTest() { // Disable the heuristic to isolate COOP pages from the default - // SiteInstance. This is otherwise on by default on Android. + // SiteInstance. This is otherwise on by default on Android. Disable default + // SiteInstanceGroups, as that's covered in the SiteInstanceGroup version of + // this test. feature_list_.InitWithFeatures( - {}, {features::kSiteIsolationForCrossOriginOpenerPolicy}); + {}, {features::kSiteIsolationForCrossOriginOpenerPolicy, + features::kDefaultSiteInstanceGroups}); } void SetUpOnMainThread() override { @@ -332,6 +339,20 @@ base::test::ScopedFeatureList feature_list_; }; +// This test is the same as NoSiteIsolationCrossOriginIsolationBrowserTest, +// except it enables and uses default SiteInstanceGroup instead of default +// SiteInstance. +class DefaultSiteInstanceGroupCrossOriginIsolationBrowserTest + : public NoSiteIsolationCrossOriginIsolationBrowserTest { + public: + DefaultSiteInstanceGroupCrossOriginIsolationBrowserTest() { + feature_list_.InitWithFeatures({features::kDefaultSiteInstanceGroups}, {}); + } + + private: + base::test::ScopedFeatureList feature_list_; +}; + using VirtualBrowsingContextGroupTest = CrossOriginOpenerPolicyBrowserTest; using SoapByDefaultVirtualBrowsingContextGroupTest = CrossOriginOpenerPolicyBrowserTest; @@ -4325,6 +4346,11 @@ NoSiteIsolationCrossOriginIsolationBrowserTest, kTestParams, CrossOriginOpenerPolicyBrowserTest::DescribeParams); +INSTANTIATE_TEST_SUITE_P( + All, + DefaultSiteInstanceGroupCrossOriginIsolationBrowserTest, + kTestParams, + CrossOriginOpenerPolicyBrowserTest::DescribeParams); INSTANTIATE_TEST_SUITE_P(All, ProcessReuseOnPrerenderCOOPSwapBrowserTest, kTestParams, @@ -5221,6 +5247,64 @@ } } +IN_PROC_BROWSER_TEST_P(DefaultSiteInstanceGroupCrossOriginIsolationBrowserTest, + COICanLiveInDefaultSiteInstanceGroup) { + GURL isolated_page( + https_server()->GetURL("a.test", + "/set-header" + "?cross-origin-opener-policy: same-origin" + "&cross-origin-embedder-policy: require-corp")); + GURL non_isolated_page(https_server()->GetURL("a.test", "/title1.html")); + + EXPECT_TRUE(NavigateToURL(shell(), isolated_page)); + SiteInstanceImpl* main_frame_si = current_frame_host()->GetSiteInstance(); + EXPECT_TRUE(main_frame_si->IsCrossOriginIsolated()); + EXPECT_EQ(main_frame_si->group(), + main_frame_si->DefaultSiteInstanceGroupForBrowsingInstance()); + + { + // Open a popup to a page with similar isolation. Pages that have compatible + // cross origin isolation should be put in the same default + // SiteInstanceGroup. + ShellAddedObserver shell_observer; + EXPECT_TRUE(ExecJs(current_frame_host(), + JsReplace("window.open($1);", isolated_page))); + WebContentsImpl* popup = static_cast<WebContentsImpl*>( + shell_observer.GetShell()->web_contents()); + EXPECT_TRUE(WaitForLoadStop(popup)); + + SiteInstanceImpl* popup_si = + popup->GetPrimaryMainFrame()->GetSiteInstance(); + EXPECT_TRUE(popup_si->IsCrossOriginIsolated()); + EXPECT_EQ(popup_si->group(), + popup_si->DefaultSiteInstanceGroupForBrowsingInstance()); + EXPECT_EQ(popup_si, main_frame_si); + + popup->Close(); + } + + { + // Open a popup to a same origin non-isolated page. This page should live in + // a different BrowsingInstance in the default non-isolated + // SiteInstanceGroup. + ShellAddedObserver shell_observer; + EXPECT_TRUE(ExecJs(current_frame_host(), + JsReplace("window.open($1);", non_isolated_page))); + WebContentsImpl* popup = static_cast<WebContentsImpl*>( + shell_observer.GetShell()->web_contents()); + EXPECT_TRUE(WaitForLoadStop(popup)); + + SiteInstanceImpl* popup_si = + popup->GetPrimaryMainFrame()->GetSiteInstance(); + EXPECT_FALSE(popup_si->IsCrossOriginIsolated()); + EXPECT_EQ(popup_si->group(), + popup_si->DefaultSiteInstanceGroupForBrowsingInstance()); + EXPECT_NE(popup_si, main_frame_si); + + popup->Close(); + } +} + IN_PROC_BROWSER_TEST_P(CrossOriginOpenerPolicyBrowserTest, ConsoleErrorOnWindowLocationAccess) { const GURL non_coop_page = https_server()->GetURL("a.test", "/title1.html");
diff --git a/content/browser/security/dip/document_isolation_policy_browsertest.cc b/content/browser/security/dip/document_isolation_policy_browsertest.cc index bbc743f..025fbeb9 100644 --- a/content/browser/security/dip/document_isolation_policy_browsertest.cc +++ b/content/browser/security/dip/document_isolation_policy_browsertest.cc
@@ -6,6 +6,7 @@ #include "base/command_line.h" #include "base/strings/escape.h" +#include "base/strings/stringprintf.h" #include "base/test/bind.h" #include "base/test/gtest_util.h" #include "base/test/scoped_feature_list.h"
diff --git a/content/browser/service_host/service_process_host_impl.cc b/content/browser/service_host/service_process_host_impl.cc index 96c9563..d9c14f9 100644 --- a/content/browser/service_host/service_process_host_impl.cc +++ b/content/browser/service_host/service_process_host_impl.cc
@@ -39,35 +39,42 @@ // TODO(crbug.com/40633267): Once UtilityProcessHost is used only by service // processes, its logic can be inlined here. void LaunchServiceProcess(mojo::GenericPendingReceiver receiver, - ServiceProcessHost::Options options, + ServiceProcessHost::Options service_options, sandbox::mojom::Sandbox sandbox) { - UtilityProcessHost* host = - new UtilityProcessHost(std::make_unique<UtilityProcessClient>( - *receiver.interface_name(), options.site, - std::move(options.process_callback))); - host->SetName(!options.display_name.empty() - ? options.display_name - : base::UTF8ToUTF16(*receiver.interface_name())); - host->SetMetricsName(*receiver.interface_name()); if (!ShouldEnableSandbox(sandbox)) { sandbox = sandbox::mojom::Sandbox::kNoSandbox; } - host->SetSandboxType(sandbox); - host->SetExtraCommandLineSwitches(std::move(options.extra_switches)); - if (options.child_flags) { - host->set_child_flags(*options.child_flags); + UtilityProcessHost::Options utility_options; + + const auto service_interface_name = receiver.interface_name().value(); + + utility_options + .WithName(!service_options.display_name.empty() + ? service_options.display_name + : base::UTF8ToUTF16(service_interface_name)) + .WithMetricsName(service_interface_name) + .WithSandboxType(sandbox) + .WithExtraCommandLineSwitches(std::move(service_options.extra_switches)); + + if (service_options.child_flags) { + utility_options.WithChildFlags(*service_options.child_flags); } #if BUILDFLAG(IS_WIN) - if (!options.preload_libraries.empty()) { - host->SetPreloadLibraries(options.preload_libraries); + if (!service_options.preload_libraries.empty()) { + utility_options.WithPreloadLibraries(service_options.preload_libraries); } #endif // BUILDFLAG(IS_WIN) - if (options.allow_gpu_client.has_value() && - options.allow_gpu_client.value()) { - host->SetAllowGpuClient(); + if (service_options.allow_gpu_client.has_value() && + service_options.allow_gpu_client.value()) { + utility_options.WithGpuClientAllowed(); } - host->Start(); - host->GetChildProcess()->BindServiceInterface(std::move(receiver)); + + utility_options.WithBoundServiceInterfaceOnChildProcess(std::move(receiver)); + + UtilityProcessHost::Start(std::move(utility_options), + std::make_unique<UtilityProcessClient>( + service_interface_name, service_options.site, + std::move(service_options.process_callback))); } } // namespace
diff --git a/content/browser/service_host/utility_process_host.cc b/content/browser/service_host/utility_process_host.cc index e2c72b43..7db71d2 100644 --- a/content/browser/service_host/utility_process_host.cc +++ b/content/browser/service_host/utility_process_host.cc
@@ -148,21 +148,30 @@ g_utility_main_thread_factory = create; } -UtilityProcessHost::UtilityProcessHost() - : UtilityProcessHost(nullptr /* client */) {} - -UtilityProcessHost::UtilityProcessHost(std::unique_ptr<Client> client) +UtilityProcessHost::Options::Options() : sandbox_type_(sandbox::mojom::Sandbox::kUtility), #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) child_flags_(ChildProcessHost::CHILD_ALLOW_SELF), #else child_flags_(ChildProcessHost::CHILD_NORMAL), #endif - started_(false), - name_(u"utility process"), - file_data_(std::make_unique<ChildProcessLauncherFileData>()), #if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) allowed_gpu_(false), +#endif // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) + file_data_(std::make_unique<ChildProcessLauncherFileData>()), + name_(u"utility process") { +} + +UtilityProcessHost::Options::~Options() = default; + +UtilityProcessHost::Options& UtilityProcessHost::Options::operator=( + UtilityProcessHost::Options&&) = default; +UtilityProcessHost::Options::Options(Options&&) = default; + +UtilityProcessHost::UtilityProcessHost(Options options, + std::unique_ptr<Client> client) + : options_(std::move(options)), +#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) gpu_client_(nullptr, base::OnTaskRunnerDeleter(nullptr)), #endif // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) client_(std::move(client)) { @@ -178,82 +187,132 @@ } } -base::WeakPtr<UtilityProcessHost> UtilityProcessHost::AsWeakPtr() { - return weak_ptr_factory_.GetWeakPtr(); -} - -void UtilityProcessHost::SetSandboxType(sandbox::mojom::Sandbox sandbox_type) { +UtilityProcessHost::Options& UtilityProcessHost::Options::WithSandboxType( + sandbox::mojom::Sandbox sandbox_type) { sandbox_type_ = sandbox_type; + return *this; } -const ChildProcessData& UtilityProcessHost::GetData() { - return process_->GetData(); -} - -#if BUILDFLAG(IS_POSIX) -void UtilityProcessHost::SetEnv(const base::EnvironmentMap& env) { - env_ = env; -} -#endif - -bool UtilityProcessHost::Start() { - return StartProcess(); -} - -void UtilityProcessHost::SetMetricsName(const std::string& metrics_name) { - metrics_name_ = metrics_name; -} - -void UtilityProcessHost::SetName(const std::u16string& name) { +UtilityProcessHost::Options& UtilityProcessHost::Options::WithName( + const std::u16string& name) { name_ = name; + return *this; } -void UtilityProcessHost::SetExtraCommandLineSwitches( +UtilityProcessHost::Options& UtilityProcessHost::Options::WithMetricsName( + const std::string& metrics_name) { + metrics_name_ = metrics_name; + return *this; +} + +UtilityProcessHost::Options& UtilityProcessHost::Options::WithChildFlags( + int flags) { + child_flags_ = flags; + return *this; +} + +UtilityProcessHost::Options& +UtilityProcessHost::Options::WithExtraCommandLineSwitches( std::vector<std::string> switches) { extra_switches_ = std::move(switches); + return *this; } #if BUILDFLAG(IS_WIN) -void UtilityProcessHost::SetPreloadLibraries( +UtilityProcessHost::Options& UtilityProcessHost::Options::WithPreloadLibraries( const std::vector<base::FilePath>& preloads) { preload_libraries_ = preloads; + return *this; } #endif // BUILDFLAG(IS_WIN) -void UtilityProcessHost::SetAllowGpuClient() { +UtilityProcessHost::Options& +UtilityProcessHost::Options::WithGpuClientAllowed() { #if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) allowed_gpu_ = true; #endif // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) + return *this; } #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) -void UtilityProcessHost::AddFileToPreload( +UtilityProcessHost::Options& UtilityProcessHost::Options::WithFileToPreload( std::string key, std::variant<base::FilePath, base::ScopedFD> file) { DCHECK_EQ(file_data_->files_to_preload.count(key), 0u); file_data_->files_to_preload.insert({std::move(key), std::move(file)}); + return *this; } #endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) +#if BUILDFLAG(IS_POSIX) +UtilityProcessHost::Options& UtilityProcessHost::Options::WithEnvironment( + const base::EnvironmentMap& env) { + env_ = env; + return *this; +} +#endif + #if BUILDFLAG(USE_ZYGOTE) -void UtilityProcessHost::SetZygoteForTesting(ZygoteCommunication* handle) { +UtilityProcessHost::Options& UtilityProcessHost::Options::WithZygoteForTesting( + ZygoteCommunication* handle) { zygote_for_testing_ = handle; + return *this; } #endif // BUILDFLAG(USE_ZYGOTE) +UtilityProcessHost::Options& +UtilityProcessHost::Options::WithBoundReceiverOnChildProcessForTesting( + mojo::GenericPendingReceiver receiver) { + CHECK(!receiver_to_bind_.has_value()) << "Can only bind one receiver."; + receiver_to_bind_.emplace(std::move(receiver)); + return *this; +} + +UtilityProcessHost::Options& +UtilityProcessHost::Options::WithBoundServiceInterfaceOnChildProcess( + mojo::GenericPendingReceiver receiver) { + CHECK(!service_interface_to_bind_.has_value()) + << "Can only bind one service interface."; + service_interface_to_bind_.emplace(std::move(receiver)); + return *this; +} + +UtilityProcessHost::Options UtilityProcessHost::Options::Pass() { + return std::move(*this); +} + +// static +bool UtilityProcessHost::Start(Options options, + std::unique_ptr<Client> client) { + UtilityProcessHost* host = + new UtilityProcessHost(std::move(options), std::move(client)); + if (!host->StartProcess()) { + return false; + } + + host->MaybeBindMojoInterfaces(); + + return true; +} + mojom::ChildProcess* UtilityProcessHost::GetChildProcess() { return static_cast<ChildProcessHostImpl*>(process_->GetHost()) ->child_process(); } -bool UtilityProcessHost::StartProcess() { - if (started_) { - return true; +void UtilityProcessHost::MaybeBindMojoInterfaces() { + if (options_.receiver_to_bind_) { + GetChildProcess()->BindReceiver(std::move(*options_.receiver_to_bind_)); } + if (options_.service_interface_to_bind_) { + GetChildProcess()->BindServiceInterface( + std::move(*options_.service_interface_to_bind_)); + } +} - started_ = true; - process_->SetName(name_); - process_->SetMetricsName(metrics_name_); +bool UtilityProcessHost::StartProcess() { + process_->SetName(options_.name_); + process_->SetMetricsName(options_.metrics_name_); if (RenderProcessHost::run_renderer_in_process()) { DCHECK(g_utility_main_thread_factory); @@ -263,218 +322,223 @@ InProcessChildThreadParams(GetIOThreadTaskRunner({}), process_->GetInProcessMojoInvitation()))); in_process_thread_->Start(); - } else { - const base::CommandLine& browser_command_line = - *base::CommandLine::ForCurrentProcess(); + return true; + } - bool has_cmd_prefix = - browser_command_line.HasSwitch(switches::kUtilityCmdPrefix); + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); + + bool has_cmd_prefix = + browser_command_line.HasSwitch(switches::kUtilityCmdPrefix); #if BUILDFLAG(IS_ANDROID) - // readlink("/prof/self/exe") sometimes fails on Android at startup. - // As a workaround skip calling it here, since the executable name is - // not needed on Android anyway. See crbug.com/500854. - std::unique_ptr<base::CommandLine> cmd_line = - std::make_unique<base::CommandLine>(base::CommandLine::NO_PROGRAM); - if (metrics_name_ == network::mojom::NetworkService::Name_ && - base::FeatureList::IsEnabled(features::kWarmUpNetworkProcess)) { - process_->EnableWarmUpConnection(); - } + // readlink("/prof/self/exe") sometimes fails on Android at startup. + // As a workaround skip calling it here, since the executable name is + // not needed on Android anyway. See crbug.com/500854. + std::unique_ptr<base::CommandLine> cmd_line = + std::make_unique<base::CommandLine>(base::CommandLine::NO_PROGRAM); + if (options_.metrics_name_ == network::mojom::NetworkService::Name_ && + base::FeatureList::IsEnabled(features::kWarmUpNetworkProcess)) { + process_->EnableWarmUpConnection(); + } #else // BUILDFLAG(IS_ANDROID) #if BUILDFLAG(IS_MAC) - if (sandbox_type_ == sandbox::mojom::Sandbox::kServiceWithJit) { - DCHECK_EQ(child_flags_, ChildProcessHost::CHILD_RENDERER); - } + if (options_.sandbox_type_ == sandbox::mojom::Sandbox::kServiceWithJit) { + DCHECK_EQ(options_.child_flags_, ChildProcessHost::CHILD_RENDERER); + } #endif // BUILDFLAG(IS_MAC) - int child_flags = child_flags_; + int child_flags = options_.child_flags_; - // When running under gdb, forking /proc/self/exe ends up forking the gdb - // executable instead of Chromium. It is almost safe to assume that no - // updates will happen while a developer is running with - // |switches::kUtilityCmdPrefix|. See ChildProcessHost::GetChildPath() for - // a similar case with Valgrind. - if (has_cmd_prefix) { - child_flags = ChildProcessHost::CHILD_NORMAL; - } + // When running under gdb, forking /proc/self/exe ends up forking the gdb + // executable instead of Chromium. It is almost safe to assume that no + // updates will happen while a developer is running with + // |switches::kUtilityCmdPrefix|. See ChildProcessHost::GetChildPath() for + // a similar case with Valgrind. + if (has_cmd_prefix) { + child_flags = ChildProcessHost::CHILD_NORMAL; + } - base::FilePath exe_path = ChildProcessHost::GetChildPath(child_flags); - if (exe_path.empty()) { - NOTREACHED() << "Unable to get utility process binary name."; - } + base::FilePath exe_path = ChildProcessHost::GetChildPath(child_flags); + if (exe_path.empty()) { + NOTREACHED() << "Unable to get utility process binary name."; + } - std::unique_ptr<base::CommandLine> cmd_line = - std::make_unique<base::CommandLine>(exe_path); + std::unique_ptr<base::CommandLine> cmd_line = + std::make_unique<base::CommandLine>(exe_path); #endif // BUILDFLAG(IS_ANDROID) - cmd_line->AppendSwitchASCII(switches::kProcessType, - switches::kUtilityProcess); - // Specify the type of utility process for debugging/profiling purposes. - cmd_line->AppendSwitchASCII(switches::kUtilitySubType, metrics_name_); - std::string locale = GetContentClient()->browser()->GetApplicationLocale(); - cmd_line->AppendSwitchASCII(switches::kLang, locale); + cmd_line->AppendSwitchASCII(switches::kProcessType, + switches::kUtilityProcess); + // Specify the type of utility process for debugging/profiling purposes. + cmd_line->AppendSwitchASCII(switches::kUtilitySubType, + options_.metrics_name_); + std::string locale = GetContentClient()->browser()->GetApplicationLocale(); + cmd_line->AppendSwitchASCII(switches::kLang, locale); #if BUILDFLAG(IS_WIN) - cmd_line->AppendArgNative(UtilityToAppLaunchPrefetchArg(metrics_name_)); + cmd_line->AppendArgNative( + UtilityToAppLaunchPrefetchArg(options_.metrics_name_)); #endif // BUILDFLAG(IS_WIN) - sandbox::policy::SetCommandLineFlagsForSandboxType(cmd_line.get(), - sandbox_type_); + sandbox::policy::SetCommandLineFlagsForSandboxType(cmd_line.get(), + options_.sandbox_type_); - // Browser command-line switches to propagate to the utility process. - static const char* const kSwitchNames[] = { - network::switches::kAdditionalTrustTokenKeyCommitments, - network::switches::kForceEffectiveConnectionType, - network::switches::kHostResolverRules, - network::switches::kIgnoreBadMessageForTesting, - network::switches::kIgnoreCertificateErrorsSPKIList, - network::switches::kTestThirdPartyCookiePhaseout, - network::switches::kDisableSharedDictionaryStorageCleanupForTesting, - network::switches::kStoreProbabilisticRevealTokens, - sandbox::policy::switches::kNoSandbox, + // Browser command-line switches to propagate to the utility process. + static const char* const kSwitchNames[] = { + network::switches::kAdditionalTrustTokenKeyCommitments, + network::switches::kForceEffectiveConnectionType, + network::switches::kHostResolverRules, + network::switches::kIgnoreBadMessageForTesting, + network::switches::kIgnoreCertificateErrorsSPKIList, + network::switches::kTestThirdPartyCookiePhaseout, + network::switches::kDisableSharedDictionaryStorageCleanupForTesting, + network::switches::kStoreProbabilisticRevealTokens, + sandbox::policy::switches::kNoSandbox, #if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS) - switches::kDisableDevShmUsage, + switches::kDisableDevShmUsage, #endif #if BUILDFLAG(IS_MAC) - sandbox::policy::switches::kDisableMetalShaderCache, - sandbox::policy::switches::kEnableSandboxLogging, + sandbox::policy::switches::kDisableMetalShaderCache, + sandbox::policy::switches::kEnableSandboxLogging, #endif - switches::kEnableBackgroundThreadPool, - switches::kEnableExperimentalCookieFeatures, - switches::kForceTextDirection, - switches::kForceUIDirection, - switches::kIgnoreCertificateErrors, - switches::kOverrideUseSoftwareGLForTests, - switches::kOverrideEnabledCdmInterfaceVersion, - switches::kDisableAcceleratedMjpegDecode, - switches::kUseFakeDeviceForMediaStream, - switches::kUseFakeMjpegDecodeAccelerator, - switches::kUseFileForFakeVideoCapture, - switches::kUseMockCertVerifierForTesting, - switches::kMockCertVerifierDefaultResultForTesting, - switches::kUtilityStartupDialog, - switches::kUseANGLE, - switches::kUseGL, - switches::kEnableExperimentalWebPlatformFeatures, - // These flags are used by the audio service: - switches::kAudioBufferSize, - switches::kDisableAudioInput, - switches::kDisableAudioOutput, - switches::kFailAudioStreamCreation, - switches::kMuteAudio, - switches::kUseFileForFakeAudioCapture, + switches::kEnableBackgroundThreadPool, + switches::kEnableExperimentalCookieFeatures, + switches::kForceTextDirection, + switches::kForceUIDirection, + switches::kIgnoreCertificateErrors, + switches::kOverrideUseSoftwareGLForTests, + switches::kOverrideEnabledCdmInterfaceVersion, + switches::kDisableAcceleratedMjpegDecode, + switches::kUseFakeDeviceForMediaStream, + switches::kUseFakeMjpegDecodeAccelerator, + switches::kUseFileForFakeVideoCapture, + switches::kUseMockCertVerifierForTesting, + switches::kMockCertVerifierDefaultResultForTesting, + switches::kUtilityStartupDialog, + switches::kUseANGLE, + switches::kUseGL, + switches::kEnableExperimentalWebPlatformFeatures, + // These flags are used by the audio service: + switches::kAudioBufferSize, + switches::kDisableAudioInput, + switches::kDisableAudioOutput, + switches::kFailAudioStreamCreation, + switches::kMuteAudio, + switches::kUseFileForFakeAudioCapture, #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FREEBSD) || \ BUILDFLAG(IS_SOLARIS) - switches::kAlsaInputDevice, - switches::kAlsaOutputDevice, + switches::kAlsaInputDevice, + switches::kAlsaOutputDevice, #endif #if BUILDFLAG(USE_CRAS) - switches::kUseCras, + switches::kUseCras, #endif #if BUILDFLAG(IS_WIN) - switches::kDisableHighResTimer, - switches::kEnableExclusiveAudio, - switches::kForceWaveAudio, - switches::kRaiseTimerFrequency, - switches::kTrySupportedChannelLayouts, - switches::kWaveOutBuffers, - switches::kWebXrForceRuntime, - sandbox::policy::switches::kAddXrAppContainerCaps, + switches::kDisableHighResTimer, + switches::kEnableExclusiveAudio, + switches::kForceWaveAudio, + switches::kRaiseTimerFrequency, + switches::kTrySupportedChannelLayouts, + switches::kWaveOutBuffers, + switches::kWebXrForceRuntime, + sandbox::policy::switches::kAddXrAppContainerCaps, #endif #if BUILDFLAG(ENABLE_VR) - device::switches::kWebXrHandAnonymizationStrategy, + device::switches::kWebXrHandAnonymizationStrategy, #endif - network::switches::kIpAddressSpaceOverrides, + network::switches::kIpAddressSpaceOverrides, #if BUILDFLAG(IS_CHROMEOS) - switches::kSchedulerBoostUrgent, + switches::kSchedulerBoostUrgent, #endif - switches::kFakeBackgroundBlurTogglePeriod, + switches::kFakeBackgroundBlurTogglePeriod, #if BUILDFLAG(USE_LINUX_VIDEO_ACCELERATION) - switches::kHardwareVideoDecodeFrameRate, + switches::kHardwareVideoDecodeFrameRate, #endif #if BUILDFLAG(IS_OZONE) - switches::kRenderNodeOverride, + switches::kRenderNodeOverride, #endif - }; - cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames); + }; + cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames); - network_session_configurator::CopyNetworkSwitches(browser_command_line, - cmd_line.get()); + network_session_configurator::CopyNetworkSwitches(browser_command_line, + cmd_line.get()); - if (has_cmd_prefix) { - // Launch the utility child process with some prefix - // (usually "xterm -e gdb --args"). - cmd_line->PrependWrapper(browser_command_line.GetSwitchValueNative( - switches::kUtilityCmdPrefix)); - } + if (has_cmd_prefix) { + // Launch the utility child process with some prefix + // (usually "xterm -e gdb --args"). + cmd_line->PrependWrapper( + browser_command_line.GetSwitchValueNative(switches::kUtilityCmdPrefix)); + } - for (const auto& extra_switch : extra_switches_) { - cmd_line->AppendSwitch(extra_switch); - } + for (const auto& extra_switch : options_.extra_switches_) { + cmd_line->AppendSwitch(extra_switch); + } #if BUILDFLAG(IS_WIN) - if (media::IsMediaFoundationD3D11VideoCaptureEnabled()) { - // MediaFoundationD3D11VideoCapture requires Gpu memory buffers, - // which are unavailable if the GPU process isn't running or if - // D3D shared images are not supported. - if (!GpuDataManagerImpl::GetInstance()->IsGpuCompositingDisabled() && - GpuDataManagerImpl::GetInstance()->GetGPUInfo().shared_image_d3d) { - cmd_line->AppendSwitch(switches::kVideoCaptureUseGpuMemoryBuffer); - } + if (media::IsMediaFoundationD3D11VideoCaptureEnabled()) { + // MediaFoundationD3D11VideoCapture requires Gpu memory buffers, + // which are unavailable if the GPU process isn't running or if + // D3D shared images are not supported. + if (!GpuDataManagerImpl::GetInstance()->IsGpuCompositingDisabled() && + GpuDataManagerImpl::GetInstance()->GetGPUInfo().shared_image_d3d) { + cmd_line->AppendSwitch(switches::kVideoCaptureUseGpuMemoryBuffer); } + } #endif #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) - file_data_->files_to_preload.merge(GetV8SnapshotFilesToPreload(*cmd_line)); + options_.file_data_->files_to_preload.merge( + GetV8SnapshotFilesToPreload(*cmd_line)); #endif // BUILDFLAG(IS_POSIX) #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - // The network service should have access to the parent directories - // necessary for its usage. - if (sandbox_type_ == sandbox::mojom::Sandbox::kNetwork) { - std::vector<base::FilePath> network_context_parent_dirs = - GetContentClient()->browser()->GetNetworkContextsParentDirectory(); - file_data_->files_to_preload[kNetworkContextParentDirsDescriptor] = - PassNetworkContextParentDirs(std::move(network_context_parent_dirs)); - } + // The network service should have access to the parent directories + // necessary for its usage. + if (options_.sandbox_type_ == sandbox::mojom::Sandbox::kNetwork) { + std::vector<base::FilePath> network_context_parent_dirs = + GetContentClient()->browser()->GetNetworkContextsParentDirectory(); + options_.file_data_->files_to_preload[kNetworkContextParentDirsDescriptor] = + PassNetworkContextParentDirs(std::move(network_context_parent_dirs)); + } #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) #if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) && !BUILDFLAG(IS_WIN) - // Pass `kVideoCaptureUseGpuMemoryBuffer` flag to video capture service only - // when the video capture use GPU memory buffer enabled. - if (metrics_name_ == video_capture::mojom::VideoCaptureService::Name_) { - bool pass_gpu_buffer_flag = - switches::IsVideoCaptureUseGpuMemoryBufferEnabled(); + // Pass `kVideoCaptureUseGpuMemoryBuffer` flag to video capture service only + // when the video capture use GPU memory buffer enabled. + if (options_.metrics_name_ == + video_capture::mojom::VideoCaptureService::Name_) { + bool pass_gpu_buffer_flag = + switches::IsVideoCaptureUseGpuMemoryBufferEnabled(); #if BUILDFLAG(IS_LINUX) - // Check if NV12 GPU memory buffer supported at the same time. - pass_gpu_buffer_flag = - pass_gpu_buffer_flag && - GpuDataManagerImpl::GetInstance()->IsGpuMemoryBufferNV12Supported(); + // Check if NV12 GPU memory buffer supported at the same time. + pass_gpu_buffer_flag = + pass_gpu_buffer_flag && + GpuDataManagerImpl::GetInstance()->IsGpuMemoryBufferNV12Supported(); #endif // BUILDFLAG(IS_LINUX) - if (pass_gpu_buffer_flag) { - cmd_line->AppendSwitch(switches::kVideoCaptureUseGpuMemoryBuffer); - } + if (pass_gpu_buffer_flag) { + cmd_line->AppendSwitch(switches::kVideoCaptureUseGpuMemoryBuffer); } + } #endif // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) && !BUILDFLAG(IS_WIN) - std::unique_ptr<UtilitySandboxedProcessLauncherDelegate> delegate = - std::make_unique<UtilitySandboxedProcessLauncherDelegate>( - sandbox_type_, env_, *cmd_line); + std::unique_ptr<UtilitySandboxedProcessLauncherDelegate> delegate = + std::make_unique<UtilitySandboxedProcessLauncherDelegate>( + options_.sandbox_type_, options_.env_, *cmd_line); #if BUILDFLAG(IS_WIN) - if (!preload_libraries_.empty()) { - delegate->SetPreloadLibraries(preload_libraries_); - } + if (!options_.preload_libraries_.empty()) { + delegate->SetPreloadLibraries(options_.preload_libraries_); + } #endif // BUILDFLAG(IS_WIN) #if BUILDFLAG(USE_ZYGOTE) - if (zygote_for_testing_.has_value()) { - delegate->SetZygote(zygote_for_testing_.value()); - } + if (options_.zygote_for_testing_.has_value()) { + delegate->SetZygote(options_.zygote_for_testing_.value()); + } #endif // BUILDFLAG(USE_ZYGOTE) - process_->LaunchWithFileData(std::move(delegate), std::move(cmd_line), - std::move(file_data_), true); - } + process_->LaunchWithFileData(std::move(delegate), std::move(cmd_line), + std::move(options_.file_data_), true); return true; } @@ -502,7 +566,7 @@ } std::optional<std::string> UtilityProcessHost::GetServiceName() { - return metrics_name_; + return options_.metrics_name_; } } // namespace content
diff --git a/content/browser/service_host/utility_process_host.h b/content/browser/service_host/utility_process_host.h index d13e6db4..4335d7ff 100644 --- a/content/browser/service_host/utility_process_host.h +++ b/content/browser/service_host/utility_process_host.h
@@ -12,7 +12,6 @@ #include <vector> #include "base/environment.h" -#include "base/memory/weak_ptr.h" #include "base/process/launch.h" #include "build/build_config.h" #include "build/chromecast_buildflags.h" @@ -43,24 +42,14 @@ namespace content { class BrowserChildProcessHostImpl; class InProcessChildThreadParams; -struct ChildProcessData; typedef base::Thread* (*UtilityMainThreadFactoryFunction)( const InProcessChildThreadParams&); -// This class acts as the browser-side host to a utility child process. A -// utility process is a short-lived process that is created to run a specific -// task. This class lives solely on the UI thread. -// If you need a single method call in the process, use StartFooBar(p). -// If you need multiple batches of work to be done in the process, use -// StartBatchMode(), then multiple calls to StartFooBar(p), then finish with -// EndBatchMode(). -// If you need to bind Mojo interfaces, use Start() to start the child -// process and then call BindInterface(). -// -// Note: If your class keeps a ptr to an object of this type, grab a weak ptr to -// avoid a use after free since this object is deleted synchronously but the -// client notification is asynchronous. See http://crbug.com/108871. +// This class acts as the browser-side host to a utility child process hosting a +// mojo service. This class lives solely on the UI thread. If you need to bind +// a Mojo interface, specify it via `WithBoundServiceInterfaceOnChildProcess` on +// the `Options` passed in. class CONTENT_EXPORT UtilityProcessHost : public BrowserChildProcessHostDelegate { public: @@ -78,71 +67,136 @@ virtual void OnProcessCrashed() {} }; - // This class is self-owned. It must be instantiated using new, and shouldn't - // be deleted manually. - // TODO(crbug.com/40254698): Make it clearer the caller of the - // constructor do not own memory. A static method to create them + private - // constructor could be better. - UtilityProcessHost(); - explicit UtilityProcessHost(std::unique_ptr<Client> client); + struct CONTENT_EXPORT Options { + Options(); + ~Options(); + + Options(const Options&) = delete; + Options& operator=(const Options&) = delete; + + Options(Options&&); + Options& operator=(Options&&); + + // Makes the process run with a specific sandbox type, or unsandboxed if + // Sandbox::kNoSandbox is specified. + Options& WithSandboxType(sandbox::mojom::Sandbox sandbox_type); + + // Sets the name of the process to appear in the task manager. + Options& WithName(const std::u16string& name); + + // Sets the name used for metrics reporting. This should not be a localized + // name. This is recorded to metrics, so update UtilityProcessNameHash enum + // in enums.xml if new values are passed here. + Options& WithMetricsName(const std::string& metrics_name); + + Options& WithChildFlags(int flags); + + // Provides extra switches to append to the process's command line. + Options& WithExtraCommandLineSwitches(std::vector<std::string> switches); + +#if BUILDFLAG(IS_WIN) + // Specifies libraries to preload before the sandbox is locked down. Paths + // should be absolute. + Options& WithPreloadLibraries(const std::vector<base::FilePath>& preloads); +#endif // BUILDFLAG(IS_WIN) + + // Allows the child process to bind viz.mojom.Gpu. + Options& WithGpuClientAllowed(); + +#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) + // Adds to ChildProcessLauncherFileData::files_to_preload, which maps |key| + // -> |file| in the new process's base::FileDescriptorStore. + Options& WithFileToPreload( + std::string key, + std::variant<base::FilePath, base::ScopedFD> file); +#endif + +#if BUILDFLAG(IS_POSIX) + Options& WithEnvironment(const base::EnvironmentMap& env); +#endif + +#if BUILDFLAG(USE_ZYGOTE) + Options& WithZygoteForTesting(ZygoteCommunication* handle); +#endif // BUILDFLAG(USE_ZYGOTE) + + // Requests that the process bind a receiving pipe targeting the interface + // named by `receiver`. Calls to this method generally end up in + // `ChildThreadImpl::OnBindReceiver()` and the option is used for testing + // only. + Options& WithBoundReceiverOnChildProcessForTesting( + mojo::GenericPendingReceiver receiver); + + // Requests that the utility process bind a receiving pipe targeting the + // service interface named by `receiver`. + Options& WithBoundServiceInterfaceOnChildProcess( + mojo::GenericPendingReceiver receiver); + + // Passes the contents of this Options object to a newly returned Options + // value. This can be called when moving an in-line built Options object + // directly into a call to `Start`. + Options Pass(); + + private: + friend class UtilityProcessHost; + + sandbox::mojom::Sandbox sandbox_type_; + + // Map of environment variables to values. + base::EnvironmentMap env_; + + // The non-localized name used for metrics reporting. + std::string metrics_name_; + + // ChildProcessHost flags to use when starting the child process. + int child_flags_; + + // Extra command line switches to append. + std::vector<std::string> extra_switches_; + +#if BUILDFLAG(IS_WIN) + // Libraries to load before sandbox lockdown. Only used on Windows. + std::vector<base::FilePath> preload_libraries_; +#endif // BUILDFLAG(IS_WIN) + +#if BUILDFLAG(USE_ZYGOTE) + std::optional<raw_ptr<ZygoteCommunication>> zygote_for_testing_; +#endif // BUILDFLAG(USE_ZYGOTE) + +#if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) + // Whether or not to bind viz::mojom::Gpu to the utility process. + bool allowed_gpu_; +#endif // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) + + // A mojo receiver to bind once the process starts. + std::optional<mojo::GenericPendingReceiver> receiver_to_bind_; + + // A mojo service interface to bind once the process starts. + std::optional<mojo::GenericPendingReceiver> service_interface_to_bind_; + + // Extra files and file descriptors to preload in the new process. + std::unique_ptr<ChildProcessLauncherFileData> file_data_; + + // The process name used to identify the process in task manager. + std::u16string name_; + }; + + // Creates and starts a new UtilityProcessHost with the specified `Options`. + // Pass a `client` if delegate callbacks are needed. + static bool Start(Options options, std::unique_ptr<Client> client = nullptr); UtilityProcessHost(const UtilityProcessHost&) = delete; UtilityProcessHost& operator=(const UtilityProcessHost&) = delete; + private: + UtilityProcessHost(Options options, std::unique_ptr<Client> client); + ~UtilityProcessHost() override; - base::WeakPtr<UtilityProcessHost> AsWeakPtr(); - - // Makes the process run with a specific sandbox type, or unsandboxed if - // Sandbox::kNoSandbox is specified. - void SetSandboxType(sandbox::mojom::Sandbox sandbox_type); - - // Returns information about the utility child process. - const ChildProcessData& GetData(); -#if BUILDFLAG(IS_POSIX) - void SetEnv(const base::EnvironmentMap& env); -#endif - - // Starts the utility process. - bool Start(); - - // Sets the name of the process to appear in the task manager. - void SetName(const std::u16string& name); - - // Sets the name used for metrics reporting. This should not be a localized - // name. This is recorded to metrics, so update UtilityProcessNameHash enum in - // enums.xml if new values are passed here. - void SetMetricsName(const std::string& metrics_name); - - void set_child_flags(int flags) { child_flags_ = flags; } - - // Provides extra switches to append to the process's command line. - void SetExtraCommandLineSwitches(std::vector<std::string> switches); - - // Allows the child process to bind viz.mojom.Gpu. - void SetAllowGpuClient(); - -#if BUILDFLAG(IS_WIN) - // Specifies libraries to preload before the sandbox is locked down. Paths - // should be absolute. - void SetPreloadLibraries(const std::vector<base::FilePath>& preloads); -#endif // BUILDFLAG(IS_WIN) - -#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) - // Adds to ChildProcessLauncherFileData::files_to_preload, which maps |key| -> - // |file| in the new process's base::FileDescriptorStore. - void AddFileToPreload(std::string key, - std::variant<base::FilePath, base::ScopedFD> file); -#endif - -#if BUILDFLAG(USE_ZYGOTE) - void SetZygoteForTesting(ZygoteCommunication* handle); -#endif // BUILDFLAG(USE_ZYGOTE) - // Returns a control interface for the running child process. mojom::ChildProcess* GetChildProcess(); - private: + void MaybeBindMojoInterfaces(); + // Starts the child process if needed, returns true on success. bool StartProcess(); @@ -153,23 +207,7 @@ std::optional<std::string> GetServiceName() override; void BindHostReceiver(mojo::GenericPendingReceiver receiver) override; - // Launch the child process with switches that will setup this sandbox type. - sandbox::mojom::Sandbox sandbox_type_; - - // ChildProcessHost flags to use when starting the child process. - int child_flags_; - - // Map of environment variables to values. - base::EnvironmentMap env_; - - // True if StartProcess() has been called. - bool started_; - - // The process name used to identify the process in task manager. - std::u16string name_; - - // The non-localized name used for metrics reporting. - std::string metrics_name_; + Options options_; // Child process host implementation. std::unique_ptr<BrowserChildProcessHostImpl> process_; @@ -177,21 +215,6 @@ // Used in single-process mode instead of |process_|. std::unique_ptr<base::Thread> in_process_thread_; - // Extra command line switches to append. - std::vector<std::string> extra_switches_; - -#if BUILDFLAG(IS_WIN) - // Libraries to load before sandbox lockdown. Only used on Windows. - std::vector<base::FilePath> preload_libraries_; -#endif // BUILDFLAG(IS_WIN) - - // Extra files and file descriptors to preload in the new process. - std::unique_ptr<ChildProcessLauncherFileData> file_data_; - -#if BUILDFLAG(USE_ZYGOTE) - std::optional<raw_ptr<ZygoteCommunication>> zygote_for_testing_; -#endif // BUILDFLAG(USE_ZYGOTE) - // Indicates whether the process has been successfully launched yet, or if // launch failed. enum class LaunchState { @@ -202,14 +225,10 @@ LaunchState launch_state_ = LaunchState::kLaunchInProgress; #if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) - bool allowed_gpu_; std::unique_ptr<viz::GpuClient, base::OnTaskRunnerDeleter> gpu_client_; #endif // BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) std::unique_ptr<Client> client_; - - // Used to vend weak pointers, and should always be declared last. - base::WeakPtrFactory<UtilityProcessHost> weak_ptr_factory_{this}; }; } // namespace content
diff --git a/content/browser/service_host/utility_process_host_browsertest.cc b/content/browser/service_host/utility_process_host_browsertest.cc index 00176ea..60ba2d72 100644 --- a/content/browser/service_host/utility_process_host_browsertest.cc +++ b/content/browser/service_host/utility_process_host_browsertest.cc
@@ -79,25 +79,22 @@ void SetUpOnMainThread() override { DCHECK_CURRENTLY_ON(BrowserThread::UI); BrowserChildProcessObserver::Add(this); - - host_ = new UtilityProcessHost(); // Owned by a global list. - host_->SetName(u"TestProcess"); - host_->SetMetricsName(kTestProcessName); } - void TearDownOnMainThread() override { - // `host_` is about to be deleted during BrowserMainRunnerImpl::Shutdown(). - host_ = nullptr; + UtilityProcessHost::Options DefaultOptions() { + return UtilityProcessHost::Options() + .WithName(u"TestProcess") + .WithMetricsName(kTestProcessName) + .Pass(); } - void SetExpectFailLaunch() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); + void AddFailedLaunchOptions(UtilityProcessHost::Options& options) { expect_failed_launch_ = true; #if BUILDFLAG(IS_WIN) // The Windows sandbox does not like the child process being a different // process, so launch unsandboxed for the purpose of this test. - host_->SetSandboxType(sandbox::mojom::Sandbox::kNoSandbox); + options.WithSandboxType(sandbox::mojom::Sandbox::kNoSandbox); #endif // Simulate a catastrophic launch failure for all child processes by // making the path to the process non-existent. @@ -106,10 +103,9 @@ base::FilePath(FILE_PATH_LITERAL("non_existent_path"))); } - void SetElevated() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); + void AddElevatedOptions(UtilityProcessHost::Options& options) { #if BUILDFLAG(IS_WIN) - host_->SetSandboxType( + options.WithSandboxType( sandbox::mojom::Sandbox::kNoSandboxAndElevatedPrivileges); #else NOTREACHED(); @@ -118,18 +114,19 @@ // After `service_` is bound, `run_test` is invoked, and then the RunLoop will // run. - void RunUtilityProcess(base::OnceClosure run_test) { + void RunUtilityProcess(UtilityProcessHost::Options options, + base::OnceClosure run_test) { DCHECK_CURRENTLY_ON(BrowserThread::UI); base::RunLoop run_loop; done_closure_ = base::BindOnce(&UtilityProcessHostBrowserTest::DoneRunning, base::Unretained(this), run_loop.QuitClosure()); - EXPECT_TRUE(host_->Start()); - - host_->GetChildProcess()->BindServiceInterface( + options.WithBoundServiceInterfaceOnChildProcess( service_.BindNewPipeAndPassReceiver()); + UtilityProcessHost::Start(std::move(options)); + std::move(run_test).Run(); run_loop.Run(); } @@ -207,7 +204,6 @@ GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(done_closure_)); } - raw_ptr<UtilityProcessHost, AcrossTasksDanglingUntriaged> host_; mojo::Remote<mojom::TestService> service_; base::OnceClosure done_closure_; bool expect_crashed_ = false; @@ -285,6 +281,7 @@ IN_PROC_BROWSER_TEST_F(UtilityProcessHostBrowserTest, LaunchProcess) { RunUtilityProcess( + DefaultOptions(), base::BindOnce(&UtilityProcessHostBrowserTest::RunBasicPingPongTest, base::Unretained(this))); } @@ -301,18 +298,20 @@ #endif IN_PROC_BROWSER_TEST_F(UtilityProcessHostBrowserTest, MAYBE_FileDescriptorStore) { + UtilityProcessHost::Options options = DefaultOptions(); // Tests whether base::FileDescriptorStore works in content by passing it a // file descriptor for a pipe on launch. This test ensures the process is // launched without a zygote. #if BUILDFLAG(USE_ZYGOTE) - host_->SetZygoteForTesting(nullptr); + options.WithZygoteForTesting(nullptr); #endif base::ScopedFD read_fd; base::ScopedFD write_fd; ASSERT_TRUE(base::CreatePipe(&read_fd, &write_fd)); - host_->AddFileToPreload(mojom::kTestPipeKey, std::move(write_fd)); RunUtilityProcess( + options.WithFileToPreload(mojom::kTestPipeKey, std::move(write_fd)) + .Pass(), base::BindOnce(&UtilityProcessHostBrowserTest::RunFileDescriptorStoreTest, base::Unretained(this), std::move(read_fd))); } @@ -324,12 +323,13 @@ // Tests whether base::FileDescriptorStore works in content by passing it a // file descriptor for a pipe on launch. This test ensures the process is // launched with the unsandboxed zygote. - host_->SetZygoteForTesting(GetUnsandboxedZygote()); - base::ScopedFD read_fd, write_fd; ASSERT_TRUE(base::CreatePipe(&read_fd, &write_fd)); - host_->AddFileToPreload(mojom::kTestPipeKey, std::move(write_fd)); RunUtilityProcess( + DefaultOptions() + .WithZygoteForTesting(GetUnsandboxedZygote()) + .WithFileToPreload(mojom::kTestPipeKey, std::move(write_fd)) + .Pass(), base::BindOnce(&UtilityProcessHostBrowserTest::RunFileDescriptorStoreTest, base::Unretained(this), std::move(read_fd))); } @@ -339,12 +339,13 @@ // Tests whether base::FileDescriptorStore works in content by passing it a // file descriptor for a pipe on launch. This test ensures the process is // launched with the generic zygote. - host_->SetZygoteForTesting(GetGenericZygote()); - base::ScopedFD read_fd, write_fd; ASSERT_TRUE(base::CreatePipe(&read_fd, &write_fd)); - host_->AddFileToPreload(mojom::kTestPipeKey, std::move(write_fd)); RunUtilityProcess( + DefaultOptions() + .WithZygoteForTesting(GetGenericZygote()) + .WithFileToPreload(mojom::kTestPipeKey, std::move(write_fd)) + .Pass(), base::BindOnce(&UtilityProcessHostBrowserTest::RunFileDescriptorStoreTest, base::Unretained(this), std::move(read_fd))); } @@ -363,6 +364,7 @@ IN_PROC_BROWSER_TEST_F(UtilityProcessHostBrowserTest, MAYBE_LaunchProcessAndCrash) { RunUtilityProcess( + DefaultOptions(), base::BindOnce(&UtilityProcessHostBrowserTest::RunCrashImmediatelyTest, base::Unretained(this))); } @@ -376,10 +378,12 @@ // See also ServiceProcessLauncherTest.FailToLaunchProcess. #if !BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_MAC) IN_PROC_BROWSER_TEST_F(UtilityProcessHostBrowserTest, FailToLaunchProcess) { - SetExpectFailLaunch(); + UtilityProcessHost::Options options = DefaultOptions(); + AddFailedLaunchOptions(options); // If the ping-pong test completes, the test will fail because that means the // process did not fail to launch. RunUtilityProcess( + std::move(options), base::BindOnce(&UtilityProcessHostBrowserTest::RunBasicPingPongTest, base::Unretained(this))); } @@ -387,8 +391,11 @@ #if BUILDFLAG(IS_WIN) IN_PROC_BROWSER_TEST_F(UtilityProcessHostBrowserTest, LaunchElevatedProcess) { - SetElevated(); RunUtilityProcess( + DefaultOptions() + .WithSandboxType( + sandbox::mojom::Sandbox::kNoSandboxAndElevatedPrivileges) + .Pass(), mojo::core::IsMojoIpczEnabled() ? base::BindOnce( &UtilityProcessHostBrowserTest::RunSharedMemoryHandleTest, @@ -400,8 +407,11 @@ // Disabled because currently this causes a WER dialog to appear. IN_PROC_BROWSER_TEST_F(UtilityProcessHostBrowserTest, DISABLED_LaunchElevatedProcessAndCrash) { - SetElevated(); RunUtilityProcess( + DefaultOptions() + .WithSandboxType( + sandbox::mojom::Sandbox::kNoSandboxAndElevatedPrivileges) + .Pass(), base::BindOnce(&UtilityProcessHostBrowserTest::RunCrashImmediatelyTest, base::Unretained(this))); } @@ -429,8 +439,10 @@ IN_PROC_BROWSER_TEST_F(NetworkServiceProcessIdentityTest, LaunchService) { // The process requirement is applied to the network service based on its // sandbox type. - host_->SetSandboxType(sandbox::mojom::Sandbox::kNetwork); RunUtilityProcess( + DefaultOptions() + .WithSandboxType(sandbox::mojom::Sandbox::kNetwork) + .Pass(), base::BindOnce(&UtilityProcessHostBrowserTest::RunBasicPingPongTest, base::Unretained(this))); }
diff --git a/content/browser/service_host/utility_process_host_receiver_bindings.cc b/content/browser/service_host/utility_process_host_receiver_bindings.cc index e405a62..9b1f9e4 100644 --- a/content/browser/service_host/utility_process_host_receiver_bindings.cc +++ b/content/browser/service_host/utility_process_host_receiver_bindings.cc
@@ -31,7 +31,7 @@ } #endif #if BUILDFLAG(ENABLE_GPU_CHANNEL_MEDIA_CAPTURE) - if (allowed_gpu_) { + if (options_.allowed_gpu_) { // TODO(crbug.com/328099369) Remove once all clients get this directly. if (auto gpu_receiver = receiver.As<viz::mojom::Gpu>()) { gpu_client_ = content::CreateGpuClient(std::move(gpu_receiver));
diff --git a/content/browser/service_host/utility_process_sandbox_browsertest.cc b/content/browser/service_host/utility_process_sandbox_browsertest.cc index ac83bb9..5305510 100644 --- a/content/browser/service_host/utility_process_sandbox_browsertest.cc +++ b/content/browser/service_host/utility_process_sandbox_browsertest.cc
@@ -77,14 +77,15 @@ done_closure_ = base::BindOnce(&UtilityProcessSandboxBrowserTest::DoneRunning, base::Unretained(this), run_loop.QuitClosure()); - UtilityProcessHost* host = new UtilityProcessHost(); - host->SetSandboxType(GetParam()); - host->SetName(u"SandboxTestProcess"); - host->SetMetricsName(kTestProcessName); - EXPECT_TRUE(host->Start()); + EXPECT_TRUE(UtilityProcessHost::Start( + UtilityProcessHost::Options() + .WithSandboxType(GetParam()) + .WithName(u"SandboxTestProcess") + .WithMetricsName(kTestProcessName) + .WithBoundReceiverOnChildProcessForTesting( + service_.BindNewPipeAndPassReceiver()) + .Pass())); - host->GetChildProcess()->BindReceiver( - service_.BindNewPipeAndPassReceiver()); service_->GetSandboxStatus( base::BindOnce(&UtilityProcessSandboxBrowserTest::OnGotSandboxStatus, base::Unretained(this)));
diff --git a/content/browser/site_instance_group.cc b/content/browser/site_instance_group.cc index dc4c63f..d3aa7c4 100644 --- a/content/browser/site_instance_group.cc +++ b/content/browser/site_instance_group.cc
@@ -42,6 +42,10 @@ return weak_ptr_factory_.GetSafeRef(); } +base::WeakPtr<SiteInstanceGroup> SiteInstanceGroup::GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + base::WeakPtr<SiteInstanceGroup> SiteInstanceGroup::GetWeakPtrToAllowDangling() { return weak_ptr_factory_.GetWeakPtr();
diff --git a/content/browser/site_instance_group.h b/content/browser/site_instance_group.h index ddac1f98..147d485 100644 --- a/content/browser/site_instance_group.h +++ b/content/browser/site_instance_group.h
@@ -96,6 +96,7 @@ SiteInstanceGroupId GetId() const; base::SafeRef<SiteInstanceGroup> GetSafeRef(); + base::WeakPtr<SiteInstanceGroup> GetWeakPtr(); // TODO(crbug.com/40258727): Remove this. Please don't use it. base::WeakPtr<SiteInstanceGroup> GetWeakPtrToAllowDangling();
diff --git a/content/browser/site_instance_group_browsertest.cc b/content/browser/site_instance_group_browsertest.cc index c3e5bdc..8556c542 100644 --- a/content/browser/site_instance_group_browsertest.cc +++ b/content/browser/site_instance_group_browsertest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/strings/stringprintf.h" #include "base/test/scoped_feature_list.h" #include "content/browser/process_lock.h" #include "content/browser/web_contents/web_contents_impl.h" @@ -489,6 +490,167 @@ EXPECT_EQ(a_instance->group(), data_instance->group()); } +// Tests for the default SiteInstanceGroup behaviour. This is parameterized to +// also run in the legacy mode which puts unisolated sites in the default +// SiteInstance. +class DefaultSiteInstanceGroupTest + : public ContentBrowserTestBase, + public ::testing::WithParamInterface<bool> { + public: + void SetUpCommandLine(base::CommandLine* command_line) override { + // This feature only takes effect when there is no full site isolation. + command_line->RemoveSwitch(switches::kSitePerProcess); + command_line->AppendSwitch(switches::kDisableSiteIsolation); + + if (IsDefaultSiteInstanceGroupEnabled()) { + feature_list_.InitAndEnableFeature(features::kDefaultSiteInstanceGroups); + } else { + feature_list_.InitAndDisableFeature(features::kDefaultSiteInstanceGroups); + } + } + + static std::string DescribeParams( + const testing::TestParamInfo<ParamType>& info) { + return info.param ? "UseDefaultSiteInstanceGroups" + : "UseDefaultSiteInstances"; + } + + protected: + bool IsDefaultSiteInstanceGroupEnabled() const { return GetParam(); } + + private: + base::test::ScopedFeatureList feature_list_; +}; + +// Basic use case, where multiple unisolated sites are put in the default +// SiteInstanceGroup, but have their own SiteInstances. +IN_PROC_BROWSER_TEST_P(DefaultSiteInstanceGroupTest, + DefaultSiteInstanceGroupProperties) { + // Navigate to a non-isolated site. + GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html")); + EXPECT_TRUE(NavigateToURL(shell(), url_c)); + + scoped_refptr<SiteInstanceImpl> c_instance = + main_frame_host()->GetSiteInstance(); + EXPECT_FALSE(c_instance->RequiresDedicatedProcess()); + EXPECT_TRUE(c_instance->GetProcess()->GetProcessLock().allows_any_site()); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_EQ(c_instance->group(), + c_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + } else { + EXPECT_TRUE(c_instance->IsDefaultSiteInstance()); + } + + // Navigate to a site with a cross-site iframe, both of which are not + // isolated. + GURL url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b)")); + EXPECT_TRUE(NavigateToURL(shell(), url)); + + scoped_refptr<SiteInstanceImpl> a_instance = + main_frame_host()->GetSiteInstance(); + scoped_refptr<SiteInstanceImpl> b_instance = main_frame() + ->child_at(0) + ->render_manager() + ->current_frame_host() + ->GetSiteInstance(); + EXPECT_TRUE(a_instance->GetProcess()->GetProcessLock().allows_any_site()); + EXPECT_FALSE(a_instance->RequiresDedicatedProcess()); + EXPECT_FALSE(b_instance->RequiresDedicatedProcess()); + if (ShouldUseDefaultSiteInstanceGroup()) { + // A, B and C should all have their own SiteInstances, but all share the + // default SiteInstanceGroup. + EXPECT_NE(a_instance, b_instance); + EXPECT_NE(a_instance, c_instance); + EXPECT_NE(b_instance, c_instance); + EXPECT_EQ(a_instance->group(), + a_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + EXPECT_EQ(a_instance->group(), b_instance->group()); + } else { + EXPECT_TRUE(a_instance->IsDefaultSiteInstance()); + EXPECT_EQ(a_instance, b_instance); + } + + // Navigate to an isolated site that does not use the default + // SiteInstance/Group. + IsolateOriginsForTesting(embedded_test_server(), web_contents(), {"d.com"}); + GURL url_d(embedded_test_server()->GetURL("d.com", "/title1.html")); + EXPECT_TRUE(NavigateToURL(shell(), url_d)); + + scoped_refptr<SiteInstanceImpl> d_instance = + main_frame_host()->GetSiteInstance(); + EXPECT_TRUE(d_instance->RequiresDedicatedProcess()); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_NE(d_instance->group(), + d_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + EXPECT_FALSE(d_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + } else { + EXPECT_FALSE(d_instance->IsDefaultSiteInstance()); + } +} + +IN_PROC_BROWSER_TEST_P(DefaultSiteInstanceGroupTest, + ProcessHasAllowsAnySiteLock) { + // Test starts with a tab that has an unassigned SiteInstance. The associated + // SiteInstance should not yet have a site set. The process is locked with an + // allow-any-site lock. + scoped_refptr<SiteInstanceImpl> start_instance = + main_frame_host()->GetSiteInstance(); + EXPECT_FALSE(start_instance->HasSite()); + EXPECT_TRUE(start_instance->HasProcess()); + RenderProcessHost* start_process = start_instance->GetProcess(); + EXPECT_FALSE(start_process->GetProcessLock().is_locked_to_site()); + EXPECT_FALSE(start_process->GetProcessLock().is_invalid()); + EXPECT_EQ(start_process->GetProcessLock().lock_url(), GURL()); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_NE(start_instance->group(), + start_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + } else { + EXPECT_FALSE(start_instance->IsDefaultSiteInstance()); + } + + // Do a browser-initiated navigation to about:blank. Because it's an + // about:blank navigation without an initiator, it should stay in the previous + // SiteInstance which hasn't had a site set yet, and not 'use up' a + // SiteInstance. + EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank"))); + scoped_refptr<SiteInstanceImpl> blank_instance = + main_frame_host()->GetSiteInstance(); + EXPECT_FALSE(start_instance->HasSite()); + RenderProcessHost* blank_process = blank_instance->GetProcess(); + EXPECT_EQ(start_process, blank_process); + EXPECT_EQ(start_instance, blank_instance); + EXPECT_FALSE(blank_process->GetProcessLock().is_locked_to_site()); + EXPECT_FALSE(blank_process->GetProcessLock().is_invalid()); + EXPECT_EQ(blank_process->GetProcessLock().lock_url().spec(), ""); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_NE(start_instance->group(), + start_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + } else { + EXPECT_FALSE(start_instance->IsDefaultSiteInstance()); + } + + // Now navigate to a 'real' site. Since the previous SiteInstance still did + // not have a site set, that will now be set as foo.com. With default + // SiteInstance/Group, all navigations should remain in the same process. + GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html")); + EXPECT_TRUE(NavigateToURL(shell(), foo_url)); + scoped_refptr<SiteInstanceImpl> foo_instance = + main_frame_host()->GetSiteInstance(); + EXPECT_TRUE(foo_instance->HasSite()); + RenderProcessHost* foo_process = foo_instance->GetProcess(); + EXPECT_TRUE(foo_process->GetProcessLock().allows_any_site()); + EXPECT_FALSE(blank_process->GetProcessLock().is_invalid()); + EXPECT_EQ(foo_process->GetProcessLock().lock_url().spec(), ""); + EXPECT_EQ(foo_instance, start_instance); + if (ShouldUseDefaultSiteInstanceGroup()) { + EXPECT_EQ(foo_instance->group(), + foo_instance->DefaultSiteInstanceGroupForBrowsingInstance()); + } else { + EXPECT_TRUE(foo_instance->IsDefaultSiteInstance()); + } +} + INSTANTIATE_TEST_SUITE_P(All, DataURLSiteInstanceGroupTest, ::testing::Bool(), @@ -497,4 +659,8 @@ DataURLSiteInstanceGroupTestWithoutSiteIsolation, ::testing::Bool(), &DataURLSiteInstanceGroupTest::DescribeParams); +INSTANTIATE_TEST_SUITE_P(All, + DefaultSiteInstanceGroupTest, + ::testing::Bool(), + &DefaultSiteInstanceGroupTest::DescribeParams); } // namespace content
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc index 8db7e1a..c88f607 100644 --- a/content/browser/site_instance_impl.cc +++ b/content/browser/site_instance_impl.cc
@@ -22,6 +22,7 @@ #include "content/browser/renderer_host/render_process_host_impl.h" #include "content/browser/site_instance_group.h" #include "content/browser/storage_partition_impl.h" +#include "content/common/content_navigation_policy.h" #include "content/common/features.h" #include "content/public/browser/browser_or_resource_context.h" #include "content/public/browser/content_browser_client.h" @@ -396,6 +397,7 @@ } void SiteInstanceImpl::AddSiteInfoToDefault(const SiteInfo& site_info) { + DCHECK(!ShouldUseDefaultSiteInstanceGroup()); DCHECK(IsDefaultSiteInstance()); default_site_instance_state_->AddSiteInfo(site_info); } @@ -469,8 +471,17 @@ allocation_context.navigation_context->requires_new_process_for_coop = coop_reuse_process_failed_; } - SetProcessInternal(RenderProcessHostImpl::GetProcessHostForSiteInstance( - this, allocation_context)); + + // See if `this` can be placed in the default SiteInstanceGroup, otherwise + // create a process and associated SiteInstanceGroup. + if (CanPutSiteInstanceInDefaultGroup() && + browsing_instance_->has_default_site_instance_group()) { + browsing_instance_->default_site_instance_group()->AddSiteInstance(this); + SetSiteInstanceGroup(browsing_instance_->default_site_instance_group()); + } else { + SetProcessInternal(RenderProcessHostImpl::GetProcessHostForSiteInstance( + this, allocation_context)); + } } DCHECK(site_instance_group_); @@ -513,6 +524,13 @@ return; } + // If `this` can go in the default SiteInstanceGroup and one exists, prefer + // that SiteInstanceGroup and process. + if (CanPutSiteInstanceInDefaultGroup() && + browsing_instance()->has_default_site_instance_group()) { + return; + } + // TODO(crbug.com/40676483): Don't try to reuse process if either of the // SiteInstances are cross-origin isolated (uses COOP/COEP). SetProcessInternal(existing_process); @@ -525,6 +543,14 @@ site_instance_group_->AddSiteInstance(this); } + // Check if the process created should become the default SiteInstanceGroup's + // process. If so, set `site_instance_group_` to be the default + // SiteInstanceGroup. We should only get here if a process needs to be created + // for the default SiteInstanceGroup. + if (CanPutSiteInstanceInDefaultGroup()) { + MaybeSetDefaultSiteInstanceGroup(); + } + LockProcessIfNeeded(); // If we are using process-per-site, we need to register this process @@ -684,11 +710,12 @@ void SiteInstanceImpl::ConvertToDefaultOrSetSite(const UrlInfo& url_info) { DCHECK(!has_site_); - if (!browsing_instance_->has_default_site_instance()) { + if (!ShouldUseDefaultSiteInstanceGroup() && + !browsing_instance_->has_default_site_instance()) { // We want to set a SiteInfo in this SiteInstance, from information in a - // UrlInfo. The WebExposedIsolationInfo must be compatible for this function - // to not violate WebExposedIsolationInfo isolation invariant within a - // BrowsingInstance. + // UrlInfo. The WebExposedIsolationInfo must be compatible for this + // function to not violate WebExposedIsolationInfo isolation invariant + // within a BrowsingInstance. DCHECK(WebExposedIsolationInfo::AreCompatible( url_info.web_exposed_isolation_info, GetWebExposedIsolationInfo())); @@ -700,8 +727,8 @@ const SiteInfo site_info = SiteInfo::Create(GetIsolationContext(), updated_url_info); - if (CanBePlacedInDefaultSiteInstance(GetIsolationContext(), - updated_url_info.url, site_info)) { + if (CanBePlacedInDefaultSiteInstanceOrGroup( + GetIsolationContext(), updated_url_info.url, site_info)) { SetSiteInfoToDefault(site_info.storage_partition_config()); AddSiteInfoToDefault(site_info); @@ -711,6 +738,31 @@ } SetSite(url_info); + + // If `this` should go in the default SiteInstanceGroup, it needs to be a + // regular SiteInstance with a site (unlike the default SiteInstance), so + // SetSite needs to be called first. + if (ShouldUseDefaultSiteInstanceGroup()) { + MaybeSetDefaultSiteInstanceGroup(); + } +} + +void SiteInstanceImpl::MaybeSetDefaultSiteInstanceGroup() { + CHECK(ShouldUseDefaultSiteInstanceGroup()); + if (!browsing_instance_->has_default_site_instance_group() && + CanBePlacedInDefaultSiteInstanceOrGroup(GetIsolationContext(), + GetSiteURL(), site_info_)) { + CHECK(HasProcess()); + CHECK(has_group()); + browsing_instance_->set_default_site_instance_group( + site_instance_group_->GetWeakPtr()); + } +} + +bool SiteInstanceImpl::CanPutSiteInstanceInDefaultGroup() { + return ShouldUseDefaultSiteInstanceGroup() && + CanBePlacedInDefaultSiteInstanceOrGroup(GetIsolationContext(), + GetSiteURL(), site_info_); } SiteInstanceProcessAssignment @@ -1045,8 +1097,8 @@ updated_url_info.web_exposed_isolation_info = GetWebExposedIsolationInfo(); auto site_info = SiteInfo::Create(GetIsolationContext(), updated_url_info); - return CanBePlacedInDefaultSiteInstance(GetIsolationContext(), url, - site_info) && + return CanBePlacedInDefaultSiteInstanceOrGroup(GetIsolationContext(), url, + site_info) && !browsing_instance_->HasSiteInstance(site_info); } @@ -1370,9 +1422,10 @@ site_info_.web_exposed_isolation_info(); auto site_info = SiteInfo::Create(GetIsolationContext(), updated_url_info); - if (kCreateForURLAllowsDefaultSiteInstance && - CanBePlacedInDefaultSiteInstance(GetIsolationContext(), url_info.url, - site_info)) { + if (!ShouldUseDefaultSiteInstanceGroup() && + kCreateForURLAllowsDefaultSiteInstance && + CanBePlacedInDefaultSiteInstanceOrGroup(GetIsolationContext(), + url_info.url, site_info)) { site_info = SiteInfo::CreateForDefaultSiteInstance( GetIsolationContext(), site_info.storage_partition_config(), GetWebExposedIsolationInfo()); @@ -1390,23 +1443,31 @@ } // static -bool SiteInstanceImpl::CanBePlacedInDefaultSiteInstance( +bool SiteInstanceImpl::CanBePlacedInDefaultSiteInstanceOrGroup( const IsolationContext& isolation_context, const GURL& url, const SiteInfo& site_info) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + // Empty URLs, like the initial empty document, should not be placed in the + // default SiteInstance or group. The initial empty document's SiteInstance + // can be reused, including for navigations to isolated sites. Avoid the case + // where a SiteInstance or group set as the default can then become isolated. + if (url.is_empty()) { + return false; + } + // Exclude "file://" URLs from the default SiteInstance to prevent the - // default SiteInstance process from accumulating file access grants that - // could be exploited by other non-isolated sites. + // default SiteInstance/Group process from accumulating file access grants + // that could be exploited by other non-isolated sites. if (url.SchemeIs(url::kFileScheme)) return false; - // Don't use the default SiteInstance when SiteInstance doesn't assign a + // Don't use the default SiteInstance/Group when SiteInstance doesn't assign a // site URL for |url|, since in that case the SiteInstance should remain // unused, and a subsequent navigation should always be able to reuse it, // whether or not it's to a site requiring a dedicated process or to a site - // that will use the default SiteInstance. + // that will use the default SiteInstance/Group. if (!ShouldAssignSiteForURL(url)) return false; @@ -1634,12 +1695,18 @@ } RenderProcessHost* SiteInstanceImpl::GetDefaultProcessForBrowsingInstance() { - if (SiteInstanceImpl* default_instance = - browsing_instance_->default_site_instance()) { - return default_instance->HasProcess() ? default_instance->GetProcess() - : nullptr; + if (ShouldUseDefaultSiteInstanceGroup()) { + return browsing_instance_->has_default_site_instance_group() + ? browsing_instance_->default_site_instance_group()->process() + : nullptr; + } else { + if (SiteInstanceImpl* default_instance = + browsing_instance_->default_site_instance()) { + return default_instance->HasProcess() ? default_instance->GetProcess() + : nullptr; + } + return nullptr; } - return nullptr; } void SiteInstanceImpl::SetProcessForTesting(RenderProcessHost* process) {
diff --git a/content/browser/site_instance_impl.h b/content/browser/site_instance_impl.h index 802972bf..c0e49c74 100644 --- a/content/browser/site_instance_impl.h +++ b/content/browser/site_instance_impl.h
@@ -350,6 +350,16 @@ // logic is run. void ConvertToDefaultOrSetSite(const UrlInfo& url_info); + // If `browsing_instance_` does not have a default SiteInstanceGroup set and + // if `site_instance_group_` is eligible to become the default + // SiteInstanceGroup, this function makes `site_instance_group_` the new + // default SiteInstanceGroup. Otherwise, this is a no-op. + void MaybeSetDefaultSiteInstanceGroup(); + + // Checks if the default SiteInstanceGroup feature is enabled, and if the + // SiteInstance can be placed in the default SiteInstanceGroup. + bool CanPutSiteInstanceInDefaultGroup(); + // Returns whether SetSite() has been called. // // In some cases, the "site" is not set at SiteInstance creation time, and @@ -545,6 +555,8 @@ // where it is safe. It is not generally safe to change the process of a // SiteInstance, unless the RenderProcessHost itself is entirely destroyed and // a new one later replaces it. + // Before creating a process and calling this method, check if `this` can be + // placed in the default SiteInstanceGroup. void SetProcessInternal(RenderProcessHost* process); // Returns true if |original_url()| is the same site as @@ -576,14 +588,14 @@ bool should_compare_effective_urls); // Returns true if |url| and its |site_url| can be placed inside a default - // SiteInstance. + // SiteInstance or default SiteInstanceGroup. // // Note: |url| and |site_info| must be consistent with each other. In contexts // where the caller only has |url| it can use // SiteInfo::Create() to generate |site_info|. This call is // intentionally not set as a default value to encourage the caller to reuse // a SiteInfo computation if they already have one. - static bool CanBePlacedInDefaultSiteInstance( + static bool CanBePlacedInDefaultSiteInstanceOrGroup( const IsolationContext& isolation_context, const GURL& url, const SiteInfo& site_info);
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc index 6b6eec74..e55fc5e 100644 --- a/content/browser/storage_partition_impl.cc +++ b/content/browser/storage_partition_impl.cc
@@ -2461,6 +2461,12 @@ top_frame_origin, std::move(event_record)); } +#if BUILDFLAG(IS_MAC) +bool StoragePartitionImpl::IsStorageServiceRemoteValid() const { + return GetStorageServiceRemoteStorage().is_bound(); +} +#endif // BUILDFLAG(IS_MAC) + void StoragePartitionImpl::Clone( mojo::PendingReceiver<network::mojom::URLLoaderNetworkServiceObserver> observer) {
diff --git a/content/browser/storage_partition_impl.h b/content/browser/storage_partition_impl.h index 3b1355902..da85f80 100644 --- a/content/browser/storage_partition_impl.h +++ b/content/browser/storage_partition_impl.h
@@ -407,6 +407,10 @@ return shared_storage_header_observer_.get(); } +#if BUILDFLAG(IS_MAC) + bool IsStorageServiceRemoteValid() const; +#endif // BUILDFLAG(IS_MAC) + // Can return nullptr while `this` is being destroyed. BrowserContext* browser_context() const;
diff --git a/content/browser/usb/web_usb_service_impl_unittest.cc b/content/browser/usb/web_usb_service_impl_unittest.cc index 3c90d2a..6668547 100644 --- a/content/browser/usb/web_usb_service_impl_unittest.cc +++ b/content/browser/usb/web_usb_service_impl_unittest.cc
@@ -14,6 +14,7 @@ #include "base/barrier_closure.h" #include "base/functional/bind.h" #include "base/run_loop.h" +#include "base/strings/stringprintf.h" #include "base/test/gmock_callback_support.h" #include "base/test/test_future.h" #include "content/browser/service_worker/embedded_worker_test_helper.h"
diff --git a/content/browser/web_contents/partitioned_popins_controller_browsertest.cc b/content/browser/web_contents/partitioned_popins_controller_browsertest.cc index ad8db42..4f357f6 100644 --- a/content/browser/web_contents/partitioned_popins_controller_browsertest.cc +++ b/content/browser/web_contents/partitioned_popins_controller_browsertest.cc
@@ -7,6 +7,7 @@ #include "base/check_op.h" #include "base/memory/weak_ptr.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "base/test/scoped_feature_list.h" #include "content/browser/renderer_host/render_frame_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h"
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc index b70e429..0798b89 100644 --- a/content/browser/webid/federated_auth_request_impl.cc +++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -21,6 +21,7 @@ #include "base/metrics/histogram_macros.h" #include "base/rand_util.h" #include "base/strings/escape.h" +#include "base/strings/stringprintf.h" #include "base/strings/to_string.h" #include "base/task/sequenced_task_runner.h" #include "base/time/time.h"
diff --git a/content/browser/webid/idp_network_request_manager.cc b/content/browser/webid/idp_network_request_manager.cc index b44932f..78755491 100644 --- a/content/browser/webid/idp_network_request_manager.cc +++ b/content/browser/webid/idp_network_request_manager.cc
@@ -11,6 +11,7 @@ #include "base/json/json_writer.h" #include "base/strings/escape.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "base/strings/to_string.h" #include "base/strings/utf_string_conversions.h" #include "base/task/sequenced_task_runner.h"
diff --git a/content/common/render_widget_host_ns_view.mojom b/content/common/render_widget_host_ns_view.mojom index 1e300e82..bbcf43d7 100644 --- a/content/common/render_widget_host_ns_view.mojom +++ b/content/common/render_widget_host_ns_view.mojom
@@ -162,18 +162,20 @@ // Indicates whether or not the NSView is its NSWindow's first responder. OnFirstResponderChanged(bool is_first_responder); - // Indicates whether or not the NSView is its NSWindow's first responder. - OnWindowIsKeyChanged(bool is_key); - // Indicates whether or not the NSView's NSWindow is key. - OnBoundsInWindowChanged( - gfx.mojom.Rect view_bounds_in_window_dip, - bool attached_to_window); + OnWindowIsKeyChanged(bool is_key); // Indicates the NSView's bounds in its NSWindow's DIP coordinate system (with // the origin at the upper-left corner), and indicate if the the NSView is // attached to an NSWindow (if it is not, then |view_bounds_in_window_dip|'s // origin is meaningless, but its size is still relevant). + OnBoundsInWindowChanged( + gfx.mojom.Rect view_bounds_in_window_dip, + bool attached_to_window); + + // Indicates the NSView's NSWindow's frame in the global display::Screen + // DIP coordinate system (where the origin the upper-left corner of + // Screen::GetPrimaryDisplay). OnWindowFrameInScreenChanged( gfx.mojom.Rect window_frame_in_screen_dip);
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc index eb036b9..2082523 100644 --- a/content/public/browser/content_browser_client.cc +++ b/content/public/browser/content_browser_client.cc
@@ -192,11 +192,12 @@ return true; } -std::optional<ContentBrowserClient::SpareProcessRefusedByEmbedderReason> -ContentBrowserClient::ShouldUseSpareRenderProcessHost( +bool ContentBrowserClient::ShouldUseSpareRenderProcessHost( BrowserContext* browser_context, - const GURL& site_url) { - return std::nullopt; + const GURL& site_url, + std::optional<SpareProcessRefusedByEmbedderReason>& refused_reason) { + refused_reason = std::nullopt; + return true; } bool ContentBrowserClient::DoesSiteRequireDedicatedProcess(
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h index 8c53f1a..3cff2ad 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h
@@ -461,8 +461,8 @@ // Returns whether a spare RenderProcessHost should be used for navigating to // the specified site URL. If the spare render process can be used, the - // function will return an empty value. Otherwise the detailed reason will be - // returned. + // function will return true with an empty refused_reason. Otherwise the + // function returns false and the detailed reason will be returned. // // Using the spare RenderProcessHost is advisable, because it can improve // performance of a navigation that requires a new process. On the other @@ -471,9 +471,10 @@ // ContentBrowserClient::AppendExtraCommandLineSwitches and add some cmdline // switches at navigation time (and this won't work for the spare, because the // spare RenderProcessHost is launched ahead of time). - virtual std::optional<SpareProcessRefusedByEmbedderReason> - ShouldUseSpareRenderProcessHost(BrowserContext* browser_context, - const GURL& site_url); + virtual bool ShouldUseSpareRenderProcessHost( + BrowserContext* browser_context, + const GURL& site_url, + std::optional<SpareProcessRefusedByEmbedderReason>& refused_reason); // Returns true if site isolation should be enabled for |effective_site_url|. // This call allows the embedder to supplement the site isolation policy
diff --git a/content/public/test/keep_alive_url_loader_utils.cc b/content/public/test/keep_alive_url_loader_utils.cc index 5855338..ae273cf 100644 --- a/content/public/test/keep_alive_url_loader_utils.cc +++ b/content/public/test/keep_alive_url_loader_utils.cc
@@ -285,6 +285,33 @@ impl_->get()->WaitForTotalOnCompleteProcessed(error_codes); } +KeepAliveRequestUkmMatcher::CommonUkm::CommonUkm( + KeepAliveRequestTracker::RequestType request_type, + size_t category_id, + size_t num_redirects, + bool is_context_detached, + KeepAliveRequestTracker::RequestStageType end_stage, + std::optional<KeepAliveRequestTracker::RequestStageType> previous_stage, + const std::optional<base::UnguessableToken>& keepalive_token, + std::optional<int64_t> failed_error_code, + std::optional<int64_t> failed_extended_error_code, + std::optional<int64_t> completed_error_code, + std::optional<int64_t> completed_extended_error_code) + : request_type(request_type), + category_id(category_id), + num_redirects(num_redirects), + is_context_detached(is_context_detached), + end_stage(end_stage), + previous_stage(previous_stage), + keepalive_token(keepalive_token), + failed_error_code(failed_error_code), + failed_extended_error_code(failed_extended_error_code), + completed_error_code(completed_error_code), + completed_extended_error_code(completed_extended_error_code) {} + +KeepAliveRequestUkmMatcher::CommonUkm::CommonUkm(const CommonUkm& other) = + default; + const ukm::mojom::UkmEntry* KeepAliveRequestUkmMatcher::GetUkmEntry() { auto entries = ukm_recorder().GetEntriesByName(UkmEvent::kEntryName); CHECK_EQ(entries.size(), 1u) @@ -307,8 +334,10 @@ KeepAliveRequestTracker::RequestStageType end_stage, std::optional<KeepAliveRequestTracker::RequestStageType> previous_stage, const std::optional<base::UnguessableToken>& keepalive_token, - std::optional<int64_t> error_code, - std::optional<int64_t> extended_error_code) { + std::optional<int64_t> failed_error_code, + std::optional<int64_t> failed_extended_error_code, + std::optional<int64_t> completed_error_code, + std::optional<int64_t> completed_extended_error_code) { EXPECT_TRUE(ukm_recorder().EntryHasMetric(entry, "Id.Low")); EXPECT_TRUE(ukm_recorder().EntryHasMetric(entry, "Id.High")); if (keepalive_token.has_value()) { @@ -335,21 +364,39 @@ EXPECT_FALSE(ukm_recorder().EntryHasMetric(entry, "PreviousStage")); } - if (error_code.has_value()) { - ukm_recorder().ExpectEntryMetric(entry, "CompletionStatus.ErrorCode", - static_cast<int64_t>(*error_code)); + if (failed_error_code.has_value()) { + ukm_recorder().ExpectEntryMetric(entry, "RequestFailed.ErrorCode", + static_cast<int64_t>(*failed_error_code)); } else { EXPECT_FALSE( - ukm_recorder().EntryHasMetric(entry, "CompletionStatus.ErrorCode")); + ukm_recorder().EntryHasMetric(entry, "RequestFailed.ErrorCode")); } - if (extended_error_code.has_value()) { + if (failed_extended_error_code.has_value()) { ukm_recorder().ExpectEntryMetric( - entry, "CompletionStatus.ExtendedErrorCode", - static_cast<int64_t>(*extended_error_code)); + entry, "RequestFailed.ExtendedErrorCode", + static_cast<int64_t>(*failed_extended_error_code)); } else { EXPECT_FALSE(ukm_recorder().EntryHasMetric( - entry, "CompletionStatus.ExtendedErrorCode")); + entry, "RequestFailed.ExtendedErrorCode")); + } + + if (completed_error_code.has_value()) { + ukm_recorder().ExpectEntryMetric( + entry, "LoaderCompleted.ErrorCode", + static_cast<int64_t>(*completed_error_code)); + } else { + EXPECT_FALSE( + ukm_recorder().EntryHasMetric(entry, "LoaderCompleted.ErrorCode")); + } + + if (completed_extended_error_code.has_value()) { + ukm_recorder().ExpectEntryMetric( + entry, "LoaderCompleted.ExtendedErrorCode", + static_cast<int64_t>(*completed_extended_error_code)); + } else { + EXPECT_FALSE(ukm_recorder().EntryHasMetric( + entry, "LoaderCompleted.ExtendedErrorCode")); } } @@ -361,12 +408,16 @@ KeepAliveRequestTracker::RequestStageType end_stage, std::optional<KeepAliveRequestTracker::RequestStageType> previous_stage, const std::optional<base::UnguessableToken>& keepalive_token, - std::optional<int64_t> error_code, - std::optional<int64_t> extended_error_code) { + std::optional<int64_t> failed_error_code, + std::optional<int64_t> failed_extended_error_code, + std::optional<int64_t> completed_error_code, + std::optional<int64_t> completed_extended_error_code) { const ukm::mojom::UkmEntry* entry = GetUkmEntry(); ExpectCommonUkm(entry, request_type, category_id, num_redirects, is_context_detached, end_stage, previous_stage, - keepalive_token, error_code, extended_error_code); + keepalive_token, failed_error_code, + failed_extended_error_code, completed_error_code, + completed_extended_error_code); } void KeepAliveRequestUkmMatcher::ExpectCommonUkms( @@ -378,11 +429,12 @@ << ukms.size(); for (size_t i = 0; i < entries.size(); ++i) { - ExpectCommonUkm(entries[i], ukms[i].request_type, ukms[i].category_id, - ukms[i].num_redirects, ukms[i].is_context_detached, - ukms[i].end_stage, ukms[i].previous_stage, - ukms[i].keepalive_token, ukms[i].error_code, - ukms[i].extended_error_code); + ExpectCommonUkm( + entries[i], ukms[i].request_type, ukms[i].category_id, + ukms[i].num_redirects, ukms[i].is_context_detached, ukms[i].end_stage, + ukms[i].previous_stage, ukms[i].keepalive_token, + ukms[i].failed_error_code, ukms[i].failed_extended_error_code, + ukms[i].completed_error_code, ukms[i].completed_extended_error_code); } }
diff --git a/content/public/test/keep_alive_url_loader_utils.h b/content/public/test/keep_alive_url_loader_utils.h index c13daca6..c6a037d 100644 --- a/content/public/test/keep_alive_url_loader_utils.h +++ b/content/public/test/keep_alive_url_loader_utils.h
@@ -110,8 +110,26 @@ std::optional<KeepAliveRequestTracker::RequestStageType> previous_stage = std::nullopt; std::optional<base::UnguessableToken> keepalive_token = std::nullopt; - std::optional<int64_t> error_code = std::nullopt; - std::optional<int64_t> extended_error_code = std::nullopt; + std::optional<int64_t> failed_error_code = std::nullopt; + std::optional<int64_t> failed_extended_error_code = std::nullopt; + std::optional<int64_t> completed_error_code = std::nullopt; + std::optional<int64_t> completed_extended_error_code = std::nullopt; + + CommonUkm( + KeepAliveRequestTracker::RequestType request_type, + size_t category_id, + size_t num_redirects, + bool is_context_detached, + KeepAliveRequestTracker::RequestStageType end_stage, + std::optional<KeepAliveRequestTracker::RequestStageType> + previous_stage = std::nullopt, + const std::optional<base::UnguessableToken>& keepalive_token = + std::nullopt, + std::optional<int64_t> failed_error_code = std::nullopt, + std::optional<int64_t> failed_extended_error_code = std::nullopt, + std::optional<int64_t> completed_error_code = std::nullopt, + std::optional<int64_t> completed_extended_error_code = std::nullopt); + CommonUkm(const CommonUkm& other); }; // Verifies all the common UKM metrics. @@ -128,8 +146,10 @@ std::nullopt, const std::optional<base::UnguessableToken>& keepalive_token = std::nullopt, - std::optional<int64_t> error_code = std::nullopt, - std::optional<int64_t> extended_error_code = std::nullopt); + std::optional<int64_t> failed_error_code = std::nullopt, + std::optional<int64_t> failed_extended_error_code = std::nullopt, + std::optional<int64_t> completed_error_code = std::nullopt, + std::optional<int64_t> completed_extended_error_code = std::nullopt); void ExpectCommonUkms(const std::vector<CommonUkm>& ukms); // Verifies that UKM TimeDelta.* listed in `time_sorted_metric_names` are all @@ -155,8 +175,10 @@ std::nullopt, const std::optional<base::UnguessableToken>& keepalive_token = std::nullopt, - std::optional<int64_t> error_code = std::nullopt, - std::optional<int64_t> extended_error_code = std::nullopt); + std::optional<int64_t> failed_error_code = std::nullopt, + std::optional<int64_t> failed_extended_error_code = std::nullopt, + std::optional<int64_t> completed_error_code = std::nullopt, + std::optional<int64_t> completed_extended_error_code = std::nullopt); }; // `NavigationKeepAliveRequestUkmMatcher` provides common matchers and
diff --git a/content/public/test/prerender_test_util.cc b/content/public/test/prerender_test_util.cc index 0f24e54..54a47f2 100644 --- a/content/public/test/prerender_test_util.cc +++ b/content/public/test/prerender_test_util.cc
@@ -8,6 +8,7 @@ #include "base/functional/callback_helpers.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/test/bind.h" #include "base/trace_event/typed_macros.h"
diff --git a/content/public/test/test_utils.cc b/content/public/test/test_utils.cc index 635de19..e80f8ed 100644 --- a/content/public/test/test_utils.cc +++ b/content/public/test/test_utils.cc
@@ -563,6 +563,8 @@ // for the default SiteInstance). It is important not to call // SiteInfo::CreateForTesting here to avoid an infinite recursive call when // computing values for the SiteInfo. + // TODO(crbug.com/390571607): Make sure this test works as intended in + // default SiteInstanceGroup mode. GURL maybe_modified_url = SiteInfo::GetSiteForURLForTest(IsolationContext(browser_context), UrlInfo::CreateForTesting(pair.first),
diff --git a/content/shell/browser/shell_platform_delegate_ios.mm b/content/shell/browser/shell_platform_delegate_ios.mm index 652a75be..e3e9ce53 100644 --- a/content/shell/browser/shell_platform_delegate_ios.mm +++ b/content/shell/browser/shell_platform_delegate_ios.mm
@@ -126,6 +126,54 @@ alpha:1.0]; } +#if BUILDFLAG(IS_IOS_TVOS) +// The following methods handle tvOS's focus engine by implementing the +// following behavior: +// +// 1. The content view is focused and receives user input by default. +// 2. Pressing the Menu button in the remote control switches focus to +// `_headerContentView` so that users can use the toolbar and the location +// bar. +// 3. Pressing the Menu button again after that will switch to the home screen, +// and swiping down to focus the content view will reset the behavior +// described in 1). +- (void)pressesBegan:(NSSet<UIPress*>*)presses + withEvent:(UIPressesEvent*)event { + for (UIPress* press in presses) { + if (press.type == UIPressTypeMenu) { + if (_shell->web_contents()->GetContentNativeView().Get().focused) { + _headerContentView.userInteractionEnabled = YES; + [self setNeedsFocusUpdate]; + return; + } + } + } + [super pressesBegan:presses withEvent:event]; +} + +- (void)didUpdateFocusInContext:(UIFocusUpdateContext*)context + withAnimationCoordinator:(UIFocusAnimationCoordinator*)coordinator { + if (_shell) { + const UIView* native_web_contents_view = + _shell->web_contents()->GetContentNativeView().Get(); + if (context.nextFocusedView == native_web_contents_view) { + _headerContentView.userInteractionEnabled = NO; + _shell->web_contents()->Focus(); + } + } +} + +- (NSArray<id<UIFocusEnvironment>>*)preferredFocusEnvironments { + // `userInteractionEnabled` is false when we create `_headerContentView` so + // that we focus on `_contentView` by default instead of the Back button in + // the toolbar. + // We set it to true when explicitly pressing the Back button on the remote + // control in order to focus the toolbar. + return _headerContentView.userInteractionEnabled ? @[ _headerContentView ] + : @[ _contentView ]; +} +#endif + - (void)viewDidLoad { [super viewDidLoad]; @@ -168,6 +216,12 @@ _headerBackgroundView.layoutMarginsRelativeArrangement = YES; _headerBackgroundView.preservesSuperviewLayoutMargins = YES; +#if BUILDFLAG(IS_IOS_TVOS) + // On tvOS, make it impossible to focus `_headerContentView` by simply + // swiping up on the remote control since this behavior is not intuitive. + _headerContentView.userInteractionEnabled = NO; +#endif + _headerContentView.alignment = UIStackViewAlignmentCenter; _headerContentView.axis = UILayoutConstraintAxisHorizontal; _headerContentView.spacing = 16.0;
diff --git a/device/gamepad/gamepad_id_list.cc b/device/gamepad/gamepad_id_list.cc index 869105c43..16885bc 100644 --- a/device/gamepad/gamepad_id_list.cc +++ b/device/gamepad/gamepad_id_list.cc
@@ -613,6 +613,7 @@ {{0x2dc8, 0x2830}, kXInputTypeNone}, {{0x2dc8, 0x3000}, kXInputTypeNone}, {{0x2dc8, 0x3001}, kXInputTypeNone}, + {{0x2dc8, 0x301b}, kXInputTypeNone}, {{0x2dc8, 0x3106}, kXInputTypeXbox360}, {{0x2dc8, 0x3820}, kXInputTypeNone}, {{0x2dc8, 0x9001}, kXInputTypeNone},
diff --git a/device/gamepad/gamepad_id_list.h b/device/gamepad/gamepad_id_list.h index 191bd14..92b85c0 100644 --- a/device/gamepad/gamepad_id_list.h +++ b/device/gamepad/gamepad_id_list.h
@@ -42,6 +42,7 @@ // Fake IDs for devices which report as 0x0000 0x0000 kPowerALicPro = 0x0000ff00, // ID values for supported devices. + k8BitDoProduct301b = 0x2dc8301b, k8BitDoProduct3106 = 0x2dc83106, kAcerProduct1304 = 0x05021304, kAcerProduct1305 = 0x05021305,
diff --git a/device/gamepad/gamepad_standard_mappings_mac.mm b/device/gamepad/gamepad_standard_mappings_mac.mm index bd89f78..a7b33e84 100644 --- a/device/gamepad/gamepad_standard_mappings_mac.mm +++ b/device/gamepad/gamepad_standard_mappings_mac.mm
@@ -139,6 +139,12 @@ mapped->axes_length = AXIS_INDEX_COUNT; } +void Mapper8BitDoBluetooth(const Gamepad& input, Gamepad* mapped) { + MapperXboxBluetooth(input, mapped); + mapped->buttons[BUTTON_INDEX_LEFT_TRIGGER] = AxisToButton(input.axes[4]); + mapped->buttons[BUTTON_INDEX_RIGHT_TRIGGER] = AxisToButton(input.axes[3]); +} + void MapperPlaystationSixAxis(const Gamepad& input, Gamepad* mapped) { *mapped = input; mapped->buttons[BUTTON_INDEX_PRIMARY] = input.buttons[14]; @@ -782,6 +788,8 @@ } kAvailableMappings[] = { // PowerA Wireless Controller - Nintendo GameCube style {GamepadId::kPowerALicPro, MapperSwitchPro}, + // 8BitDo Ultimate Wireless 2C (Bluetooth) + {GamepadId::k8BitDoProduct301b, Mapper8BitDoBluetooth}, // Snakebyte iDroid:con {GamepadId::kBroadcomProduct8502, MapperSnakebyteIDroidCon}, // DragonRise Generic USB
diff --git a/ios/chrome/browser/autofill/ui_bundled/autofill_ui_constants.h b/ios/chrome/browser/autofill/ui_bundled/autofill_ui_constants.h index 691cff9..1f0adcc 100644 --- a/ios/chrome/browser/autofill/ui_bundled/autofill_ui_constants.h +++ b/ios/chrome/browser/autofill/ui_bundled/autofill_ui_constants.h
@@ -13,6 +13,11 @@ // the UI does. const base::TimeDelta kSelectSuggestionDelay = base::Milliseconds(500); +// The delay between showing the confirmation and dismissing the progress +// dialog. +const base::TimeDelta kProgressDialogConfirmationDismissDelay = + base::Seconds(1); + } // namespace autofill_ui_constants #endif // IOS_CHROME_BROWSER_AUTOFILL_UI_BUNDLED_AUTOFILL_UI_CONSTANTS_H_
diff --git a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/BUILD.gn b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/BUILD.gn index fedcb52..0949bcd 100644 --- a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/BUILD.gn +++ b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/BUILD.gn
@@ -165,6 +165,7 @@ "//components/url_formatter", "//ios/chrome/app/strings", "//ios/chrome/browser/autofill/model:features", + "//ios/chrome/browser/autofill/ui_bundled:autofill_ui_constants", "//ios/chrome/browser/autofill/ui_bundled:eg_test_support+eg2", "//ios/chrome/browser/autofill/ui_bundled/authentication:card_unmask_authentication_selection_constants", "//ios/chrome/browser/autofill/ui_bundled/manual_fill:eg_test_support+eg2",
diff --git a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_egtest.mm b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_egtest.mm index 3d8ab35..2a70828 100644 --- a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_egtest.mm +++ b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_egtest.mm
@@ -16,6 +16,7 @@ #import "components/url_formatter/elide_url.h" #import "ios/chrome/browser/autofill/model/features.h" #import "ios/chrome/browser/autofill/ui_bundled/autofill_app_interface.h" +#import "ios/chrome/browser/autofill/ui_bundled/autofill_ui_constants.h" #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_matchers.h" #import "ios/chrome/browser/metrics/model/metrics_app_interface.h" #import "ios/chrome/browser/settings/ui_bundled/settings_root_table_constants.h" @@ -42,6 +43,11 @@ const char kFormCardNumber[] = "CCNo"; const char kFormCardExpirationMonth[] = "CCExpiresMonth"; const char kFormCardExpirationYear[] = "CCExpiresYear"; +NSString* const kTriggeringRequestUrl = + @"https://payments.google.com/payments/apis-secure/creditcardservice/" + @"getrealpan?s7e_suffix=chromewallet"; +NSString* const kSuccessResponseNoAuthNeeded = + @"{ \"pan\": \"5411111111112109\" }"; // Matcher for the credit card suggestion chip. id<GREYMatcher> KeyboardAccessoryCreditCardSuggestionChip() { @@ -119,6 +125,14 @@ (testAttemptToOpenPaymentsBottomSheetWithoutCreditCardOnV3)]) { config.features_enabled.push_back(kAutofillPaymentsSheetV3Ios); config.features_enabled.push_back(kStatelessFormSuggestionController); + } else if ([self + isRunningTest:@selector(testFillingFromKeyboardOnAutofocus)]) { + config.features_enabled.push_back( + autofill::features::kAutofillEnableFpanRiskBasedAuthentication); + } else if ([self isRunningTest:@selector + (testUpdateBottomSheetOnAddServerCreditCard)]) { + config.features_enabled.push_back( + autofill::features::kAutofillEnableFpanRiskBasedAuthentication); } return config; } @@ -462,6 +476,8 @@ id<GREYMatcher> continueButton = WaitOnResponsiveContinueButton(); + [AutofillAppInterface setUpFakeCreditCardServer]; + // Add a credit card to the Personal Data Manager. id<GREYMatcher> serverCreditCardEntry = grey_text([AutofillAppInterface saveMaskedCreditCard]); @@ -491,9 +507,26 @@ [[EarlGrey selectElementWithMatcher:continueButton] performAction:grey_tap()]; - // Verify the CVC requester is visible. - [[EarlGrey selectElementWithMatcher:grey_text(@"Verification")] - assertWithMatcher:grey_notNil()]; + // Wait for the progress dialog to appear. + [ChromeEarlGrey waitForUIElementToAppearWithMatcher: + chrome_test_util::StaticTextWithAccessibilityLabelId( + IDS_AUTOFILL_CARD_UNMASK_PROGRESS_DIALOG_TITLE)]; + // Fake the successful server response that triggers Dismiss. + [AutofillAppInterface setPaymentsResponse:kSuccessResponseNoAuthNeeded + forRequest:kTriggeringRequestUrl + withErrorCode:net::HTTP_OK]; + // This delay is the autodismiss delay (1 second) + extra time to avoid + // flakiness on the simulators (2 seconds). + const base::TimeDelta total_delay_for_dismiss = + autofill_ui_constants::kProgressDialogConfirmationDismissDelay + + base::Seconds(2); + + // Wait for the dialog to disappear after the delay. + [ChromeEarlGrey + waitForUIElementToDisappearWithMatcher: + chrome_test_util::StaticTextWithAccessibilityLabelId( + IDS_AUTOFILL_CARD_UNMASK_PROGRESS_DIALOG_TITLE) + timeout:total_delay_for_dismiss]; GREYAssertNil( [MetricsAppInterface @@ -503,9 +536,6 @@ @"Autofill.TouchToFill.CreditCard.SelectedIndex"], @"Unexpected histogram error for touch to fill credit card selected " @"index"); - - // TODO(crbug.com/40577448): Figure out a way to enter CVC and get the - // unlocked card result. } // Tests that accessing a long press menu does not disable the bottom sheet. @@ -798,6 +828,8 @@ // for this test case. [AutofillAppInterface clearCreditCardStore]; + [AutofillAppInterface setUpFakeCreditCardServer]; + // Add the server credit card. Before loading the page so it can be in the // autofill suggestion upon autofocusing the credit card field. [AutofillAppInterface saveMaskedCreditCard];
diff --git a/ios/chrome/browser/autofill/ui_bundled/progress_dialog/BUILD.gn b/ios/chrome/browser/autofill/ui_bundled/progress_dialog/BUILD.gn index 3faac1e..499e33d 100644 --- a/ios/chrome/browser/autofill/ui_bundled/progress_dialog/BUILD.gn +++ b/ios/chrome/browser/autofill/ui_bundled/progress_dialog/BUILD.gn
@@ -16,6 +16,7 @@ "//components/strings", "//ios/chrome/browser/alert_view/ui_bundled", "//ios/chrome/browser/autofill/model:model_internal", + "//ios/chrome/browser/autofill/ui_bundled:autofill_ui_constants", "//ios/chrome/browser/autofill/ui_bundled:coordinator", "//ios/chrome/browser/shared/coordinator/chrome_coordinator", "//ios/chrome/browser/shared/model/browser",
diff --git a/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_egtest.mm b/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_egtest.mm index c598ab7..c7de6a4 100644 --- a/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_egtest.mm +++ b/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_egtest.mm
@@ -36,13 +36,6 @@ } // namespace -// Matcher for the "Cancel" button. -id<GREYMatcher> CancelButton() { - return grey_allOf( - chrome_test_util::CancelButton(), - grey_not(grey_accessibilityTrait(UIAccessibilityTraitNotEnabled)), nil); -} - @interface AutofillProgressDialogDismissEGTest : ChromeTestCase { NSString* _enrolledCardNameAndLastFour; } @@ -126,13 +119,18 @@ forRequest:kTriggeringRequestUrl withErrorCode:net::HTTP_OK]; - // Wait for the dialog to disappear after the delay. This delay is the - // autodismiss delay (1 second) + extra time to avoid flakiness on the - // simulators (2 seconds). - [ChromeEarlGrey waitForUIElementToDisappearWithMatcher: - chrome_test_util::StaticTextWithAccessibilityLabelId( - IDS_AUTOFILL_CARD_UNMASK_PROGRESS_DIALOG_TITLE) - timeout:base::Seconds(3)]; + // This delay is the autodismiss delay (1 second) + extra time to avoid + // flakiness on the simulators (2 seconds). + const base::TimeDelta total_delay_for_dismiss = + autofill_ui_constants::kProgressDialogConfirmationDismissDelay + + base::Seconds(2); + + // Wait for the dialog to disappear after the delay. + [ChromeEarlGrey + waitForUIElementToDisappearWithMatcher: + chrome_test_util::StaticTextWithAccessibilityLabelId( + IDS_AUTOFILL_CARD_UNMASK_PROGRESS_DIALOG_TITLE) + timeout:total_delay_for_dismiss]; } @end
diff --git a/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_mediator.mm b/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_mediator.mm index 370938b..d243e45d 100644 --- a/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_mediator.mm +++ b/ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_mediator.mm
@@ -17,16 +17,11 @@ #import "components/strings/grit/components_strings.h" #import "ios/chrome/browser/alert_view/ui_bundled/alert_action.h" #import "ios/chrome/browser/alert_view/ui_bundled/alert_consumer.h" +#import "ios/chrome/browser/autofill/ui_bundled/autofill_ui_constants.h" #import "ios/chrome/browser/autofill/ui_bundled/progress_dialog/autofill_progress_dialog_mediator_delegate.h" #import "ios/chrome/browser/shared/ui/symbols/symbols.h" #import "ui/base/l10n/l10n_util.h" -namespace { -// The delay between showing the confirmation and dismissing the progress -// dialog. -constexpr base::TimeDelta kConfirmationDismissDelay = base::Seconds(1); -} // namespace - AutofillProgressDialogMediator::AutofillProgressDialogMediator( autofill::AutofillProgressDialogControllerImpl* model_controller, id<AutofillProgressDialogMediatorDelegate> delegate) @@ -64,7 +59,8 @@ weak_ptr_factory_.GetWeakPtr()); base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( - FROM_HERE, std::move(closure), kConfirmationDismissDelay); + FROM_HERE, std::move(closure), + autofill_ui_constants::kProgressDialogConfirmationDismissDelay); } void AutofillProgressDialogMediator::InvalidateControllerForCallbacks() {
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm index bef4eaed8..0d983729 100644 --- a/ios/chrome/browser/flags/about_flags.mm +++ b/ios/chrome/browser/flags/about_flags.mm
@@ -2688,6 +2688,15 @@ flags_ui::kOsIos, FEATURE_VALUE_TYPE( autofill::features::kAutofillEnableFpanRiskBasedAuthentication)}, + {"autofill-enable-multiple-request-in-virtual-card-downstream-enrollment", + flag_descriptions:: + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollmentName, + flag_descriptions:: + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollmentDescription, + flags_ui::kOsIos, + FEATURE_VALUE_TYPE( + autofill::features:: + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment)}, }; bool SkipConditionalFeatureEntry(const flags_ui::FeatureEntry& entry) {
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc index 0828f56..bd2f4c5 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -102,6 +102,16 @@ "all credit card form types and address form events will log to all " "address form types."; +const char + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollmentName[] = + "Enable multiple server request support for virtual card downstream " + "enrollment"; +const char + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollmentDescription + [] = "When enabled, Chrome will be able to send preflight call for " + "enrollment earlier in the flow with the multiple server request " + "support."; + const char kAutofillEnableFpanRiskBasedAuthenticationName[] = "Enable risk-based authentication for FPAN retrieval"; const char kAutofillEnableFpanRiskBasedAuthenticationDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h index efc1af6..4a6320f 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -72,6 +72,12 @@ extern const char kAutofillEnableLogFormEventsToAllParsedFormTypesName[]; extern const char kAutofillEnableLogFormEventsToAllParsedFormTypesDescription[]; +extern const char + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollmentName[]; +extern const char + kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollmentDescription + []; + extern const char kAutofillEnablePrefetchingRiskDataForRetrievalName[]; extern const char kAutofillEnablePrefetchingRiskDataForRetrievalDescription[];
diff --git a/ios/chrome/browser/home_customization/coordinator/BUILD.gn b/ios/chrome/browser/home_customization/coordinator/BUILD.gn index 26e1bb5..f27ae3f 100644 --- a/ios/chrome/browser/home_customization/coordinator/BUILD.gn +++ b/ios/chrome/browser/home_customization/coordinator/BUILD.gn
@@ -24,6 +24,7 @@ "//base", "//base:i18n", "//components/commerce/core:feature_list", + "//components/image_fetcher/core", "//components/prefs", "//ios/chrome/app/strings", "//ios/chrome/browser/content_suggestions/ui_bundled/set_up_list:utils", @@ -32,6 +33,7 @@ "//ios/chrome/browser/home_customization/model", "//ios/chrome/browser/home_customization/ui", "//ios/chrome/browser/home_customization/utils", + "//ios/chrome/browser/image_fetcher/model", "//ios/chrome/browser/ntp/ui_bundled:logo", "//ios/chrome/browser/parcel_tracking:features", "//ios/chrome/browser/shared/coordinator/alert", @@ -71,6 +73,7 @@ "//ios/chrome/browser/discover_feed/model:discover_feed_visibility_browser_agent", "//ios/chrome/browser/home_customization/ui", "//ios/chrome/browser/home_customization/utils", + "//ios/chrome/browser/image_fetcher/model", "//ios/chrome/browser/shared/model/browser/test:test_support", "//ios/chrome/browser/shared/model/prefs:browser_prefs", "//ios/chrome/browser/shared/model/prefs:pref_names",
diff --git a/ios/chrome/browser/home_customization/coordinator/DEPS b/ios/chrome/browser/home_customization/coordinator/DEPS index 6d8f78b3..3ef772d 100644 --- a/ios/chrome/browser/home_customization/coordinator/DEPS +++ b/ios/chrome/browser/home_customization/coordinator/DEPS
@@ -5,5 +5,6 @@ "+ios/chrome/browser/parcel_tracking", "+ios/chrome/browser/content_suggestions/ui_bundled/set_up_list", "+ios/chrome/browser/ntp/ui_bundled", - "+third_party/material_color_utilities/src/cpp" + "+third_party/material_color_utilities/src/cpp", + "+ios/chrome/browser/image_fetcher", ]
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_background_picker_action_sheet_coordinator.mm b/ios/chrome/browser/home_customization/coordinator/home_customization_background_picker_action_sheet_coordinator.mm index c24960d..61f334a 100644 --- a/ios/chrome/browser/home_customization/coordinator/home_customization_background_picker_action_sheet_coordinator.mm +++ b/ios/chrome/browser/home_customization/coordinator/home_customization_background_picker_action_sheet_coordinator.mm
@@ -4,6 +4,7 @@ #import "ios/chrome/browser/home_customization/coordinator/home_customization_background_picker_action_sheet_coordinator.h" +#import "components/image_fetcher/core/image_fetcher_service.h" #import "ios/chrome/browser/home_customization/coordinator/home_customization_background_color_picker_mediator.h" #import "ios/chrome/browser/home_customization/coordinator/home_customization_background_photo_picker_coordinator.h" #import "ios/chrome/browser/home_customization/coordinator/home_customization_background_preset_gallery_picker_mediator.h" @@ -11,6 +12,7 @@ #import "ios/chrome/browser/home_customization/ui/home_customization_background_photo_library_picker_view_controller.h" #import "ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_view_controller.h" #import "ios/chrome/browser/home_customization/ui/home_customization_logo_vendor_provider.h" +#import "ios/chrome/browser/image_fetcher/model/image_fetcher_service_factory.h" #import "ios/chrome/browser/shared/model/browser/browser.h" #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h" #import "ios/chrome/grit/ios_strings.h" @@ -47,11 +49,14 @@ - (void)start { __weak __typeof(self) weakSelf = self; + image_fetcher::ImageFetcherService* imageFetcherService = + ImageFetcherServiceFactory::GetForProfile(self.browser->GetProfile()); + _backgroundColorPickerMediator = [[HomeCustomizationBackgroundColorPickerMediator alloc] init]; - _backgroundPresetGalleryPickerMediator = - [[HomeCustomizationBackgroundPresetGalleryPickerMediator alloc] init]; + [[HomeCustomizationBackgroundPresetGalleryPickerMediator alloc] + initWithImageFetcherService:imageFetcherService]; [self addItemWithTitle:
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_background_preset_gallery_picker_mediator.h b/ios/chrome/browser/home_customization/coordinator/home_customization_background_preset_gallery_picker_mediator.h index 240f6b7d..84c1bc0 100644 --- a/ios/chrome/browser/home_customization/coordinator/home_customization_background_preset_gallery_picker_mediator.h +++ b/ios/chrome/browser/home_customization/coordinator/home_customization_background_preset_gallery_picker_mediator.h
@@ -9,6 +9,10 @@ #import "ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_mutator.h" +namespace image_fetcher { +class ImageFetcherService; +} + @protocol HomeCustomizationBackgroundPresetGalleryPickerConsumer; // A mediator that generates and configures background presets for the Home @@ -21,6 +25,11 @@ id<HomeCustomizationBackgroundPresetGalleryPickerConsumer> consumer; +// Initializes a new instance of the background preset gallery picker mediator +// with the provided image fetcher service. +- (instancetype)initWithImageFetcherService: + (image_fetcher::ImageFetcherService*)imageFetcherService; + // Provide a collection of background configurations to the consumer. - (void)configureBackgroundConfigurations;
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_background_preset_gallery_picker_mediator.mm b/ios/chrome/browser/home_customization/coordinator/home_customization_background_preset_gallery_picker_mediator.mm index ea8cc1fc..c9d1ebe 100644 --- a/ios/chrome/browser/home_customization/coordinator/home_customization_background_preset_gallery_picker_mediator.mm +++ b/ios/chrome/browser/home_customization/coordinator/home_customization_background_preset_gallery_picker_mediator.mm
@@ -6,12 +6,32 @@ #import <Foundation/Foundation.h> +#import "components/image_fetcher/core/image_fetcher.h" +#import "components/image_fetcher/core/image_fetcher_service.h" #import "ios/chrome/browser/home_customization/model/background_collection_configuration.h" #import "ios/chrome/browser/home_customization/model/background_customization_configuration.h" #import "ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_consumer.h" +#import "ui/gfx/image/image.h" +#import "url/gurl.h" + +@interface HomeCustomizationBackgroundPresetGalleryPickerMediator () { + // The image fetcher used to download individual background preset images. + raw_ptr<image_fetcher::ImageFetcher> _imageFetcher; +} +@end @implementation HomeCustomizationBackgroundPresetGalleryPickerMediator +- (instancetype)initWithImageFetcherService: + (image_fetcher::ImageFetcherService*)imageFetcherService { + self = [super init]; + if (self) { + _imageFetcher = imageFetcherService->GetImageFetcher( + image_fetcher::ImageFetcherConfig::kDiskCacheOnly); + } + return self; +} + - (void)configureBackgroundConfigurations { NSMutableArray<BackgroundCollectionConfiguration*>* configurations = [NSMutableArray array]; @@ -74,9 +94,32 @@ selectedBackgroundId:selectedBackgroundId]; } +#pragma mark - HomeCustomizationBackgroundPresetGalleryPickerMutator + - (void)applyBackgroundForConfiguration: (BackgroundCustomizationConfiguration*)backgroundConfiguration { // TODO(crbug.com/408243803): apply NTP background configuration to NTP. } +- (void)fetchBackgroundCustomizationThumbnailURLImage:(GURL)thumbnailURL + completion: + (void (^)(UIImage*))completion { + CHECK(!thumbnailURL.is_empty()); + CHECK(thumbnailURL.is_valid()); + + _imageFetcher->FetchImage( + thumbnailURL, + base::BindOnce(^(const gfx::Image& image, + const image_fetcher::RequestMetadata& metadata) { + if (!image.IsEmpty()) { + UIImage* uiImage = image.ToUIImage(); + if (completion) { + completion(uiImage); + } + } + }), + // TODO (crbug.com/417234848): Add annotation. + image_fetcher::ImageFetcherParams(NO_TRAFFIC_ANNOTATION_YET, "Test")); +} + @end
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator.mm b/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator.mm index 80ae099e..399b850 100644 --- a/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator.mm +++ b/ios/chrome/browser/home_customization/coordinator/home_customization_coordinator.mm
@@ -4,6 +4,7 @@ #import "ios/chrome/browser/home_customization/coordinator/home_customization_coordinator.h" +#import "components/image_fetcher/core/image_fetcher_service.h" #import "ios/chrome/browser/discover_feed/model/discover_feed_visibility_browser_agent.h" #import "ios/chrome/browser/home_customization/coordinator/home_customization_background_picker_action_sheet_coordinator.h" #import "ios/chrome/browser/home_customization/coordinator/home_customization_delegate.h" @@ -15,6 +16,7 @@ #import "ios/chrome/browser/home_customization/ui/home_customization_magic_stack_view_controller.h" #import "ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.h" #import "ios/chrome/browser/home_customization/utils/home_customization_constants.h" +#import "ios/chrome/browser/image_fetcher/model/image_fetcher_service_factory.h" #import "ios/chrome/browser/ntp/ui_bundled/logo_vendor.h" #import "ios/chrome/browser/shared/coordinator/alert/action_sheet_coordinator.h" #import "ios/chrome/browser/shared/model/browser/browser.h" @@ -72,10 +74,14 @@ #pragma mark - ChromeCoordinator - (void)start { + image_fetcher::ImageFetcherService* imageFetcherService = + ImageFetcherServiceFactory::GetForProfile(self.browser->GetProfile()); + _mediator = [[HomeCustomizationMediator alloc] initWithPrefService:self.profile->GetPrefs() discoverFeedVisibilityBrowserAgent:DiscoverFeedVisibilityBrowserAgent:: - FromBrowser(self.browser)]; + FromBrowser(self.browser) + imageFetcherService:imageFetcherService]; _mediator.navigationDelegate = self; // The Customization menu consists of a stack of presenting view controllers.
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.h b/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.h index f843968..3f844cf 100644 --- a/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.h +++ b/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.h
@@ -16,6 +16,10 @@ @protocol HomeCustomizationNavigationDelegate; class PrefService; +namespace image_fetcher { +class ImageFetcherService; +} + // The mediator for the Home surface's customization menu. @interface HomeCustomizationMediator : NSObject <HomeCustomizationMutator> @@ -23,6 +27,8 @@ - (instancetype)initWithPrefService:(PrefService*)prefService discoverFeedVisibilityBrowserAgent: (DiscoverFeedVisibilityBrowserAgent*)discoverFeedVisibilityBrowserAgent + imageFetcherService: + (image_fetcher::ImageFetcherService*)imageFetcherService NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE;
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.mm b/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.mm index 9701bab0..c15f2dc 100644 --- a/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.mm +++ b/ios/chrome/browser/home_customization/coordinator/home_customization_mediator.mm
@@ -9,6 +9,8 @@ #import "base/strings/sys_string_conversions.h" #import "base/strings/utf_string_conversions.h" #import "components/commerce/core/commerce_feature_list.h" +#import "components/image_fetcher/core/image_fetcher.h" +#import "components/image_fetcher/core/image_fetcher_service.h" #import "components/prefs/pref_service.h" #import "ios/chrome/browser/content_suggestions/ui_bundled/set_up_list/utils.h" #import "ios/chrome/browser/discover_feed/model/discover_feed_visibility_browser_agent.h" @@ -24,6 +26,7 @@ #import "ios/chrome/browser/parcel_tracking/features.h" #import "ios/chrome/browser/shared/model/prefs/pref_names.h" #import "ios/chrome/browser/shared/public/features/features.h" +#import "ui/gfx/image/image.h" #import "url/gurl.h" @implementation HomeCustomizationMediator { @@ -32,15 +35,21 @@ // Browser agent to be notified of Discover eligibility. raw_ptr<DiscoverFeedVisibilityBrowserAgent> _discoverFeedVisibilityBrowserAgent; + // The image fetcher used to download individual background images. + raw_ptr<image_fetcher::ImageFetcher> _imageFetcher; } - (instancetype)initWithPrefService:(PrefService*)prefService - discoverFeedVisibilityBrowserAgent:(DiscoverFeedVisibilityBrowserAgent*) - discoverFeedVisibilityBrowserAgent { + discoverFeedVisibilityBrowserAgent: + (DiscoverFeedVisibilityBrowserAgent*)discoverFeedVisibilityBrowserAgent + imageFetcherService:(image_fetcher::ImageFetcherService*) + imageFetcherService { self = [super init]; if (self) { _prefService = prefService; _discoverFeedVisibilityBrowserAgent = discoverFeedVisibilityBrowserAgent; + _imageFetcher = imageFetcherService->GetImageFetcher( + image_fetcher::ImageFetcherConfig::kDiskCacheOnly); } return self; } @@ -261,4 +270,25 @@ // TODO(crbug.com/408243803): apply NTP background configuration to NTP. } +- (void)fetchBackgroundCustomizationThumbnailURLImage:(GURL)thumbnailURL + completion: + (void (^)(UIImage*))completion { + CHECK(!thumbnailURL.is_empty()); + CHECK(thumbnailURL.is_valid()); + + _imageFetcher->FetchImage( + thumbnailURL, + base::BindOnce(^(const gfx::Image& image, + const image_fetcher::RequestMetadata& metadata) { + if (!image.IsEmpty()) { + UIImage* uiImage = image.ToUIImage(); + if (completion) { + completion(uiImage); + } + } + }), + // TODO (crbug.com/417234848): Add annotation. + image_fetcher::ImageFetcherParams(NO_TRAFFIC_ANNOTATION_YET, "Test")); +} + @end
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_mediator_unittest.mm b/ios/chrome/browser/home_customization/coordinator/home_customization_mediator_unittest.mm index 1da80c5..8c966c1 100644 --- a/ios/chrome/browser/home_customization/coordinator/home_customization_mediator_unittest.mm +++ b/ios/chrome/browser/home_customization/coordinator/home_customization_mediator_unittest.mm
@@ -11,6 +11,7 @@ #import "ios/chrome/browser/discover_feed/model/discover_feed_visibility_browser_agent.h" #import "ios/chrome/browser/home_customization/ui/home_customization_main_consumer.h" #import "ios/chrome/browser/home_customization/utils/home_customization_constants.h" +#import "ios/chrome/browser/image_fetcher/model/image_fetcher_service_factory.h" #import "ios/chrome/browser/shared/model/browser/test/test_browser.h" #import "ios/chrome/browser/shared/model/prefs/browser_prefs.h" #import "ios/chrome/browser/shared/model/prefs/pref_names.h" @@ -62,10 +63,14 @@ pref_service_ = profile_->GetPrefs(); discover_feed_visibility_browser_agent_ = DiscoverFeedVisibilityBrowserAgent::FromBrowser(browser); - mediator_ = - [[HomeCustomizationMediator alloc] initWithPrefService:pref_service_ - discoverFeedVisibilityBrowserAgent: - discover_feed_visibility_browser_agent_]; + imageFetcherService_ = + ImageFetcherServiceFactory::GetForProfile(browser->GetProfile()); + + mediator_ = [[HomeCustomizationMediator alloc] + initWithPrefService:pref_service_ + discoverFeedVisibilityBrowserAgent: + discover_feed_visibility_browser_agent_ + imageFetcherService:imageFetcherService_]; } protected: @@ -75,6 +80,7 @@ discover_feed_visibility_browser_agent_; raw_ptr<PrefService> pref_service_; std::unique_ptr<TestProfileIOS> profile_; + raw_ptr<image_fetcher::ImageFetcherService> imageFetcherService_; }; // Tests that the mediator populates the main page data for its consumer based
diff --git a/ios/chrome/browser/home_customization/ui/BUILD.gn b/ios/chrome/browser/home_customization/ui/BUILD.gn index d43e099..ceb1a197 100644 --- a/ios/chrome/browser/home_customization/ui/BUILD.gn +++ b/ios/chrome/browser/home_customization/ui/BUILD.gn
@@ -62,6 +62,7 @@ "//ios/chrome/common/ui/util:dynamic_type_util", "//ios/public/provider/chrome/browser/ui_utils:ui_utils_api", "//ui/base", + "//url", ] frameworks = [ "UIKit.framework" ] }
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_background_cell.h b/ios/chrome/browser/home_customization/ui/home_customization_background_cell.h index 623cffc..bac48c9 100644 --- a/ios/chrome/browser/home_customization/ui/home_customization_background_cell.h +++ b/ios/chrome/browser/home_customization/ui/home_customization_background_cell.h
@@ -26,6 +26,9 @@ (BackgroundCustomizationConfiguration*)backgroundConfiguration logoVendor:(id<LogoVendor>)logoVendor; +// Updates the background image displayed behind the cell’s content. +- (void)updateBackgroundImage:(UIImage*)image; + @end #endif // IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_BACKGROUND_CELL_H_
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_background_cell.mm b/ios/chrome/browser/home_customization/ui/home_customization_background_cell.mm index 4cd277a..05a8848 100644 --- a/ios/chrome/browser/home_customization/ui/home_customization_background_cell.mm +++ b/ios/chrome/browser/home_customization/ui/home_customization_background_cell.mm
@@ -57,6 +57,9 @@ @implementation HomeCustomizationBackgroundCell { // Associated background configuration. BackgroundCustomizationConfiguration* _backgroundConfiguration; + + // The background image of the cell. + UIImageView* _backgroundImageView; } - (instancetype)initWithFrame:(CGRect)frame { @@ -81,6 +84,15 @@ self.innerContentView.layer.cornerRadius = kContentViewCornerRadius; self.innerContentView.layer.masksToBounds = YES; self.innerContentView.axis = UILayoutConstraintAxisVertical; + + // Adds the empty background image. + _backgroundImageView = [[UIImageView alloc] init]; + _backgroundImageView.contentMode = UIViewContentModeScaleAspectFill; + _backgroundImageView.clipsToBounds = YES; + _backgroundImageView.translatesAutoresizingMaskIntoConstraints = NO; + [self.innerContentView addSubview:_backgroundImageView]; + AddSameConstraints(_backgroundImageView, self.innerContentView); + [self.borderWrapperView addSubview:self.innerContentView]; // Constraints for positioning the border wrapper view inside the cell. @@ -126,6 +138,10 @@ ]]; } +- (void)updateBackgroundImage:(UIImage*)image { + [_backgroundImageView setImage:image]; +} + #pragma mark - Setters - (void)setSelected:(BOOL)selected {
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_mutator.h b/ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_mutator.h index ac2f2af..82ffce71 100644 --- a/ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_mutator.h +++ b/ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_mutator.h
@@ -16,6 +16,14 @@ - (void)applyBackgroundForConfiguration: (BackgroundCustomizationConfiguration*)backgroundConfiguration; +// Downloads and returns a thumbnail image from the given GURL. The image is +// returned asynchronously through the `completion` block. The method is +// intended to be used for background customization thumbnails, such as loading +// preview images for a collection view cell when it becomes visible. +- (void)fetchBackgroundCustomizationThumbnailURLImage:(GURL)thumbnailURL + completion: + (void (^)(UIImage*))completion; + @end #endif // IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_BACKGROUND_PRESET_GALLERY_PICKER_MUTATOR_H_
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_view_controller.mm b/ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_view_controller.mm index 6041ce3..f913007 100644 --- a/ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_view_controller.mm +++ b/ios/chrome/browser/home_customization/ui/home_customization_background_preset_gallery_picker_view_controller.mm
@@ -20,6 +20,7 @@ #import "ios/chrome/common/ui/util/constraints_ui_util.h" #import "ios/chrome/grit/ios_strings.h" #import "ui/base/l10n/l10n_util_mac.h" +#import "url/gurl.h" // Define constants within the namespace namespace { @@ -181,6 +182,24 @@ [self.mutator applyBackgroundForConfiguration:backgroundConfiguration]; } +- (void)collectionView:(UICollectionView*)collectionView + willDisplayCell:(HomeCustomizationBackgroundCell*)cell + forItemAtIndexPath:(NSIndexPath*)indexPath { + NSString* itemIdentifier = + [_diffableDataSource itemIdentifierForIndexPath:indexPath]; + BackgroundCustomizationConfiguration* backgroundConfiguration = + _backgroundCustomizationConfigurationMap[itemIdentifier]; + + if (!backgroundConfiguration.thumbnailURL.is_empty()) { + [self.mutator + fetchBackgroundCustomizationThumbnailURLImage:backgroundConfiguration + .thumbnailURL + completion:^(UIImage* image) { + [cell updateBackgroundImage:image]; + }]; + } +} + #pragma mark - HomeCustomizationViewControllerProtocol - (NSCollectionLayoutSection*)
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.mm b/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.mm index 56538b9e1..80bd555 100644 --- a/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.mm +++ b/ios/chrome/browser/home_customization/ui/home_customization_main_view_controller.mm
@@ -224,6 +224,34 @@ [self.mutator applyBackgroundForConfiguration:backgroundConfiguration]; } +- (void)collectionView:(UICollectionView*)collectionView + willDisplayCell:(UICollectionViewCell*)cell + forItemAtIndexPath:(NSIndexPath*)indexPath { + CustomizationSection* section = + [self.diffableDataSource snapshot].sectionIdentifiers[indexPath.section]; + NSString* itemIdentifier = + [_diffableDataSource itemIdentifierForIndexPath:indexPath]; + if (![section isEqualToString:kCustomizationSectionBackground] || + ![cell isKindOfClass:[HomeCustomizationBackgroundCell class]]) { + return; + } + + BackgroundCustomizationConfiguration* backgroundConfiguration = + _backgroundCustomizationConfigurationMap[itemIdentifier]; + + if (backgroundConfiguration && + !backgroundConfiguration.thumbnailURL.is_empty()) { + [self.mutator + fetchBackgroundCustomizationThumbnailURLImage:backgroundConfiguration + .thumbnailURL + completion:^(UIImage* image) { + [(HomeCustomizationBackgroundCell*) + cell + updateBackgroundImage:image]; + }]; + } +} + #pragma mark - HomeCustomizationMainConsumer - (void)populateToggles:(std::map<CustomizationToggleType, BOOL>)toggleMap {
diff --git a/ios/chrome/browser/home_customization/ui/home_customization_mutator.h b/ios/chrome/browser/home_customization/ui/home_customization_mutator.h index 1a80e51e..ff15a4b 100644 --- a/ios/chrome/browser/home_customization/ui/home_customization_mutator.h +++ b/ios/chrome/browser/home_customization/ui/home_customization_mutator.h
@@ -30,6 +30,13 @@ - (void)applyBackgroundForConfiguration: (BackgroundCustomizationConfiguration*)backgroundConfiguration; +// Downloads and returns a thumbnail image from the given GURL. The image is +// returned asynchronously through the `completion` block. The method is +// intended to be used for background customization thumbnails, such as loading +// preview images for a collection view cell when it becomes visible. +- (void)fetchBackgroundCustomizationThumbnailURLImage:(GURL)thumbnailURL + completion: + (void (^)(UIImage*))completion; @end #endif // IOS_CHROME_BROWSER_HOME_CUSTOMIZATION_UI_HOME_CUSTOMIZATION_MUTATOR_H_
diff --git a/ios/chrome/browser/passwords/model/BUILD.gn b/ios/chrome/browser/passwords/model/BUILD.gn index 7a50044..ac5fda2 100644 --- a/ios/chrome/browser/passwords/model/BUILD.gn +++ b/ios/chrome/browser/passwords/model/BUILD.gn
@@ -91,6 +91,7 @@ "//ios/chrome/browser/safe_browsing/model", "//ios/chrome/browser/shared/model/application_context", "//ios/chrome/browser/shared/model/browser", + "//ios/chrome/browser/shared/model/prefs:pref_names", "//ios/chrome/browser/shared/model/profile", "//ios/chrome/browser/shared/model/profile:profile_keyed_service_factory", "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client.mm b/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client.mm index fd02049..338bb04f 100644 --- a/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client.mm +++ b/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client.mm
@@ -41,6 +41,7 @@ #import "ios/chrome/browser/safe_browsing/model/chrome_password_protection_service.h" #import "ios/chrome/browser/safe_browsing/model/chrome_password_protection_service_factory.h" #import "ios/chrome/browser/shared/model/application_context/application_context.h" +#import "ios/chrome/browser/shared/model/prefs/pref_names.h" #import "ios/chrome/browser/shared/model/profile/profile_ios.h" #import "ios/chrome/browser/shared/public/commands/credential_provider_promo_commands.h" #import "ios/chrome/browser/shared/public/features/features.h" @@ -225,6 +226,8 @@ [bridge_ showCredentialProviderPromo:CredentialProviderPromoTrigger:: SuccessfulLoginUsingExistingPassword]; + GetLocalStatePrefs()->SetTime(prefs::kIosSuccessfulLoginWithExistingPassword, + base::Time::Now()); } bool IOSChromePasswordManagerClient::IsPasswordChangeOngoing() {
diff --git a/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client_unittest.mm b/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client_unittest.mm index 9acd2665..5565e3b 100644 --- a/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client_unittest.mm +++ b/ios/chrome/browser/passwords/model/ios_chrome_password_manager_client_unittest.mm
@@ -33,6 +33,7 @@ #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h" #import "ios/chrome/browser/shared/public/commands/credential_provider_promo_commands.h" #import "ios/chrome/browser/web/model/chrome_web_client.h" +#import "ios/chrome/test/ios_chrome_scoped_testing_local_state.h" #import "ios/web/public/browser_state.h" #import "ios/web/public/test/scoped_testing_web_client.h" #import "ios/web/public/test/web_state_test_util.h" @@ -123,6 +124,7 @@ web::WebState* web_state() { return web_state_.get(); } + IOSChromeScopedTestingLocalState scoped_testing_local_state_; web::ScopedTestingWebClient web_client_; web::WebTaskEnvironment task_environment_; std::unique_ptr<TestProfileIOS> profile_;
diff --git a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm index 3208858..c6e51b8d 100644 --- a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm +++ b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
@@ -516,6 +516,9 @@ registry->RegisterBooleanPref(prefs::kIosCredentialProviderPromoPolicyEnabled, true); + registry->RegisterTimePref(prefs::kIosSuccessfulLoginWithExistingPassword, + base::Time()); + registry->RegisterTimePref(prefs::kIosDefaultBrowserBlueDotPromoFirstDisplay, base::Time());
diff --git a/ios/chrome/browser/shared/model/prefs/pref_names.h b/ios/chrome/browser/shared/model/prefs/pref_names.h index dfc5b8cf..c5232aaf 100644 --- a/ios/chrome/browser/shared/model/prefs/pref_names.h +++ b/ios/chrome/browser/shared/model/prefs/pref_names.h
@@ -211,6 +211,11 @@ inline constexpr char kIosCredentialProviderPromoDisplayTime[] = "ios.credential_provider_promo_display_time"; +// The timestamp of the last time the user had a successful login with an +// existing saved password. +inline constexpr char kIosSuccessfulLoginWithExistingPassword[] = + "ios.successful_login_with_existing_password"; + // Boolean that is true when the CredentialProviderPromoEnabled policy is // enabled. inline constexpr char kIosCredentialProviderPromoPolicyEnabled[] =
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm b/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm index 6490803a9..7b9cfd00 100644 --- a/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm +++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
@@ -61,7 +61,10 @@ // The amount of time used to determine if Lens was opened recently. const base::TimeDelta kLensOpenedRecency = base::Days(30); // The amount of time used to determine if the CPE promo was displayed recently. -const base::TimeDelta kCPEPromoRecency = base::Days(30); +const base::TimeDelta kCPEPromoRecency = base::Days(7); +// The amount of time used to determine if the user successfully logged in +// recently. +const base::TimeDelta kSuccessfullLoginRecency = base::Days(30); // The amount of time used to determine if the user should be classified. const base::TimeDelta kClassifyUserRecency = base::Hours(2); @@ -591,9 +594,9 @@ if (IsRecent(promo_display_time, kCPEPromoRecency)) { return false; } - // TODO(crbug.com/417940156): Refine CPE trigger criteria to include: - // * have used autofill in the last 30 days. - return true; + base::Time login_time = + local_state_->GetTime(prefs::kIosSuccessfulLoginWithExistingPassword); + return IsRecent(login_time, kSuccessfullLoginRecency); } bool TipsNotificationClient::IsSceneLevelForegroundActive() {
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm b/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm index 6505763..f5e02b34 100644 --- a/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm +++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm
@@ -779,7 +779,7 @@ } // Tests that the client can register a CPE Promo notification, only when the -// CPE promo was displayed more than 30 days ago. +// CPE promo was displayed more than 7 days ago. TEST_F(TipsNotificationClientTest, CPERequest) { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature(kIOSExpandedTips); @@ -797,6 +797,8 @@ }); PrefService* local_state = GetApplicationContext()->GetLocalState(); local_state->SetTime(prefs::kIosCredentialProviderPromoDisplayTime, + base::Time::Now() - base::Days(6)); + local_state->SetTime(prefs::kIosSuccessfulLoginWithExistingPassword, base::Time::Now() - base::Days(29)); // A notification should not be requested yet because promo display time is @@ -809,7 +811,7 @@ // Simulate that the CPE promo was displayed more than 30 days ago. local_state->SetTime(prefs::kIosCredentialProviderPromoDisplayTime, - base::Time::Now() - base::Days(31)); + base::Time::Now() - base::Days(8)); SetupMockNotificationCenter(); StubGetPendingRequests(nil); ExpectNotificationRequest(TipsNotificationType::kCPE);
diff --git a/media/base/cdm_capability.cc b/media/base/cdm_capability.cc index 93dad433..9a899f2 100644 --- a/media/base/cdm_capability.cc +++ b/media/base/cdm_capability.cc
@@ -79,20 +79,12 @@ return "kDisconnectionError"; case CdmCapabilityQueryStatus::kMediaFoundationGetCdmFactoryFailed: return "kMediaFoundationGetCdmFactoryFailed. For the actual error code, " - "please check out " - "about://histograms/" - "#Media.EME.{KeySystem}.CdmCapabilityQueryStatus." + - std::string(kMediaFoundationGetCdmFactoryHresultUmaPostfix) + - " where KeySystem is a key " - "system."; + "please check out about://histograms/#" + + std::string(kMediaFoundationGetCdmFactoryHresultUmaPostfix); case CdmCapabilityQueryStatus::kCreateDummyMediaFoundationCdmFailed: return "kCreateDummyMediaFoundationCdmFailed. For the actual error code, " - "please check out " - "about://histograms/" - "#Media.EME.{KeySystem}.CdmCapabilityQueryStatus." + - std::string(kCreateDummyMediaFoundationCdmHresultUmaPostfix) + - " where KeySystem is a key " - "system."; + "please check out about://histograms/#" + + std::string(kCreateDummyMediaFoundationCdmHresultUmaPostfix); case CdmCapabilityQueryStatus::kUnexpectedEmptyCapability: return "kUnexpectedEmptyCapability"; case CdmCapabilityQueryStatus::kNoMediaDrmSupport:
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc index 1eb4ccd..6c5703c 100644 --- a/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc +++ b/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc
@@ -12,6 +12,7 @@ #include "base/bits.h" #include "base/memory/raw_ptr.h" #include "base/run_loop.h" +#include "base/strings/stringprintf.h" #include "base/test/gmock_callback_support.h" #include "base/test/task_environment.h" #include "build/build_config.h"
diff --git a/media/mojo/services/mojo_video_encode_accelerator_service.cc b/media/mojo/services/mojo_video_encode_accelerator_service.cc index 0449902..74ee2b3 100644 --- a/media/mojo/services/mojo_video_encode_accelerator_service.cc +++ b/media/mojo/services/mojo_video_encode_accelerator_service.cc
@@ -8,6 +8,7 @@ #include <utility> #include "base/logging.h" +#include "base/strings/stringprintf.h" #include "base/task/bind_post_task.h" #include "base/task/sequenced_task_runner.h" #include "base/trace_event/trace_event.h"
diff --git a/mojo/golden/generate.py b/mojo/golden/generate.py index 6c6361c..b19c5c9e 100755 --- a/mojo/golden/generate.py +++ b/mojo/golden/generate.py
@@ -17,7 +17,6 @@ _GENERATOR_SCRIPT = os.path.join( _SCRIPT_DIR, '../public/tools/bindings/mojom_bindings_generator.py') - def removesuffix(string, suffix): if not suffix or not string.endswith(suffix): return string @@ -49,10 +48,16 @@ ], check=True) - for lang in ['typescript', 'c++']: + for lang in ['typescript', 'c++', 'java']: + language_flags = [] + lang_tmp_output = f'{tmp_bindings_dir}/{lang}' lang_output = f'{output_dir}/{lang}' + if lang == 'java': + language_flags += ['--java_output_directory=' + lang_tmp_output] + + # Paths to module files relative to the bindings output directory. mojom_modules = (os.path.join('../../modules', removesuffix(module_filename, '-module')) @@ -62,12 +67,13 @@ '--bytecode_path', tmp_bytecode_dir, '--generators', lang, # typemap is hardcoded for now. '--typemap', f'{input_dir}/typemap.json', - *mojom_modules - ], + *language_flags, *mojom_modules], check=True) # Append '.golden' file extension to avoid presubmit checks. - for entry in os.scandir(lang_tmp_output): - os.rename(entry.path, entry.path + '.golden') + for root, dirs, files in os.walk(lang_tmp_output): + for file in files: + path = root + '/'.join(dirs) + '/' + file + os.rename(path, path + '.golden') shutil.copytree(lang_tmp_output, lang_output, dirs_exist_ok=True)
diff --git a/mojo/golden/generated/c++/results.test-mojom.cc.golden b/mojo/golden/generated/c++/results.test-mojom.cc.golden index 5470928..25faeff 100644 --- a/mojo/golden/generated/c++/results.test-mojom.cc.golden +++ b/mojo/golden/generated/c++/results.test-mojom.cc.golden
@@ -734,15 +734,18 @@ mojo::internal::MessageFragment<decltype(params->result)> result_fragment(params.message()); result_fragment.Claim(¶ms->result); + mojo::internal::Serialize<::golden::ResultInterface_SyncMethod_ResponseParam_ResultDataView>( - in_result, - result_fragment, - true); + in_result, + result_fragment, + true); - MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( - params->result.is_null(), - mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, - "null result in "); + + MOJO_INTERNAL_CHECK_SERIALIZATION( + mojo::internal::SendValidation::kDefault, + !(params->result.is_null()), + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + "null result in "); #if defined(ENABLE_IPC_FUZZER) message.set_interface_name(ResultInterface::Name_);
diff --git a/mojo/golden/generated/java/org/chromium/golden/BasicStruct.java.golden b/mojo/golden/generated/java/org/chromium/golden/BasicStruct.java.golden new file mode 100644 index 0000000..ed434ad --- /dev/null +++ b/mojo/golden/generated/java/org/chromium/golden/BasicStruct.java.golden
@@ -0,0 +1,81 @@ +// BasicStruct.java is auto generated by mojom_bindings_generator.py, do not edit + + +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is autogenerated by: +// mojo/public/tools/bindings/mojom_bindings_generator.py +// For: +// basic_struct.test-mojom +// + +package org.chromium.golden; + +import androidx.annotation.IntDef; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; + + +@NullMarked +@SuppressWarnings("NullAway") +public final class BasicStruct extends org.chromium.mojo.bindings.Struct { + + private static final int STRUCT_SIZE = 16; + private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] {new org.chromium.mojo.bindings.DataHeader(16, 0)}; + private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[0]; + public boolean myBool; + + private BasicStruct(int version) { + super(STRUCT_SIZE, version); + } + + public BasicStruct() { + this(0); + } + + public static BasicStruct deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message)); + } + + /** + * Similar to the method above, but deserializes from a |ByteBuffer| instance. + * + * @throws org.chromium.mojo.bindings.DeserializationException on deserialization failure. + */ + public static BasicStruct deserialize(java.nio.ByteBuffer data) { + return deserialize(new org.chromium.mojo.bindings.Message( + data, new java.util.ArrayList<org.chromium.mojo.system.Handle>())); + } + + @SuppressWarnings("unchecked") + public static BasicStruct decode(org.chromium.mojo.bindings.@Nullable Decoder decoder0) { + if (decoder0 == null) { + return null; + } + decoder0.increaseStackDepth(); + BasicStruct result; + try { + org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY); + final int elementsOrVersion = mainDataHeader.elementsOrVersion; + result = new BasicStruct(elementsOrVersion); + { + + result.myBool = decoder0.readBoolean(8, 0); + } + + } finally { + decoder0.decreaseStackDepth(); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder) { + org.chromium.mojo.bindings.Encoder encoder0 = encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO); + + encoder0.encode(this.myBool, 8, 0); + } +} \ No newline at end of file
diff --git a/mojo/golden/generated/java/org/chromium/golden/IFace.java.golden b/mojo/golden/generated/java/org/chromium/golden/IFace.java.golden new file mode 100644 index 0000000..60838345 --- /dev/null +++ b/mojo/golden/generated/java/org/chromium/golden/IFace.java.golden
@@ -0,0 +1,40 @@ +// IFace.java is auto generated by mojom_bindings_generator.py, do not edit + + +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is autogenerated by: +// mojo/public/tools/bindings/mojom_bindings_generator.py +// For: +// interface.test-mojom +// + +package org.chromium.golden; + +import androidx.annotation.IntDef; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; + + +public interface IFace extends org.chromium.mojo.bindings.Interface { + + + + public interface Proxy extends IFace, org.chromium.mojo.bindings.Interface.Proxy { + } + + Manager<IFace, IFace.Proxy> MANAGER = IFace_Internal.MANAGER; + + void method( +boolean param, +Method_Response callback); + + interface Method_Response { + public void call( + String result); + } + + +}
diff --git a/mojo/golden/generated/java/org/chromium/golden/IFaceWithTypemap.java.golden b/mojo/golden/generated/java/org/chromium/golden/IFaceWithTypemap.java.golden new file mode 100644 index 0000000..b11654e --- /dev/null +++ b/mojo/golden/generated/java/org/chromium/golden/IFaceWithTypemap.java.golden
@@ -0,0 +1,40 @@ +// IFaceWithTypemap.java is auto generated by mojom_bindings_generator.py, do not edit + + +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is autogenerated by: +// mojo/public/tools/bindings/mojom_bindings_generator.py +// For: +// typemap.test-mojom +// + +package org.chromium.golden; + +import androidx.annotation.IntDef; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; + + +public interface IFaceWithTypemap extends org.chromium.mojo.bindings.Interface { + + + + public interface Proxy extends IFaceWithTypemap, org.chromium.mojo.bindings.Interface.Proxy { + } + + Manager<IFaceWithTypemap, IFaceWithTypemap.Proxy> MANAGER = IFaceWithTypemap_Internal.MANAGER; + + void echo( +Typemapped param, +Echo_Response callback); + + interface Echo_Response { + public void call( + Typemapped out); + } + + +}
diff --git a/mojo/golden/generated/java/org/chromium/golden/IFaceWithTypemap_Internal.java.golden b/mojo/golden/generated/java/org/chromium/golden/IFaceWithTypemap_Internal.java.golden new file mode 100644 index 0000000..da9c345 --- /dev/null +++ b/mojo/golden/generated/java/org/chromium/golden/IFaceWithTypemap_Internal.java.golden
@@ -0,0 +1,361 @@ +// IFaceWithTypemap_Internal.java is auto generated by mojom_bindings_generator.py, do not edit + + +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is autogenerated by: +// mojo/public/tools/bindings/mojom_bindings_generator.py +// For: +// typemap.test-mojom +// + +package org.chromium.golden; + +import androidx.annotation.IntDef; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; + + +class IFaceWithTypemap_Internal { + + public static final org.chromium.mojo.bindings.Interface.Manager<IFaceWithTypemap, IFaceWithTypemap.Proxy> MANAGER = + new org.chromium.mojo.bindings.Interface.Manager<IFaceWithTypemap, IFaceWithTypemap.Proxy>() { + + @Override + public String getName() { + return "golden.IFaceWithTypemap"; + } + + @Override + public int getVersion() { + return 0; + } + + @Override + public Proxy buildProxy(org.chromium.mojo.system.Core core, + org.chromium.mojo.bindings.MessageReceiverWithResponder messageReceiver) { + return new Proxy(core, messageReceiver); + } + + @Override + public Stub buildStub(org.chromium.mojo.system.Core core, IFaceWithTypemap impl) { + return new Stub(core, impl); + } + + @Override + public IFaceWithTypemap[] buildArray(int size) { + return new IFaceWithTypemap[size]; + } + }; + + + private static final int ECHO_ORDINAL = 0; + + + static final class Proxy extends org.chromium.mojo.bindings.Interface.AbstractProxy implements IFaceWithTypemap.Proxy { + + Proxy(org.chromium.mojo.system.Core core, + org.chromium.mojo.bindings.MessageReceiverWithResponder messageReceiver) { + super(core, messageReceiver); + } + + + @Override + public void echo( +Typemapped param, +Echo_Response callback) { + + IFaceWithTypemapEchoParams _message = new IFaceWithTypemapEchoParams(); + + _message.param = param; + + + getProxyHandler().getMessageReceiver().acceptWithResponder( + _message.serializeWithHeader( + getProxyHandler().getCore(), + new org.chromium.mojo.bindings.MessageHeader( + ECHO_ORDINAL, + org.chromium.mojo.bindings.MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, + 0)), + new IFaceWithTypemapEchoResponseParamsForwardToCallback(callback)); + + } + + + } + + static final class Stub extends org.chromium.mojo.bindings.Interface.Stub<IFaceWithTypemap> { + + Stub(org.chromium.mojo.system.Core core, IFaceWithTypemap impl) { + super(core, impl); + } + + @Override + public boolean accept(org.chromium.mojo.bindings.Message message) { + try { + org.chromium.mojo.bindings.ServiceMessage messageWithHeader = + message.asServiceMessage(); + org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader(); + int flags = org.chromium.mojo.bindings.MessageHeader.NO_FLAG; + if (header.hasFlag(org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_SYNC_FLAG)) { + flags = flags | org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_SYNC_FLAG; + } + if (!header.validateHeader(flags)) { + return false; + } + switch(header.getType()) { + + case org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants.RUN_OR_CLOSE_PIPE_MESSAGE_ID: + return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRunOrClosePipe( + IFaceWithTypemap_Internal.MANAGER, messageWithHeader); + + + + + default: + return false; + } + } catch (org.chromium.mojo.bindings.DeserializationException e) { + System.err.println(e.toString()); + return false; + } + } + + @Override + public boolean acceptWithResponder(org.chromium.mojo.bindings.Message message, org.chromium.mojo.bindings.MessageReceiver receiver) { + try { + org.chromium.mojo.bindings.ServiceMessage messageWithHeader = + message.asServiceMessage(); + org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader(); + int flags = org.chromium.mojo.bindings.MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG; + if (header.hasFlag(org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_SYNC_FLAG)) { + flags = flags | org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_SYNC_FLAG; + } + if (!header.validateHeader(flags)) { + return false; + } + switch(header.getType()) { + + case org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants.RUN_MESSAGE_ID: + return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRun( + getCore(), IFaceWithTypemap_Internal.MANAGER, messageWithHeader, receiver); + + + + + + + + case ECHO_ORDINAL: { + + IFaceWithTypemapEchoParams data = + IFaceWithTypemapEchoParams.deserialize(messageWithHeader.getPayload()); + + getImpl().echo(data.param, new IFaceWithTypemapEchoResponseParamsProxyToResponder(getCore(), receiver, header.getRequestId())); + return true; + } + + + default: + return false; + } + } catch (org.chromium.mojo.bindings.DeserializationException e) { + System.err.println(e.toString()); + return false; + } + } + } + + + + static final class IFaceWithTypemapEchoParams extends org.chromium.mojo.bindings.Struct { + + private static final int STRUCT_SIZE = 16; + private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] {new org.chromium.mojo.bindings.DataHeader(16, 0)}; + private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[0]; + public Typemapped param; + + private IFaceWithTypemapEchoParams(int version) { + super(STRUCT_SIZE, version); + } + + public IFaceWithTypemapEchoParams() { + this(0); + } + + public static IFaceWithTypemapEchoParams deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message)); + } + + /** + * Similar to the method above, but deserializes from a |ByteBuffer| instance. + * + * @throws org.chromium.mojo.bindings.DeserializationException on deserialization failure. + */ + public static IFaceWithTypemapEchoParams deserialize(java.nio.ByteBuffer data) { + return deserialize(new org.chromium.mojo.bindings.Message( + data, new java.util.ArrayList<org.chromium.mojo.system.Handle>())); + } + + @SuppressWarnings("unchecked") + public static IFaceWithTypemapEchoParams decode(org.chromium.mojo.bindings.@Nullable Decoder decoder0) { + if (decoder0 == null) { + return null; + } + decoder0.increaseStackDepth(); + IFaceWithTypemapEchoParams result; + try { + org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY); + final int elementsOrVersion = mainDataHeader.elementsOrVersion; + result = new IFaceWithTypemapEchoParams(elementsOrVersion); + { + + org.chromium.mojo.bindings.Decoder decoder1 = decoder0.readPointer(8, false); + result.param = Typemapped.decode(decoder1); + } + + } finally { + decoder0.decreaseStackDepth(); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder) { + org.chromium.mojo.bindings.Encoder encoder0 = encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO); + + encoder0.encode(this.param, 8, false); + } + } + + + + + static final class IFaceWithTypemapEchoResponseParams extends org.chromium.mojo.bindings.Struct { + + private static final int STRUCT_SIZE = 16; + private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] {new org.chromium.mojo.bindings.DataHeader(16, 0)}; + private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[0]; + public Typemapped out; + + private IFaceWithTypemapEchoResponseParams(int version) { + super(STRUCT_SIZE, version); + } + + public IFaceWithTypemapEchoResponseParams() { + this(0); + } + + public static IFaceWithTypemapEchoResponseParams deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message)); + } + + /** + * Similar to the method above, but deserializes from a |ByteBuffer| instance. + * + * @throws org.chromium.mojo.bindings.DeserializationException on deserialization failure. + */ + public static IFaceWithTypemapEchoResponseParams deserialize(java.nio.ByteBuffer data) { + return deserialize(new org.chromium.mojo.bindings.Message( + data, new java.util.ArrayList<org.chromium.mojo.system.Handle>())); + } + + @SuppressWarnings("unchecked") + public static IFaceWithTypemapEchoResponseParams decode(org.chromium.mojo.bindings.@Nullable Decoder decoder0) { + if (decoder0 == null) { + return null; + } + decoder0.increaseStackDepth(); + IFaceWithTypemapEchoResponseParams result; + try { + org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY); + final int elementsOrVersion = mainDataHeader.elementsOrVersion; + result = new IFaceWithTypemapEchoResponseParams(elementsOrVersion); + { + + org.chromium.mojo.bindings.Decoder decoder1 = decoder0.readPointer(8, false); + result.out = Typemapped.decode(decoder1); + } + + } finally { + decoder0.decreaseStackDepth(); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder) { + org.chromium.mojo.bindings.Encoder encoder0 = encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO); + + encoder0.encode(this.out, 8, false); + } + } + + static class IFaceWithTypemapEchoResponseParamsForwardToCallback extends org.chromium.mojo.bindings.SideEffectFreeCloseable + implements org.chromium.mojo.bindings.MessageReceiver { + private final IFaceWithTypemap.Echo_Response mCallback; + + IFaceWithTypemapEchoResponseParamsForwardToCallback(IFaceWithTypemap.Echo_Response callback) { + this.mCallback = callback; + } + + @Override + public boolean accept(org.chromium.mojo.bindings.Message message) { + try { + org.chromium.mojo.bindings.ServiceMessage messageWithHeader = + message.asServiceMessage(); + org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader(); + if (!header.validateHeader(ECHO_ORDINAL, + org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) { + return false; + } + + IFaceWithTypemapEchoResponseParams response = IFaceWithTypemapEchoResponseParams.deserialize(messageWithHeader.getPayload()); + + mCallback.call(response.out); + return true; + } catch (org.chromium.mojo.bindings.DeserializationException e) { + return false; + } + } + } + + static class IFaceWithTypemapEchoResponseParamsProxyToResponder implements IFaceWithTypemap.Echo_Response { + + private final org.chromium.mojo.system.Core mCore; + private final org.chromium.mojo.bindings.MessageReceiver mMessageReceiver; + private final long mRequestId; + + IFaceWithTypemapEchoResponseParamsProxyToResponder( + org.chromium.mojo.system.Core core, + org.chromium.mojo.bindings.MessageReceiver messageReceiver, + long requestId) { + mCore = core; + mMessageReceiver = messageReceiver; + mRequestId = requestId; + } + + @Override + public void call(Typemapped out) { + IFaceWithTypemapEchoResponseParams _response = new IFaceWithTypemapEchoResponseParams(); + + _response.out = out; + + org.chromium.mojo.bindings.ServiceMessage _message = + _response.serializeWithHeader( + mCore, + new org.chromium.mojo.bindings.MessageHeader( + ECHO_ORDINAL, + org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_RESPONSE_FLAG, + mRequestId)); + mMessageReceiver.accept(_message); + } + } + + + +}
diff --git a/mojo/golden/generated/java/org/chromium/golden/IFace_Internal.java.golden b/mojo/golden/generated/java/org/chromium/golden/IFace_Internal.java.golden new file mode 100644 index 0000000..e744596 --- /dev/null +++ b/mojo/golden/generated/java/org/chromium/golden/IFace_Internal.java.golden
@@ -0,0 +1,359 @@ +// IFace_Internal.java is auto generated by mojom_bindings_generator.py, do not edit + + +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is autogenerated by: +// mojo/public/tools/bindings/mojom_bindings_generator.py +// For: +// interface.test-mojom +// + +package org.chromium.golden; + +import androidx.annotation.IntDef; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; + + +class IFace_Internal { + + public static final org.chromium.mojo.bindings.Interface.Manager<IFace, IFace.Proxy> MANAGER = + new org.chromium.mojo.bindings.Interface.Manager<IFace, IFace.Proxy>() { + + @Override + public String getName() { + return "golden.IFace"; + } + + @Override + public int getVersion() { + return 0; + } + + @Override + public Proxy buildProxy(org.chromium.mojo.system.Core core, + org.chromium.mojo.bindings.MessageReceiverWithResponder messageReceiver) { + return new Proxy(core, messageReceiver); + } + + @Override + public Stub buildStub(org.chromium.mojo.system.Core core, IFace impl) { + return new Stub(core, impl); + } + + @Override + public IFace[] buildArray(int size) { + return new IFace[size]; + } + }; + + + private static final int METHOD_ORDINAL = 0; + + + static final class Proxy extends org.chromium.mojo.bindings.Interface.AbstractProxy implements IFace.Proxy { + + Proxy(org.chromium.mojo.system.Core core, + org.chromium.mojo.bindings.MessageReceiverWithResponder messageReceiver) { + super(core, messageReceiver); + } + + + @Override + public void method( +boolean param, +Method_Response callback) { + + IFaceMethodParams _message = new IFaceMethodParams(); + + _message.param = param; + + + getProxyHandler().getMessageReceiver().acceptWithResponder( + _message.serializeWithHeader( + getProxyHandler().getCore(), + new org.chromium.mojo.bindings.MessageHeader( + METHOD_ORDINAL, + org.chromium.mojo.bindings.MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, + 0)), + new IFaceMethodResponseParamsForwardToCallback(callback)); + + } + + + } + + static final class Stub extends org.chromium.mojo.bindings.Interface.Stub<IFace> { + + Stub(org.chromium.mojo.system.Core core, IFace impl) { + super(core, impl); + } + + @Override + public boolean accept(org.chromium.mojo.bindings.Message message) { + try { + org.chromium.mojo.bindings.ServiceMessage messageWithHeader = + message.asServiceMessage(); + org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader(); + int flags = org.chromium.mojo.bindings.MessageHeader.NO_FLAG; + if (header.hasFlag(org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_SYNC_FLAG)) { + flags = flags | org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_SYNC_FLAG; + } + if (!header.validateHeader(flags)) { + return false; + } + switch(header.getType()) { + + case org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants.RUN_OR_CLOSE_PIPE_MESSAGE_ID: + return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRunOrClosePipe( + IFace_Internal.MANAGER, messageWithHeader); + + + + + default: + return false; + } + } catch (org.chromium.mojo.bindings.DeserializationException e) { + System.err.println(e.toString()); + return false; + } + } + + @Override + public boolean acceptWithResponder(org.chromium.mojo.bindings.Message message, org.chromium.mojo.bindings.MessageReceiver receiver) { + try { + org.chromium.mojo.bindings.ServiceMessage messageWithHeader = + message.asServiceMessage(); + org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader(); + int flags = org.chromium.mojo.bindings.MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG; + if (header.hasFlag(org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_SYNC_FLAG)) { + flags = flags | org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_SYNC_FLAG; + } + if (!header.validateHeader(flags)) { + return false; + } + switch(header.getType()) { + + case org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants.RUN_MESSAGE_ID: + return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRun( + getCore(), IFace_Internal.MANAGER, messageWithHeader, receiver); + + + + + + + + case METHOD_ORDINAL: { + + IFaceMethodParams data = + IFaceMethodParams.deserialize(messageWithHeader.getPayload()); + + getImpl().method(data.param, new IFaceMethodResponseParamsProxyToResponder(getCore(), receiver, header.getRequestId())); + return true; + } + + + default: + return false; + } + } catch (org.chromium.mojo.bindings.DeserializationException e) { + System.err.println(e.toString()); + return false; + } + } + } + + + + static final class IFaceMethodParams extends org.chromium.mojo.bindings.Struct { + + private static final int STRUCT_SIZE = 16; + private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] {new org.chromium.mojo.bindings.DataHeader(16, 0)}; + private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[0]; + public boolean param; + + private IFaceMethodParams(int version) { + super(STRUCT_SIZE, version); + } + + public IFaceMethodParams() { + this(0); + } + + public static IFaceMethodParams deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message)); + } + + /** + * Similar to the method above, but deserializes from a |ByteBuffer| instance. + * + * @throws org.chromium.mojo.bindings.DeserializationException on deserialization failure. + */ + public static IFaceMethodParams deserialize(java.nio.ByteBuffer data) { + return deserialize(new org.chromium.mojo.bindings.Message( + data, new java.util.ArrayList<org.chromium.mojo.system.Handle>())); + } + + @SuppressWarnings("unchecked") + public static IFaceMethodParams decode(org.chromium.mojo.bindings.@Nullable Decoder decoder0) { + if (decoder0 == null) { + return null; + } + decoder0.increaseStackDepth(); + IFaceMethodParams result; + try { + org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY); + final int elementsOrVersion = mainDataHeader.elementsOrVersion; + result = new IFaceMethodParams(elementsOrVersion); + { + + result.param = decoder0.readBoolean(8, 0); + } + + } finally { + decoder0.decreaseStackDepth(); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder) { + org.chromium.mojo.bindings.Encoder encoder0 = encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO); + + encoder0.encode(this.param, 8, 0); + } + } + + + + + static final class IFaceMethodResponseParams extends org.chromium.mojo.bindings.Struct { + + private static final int STRUCT_SIZE = 16; + private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] {new org.chromium.mojo.bindings.DataHeader(16, 0)}; + private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[0]; + public String result; + + private IFaceMethodResponseParams(int version) { + super(STRUCT_SIZE, version); + } + + public IFaceMethodResponseParams() { + this(0); + } + + public static IFaceMethodResponseParams deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message)); + } + + /** + * Similar to the method above, but deserializes from a |ByteBuffer| instance. + * + * @throws org.chromium.mojo.bindings.DeserializationException on deserialization failure. + */ + public static IFaceMethodResponseParams deserialize(java.nio.ByteBuffer data) { + return deserialize(new org.chromium.mojo.bindings.Message( + data, new java.util.ArrayList<org.chromium.mojo.system.Handle>())); + } + + @SuppressWarnings("unchecked") + public static IFaceMethodResponseParams decode(org.chromium.mojo.bindings.@Nullable Decoder decoder0) { + if (decoder0 == null) { + return null; + } + decoder0.increaseStackDepth(); + IFaceMethodResponseParams result; + try { + org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY); + final int elementsOrVersion = mainDataHeader.elementsOrVersion; + result = new IFaceMethodResponseParams(elementsOrVersion); + { + + result.result = decoder0.readString(8, false); + } + + } finally { + decoder0.decreaseStackDepth(); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder) { + org.chromium.mojo.bindings.Encoder encoder0 = encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO); + + encoder0.encode(this.result, 8, false); + } + } + + static class IFaceMethodResponseParamsForwardToCallback extends org.chromium.mojo.bindings.SideEffectFreeCloseable + implements org.chromium.mojo.bindings.MessageReceiver { + private final IFace.Method_Response mCallback; + + IFaceMethodResponseParamsForwardToCallback(IFace.Method_Response callback) { + this.mCallback = callback; + } + + @Override + public boolean accept(org.chromium.mojo.bindings.Message message) { + try { + org.chromium.mojo.bindings.ServiceMessage messageWithHeader = + message.asServiceMessage(); + org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader(); + if (!header.validateHeader(METHOD_ORDINAL, + org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) { + return false; + } + + IFaceMethodResponseParams response = IFaceMethodResponseParams.deserialize(messageWithHeader.getPayload()); + + mCallback.call(response.result); + return true; + } catch (org.chromium.mojo.bindings.DeserializationException e) { + return false; + } + } + } + + static class IFaceMethodResponseParamsProxyToResponder implements IFace.Method_Response { + + private final org.chromium.mojo.system.Core mCore; + private final org.chromium.mojo.bindings.MessageReceiver mMessageReceiver; + private final long mRequestId; + + IFaceMethodResponseParamsProxyToResponder( + org.chromium.mojo.system.Core core, + org.chromium.mojo.bindings.MessageReceiver messageReceiver, + long requestId) { + mCore = core; + mMessageReceiver = messageReceiver; + mRequestId = requestId; + } + + @Override + public void call(String result) { + IFaceMethodResponseParams _response = new IFaceMethodResponseParams(); + + _response.result = result; + + org.chromium.mojo.bindings.ServiceMessage _message = + _response.serializeWithHeader( + mCore, + new org.chromium.mojo.bindings.MessageHeader( + METHOD_ORDINAL, + org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_RESPONSE_FLAG, + mRequestId)); + mMessageReceiver.accept(_message); + } + } + + + +}
diff --git a/mojo/golden/generated/java/org/chromium/golden/OptionalPrimitives.java.golden b/mojo/golden/generated/java/org/chromium/golden/OptionalPrimitives.java.golden new file mode 100644 index 0000000..4a052a5e --- /dev/null +++ b/mojo/golden/generated/java/org/chromium/golden/OptionalPrimitives.java.golden
@@ -0,0 +1,146 @@ +// OptionalPrimitives.java is auto generated by mojom_bindings_generator.py, do not edit + + +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is autogenerated by: +// mojo/public/tools/bindings/mojom_bindings_generator.py +// For: +// optional_primitives.test-mojom +// + +package org.chromium.golden; + +import androidx.annotation.IntDef; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; + + +@NullMarked +@SuppressWarnings("NullAway") +public final class OptionalPrimitives extends org.chromium.mojo.bindings.Struct { + + private static final int STRUCT_SIZE = 40; + private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] {new org.chromium.mojo.bindings.DataHeader(40, 0)}; + private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[0]; + public @Nullable Integer int; + public @Nullable Integer[] uints; + public @Nullable Boolean[] boolarray; + public java.util.Map<Boolean, @Nullable Boolean> bitmap; + + private OptionalPrimitives(int version) { + super(STRUCT_SIZE, version); + } + + public OptionalPrimitives() { + this(0); + } + + public static OptionalPrimitives deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message)); + } + + /** + * Similar to the method above, but deserializes from a |ByteBuffer| instance. + * + * @throws org.chromium.mojo.bindings.DeserializationException on deserialization failure. + */ + public static OptionalPrimitives deserialize(java.nio.ByteBuffer data) { + return deserialize(new org.chromium.mojo.bindings.Message( + data, new java.util.ArrayList<org.chromium.mojo.system.Handle>())); + } + + @SuppressWarnings("unchecked") + public static OptionalPrimitives decode(org.chromium.mojo.bindings.@Nullable Decoder decoder0) { + if (decoder0 == null) { + return null; + } + decoder0.increaseStackDepth(); + OptionalPrimitives result; + try { + org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY); + final int elementsOrVersion = mainDataHeader.elementsOrVersion; + result = new OptionalPrimitives(elementsOrVersion); + { + + if (decoder0.readBoolean(8, 0)) { + result.int = new Integer(decoder0.readInt(12)); + } else { + result.int = null; + } + } + { + + result.uints = decoder0.readIntNullables(16, org.chromium.mojo.bindings.BindingsHelper.ELEMENT_NULLABLE, org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH); + } + { + + result.boolarray = decoder0.readBooleanNullables(24, org.chromium.mojo.bindings.BindingsHelper.ELEMENT_NULLABLE, org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH); + } + { + + org.chromium.mojo.bindings.Decoder decoder1 = decoder0.readPointer(32, false); + { + decoder1.readDataHeaderForMap(); + boolean[] keys0; + Boolean[] values0; + { + + keys0 = decoder1.readBooleans(org.chromium.mojo.bindings.DataHeader.HEADER_SIZE, org.chromium.mojo.bindings.BindingsHelper.NOTHING_NULLABLE, org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH); + } + { + + values0 = decoder1.readBooleanNullables(org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE, org.chromium.mojo.bindings.BindingsHelper.ELEMENT_NULLABLE, keys0.length); + } + result.bitmap = new java.util.HashMap<Boolean, Boolean>(); + for (int index0 = 0; index0 < keys0.length; ++index0) { + result.bitmap.put(keys0[index0], values0[index0]); + } + } + } + + } finally { + decoder0.decreaseStackDepth(); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder) { + org.chromium.mojo.bindings.Encoder encoder0 = encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO); + final boolean int$flag = this.int != null; + final int int$value = int$flag + ? this.int + : 0; + + encoder0.encode(int$flag, 8, 0); + + encoder0.encode(int$value, 12); + + encoder0.encode(this.uints, 16, org.chromium.mojo.bindings.BindingsHelper.ELEMENT_NULLABLE, org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH); + + encoder0.encode(this.boolarray, 24, org.chromium.mojo.bindings.BindingsHelper.ELEMENT_NULLABLE, org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH); + + if (this.bitmap == null) { + encoder0.encodeNullPointer(32, false); + } else { + org.chromium.mojo.bindings.Encoder encoder1 = encoder0.encoderForMap(32); + int size0 = this.bitmap.size(); + boolean[] keys0 = new boolean[size0]; + Boolean[] values0 = new Boolean[size0]; + int index0 = 0; + for (java.util.Map.Entry<Boolean, Boolean> entry0 : this.bitmap.entrySet()) { + keys0[index0] = entry0.getKey(); + values0[index0] = entry0.getValue(); + ++index0; + } + + encoder1.encode(keys0, org.chromium.mojo.bindings.DataHeader.HEADER_SIZE, org.chromium.mojo.bindings.BindingsHelper.NOTHING_NULLABLE, org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH); + + encoder1.encode(values0, org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE, org.chromium.mojo.bindings.BindingsHelper.ELEMENT_NULLABLE, org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH); + } + } +} \ No newline at end of file
diff --git a/mojo/golden/generated/java/org/chromium/golden/ResultInterface.java.golden b/mojo/golden/generated/java/org/chromium/golden/ResultInterface.java.golden new file mode 100644 index 0000000..ebbc7043 --- /dev/null +++ b/mojo/golden/generated/java/org/chromium/golden/ResultInterface.java.golden
@@ -0,0 +1,50 @@ +// ResultInterface.java is auto generated by mojom_bindings_generator.py, do not edit + + +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is autogenerated by: +// mojo/public/tools/bindings/mojom_bindings_generator.py +// For: +// results.test-mojom +// + +package org.chromium.golden; + +import androidx.annotation.IntDef; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; + + +public interface ResultInterface extends org.chromium.mojo.bindings.Interface { + + + + public interface Proxy extends ResultInterface, org.chromium.mojo.bindings.Interface.Proxy { + } + + Manager<ResultInterface, ResultInterface.Proxy> MANAGER = ResultInterface_Internal.MANAGER; + + void method( +boolean a, +Method_Response callback); + + interface Method_Response { + public void call( + ResultInterfaceMethodResponseParamResult result); + } + + + void syncMethod( +boolean a, +SyncMethod_Response callback); + + interface SyncMethod_Response { + public void call( + ResultInterfaceSyncMethodResponseParamResult result); + } + + +}
diff --git a/mojo/golden/generated/java/org/chromium/golden/ResultInterfaceMethodResponseParamResult.java.golden b/mojo/golden/generated/java/org/chromium/golden/ResultInterfaceMethodResponseParamResult.java.golden new file mode 100644 index 0000000..f29b2eb91 --- /dev/null +++ b/mojo/golden/generated/java/org/chromium/golden/ResultInterfaceMethodResponseParamResult.java.golden
@@ -0,0 +1,104 @@ +// ResultInterfaceMethodResponseParamResult.java is auto generated by mojom_bindings_generator.py, do not edit + + +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is autogenerated by: +// mojo/public/tools/bindings/mojom_bindings_generator.py +// For: +// results.test-mojom +// + +package org.chromium.golden; + +import androidx.annotation.IntDef; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; + + +@NullMarked +@SuppressWarnings("NullAway") +public final class ResultInterfaceMethodResponseParamResult extends org.chromium.mojo.bindings.Union { + + public static final class Tag { + public static final int Success = 0; + public static final int Failure = 1; + }; + private boolean mSuccess; + private ResultTestError mFailure; + + public void setSuccess(boolean success) { + this.mTag = Tag.Success; + this.mSuccess = success; + } + + public boolean getSuccess() { + assert this.mTag == Tag.Success; + return this.mSuccess; + } + + public void setFailure(ResultTestError failure) { + this.mTag = Tag.Failure; + this.mFailure = failure; + } + + public ResultTestError getFailure() { + assert this.mTag == Tag.Failure; + return this.mFailure; + } + + + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder0, int offset) { + encoder0.encode(org.chromium.mojo.bindings.BindingsHelper.UNION_SIZE, offset); + encoder0.encode(this.mTag, offset + 4); + switch (mTag) { + case Tag.Success: { + + encoder0.encode(this.mSuccess, offset + 8, 0); + break; + } + case Tag.Failure: { + + encoder0.encode(this.mFailure, offset + 8, false); + break; + } + default: { + break; + } + } + } + + public static ResultInterfaceMethodResponseParamResult deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message).decoderForSerializedUnion(), 0); + } + + public static final ResultInterfaceMethodResponseParamResult decode(org.chromium.mojo.bindings.Decoder decoder0, int offset) { + org.chromium.mojo.bindings.DataHeader dataHeader = decoder0.readDataHeaderForUnion(offset); + if (dataHeader.size == 0) { + return null; + } + ResultInterfaceMethodResponseParamResult result = new ResultInterfaceMethodResponseParamResult(); + switch (dataHeader.elementsOrVersion) { + case Tag.Success: { + + result.mSuccess = decoder0.readBoolean(offset + org.chromium.mojo.bindings.DataHeader.HEADER_SIZE, 0); + result.mTag = Tag.Success; + break; + } + case Tag.Failure: { + + org.chromium.mojo.bindings.Decoder decoder1 = decoder0.readPointer(offset + org.chromium.mojo.bindings.DataHeader.HEADER_SIZE, false); + result.mFailure = ResultTestError.decode(decoder1); + result.mTag = Tag.Failure; + break; + } + default: { + break; + } + } + return result; + } +} \ No newline at end of file
diff --git a/mojo/golden/generated/java/org/chromium/golden/ResultInterfaceSyncMethodResponseParamResult.java.golden b/mojo/golden/generated/java/org/chromium/golden/ResultInterfaceSyncMethodResponseParamResult.java.golden new file mode 100644 index 0000000..23918e85 --- /dev/null +++ b/mojo/golden/generated/java/org/chromium/golden/ResultInterfaceSyncMethodResponseParamResult.java.golden
@@ -0,0 +1,104 @@ +// ResultInterfaceSyncMethodResponseParamResult.java is auto generated by mojom_bindings_generator.py, do not edit + + +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is autogenerated by: +// mojo/public/tools/bindings/mojom_bindings_generator.py +// For: +// results.test-mojom +// + +package org.chromium.golden; + +import androidx.annotation.IntDef; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; + + +@NullMarked +@SuppressWarnings("NullAway") +public final class ResultInterfaceSyncMethodResponseParamResult extends org.chromium.mojo.bindings.Union { + + public static final class Tag { + public static final int Success = 0; + public static final int Failure = 1; + }; + private boolean mSuccess; + private ResultTestError mFailure; + + public void setSuccess(boolean success) { + this.mTag = Tag.Success; + this.mSuccess = success; + } + + public boolean getSuccess() { + assert this.mTag == Tag.Success; + return this.mSuccess; + } + + public void setFailure(ResultTestError failure) { + this.mTag = Tag.Failure; + this.mFailure = failure; + } + + public ResultTestError getFailure() { + assert this.mTag == Tag.Failure; + return this.mFailure; + } + + + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder0, int offset) { + encoder0.encode(org.chromium.mojo.bindings.BindingsHelper.UNION_SIZE, offset); + encoder0.encode(this.mTag, offset + 4); + switch (mTag) { + case Tag.Success: { + + encoder0.encode(this.mSuccess, offset + 8, 0); + break; + } + case Tag.Failure: { + + encoder0.encode(this.mFailure, offset + 8, false); + break; + } + default: { + break; + } + } + } + + public static ResultInterfaceSyncMethodResponseParamResult deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message).decoderForSerializedUnion(), 0); + } + + public static final ResultInterfaceSyncMethodResponseParamResult decode(org.chromium.mojo.bindings.Decoder decoder0, int offset) { + org.chromium.mojo.bindings.DataHeader dataHeader = decoder0.readDataHeaderForUnion(offset); + if (dataHeader.size == 0) { + return null; + } + ResultInterfaceSyncMethodResponseParamResult result = new ResultInterfaceSyncMethodResponseParamResult(); + switch (dataHeader.elementsOrVersion) { + case Tag.Success: { + + result.mSuccess = decoder0.readBoolean(offset + org.chromium.mojo.bindings.DataHeader.HEADER_SIZE, 0); + result.mTag = Tag.Success; + break; + } + case Tag.Failure: { + + org.chromium.mojo.bindings.Decoder decoder1 = decoder0.readPointer(offset + org.chromium.mojo.bindings.DataHeader.HEADER_SIZE, false); + result.mFailure = ResultTestError.decode(decoder1); + result.mTag = Tag.Failure; + break; + } + default: { + break; + } + } + return result; + } +} \ No newline at end of file
diff --git a/mojo/golden/generated/java/org/chromium/golden/ResultInterface_Internal.java.golden b/mojo/golden/generated/java/org/chromium/golden/ResultInterface_Internal.java.golden new file mode 100644 index 0000000..d638c82 --- /dev/null +++ b/mojo/golden/generated/java/org/chromium/golden/ResultInterface_Internal.java.golden
@@ -0,0 +1,587 @@ +// ResultInterface_Internal.java is auto generated by mojom_bindings_generator.py, do not edit + + +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is autogenerated by: +// mojo/public/tools/bindings/mojom_bindings_generator.py +// For: +// results.test-mojom +// + +package org.chromium.golden; + +import androidx.annotation.IntDef; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; + + +class ResultInterface_Internal { + + public static final org.chromium.mojo.bindings.Interface.Manager<ResultInterface, ResultInterface.Proxy> MANAGER = + new org.chromium.mojo.bindings.Interface.Manager<ResultInterface, ResultInterface.Proxy>() { + + @Override + public String getName() { + return "golden.ResultInterface"; + } + + @Override + public int getVersion() { + return 0; + } + + @Override + public Proxy buildProxy(org.chromium.mojo.system.Core core, + org.chromium.mojo.bindings.MessageReceiverWithResponder messageReceiver) { + return new Proxy(core, messageReceiver); + } + + @Override + public Stub buildStub(org.chromium.mojo.system.Core core, ResultInterface impl) { + return new Stub(core, impl); + } + + @Override + public ResultInterface[] buildArray(int size) { + return new ResultInterface[size]; + } + }; + + + private static final int METHOD_ORDINAL = 0; + + private static final int SYNC_METHOD_ORDINAL = 1; + + + static final class Proxy extends org.chromium.mojo.bindings.Interface.AbstractProxy implements ResultInterface.Proxy { + + Proxy(org.chromium.mojo.system.Core core, + org.chromium.mojo.bindings.MessageReceiverWithResponder messageReceiver) { + super(core, messageReceiver); + } + + + @Override + public void method( +boolean a, +Method_Response callback) { + + ResultInterfaceMethodParams _message = new ResultInterfaceMethodParams(); + + _message.a = a; + + + getProxyHandler().getMessageReceiver().acceptWithResponder( + _message.serializeWithHeader( + getProxyHandler().getCore(), + new org.chromium.mojo.bindings.MessageHeader( + METHOD_ORDINAL, + org.chromium.mojo.bindings.MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, + 0)), + new ResultInterfaceMethodResponseParamsForwardToCallback(callback)); + + } + + + @Override + public void syncMethod( +boolean a, +SyncMethod_Response callback) { + + ResultInterfaceSyncMethodParams _message = new ResultInterfaceSyncMethodParams(); + + _message.a = a; + + + getProxyHandler().getMessageReceiver().acceptWithResponder( + _message.serializeWithHeader( + getProxyHandler().getCore(), + new org.chromium.mojo.bindings.MessageHeader( + SYNC_METHOD_ORDINAL, + org.chromium.mojo.bindings.MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG, + 0)), + new ResultInterfaceSyncMethodResponseParamsForwardToCallback(callback)); + + } + + + } + + static final class Stub extends org.chromium.mojo.bindings.Interface.Stub<ResultInterface> { + + Stub(org.chromium.mojo.system.Core core, ResultInterface impl) { + super(core, impl); + } + + @Override + public boolean accept(org.chromium.mojo.bindings.Message message) { + try { + org.chromium.mojo.bindings.ServiceMessage messageWithHeader = + message.asServiceMessage(); + org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader(); + int flags = org.chromium.mojo.bindings.MessageHeader.NO_FLAG; + if (header.hasFlag(org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_SYNC_FLAG)) { + flags = flags | org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_SYNC_FLAG; + } + if (!header.validateHeader(flags)) { + return false; + } + switch(header.getType()) { + + case org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants.RUN_OR_CLOSE_PIPE_MESSAGE_ID: + return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRunOrClosePipe( + ResultInterface_Internal.MANAGER, messageWithHeader); + + + + + + + default: + return false; + } + } catch (org.chromium.mojo.bindings.DeserializationException e) { + System.err.println(e.toString()); + return false; + } + } + + @Override + public boolean acceptWithResponder(org.chromium.mojo.bindings.Message message, org.chromium.mojo.bindings.MessageReceiver receiver) { + try { + org.chromium.mojo.bindings.ServiceMessage messageWithHeader = + message.asServiceMessage(); + org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader(); + int flags = org.chromium.mojo.bindings.MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG; + if (header.hasFlag(org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_SYNC_FLAG)) { + flags = flags | org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_SYNC_FLAG; + } + if (!header.validateHeader(flags)) { + return false; + } + switch(header.getType()) { + + case org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants.RUN_MESSAGE_ID: + return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRun( + getCore(), ResultInterface_Internal.MANAGER, messageWithHeader, receiver); + + + + + + + + case METHOD_ORDINAL: { + + ResultInterfaceMethodParams data = + ResultInterfaceMethodParams.deserialize(messageWithHeader.getPayload()); + + getImpl().method(data.a, new ResultInterfaceMethodResponseParamsProxyToResponder(getCore(), receiver, header.getRequestId())); + return true; + } + + + + + + + + case SYNC_METHOD_ORDINAL: { + + ResultInterfaceSyncMethodParams data = + ResultInterfaceSyncMethodParams.deserialize(messageWithHeader.getPayload()); + + getImpl().syncMethod(data.a, new ResultInterfaceSyncMethodResponseParamsProxyToResponder(getCore(), receiver, header.getRequestId())); + return true; + } + + + default: + return false; + } + } catch (org.chromium.mojo.bindings.DeserializationException e) { + System.err.println(e.toString()); + return false; + } + } + } + + + + static final class ResultInterfaceMethodParams extends org.chromium.mojo.bindings.Struct { + + private static final int STRUCT_SIZE = 16; + private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] {new org.chromium.mojo.bindings.DataHeader(16, 0)}; + private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[0]; + public boolean a; + + private ResultInterfaceMethodParams(int version) { + super(STRUCT_SIZE, version); + } + + public ResultInterfaceMethodParams() { + this(0); + } + + public static ResultInterfaceMethodParams deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message)); + } + + /** + * Similar to the method above, but deserializes from a |ByteBuffer| instance. + * + * @throws org.chromium.mojo.bindings.DeserializationException on deserialization failure. + */ + public static ResultInterfaceMethodParams deserialize(java.nio.ByteBuffer data) { + return deserialize(new org.chromium.mojo.bindings.Message( + data, new java.util.ArrayList<org.chromium.mojo.system.Handle>())); + } + + @SuppressWarnings("unchecked") + public static ResultInterfaceMethodParams decode(org.chromium.mojo.bindings.@Nullable Decoder decoder0) { + if (decoder0 == null) { + return null; + } + decoder0.increaseStackDepth(); + ResultInterfaceMethodParams result; + try { + org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY); + final int elementsOrVersion = mainDataHeader.elementsOrVersion; + result = new ResultInterfaceMethodParams(elementsOrVersion); + { + + result.a = decoder0.readBoolean(8, 0); + } + + } finally { + decoder0.decreaseStackDepth(); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder) { + org.chromium.mojo.bindings.Encoder encoder0 = encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO); + + encoder0.encode(this.a, 8, 0); + } + } + + + + + static final class ResultInterfaceMethodResponseParams extends org.chromium.mojo.bindings.Struct { + + private static final int STRUCT_SIZE = 24; + private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] {new org.chromium.mojo.bindings.DataHeader(24, 0)}; + private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[0]; + public ResultInterfaceMethodResponseParamResult result; + + private ResultInterfaceMethodResponseParams(int version) { + super(STRUCT_SIZE, version); + } + + public ResultInterfaceMethodResponseParams() { + this(0); + } + + public static ResultInterfaceMethodResponseParams deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message)); + } + + /** + * Similar to the method above, but deserializes from a |ByteBuffer| instance. + * + * @throws org.chromium.mojo.bindings.DeserializationException on deserialization failure. + */ + public static ResultInterfaceMethodResponseParams deserialize(java.nio.ByteBuffer data) { + return deserialize(new org.chromium.mojo.bindings.Message( + data, new java.util.ArrayList<org.chromium.mojo.system.Handle>())); + } + + @SuppressWarnings("unchecked") + public static ResultInterfaceMethodResponseParams decode(org.chromium.mojo.bindings.@Nullable Decoder decoder0) { + if (decoder0 == null) { + return null; + } + decoder0.increaseStackDepth(); + ResultInterfaceMethodResponseParams result; + try { + org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY); + final int elementsOrVersion = mainDataHeader.elementsOrVersion; + result = new ResultInterfaceMethodResponseParams(elementsOrVersion); + { + + result.result = ResultInterfaceMethodResponseParamResult.decode(decoder0, 8); + } + + } finally { + decoder0.decreaseStackDepth(); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder) { + org.chromium.mojo.bindings.Encoder encoder0 = encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO); + + encoder0.encode(this.result, 8, false); + } + } + + static class ResultInterfaceMethodResponseParamsForwardToCallback extends org.chromium.mojo.bindings.SideEffectFreeCloseable + implements org.chromium.mojo.bindings.MessageReceiver { + private final ResultInterface.Method_Response mCallback; + + ResultInterfaceMethodResponseParamsForwardToCallback(ResultInterface.Method_Response callback) { + this.mCallback = callback; + } + + @Override + public boolean accept(org.chromium.mojo.bindings.Message message) { + try { + org.chromium.mojo.bindings.ServiceMessage messageWithHeader = + message.asServiceMessage(); + org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader(); + if (!header.validateHeader(METHOD_ORDINAL, + org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) { + return false; + } + + ResultInterfaceMethodResponseParams response = ResultInterfaceMethodResponseParams.deserialize(messageWithHeader.getPayload()); + + mCallback.call(response.result); + return true; + } catch (org.chromium.mojo.bindings.DeserializationException e) { + return false; + } + } + } + + static class ResultInterfaceMethodResponseParamsProxyToResponder implements ResultInterface.Method_Response { + + private final org.chromium.mojo.system.Core mCore; + private final org.chromium.mojo.bindings.MessageReceiver mMessageReceiver; + private final long mRequestId; + + ResultInterfaceMethodResponseParamsProxyToResponder( + org.chromium.mojo.system.Core core, + org.chromium.mojo.bindings.MessageReceiver messageReceiver, + long requestId) { + mCore = core; + mMessageReceiver = messageReceiver; + mRequestId = requestId; + } + + @Override + public void call(ResultInterfaceMethodResponseParamResult result) { + ResultInterfaceMethodResponseParams _response = new ResultInterfaceMethodResponseParams(); + + _response.result = result; + + org.chromium.mojo.bindings.ServiceMessage _message = + _response.serializeWithHeader( + mCore, + new org.chromium.mojo.bindings.MessageHeader( + METHOD_ORDINAL, + org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_RESPONSE_FLAG, + mRequestId)); + mMessageReceiver.accept(_message); + } + } + + + + + static final class ResultInterfaceSyncMethodParams extends org.chromium.mojo.bindings.Struct { + + private static final int STRUCT_SIZE = 16; + private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] {new org.chromium.mojo.bindings.DataHeader(16, 0)}; + private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[0]; + public boolean a; + + private ResultInterfaceSyncMethodParams(int version) { + super(STRUCT_SIZE, version); + } + + public ResultInterfaceSyncMethodParams() { + this(0); + } + + public static ResultInterfaceSyncMethodParams deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message)); + } + + /** + * Similar to the method above, but deserializes from a |ByteBuffer| instance. + * + * @throws org.chromium.mojo.bindings.DeserializationException on deserialization failure. + */ + public static ResultInterfaceSyncMethodParams deserialize(java.nio.ByteBuffer data) { + return deserialize(new org.chromium.mojo.bindings.Message( + data, new java.util.ArrayList<org.chromium.mojo.system.Handle>())); + } + + @SuppressWarnings("unchecked") + public static ResultInterfaceSyncMethodParams decode(org.chromium.mojo.bindings.@Nullable Decoder decoder0) { + if (decoder0 == null) { + return null; + } + decoder0.increaseStackDepth(); + ResultInterfaceSyncMethodParams result; + try { + org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY); + final int elementsOrVersion = mainDataHeader.elementsOrVersion; + result = new ResultInterfaceSyncMethodParams(elementsOrVersion); + { + + result.a = decoder0.readBoolean(8, 0); + } + + } finally { + decoder0.decreaseStackDepth(); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder) { + org.chromium.mojo.bindings.Encoder encoder0 = encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO); + + encoder0.encode(this.a, 8, 0); + } + } + + + + + static final class ResultInterfaceSyncMethodResponseParams extends org.chromium.mojo.bindings.Struct { + + private static final int STRUCT_SIZE = 24; + private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] {new org.chromium.mojo.bindings.DataHeader(24, 0)}; + private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[0]; + public ResultInterfaceSyncMethodResponseParamResult result; + + private ResultInterfaceSyncMethodResponseParams(int version) { + super(STRUCT_SIZE, version); + } + + public ResultInterfaceSyncMethodResponseParams() { + this(0); + } + + public static ResultInterfaceSyncMethodResponseParams deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message)); + } + + /** + * Similar to the method above, but deserializes from a |ByteBuffer| instance. + * + * @throws org.chromium.mojo.bindings.DeserializationException on deserialization failure. + */ + public static ResultInterfaceSyncMethodResponseParams deserialize(java.nio.ByteBuffer data) { + return deserialize(new org.chromium.mojo.bindings.Message( + data, new java.util.ArrayList<org.chromium.mojo.system.Handle>())); + } + + @SuppressWarnings("unchecked") + public static ResultInterfaceSyncMethodResponseParams decode(org.chromium.mojo.bindings.@Nullable Decoder decoder0) { + if (decoder0 == null) { + return null; + } + decoder0.increaseStackDepth(); + ResultInterfaceSyncMethodResponseParams result; + try { + org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY); + final int elementsOrVersion = mainDataHeader.elementsOrVersion; + result = new ResultInterfaceSyncMethodResponseParams(elementsOrVersion); + { + + result.result = ResultInterfaceSyncMethodResponseParamResult.decode(decoder0, 8); + } + + } finally { + decoder0.decreaseStackDepth(); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder) { + org.chromium.mojo.bindings.Encoder encoder0 = encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO); + + encoder0.encode(this.result, 8, false); + } + } + + static class ResultInterfaceSyncMethodResponseParamsForwardToCallback extends org.chromium.mojo.bindings.SideEffectFreeCloseable + implements org.chromium.mojo.bindings.MessageReceiver { + private final ResultInterface.SyncMethod_Response mCallback; + + ResultInterfaceSyncMethodResponseParamsForwardToCallback(ResultInterface.SyncMethod_Response callback) { + this.mCallback = callback; + } + + @Override + public boolean accept(org.chromium.mojo.bindings.Message message) { + try { + org.chromium.mojo.bindings.ServiceMessage messageWithHeader = + message.asServiceMessage(); + org.chromium.mojo.bindings.MessageHeader header = messageWithHeader.getHeader(); + if (!header.validateHeader(SYNC_METHOD_ORDINAL, + org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_RESPONSE_FLAG| org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_SYNC_FLAG)) { + return false; + } + + ResultInterfaceSyncMethodResponseParams response = ResultInterfaceSyncMethodResponseParams.deserialize(messageWithHeader.getPayload()); + + mCallback.call(response.result); + return true; + } catch (org.chromium.mojo.bindings.DeserializationException e) { + return false; + } + } + } + + static class ResultInterfaceSyncMethodResponseParamsProxyToResponder implements ResultInterface.SyncMethod_Response { + + private final org.chromium.mojo.system.Core mCore; + private final org.chromium.mojo.bindings.MessageReceiver mMessageReceiver; + private final long mRequestId; + + ResultInterfaceSyncMethodResponseParamsProxyToResponder( + org.chromium.mojo.system.Core core, + org.chromium.mojo.bindings.MessageReceiver messageReceiver, + long requestId) { + mCore = core; + mMessageReceiver = messageReceiver; + mRequestId = requestId; + } + + @Override + public void call(ResultInterfaceSyncMethodResponseParamResult result) { + ResultInterfaceSyncMethodResponseParams _response = new ResultInterfaceSyncMethodResponseParams(); + + _response.result = result; + + org.chromium.mojo.bindings.ServiceMessage _message = + _response.serializeWithHeader( + mCore, + new org.chromium.mojo.bindings.MessageHeader( + SYNC_METHOD_ORDINAL, + org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_RESPONSE_FLAG| org.chromium.mojo.bindings.MessageHeader.MESSAGE_IS_SYNC_FLAG, + mRequestId)); + mMessageReceiver.accept(_message); + } + } + + + +}
diff --git a/mojo/golden/generated/java/org/chromium/golden/ResultTestError.java.golden b/mojo/golden/generated/java/org/chromium/golden/ResultTestError.java.golden new file mode 100644 index 0000000..c74ab87 --- /dev/null +++ b/mojo/golden/generated/java/org/chromium/golden/ResultTestError.java.golden
@@ -0,0 +1,74 @@ +// ResultTestError.java is auto generated by mojom_bindings_generator.py, do not edit + + +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is autogenerated by: +// mojo/public/tools/bindings/mojom_bindings_generator.py +// For: +// results.test-mojom +// + +package org.chromium.golden; + +import androidx.annotation.IntDef; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; + + +@NullMarked +@SuppressWarnings("NullAway") +public final class ResultTestError extends org.chromium.mojo.bindings.Struct { + + private static final int STRUCT_SIZE = 8; + private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] {new org.chromium.mojo.bindings.DataHeader(8, 0)}; + private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[0]; + + private ResultTestError(int version) { + super(STRUCT_SIZE, version); + } + + public ResultTestError() { + this(0); + } + + public static ResultTestError deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message)); + } + + /** + * Similar to the method above, but deserializes from a |ByteBuffer| instance. + * + * @throws org.chromium.mojo.bindings.DeserializationException on deserialization failure. + */ + public static ResultTestError deserialize(java.nio.ByteBuffer data) { + return deserialize(new org.chromium.mojo.bindings.Message( + data, new java.util.ArrayList<org.chromium.mojo.system.Handle>())); + } + + @SuppressWarnings("unchecked") + public static ResultTestError decode(org.chromium.mojo.bindings.@Nullable Decoder decoder0) { + if (decoder0 == null) { + return null; + } + decoder0.increaseStackDepth(); + ResultTestError result; + try { + org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY); + final int elementsOrVersion = mainDataHeader.elementsOrVersion; + result = new ResultTestError(elementsOrVersion); + + } finally { + decoder0.decreaseStackDepth(); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder) { + encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO); + } +} \ No newline at end of file
diff --git a/mojo/golden/generated/java/org/chromium/golden/Typemapped.java.golden b/mojo/golden/generated/java/org/chromium/golden/Typemapped.java.golden new file mode 100644 index 0000000..e3b7b87 --- /dev/null +++ b/mojo/golden/generated/java/org/chromium/golden/Typemapped.java.golden
@@ -0,0 +1,105 @@ +// Typemapped.java is auto generated by mojom_bindings_generator.py, do not edit + + +// Copyright 2014 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is autogenerated by: +// mojo/public/tools/bindings/mojom_bindings_generator.py +// For: +// typemap.test-mojom +// + +package org.chromium.golden; + +import androidx.annotation.IntDef; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; + + +@NullMarked +@SuppressWarnings("NullAway") +public final class Typemapped extends org.chromium.mojo.bindings.Struct { + + private static final int STRUCT_SIZE = 24; + private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] {new org.chromium.mojo.bindings.DataHeader(24, 0)}; + private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[0]; + public byte field; + public @Nullable Integer optional; + public @Nullable Boolean[] optionalContainer; + + private Typemapped(int version) { + super(STRUCT_SIZE, version); + } + + public Typemapped() { + this(0); + } + + public static Typemapped deserialize(org.chromium.mojo.bindings.Message message) { + return decode(new org.chromium.mojo.bindings.Decoder(message)); + } + + /** + * Similar to the method above, but deserializes from a |ByteBuffer| instance. + * + * @throws org.chromium.mojo.bindings.DeserializationException on deserialization failure. + */ + public static Typemapped deserialize(java.nio.ByteBuffer data) { + return deserialize(new org.chromium.mojo.bindings.Message( + data, new java.util.ArrayList<org.chromium.mojo.system.Handle>())); + } + + @SuppressWarnings("unchecked") + public static Typemapped decode(org.chromium.mojo.bindings.@Nullable Decoder decoder0) { + if (decoder0 == null) { + return null; + } + decoder0.increaseStackDepth(); + Typemapped result; + try { + org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY); + final int elementsOrVersion = mainDataHeader.elementsOrVersion; + result = new Typemapped(elementsOrVersion); + { + + result.field = decoder0.readByte(8); + } + { + + if (decoder0.readBoolean(9, 0)) { + result.optional = new Integer(decoder0.readInt(12)); + } else { + result.optional = null; + } + } + { + + result.optionalContainer = decoder0.readBooleanNullables(16, org.chromium.mojo.bindings.BindingsHelper.ELEMENT_NULLABLE, org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH); + } + + } finally { + decoder0.decreaseStackDepth(); + } + return result; + } + + @SuppressWarnings("unchecked") + @Override + protected final void encode(org.chromium.mojo.bindings.Encoder encoder) { + org.chromium.mojo.bindings.Encoder encoder0 = encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO); + + encoder0.encode(this.field, 8); + final boolean optional$flag = this.optional != null; + final int optional$value = optional$flag + ? this.optional + : 0; + + encoder0.encode(optional$flag, 9, 0); + + encoder0.encode(optional$value, 12); + + encoder0.encode(this.optionalContainer, 16, org.chromium.mojo.bindings.BindingsHelper.ELEMENT_NULLABLE, org.chromium.mojo.bindings.BindingsHelper.UNSPECIFIED_ARRAY_LENGTH); + } +} \ No newline at end of file
diff --git a/mojo/public/tools/bindings/generators/mojom_java_generator.py b/mojo/public/tools/bindings/generators/mojom_java_generator.py index bb55b150..e89e3a4 100644 --- a/mojo/public/tools/bindings/generators/mojom_java_generator.py +++ b/mojo/public/tools/bindings/generators/mojom_java_generator.py
@@ -638,16 +638,17 @@ # srcjar in the output directory. basename = "%s.srcjar" % self.module.path zip_filename = os.path.join(self.output_dir, basename) - with TempDir() as temp_java_root: - self.output_dir = os.path.join(temp_java_root, package_path) - self._DoGenerateFiles(); - with action_helpers.atomic_output(zip_filename) as f: - zip_helpers.zip_directory(f, temp_java_root) if args.java_output_directory: # If requested, generate the java files directly into indicated directory. self.output_dir = os.path.join(args.java_output_directory, package_path) self._DoGenerateFiles(); + else: + with TempDir() as temp_java_root: + self.output_dir = os.path.join(temp_java_root, package_path) + self._DoGenerateFiles() + with action_helpers.atomic_output(zip_filename) as f: + zip_helpers.zip_directory(f, temp_java_root) def GetJinjaParameters(self): return {
diff --git a/net/BUILD.gn b/net/BUILD.gn index b580309..4e802dba 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn
@@ -395,6 +395,8 @@ "cert/test_root_certs.h", "cert/time_conversions.cc", "cert/time_conversions.h", + "cert/two_qwac.cc", + "cert/two_qwac.h", "cert/x509_cert_types.cc", "cert/x509_cert_types.h", "cert/x509_certificate.cc", @@ -2750,6 +2752,7 @@ "cert/signed_certificate_timestamp_unittest.cc", "cert/test_root_certs_unittest.cc", "cert/time_conversions_unittest.cc", + "cert/two_qwac_unittest.cc", "cert/x509_cert_types_unittest.cc", "cert/x509_certificate_unittest.cc", "cert/x509_util_unittest.cc",
diff --git a/net/cert/qwac.cc b/net/cert/qwac.cc index 38a9eb3b..189104757 100644 --- a/net/cert/qwac.cc +++ b/net/cert/qwac.cc
@@ -4,13 +4,8 @@ #include "net/cert/qwac.h" -#include "base/base64.h" -#include "base/base64url.h" #include "base/containers/contains.h" -#include "base/json/json_reader.h" #include "base/logging.h" -#include "base/strings/string_split.h" -#include "net/cert/x509_util.h" #include "third_party/boringssl/src/pki/parser.h" namespace net { @@ -209,333 +204,4 @@ return QwacEkuStatus::kHasQwacEku; } -Jades2QwacHeader::Jades2QwacHeader() = default; -Jades2QwacHeader::Jades2QwacHeader(const Jades2QwacHeader& other) = default; -Jades2QwacHeader::Jades2QwacHeader(Jades2QwacHeader&& other) = default; -Jades2QwacHeader::~Jades2QwacHeader() = default; - -namespace { - -std::optional<Jades2QwacHeader> ParseJades2QwacHeader( - std::string_view header_string) { - Jades2QwacHeader parsed_header; - // The header of a JWS is a JSON-encoded object (RFC 7515, section 4). - std::optional<base::Value> header_value = - base::JSONReader::Read(header_string, base::JSON_PARSE_RFC); - if (!header_value.has_value() || !header_value->is_dict()) { - return std::nullopt; - } - base::Value::Dict& header = header_value->GetDict(); - - // "alg" (Algorithm) parameter - RFC 7515, section 4.1.1 - std::string* alg = header.FindString("alg"); - if (!alg || *alg == "") { - return std::nullopt; - } - parsed_header.sig_alg = *alg; - header.Remove("alg"); - // TODO(crbug.com/392929826): process alg (check that it matches the alg in - // x5c). - - // "kid" (Key ID) parameter - RFC 7515, section 4.1.4 - // - // The Key ID can be of any type and is used to identify the key used for - // signing. In this profile, the key used to verify the signature will be - // found in the "x5c" parameter, so the "kid" is useless to us and is ignored. - header.Remove("kid"); - - // "cty" (Content Type) parameter - RFC 7515, section 4.1.10 - // - // ETSI TS 119 411-5 V2.1.1 requires the "cty" parameter to be - // "TLS-Certificate-Binding-v1". - std::string* cty = header.FindString("cty"); - if (!cty || *cty != "TLS-Certificate-Binding-v1") { - return std::nullopt; - } - header.Remove("cty"); - - // "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) parameter (RFC 7515, - // section 4.1.8) is the base64url-encoded SHA-256 thumbprint of the - // DER encoding of the X.509 certificate used to sign the JWS. This value is - // not needed to verify the signature (the leaf cert of the "x5c" parameter is - // the signing cert), and it is optional according to RFC 7515, so we ignore - // it. - if (header.FindString("x5t#S256")) { - header.Remove("x5t#S256"); - } - - // "x5c" (X.509 Certificate Chain) header - RFC 7515 section 4.1.6 - base::ListValue* x5c_list = header.FindList("x5c"); - if (!x5c_list) { - return std::nullopt; - } - - size_t i = 0; - bssl::UniquePtr<CRYPTO_BUFFER> leaf; - std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates; - for (const base::Value& cert_value : *x5c_list) { - // RFC 7515 section 4.1.6: - // "Each string in the array is a base64-encoded (not base64url-encoded) DER - // PKIX certificate value." - if (!cert_value.is_string()) { - return std::nullopt; - } - auto cert_bytes = base::Base64Decode(cert_value.GetString()); - if (!cert_bytes.has_value()) { - return std::nullopt; - } - auto buf = x509_util::CreateCryptoBuffer(*cert_bytes); - if (i == 0) { - leaf = std::move(buf); - } else { - intermediates.emplace_back(std::move(buf)); - } - i++; - } - parsed_header.two_qwac_cert = X509Certificate::CreateFromBuffer( - std::move(leaf), std::move(intermediates)); - if (!parsed_header.two_qwac_cert) { - return std::nullopt; - } - header.Remove("x5c"); - - // "iat" header. TS 119 182-1 section 5.1.11 defines this header parameter to - // be almost the same as RFC 7519's JWT "iat" claim. Despite TS 119 182-1 - // citing RFC 7519 as the definition for this header parameter, JWS header - // parameters and JWT claims are not the same thing. In any case, ETSI defines - // this header to be an integer representing the claimed signing time. - // - // I see no indication in TS 119 411-5 that "iat" is required to be present, - // and RFC 7519 specifies it as optional. Further, I haven't yet found an - // indication as to how one would interpret and apply this field in signature - // validation, so I'm ignoring it. - if (header.FindInt("iat")) { - header.Remove("iat"); - } - - // "exp" header. TS 119 411-5 Annex B defines this as the expiry date of the - // binding, and like TS 119 182-1 for the "iat" header, incorrectly cites RFC - // 7519's claim definition of the field (section 4.1.4). Unlike the ETSI - // specification for "iat" that restricts its NumericDate type to an integer, - // we only have the RFC 7519 definition of "exp" to use, which defines - // NumericDate as a JSON numeric value. RFC 7159 allows JSON numeric values to - // contain a fraction part. - // - // Like the "iat" header, TS 119 411-5 does not require the presence of "exp", - // RFC 7519 specifies it as optional, and there is no indication in any ETSI - // spec on how this field would affect signature validation, so it is ignored. - if (header.FindDouble("exp")) { - header.Remove("exp"); - } - - // "sigD" header - ETSI TS 119 182-1 section 5.2.8, with additional - // requirements specified in ETSI TS 119 411-5 Annex B. This parameter is a - // JSON object and is required to be present. - base::DictValue* sig_d = header.FindDict("sigD"); - if (!sig_d) { - return std::nullopt; - } - - // The sigD header must have a "mId" (mechanism ID) of - // "http://uri.etsi.org/19182/ObjectIdByURIHash". (ETSI TS 119 411-5 Annex B.) - std::string* m_id = sig_d->FindString("mId"); - if (!m_id || *m_id != "http://uri.etsi.org/19182/ObjectIdByURIHash") { - return std::nullopt; - } - sig_d->Remove("mId"); - - // The sigD header must have a "pars" member, which is a list of strings. We - // don't care about the contents of this list, but its size must match that of - // "hashV". (ETSI 119 182-1 clause 5.2.8.) - const base::ListValue* pars = sig_d->FindList("pars"); - if (!pars) { - return std::nullopt; - } - size_t bound_cert_count = pars->size(); - for (const base::Value& par : *pars) { - if (!par.is_string()) { - return std::nullopt; - } - } - sig_d->Remove("pars"); - - // The sigD header must have a "hashM" member (TS 119 182-1 - // section 5.2.8.3.3), which is a string identifying the hashing algorithm - // used for the "hashV" member. ETSI TS 119 411-5 only requires that S256, - // S384, and S512 be supported. - std::string* hash_m = sig_d->FindString("hashM"); - if (!hash_m) { - return std::nullopt; - } - if (*hash_m == "S256") { - parsed_header.hash_alg = crypto::hash::kSha256; - } else if (*hash_m == "S384") { - // TODO(crbug.com/392929826): add SHA-384 to crypto/hash.h. - return std::nullopt; - } else if (*hash_m == "S512") { - parsed_header.hash_alg = crypto::hash::kSha512; - } else { - // Unsupported hashing algorithm. - return std::nullopt; - } - sig_d->Remove("hashM"); - - // The sigD header must have a "hashV" member, which is a list of - // base64url-encoded digest values of the base64url-encoded data objects. - // (ETSI TS 119 182-1 clause 5.2.8. The "b64" header parameter is absent, so - // the digest is computed over the base64url-encoded data object instead of - // computed directly over the data object.) - const base::ListValue* hash_v = sig_d->FindList("hashV"); - if (!hash_v) { - return std::nullopt; - } - if (hash_v->size() != bound_cert_count) { - return std::nullopt; - } - parsed_header.bound_cert_hashes.reserve(bound_cert_count); - for (const base::Value& hash_value : *hash_v) { - const std::string* hash_b64url = hash_value.GetIfString(); - if (!hash_b64url) { - return std::nullopt; - } - // ETSI TS 119 182-1 fails to specify the definition of "base64url-encoded". - // Given that other uses of base64url encoding come from the JWS spec, and - // JWS disallows padding in its base64url encoding, we disallow it here as - // well. - auto hash = base::Base64UrlDecode( - *hash_b64url, base::Base64UrlDecodePolicy::DISALLOW_PADDING); - if (!hash.has_value()) { - return std::nullopt; - } - parsed_header.bound_cert_hashes.emplace_back(std::move(*hash)); - } - sig_d->Remove("hashV"); - - // Given the mId used, the sigD header may have a "ctys" member (TS 119 182-1 - // clause 5.2.8.3.3), with semantics and syntax as specified in clause - // 5.2.8.1. Clause 5.2.8.1 defines the "ctys" member's syntax to be an array - // of strings. This array has the same length as the "pars" (and "hashV") - // array, and each element is the content type (RFC 7515 section 4.1.10) of - // the data object referred to by the value in "pars" at the same index. - // RFC 7515 specifies that the content type parameter is ignored by JWS - // implementations and processing of it is performed by the JWS application. - // Since neither ETSI TS 119 182-1 nor TS 119 411-5 provide guidance on the - // content type used for the individual data objects, this implementation has - // no opinion on the stated content types. - const base::ListValue* ctys = sig_d->FindList("ctys"); - if (ctys) { - if (ctys->size() != bound_cert_count) { - return std::nullopt; - } - for (const base::Value& cty_value : *ctys) { - if (!cty_value.is_string()) { - return std::nullopt; - } - } - } else if (sig_d->contains("ctys")) { - // check that there isn't a "ctys" of the wrong type - return std::nullopt; - } - sig_d->Remove("ctys"); - - // sigD has no other members than the aforementioned "mId", "pars", "hashM", - // "hashV", and "ctys". (ETSI TS 119 182-1 clause 5.2.8.) - if (!sig_d->empty()) { - return std::nullopt; - } - header.Remove("sigD"); - - // The header must not contain fields other than "alg", "kid", "cty", - // "x5t#S256", "x5c", "iat", "exp", or "sigD", as required by ETSI TS 119 - // 411-5 V2.1.1, Annex B. - // - // ETSI TS 119 182-1 V1.2.1 section 5.1.9 specifies that if the "sigD" header - // parameter is present, then the "crit" header parameter shall also be - // present with "sigD" as one of its array elements. This is in conflict with - // the requirement in 119 411-5 V2.1.1 Annex B. To resolve this conflict, this - // implementation will allow the presence of "crit", but if it is present, it - // must be an array containing exactly "sigD". - const auto* crit_value = header.Find("crit"); - if (crit_value) { - if (!crit_value->is_list()) { - return std::nullopt; - } - const auto& crit_list = crit_value->GetList(); - if (crit_list.size() != 1 || !crit_list.contains("sigD")) { - return std::nullopt; - } - } - header.Remove("crit"); - - if (!header.empty()) { - return std::nullopt; - } - - return parsed_header; -} - -} // namespace - -TwoQwacCertBinding::TwoQwacCertBinding(Jades2QwacHeader header, - std::string header_string, - std::vector<uint8_t> signature) - : header(header), header_string(header_string), signature(signature) {} - -TwoQwacCertBinding::TwoQwacCertBinding(const TwoQwacCertBinding& other) = - default; -TwoQwacCertBinding::TwoQwacCertBinding(TwoQwacCertBinding&& other) = default; -TwoQwacCertBinding::~TwoQwacCertBinding() = default; - -std::optional<TwoQwacCertBinding> TwoQwacCertBinding::Parse( - std::string_view jws) { - // ETSI TS 119 411-5 V2.1.1 Annex B: The JAdES signatures shall be serialized - // using JWS Compact Serialization as specified in IETF RFC 7515. - // - // The JWS Compact Serialization format consists of 3 components separated by - // a dot (".") (RFC 7515, section 7.1). - std::vector<std::string_view> jws_components = base::SplitStringPiece( - jws, ".", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); - if (jws_components.size() != 3) { - // Reject a JWS that does not consist of 3 components. - return std::nullopt; - } - std::string_view header_b64 = jws_components[0]; - std::string_view payload_b64 = jws_components[1]; - std::string_view signature_b64 = jws_components[2]; - - // The 3 components of a JWS are the header, the payload, and the signature. - // The components are base64url encoded (RFC 7515, section 7.1) and the base64 - // encoding is without any padding "=" characters (Ibid., section 2). - std::string header_string; - if (!base::Base64UrlDecode(header_b64, - base::Base64UrlDecodePolicy::DISALLOW_PADDING, - &header_string)) { - return std::nullopt; - } - std::optional<std::vector<uint8_t>> signature = base::Base64UrlDecode( - signature_b64, base::Base64UrlDecodePolicy::DISALLOW_PADDING); - if (!signature.has_value()) { - return std::nullopt; - } - - // Parse the JWS/JAdES header. - auto header = ParseJades2QwacHeader(header_string); - if (!header.has_value()) { - return std::nullopt; - } - - // ETSI TS 119 411-5 V2.1.1 Annex B specifies a "sigD" header parameter. This - // header parameter is defined in ETSI TS 119 182-1 V1.2.1, section 5.2.8, - // which states "The sigD header parameter shall not appear in JAdES - // signatures whose JWS Payload is attached". Thus, it can be inferred that - // the JWS Payload is detached. A detached payload for a JWS means that the - // encoded payload is empty (RFC 7515, Appendix F). - if (!payload_b64.empty()) { - return std::nullopt; - } - - TwoQwacCertBinding cert_binding(*header, header_string, *signature); - return cert_binding; -} - } // namespace net
diff --git a/net/cert/qwac.h b/net/cert/qwac.h index a0ab62b..2a13014 100644 --- a/net/cert/qwac.h +++ b/net/cert/qwac.h
@@ -9,14 +9,10 @@ #include <optional> #include <set> -#include <string_view> #include <utility> #include <vector> -#include "base/memory/raw_ptr.h" -#include "crypto/hash.h" #include "net/base/net_export.h" -#include "net/cert/x509_certificate.h" #include "third_party/boringssl/src/pki/input.h" #include "third_party/boringssl/src/pki/parsed_certificate.h" @@ -158,68 +154,6 @@ NET_EXPORT_PRIVATE QwacEkuStatus Has2QwacEku(const bssl::ParsedCertificate* cert); -// Contains fields from a JAdES (ETSI TS 119 182-1) signature header needed for -// verifying 2-QWAC TLS certificate bindings. While JAdES is a profile of JWS -// (RFC 7515), this is not general-purpose JWS or JWT code. It is also not -// general-purpose JAdES code, as only fields needed for 2-QWAC TLS certificate -// bindings are present here. -struct NET_EXPORT_PRIVATE Jades2QwacHeader { - Jades2QwacHeader(); - Jades2QwacHeader(const Jades2QwacHeader& other); - Jades2QwacHeader(Jades2QwacHeader&& other); - ~Jades2QwacHeader(); - - // The signature algorithm used to sign the JWS, as provided by the "alg" JWS - // Header Parameter (RFC 7515, section 4.1.1). Valid values for this field can - // be found in the JSON Web Signature and Encryption Algorithms IANA registry - // (https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms). - // The consumer of this struct must check that the algorithm provided in this - // field matches the signature algorithm of the leaf cert in |two_qwac_cert|. - std::string sig_alg; - - // The certificate chain with a leaf cert that is a 2-QWAC. This certificate - // chain is used to sign the JWS, which binds the 2-QWAC to a set of TLS - // serverAuth certificates. - scoped_refptr<net::X509Certificate> two_qwac_cert; - - // The hash algorithm used to hash the bound certificates. - crypto::hash::HashKind hash_alg; - - // The hashes of the bound certificates (base64url-encoded), hashed using - // |hash_alg|. Note: this is Digest(base64url(cert)), because that's what the - // JAdES and 2-QWAC specs require (not that it makes any sense to do that). - std::vector<std::vector<uint8_t>> bound_cert_hashes; -}; - -// A TwoQwacCertBinding represents a JAdES Signature (ETSI TS 119 182-1, -// clause 3.1) used for 2-QWACs (ETSI TS 119 411-5, clause 6.2.2). It comes from -// a TLS Certificate Binding (ETSI TS 119 411-5 annex B). Note that a JAdES -// Signature (which is also a JWS, a.k.a. JSON Web Signature) consists of a -// header and a cryptographic signature, not just a signature. -struct NET_EXPORT_PRIVATE TwoQwacCertBinding { - TwoQwacCertBinding(Jades2QwacHeader header, - std::string header_string, - std::vector<uint8_t> signature); - TwoQwacCertBinding(const TwoQwacCertBinding& other); - TwoQwacCertBinding(TwoQwacCertBinding&& other); - ~TwoQwacCertBinding(); - - // Parses a TLS Certificate Binding structure that contains a 2-QWAC - // certificate chain. - // TODO(crbug.com/392929826): Add a fuzz test for this function. - static std::optional<TwoQwacCertBinding> Parse(std::string_view jws); - - // The parsed JWS Header from the certificate binding structure. - Jades2QwacHeader header; - - // The unparsed JWS Header, needed for verifying the signature. - std::string header_string; - - // The JWS Signature (RFC 7515 section 2)/JAdES Signature Value (ETSI TS 119 - // 182-1 clause 3.1) from the certificate binding structure. - std::vector<uint8_t> signature; -}; - } // namespace net #endif // NET_CERT_QWAC_H_
diff --git a/net/cert/qwac_fuzztest.cc b/net/cert/qwac_fuzztest.cc index 3905260..3c7e959 100644 --- a/net/cert/qwac_fuzztest.cc +++ b/net/cert/qwac_fuzztest.cc
@@ -9,6 +9,7 @@ #include "base/containers/span.h" #include "base/containers/to_vector.h" #include "net/cert/qwac.h" +#include "net/cert/two_qwac.h" #include "third_party/fuzztest/src/fuzztest/fuzztest.h" #include "third_party/googletest/src/googletest/include/gtest/gtest.h"
diff --git a/net/cert/qwac_unittest.cc b/net/cert/qwac_unittest.cc index 8fa2087..b3d14c3 100644 --- a/net/cert/qwac_unittest.cc +++ b/net/cert/qwac_unittest.cc
@@ -6,11 +6,6 @@ #include <stdint.h> -#include "base/base64.h" -#include "base/base64url.h" -#include "base/functional/callback.h" -#include "base/json/json_string_value_serializer.h" -#include "base/values.h" #include "net/test/cert_builder.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -326,433 +321,5 @@ } } -// Builds a header that has the minimal required set of parameters -base::DictValue MinimalBindingHeader() { - auto [leaf, root] = net::CertBuilder::CreateSimpleChain2(); - base::Value::Dict header = - base::Value::Dict() - .Set("alg", "test alg") - .Set("cty", "TLS-Certificate-Binding-v1") - .Set("x5c", base::Value::List() - // These are base64 encoded, not base64url encoded - .Append(base::Base64Encode(leaf->GetDER())) - .Append(base::Base64Encode(root->GetDER()))) - .Set("sigD", - base::Value::Dict() - .Set("mId", "http://uri.etsi.org/19182/ObjectIdByURIHash") - .Set("pars", base::Value::List().Append("").Append("")) - .Set("hashM", "S256") - // These are hashes of the certs that this - // TlsCertificateBinding binds, not hashes of the certs in - // the x5c cert chain. - .Set("hashV", base::Value::List() - .Append("fakehash1A") - .Append("fakehash2A"))); - return header; -} - -// Creates a TLS Certificate Binding from the provided header. This test helper -// leaves the signature empty. -std::string CreateTwoQwacCertBinding(const base::DictValue& header) { - std::string header_string; - EXPECT_TRUE(JSONStringValueSerializer(&header_string).Serialize(header)); - // Create the JWS from the header. - std::string jws; - base::Base64UrlEncode(header_string, - base::Base64UrlEncodePolicy::OMIT_PADDING, &jws); - // Add empty payload and signature to JWS. - jws += ".."; - return jws; -} - -TEST(ParseTlsCertificateBinding, MinimalValidBinding) { - // TODO(crbug.com/392929826): Once we start validating signatures, these - // tests will probably need to be updated to have real algorithms, - // certificates, and signatures. (This is assuming that some basic checks are - // added to the parsing code, e.g. that we can parse certs into a - // net::X509Certificate and check that the "alg" matches the leaf cert.) - - // Build a header that has the minimally required set of parameters - base::DictValue header = MinimalBindingHeader(); - std::string jws = CreateTwoQwacCertBinding(header); - auto cert_binding = TwoQwacCertBinding::Parse(jws); - ASSERT_TRUE(cert_binding.has_value()); -} - -TEST(ParseTlsCertificateBinding, MaximalValidBinding) { - // Create a header that has all allowed fields - base::DictValue header = MinimalBindingHeader(); - header.Set("kid", base::Value::Dict() - .Set("random key", "random value") - .Set("kids can have", "whatever they want")); - header.Set("x5t#S256", "base64urlhashA"); - header.Set("iat", 12345); - header.Set("exp", 67.89); - header.Set("crit", base::ListValue().Append("sigD")); - - header.FindDict("sigD")->Set( - "ctys", - base::Value::List().Append("content-type1").Append("content-type2")); - - std::string jws = CreateTwoQwacCertBinding(header); - auto cert_binding = TwoQwacCertBinding::Parse(jws); - ASSERT_TRUE(cert_binding.has_value()); -} - -// Test failure when the JWS header isn't a JSON object. -TEST(ParseTlsCertificateBinding, JwsHeaderNotObject) { - std::string header = "[]"; - std::string jws; - base::Base64UrlEncode(header, base::Base64UrlEncodePolicy::OMIT_PADDING, - &jws); - jws += ".."; - EXPECT_FALSE(TwoQwacCertBinding::Parse(jws).has_value()); -} - -// Test failure when the JWS header isn't JSON. -TEST(ParseTlsCertificateBinding, JwsHeaderNotJson) { - std::string header = "AAA"; - std::string jws; - base::Base64UrlEncode(header, base::Base64UrlEncodePolicy::OMIT_PADDING, - &jws); - jws += ".."; - EXPECT_FALSE(TwoQwacCertBinding::Parse(jws).has_value()); -} - -// Test failure when the JWS header isn't valid base64url. -TEST(ParseTlsCertificateBinding, JwsHeaderNotBase64) { - // the header is encoded as "A", which is too short to be base64url. - std::string jws = "A.."; - EXPECT_FALSE(TwoQwacCertBinding::Parse(jws).has_value()); -} - -// Test failure when the JWS payload is non-empty. -TEST(ParseTlsCertificateBinding, JwsPayloadNonEmpty) { - std::string header_string; - EXPECT_TRUE(JSONStringValueSerializer(&header_string) - .Serialize(MinimalBindingHeader())); - std::string header_b64; - base::Base64UrlEncode(header_string, - base::Base64UrlEncodePolicy::OMIT_PADDING, &header_b64); - // Make a JWS consisting of a valid header, a payload (base64url-encoded as - // "AAAA") and an empty signature. - std::string jws = header_b64 + ".AAAA."; - EXPECT_FALSE(TwoQwacCertBinding::Parse(jws).has_value()); -} - -// Test failure when the JWS signature is not valid base64url. -TEST(ParseTlsCertificateBinding, JwsSignatureNotBase64) { - std::string header_string; - EXPECT_TRUE(JSONStringValueSerializer(&header_string) - .Serialize(MinimalBindingHeader())); - std::string header_b64; - base::Base64UrlEncode(header_string, - base::Base64UrlEncodePolicy::OMIT_PADDING, &header_b64); - std::string jws = header_b64 + "..A"; - EXPECT_FALSE(TwoQwacCertBinding::Parse(jws).has_value()); -} - -// Test failure when the JWS consists of 2 components instead of 3. -TEST(ParseTlsCertificateBinding, JwsHas2Components) { - std::string header_string; - EXPECT_TRUE(JSONStringValueSerializer(&header_string) - .Serialize(MinimalBindingHeader())); - std::string header_b64; - base::Base64UrlEncode(header_string, - base::Base64UrlEncodePolicy::OMIT_PADDING, &header_b64); - std::string jws = header_b64 + ".AAAA"; - EXPECT_FALSE(TwoQwacCertBinding::Parse(jws).has_value()); -} - -// Test failure when the JWS consists of 4 components instead of 3. -TEST(ParseTlsCertificateBinding, JwsHas4Components) { - std::string header_string; - EXPECT_TRUE(JSONStringValueSerializer(&header_string) - .Serialize(MinimalBindingHeader())); - std::string header_b64; - base::Base64UrlEncode(header_string, - base::Base64UrlEncodePolicy::OMIT_PADDING, &header_b64); - std::string jws = header_b64 + "..AAAA.AAAA"; - EXPECT_FALSE(TwoQwacCertBinding::Parse(jws).has_value()); -} - -TEST(ParseTlsCertificateBinding, InvalidFields) { - const struct { - std::string header_key; - base::Value value; - } kTests[] = { - { - // "alg" expects a string - "alg", - base::Value(1), - }, - { - // "alg" expects a non-empty string - "alg", - base::Value(""), - }, - { - // "cty" expects a string - "cty", - base::Value(1), - }, - { - // "cty" expects a specific value for its string - "cty", - base::Value("TLS-Certificate-Binding-v2"), - }, - { - // "x5t#S256" expects a string - "x5t#S256", - base::Value(1), - }, - { - // "x5c" expects a list - "x5c", - base::Value("wrong type"), - }, - { - // "x5c" expects strings in its list - "x5c", - base::Value(base::ListValue().Append(1)), - }, - { - // "x5c" expects base64 strings in its list. Test with a base64url - // (but not regular base64) string. - "x5c", - base::Value(base::ListValue().Append("M-_A")), - }, - { - // "x5c" expects the base64 strings in its list to be valid X.509 - // certificates. This string is valid base64, but is a (very) - // truncated X.509 certificate. - "x5c", - base::Value(base::ListValue().Append("MIID")), - }, - { - // "iat" expects an int (when used for 2-QWACs). "iat" more generally - // (according to RFC 7519) can be a double, but we don't allow that, - // so explicitly check that doubles are rejected. - "iat", - base::Value(1.0), - }, - { - // "exp" expects a numeric value - "exp", - base::Value("wrong type"), - }, - { - // "crit", if present, can only contain "sigD" - "crit", - base::Value(base::ListValue().Append("sigD").Append("x5c")), - }, - { - // "crit" expects a list - "crit", - base::Value("wrong type"), - }, - { - // "sigD" expects an object - "sigD", - base::Value(base::ListValue()), - }, - { - // The 2-QWAC TLS Certificate Binding JAdES profile only allows - // specific fields in the JWS header, and "x5u" is not one of them. - "x5u", - base::Value("X.509 URL"), - }, - }; - - for (const auto& test : kTests) { - SCOPED_TRACE(test.header_key); - base::DictValue header = MinimalBindingHeader(); - header.Set(test.header_key, test.value.Clone()); - std::string jws = CreateTwoQwacCertBinding(header); - auto cert_binding = TwoQwacCertBinding::Parse(jws); - ASSERT_FALSE(cert_binding.has_value()); - } -} - -TEST(ParseTlsCertificateBinding, SigDHeaderParam) { - const struct { - std::string name; - base::RepeatingCallback<void(base::DictValue*)> header_func; - bool valid; - } kTests[] = { - { - "wrong mId", - base::BindRepeating([](base::DictValue* sig_d) { - sig_d->Set("mId", "http://uri.etsi.org/19182/ObjectIdByURI"); - }), - false, - }, - { - "wrong mId type", - base::BindRepeating( - [](base::DictValue* sig_d) { sig_d->Set("mId", 1); }), - false, - }, - { - "wrong pars type", - base::BindRepeating( - [](base::DictValue* sig_d) { sig_d->Set("pars", 1); }), - false, - }, - { - // This repeats the default value used in MinimalBindingHeader() in - // other tests, but is here for completeness. - "SHA-256 supported", - base::BindRepeating( - [](base::DictValue* sig_d) { sig_d->Set("hashM", "S256"); }), - true, - }, - { - // TODO(crbug.com/392929826): Support SHA-384. - "SHA-384 not supported", - base::BindRepeating( - [](base::DictValue* sig_d) { sig_d->Set("hashM", "S384"); }), - false, - }, - { - "SHA-512 supported", - base::BindRepeating( - [](base::DictValue* sig_d) { sig_d->Set("hashM", "S512"); }), - true, - }, - { - "Other hashM values not supported", - base::BindRepeating( - [](base::DictValue* sig_d) { sig_d->Set("hashM", "SHA1"); }), - false, - }, - { - "wrong hashM type", - base::BindRepeating( - [](base::DictValue* sig_d) { sig_d->Set("hashM", 1); }), - false, - }, - { - "wrong type in pars list", - base::BindRepeating([](base::DictValue* sig_d) { - // "pars" and "hashV" must have the same length. - sig_d->Set("pars", base::ListValue().Append(1)); - sig_d->Set("hashV", base::ListValue().Append("fakehash")); - }), - false, - }, - { - "disallowed base64 padding in hashV", - base::BindRepeating([](base::DictValue* sig_d) { - // "ctys" must have the same length as "pars" and "hashV". - sig_d->Set("pars", base::ListValue().Append("URL")); - // hashV list elements are base64url encoded with no padding - sig_d->Set("hashV", base::ListValue().Append("fakehashAA==")); - }), - false, - }, - { - "bad base64url encoding in hashV", - base::BindRepeating([](base::DictValue* sig_d) { - // "ctys" must have the same length as "pars" and "hashV". - sig_d->Set("pars", base::ListValue().Append("URL")); - // a base64url input (with no padding) is malformed if its length - // mod 4 is 1. - sig_d->Set("hashV", base::ListValue().Append("fakehash1")); - }), - false, - }, - { - "wrong type in hashV list", - base::BindRepeating([](base::DictValue* sig_d) { - // "ctys" must have the same length as "pars" and "hashV". - sig_d->Set("pars", base::ListValue().Append("URL")); - sig_d->Set("hashV", base::ListValue().Append(1)); - }), - false, - }, - { - "wrong hashV type", - base::BindRepeating( - [](base::DictValue* sig_d) { sig_d->Set("hashV", 1); }), - false, - }, - { - "correct ctys type", - base::BindRepeating([](base::DictValue* sig_d) { - // "ctys" must have the same length as "pars" and "hashV". - sig_d->Set("pars", base::ListValue().Append("URL")); - sig_d->Set("hashV", base::ListValue().Append("fakehash")); - sig_d->Set("ctys", base::ListValue().Append("content type")); - }), - true, - }, - { - "wrong ctys type", - base::BindRepeating( - [](base::DictValue* sig_d) { sig_d->Set("ctys", "wrong type"); }), - false, - }, - { - "wrong type inside ctys list", - base::BindRepeating([](base::DictValue* sig_d) { - // "ctys" must have the same length as "pars" and "hashV". - sig_d->Set("pars", base::ListValue().Append("URL")); - sig_d->Set("hashV", base::ListValue().Append("fakehash")); - sig_d->Set("ctys", base::ListValue().Append(1)); - }), - false, - }, - { - "pars length mismatch", - base::BindRepeating([](base::DictValue* sig_d) { - // "ctys" must have the same length as "pars" and "hashV". - sig_d->Set("pars", base::ListValue().Append("URL").Append("URL 2")); - sig_d->Set("hashV", base::ListValue().Append("fakehash")); - sig_d->Set("ctys", base::ListValue().Append("content type")); - }), - false, - }, - { - "hashV length mismatch", - base::BindRepeating([](base::DictValue* sig_d) { - // "ctys" must have the same length as "pars" and "hashV". - sig_d->Set("pars", base::ListValue().Append("URL")); - sig_d->Set("hashV", - base::ListValue().Append("fakehash").Append("hashfake")); - sig_d->Set("ctys", base::ListValue().Append("content type")); - }), - false, - }, - { - "ctys length mismatch", - base::BindRepeating([](base::DictValue* sig_d) { - // "ctys" must have the same length as "pars" and "hashV". - sig_d->Set("pars", base::ListValue().Append("URL")); - sig_d->Set("hashV", base::ListValue().Append("fakehash")); - sig_d->Set("ctys", base::ListValue() - .Append("content type") - .Append("content type")); - }), - false, - }, - { - "unknown member in sigD", - base::BindRepeating( - [](base::DictValue* sig_d) { sig_d->Set("spURI", "URL"); }), - false, - }, - }; - - for (const auto& test : kTests) { - SCOPED_TRACE(test.name); - base::DictValue header = MinimalBindingHeader(); - test.header_func.Run(header.FindDict("sigD")); - std::string jws = CreateTwoQwacCertBinding(header); - auto cert_binding = TwoQwacCertBinding::Parse(jws); - EXPECT_EQ(cert_binding.has_value(), test.valid); - } -} - } // namespace } // namespace net
diff --git a/net/cert/two_qwac.cc b/net/cert/two_qwac.cc new file mode 100644 index 0000000..ed30cc8 --- /dev/null +++ b/net/cert/two_qwac.cc
@@ -0,0 +1,344 @@ +// 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 "net/cert/two_qwac.h" + +#include "base/base64.h" +#include "base/base64url.h" +#include "base/json/json_reader.h" +#include "base/strings/string_split.h" +#include "net/cert/x509_util.h" + +namespace net { + +Jades2QwacHeader::Jades2QwacHeader() = default; +Jades2QwacHeader::Jades2QwacHeader(const Jades2QwacHeader& other) = default; +Jades2QwacHeader::Jades2QwacHeader(Jades2QwacHeader&& other) = default; +Jades2QwacHeader::~Jades2QwacHeader() = default; + +namespace { + +std::optional<Jades2QwacHeader> ParseJades2QwacHeader( + std::string_view header_string) { + Jades2QwacHeader parsed_header; + // The header of a JWS is a JSON-encoded object (RFC 7515, section 4). + std::optional<base::Value> header_value = + base::JSONReader::Read(header_string, base::JSON_PARSE_RFC); + if (!header_value.has_value() || !header_value->is_dict()) { + return std::nullopt; + } + base::Value::Dict& header = header_value->GetDict(); + + // "alg" (Algorithm) parameter - RFC 7515, section 4.1.1 + std::string* alg = header.FindString("alg"); + if (!alg || *alg == "") { + return std::nullopt; + } + parsed_header.sig_alg = *alg; + header.Remove("alg"); + // TODO(crbug.com/392929826): process alg (check that it matches the alg in + // x5c). + + // "kid" (Key ID) parameter - RFC 7515, section 4.1.4 + // + // The Key ID can be of any type and is used to identify the key used for + // signing. In this profile, the key used to verify the signature will be + // found in the "x5c" parameter, so the "kid" is useless to us and is ignored. + header.Remove("kid"); + + // "cty" (Content Type) parameter - RFC 7515, section 4.1.10 + // + // ETSI TS 119 411-5 V2.1.1 requires the "cty" parameter to be + // "TLS-Certificate-Binding-v1". + std::string* cty = header.FindString("cty"); + if (!cty || *cty != "TLS-Certificate-Binding-v1") { + return std::nullopt; + } + header.Remove("cty"); + + // "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) parameter (RFC 7515, + // section 4.1.8) is the base64url-encoded SHA-256 thumbprint of the + // DER encoding of the X.509 certificate used to sign the JWS. This value is + // not needed to verify the signature (the leaf cert of the "x5c" parameter is + // the signing cert), and it is optional according to RFC 7515, so we ignore + // it. + if (header.FindString("x5t#S256")) { + header.Remove("x5t#S256"); + } + + // "x5c" (X.509 Certificate Chain) header - RFC 7515 section 4.1.6 + base::ListValue* x5c_list = header.FindList("x5c"); + if (!x5c_list) { + return std::nullopt; + } + + size_t i = 0; + bssl::UniquePtr<CRYPTO_BUFFER> leaf; + std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates; + for (const base::Value& cert_value : *x5c_list) { + // RFC 7515 section 4.1.6: + // "Each string in the array is a base64-encoded (not base64url-encoded) DER + // PKIX certificate value." + if (!cert_value.is_string()) { + return std::nullopt; + } + auto cert_bytes = base::Base64Decode(cert_value.GetString()); + if (!cert_bytes.has_value()) { + return std::nullopt; + } + auto buf = x509_util::CreateCryptoBuffer(*cert_bytes); + if (i == 0) { + leaf = std::move(buf); + } else { + intermediates.emplace_back(std::move(buf)); + } + i++; + } + parsed_header.two_qwac_cert = X509Certificate::CreateFromBuffer( + std::move(leaf), std::move(intermediates)); + if (!parsed_header.two_qwac_cert) { + return std::nullopt; + } + header.Remove("x5c"); + + // "iat" header. TS 119 182-1 section 5.1.11 defines this header parameter to + // be almost the same as RFC 7519's JWT "iat" claim. Despite TS 119 182-1 + // citing RFC 7519 as the definition for this header parameter, JWS header + // parameters and JWT claims are not the same thing. In any case, ETSI defines + // this header to be an integer representing the claimed signing time. + // + // I see no indication in TS 119 411-5 that "iat" is required to be present, + // and RFC 7519 specifies it as optional. Further, I haven't yet found an + // indication as to how one would interpret and apply this field in signature + // validation, so I'm ignoring it. + if (header.FindInt("iat")) { + header.Remove("iat"); + } + + // "exp" header. TS 119 411-5 Annex B defines this as the expiry date of the + // binding, and like TS 119 182-1 for the "iat" header, incorrectly cites RFC + // 7519's claim definition of the field (section 4.1.4). Unlike the ETSI + // specification for "iat" that restricts its NumericDate type to an integer, + // we only have the RFC 7519 definition of "exp" to use, which defines + // NumericDate as a JSON numeric value. RFC 7159 allows JSON numeric values to + // contain a fraction part. + // + // Like the "iat" header, TS 119 411-5 does not require the presence of "exp", + // RFC 7519 specifies it as optional, and there is no indication in any ETSI + // spec on how this field would affect signature validation, so it is ignored. + if (header.FindDouble("exp")) { + header.Remove("exp"); + } + + // "sigD" header - ETSI TS 119 182-1 section 5.2.8, with additional + // requirements specified in ETSI TS 119 411-5 Annex B. This parameter is a + // JSON object and is required to be present. + base::DictValue* sig_d = header.FindDict("sigD"); + if (!sig_d) { + return std::nullopt; + } + + // The sigD header must have a "mId" (mechanism ID) of + // "http://uri.etsi.org/19182/ObjectIdByURIHash". (ETSI TS 119 411-5 Annex B.) + std::string* m_id = sig_d->FindString("mId"); + if (!m_id || *m_id != "http://uri.etsi.org/19182/ObjectIdByURIHash") { + return std::nullopt; + } + sig_d->Remove("mId"); + + // The sigD header must have a "pars" member, which is a list of strings. We + // don't care about the contents of this list, but its size must match that of + // "hashV". (ETSI 119 182-1 clause 5.2.8.) + const base::ListValue* pars = sig_d->FindList("pars"); + if (!pars) { + return std::nullopt; + } + size_t bound_cert_count = pars->size(); + for (const base::Value& par : *pars) { + if (!par.is_string()) { + return std::nullopt; + } + } + sig_d->Remove("pars"); + + // The sigD header must have a "hashM" member (TS 119 182-1 + // section 5.2.8.3.3), which is a string identifying the hashing algorithm + // used for the "hashV" member. ETSI TS 119 411-5 only requires that S256, + // S384, and S512 be supported. + std::string* hash_m = sig_d->FindString("hashM"); + if (!hash_m) { + return std::nullopt; + } + if (*hash_m == "S256") { + parsed_header.hash_alg = crypto::hash::kSha256; + } else if (*hash_m == "S384") { + // TODO(crbug.com/392929826): add SHA-384 to crypto/hash.h. + return std::nullopt; + } else if (*hash_m == "S512") { + parsed_header.hash_alg = crypto::hash::kSha512; + } else { + // Unsupported hashing algorithm. + return std::nullopt; + } + sig_d->Remove("hashM"); + + // The sigD header must have a "hashV" member, which is a list of + // base64url-encoded digest values of the base64url-encoded data objects. + // (ETSI TS 119 182-1 clause 5.2.8. The "b64" header parameter is absent, so + // the digest is computed over the base64url-encoded data object instead of + // computed directly over the data object.) + const base::ListValue* hash_v = sig_d->FindList("hashV"); + if (!hash_v) { + return std::nullopt; + } + if (hash_v->size() != bound_cert_count) { + return std::nullopt; + } + parsed_header.bound_cert_hashes.reserve(bound_cert_count); + for (const base::Value& hash_value : *hash_v) { + const std::string* hash_b64url = hash_value.GetIfString(); + if (!hash_b64url) { + return std::nullopt; + } + // ETSI TS 119 182-1 fails to specify the definition of "base64url-encoded". + // Given that other uses of base64url encoding come from the JWS spec, and + // JWS disallows padding in its base64url encoding, we disallow it here as + // well. + auto hash = base::Base64UrlDecode( + *hash_b64url, base::Base64UrlDecodePolicy::DISALLOW_PADDING); + if (!hash.has_value()) { + return std::nullopt; + } + parsed_header.bound_cert_hashes.emplace_back(std::move(*hash)); + } + sig_d->Remove("hashV"); + + // Given the mId used, the sigD header may have a "ctys" member (TS 119 182-1 + // clause 5.2.8.3.3), with semantics and syntax as specified in clause + // 5.2.8.1. Clause 5.2.8.1 defines the "ctys" member's syntax to be an array + // of strings. This array has the same length as the "pars" (and "hashV") + // array, and each element is the content type (RFC 7515 section 4.1.10) of + // the data object referred to by the value in "pars" at the same index. + // RFC 7515 specifies that the content type parameter is ignored by JWS + // implementations and processing of it is performed by the JWS application. + // Since neither ETSI TS 119 182-1 nor TS 119 411-5 provide guidance on the + // content type used for the individual data objects, this implementation has + // no opinion on the stated content types. + const base::ListValue* ctys = sig_d->FindList("ctys"); + if (ctys) { + if (ctys->size() != bound_cert_count) { + return std::nullopt; + } + for (const base::Value& cty_value : *ctys) { + if (!cty_value.is_string()) { + return std::nullopt; + } + } + } else if (sig_d->contains("ctys")) { + // check that there isn't a "ctys" of the wrong type + return std::nullopt; + } + sig_d->Remove("ctys"); + + // sigD has no other members than the aforementioned "mId", "pars", "hashM", + // "hashV", and "ctys". (ETSI TS 119 182-1 clause 5.2.8.) + if (!sig_d->empty()) { + return std::nullopt; + } + header.Remove("sigD"); + + // The header must not contain fields other than "alg", "kid", "cty", + // "x5t#S256", "x5c", "iat", "exp", or "sigD", as required by ETSI TS 119 + // 411-5 V2.1.1, Annex B. + // + // ETSI TS 119 182-1 V1.2.1 section 5.1.9 specifies that if the "sigD" header + // parameter is present, then the "crit" header parameter shall also be + // present with "sigD" as one of its array elements. This is in conflict with + // the requirement in 119 411-5 V2.1.1 Annex B. To resolve this conflict, this + // implementation will allow the presence of "crit", but if it is present, it + // must be an array containing exactly "sigD". + const auto* crit_value = header.Find("crit"); + if (crit_value) { + if (!crit_value->is_list()) { + return std::nullopt; + } + const auto& crit_list = crit_value->GetList(); + if (crit_list.size() != 1 || !crit_list.contains("sigD")) { + return std::nullopt; + } + } + header.Remove("crit"); + + if (!header.empty()) { + return std::nullopt; + } + + return parsed_header; +} + +} // namespace + +TwoQwacCertBinding::TwoQwacCertBinding(Jades2QwacHeader header, + std::string header_string, + std::vector<uint8_t> signature) + : header(header), header_string(header_string), signature(signature) {} + +TwoQwacCertBinding::TwoQwacCertBinding(const TwoQwacCertBinding& other) = + default; +TwoQwacCertBinding::TwoQwacCertBinding(TwoQwacCertBinding&& other) = default; +TwoQwacCertBinding::~TwoQwacCertBinding() = default; + +std::optional<TwoQwacCertBinding> TwoQwacCertBinding::Parse( + std::string_view jws) { + // ETSI TS 119 411-5 V2.1.1 Annex B: The JAdES signatures shall be serialized + // using JWS Compact Serialization as specified in IETF RFC 7515. + // + // The JWS Compact Serialization format consists of 3 components separated by + // a dot (".") (RFC 7515, section 7.1). + std::vector<std::string_view> jws_components = base::SplitStringPiece( + jws, ".", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); + if (jws_components.size() != 3) { + // Reject a JWS that does not consist of 3 components. + return std::nullopt; + } + std::string_view header_b64 = jws_components[0]; + std::string_view payload_b64 = jws_components[1]; + std::string_view signature_b64 = jws_components[2]; + + // The 3 components of a JWS are the header, the payload, and the signature. + // The components are base64url encoded (RFC 7515, section 7.1) and the base64 + // encoding is without any padding "=" characters (Ibid., section 2). + std::string header_string; + if (!base::Base64UrlDecode(header_b64, + base::Base64UrlDecodePolicy::DISALLOW_PADDING, + &header_string)) { + return std::nullopt; + } + std::optional<std::vector<uint8_t>> signature = base::Base64UrlDecode( + signature_b64, base::Base64UrlDecodePolicy::DISALLOW_PADDING); + if (!signature.has_value()) { + return std::nullopt; + } + + // Parse the JWS/JAdES header. + auto header = ParseJades2QwacHeader(header_string); + if (!header.has_value()) { + return std::nullopt; + } + + // ETSI TS 119 411-5 V2.1.1 Annex B specifies a "sigD" header parameter. This + // header parameter is defined in ETSI TS 119 182-1 V1.2.1, section 5.2.8, + // which states "The sigD header parameter shall not appear in JAdES + // signatures whose JWS Payload is attached". Thus, it can be inferred that + // the JWS Payload is detached. A detached payload for a JWS means that the + // encoded payload is empty (RFC 7515, Appendix F). + if (!payload_b64.empty()) { + return std::nullopt; + } + + TwoQwacCertBinding cert_binding(*header, header_string, *signature); + return cert_binding; +} + +} // namespace net
diff --git a/net/cert/two_qwac.h b/net/cert/two_qwac.h new file mode 100644 index 0000000..8aa29b73 --- /dev/null +++ b/net/cert/two_qwac.h
@@ -0,0 +1,85 @@ +// 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 NET_CERT_TWO_QWAC_H_ +#define NET_CERT_TWO_QWAC_H_ + +#include <stdint.h> + +#include <optional> +#include <string_view> +#include <utility> +#include <vector> + +#include "crypto/hash.h" +#include "net/base/net_export.h" +#include "net/cert/x509_certificate.h" + +namespace net { + +// Contains fields from a JAdES (ETSI TS 119 182-1) signature header needed for +// verifying 2-QWAC TLS certificate bindings. While JAdES is a profile of JWS +// (RFC 7515), this is not general-purpose JWS or JWT code. It is also not +// general-purpose JAdES code, as only fields needed for 2-QWAC TLS certificate +// bindings are present here. +struct NET_EXPORT_PRIVATE Jades2QwacHeader { + Jades2QwacHeader(); + Jades2QwacHeader(const Jades2QwacHeader& other); + Jades2QwacHeader(Jades2QwacHeader&& other); + ~Jades2QwacHeader(); + + // The signature algorithm used to sign the JWS, as provided by the "alg" JWS + // Header Parameter (RFC 7515, section 4.1.1). Valid values for this field can + // be found in the JSON Web Signature and Encryption Algorithms IANA registry + // (https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms). + // The consumer of this struct must check that the algorithm provided in this + // field matches the signature algorithm of the leaf cert in |two_qwac_cert|. + std::string sig_alg; + + // The certificate chain with a leaf cert that is a 2-QWAC. This certificate + // chain is used to sign the JWS, which binds the 2-QWAC to a set of TLS + // serverAuth certificates. + scoped_refptr<net::X509Certificate> two_qwac_cert; + + // The hash algorithm used to hash the bound certificates. + crypto::hash::HashKind hash_alg; + + // The hashes of the bound certificates (base64url-encoded), hashed using + // |hash_alg|. Note: this is Digest(base64url(cert)), because that's what the + // JAdES and 2-QWAC specs require (not that it makes any sense to do that). + std::vector<std::vector<uint8_t>> bound_cert_hashes; +}; + +// A TwoQwacCertBinding represents a JAdES Signature (ETSI TS 119 182-1, +// clause 3.1) used for 2-QWACs (ETSI TS 119 411-5, clause 6.2.2). It comes from +// a TLS Certificate Binding (ETSI TS 119 411-5 annex B). Note that a JAdES +// Signature (which is also a JWS, a.k.a. JSON Web Signature) consists of a +// header and a cryptographic signature, not just a signature. +struct NET_EXPORT_PRIVATE TwoQwacCertBinding { + TwoQwacCertBinding(Jades2QwacHeader header, + std::string header_string, + std::vector<uint8_t> signature); + TwoQwacCertBinding(const TwoQwacCertBinding& other); + TwoQwacCertBinding(TwoQwacCertBinding&& other); + ~TwoQwacCertBinding(); + + // Parses a TLS Certificate Binding structure that contains a 2-QWAC + // certificate chain. + // TODO(crbug.com/392929826): Add a fuzz test for this function. + static std::optional<TwoQwacCertBinding> Parse(std::string_view jws); + + // The parsed JWS Header from the certificate binding structure. + Jades2QwacHeader header; + + // The unparsed JWS Header, needed for verifying the signature. + std::string header_string; + + // The JWS Signature (RFC 7515 section 2)/JAdES Signature Value (ETSI TS 119 + // 182-1 clause 3.1) from the certificate binding structure. + std::vector<uint8_t> signature; +}; + +} // namespace net + +#endif // NET_CERT_TWO_QWAC_H_
diff --git a/net/cert/two_qwac_unittest.cc b/net/cert/two_qwac_unittest.cc new file mode 100644 index 0000000..d8306fb1 --- /dev/null +++ b/net/cert/two_qwac_unittest.cc
@@ -0,0 +1,449 @@ +// 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 "net/cert/two_qwac.h" + +#include <stdint.h> + +#include "base/base64.h" +#include "base/base64url.h" +#include "base/functional/callback.h" +#include "base/json/json_string_value_serializer.h" +#include "base/values.h" +#include "net/test/cert_builder.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace { + +// Builds a header that has the minimal required set of parameters +base::DictValue MinimalBindingHeader() { + auto [leaf, root] = net::CertBuilder::CreateSimpleChain2(); + base::Value::Dict header = + base::Value::Dict() + .Set("alg", "test alg") + .Set("cty", "TLS-Certificate-Binding-v1") + .Set("x5c", base::Value::List() + // These are base64 encoded, not base64url encoded + .Append(base::Base64Encode(leaf->GetDER())) + .Append(base::Base64Encode(root->GetDER()))) + .Set("sigD", + base::Value::Dict() + .Set("mId", "http://uri.etsi.org/19182/ObjectIdByURIHash") + .Set("pars", base::Value::List().Append("").Append("")) + .Set("hashM", "S256") + // These are hashes of the certs that this + // TlsCertificateBinding binds, not hashes of the certs in + // the x5c cert chain. + .Set("hashV", base::Value::List() + .Append("fakehash1A") + .Append("fakehash2A"))); + return header; +} + +// Creates a TLS Certificate Binding from the provided header. This test helper +// leaves the signature empty. +std::string CreateTwoQwacCertBinding(const base::DictValue& header) { + std::string header_string; + EXPECT_TRUE(JSONStringValueSerializer(&header_string).Serialize(header)); + // Create the JWS from the header. + std::string jws; + base::Base64UrlEncode(header_string, + base::Base64UrlEncodePolicy::OMIT_PADDING, &jws); + // Add empty payload and signature to JWS. + jws += ".."; + return jws; +} + +TEST(ParseTlsCertificateBinding, MinimalValidBinding) { + // TODO(crbug.com/392929826): Once we start validating signatures, these + // tests will probably need to be updated to have real algorithms, + // certificates, and signatures. (This is assuming that some basic checks are + // added to the parsing code, e.g. that we can parse certs into a + // net::X509Certificate and check that the "alg" matches the leaf cert.) + + // Build a header that has the minimally required set of parameters + base::DictValue header = MinimalBindingHeader(); + std::string jws = CreateTwoQwacCertBinding(header); + auto cert_binding = TwoQwacCertBinding::Parse(jws); + ASSERT_TRUE(cert_binding.has_value()); +} + +TEST(ParseTlsCertificateBinding, MaximalValidBinding) { + // Create a header that has all allowed fields + base::DictValue header = MinimalBindingHeader(); + header.Set("kid", base::Value::Dict() + .Set("random key", "random value") + .Set("kids can have", "whatever they want")); + header.Set("x5t#S256", "base64urlhashA"); + header.Set("iat", 12345); + header.Set("exp", 67.89); + header.Set("crit", base::ListValue().Append("sigD")); + + header.FindDict("sigD")->Set( + "ctys", + base::Value::List().Append("content-type1").Append("content-type2")); + + std::string jws = CreateTwoQwacCertBinding(header); + auto cert_binding = TwoQwacCertBinding::Parse(jws); + ASSERT_TRUE(cert_binding.has_value()); +} + +// Test failure when the JWS header isn't a JSON object. +TEST(ParseTlsCertificateBinding, JwsHeaderNotObject) { + std::string header = "[]"; + std::string jws; + base::Base64UrlEncode(header, base::Base64UrlEncodePolicy::OMIT_PADDING, + &jws); + jws += ".."; + EXPECT_FALSE(TwoQwacCertBinding::Parse(jws).has_value()); +} + +// Test failure when the JWS header isn't JSON. +TEST(ParseTlsCertificateBinding, JwsHeaderNotJson) { + std::string header = "AAA"; + std::string jws; + base::Base64UrlEncode(header, base::Base64UrlEncodePolicy::OMIT_PADDING, + &jws); + jws += ".."; + EXPECT_FALSE(TwoQwacCertBinding::Parse(jws).has_value()); +} + +// Test failure when the JWS header isn't valid base64url. +TEST(ParseTlsCertificateBinding, JwsHeaderNotBase64) { + // the header is encoded as "A", which is too short to be base64url. + std::string jws = "A.."; + EXPECT_FALSE(TwoQwacCertBinding::Parse(jws).has_value()); +} + +// Test failure when the JWS payload is non-empty. +TEST(ParseTlsCertificateBinding, JwsPayloadNonEmpty) { + std::string header_string; + EXPECT_TRUE(JSONStringValueSerializer(&header_string) + .Serialize(MinimalBindingHeader())); + std::string header_b64; + base::Base64UrlEncode(header_string, + base::Base64UrlEncodePolicy::OMIT_PADDING, &header_b64); + // Make a JWS consisting of a valid header, a payload (base64url-encoded as + // "AAAA") and an empty signature. + std::string jws = header_b64 + ".AAAA."; + EXPECT_FALSE(TwoQwacCertBinding::Parse(jws).has_value()); +} + +// Test failure when the JWS signature is not valid base64url. +TEST(ParseTlsCertificateBinding, JwsSignatureNotBase64) { + std::string header_string; + EXPECT_TRUE(JSONStringValueSerializer(&header_string) + .Serialize(MinimalBindingHeader())); + std::string header_b64; + base::Base64UrlEncode(header_string, + base::Base64UrlEncodePolicy::OMIT_PADDING, &header_b64); + std::string jws = header_b64 + "..A"; + EXPECT_FALSE(TwoQwacCertBinding::Parse(jws).has_value()); +} + +// Test failure when the JWS consists of 2 components instead of 3. +TEST(ParseTlsCertificateBinding, JwsHas2Components) { + std::string header_string; + EXPECT_TRUE(JSONStringValueSerializer(&header_string) + .Serialize(MinimalBindingHeader())); + std::string header_b64; + base::Base64UrlEncode(header_string, + base::Base64UrlEncodePolicy::OMIT_PADDING, &header_b64); + std::string jws = header_b64 + ".AAAA"; + EXPECT_FALSE(TwoQwacCertBinding::Parse(jws).has_value()); +} + +// Test failure when the JWS consists of 4 components instead of 3. +TEST(ParseTlsCertificateBinding, JwsHas4Components) { + std::string header_string; + EXPECT_TRUE(JSONStringValueSerializer(&header_string) + .Serialize(MinimalBindingHeader())); + std::string header_b64; + base::Base64UrlEncode(header_string, + base::Base64UrlEncodePolicy::OMIT_PADDING, &header_b64); + std::string jws = header_b64 + "..AAAA.AAAA"; + EXPECT_FALSE(TwoQwacCertBinding::Parse(jws).has_value()); +} + +TEST(ParseTlsCertificateBinding, InvalidFields) { + const struct { + std::string header_key; + base::Value value; + } kTests[] = { + { + // "alg" expects a string + "alg", + base::Value(1), + }, + { + // "alg" expects a non-empty string + "alg", + base::Value(""), + }, + { + // "cty" expects a string + "cty", + base::Value(1), + }, + { + // "cty" expects a specific value for its string + "cty", + base::Value("TLS-Certificate-Binding-v2"), + }, + { + // "x5t#S256" expects a string + "x5t#S256", + base::Value(1), + }, + { + // "x5c" expects a list + "x5c", + base::Value("wrong type"), + }, + { + // "x5c" expects strings in its list + "x5c", + base::Value(base::ListValue().Append(1)), + }, + { + // "x5c" expects base64 strings in its list. Test with a base64url + // (but not regular base64) string. + "x5c", + base::Value(base::ListValue().Append("M-_A")), + }, + { + // "x5c" expects the base64 strings in its list to be valid X.509 + // certificates. This string is valid base64, but is a (very) + // truncated X.509 certificate. + "x5c", + base::Value(base::ListValue().Append("MIID")), + }, + { + // "iat" expects an int (when used for 2-QWACs). "iat" more generally + // (according to RFC 7519) can be a double, but we don't allow that, + // so explicitly check that doubles are rejected. + "iat", + base::Value(1.0), + }, + { + // "exp" expects a numeric value + "exp", + base::Value("wrong type"), + }, + { + // "crit", if present, can only contain "sigD" + "crit", + base::Value(base::ListValue().Append("sigD").Append("x5c")), + }, + { + // "crit" expects a list + "crit", + base::Value("wrong type"), + }, + { + // "sigD" expects an object + "sigD", + base::Value(base::ListValue()), + }, + { + // The 2-QWAC TLS Certificate Binding JAdES profile only allows + // specific fields in the JWS header, and "x5u" is not one of them. + "x5u", + base::Value("X.509 URL"), + }, + }; + + for (const auto& test : kTests) { + SCOPED_TRACE(test.header_key); + base::DictValue header = MinimalBindingHeader(); + header.Set(test.header_key, test.value.Clone()); + std::string jws = CreateTwoQwacCertBinding(header); + auto cert_binding = TwoQwacCertBinding::Parse(jws); + ASSERT_FALSE(cert_binding.has_value()); + } +} + +TEST(ParseTlsCertificateBinding, SigDHeaderParam) { + const struct { + std::string name; + base::RepeatingCallback<void(base::DictValue*)> header_func; + bool valid; + } kTests[] = { + { + "wrong mId", + base::BindRepeating([](base::DictValue* sig_d) { + sig_d->Set("mId", "http://uri.etsi.org/19182/ObjectIdByURI"); + }), + false, + }, + { + "wrong mId type", + base::BindRepeating( + [](base::DictValue* sig_d) { sig_d->Set("mId", 1); }), + false, + }, + { + "wrong pars type", + base::BindRepeating( + [](base::DictValue* sig_d) { sig_d->Set("pars", 1); }), + false, + }, + { + // This repeats the default value used in MinimalBindingHeader() in + // other tests, but is here for completeness. + "SHA-256 supported", + base::BindRepeating( + [](base::DictValue* sig_d) { sig_d->Set("hashM", "S256"); }), + true, + }, + { + // TODO(crbug.com/392929826): Support SHA-384. + "SHA-384 not supported", + base::BindRepeating( + [](base::DictValue* sig_d) { sig_d->Set("hashM", "S384"); }), + false, + }, + { + "SHA-512 supported", + base::BindRepeating( + [](base::DictValue* sig_d) { sig_d->Set("hashM", "S512"); }), + true, + }, + { + "Other hashM values not supported", + base::BindRepeating( + [](base::DictValue* sig_d) { sig_d->Set("hashM", "SHA1"); }), + false, + }, + { + "wrong hashM type", + base::BindRepeating( + [](base::DictValue* sig_d) { sig_d->Set("hashM", 1); }), + false, + }, + { + "wrong type in pars list", + base::BindRepeating([](base::DictValue* sig_d) { + // "pars" and "hashV" must have the same length. + sig_d->Set("pars", base::ListValue().Append(1)); + sig_d->Set("hashV", base::ListValue().Append("fakehash")); + }), + false, + }, + { + "disallowed base64 padding in hashV", + base::BindRepeating([](base::DictValue* sig_d) { + // "ctys" must have the same length as "pars" and "hashV". + sig_d->Set("pars", base::ListValue().Append("URL")); + // hashV list elements are base64url encoded with no padding + sig_d->Set("hashV", base::ListValue().Append("fakehashAA==")); + }), + false, + }, + { + "bad base64url encoding in hashV", + base::BindRepeating([](base::DictValue* sig_d) { + // "ctys" must have the same length as "pars" and "hashV". + sig_d->Set("pars", base::ListValue().Append("URL")); + // a base64url input (with no padding) is malformed if its length + // mod 4 is 1. + sig_d->Set("hashV", base::ListValue().Append("fakehash1")); + }), + false, + }, + { + "wrong type in hashV list", + base::BindRepeating([](base::DictValue* sig_d) { + // "ctys" must have the same length as "pars" and "hashV". + sig_d->Set("pars", base::ListValue().Append("URL")); + sig_d->Set("hashV", base::ListValue().Append(1)); + }), + false, + }, + { + "wrong hashV type", + base::BindRepeating( + [](base::DictValue* sig_d) { sig_d->Set("hashV", 1); }), + false, + }, + { + "correct ctys type", + base::BindRepeating([](base::DictValue* sig_d) { + // "ctys" must have the same length as "pars" and "hashV". + sig_d->Set("pars", base::ListValue().Append("URL")); + sig_d->Set("hashV", base::ListValue().Append("fakehash")); + sig_d->Set("ctys", base::ListValue().Append("content type")); + }), + true, + }, + { + "wrong ctys type", + base::BindRepeating( + [](base::DictValue* sig_d) { sig_d->Set("ctys", "wrong type"); }), + false, + }, + { + "wrong type inside ctys list", + base::BindRepeating([](base::DictValue* sig_d) { + // "ctys" must have the same length as "pars" and "hashV". + sig_d->Set("pars", base::ListValue().Append("URL")); + sig_d->Set("hashV", base::ListValue().Append("fakehash")); + sig_d->Set("ctys", base::ListValue().Append(1)); + }), + false, + }, + { + "pars length mismatch", + base::BindRepeating([](base::DictValue* sig_d) { + // "ctys" must have the same length as "pars" and "hashV". + sig_d->Set("pars", base::ListValue().Append("URL").Append("URL 2")); + sig_d->Set("hashV", base::ListValue().Append("fakehash")); + sig_d->Set("ctys", base::ListValue().Append("content type")); + }), + false, + }, + { + "hashV length mismatch", + base::BindRepeating([](base::DictValue* sig_d) { + // "ctys" must have the same length as "pars" and "hashV". + sig_d->Set("pars", base::ListValue().Append("URL")); + sig_d->Set("hashV", + base::ListValue().Append("fakehash").Append("hashfake")); + sig_d->Set("ctys", base::ListValue().Append("content type")); + }), + false, + }, + { + "ctys length mismatch", + base::BindRepeating([](base::DictValue* sig_d) { + // "ctys" must have the same length as "pars" and "hashV". + sig_d->Set("pars", base::ListValue().Append("URL")); + sig_d->Set("hashV", base::ListValue().Append("fakehash")); + sig_d->Set("ctys", base::ListValue() + .Append("content type") + .Append("content type")); + }), + false, + }, + { + "unknown member in sigD", + base::BindRepeating( + [](base::DictValue* sig_d) { sig_d->Set("spURI", "URL"); }), + false, + }, + }; + + for (const auto& test : kTests) { + SCOPED_TRACE(test.name); + base::DictValue header = MinimalBindingHeader(); + test.header_func.Run(header.FindDict("sigD")); + std::string jws = CreateTwoQwacCertBinding(header); + auto cert_binding = TwoQwacCertBinding::Parse(jws); + EXPECT_EQ(cert_binding.has_value(), test.valid); + } +} + +} // namespace +} // namespace net
diff --git a/services/on_device_model/BUILD.gn b/services/on_device_model/BUILD.gn index 422a017e..b18ff29 100644 --- a/services/on_device_model/BUILD.gn +++ b/services/on_device_model/BUILD.gn
@@ -42,8 +42,10 @@ } if (is_linux || is_chromeos) { + deps += [ "//gpu/config" ] + } + if (!is_fuchsia) { deps += [ - "//gpu/config", "//third_party/dawn/include/dawn:cpp_headers", "//third_party/dawn/src/dawn:proc", "//third_party/dawn/src/dawn/native",
diff --git a/services/on_device_model/pre_sandbox_init.cc b/services/on_device_model/pre_sandbox_init.cc index 5f59497..cf00550 100644 --- a/services/on_device_model/pre_sandbox_init.cc +++ b/services/on_device_model/pre_sandbox_init.cc
@@ -16,6 +16,9 @@ #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) #include "gpu/config/gpu_info_collector.h" // nogncheck +#endif + +#if !BUILDFLAG(IS_FUCHSIA) #include "third_party/dawn/include/dawn/dawn_proc.h" // nogncheck #include "third_party/dawn/include/dawn/native/DawnNative.h" // nogncheck #include "third_party/dawn/include/dawn/webgpu_cpp.h" // nogncheck @@ -53,6 +56,20 @@ } #endif +#if !BUILDFLAG(IS_FUCHSIA) +// 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. +BASE_FEATURE(kOnDeviceModelWarmDrivers, + "OnDeviceModelWarmDrivers", +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN) + base::FEATURE_ENABLED_BY_DEFAULT +#else + base::FEATURE_DISABLED_BY_DEFAULT +#endif +); +#endif + } // namespace // static @@ -74,31 +91,39 @@ } #endif -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) - // Warm any relevant drivers before attempting to bring up the sandbox. For - // good measure we initialize a device instance for any adapter with an - // appropriate backend on top of any integrated or discrete GPU. - dawnProcSetProcs(&dawn::native::GetProcs()); - auto instance = std::make_unique<dawn::native::Instance>(); - const wgpu::RequestAdapterOptions adapter_options{ - .backendType = wgpu::BackendType::Vulkan, - }; - std::vector<dawn::native::Adapter> adapters = - instance->EnumerateAdapters(&adapter_options); - for (auto& nativeAdapter : adapters) { - wgpu::Adapter adapter = wgpu::Adapter(nativeAdapter.Get()); - wgpu::AdapterInfo info; - adapter.GetInfo(&info); - if (info.adapterType == wgpu::AdapterType::IntegratedGPU || - info.adapterType == wgpu::AdapterType::DiscreteGPU) { - const wgpu::DeviceDescriptor descriptor; - wgpu::Device device{nativeAdapter.CreateDevice(&descriptor)}; - if (device) { - device.Destroy(); +#if !BUILDFLAG(IS_FUCHSIA) + if (base::FeatureList::IsEnabled(kOnDeviceModelWarmDrivers)) { + // Warm any relevant drivers before attempting to bring up the sandbox. For + // good measure we initialize a device instance for any adapter with an + // appropriate backend on top of any integrated or discrete GPU. + dawnProcSetProcs(&dawn::native::GetProcs()); + auto instance = std::make_unique<dawn::native::Instance>(); + const wgpu::RequestAdapterOptions adapter_options{ +#if BUILDFLAG(IS_WIN) + .backendType = wgpu::BackendType::D3D12, +#elif BUILDFLAG(IS_APPLE) + .backendType = wgpu::BackendType::Metal, +#else + .backendType = wgpu::BackendType::Vulkan, +#endif + }; + std::vector<dawn::native::Adapter> adapters = + instance->EnumerateAdapters(&adapter_options); + for (auto& nativeAdapter : adapters) { + wgpu::Adapter adapter = wgpu::Adapter(nativeAdapter.Get()); + wgpu::AdapterInfo info; + adapter.GetInfo(&info); + if (info.adapterType == wgpu::AdapterType::IntegratedGPU || + info.adapterType == wgpu::AdapterType::DiscreteGPU) { + const wgpu::DeviceDescriptor descriptor; + wgpu::Device device{nativeAdapter.CreateDevice(&descriptor)}; + if (device) { + device.Destroy(); + } } } } -#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#endif return true; }
diff --git a/services/video_effects/OWNERS b/services/video_effects/OWNERS index 809d551..2523d33b 100644 --- a/services/video_effects/OWNERS +++ b/services/video_effects/OWNERS
@@ -1,5 +1,4 @@ # Primary reviewers: -bialpio@chromium.org bryantchandler@chromium.org # Secondary reviewers (use if primary reviewer is unavailable):
diff --git a/sql/sandboxed_vfs.cc b/sql/sandboxed_vfs.cc index 0e64d2d8..a53291f 100644 --- a/sql/sandboxed_vfs.cc +++ b/sql/sandboxed_vfs.cc
@@ -125,7 +125,6 @@ -kUnixEpochAsJulianDay * base::Time::kMillisecondsPerDay); } -#if DCHECK_IS_ON() // `full_path_cstr` must be a filename argument passed to the VFS from SQLite. SandboxedVfsFileType VfsFileTypeFromPath(const char* full_path_cstr) { std::string_view full_path(full_path_cstr); @@ -146,7 +145,6 @@ << "Argument is not a file name buffer passed from SQLite to a VFS: " << full_path; } -#endif // DCHECK_IS_ON() } // namespace @@ -182,9 +180,7 @@ } SandboxedVfsFile::Create(std::move(file), std::move(file_path), -#if DCHECK_IS_ON() VfsFileTypeFromPath(full_path), -#endif // DCHECK_IS_ON() this, result_file); if (granted_flags) *granted_flags = requested_flags;
diff --git a/sql/sandboxed_vfs_file.cc b/sql/sandboxed_vfs_file.cc index 6b9e534..e8a9981 100644 --- a/sql/sandboxed_vfs_file.cc +++ b/sql/sandboxed_vfs_file.cc
@@ -137,18 +137,14 @@ // static void SandboxedVfsFile::Create(base::File file, base::FilePath file_path, -#if DCHECK_IS_ON() SandboxedVfsFileType file_type, -#endif // DCHECK_IS_ON() SandboxedVfs* vfs, sqlite3_file& buffer) { SandboxedVfsFileSqliteBridge& bridge = SandboxedVfsFileSqliteBridge::FromSqliteFile(buffer); bridge.sandboxed_vfs_file = new SandboxedVfsFile(std::move(file), std::move(file_path), -#if DCHECK_IS_ON() file_type, -#endif // DCHECK_IS_ON() vfs); bridge.sqlite_file.pMethods = GetSqliteIoMethods(); } @@ -224,13 +220,11 @@ DCHECK_GE(size, 0); DCHECK_GE(offset, 0); -#if DCHECK_IS_ON() // SQLite's locking protocol only acquires locks on the database file. The // journal and the WAL file are always unlocked. DCHECK(sqlite_lock_mode_ == SQLITE_LOCK_EXCLUSIVE || file_type_ != SandboxedVfsFileType::kDatabase) << "Write to database file with lock mode " << sqlite_lock_mode_; -#endif // DCHECK_IS_ON() const char* data = reinterpret_cast<const char*>(buffer); @@ -563,16 +557,12 @@ SandboxedVfsFile::SandboxedVfsFile(base::File file, base::FilePath file_path, -#if DCHECK_IS_ON() SandboxedVfsFileType file_type, -#endif // DCHECK_IS_ON() SandboxedVfs* vfs) : file_(std::move(file)), sqlite_lock_mode_(SQLITE_LOCK_NONE), vfs_(vfs), -#if DCHECK_IS_ON() file_type_(file_type), -#endif // DCHECK_IS_ON() file_path_(std::move(file_path)) { }
diff --git a/sql/sandboxed_vfs_file.h b/sql/sandboxed_vfs_file.h index 9af81a9..ef30a05 100644 --- a/sql/sandboxed_vfs_file.h +++ b/sql/sandboxed_vfs_file.h
@@ -50,9 +50,7 @@ // returned sqlite3_file object. static void Create(base::File file, base::FilePath file_path, -#if DCHECK_IS_ON() SandboxedVfsFileType file_type, -#endif // DCHECK_IS_ON() SandboxedVfs* vfs, sqlite3_file& buffer); @@ -85,9 +83,7 @@ private: SandboxedVfsFile(base::File file, base::FilePath file_path, -#if DCHECK_IS_ON() SandboxedVfsFileType file_type, -#endif // DCHECK_IS_ON() SandboxedVfs* vfs); ~SandboxedVfsFile(); @@ -97,10 +93,8 @@ int sqlite_lock_mode_; // The SandboxedVfs that created this instance. const raw_ptr<SandboxedVfs> vfs_; -#if DCHECK_IS_ON() // Tracked to check assumptions about SQLite's locking protocol. const SandboxedVfsFileType file_type_; -#endif // DCHECK_IS_ON() // Used to identify the file in IPCs to the browser process. const base::FilePath file_path_; };
diff --git a/testing/buildbot/filters/ios.content_browsertests.filter b/testing/buildbot/filters/ios.content_browsertests.filter index aa7f400..5deded18 100644 --- a/testing/buildbot/filters/ios.content_browsertests.filter +++ b/testing/buildbot/filters/ios.content_browsertests.filter
@@ -754,10 +754,6 @@ -Default/MediaTest.HLSSingleWithoutExtension/0 -Default/MediaTest.HLSSingleWithoutExtension/1 -# TODO(crbug.com/418103029): The test has been failing since -# https://crrev.com/c/6532249. --SecurityExploitBrowserTest.DidFailLoadWithInvalidErrorCode - # TODO(crbug.com/418125309): These tests have been failing since # https://crrev.com/c/6391357. -All/SitePerProcessWithMainFrameThresholdAndSiteRestrictionTest.RestrictedToURLWithContentClient/0
diff --git a/testing/scripts/common.py b/testing/scripts/common.py index 448c438d..208b0d1 100644 --- a/testing/scripts/common.py +++ b/testing/scripts/common.py
@@ -197,7 +197,20 @@ elif failures: status = result_types.FAIL test_log = '\n'.join(failures) - result_sink_client.Post(name, status, None, test_log, None) + + # Source comes from: + # infra/go/src/go.chromium.org/luci/resultdb/sink/proto/v1/test_result.proto + struct_test_dict = { + 'coarseName': None, # Not used for single tests. + 'fineName': None, # Not used for single tests. + 'caseNameComponents': ['*fixture'], + } + result_sink_client.Post(name, + status, + None, + test_log, + None, + test_id_structured=struct_test_dict) def parse_common_test_results(json_results, test_separator='/'):
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 56aefe4..a531278 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -1994,6 +1994,7 @@ { "platforms": [ "android", + "ios", "mac", "windows" ], @@ -14418,7 +14419,7 @@ ], "experiments": [ { - "name": "EnabledDefaultImpl", + "name": "EnabledBaseline", "params": { "messages_accessibility_events_investigations_param": "1" }, @@ -15178,6 +15179,36 @@ ] } ], + "NtpMicrosoftFilesCard": [ + { + "platforms": [ + "chromeos", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "Enabled_NonInsights", + "params": { + "NtpSharepointModuleDataParam": "non-insights" + }, + "enable_features": [ + "NtpSharepointModule" + ] + }, + { + "name": "Enabled_Combination", + "params": { + "NtpSharepointModuleDataParam": "combined" + }, + "enable_features": [ + "NtpSharepointModule" + ] + } + ] + } + ], "OcclusionCullingQuadSplitLimit": [ { "platforms": [ @@ -24422,6 +24453,31 @@ ] } ], + "UseFreedesktopSecretKeyProvider": [ + { + "platforms": [ + "linux" + ], + "experiments": [ + { + "name": "EnableDecryption", + "enable_features": [ + "UseFreedesktopSecretKeyProvider" + ], + "disable_features": [ + "UseFreedesktopSecretKeyProviderForEncryption" + ] + }, + { + "name": "EnableDecryptionAndEncryption", + "enable_features": [ + "UseFreedesktopSecretKeyProvider", + "UseFreedesktopSecretKeyProviderForEncryption" + ] + } + ] + } + ], "UseHeuristicForWindowsFullScreenPowerPoint": [ { "platforms": [
diff --git a/third_party/androidx/build.gradle b/third_party/androidx/build.gradle index 851d2ea3..4fd71a8 100644 --- a/third_party/androidx/build.gradle +++ b/third_party/androidx/build.gradle
@@ -305,7 +305,7 @@ google() maven { // This URL is generated by the fetch_all_androidx.py script. - url 'https://androidx.dev/snapshots/builds/13537461/artifacts/repository' + url 'https://androidx.dev/snapshots/builds/13539686/artifacts/repository' } mavenCentral() }
diff --git a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom index 86b3d4e..a96527c 100644 --- a/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom +++ b/third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom
@@ -914,6 +914,7 @@ kRowRule = 855, kTextGrow = 856, kTextShrink = 857, + kRule = 858, // 1. Add new features above this line (don't change the assigned numbers of // the existing items).
diff --git a/third_party/blink/renderer/core/DEPS b/third_party/blink/renderer/core/DEPS index e20070b..44e3253 100644 --- a/third_party/blink/renderer/core/DEPS +++ b/third_party/blink/renderer/core/DEPS
@@ -212,8 +212,6 @@ "media_values.h": [ "+ui/base/pointer/pointer_device.h", ], - # TODO(crbug.com/385173568): Remove after AIPromptAPIForExtension OT. - "origin_trial_context.cc": [ "+base/command_line.h" ], "scripted_idle_task_controller(_test)?.(cc|h)": [ "+base/task/delayed_task_handle.h", ],
diff --git a/third_party/blink/renderer/core/css/css_gap_decoration_property_utils.cc b/third_party/blink/renderer/core/css/css_gap_decoration_property_utils.cc index 967bc71..9eafab8 100644 --- a/third_party/blink/renderer/core/css/css_gap_decoration_property_utils.cc +++ b/third_party/blink/renderer/core/css/css_gap_decoration_property_utils.cc
@@ -4,6 +4,9 @@ #include "third_party/blink/renderer/core/css/css_gap_decoration_property_utils.h" +#include "third_party/blink/renderer/core/css/css_property_value.h" +#include "third_party/blink/renderer/core/css/css_value_list.h" +#include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h" #include "third_party/blink/renderer/core/css/style_color.h" #include "third_party/blink/renderer/core/style/gap_data_list.h" @@ -53,4 +56,39 @@ }); } +void CSSGapDecorationUtils::AddProperties( + CSSGapDecorationPropertyDirection direction, + const CSSValueList& rule_widths, + const CSSValueList& rule_styles, + const CSSValueList& rule_colors, + bool important, + HeapVector<CSSPropertyValue, 64>& properties) { + CSSPropertyID rule_shorthand_id; + CSSPropertyID rule_width_id; + CSSPropertyID rule_style_id; + CSSPropertyID rule_color_id; + + if (direction == CSSGapDecorationPropertyDirection::kColumn) { + rule_shorthand_id = CSSPropertyID::kColumnRule; + rule_width_id = CSSPropertyID::kColumnRuleWidth; + rule_style_id = CSSPropertyID::kColumnRuleStyle; + rule_color_id = CSSPropertyID::kColumnRuleColor; + } else { + rule_shorthand_id = CSSPropertyID::kRowRule; + rule_width_id = CSSPropertyID::kRowRuleWidth; + rule_style_id = CSSPropertyID::kRowRuleStyle; + rule_color_id = CSSPropertyID::kRowRuleColor; + } + + css_parsing_utils::AddProperty( + rule_width_id, rule_shorthand_id, rule_widths, important, + css_parsing_utils::IsImplicitProperty::kNotImplicit, properties); + css_parsing_utils::AddProperty( + rule_style_id, rule_shorthand_id, rule_styles, important, + css_parsing_utils::IsImplicitProperty::kNotImplicit, properties); + css_parsing_utils::AddProperty( + rule_color_id, rule_shorthand_id, rule_colors, important, + css_parsing_utils::IsImplicitProperty::kNotImplicit, properties); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_gap_decoration_property_utils.h b/third_party/blink/renderer/core/css/css_gap_decoration_property_utils.h index b7672c45..fb21233 100644 --- a/third_party/blink/renderer/core/css/css_gap_decoration_property_utils.h +++ b/third_party/blink/renderer/core/css/css_gap_decoration_property_utils.h
@@ -6,9 +6,12 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_GAP_DECORATION_PROPERTY_UTILS_H_ #include "third_party/blink/renderer/core/css/css_property_names.h" +#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h" namespace blink { +class CSSPropertyValue; +class CSSValueList; template <typename T> class GapDataList; class StyleColor; @@ -36,6 +39,12 @@ CSSGapDecorationPropertyType type); static CSSPropertyID GetShorthandProperty( CSSGapDecorationPropertyDirection direction); + static void AddProperties(CSSGapDecorationPropertyDirection direction, + const CSSValueList& rule_widths, + const CSSValueList& rule_styles, + const CSSValueList& rule_colors, + bool important, + HeapVector<CSSPropertyValue, 64>& properties); }; } // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5 index feb4523..b1108aa0 100644 --- a/third_party/blink/renderer/core/css/css_properties.json5 +++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -8678,6 +8678,14 @@ property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"], }, { + name: "rule", + longhands: [ + "column-rule-width", "column-rule-style", "column-rule-color", + "row-rule-width", "row-rule-style", "row-rule-color"], + property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"], + runtime_flag: "CSSGapDecoration", + }, + { name: "rule-color", longhands: ["column-rule-color", "row-rule-color"], property_methods: ["ParseShorthand", "CSSValueFromComputedStyleInternal"],
diff --git a/third_party/blink/renderer/core/css/css_property_equality.cc b/third_party/blink/renderer/core/css/css_property_equality.cc index fc4169c..9ec4976 100644 --- a/third_party/blink/renderer/core/css/css_property_equality.cc +++ b/third_party/blink/renderer/core/css/css_property_equality.cc
@@ -1293,6 +1293,7 @@ case CSSPropertyID::kPlaceSelf: case CSSPropertyID::kPositionTry: case CSSPropertyID::kRowRule: + case CSSPropertyID::kRule: case CSSPropertyID::kRuleColor: case CSSPropertyID::kRuleWidth: case CSSPropertyID::kRuleStyle:
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc index 5c84374..cf5d4c54 100644 --- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc +++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -7036,28 +7036,17 @@ // Top level parsing function for the gap-decorations shorthand, which handles // the parsing of simple <gap-rule> or repeat <gap-rule> values. -bool ConsumeGapDecorationsRuleShorthand( - CSSGapDecorationPropertyDirection direction, - bool important, - const CSSParserContext& context, - CSSParserTokenStream& stream, - HeapVector<CSSPropertyValue, 64>& properties) { - const StylePropertyShorthand& gapDecorationsRuleShorthand = - direction == CSSGapDecorationPropertyDirection::kRow - ? rowRuleShorthand() - : columnRuleShorthand(); - DCHECK_EQ(gapDecorationsRuleShorthand.length(), 3u); +bool ConsumeGapDecorationsRuleShorthand(bool important, + const CSSParserContext& context, + CSSParserTokenStream& stream, + CSSValueList*& rule_widths, + CSSValueList*& rule_styles, + CSSValueList*& rule_colors) { + CHECK(RuntimeEnabledFeatures::CSSGapDecorationEnabled()); - // If the CSSGapDecorations feature is not enabled, consume greedily since - // only single values are supported by 'column-rule' today. - if (!RuntimeEnabledFeatures::CSSGapDecorationEnabled()) { - return css_parsing_utils::ConsumeShorthandGreedilyViaLonghands( - gapDecorationsRuleShorthand, important, context, stream, properties); - } - - CSSValueList* rule_widths = CSSValueList::CreateSpaceSeparated(); - CSSValueList* rule_styles = CSSValueList::CreateSpaceSeparated(); - CSSValueList* rule_colors = CSSValueList::CreateSpaceSeparated(); + rule_widths = CSSValueList::CreateSpaceSeparated(); + rule_styles = CSSValueList::CreateSpaceSeparated(); + rule_colors = CSSValueList::CreateSpaceSeparated(); bool has_seen_auto_repeat = false; @@ -7103,27 +7092,6 @@ return false; } - css_parsing_utils::AddProperty( - CSSGapDecorationUtils::GetLonghandProperty( - direction, CSSGapDecorationPropertyType::kWidth), - CSSGapDecorationUtils::GetShorthandProperty(direction), *rule_widths, - important, css_parsing_utils::IsImplicitProperty::kNotImplicit, - properties); - - css_parsing_utils::AddProperty( - CSSGapDecorationUtils::GetLonghandProperty( - direction, CSSGapDecorationPropertyType::kStyle), - CSSGapDecorationUtils::GetShorthandProperty(direction), *rule_styles, - important, css_parsing_utils::IsImplicitProperty::kNotImplicit, - properties); - - css_parsing_utils::AddProperty( - CSSGapDecorationUtils::GetLonghandProperty( - direction, CSSGapDecorationPropertyType::kColor), - CSSGapDecorationUtils::GetShorthandProperty(direction), *rule_colors, - important, css_parsing_utils::IsImplicitProperty::kNotImplicit, - properties); - return true; }
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h index be799b8..cc0d8726 100644 --- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.h +++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.h
@@ -557,12 +557,12 @@ CSSValue* ConsumeItemTolerance(CSSParserTokenStream&, const CSSParserContext&); -bool ConsumeGapDecorationsRuleShorthand( - CSSGapDecorationPropertyDirection direction, - bool important, - const CSSParserContext&, - CSSParserTokenStream&, - HeapVector<CSSPropertyValue, 64>& properties); +bool ConsumeGapDecorationsRuleShorthand(bool important, + const CSSParserContext& context, + CSSParserTokenStream& stream, + CSSValueList*& rule_widths, + CSSValueList*& rule_styles, + CSSValueList*& rule_colors); CSSValue* ConsumeHyphenateLimitChars(CSSParserTokenStream&, const CSSParserContext&);
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc index 4a513ea..d648b53e 100644 --- a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc +++ b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
@@ -1196,9 +1196,32 @@ const CSSParserContext& context, const CSSParserLocalContext&, HeapVector<CSSPropertyValue, 64>& properties) const { - return css_parsing_utils::ConsumeGapDecorationsRuleShorthand( - CSSGapDecorationPropertyDirection::kColumn, important, context, stream, - properties); + DCHECK_EQ(columnRuleShorthand().length(), 3u); + // If the CSSGapDecorations feature is not enabled, consume greedily since + // only single values are supported by 'column-rule' today. + if (!RuntimeEnabledFeatures::CSSGapDecorationEnabled()) { + return css_parsing_utils::ConsumeShorthandGreedilyViaLonghands( + columnRuleShorthand(), important, context, stream, properties); + } + + CSSValueList* rule_widths = nullptr; + CSSValueList* rule_styles = nullptr; + CSSValueList* rule_colors = nullptr; + + if (!css_parsing_utils::ConsumeGapDecorationsRuleShorthand( + important, context, stream, rule_widths, rule_styles, rule_colors)) { + return false; + } + + CHECK(rule_widths); + CHECK(rule_styles); + CHECK(rule_colors); + + CSSGapDecorationUtils::AddProperties( + CSSGapDecorationPropertyDirection::kColumn, *rule_widths, *rule_styles, + *rule_colors, important, properties); + + return true; } const CSSValue* ColumnRule::CSSValueFromComputedStyleInternal( @@ -1217,9 +1240,25 @@ const CSSParserContext& context, const CSSParserLocalContext&, HeapVector<CSSPropertyValue, 64>& properties) const { - return css_parsing_utils::ConsumeGapDecorationsRuleShorthand( - CSSGapDecorationPropertyDirection::kRow, important, context, stream, - properties); + DCHECK_EQ(rowRuleShorthand().length(), 3u); + CSSValueList* rule_widths = nullptr; + CSSValueList* rule_styles = nullptr; + CSSValueList* rule_colors = nullptr; + + if (!css_parsing_utils::ConsumeGapDecorationsRuleShorthand( + important, context, stream, rule_widths, rule_styles, rule_colors)) { + return false; + } + + CHECK(rule_widths); + CHECK(rule_styles); + CHECK(rule_colors); + + CSSGapDecorationUtils::AddProperties(CSSGapDecorationPropertyDirection::kRow, + *rule_widths, *rule_styles, *rule_colors, + important, properties); + + return true; } const CSSValue* RowRule::CSSValueFromComputedStyleInternal( @@ -4134,6 +4173,53 @@ *this, style, &style.MaskLayers()); } +bool Rule::ParseShorthand(bool important, + CSSParserTokenStream& stream, + const CSSParserContext& context, + const CSSParserLocalContext& local_context, + HeapVector<CSSPropertyValue, 64>& properties) const { + DCHECK_EQ(ruleShorthand().length(), 6u); + CSSValueList* rule_widths = nullptr; + CSSValueList* rule_styles = nullptr; + CSSValueList* rule_colors = nullptr; + + if (!css_parsing_utils::ConsumeGapDecorationsRuleShorthand( + important, context, stream, rule_widths, rule_styles, rule_colors)) { + return false; + } + + CHECK(rule_widths); + CHECK(rule_styles); + CHECK(rule_colors); + + CSSGapDecorationUtils::AddProperties( + CSSGapDecorationPropertyDirection::kColumn, *rule_widths, *rule_styles, + *rule_colors, important, properties); + CSSGapDecorationUtils::AddProperties(CSSGapDecorationPropertyDirection::kRow, + *rule_widths, *rule_styles, *rule_colors, + important, properties); + + return true; +} + +const CSSValue* Rule::CSSValueFromComputedStyleInternal( + const ComputedStyle& style, + const LayoutObject* layout_object, + bool allow_visited_style, + CSSValuePhase value_phase) const { + const CSSValue* column_value = + GetCSSPropertyColumnRule().CSSValueFromComputedStyle( + style, layout_object, allow_visited_style, value_phase); + const CSSValue* row_value = GetCSSPropertyRowRule().CSSValueFromComputedStyle( + style, layout_object, allow_visited_style, value_phase); + + if (!base::ValuesEquivalent(column_value, row_value)) { + return nullptr; + } + + return column_value; +} + bool RuleColor::ParseShorthand( bool important, CSSParserTokenStream& stream,
diff --git a/third_party/blink/renderer/core/css/style_property_serializer.cc b/third_party/blink/renderer/core/css/style_property_serializer.cc index cfe620a9..b92440c 100644 --- a/third_party/blink/renderer/core/css/style_property_serializer.cc +++ b/third_party/blink/renderer/core/css/style_property_serializer.cc
@@ -666,6 +666,9 @@ return GetLayeredShorthandValue(maskPositionShorthand()); case CSSPropertyID::kMask: return GetLayeredShorthandValue(maskShorthand()); + case CSSPropertyID::kRule: + return GetShorthandValueForRule(rowRuleShorthand(), + columnRuleShorthand()); case CSSPropertyID::kRuleColor: return GetShorthandValueForBidirectionalGapRules(ruleColorShorthand()); case CSSPropertyID::kRuleWidth: @@ -1907,6 +1910,29 @@ return result.ReleaseString(); } +String StylePropertySerializer::GetShorthandValueForRule( + const StylePropertyShorthand& row_rule_shorthand, + const StylePropertyShorthand& column_rule_shorthand) const { + CHECK_EQ(column_rule_shorthand.length(), row_rule_shorthand.length()); + for (wtf_size_t i = 0; i < row_rule_shorthand.length(); ++i) { + const CSSValue* row_rule_data = + property_set_.GetPropertyCSSValue(*row_rule_shorthand.properties()[i]); + const CSSValue* column_rule_data = property_set_.GetPropertyCSSValue( + *column_rule_shorthand.properties()[i]); + + if (!base::ValuesEquivalent(row_rule_data, column_rule_data)) { + return String(); + } + } + // If the values are equivalent, serialize one of the shorthands. + // The `rule` shorthand is bi-directional, so the values should be + // equivalent. + // + // https://drafts.csswg.org/css-gaps-1/#rule-bi-directional + return GetShorthandValueForGapDecorationsRule( + column_rule_shorthand, CSSGapDecorationPropertyDirection::kColumn); +} + String StylePropertySerializer::GetShorthandValueForBidirectionalGapRules( const StylePropertyShorthand& shorthand) const { DCHECK(shorthand.length() == 2u);
diff --git a/third_party/blink/renderer/core/css/style_property_serializer.h b/third_party/blink/renderer/core/css/style_property_serializer.h index b2c5313d..257a2059d 100644 --- a/third_party/blink/renderer/core/css/style_property_serializer.h +++ b/third_party/blink/renderer/core/css/style_property_serializer.h
@@ -64,6 +64,8 @@ String PageBreakPropertyValue(const StylePropertyShorthand&) const; String GetShorthandValue(const StylePropertyShorthand&, String separator = " ") const; + String GetShorthandValueForRule(const StylePropertyShorthand&, + const StylePropertyShorthand&) const; String GetShorthandValueForBidirectionalGapRules( const StylePropertyShorthand&) const; String GetShorthandValueForGapDecorationsRule(
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index a28706c2..23a07e44 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2625,7 +2625,7 @@ gfx::PointF new_position = current_position + displacement; std::unique_ptr<cc::SnapSelectionStrategy> strategy = - cc::SnapSelectionStrategy::CreateForEndAndDirection( + cc::SnapSelectionStrategy::CreateForDisplacement( current_position, displacement, RuntimeEnabledFeatures::FractionalScrollOffsetsEnabled()); new_position = @@ -2726,7 +2726,7 @@ gfx::PointF new_position = viewport->ScrollPosition() + displacement; gfx::PointF current_position = viewport->ScrollPosition(); std::unique_ptr<cc::SnapSelectionStrategy> strategy = - cc::SnapSelectionStrategy::CreateForEndAndDirection( + cc::SnapSelectionStrategy::CreateForDisplacement( current_position, displacement, RuntimeEnabledFeatures::FractionalScrollOffsetsEnabled()); new_position =
diff --git a/third_party/blink/renderer/core/dom/scroll_button_pseudo_element.cc b/third_party/blink/renderer/core/dom/scroll_button_pseudo_element.cc index f55c334f1..e5edefb 100644 --- a/third_party/blink/renderer/core/dom/scroll_button_pseudo_element.cc +++ b/third_party/blink/renderer/core/dom/scroll_button_pseudo_element.cc
@@ -29,13 +29,18 @@ ScrollOffset CalculateSnappedScrollPosition( const ScrollableArea* scrollable_area, - gfx::Vector2dF scaled_delta) { + ScrollDirectionPhysical direction) { gfx::PointF current_position = scrollable_area->ScrollPosition(); std::unique_ptr<cc::SnapSelectionStrategy> strategy = - cc::SnapSelectionStrategy::CreateForEndAndDirection( - current_position, scaled_delta, - RuntimeEnabledFeatures::FractionalScrollOffsetsEnabled()); - current_position += scaled_delta; + scrollable_area->PageScrollSnapStrategy(direction); + gfx::Vector2dF displacement = ToScrollDelta(direction, 1); + displacement.Scale( + scrollable_area->ScrollStep(ui::ScrollGranularity::kScrollByPage, + kHorizontalScrollbar), + scrollable_area->ScrollStep(ui::ScrollGranularity::kScrollByPage, + kVerticalScrollbar)); + + current_position += displacement; if (std::optional<cc::SnapPositionData> snap_position = scrollable_area->GetSnapPosition(*strategy)) { if (snap_position->type != cc::SnapPositionData::Type::kNone) { @@ -81,26 +86,26 @@ GetPseudoId() == kPseudoIdScrollButtonInlineEnd, GetPseudoId() == kPseudoIdScrollButtonBlockStart, GetPseudoId() == kPseudoIdScrollButtonBlockEnd); - gfx::Vector2dF displacement; + std::optional<ScrollDirectionPhysical> direction; if (mapping.Top()) { - displacement.set_y(-scrollable_area->ScrollStep( - ui::ScrollGranularity::kScrollByPage, kVerticalScrollbar)); + direction = ScrollDirectionPhysical::kScrollUp; } else if (mapping.Bottom()) { - displacement.set_y(scrollable_area->ScrollStep( - ui::ScrollGranularity::kScrollByPage, kVerticalScrollbar)); + direction = ScrollDirectionPhysical::kScrollDown; } else if (mapping.Left()) { - displacement.set_x(-scrollable_area->ScrollStep( - ui::ScrollGranularity::kScrollByPage, kHorizontalScrollbar)); + direction = ScrollDirectionPhysical::kScrollLeft; } else if (mapping.Right()) { - displacement.set_x(scrollable_area->ScrollStep( - ui::ScrollGranularity::kScrollByPage, kHorizontalScrollbar)); + direction = ScrollDirectionPhysical::kScrollRight; } - if (!displacement.IsZero()) { + if (direction) { + gfx::Vector2dF displacement = ToScrollDelta(*direction, 1); + displacement.Scale( + scrollable_area->ScrollStep(ui::ScrollGranularity::kScrollByPage, + kHorizontalScrollbar), + scrollable_area->ScrollStep(ui::ScrollGranularity::kScrollByPage, + kVerticalScrollbar)); gfx::PointF current_position = scrollable_area->ScrollPosition(); std::unique_ptr<cc::SnapSelectionStrategy> strategy = - cc::SnapSelectionStrategy::CreateForEndAndDirection( - current_position, displacement, - RuntimeEnabledFeatures::FractionalScrollOffsetsEnabled()); + scrollable_area->PageScrollSnapStrategy(*direction); gfx::PointF new_position = scrollable_area->GetSnapPositionAndSetTarget(*strategy).value_or( current_position + displacement); @@ -179,36 +184,22 @@ if (mapping.Top()) { enabled_ = current_position.y() > CalculateSnappedScrollPosition( - scrollable_area, - gfx::Vector2dF(0, -scrollable_area->ScrollStep( - ui::ScrollGranularity::kScrollByPage, - kVerticalScrollbar))) + scrollable_area, ScrollDirectionPhysical::kScrollUp) .y(); } else if (mapping.Bottom()) { enabled_ = current_position.y() < CalculateSnappedScrollPosition( - scrollable_area, - gfx::Vector2dF(0, scrollable_area->ScrollStep( - ui::ScrollGranularity::kScrollByPage, - kVerticalScrollbar))) + scrollable_area, ScrollDirectionPhysical::kScrollDown) .y(); } else if (mapping.Left()) { enabled_ = current_position.x() > CalculateSnappedScrollPosition( - scrollable_area, - gfx::Vector2dF(-scrollable_area->ScrollStep( - ui::ScrollGranularity::kScrollByPage, - kHorizontalScrollbar), - 0)) + scrollable_area, ScrollDirectionPhysical::kScrollLeft) .x(); } else if (mapping.Right()) { enabled_ = current_position.x() < CalculateSnappedScrollPosition( - scrollable_area, - gfx::Vector2dF(scrollable_area->ScrollStep( - ui::ScrollGranularity::kScrollByPage, - kHorizontalScrollbar), - 0)) + scrollable_area, ScrollDirectionPhysical::kScrollRight) .x(); } if (enabled != enabled_) {
diff --git a/third_party/blink/renderer/core/exported/web_node_test.cc b/third_party/blink/renderer/core/exported/web_node_test.cc index 93724dcd..5add9b46 100644 --- a/third_party/blink/renderer/core/exported/web_node_test.cc +++ b/third_party/blink/renderer/core/exported/web_node_test.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "base/strings/stringprintf.h" #include "base/test/mock_callback.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/web/web_dom_event.h"
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc index a863814a..23e8b082 100644 --- a/third_party/blink/renderer/core/frame/local_dom_window.cc +++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -1871,7 +1871,7 @@ gfx::PointF new_scaled_position = current_position + scaled_delta; std::unique_ptr<cc::SnapSelectionStrategy> strategy = - cc::SnapSelectionStrategy::CreateForEndAndDirection( + cc::SnapSelectionStrategy::CreateForDisplacement( current_position, scaled_delta, RuntimeEnabledFeatures::FractionalScrollOffsetsEnabled()); new_scaled_position =
diff --git a/third_party/blink/renderer/core/input/event_handler_test.cc b/third_party/blink/renderer/core/input/event_handler_test.cc index 473c00d4..1cfa0ef 100644 --- a/third_party/blink/renderer/core/input/event_handler_test.cc +++ b/third_party/blink/renderer/core/input/event_handler_test.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "base/strings/stringprintf.h" #include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc index 93a4f622..febf350 100644 --- a/third_party/blink/renderer/core/input/scroll_manager.cc +++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -222,33 +222,27 @@ ScrollableArea* scrollable_area = ScrollableArea::GetForScrolling(box); DCHECK(scrollable_area); - ScrollOffset delta = ToScrollDelta(physical_direction, 1); - delta.Scale(scrollable_area->ScrollStep(granularity, kHorizontalScrollbar), - scrollable_area->ScrollStep(granularity, kVerticalScrollbar)); // Pressing the arrow key is considered as a scroll with intended direction // only. Pressing the PgUp/PgDn key is considered as a scroll with intended // direction and end position. Pressing the Home/End key is considered as a // scroll with intended end position only. switch (granularity) { case ui::ScrollGranularity::kScrollByLine: { - if (scrollable_area->SnapForDirection(delta)) + if (scrollable_area->SnapForDirection(physical_direction)) { return true; + } break; } case ui::ScrollGranularity::kScrollByPage: { - if (scrollable_area->SnapForEndAndDirection(delta)) + if (scrollable_area->SnapForPageScroll(physical_direction)) { return true; + } break; } case ui::ScrollGranularity::kScrollByDocument: { - gfx::PointF end_position = scrollable_area->ScrollPosition() + delta; - bool scrolled_x = physical_direction == kScrollLeft || - physical_direction == kScrollRight; - bool scrolled_y = physical_direction == kScrollUp || - physical_direction == kScrollDown; - if (scrollable_area->SnapForEndPosition(end_position, scrolled_x, - scrolled_y)) + if (scrollable_area->SnapForDocumentScroll(physical_direction)) { return true; + } break; } default:
diff --git a/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc b/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc index 059b225..2867bf4 100644 --- a/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc +++ b/third_party/blink/renderer/core/origin_trials/origin_trial_context.cc
@@ -7,7 +7,6 @@ #include <ostream> #include <vector> -#include "base/command_line.h" #include "base/feature_list.h" #include "base/time/time.h" #include "build/build_config.h" @@ -564,14 +563,6 @@ return base::FeatureList::IsEnabled(features::kLanguageDetectionAPI); } - // TODO(crbug.com/385173568): Remove after AIPromptAPIForExtension OT. - if (trial_name == "AIPromptAPIForExtension") { - return base::FeatureList::IsEnabled(features::kAIPromptAPI) && - base::FeatureList::IsEnabled(features::kAIPromptAPIForExtension) && - base::CommandLine::ForCurrentProcess()->HasSwitch( - "extension-process"); - } - if (trial_name == "SpeculationRulesTargetHint") { return base::FeatureList::IsEnabled(features::kPrerender2InNewTab); }
diff --git a/third_party/blink/renderer/core/paint/README.md b/third_party/blink/renderer/core/paint/README.md index ceb006c8..13d002e 100644 --- a/third_party/blink/renderer/core/paint/README.md +++ b/third_party/blink/renderer/core/paint/README.md
@@ -531,3 +531,11 @@ cc::PaintedOverlayScrollbarLayer depending on the type of the scrollbar. Custom scrollbars are still painted into drawing display items directly. + +## Pixel snapping and bluriness + +Bluriness can happen when drawings are not aligned to screen pixels. In +Chromium, we try to align drawings to screen pixels when possible / +necessary in almost every stage of rendering. +[This document](https://docs.google.com/document/d/14qWYuGOJRELueTORi2ais5BIJGdo8HADXe07BjjThIs/edit) +contains some useful links to related docs and bugs.
diff --git a/third_party/blink/renderer/core/paint/filter_effect_builder.cc b/third_party/blink/renderer/core/paint/filter_effect_builder.cc index 46b7255..bc4d385 100644 --- a/third_party/blink/renderer/core/paint/filter_effect_builder.cc +++ b/third_party/blink/renderer/core/paint/filter_effect_builder.cc
@@ -131,10 +131,7 @@ const cc::PaintFlags* fill_flags, const cc::PaintFlags* stroke_flags) : reference_box_(reference_box), - viewport_( - RuntimeEnabledFeatures::SvgFilterUserSpaceViewportForNonSvgEnabled() - ? viewport - : std::nullopt), + viewport_(viewport), zoom_(zoom), shorthand_scale_(1), current_color_(current_color), @@ -537,12 +534,7 @@ // primitives since the behavior in these two cases (no primitives, empty // region) should match. if (filter_region.IsEmpty()) { - // TODO(fs): We rely on the presence of a node map here to opt-in to the - // check for an empty filter region. The reason for this is that we lack a - // viewport to resolve against for HTML content. This is crbug.com/512453. - if (viewport_ || node_map) { - return result; - } + return result; } if (!previous_effect)
diff --git a/third_party/blink/renderer/core/scheduler/dom_task.cc b/third_party/blink/renderer/core/scheduler/dom_task.cc index 16f6df5..3d68cf34 100644 --- a/third_party/blink/renderer/core/scheduler/dom_task.cc +++ b/third_party/blink/renderer/core/scheduler/dom_task.cc
@@ -171,8 +171,9 @@ // For the main thread (tracker exists), create the task scope with the signal // to set up propagation. On workers, set the current context here since there // is no tracker. - if (auto* tracker = - scheduler::TaskAttributionTracker::From(script_state->GetIsolate())) { + auto* tracker = + scheduler::TaskAttributionTracker::From(script_state->GetIsolate()); + if (tracker) { task_attribution_scope = tracker->CreateTaskScope( script_state, parent_task_, scheduler::TaskAttributionTracker::TaskScopeType::kSchedulerPostTask, @@ -202,6 +203,11 @@ } execution_state_ = pending_result.IsEmpty() ? ExecutionState::kFinished : ExecutionState::kRunningAsync; + // If this is a worker, clear the context to prevent it from leaking to the + // next task (`task_attribution_scope` handles this on the main thread). + if (!tracker) { + ScriptWrappableTaskState::SetCurrent(script_state, nullptr); + } } void DOMTask::OnAbort() {
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.cc b/third_party/blink/renderer/core/scroll/scrollable_area.cc index ccb6404..0097952 100644 --- a/third_party/blink/renderer/core/scroll/scrollable_area.cc +++ b/third_party/blink/renderer/core/scroll/scrollable_area.cc
@@ -31,6 +31,8 @@ #include "third_party/blink/renderer/core/scroll/scrollable_area.h" +#include <limits> + #include "base/task/single_thread_task_runner.h" #include "build/build_config.h" #include "cc/input/main_thread_scrolling_reason.h" @@ -1068,17 +1070,19 @@ return PixelsPerLineStep(GetLayoutBox()->GetFrame()); } -int ScrollableArea::PageStep(ScrollbarOrientation orientation) const { +gfx::Size ScrollableArea::PageSize() const { // Paging scroll operations should take scroll-padding into account [1]. So we // use the snapport rect to calculate the page step instead of the visible // rect. // [1] https://drafts.csswg.org/css-scroll-snap/#scroll-padding - const gfx::Size snapport_size = - VisibleScrollSnapportRect(kExcludeScrollbars).PixelSnappedSize(); - const int snapport_length = (orientation == kHorizontalScrollbar) - ? snapport_size.width() - : snapport_size.height(); - return cc::ScrollUtils::CalculatePageStep(snapport_length); + return VisibleScrollSnapportRect(kExcludeScrollbars).PixelSnappedSize(); +} + +int ScrollableArea::PageStep(ScrollbarOrientation orientation) const { + gfx::Size page_size = PageSize(); + return cc::ScrollUtils::CalculatePageStep(orientation == kHorizontalScrollbar + ? page_size.width() + : page_size.height()); } int ScrollableArea::DocumentStep(ScrollbarOrientation orientation) const { @@ -1203,28 +1207,68 @@ std::move(on_finish)); } -bool ScrollableArea::SnapForDirection(const ScrollOffset& delta, - base::ScopedClosureRunner on_finish) { +bool ScrollableArea::SnapForDirection(ScrollDirectionPhysical direction) { DCHECK(IsRootFrameViewport() || !GetLayoutBox()->IsGlobalRootScroller()); + ScrollOffset delta = ToScrollDelta(direction, 1); + delta.Scale(LineStep(kHorizontalScrollbar), LineStep(kVerticalScrollbar)); + gfx::PointF current_position = ScrollPosition(); std::unique_ptr<cc::SnapSelectionStrategy> strategy = cc::SnapSelectionStrategy::CreateForDirection( current_position, delta, RuntimeEnabledFeatures::FractionalScrollOffsetsEnabled()); - return PerformSnapping(*strategy, mojom::blink::ScrollBehavior::kSmooth, - std::move(on_finish)); + return PerformSnapping(*strategy, mojom::blink::ScrollBehavior::kSmooth); } -bool ScrollableArea::SnapForEndAndDirection(const ScrollOffset& delta) { +bool ScrollableArea::SnapForPageScroll(ScrollDirectionPhysical direction) { DCHECK(IsRootFrameViewport() || !GetLayoutBox()->IsGlobalRootScroller()); - gfx::PointF current_position = ScrollPosition(); std::unique_ptr<cc::SnapSelectionStrategy> strategy = - cc::SnapSelectionStrategy::CreateForEndAndDirection( - current_position, delta, - RuntimeEnabledFeatures::FractionalScrollOffsetsEnabled()); + PageScrollSnapStrategy(direction); return PerformSnapping(*strategy); } +bool ScrollableArea::SnapForDocumentScroll(ScrollDirectionPhysical direction) { + ScrollOffset delta = ToScrollDelta(direction, 1); + delta.Scale(DocumentStep(kHorizontalScrollbar), + DocumentStep(kVerticalScrollbar)); + gfx::PointF end_position = ScrollPosition() + delta; + bool scrolled_x = direction == kScrollLeft || direction == kScrollRight; + bool scrolled_y = direction == kScrollUp || direction == kScrollDown; + return SnapForEndPosition(end_position, scrolled_x, scrolled_y); +} + +std::unique_ptr<cc::SnapSelectionStrategy> +ScrollableArea::PageScrollSnapStrategy( + ScrollDirectionPhysical direction) const { + gfx::PointF current_position = ScrollPosition(); + gfx::Size page_size = PageSize(); + ScrollOffset unit_direction = ToScrollDelta(direction, 1); + ScrollOffset delta = unit_direction; + delta.Scale(cc::ScrollUtils::CalculatePageStep(page_size.width()), + cc::ScrollUtils::CalculatePageStep(page_size.height())); + + // When scrolling by a page, we prefer that we scroll no more than a page, + // but at least by a reasonable proportion of that page. + ScrollOffset preferred_max_delta = unit_direction; + preferred_max_delta.Scale( + cc::ScrollUtils::CalculateMaxPageSnap(page_size.width()), + cc::ScrollUtils::CalculateMaxPageSnap(page_size.height())); + ScrollOffset preferred_min_delta = unit_direction; + preferred_min_delta.Scale( + cc::ScrollUtils::CalculateMinPageSnap(page_size.width()), + cc::ScrollUtils::CalculateMinPageSnap(page_size.height())); + if (direction == ScrollDirectionPhysical::kScrollDown || + direction == ScrollDirectionPhysical::kScrollUp) { + preferred_max_delta.set_x(std::numeric_limits<float>::max()); + } else { + preferred_max_delta.set_y(std::numeric_limits<float>::max()); + } + + return cc::SnapSelectionStrategy::CreateForPreferredDisplacement( + current_position, delta, preferred_min_delta, preferred_max_delta, + RuntimeEnabledFeatures::FractionalScrollOffsetsEnabled()); +} + void ScrollableArea::SnapAfterLayout() { const cc::SnapContainerData* container_data = GetSnapContainerData(); if (!container_data || !container_data->size()) {
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.h b/third_party/blink/renderer/core/scroll/scrollable_area.h index e407145..3c837ef 100644 --- a/third_party/blink/renderer/core/scroll/scrollable_area.h +++ b/third_party/blink/renderer/core/scroll/scrollable_area.h
@@ -41,6 +41,7 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/layout/geometry/physical_rect.h" #include "third_party/blink/renderer/core/loader/history_item.h" +#include "third_party/blink/renderer/core/scroll/scroll_types.h" #include "third_party/blink/renderer/core/scroll/scrollbar.h" #include "third_party/blink/renderer/core/style/scroll_start_data.h" #include "third_party/blink/renderer/platform/graphics/compositor_element_id.h" @@ -183,7 +184,7 @@ virtual void UpdateFocusDataForSnapAreas() {} // SnapAtCurrentPosition(), SnapForEndPosition(), SnapForDirection(), and - // SnapForEndAndDirection() return true if snapping was performed, and false + // SnapForDisplacement() return true if snapping was performed, and false // otherwise. Note that this does not necessarily mean that any scrolling was // performed as a result e.g., if we are already at the snap point. // The scroll callback parameter is used to set the hover state dirty and @@ -201,10 +202,11 @@ bool scrolled_x, bool scrolled_y, base::ScopedClosureRunner on_finish = base::ScopedClosureRunner()); - bool SnapForDirection( - const ScrollOffset& delta, - base::ScopedClosureRunner on_finish = base::ScopedClosureRunner()); - bool SnapForEndAndDirection(const ScrollOffset& delta); + bool SnapForDirection(ScrollDirectionPhysical direction); + bool SnapForPageScroll(ScrollDirectionPhysical direction); + bool SnapForDocumentScroll(ScrollDirectionPhysical direction); + std::unique_ptr<cc::SnapSelectionStrategy> PageScrollSnapStrategy( + ScrollDirectionPhysical direction) const; void SnapAfterLayout(); // Tries to find a target snap position. If found, returns the target @@ -550,6 +552,8 @@ void OnScrollFinished(bool enqueue_scrollend); float ScrollStep(ui::ScrollGranularity, ScrollbarOrientation) const; + std::unique_ptr<cc::SnapSelectionStrategy> PageSnap( + ScrollbarOrientation) const; // Injects a gesture scroll event based on the given parameters for mouse // events on a scrollbar of this scrollable area. @@ -696,6 +700,8 @@ virtual int DocumentStep(ScrollbarOrientation) const; virtual float PixelStep(ScrollbarOrientation) const; + gfx::Size PageSize() const; + // Returns true if a snap point was found. bool PerformSnapping( const cc::SnapSelectionStrategy& strategy,
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index 9bc078d2..8663dd75 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -320,32 +320,25 @@ }, { name: "AIPromptAPI", - // OriginTrialContext::CanEnableTrialFromName limits access to extensions. - origin_trial_feature_name: "AIPromptAPIForExtension", - origin_trial_allows_third_party: true, + public: true, status: { "Win": "experimental", "Mac": "experimental", "Linux": "experimental", "default": "", }, - base_feature_status: "enabled", - copied_from_base_feature_if: "overridden", - implied_by: ["AIPromptAPIMultimodalInput", "AIPromptAPIForExtension"], + implied_by: ["AIPromptAPIMultimodalInput"], }, { - // Extension-specific feature checked in addition to "AIPromptAPI". + // Extension access to "AIPromptAPI". name: "AIPromptAPIForExtension", - origin_trial_feature_name: "AIPromptAPIForExtension", - origin_trial_allows_third_party: true, + public: true, status: { - "Win": "experimental", - "Mac": "experimental", - "Linux": "experimental", + "Win": "stable", + "Mac": "stable", + "Linux": "stable", "default": "", }, - base_feature_status: "enabled", - copied_from_base_feature_if: "overridden", }, { name: "AIPromptAPIForWorkers", @@ -361,18 +354,10 @@ }, }, { + // Gates access to the responseConstraint enhancement for "AIPromptAPI". + // This feature alone does not expose any "AIPromptAPI" feature access. name: "AIPromptAPIStructuredOutput", - origin_trial_feature_name: "AIPromptAPIForExtension", - origin_trial_allows_third_party: true, - status: { - "Win": "experimental", - "Mac": "experimental", - "Linux": "experimental", - "default": "", - }, - base_feature_status: "enabled", - copied_from_base_feature_if: "overridden", - implied_by: ["AIPromptAPI"], + status: "stable", }, { name: "AIProofreadingAPI", @@ -4713,10 +4698,6 @@ status: "stable", }, { - name: "SvgFilterUserSpaceViewportForNonSvg", - status: "stable", - }, - { name: "SvgInlineRootPixelSnappingScaleAdjustment", status: "test", },
diff --git a/third_party/blink/renderer/platform/text/character.h b/third_party/blink/renderer/platform/text/character.h index b74d6f1..508d6fa 100644 --- a/third_party/blink/renderer/platform/text/character.h +++ b/third_party/blink/renderer/platform/text/character.h
@@ -319,11 +319,13 @@ } inline bool Character::MayNeedEastAsianSpacing(UChar32 ch) { - // `EastAsianSpacingType::kWide` may need the spacing. - // U+2000-206F General Punctuation has rather popular characters, such as ZWSP - // and curly quotation marks. Exclude the largest range of non-`kWide` that - // include them. - return ch >= 0x02C7 && !IsInRange(ch, 0x1200, 0x3004); + // `EastAsianSpacingType::kWide` may need the spacing. U+02C7 is the minimum + // code point of `kWide`. + return ch >= 0x02C7 && ch != kObjectReplacementCharacter && + // U+2000-206F General Punctuation has rather popular characters, such + // as ZWSP and curly quotation marks. Exclude the largest range of + // non-`kWide` that include them. + !IsInRange(ch, 0x1200, 0x3004); } } // namespace blink
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py index 0a7e2925..80f68bd3 100755 --- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py +++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -2393,8 +2393,6 @@ ], 'allowed': [ 'attribution_reporting::features::.*', - # TODO(crbug.com/385173568): Remove after AIPromptAPIForExtension OT. - 'base::CommandLine', ] }, {
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 31d75ea4..60e3e77 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -9330,7 +9330,7 @@ # Gardener 2025-03-06 crbug.com/400726001 [ Win ] external/wpt/css/css-ruby/ruby-text-combine-upright-002b.html [ Failure Pass ] crbug.com/400726001 [ Mac14 ] external/wpt/css/css-ruby/ruby-text-combine-upright-002b.html [ Failure Pass ] -crbug.com/395503013 [ Mac ] external/wpt/soft-navigation-heuristics/first-interaction-not-softnav.tentative.html [ Pass Timeout ] +crbug.com/395503013 [ Mac ] external/wpt/soft-navigation-heuristics/first-interaction-not-softnav.tentative.html [ Failure Pass Timeout ] # Gardener 2025-03-07 crbug.com/398165466 [ Mac12 ] http/tests/inspector-protocol/tracing/screenshots.js [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-bidirectional-shorthands.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-bidirectional-shorthands.html index 9ff3815..2ace9f2 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-bidirectional-shorthands.html +++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-bidirectional-shorthands.html
@@ -44,6 +44,11 @@ test(function() { assert_equals(window.getComputedStyle(document.getElementById('target1')).getPropertyValue('rule-style'), 'solid'); }, "rule-style shorthand computed from longhand values"); + test(function() { + assert_equals(window.getComputedStyle(document.getElementById('target1')).getPropertyValue('rule'), '10px solid rgb(0, 255, 0)'); + }, "rule shorthand computed from longhand values"); + + test(function() { assert_equals(window.getComputedStyle(document.getElementById('target2')).getPropertyValue('rule-color'), ''); @@ -54,6 +59,9 @@ test(function() { assert_equals(window.getComputedStyle(document.getElementById('target2')).getPropertyValue('rule-style'), ''); }, "rule-style shorthand cannot be computed from longhand values so expect an empty string"); + test(function() { + assert_equals(window.getComputedStyle(document.getElementById('target2')).getPropertyValue('rule'), ''); + }, "rule shorthand cannot be computed from longhand values so expect an empty string"); </script> </body> </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand-computed.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand-computed.html index 686560d..63e108d 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand-computed.html +++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand-computed.html
@@ -23,8 +23,7 @@ } </style> <script> -// TODO(samomekarajr): Add `rule` to this test when implemented. -const properties = ["column-rule", "row-rule"]; +const properties = ["column-rule", "row-rule", "rule"]; for (let property of properties) { const currentcolor = "rgb(0, 255, 0)"; const mediumWidth = getComputedStyle(document.getElementById('reference')).columnRuleWidth; // e.g. 3px.
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand-invalid.html index b8ba8b0..bbc347c 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand-invalid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand-invalid.html
@@ -12,8 +12,7 @@ </head> <body> <script> -// TODO(samomekarajr): Add `rule` to this test. -const properties = ["column-rule", "row-rule"]; +const properties = ["column-rule", "row-rule", "rule"]; for (let property of properties) { test_invalid_value(property, "auto");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand-valid.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand-valid.html index 9b37ed7b..cc05cf3 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand-valid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand-valid.html
@@ -13,8 +13,7 @@ <body> <script> -// TODO(samomekarajr): Add `rule` to this test. -const properties = ["column-rule", "row-rule"]; +const properties = ["column-rule", "row-rule", "rule"]; for (let property of properties) { // <gap-rule> = [<line-width> || <line-style> || <line-color>] test_valid_value(property, "5px solid red");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand.html b/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand.html index f65ab34..c6c2a37 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand.html +++ b/third_party/blink/web_tests/external/wpt/css/css-gaps/parsing/gap-decorations-rule-shorthand.html
@@ -18,111 +18,177 @@ 'row-rule': ['row-rule-width', 'row-rule-style', 'row-rule-color'], + 'rule': [ + ['column-rule-width', 'row-rule-width'], + ['column-rule-style', 'row-rule-style'], + ['column-rule-color', 'row-rule-color'], + ] + }; -for(rule_property in rule_properties) { - const [width, style, color] = rule_properties[rule_property]; +const testCases = [ // <gap-rule> = [<line-width> || <line-style> || <line-color>]. - test_shorthand_value(rule_property, '5px solid red', { - [width]: '5px', - [style]: 'solid', - [color]: 'red' - }); + { + input: '5px solid red', + expected: { + width: '5px', + style: 'solid', + color: 'red' + } + }, + { + input: 'double', + expected: { + width: 'medium', + style: 'double', + color: 'currentcolor' + } + }, + { + input: 'blue 10px', + expected: { + width: '10px', + style: 'none', + color: 'blue' + } + }, - test_shorthand_value(rule_property, 'double', { - [width]: 'medium', - [style]: 'double', - [color]: 'currentcolor' - }); + // <gap-auto-repeat-rule> = repeat(auto, <gap-rule># ). + { + input: 'repeat(auto, 5px solid green)', + expected: { + width: 'repeat(auto, 5px)', + style: 'repeat(auto, solid)', + color: 'repeat(auto, green)' + } + }, + { + input: 'repeat(auto, 5px solid yellow, 10px dotted blue)', + expected: { + width: 'repeat(auto, 5px 10px)', + style: 'repeat(auto, solid dotted)', + color: 'repeat(auto, yellow blue)' + } + }, + { + input: 'repeat(auto, blue 6px, 5px solid red)', + expected: { + width: 'repeat(auto, 6px 5px)', + style: 'repeat(auto, none solid)', + color: 'repeat(auto, blue red)' + } + }, - test_shorthand_value(rule_property, 'blue 10px', { - [width]: '10px', - [style]: 'none', - [color]: 'blue' - }); - - // <gap-auto-repeat-rule> = repeat( auto , <gap-rule># ). - test_shorthand_value(rule_property, 'repeat(auto, 5px solid green)', { - [width]: 'repeat(auto, 5px)', - [style]: 'repeat(auto, solid)', - [color]: 'repeat(auto, green)' - }); - - test_shorthand_value(rule_property, 'repeat(auto, 5px solid yellow, 10px dotted blue)', { - [width]: 'repeat(auto, 5px 10px)', - [style]: 'repeat(auto, solid dotted)', - [color]: 'repeat(auto, yellow blue)' - }); - - test_shorthand_value(rule_property, 'repeat(auto, blue 6px, 5px solid red)', { - [width]: 'repeat(auto, 6px 5px)', - [style]: 'repeat(auto, none solid)', - [color]: 'repeat(auto, blue red)' - }); - - // <gap-repeat-rule> = repeat( <integer [1,∞]> , <gap-rule># ). - test_shorthand_value(rule_property, 'repeat(4, 15px dotted pink)', { - [width]: 'repeat(4, 15px)', - [style]: 'repeat(4, dotted)', - [color]: 'repeat(4, pink)' - }); - test_shorthand_value(rule_property, 'repeat(1, 15px ridge yellow, 10px dotted blue, 15px double green)', { - [width]: 'repeat(1, 15px 10px 15px)', - [style]: 'repeat(1, ridge dotted double)', - [color]: 'repeat(1, yellow blue green)' - }); - test_shorthand_value(rule_property, 'repeat(3, lime 16px, dashed purple, 10px dotted)', { - [width]: 'repeat(3, 16px medium 10px)', - [style]: 'repeat(3, none dashed dotted)', - [color]: 'repeat(3, lime purple currentcolor)' - }); + // <gap-repeat-rule> = repeat(<integer [1,∞]>, <gap-rule># ). + { + input: 'repeat(4, 15px dotted pink)', + expected: { + width: 'repeat(4, 15px)', + style: 'repeat(4, dotted)', + color: 'repeat(4, pink)' + } + }, + { + input: 'repeat(1, 15px ridge yellow, 10px dotted blue, 15px double green)', + expected: { + width: 'repeat(1, 15px 10px 15px)', + style: 'repeat(1, ridge dotted double)', + color: 'repeat(1, yellow blue green)' + } + }, + { + input: 'repeat(3, lime 16px, dashed purple, 10px dotted)', + expected: { + width: 'repeat(3, 16px medium 10px)', + style: 'repeat(3, none dashed dotted)', + color: 'repeat(3, lime purple currentcolor)' + } + }, // <gap-rule-list> = <gap-rule-or-repeat>#. // <gap-rule-or-repeat> = <gap-rule> | <gap-repeat-rule>. - test_shorthand_value(rule_property, 'thin, dashed, hotpink', { - [width]: 'thin medium medium', - [style]: 'none dashed none', - [color]: 'currentcolor currentcolor hotpink' - }); - test_shorthand_value(rule_property, '5px double salmon, repeat(4, 5px ridge red)', { - [width]: '5px repeat(4, 5px)', - [style]: 'double repeat(4, ridge)', - [color]: 'salmon repeat(4, red)' - }); - test_shorthand_value(rule_property, - 'repeat(2, dashed gray, 10px blue dotted, 20px double), 5px solid red, repeat(4, blue 6px, 5px solid white)', { - [width]: 'repeat(2, medium 10px 20px) 5px repeat(4, 6px 5px)', - [style]: 'repeat(2, dashed dotted double) solid repeat(4, none solid)', - [color]: 'repeat(2, gray blue currentcolor) red repeat(4, blue white)' - }); - test_shorthand_value(rule_property, 'repeat(4, thick hidden skyblue), repeat(3, 5px solid red, 10px dotted)', { - [width]: 'repeat(4, thick) repeat(3, 5px 10px)', - [style]: 'repeat(4, hidden) repeat(3, solid dotted)', - [color]: 'repeat(4, skyblue) repeat(3, red currentcolor)' - }); + { + input: 'thin, dashed, hotpink', + expected: { + width: 'thin medium medium', + style: 'none dashed none', + color: 'currentcolor currentcolor hotpink' + } + }, + { + input: '5px double salmon, repeat(4, 5px ridge red)', + expected: { + width: '5px repeat(4, 5px)', + style: 'double repeat(4, ridge)', + color: 'salmon repeat(4, red)' + } + }, + { + input: 'repeat(2, dashed gray, 10px blue dotted, 20px double), 5px solid red, repeat(4, blue 6px, 5px solid white)', + expected: { + width: 'repeat(2, medium 10px 20px) 5px repeat(4, 6px 5px)', + style: 'repeat(2, dashed dotted double) solid repeat(4, none solid)', + color: 'repeat(2, gray blue currentcolor) red repeat(4, blue white)' + } + }, + { + input: 'repeat(4, thick hidden skyblue), repeat(3, 5px solid red, 10px dotted)', + expected: { + width: 'repeat(4, thick) repeat(3, 5px 10px)', + style: 'repeat(4, hidden) repeat(3, solid dotted)', + color: 'repeat(4, skyblue) repeat(3, red currentcolor)' + } + }, // <gap-auto-rule-list> = <gap-rule-or-repeat>#? , // <gap-auto-repeat-rule> , // <gap-rule-or-repeat>#?. - test_shorthand_value(rule_property, - 'repeat(auto, 10px solid red), medium dotted green, repeat(3, thick dashed blue, 15px double green)', { - [width]: 'repeat(auto, 10px) medium repeat(3, thick 15px)', - [style]: 'repeat(auto, solid) dotted repeat(3, dashed double)', - [color]: 'repeat(auto, red) green repeat(3, blue green)' - }); + { + input: 'repeat(auto, 10px solid red), medium dotted green, repeat(3, thick dashed blue, 15px double green)', + expected: { + width: 'repeat(auto, 10px) medium repeat(3, thick 15px)', + style: 'repeat(auto, solid) dotted repeat(3, dashed double)', + color: 'repeat(auto, red) green repeat(3, blue green)' + } + }, + { + input: 'ridge red, repeat(auto, 5px solid green), 10px dotted blue', + expected: { + width: 'medium repeat(auto, 5px) 10px', + style: 'ridge repeat(auto, solid) dotted', + color: 'red repeat(auto, green) blue' + } + }, + { + input: '10px dotted salmon, repeat(4, thin blue, hidden 5px purple), repeat(auto, 5px solid red, teal)', + expected: { + width: '10px repeat(4, thin 5px) repeat(auto, 5px medium)', + style: 'dotted repeat(4, none hidden) repeat(auto, solid none)', + color: 'salmon repeat(4, blue purple) repeat(auto, red teal)' + } + } ]; - test_shorthand_value(rule_property, 'ridge red, repeat(auto, 5px solid green), 10px dotted blue', { - [width]: 'medium repeat(auto, 5px) 10px', - [style]: 'ridge repeat(auto, solid) dotted', - [color]: 'red repeat(auto, green) blue' - }); +for(rule_property in rule_properties) { + const [width, style, color] = rule_properties[rule_property]; - test_shorthand_value(rule_property, - '10px dotted salmon, repeat(4, thin blue, hidden 5px purple), repeat(auto, 5px solid red, teal)', { - [width]: '10px repeat(4, thin 5px) repeat(auto, 5px medium)', - [style]: 'dotted repeat(4, none hidden) repeat(auto, solid none)', - [color]: 'salmon repeat(4, blue purple) repeat(auto, red teal)' - }); + for (const { input, expected } of testCases) { + if (rule_property === 'rule') { + test_shorthand_value(rule_property, input, { + [width[0]]: expected.width, + [width[1]]: expected.width, + [style[0]]: expected.style, + [style[1]]: expected.style, + [color[0]]: expected.color, + [color[1]]: expected.color + }); + } else { + test_shorthand_value(rule_property, input, { + [width]: expected.width, + [style]: expected.style, + [color]: expected.color + }); + } + } } </script> </body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/input/paged.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/input/paged.html new file mode 100644 index 0000000..ef55259 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-snap/input/paged.html
@@ -0,0 +1,179 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type" /> +<title>Page scroll snapping</title> +<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1"> +<meta name="flags" content="should"> +<meta name="assert" + content="Test passes if page operation doesn't skip content"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/dom/events/scrolling/scroll_support.js"></script> +<script src="../support/common.js"></script> + +<style> +html, body { + margin: 0; +} +.scroller { + height: 100vh; + overflow: auto; + position: relative; + scroll-snap-type: y mandatory; + counter-reset: --page; +} + +.gap { + height: 100vh; +} + +.page { + counter-increment: --page; + height: 90vh; + scroll-snap-align: center; + padding: 8px; + position: relative; + --page: counter(--page); +} +.short { + height: 25vh; +} +.page > div::before { + content: "Page " counter(--page); + font-size: 1.5em; +} +.page > div { + box-sizing: border-box; + border: 3px solid black; + border-radius: 5px; + overflow: clip; /* Make sure font size doesn't cause pages to be larger than expected. */ + padding: 8px; + height: 100%; +} +</style> +<div class="scroller" tabindex="0"> + <div class="page"> + <div> + <p>This tests what happens when you perform a paging scroll (e.g. space bar or page down key) with mandatory scroll snap.</p> + <p>When snapped to this page, pressing page down should not skip page 2.</p> + </div> + </div> + <div class="short page"> + <div> + <p>This page should not be skipped by paging scroll operations.</p> + </div> + </div> + <div class="page"> + <div> + <p>We must stop at this page before going to page 4.</p> + </div> + </div> + <div class="short page"> + <div> + <p>Pages 4, 5, and 6 should be a single snap stop on page 5.</p> + </div> + </div> + <div class="short page"> + <div> + <p> + This should be the snapped page when paging. + The next page operation should jump to page 7. + </p> + </div> + </div> + <div class="short page"> + <div></div> + </div> + <div class="page"> + <div> + <p> + The next page is further than a page away, + but there are no closer snap points + so it should be scrolled to next. + </p> + </div> + </div> + <div class="gap"></div> + <div class="page"> + <div> + <p> + The last page + </p> + </div> + </div> +</div> + +<script> +const scroller = document.querySelector(".scroller"); + +scrollTop = () => scroller.scrollTop; + +async function snapTo(page) { + if (page == 1 && scroller.scrollTop == 0) + return; + let scrollEndPromise = waitForScrollEndFallbackToDelayWithoutScrollEvent(scroller); + scroller.scrollTop = 0; + await scrollEndPromise; + if (page > 1) { + scrollEndPromise = waitForScrollEndFallbackToDelayWithoutScrollEvent(scroller); + scroller.querySelector(`.page[data-page="${page}"]`).scrollIntoView({block: "center"}); + await scrollEndPromise; + } +} + +scroller.querySelectorAll('.page').forEach((div, index) => { + div.setAttribute("data-page", index + 1); +}); +function visiblePages() { + return Array.prototype.slice.apply( + scroller.querySelectorAll('.page')).filter( + div => div.offsetTop >= scroller.scrollTop && + div.offsetTop + div.offsetHeight <= scroller.scrollTop + scroller.clientHeight).map( + div => parseInt(div.getAttribute("data-page"))); +} + +async function pageDown() { + const scrollEndPromise = waitForScrollEndFallbackToDelayWithoutScrollEvent(scroller); + await keyPress(scroller, "Space"); + await scrollEndPromise; +} + +promise_test(async t => { + await snapTo(1); + assert_array_equals(visiblePages(), [1]); + await pageDown(); + assert_array_equals(visiblePages(), [2]); +}, `Doesn't skip past small snappable content`); + +promise_test(async t => { + await snapTo(2); + assert_array_equals(visiblePages(), [2]); + await pageDown(); + assert_array_equals(visiblePages(), [3]); +}, `Doesn't skip past large snappable content`); + +promise_test(async t => { + await snapTo(3); + assert_array_equals(visiblePages(), [3]); + await pageDown(); + assert_array_equals(visiblePages(), [4, 5, 6]); +}, `Scrolls multiple smaller items into view`); + +promise_test(async t => { + await snapTo(5); + assert_array_equals(visiblePages(), [4, 5, 6]); + await pageDown(); + assert_array_equals(visiblePages(), [7]); +}, `Scrolls past items currently in view`); + +promise_test(async t => { + await snapTo(7); + assert_array_equals(visiblePages(), [7]); + await pageDown(); + assert_array_equals(visiblePages(), [8]); +}, `Scrolls more than a page if necessary`); + +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-autospace/text-autospace-mixed-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-autospace/text-autospace-mixed-001-ref.html new file mode 100644 index 0000000..be782ab --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-autospace/text-autospace-mixed-001-ref.html
@@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<link rel="help" href="https://drafts.csswg.org/css-text-4/#text-autospace-property"> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> +<style> +#container > div { + font-family: Ahem; + font-size: 40px; + text-autospace: no-autospace; + + & > span { + margin-left: calc(1em / 8); + margin-right: calc(1em / 8); + } +} +</style> +<div id="container"> + <div>国国<span>אבג</span>国国</div> + <div>国国<span>אבג</span>国国</div> + <div>国国<span>مُرَبَّعْ</span>国国</div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-autospace/text-autospace-mixed-001.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-autospace/text-autospace-mixed-001.html index 731f7456..9e35ce0 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-text/text-autospace/text-autospace-mixed-001.html +++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-autospace/text-autospace-mixed-001.html
@@ -1,47 +1,16 @@ <!DOCTYPE html> <meta charset="utf-8"> <link rel="help" href="https://drafts.csswg.org/css-text-4/#text-autospace-property"> +<link rel="match" href="text-autospace-mixed-001-ref.html"> <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="../support/get-char-advances.js"></script> <style> -.test { +#container > div { font-family: Ahem; font-size: 40px; } -.no-autospace { - text-autospace: no-autospace; -} </style> <div id="container"> - <div class="test" expect="1,5">国国אבג国国</div> - <div class="test" expect="1,5">国国<span>אב</span>ג国国</div> - <div class="test" expect="1,11">国国مُرَبَّعْ国国</div> + <div>国国אבג国国</div> + <div>国国<span>אב</span>ג国国</div> + <div>国国مُرَبَّعْ国国</div> </div> -<script> -// Compute expected advances from advances without `text-autospace` and the -// `expect` attribute. -const container = document.getElementById('container'); -container.classList.add('no-autospace'); -const tests = []; -for (const element of document.getElementsByClassName('test')) { - const em = parseFloat(getComputedStyle(element).fontSize); - const spacing = em / 8; - const advances = getCharAdvances(element); - const expect = element.getAttribute('expect').split(',').map(i => parseInt(i)); - for (const i of expect) { - advances[i] += spacing; - } - tests.push({element: element, advances: advances}); -} - -// Apply `text-autospace` and compare the actual advances. -container.classList.remove('no-autospace'); -for (const t of tests) { - const advances = getCharAdvances(t.element); - test(() => { - assert_array_equals(advances, t.advances); - }) -} -</script>
diff --git a/third_party/blink/web_tests/external/wpt/lint.ignore b/third_party/blink/web_tests/external/wpt/lint.ignore index b391808..10b5dad 100644 --- a/third_party/blink/web_tests/external/wpt/lint.ignore +++ b/third_party/blink/web_tests/external/wpt/lint.ignore
@@ -263,6 +263,7 @@ SET TIMEOUT: reporting/resources/first-csp-report.https.sub.html SET TIMEOUT: reporting/resources/second-csp-report.https.sub.html SET TIMEOUT: scheduler/tentative/yield/yield-inherit-across-promises.any.js +SET TIMEOUT: scheduler/tentative/yield/yield-scheduling-state-cleared.any.js SET TIMEOUT: scheduler/tentative/yield/yield-priority-timers.any.js SET TIMEOUT: secure-contexts/basic-popup-and-iframe-tests.https.js SET TIMEOUT: service-workers/cache-storage/cache-abort.https.any.js
diff --git a/third_party/blink/web_tests/external/wpt/scheduler/tentative/yield/yield-scheduling-state-cleared.any.js b/third_party/blink/web_tests/external/wpt/scheduler/tentative/yield/yield-scheduling-state-cleared.any.js new file mode 100644 index 0000000..8e6d2ee --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/scheduler/tentative/yield/yield-scheduling-state-cleared.any.js
@@ -0,0 +1,21 @@ +'use strict'; + +promise_test(async t => { + const ids = []; + // The timer task will run after the background task, and the scheduling state + // set in the background task should not leak to the timer task. + const {promise, resolve} = Promise.withResolvers(); + scheduler.postTask(async () => { + setTimeout(async () => { + let task = scheduler.postTask(() => { + ids.push('task'); + }, {priority: 'user-visible'}); + await scheduler.yield(); + ids.push('continuation'); + await task; + resolve(); + }); + }, {priority: 'background'}); + await promise; + assert_equals(ids.toString(), 'continuation,task'); +}, 'yield() does not leak priority across tasks');
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/abs.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/abs.https.any.js index de6a576..ea4370c 100644 --- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/abs.https.any.js +++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/abs.https.any.js
@@ -591,6 +591,34 @@ } } } + }, + + // int64 tests + { + 'name': 'abs int64 4D tensor', + 'graph': { + 'inputs': { + 'absInput': { + 'data': [ + // int64 range: [/* -(2**63) */ –9223372036854775808, + // /* 2**63 - 1 */ 92233720368547758087] + BigInt(-(2**63)) + 1n, -100n, 0n, 100n, BigInt(2**63) - 1n + ], + 'descriptor': {shape: [1, 1, 1, 5], dataType: 'int64'} + } + }, + 'operators': [{ + 'name': 'abs', + 'arguments': [{'input': 'absInput'}], + 'outputs': 'absOutput' + }], + 'expectedOutputs': { + 'absOutput': { + 'data': [BigInt(2**63) - 1n, 100n, 0n, 100n, BigInt(2**63) - 1n], + 'descriptor': {shape: [1, 1, 1, 5], dataType: 'int64'} + } + } + } } ];
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/neg.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/neg.https.any.js index 8bc1047..5c5045d3 100644 --- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/neg.https.any.js +++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/neg.https.any.js
@@ -604,6 +604,34 @@ } } } + }, + + // int64 tests + { + 'name': 'neg int64 4D tensor', + 'graph': { + 'inputs': { + 'negInput': { + 'data': [ + // int64 range: [/* -(2**63) */ –9223372036854775808, + // /* 2**63 - 1 */ 92233720368547758087] + BigInt(-(2**63)) + 1n, -100n, 0n, 100n, BigInt(2**63) - 1n + ], + 'descriptor': {shape: [1, 1, 1, 5], dataType: 'int64'} + } + }, + 'operators': [{ + 'name': 'neg', + 'arguments': [{'input': 'negInput'}], + 'outputs': 'negOutput' + }], + 'expectedOutputs': { + 'negOutput': { + 'data': [BigInt(2**63) - 1n, 100n, 0, -100n, BigInt(-(2**63)) + 1n], + 'descriptor': {shape: [1, 1, 1, 5], dataType: 'int64'} + } + } + } } ];
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/prelu.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/prelu.https.any.js index cfd043c..cc6e0052 100644 --- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/prelu.https.any.js +++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/prelu.https.any.js
@@ -1207,6 +1207,36 @@ } } } + }, + + // int64 tests + { + 'name': 'prelu int64 2D constant tensors', + 'graph': { + 'inputs': { + 'preluInput': { + 'data': [-4, -2, -1, 0, 0, 0, 1, 2, 4], + 'descriptor': {shape: [3, 3], dataType: 'int64'}, + 'constant': true + }, + 'preluSlope': { + 'data': [-5, 0, 5, -5, 0, 5, -5, 0, 5], + 'descriptor': {shape: [3, 3], dataType: 'int64'}, + 'constant': true + } + }, + 'operators': [{ + 'name': 'prelu', + 'arguments': [{'input': 'preluInput'}, {'slope': 'preluSlope'}], + 'outputs': 'preluOutput' + }], + 'expectedOutputs': { + 'preluOutput': { + 'data': [20, 0, -5, 0, 0, 0, 1, 2, 4], + 'descriptor': {shape: [3, 3], dataType: 'int64'} + } + } + } } ];
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/relu.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/relu.https.any.js index 63ef9fa..05e9e32 100644 --- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/relu.https.any.js +++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/relu.https.any.js
@@ -636,6 +636,34 @@ } } }, + + // int64 tests + { + 'name': 'relu int64 4D tensor', + 'graph': { + 'inputs': { + 'reluInput': { + 'data': [ + // int64 range: [/* -(2**63) */ –9223372036854775808, + // /* 2**63 - 1 */ 92233720368547758087] + BigInt(-(2**63)) + 1n, -100n, 0n, 100n, BigInt(2**63) - 1n + ], + 'descriptor': {shape: [1, 1, 1, 5], dataType: 'int64'} + } + }, + 'operators': [{ + 'name': 'relu', + 'arguments': [{'input': 'reluInput'}], + 'outputs': 'reluOutput' + }], + 'expectedOutputs': { + 'reluOutput': { + 'data': [0n, 0n, 0n, 100n, BigInt(2**63) - 1n], + 'descriptor': {shape: [1, 1, 1, 5], dataType: 'int64'} + } + } + } + } ]; if (navigator.ml) {
diff --git a/third_party/blink/web_tests/fast/scroll-snap/snaps-for-different-key-granularity.html b/third_party/blink/web_tests/fast/scroll-snap/snaps-for-different-key-granularity.html index 43a4d4a..e2818587 100644 --- a/third_party/blink/web_tests/fast/scroll-snap/snaps-for-different-key-granularity.html +++ b/third_party/blink/web_tests/fast/scroll-snap/snaps-for-different-key-granularity.html
@@ -72,45 +72,71 @@ }) } +async function scrollTo(x, y) { + if (scroller.scrollLeft == x && scroller.scrollTop == y) + return; + const scrollend = scrollEndPromise(); + scroller.scrollTo(x, y); + await scrollend; +} + +function scrollEndPromise() { + return new Promise((resolve) => { + scroller.addEventListener('scrollend', resolve, {once: true}); + }); +} + promise_test (async () => { await mouseClickOn(10, 10); - scroller.scrollTo(0, 0); + await scrollTo(0, 0); + const scrollEnd = scrollEndPromise(); await keyPress("ArrowDown"); - await waitForScrollEnd(scroller, scrollTop, 200); + await scrollEnd; + assert_approx_equals(scroller.scrollTop, 200, 1); }, "Snaps to page1-line1 after pressing ArrowDown at page1."); promise_test (async () => { await mouseClickOn(10, 10); - scroller.scrollTo(0, 1200); + await scrollTo(0, 1200); + const scrollEnd = scrollEndPromise(); await keyPress("ArrowUp"); - await waitForScrollEnd(scroller, scrollTop, 1000); + await scrollEnd; + assert_approx_equals(scroller.scrollTop, 1000, 1); }, "Snaps to page2-line2 after pressing ArrowUp at page3."); promise_test (async () => { await mouseClickOn(10, 10); - scroller.scrollTo(0, 0); + await scrollTo(0, 0); + const scrollEnd = scrollEndPromise(); await keyPress("PageDown"); - await waitForScrollEnd(scroller, scrollTop, 600); + await scrollEnd; + assert_approx_equals(scroller.scrollTop, 600, 1); }, "Snaps to page2 after pressing PageDown at page1."); promise_test (async () => { await mouseClickOn(10, 10); - scroller.scrollTo(0, 1200); + await scrollTo(0, 1200); + const scrollEnd = scrollEndPromise(); await keyPress("PageUp"); - await waitForScrollEnd(scroller, scrollTop, 600); -}, "Snaps to page2 after pressing PageUp at page3."); + await scrollEnd; + assert_approx_equals(scroller.scrollTop, 800, 1); +}, "Snaps to page2-line1 after pressing PageUp at page3."); promise_test (async () => { await mouseClickOn(10, 10); - scroller.scrollTo(0, 0); + await scrollTo(0, 0); + const scrollEnd = scrollEndPromise(); await keyPress("End"); - await waitForScrollEnd(scroller, scrollTop, 1200); + await scrollEnd; + assert_approx_equals(scroller.scrollTop, 1200, 1); }, "Snaps to page3 after pressing End at page1."); promise_test (async () => { await mouseClickOn(10, 10); - scroller.scrollTo(0, 1200); + await scrollTo(0, 1200); + const scrollEnd = scrollEndPromise(); await keyPress("Home"); - await waitForScrollEnd(scroller, scrollTop, 0); + await scrollEnd; + assert_approx_equals(scroller.scrollTop, 0, 1); }, "Snaps to page1 after pressing Home at page3."); </script>
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/neg.https.any_npu-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/neg.https.any_npu-expected.txt deleted file mode 100644 index 02ae569c..0000000 --- a/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/neg.https.any_npu-expected.txt +++ /dev/null
@@ -1,7 +0,0 @@ -This is a testharness.js-based test. -[FAIL] neg int8 4D tensor - promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16,int32]." -[FAIL] neg int32 4D tensor - assert_less_than_equal: assert_array_approx_equals_ulp: test neg int32 actual -2147483648 should be close enough to expected -2147483646 by ULP distance: expected a number less than or equal to 0 but got 2 -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/neg.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/neg.https.any_gpu-expected.txt deleted file mode 100644 index cca069b2..0000000 --- a/third_party/blink/web_tests/platform/mac-mac14-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/neg.https.any_gpu-expected.txt +++ /dev/null
@@ -1,7 +0,0 @@ -This is a testharness.js-based test. -[FAIL] neg int8 4D tensor - promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16,int32]." -[FAIL] neg int32 4D tensor - assert_less_than_equal: assert_array_approx_equals_ulp: test neg int32 actual -2147483648 should be close enough to expected -2147483646 by ULP distance: expected a number less than or equal to 0n but got 2n -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/abs.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/abs.https.any_cpu-expected.txt index 364ab1b..b55a7895 100644 --- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/abs.https.any_cpu-expected.txt +++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/abs.https.any_cpu-expected.txt
@@ -3,5 +3,7 @@ promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16,int32]." [FAIL] abs int32 4D tensor assert_less_than_equal: assert_array_approx_equals_ulp: test abs int32 actual 2147483647 should be close enough to expected 2147483646 by ULP distance: expected a number less than or equal to 0 but got 1 +[FAIL] abs int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16,int32]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/prelu.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/prelu.https.any_cpu-expected.txt index 5b37deb..8fc1924 100644 --- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/prelu.https.any_cpu-expected.txt +++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/prelu.https.any_cpu-expected.txt
@@ -1,4 +1,5 @@ This is a testharness.js-based test. -All subtests passed and are omitted for brevity. -See https://chromium.googlesource.com/chromium/src/+/HEAD/docs/testing/writing_web_tests.md#Text-Test-Baselines for details. +[FAIL] prelu int64 2D constant tensors + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, constant 'input' data type int64 must be one of [float32,float16,int32,uint32,int8,uint8,int4,uint4]." Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/relu.https.any_cpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/relu.https.any_cpu-expected.txt index b059e7c..57bc4c8 100644 --- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/relu.https.any_cpu-expected.txt +++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/relu.https.any_cpu-expected.txt
@@ -3,5 +3,7 @@ promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16,int32]." [FAIL] relu int32 4D tensor promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int32 must be one of [float32,float16]." +[FAIL] relu int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16,int32]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/abs.https.any_npu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/abs.https.any_npu-expected.txt index 364ab1b..b55a7895 100644 --- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/abs.https.any_npu-expected.txt +++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/abs.https.any_npu-expected.txt
@@ -3,5 +3,7 @@ promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16,int32]." [FAIL] abs int32 4D tensor assert_less_than_equal: assert_array_approx_equals_ulp: test abs int32 actual 2147483647 should be close enough to expected 2147483646 by ULP distance: expected a number less than or equal to 0 but got 1 +[FAIL] abs int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16,int32]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/prelu.https.any_npu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/prelu.https.any_npu-expected.txt index 5b37deb..8fc1924 100644 --- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/prelu.https.any_npu-expected.txt +++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/prelu.https.any_npu-expected.txt
@@ -1,4 +1,5 @@ This is a testharness.js-based test. -All subtests passed and are omitted for brevity. -See https://chromium.googlesource.com/chromium/src/+/HEAD/docs/testing/writing_web_tests.md#Text-Test-Baselines for details. +[FAIL] prelu int64 2D constant tensors + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, constant 'input' data type int64 must be one of [float32,float16,int32,uint32,int8,uint8,int4,uint4]." Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/relu.https.any_npu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/relu.https.any_npu-expected.txt index b059e7c..57bc4c8 100644 --- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/relu.https.any_npu-expected.txt +++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/relu.https.any_npu-expected.txt
@@ -3,5 +3,7 @@ promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16,int32]." [FAIL] relu int32 4D tensor promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int32 must be one of [float32,float16]." +[FAIL] relu int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16,int32]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/abs.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/abs.https.any_gpu-expected.txt index 364ab1b..b55a7895 100644 --- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/abs.https.any_gpu-expected.txt +++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/abs.https.any_gpu-expected.txt
@@ -3,5 +3,7 @@ promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16,int32]." [FAIL] abs int32 4D tensor assert_less_than_equal: assert_array_approx_equals_ulp: test abs int32 actual 2147483647 should be close enough to expected 2147483646 by ULP distance: expected a number less than or equal to 0 but got 1 +[FAIL] abs int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16,int32]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/prelu.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/prelu.https.any_gpu-expected.txt index 5b37deb..8fc1924 100644 --- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/prelu.https.any_gpu-expected.txt +++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/prelu.https.any_gpu-expected.txt
@@ -1,4 +1,5 @@ This is a testharness.js-based test. -All subtests passed and are omitted for brevity. -See https://chromium.googlesource.com/chromium/src/+/HEAD/docs/testing/writing_web_tests.md#Text-Test-Baselines for details. +[FAIL] prelu int64 2D constant tensors + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, constant 'input' data type int64 must be one of [float32,float16,int32,uint32,int8,uint8,int4,uint4]." Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/relu.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/relu.https.any_gpu-expected.txt index b059e7c..57bc4c8 100644 --- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/relu.https.any_gpu-expected.txt +++ b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/relu.https.any_gpu-expected.txt
@@ -3,5 +3,7 @@ promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16,int32]." [FAIL] relu int32 4D tensor promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int32 must be one of [float32,float16]." +[FAIL] relu int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16,int32]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/abs.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/abs.https.any_gpu-expected.txt index 9c9b0dc..37f7567 100644 --- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/abs.https.any_gpu-expected.txt +++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/abs.https.any_gpu-expected.txt
@@ -1,5 +1,7 @@ This is a testharness.js-based test. [FAIL] abs int8 4D tensor promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16,int32]." +[FAIL] abs int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16,int32]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/neg.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/neg.https.any_gpu-expected.txt index 0836101c..d0d0357 100644 --- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/neg.https.any_gpu-expected.txt +++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/neg.https.any_gpu-expected.txt
@@ -1,5 +1,7 @@ This is a testharness.js-based test. [FAIL] neg int8 4D tensor promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16,int32]." +[FAIL] neg int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16,int32]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/prelu.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/prelu.https.any_gpu-expected.txt index 604abb1b..45c7a67 100644 --- a/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/prelu.https.any_gpu-expected.txt +++ b/third_party/blink/web_tests/platform/mac/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/prelu.https.any_gpu-expected.txt
@@ -11,5 +11,7 @@ promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': The input and slope should have the same last dimension." [FAIL] prelu float16 broadcast 4D x 4D slope promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': The input and slope should have the same last dimension." +[FAIL] prelu int64 2D constant tensors + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/abs.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/abs.https.any_gpu-expected.txt new file mode 100644 index 0000000..12e52855 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/abs.https.any_gpu-expected.txt
@@ -0,0 +1,5 @@ +This is a testharness.js-based test. +[FAIL] abs int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16,int32,int8]." +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/neg.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/neg.https.any_gpu-expected.txt index 11e58aa..dca7842 100644 --- a/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/neg.https.any_gpu-expected.txt +++ b/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/neg.https.any_gpu-expected.txt
@@ -1,5 +1,7 @@ This is a testharness.js-based test. [FAIL] neg int32 4D tensor assert_less_than_equal: assert_array_approx_equals_ulp: test neg int32 actual -2147483648 should be close enough to expected -2147483646 by ULP distance: expected a number less than or equal to 0 but got 2 +[FAIL] neg int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16,int32,int8]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/prelu.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/prelu.https.any_gpu-expected.txt new file mode 100644 index 0000000..22edfb9 --- /dev/null +++ b/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/prelu.https.any_gpu-expected.txt
@@ -0,0 +1,5 @@ +This is a testharness.js-based test. +[FAIL] prelu int64 2D constant tensors + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16]." +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/abs.https.any_cpu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/abs.https.any_cpu-expected.txt index 9c9b0dc..37f7567 100644 --- a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/abs.https.any_cpu-expected.txt +++ b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/abs.https.any_cpu-expected.txt
@@ -1,5 +1,7 @@ This is a testharness.js-based test. [FAIL] abs int8 4D tensor promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16,int32]." +[FAIL] abs int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16,int32]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/neg.https.any_cpu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/neg.https.any_cpu-expected.txt index 0836101c..d0d0357 100644 --- a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/neg.https.any_cpu-expected.txt +++ b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/neg.https.any_cpu-expected.txt
@@ -1,5 +1,7 @@ This is a testharness.js-based test. [FAIL] neg int8 4D tensor promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16,int32]." +[FAIL] neg int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16,int32]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/prelu.https.any_cpu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/prelu.https.any_cpu-expected.txt index 604abb1b..45c7a67 100644 --- a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/prelu.https.any_cpu-expected.txt +++ b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/prelu.https.any_cpu-expected.txt
@@ -11,5 +11,7 @@ promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': The input and slope should have the same last dimension." [FAIL] prelu float16 broadcast 4D x 4D slope promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': The input and slope should have the same last dimension." +[FAIL] prelu int64 2D constant tensors + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/relu.https.any_cpu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/relu.https.any_cpu-expected.txt index 3d5939f6..19e2027 100644 --- a/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/relu.https.any_cpu-expected.txt +++ b/third_party/blink/web_tests/virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/relu.https.any_cpu-expected.txt
@@ -3,5 +3,7 @@ promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16]." [FAIL] relu int32 4D tensor promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int32 must be one of [float32,float16]." +[FAIL] relu int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/abs.https.any_npu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/abs.https.any_npu-expected.txt index 9c9b0dc..37f7567 100644 --- a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/abs.https.any_npu-expected.txt +++ b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/abs.https.any_npu-expected.txt
@@ -1,5 +1,7 @@ This is a testharness.js-based test. [FAIL] abs int8 4D tensor promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16,int32]." +[FAIL] abs int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16,int32]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/neg.https.any_npu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/neg.https.any_npu-expected.txt index 0836101c..d0d0357 100644 --- a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/neg.https.any_npu-expected.txt +++ b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/neg.https.any_npu-expected.txt
@@ -1,5 +1,7 @@ This is a testharness.js-based test. [FAIL] neg int8 4D tensor promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16,int32]." +[FAIL] neg int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16,int32]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/prelu.https.any_npu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/prelu.https.any_npu-expected.txt index 604abb1b..45c7a67 100644 --- a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/prelu.https.any_npu-expected.txt +++ b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/prelu.https.any_npu-expected.txt
@@ -11,5 +11,7 @@ promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': The input and slope should have the same last dimension." [FAIL] prelu float16 broadcast 4D x 4D slope promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'build' on 'MLGraphBuilder': The input and slope should have the same last dimension." +[FAIL] prelu int64 2D constant tensors + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/relu.https.any_npu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/relu.https.any_npu-expected.txt index 3d5939f6..19e2027 100644 --- a/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/relu.https.any_npu-expected.txt +++ b/third_party/blink/web_tests/virtual/webnn-service-on-npu/external/wpt/webnn/conformance_tests/relu.https.any_npu-expected.txt
@@ -3,5 +3,7 @@ promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16]." [FAIL] relu int32 4D tensor promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int32 must be one of [float32,float16]." +[FAIL] relu int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16]." Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/relu.https.any_gpu-expected.txt b/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/relu.https.any_gpu-expected.txt index 3d5939f6..19e2027 100644 --- a/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/relu.https.any_gpu-expected.txt +++ b/third_party/blink/web_tests/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/relu.https.any_gpu-expected.txt
@@ -3,5 +3,7 @@ promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int8 must be one of [float32,float16]." [FAIL] relu int32 4D tensor promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int32 must be one of [float32,float16]." +[FAIL] relu int64 4D tensor + promise_test: Unhandled rejection with value: object "TypeError: Unsupported data type, input 'input' data type int64 must be one of [float32,float16]." Harness: the test ran to completion.
diff --git a/third_party/glslang/src b/third_party/glslang/src index 32f71d7..9323100 160000 --- a/third_party/glslang/src +++ b/third_party/glslang/src
@@ -1 +1 @@ -Subproject commit 32f71d72a2643df675684e1795e987ac26e600df +Subproject commit 93231001597dad1149a5d035af30eda50b9e6b6c
diff --git a/third_party/googletest/src b/third_party/googletest/src index 16d4f8e..6aa03e6 160000 --- a/third_party/googletest/src +++ b/third_party/googletest/src
@@ -1 +1 @@ -Subproject commit 16d4f8eff6d7cefca6975d82a53f8fc995a6feb7 +Subproject commit 6aa03e6774f8cb70da277c56efb24b44ce29d8d7
diff --git a/third_party/lens_server_proto/README.chromium b/third_party/lens_server_proto/README.chromium index d9198677..42fc9233 100644 --- a/third_party/lens_server_proto/README.chromium +++ b/third_party/lens_server_proto/README.chromium
@@ -1,8 +1,8 @@ Name: Lens Protos Short Name: lens_overlay_proto URL: This is the canonical public repository -Version: 741226731 -Date: 2025-03-27 +Version: 759676130 +Date: 2025-05-16 License: BSD-3-Clause License File: LICENSE Shipped: yes
diff --git a/third_party/lens_server_proto/lens_overlay_client_logs.proto b/third_party/lens_server_proto/lens_overlay_client_logs.proto index 101dd0a..b82333d0 100644 --- a/third_party/lens_server_proto/lens_overlay_client_logs.proto +++ b/third_party/lens_server_proto/lens_overlay_client_logs.proto
@@ -23,6 +23,9 @@ OMNIBOX_BUTTON = 4; TOOLBAR_BUTTON = 5; FIND_IN_PAGE = 6; + OMNIBOX_PAGE_ACTION = 7; + OMNIBOX_CONTEXTUAL_SUGGESTION = 8; + HOMEWORK_ACTION_CHIP = 9; } // The Lens Overlay entry point used to access lens.
diff --git a/third_party/perfetto b/third_party/perfetto index d15aa8f..fa368a6 160000 --- a/third_party/perfetto +++ b/third_party/perfetto
@@ -1 +1 @@ -Subproject commit d15aa8fb5bed6a9e04f7f93ebdc3573b6f7a366e +Subproject commit fa368a66f714203d1232ff62ece403bd2f5e7c10
diff --git a/third_party/skia b/third_party/skia index 3b2d2d0..18b85ac 160000 --- a/third_party/skia +++ b/third_party/skia
@@ -1 +1 @@ -Subproject commit 3b2d2d0f73fcc9468a3bced6d6d17a56411a87f4 +Subproject commit 18b85aced9b7b381ce184703bdeeae53a2fbe294
diff --git a/third_party/spirv-tools/src b/third_party/spirv-tools/src index 736e415..0102146 160000 --- a/third_party/spirv-tools/src +++ b/third_party/spirv-tools/src
@@ -1 +1 @@ -Subproject commit 736e415ebaa4290d21e42e370db5e933b9dff06d +Subproject commit 01021466b5e71deaac9054f56082566c782bfd51
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps index 12211ed..d8d0687 160000 --- a/third_party/vulkan-deps +++ b/third_party/vulkan-deps
@@ -1 +1 @@ -Subproject commit 12211edbca712355258047ef4f0de13f1da23ac3 +Subproject commit d8d0687affb24a8069b74b50af9cd9245a4af273
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src index 7760c965..a440433 160000 --- a/third_party/vulkan-validation-layers/src +++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@ -Subproject commit 7760c965ea96a51e7d66433dc9f97b4cac7804f7 +Subproject commit a44043332c975777a7196406f08601f6099b24f0
diff --git a/third_party/webrtc b/third_party/webrtc index 0f1742e..59d57888 160000 --- a/third_party/webrtc +++ b/third_party/webrtc
@@ -1 +1 @@ -Subproject commit 0f1742e458d2b187a6bf4470352acb6d7387274e +Subproject commit 59d578881fea163b8bd63aa056ed38feefd273de
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index 61330e4..0fce9a8 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -48450,7 +48450,8 @@ The code that caused the issue this action was meant to track is no longer present. </obsolete> - <owner>bialpio@chromium.org</owner> + <owner>alcooper@chromium.org</owner> + <owner>xr-dev@chromium.org</owner> <description> Raised when XR service was unable to obtain necessary component. The action is not directly raised by the user's behavior as it should only be logged
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index dde00ed..60690a37 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -10093,6 +10093,8 @@ label="SupervisedUserCommittedInterstitials:disabled"/> <int value="-1506664186" label="SafeBrowsingGooglePlayProtectPrompt:disabled"/> + <int value="-1505988176" + label="AutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment:enabled"/> <int value="-1505836593" label="ImprovedIncognitoScreenshot:disabled"/> <int value="-1505222604" label="ClearIdentityInCanMakePaymentEvent:disabled"/> <int value="-1505076171" label="AutomaticUsbDetach:enabled"/> @@ -18245,6 +18247,8 @@ <int value="1570178909" label="NewOverviewLayout:enabled"/> <int value="1570786077" label="EnableFingerprintingProtectionFilter:enabled"/> <int value="1571640975" label="EcheLauncherIconsInMoreAppsButton:enabled"/> + <int value="1571706485" + label="AutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment:disabled"/> <int value="1571998166" label="DetectingHeavyPages:disabled"/> <int value="1572371872" label="MostVisitedTilesNewScoring:enabled"/> <int value="1572464760" label="pwa-update-dialog-for-name-and-icon:disabled"/>
diff --git a/tools/metrics/histograms/metadata/blink/enums.xml b/tools/metrics/histograms/metadata/blink/enums.xml index 6989c46..99aaff8 100644 --- a/tools/metrics/histograms/metadata/blink/enums.xml +++ b/tools/metrics/histograms/metadata/blink/enums.xml
@@ -7808,6 +7808,7 @@ <int value="855" label="row-rule"/> <int value="856" label="text-grow"/> <int value="857" label="text-shrink"/> + <int value="858" label="rule"/> </enum> <!-- LINT.ThenChange(//third_party/blink/public/mojom/use_counter/metrics/css_property_id.mojom:CSSSampleId) -->
diff --git a/tools/metrics/histograms/metadata/gpu/histograms.xml b/tools/metrics/histograms/metadata/gpu/histograms.xml index b6bd4a0b..ff0d50db 100644 --- a/tools/metrics/histograms/metadata/gpu/histograms.xml +++ b/tools/metrics/histograms/metadata/gpu/histograms.xml
@@ -1966,7 +1966,7 @@ <histogram name="Viz.FrameSinkVideoCapturer.CaptureDuration" units="ms" expires_after="2023-07-15"> - <owner>bialpio@chromium.org</owner> + <owner>jophba@chromium.org</owner> <owner>media-capture-dev@chromium.org</owner> <summary> The time it took from when FrameSinkVideoCapturerImpl sent a request for @@ -1979,7 +1979,7 @@ <histogram name="Viz.FrameSinkVideoCapturer.CaptureSucceeded" enum="BooleanSuccess" expires_after="2023-07-15"> - <owner>bialpio@chromium.org</owner> + <owner>jophba@chromium.org</owner> <owner>media-capture-dev@chromium.org</owner> <summary> Whether a capture initiated by FrameSinkVideoCapturerImpl succeeded. @@ -1991,7 +1991,7 @@ <histogram name="Viz.FrameSinkVideoCapturer.FrameResurrected" enum="Boolean" expires_after="2023-07-15"> - <owner>bialpio@chromium.org</owner> + <owner>jophba@chromium.org</owner> <owner>media-capture-dev@chromium.org</owner> <summary> True if the capturer has used a resurrected video frame, thus avoiding @@ -2001,7 +2001,7 @@ <histogram name="Viz.FrameSinkVideoCapturer.I420.CaptureDuration" units="ms" expires_after="2024-09-29"> - <owner>bialpio@chromium.org</owner> + <owner>jophba@chromium.org</owner> <owner>media-capture-dev@chromium.org</owner> <summary> The time it took from when FrameSinkVideoCapturerImpl sent a request for an @@ -2015,7 +2015,7 @@ <histogram name="Viz.FrameSinkVideoCapturer.I420.CaptureSucceeded" enum="BooleanSuccess" expires_after="2025-05-26"> - <owner>bialpio@chromium.org</owner> + <owner>jophba@chromium.org</owner> <owner>media-capture-dev@chromium.org</owner> <summary> Whether an I420 readback initiated by FrameSinkVideoCapturerImpl succeeded. @@ -2027,7 +2027,7 @@ <histogram name="Viz.FrameSinkVideoCapturer.I420.TotalDuration" units="ms" expires_after="2024-09-29"> - <owner>bialpio@chromium.org</owner> + <owner>jophba@chromium.org</owner> <owner>media-capture-dev@chromium.org</owner> <summary> The time it took from when FrameSinkVideoCapturerImpl decided that a new @@ -2039,7 +2039,7 @@ <histogram name="Viz.FrameSinkVideoCapturer.NV12.CaptureDuration" units="ms" expires_after="2023-07-15"> - <owner>bialpio@chromium.org</owner> + <owner>jophba@chromium.org</owner> <owner>media-capture-dev@chromium.org</owner> <summary> The time it took from when FrameSinkVideoCapturerImpl sent a request for an @@ -2054,7 +2054,7 @@ <histogram name="Viz.FrameSinkVideoCapturer.NV12.CaptureSucceeded" enum="BooleanSuccess" expires_after="2023-07-15"> - <owner>bialpio@chromium.org</owner> + <owner>jophba@chromium.org</owner> <owner>media-capture-dev@chromium.org</owner> <summary> Whether an NV12 capture initiated by FrameSinkVideoCapturerImpl succeeded. @@ -2063,7 +2063,7 @@ <histogram name="Viz.FrameSinkVideoCapturer.NV12.TotalDuration" units="ms" expires_after="2023-07-15"> - <owner>bialpio@chromium.org</owner> + <owner>jophba@chromium.org</owner> <owner>media-capture-dev@chromium.org</owner> <summary> The time it took from when FrameSinkVideoCapturerImpl decided that a new @@ -2075,7 +2075,7 @@ <histogram name="Viz.FrameSinkVideoCapturer.ReserveFrameDuration" units="ms" expires_after="2023-07-15"> - <owner>bialpio@chromium.org</owner> + <owner>jophba@chromium.org</owner> <owner>media-capture-dev@chromium.org</owner> <summary> The time it took for a frame pool to reserve a video frame that would then @@ -2087,7 +2087,6 @@ <histogram name="Viz.FrameSinkVideoCapturer.RGBA.CaptureDuration" units="ms" expires_after="2023-07-15"> <owner>jonross@chromium.org</owner> - <owner>bialpio@chromium.org</owner> <owner>viz-team-wat@google.com</owner> <summary> The time it took from when FrameSinkVideoCapturerImpl sent a request for an @@ -2098,7 +2097,7 @@ <histogram name="Viz.FrameSinkVideoCapturer.TotalDuration" units="ms" expires_after="2023-07-15"> - <owner>bialpio@chromium.org</owner> + <owner>jophba@chromium.org</owner> <owner>media-capture-dev@chromium.org</owner> <summary> The time it took from when FrameSinkVideoCapturerImpl decided that a new
diff --git a/tools/metrics/histograms/metadata/lens/enums.xml b/tools/metrics/histograms/metadata/lens/enums.xml index bee07f9..353e558 100644 --- a/tools/metrics/histograms/metadata/lens/enums.xml +++ b/tools/metrics/histograms/metadata/lens/enums.xml
@@ -107,6 +107,9 @@ <int value="6" label="LVF Shutter Button"/> <int value="7" label="LVF Gallery"/> <int value="8" label="Context Menu"/> + <int value="9" label="Omnibox Page Action"/> + <int value="10" label="Omnibox Contextual Suggestion"/> + <int value="11" label="Homework action chip"/> </enum> <!-- LINT.ThenChange(//components/lens/lens_overlay_invocation_source.h:LensOverlayInvocationSource) -->
diff --git a/tools/metrics/histograms/metadata/lens/histograms.xml b/tools/metrics/histograms/metadata/lens/histograms.xml index 0dade76..7e1b92c0 100644 --- a/tools/metrics/histograms/metadata/lens/histograms.xml +++ b/tools/metrics/histograms/metadata/lens/histograms.xml
@@ -36,9 +36,12 @@ <variant name="ContentAreaContextMenuPage"/> <variant name="ContextMenu"/> <variant name="FindInPage"/> + <variant name="HomeworkActionChip"/> <variant name="LVFGallery"/> <variant name="LVFShutterButton"/> <variant name="Omnibox"/> + <variant name="OmniboxContextualSuggestion"/> + <variant name="OmniboxPageAction"/> <variant name="Toolbar"/> <variant name="Unknown"/> </variants>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml index 4a93dde..3ea80b0d9 100644 --- a/tools/metrics/histograms/metadata/media/histograms.xml +++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -84,6 +84,11 @@ <variant name="Widevine" summary="Widevine key system"/> </variants> +<variants name="KeySystemForHardwareSecureOnly"> + <variant name="PlayReady" summary="PlayReady key system"/> + <variant name="Widevine" summary="Widevine key system"/> +</variants> + <variants name="KeySystemWithRobustness"> <variant name="ClearKey" summary="Clear Key key system"/> <variant name="PlayReady.HardwareSecure" @@ -3153,17 +3158,7 @@ <summary>The time spent to load a library MediaFoundation CDM.</summary> </histogram> -<histogram - name="Media.EME.MediaFoundationCdm.Widevine.HardwareSecure.FirstInitialize" - enum="Hresult" expires_after="2026-01-15"> - <owner>xhwang@chromium.org</owner> - <owner>media-dev-uma@chromium.org</owner> - <summary> - HRESULT of the first (and only the first) MediaFoundationCdm initialization - in a MeidaFoundationService process when using Widevine in hardware secure - mode. Reported on both successes and failures. - </summary> -</histogram> +<!-- Widevine in hardware secure mode only --> <histogram name="Media.EME.MediaFoundationCdm.Widevine.HardwareSecure.Initialize" @@ -3176,14 +3171,29 @@ </summary> </histogram> -<histogram name="Media.EME.MediaFoundationCdm.Widevine.HardwareSecure.{EmeApi}" +<histogram + name="Media.EME.MediaFoundationCdm.{KeySystem}.HardwareSecure.FirstInitialize" + enum="Hresult" expires_after="2026-01-15"> + <owner>xhwang@chromium.org</owner> + <owner>media-dev-uma@chromium.org</owner> + <summary> + HRESULT of the first (and only the first) MediaFoundationCdm initialization + in a MeidaFoundationService process when using {KeySystem} in hardware + secure mode. Reported on both successes and failures. + </summary> + <token key="KeySystem" variants="KeySystemForHardwareSecureOnly"/> +</histogram> + +<histogram + name="Media.EME.MediaFoundationCdm.{KeySystem}.HardwareSecure.{EmeApi}" enum="Hresult" expires_after="2026-01-15"> <owner>xhwang@chromium.org</owner> <owner>media-dev-uma@chromium.org</owner> <summary> HRESULT of MediaFoundationCdm and MediaFoundationCdmSession operations when - using Widevine in hardware secure mode. + using {KeySystem} in hardware secure mode. </summary> + <token key="KeySystem" variants="KeySystemForHardwareSecureOnly"/> <token key="EmeApi" variants="EmeApi"/> </histogram> @@ -3250,7 +3260,7 @@ The time spent to get CdmCapability for {KeySystem}. The value will be reported once per CDM instance for hw security in MediaFoundationService. </summary> - <token key="KeySystem" variants="KeySystem"/> + <token key="KeySystem" variants="KeySystemForHardwareSecureOnly"/> </histogram> <histogram @@ -3265,7 +3275,7 @@ browser session lifetime). IsKeySystemSupported() calls IsTypeSupported() multiple times, for different audio/video codecs and encryption schemes. </summary> - <token key="KeySystem" variants="KeySystem"/> + <token key="KeySystem" variants="KeySystemForHardwareSecureOnly"/> </histogram> <histogram name="Media.EME.MojoCdm.ConnectionError" @@ -3351,61 +3361,6 @@ </histogram> <histogram - name="Media.EME.Widevine.CdmCapabilityQueryStatus.CreateDummyMediaFoundationCdmHresult" - enum="Hresult" expires_after="2026-01-15"> - <owner>sangbaekpark@chromium.org</owner> - <owner>media-dev-uma@chromium.org</owner> - <summary> - HRESULT code if CreateDummyMediaFoundationCdm call fails when querying - Widevine CDM capability. - </summary> -</histogram> - -<histogram - name="Media.EME.Widevine.CdmCapabilityQueryStatus.MediaFoundationGetCdmFactoryHresult" - enum="Hresult" expires_after="2026-01-15"> - <owner>sangbaekpark@chromium.org</owner> - <owner>media-dev-uma@chromium.org</owner> - <summary> - HRESULT code if MediaFoundationCdmModule::GetCdmFactory call fails when - querying Widevine CDM capability. - </summary> -</histogram> - -<histogram name="Media.EME.Widevine.HardwareSecure.AllowedForSite" - enum="BooleanEnabled" expires_after="2026-01-15"> - <owner>feras@chromium.org</owner> - <owner>media-dev-uma@chromium.org</owner> - <summary> - Whether Media Foundation Widevine CDM is enabled or disabled based on - previous disabled timestamps in a "User Profile" pref. This is - reported once per browsing session when user requests hardware secure - playback if Media Foundation Widevine CDM is enabled and available. - </summary> -</histogram> - -<histogram name="Media.EME.Widevine.HardwareSecure.CdmCapabilityQueryStatus" - enum="CdmCapabilityQueryStatus" expires_after="2026-01-15"> - <owner>sangbaekpark@chromium.org</owner> - <owner>media-dev-uma@chromium.org</owner> - <summary> - The status of the CDM capability query. This can be used to inspect the - reason when no capability reported. Reported at most once per browser - session per key system when EME query is triggered by a website. - </summary> -</histogram> - -<histogram name="Media.EME.Widevine.HardwareSecure.CdmInfoStatus" - enum="CdmInfoStatus" expires_after="2026-01-15"> - <owner>xhwang@chromium.org</owner> - <owner>media-dev-uma@chromium.org</owner> - <summary> - The CdmInfo::Status of Widevine hardware secure CDM capability. Reported at - most once per browser session when EME query is triggered by a website. - </summary> -</histogram> - -<histogram name="Media.EME.Widevine.HardwareSecure.ClearLeadSupport.{VideoCodec}" enum="BooleanSupported" expires_after="2026-01-15"> <owner>vpasupathy@chromium.org</owner> @@ -3419,6 +3374,8 @@ <token key="VideoCodec" variants="VideoCodec"/> </histogram> +<!-- Widevine in hardware secure mode only --> + <histogram name="Media.EME.Widevine.HardwareSecure.Pref" enum="BooleanEnabled" expires_after="2026-01-15"> <owner>xhwang@chromium.org</owner> @@ -3431,31 +3388,6 @@ </summary> </histogram> -<histogram name="Media.EME.Widevine.HardwareSecure.Support" - enum="BooleanSupported" expires_after="2026-01-15"> - <owner>xhwang@chromium.org</owner> - <owner>media-dev-uma@chromium.org</owner> - <summary> - When hardware secure decryption is enabled, whether Widevine hardware secure - decryption is actually supported by the platform (device). Reported at most - once per browser session when EME query is triggered by a website, and when - Widevine hardware secure CDM was registered. - </summary> -</histogram> - -<histogram name="Media.EME.Widevine.HardwareSecure.Support.{VideoCodec}" - enum="BooleanSupported" expires_after="2026-01-15"> - <owner>xhwang@chromium.org</owner> - <owner>media-dev-uma@chromium.org</owner> - <summary> - When hardware secure decryption is enabled and supported by Widevine, - whether {VideoCodec} video codec is supported by the platform (device). - Reported at most once per browser session when EME query is triggered by a - website, and when Widevine hardware secure CDM was registered. - </summary> - <token key="VideoCodec" variants="VideoCodec"/> -</histogram> - <histogram name="Media.EME.Widevine.SoftwareSecure.SystemCode" enum="CdmSystemCode" expires_after="2026-01-15"> <owner>xhwang@chromium.org</owner> @@ -3477,6 +3409,30 @@ </summary> </histogram> +<histogram + name="Media.EME.{KeySystem}.CdmCapabilityQueryStatus.CreateDummyMediaFoundationCdmHresult" + enum="Hresult" expires_after="2026-01-15"> + <owner>sangbaekpark@chromium.org</owner> + <owner>media-dev-uma@chromium.org</owner> + <summary> + HRESULT code if CreateDummyMediaFoundationCdm call fails when querying + hardware secure CDM capability. + </summary> + <token key="KeySystem" variants="KeySystemForHardwareSecureOnly"/> +</histogram> + +<histogram + name="Media.EME.{KeySystem}.CdmCapabilityQueryStatus.MediaFoundationGetCdmFactoryHresult" + enum="Hresult" expires_after="2026-01-15"> + <owner>sangbaekpark@chromium.org</owner> + <owner>media-dev-uma@chromium.org</owner> + <summary> + HRESULT code if MediaFoundationCdmModule::GetCdmFactory call fails when + querying Widevine CDM capability. + </summary> + <token key="KeySystem" variants="KeySystemForHardwareSecureOnly"/> +</histogram> + <histogram name="Media.EME.{KeySystem}.CreateCdm" enum="BooleanSuccess" expires_after="2026-01-15"> <owner>xhwang@chromium.org</owner> @@ -3527,6 +3483,69 @@ <token key="KeySystem" variants="KeySystemWithRobustness"/> </histogram> +<histogram name="Media.EME.{KeySystem}.HardwareSecure.AllowedForSite" + enum="BooleanEnabled" expires_after="2026-01-15"> + <owner>feras@chromium.org</owner> + <owner>media-dev-uma@chromium.org</owner> + <summary> + Whether Media Foundation based CDM is enabled or disabled based on previous + disabled timestamps in a "User Profile" pref. This is reported + once per browsing session when user requests hardware secure playback if + Media Foundation based CDM is enabled and available. + </summary> + <token key="KeySystem" variants="KeySystemForHardwareSecureOnly"/> +</histogram> + +<histogram name="Media.EME.{KeySystem}.HardwareSecure.CdmCapabilityQueryStatus" + enum="CdmCapabilityQueryStatus" expires_after="2026-01-15"> + <owner>sangbaekpark@chromium.org</owner> + <owner>media-dev-uma@chromium.org</owner> + <summary> + The status of the CDM capability query. This can be used to inspect the + reason when no capability reported. Reported at most once per browser + session per key system when EME query is triggered by a website. + </summary> + <token key="KeySystem" variants="KeySystemForHardwareSecureOnly"/> +</histogram> + +<histogram name="Media.EME.{KeySystem}.HardwareSecure.CdmInfoStatus" + enum="CdmInfoStatus" expires_after="2026-01-15"> + <owner>xhwang@chromium.org</owner> + <owner>media-dev-uma@chromium.org</owner> + <summary> + The CdmInfo::Status of {KeySystem} hardware secure CDM capability. Reported + at most once per browser session when EME query is triggered by a website. + </summary> + <token key="KeySystem" variants="KeySystemForHardwareSecureOnly"/> +</histogram> + +<histogram name="Media.EME.{KeySystem}.HardwareSecure.Support" + enum="BooleanSupported" expires_after="2026-01-15"> + <owner>xhwang@chromium.org</owner> + <owner>media-dev-uma@chromium.org</owner> + <summary> + When hardware secure decryption is enabled, whether Widevine hardware secure + decryption is actually supported by the platform (device). Reported at most + once per browser session when EME query is triggered by a website, and when + Widevine hardware secure CDM was registered. + </summary> + <token key="KeySystem" variants="KeySystemForHardwareSecureOnly"/> +</histogram> + +<histogram name="Media.EME.{KeySystem}.HardwareSecure.Support.{VideoCodec}" + enum="BooleanSupported" expires_after="2026-01-15"> + <owner>xhwang@chromium.org</owner> + <owner>media-dev-uma@chromium.org</owner> + <summary> + When hardware secure decryption is enabled and supported by Widevine, + whether {VideoCodec} video codec is supported by the platform (device). + Reported at most once per browser session when EME query is triggered by a + website, and when Widevine hardware secure CDM was registered. + </summary> + <token key="KeySystem" variants="KeySystemForHardwareSecureOnly"/> + <token key="VideoCodec" variants="VideoCodec"/> +</histogram> + <histogram name="Media.EME.{KeySystem}.InitialKeyStatusMix" enum="CdmKeyStatusMix" expires_after="2026-01-15"> <owner>xhwang@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/page/enums.xml b/tools/metrics/histograms/metadata/page/enums.xml index 2d50426..3b2ef0a 100644 --- a/tools/metrics/histograms/metadata/page/enums.xml +++ b/tools/metrics/histograms/metadata/page/enums.xml
@@ -296,6 +296,9 @@ <int value="29" label="Lens Overlay"/> <int value="30" label="Discounts"/> <int value="31" label="Optimization Guide"/> + <int value="32" label="Collaboration Messaging"/> + <int value="33" label="Change Password"/> + <int value="34" label="Lens Overlay Homework"/> </enum> <!-- LINT.ThenChange(//chrome/browser/ui/page_action/page_action_icon_type.h:PageActionIconType) -->
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml index 95e6e515..8e541b7 100644 --- a/tools/metrics/ukm/ukm.xml +++ b/tools/metrics/ukm/ukm.xml
@@ -9133,14 +9133,15 @@ </metric> <metric name="CompletionStatus.ErrorCode"> <summary> - An integer representing the error code of the request when it completes or - fails. This comes from network::URLLoaderCompletionStatus.error_code. + Deprecated as of 2025/05/22. An integer representing the error code of the + request when it completes or fails. This comes from + network::URLLoaderCompletionStatus.error_code. </summary> </metric> <metric name="CompletionStatus.ExtendedErrorCode"> <summary> - An integer representing the error code of the request when it completes or - fails. This comes from + Deprecated as of 2025/05/22. An integer representing the error code of the + request when it completes or fails. This comes from network::URLLoaderCompletionStatus.extended_error_code. </summary> </metric> @@ -9167,6 +9168,20 @@ Whether the Context is detached or not when this event is logged. </summary> </metric> + <metric name="LoaderCompleted.ErrorCode"> + <summary> + An integer representing the error code of the request when it reaches the + kLoaderCompleted stage. This comes from + network::URLLoaderCompletionStatus.error_code. + </summary> + </metric> + <metric name="LoaderCompleted.ExtendedErrorCode"> + <summary> + An integer representing the extended error code of the request when it + reaches the kLoaderCompleted stage. This comes from + network::URLLoaderCompletionStatus.extended_error_code. + </summary> + </metric> <metric name="NumRedirects"> <summary> The number of redirects a fetch keepalive request loader has experienced @@ -9185,6 +9200,20 @@ this is also set to "LoaderCreated" stage. </summary> </metric> + <metric name="RequestFailed.ErrorCode"> + <summary> + An integer representing the error code of the request when it reaches the + kRequestfailed stage. This comes from + network::URLLoaderCompletionStatus.error_code. + </summary> + </metric> + <metric name="RequestFailed.ExtendedErrorCode"> + <summary> + An integer representing the extended error code of the request when it + reaches the kRequestFailed stage. This comes from + network::URLLoaderCompletionStatus.extended_error_code. + </summary> + </metric> <metric name="RequestType" enum="FetchKeepAliveRequestType"> <summary> An enum representing the type of the fetch keepalive request. It tells @@ -12822,6 +12851,32 @@ </history> </aggregation> </metric> + <metric name="OmniboxContextualSuggestion"> + <summary> + Time to first interaction when overlay invoked through the omnibox + contextual suggestion. + </summary> + <aggregation> + <history> + <statistics> + <quantiles type="std-percentiles"/> + </statistics> + </history> + </aggregation> + </metric> + <metric name="OmniboxPageAction"> + <summary> + Time to first interaction when overlay invoked through the omnibox Lens + page action suggestion. + </summary> + <aggregation> + <history> + <statistics> + <quantiles type="std-percentiles"/> + </statistics> + </history> + </aggregation> + </metric> <metric name="Toolbar"> <summary> Time to first interaction when overlay invoked through the toolbar. @@ -26811,10 +26866,8 @@ </event> <event name="XR.WebXR.Session"> - <owner>billorr@chromium.org</owner> - <owner>bialpio@chromium.org</owner> + <owner>alcooper@chromium.org</owner> <owner>xr-dev@chromium.org</owner> - <owner>cassew@google.com</owner> <summary> When session ends, records data for a WebXR / WebVR session. </summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index 4f25a83..50e8bb4 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@ "full_remote_path": "perfetto-luci-artifacts/v50.1/linux-arm64/trace_processor_shell" }, "win": { - "hash": "3aa49ee87d544c6dc520b31d415891d73c33c7da", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/d15aa8fb5bed6a9e04f7f93ebdc3573b6f7a366e/trace_processor_shell.exe" + "hash": "9e46664964572e7522fecfc6c6790562a652dccb", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/8435ff3dc1847a6e86d7c854a1fab08f875f9cf2/trace_processor_shell.exe" }, "linux_arm": { "hash": "99f971ca131f6d11c73f4b918099d434bdd8093c", @@ -21,8 +21,8 @@ "full_remote_path": "perfetto-luci-artifacts/v50.1/mac-arm64/trace_processor_shell" }, "linux": { - "hash": "9d97f6c8b52c68187a0be9f14c77b086687e859f", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/d15aa8fb5bed6a9e04f7f93ebdc3573b6f7a366e/trace_processor_shell" + "hash": "69bfe1bb72ec83b3b83ed45183d49d1a60064f2f", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/fa368a66f714203d1232ff62ece403bd2f5e7c10/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/ui/android/overscroll_glow.cc b/ui/android/overscroll_glow.cc index 52d0426..415b480 100644 --- a/ui/android/overscroll_glow.cc +++ b/ui/android/overscroll_glow.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/40285824): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "ui/android/overscroll_glow.h" #include <stddef.h> +#include <array> + #include "cc/slim/layer.h" #include "ui/android/edge_effect.h" #include "ui/android/window_android_compositor.h" @@ -223,7 +220,7 @@ gfx::Vector2dF overscroll_pull = gfx::ScaleVector2d(overscroll_delta, inv_width, inv_height); - const float edge_pull[EDGE_COUNT] = { + const std::array<float, EDGE_COUNT> edge_pull = { min(overscroll_pull.y(), 0.f), // Top min(overscroll_pull.x(), 0.f), // Left max(overscroll_pull.y(), 0.f), // Bottom @@ -234,7 +231,7 @@ gfx::ScaleVector2d(overscroll_location, inv_width, inv_height); displacement.set_x(max(0.f, min(1.f, displacement.x()))); displacement.set_y(max(0.f, min(1.f, displacement.y()))); - const float edge_displacement[EDGE_COUNT] = { + const std::array<float, EDGE_COUNT> edge_displacement = { 1.f - displacement.x(), // Top displacement.y(), // Left displacement.x(), // Bottom @@ -259,7 +256,7 @@ DCHECK(!velocity.IsZero()); // Only trigger on initial overscroll at a non-zero velocity - const float overscroll_velocities[EDGE_COUNT] = { + const std::array<float, EDGE_COUNT> overscroll_velocities = { y_overscroll_started ? min(velocity.y(), 0.f) : 0, // Top x_overscroll_started ? min(velocity.x(), 0.f) : 0, // Left y_overscroll_started ? max(velocity.y(), 0.f) : 0, // Bottom
diff --git a/ui/android/overscroll_glow.h b/ui/android/overscroll_glow.h index a083eaa..acd2af1 100644 --- a/ui/android/overscroll_glow.h +++ b/ui/android/overscroll_glow.h
@@ -5,6 +5,7 @@ #ifndef UI_ANDROID_OVERSCROLL_GLOW_H_ #define UI_ANDROID_OVERSCROLL_GLOW_H_ +#include <array> #include <memory> #include "base/memory/raw_ptr.h" @@ -100,10 +101,10 @@ EdgeEffect* GetOppositeEdge(int edge_index); raw_ptr<OverscrollGlowClient> client_; - std::unique_ptr<EdgeEffect> edge_effects_[EDGE_COUNT]; + std::array<std::unique_ptr<EdgeEffect>, EDGE_COUNT> edge_effects_; gfx::SizeF viewport_size_; - float edge_offsets_[EDGE_COUNT]; + std::array<float, EDGE_COUNT> edge_offsets_; bool initialized_; bool allow_horizontal_overscroll_; bool allow_vertical_overscroll_;
diff --git a/ui/android/resources/resource_manager_impl.cc b/ui/android/resources/resource_manager_impl.cc index 7fcb2c9..01d32651 100644 --- a/ui/android/resources/resource_manager_impl.cc +++ b/ui/android/resources/resource_manager_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/40285824): Remove this and convert code to safer constructs. -#pragma allow_unsafe_buffers -#endif - #include "ui/android/resources/resource_manager_impl.h" #include <inttypes.h>
diff --git a/ui/android/resources/resource_manager_impl.h b/ui/android/resources/resource_manager_impl.h index f811ad20..22d1f570e 100644 --- a/ui/android/resources/resource_manager_impl.h +++ b/ui/android/resources/resource_manager_impl.h
@@ -5,6 +5,7 @@ #ifndef UI_ANDROID_RESOURCES_RESOURCE_MANAGER_IMPL_H_ #define UI_ANDROID_RESOURCES_RESOURCE_MANAGER_IMPL_H_ +#include <array> #include <memory> #include <unordered_map> #include <unordered_set> @@ -96,7 +97,7 @@ std::unordered_map<SkColor, std::unique_ptr<ResourceMap>>; raw_ptr<cc::UIResourceManager> ui_resource_manager_; - ResourceMap resources_[ANDROID_RESOURCE_TYPE_COUNT]; + std::array<ResourceMap, ANDROID_RESOURCE_TYPE_COUNT> resources_; TintedResourceMap tinted_resources_; // The set of tints that are used for resources in the current frame.
diff --git a/ui/display/manager/managed_display_info.cc b/ui/display/manager/managed_display_info.cc index 1673cf28..daea1db 100644 --- a/ui/display/manager/managed_display_info.cc +++ b/ui/display/manager/managed_display_info.cc
@@ -393,7 +393,9 @@ clear_overscan_insets_(false), bits_per_channel_(0), variable_refresh_rate_state_(VariableRefreshRateState::kVrrNotCapable), - vsync_rate_min_(std::nullopt) {} + vsync_rate_min_(std::nullopt) { + has_overscan_ = true; +} ManagedDisplayInfo::ManagedDisplayInfo(const ManagedDisplayInfo& other) = default;
diff --git a/ui/events/android/motion_event_android.cc b/ui/events/android/motion_event_android.cc index dda3bc3e..05623ff 100644 --- a/ui/events/android/motion_event_android.cc +++ b/ui/events/android/motion_event_android.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 "ui/events/android/motion_event_android.h" #include <android/input.h>
diff --git a/ui/events/android/motion_event_android.h b/ui/events/android/motion_event_android.h index 74a8c37..0ebbf4e 100644 --- a/ui/events/android/motion_event_android.h +++ b/ui/events/android/motion_event_android.h
@@ -9,6 +9,7 @@ #include <stddef.h> #include <stdint.h> +#include <array> #include <memory> #include "base/android/scoped_java_ref.h" @@ -143,7 +144,9 @@ float tilt_x = 0; float tilt_y = 0; ToolType tool_type = ToolType::UNKNOWN; - } cached_pointers_[MAX_POINTERS_TO_CACHE]; + }; + + std::array<CachedPointer, MAX_POINTERS_TO_CACHE> cached_pointers_; static ToolType FromAndroidToolType(int android_tool_type); static base::TimeTicks FromAndroidTime(base::TimeTicks time);
diff --git a/ui/events/android/motion_event_android_java.cc b/ui/events/android/motion_event_android_java.cc index c76b073..a79be062 100644 --- a/ui/events/android/motion_event_android_java.cc +++ b/ui/events/android/motion_event_android_java.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 "ui/events/android/motion_event_android_java.h" #include <android/input.h>
diff --git a/ui/events/android/motion_event_android_native.cc b/ui/events/android/motion_event_android_native.cc index 15b8d402..425f21d2 100644 --- a/ui/events/android/motion_event_android_native.cc +++ b/ui/events/android/motion_event_android_native.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 "ui/events/android/motion_event_android_native.h" #include <android/input.h>
diff --git a/ui/views/button_drag_utils.cc b/ui/views/button_drag_utils.cc index 88f96b6..ba784f0 100644 --- a/ui/views/button_drag_utils.cc +++ b/ui/views/button_drag_utils.cc
@@ -10,6 +10,8 @@ #include "base/memory/raw_ptr.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/metadata/metadata_header_macros.h" +#include "ui/base/metadata/metadata_impl_macros.h" #include "ui/base/models/image_model.h" #include "ui/base/resource/resource_bundle.h" #include "ui/color/color_id.h" @@ -30,6 +32,29 @@ #include "ui/views/widget/widget.h" #include "url/gurl.h" +namespace { + +class DragContentsButton : public views::LabelButton { + METADATA_HEADER(DragContentsButton, views::LabelButton) + + public: + DragContentsButton(PressedCallback callback, std::u16string title) + : LabelButton(std::move(callback), title) { +#if BUILDFLAG(IS_WIN) + // For windows, label button paints icon to a layer by default, which + // causes the drag image to not render correctly. Disable this behavior. + // This is a workaround for crbug.com/394380766 + image_container_view()->DestroyLayer(); +#endif + } + ~DragContentsButton() override = default; +}; + +BEGIN_METADATA(DragContentsButton) +END_METADATA + +} // namespace + namespace button_drag_utils { // Maximum width of the link drag image in pixels. @@ -80,8 +105,8 @@ drag_widget->Init(std::move(params)); // Create a button to render the drag image for us. - views::LabelButton* button = - drag_widget->SetContentsView(std::make_unique<views::LabelButton>( + DragContentsButton* button = + drag_widget->SetContentsView(std::make_unique<DragContentsButton>( views::Button::PressedCallback(), title.empty() ? base::UTF8ToUTF16(url.spec()) : title)); button->SetTextSubpixelRenderingEnabled(false);
diff --git a/ui/webui/resources/images/BUILD.gn b/ui/webui/resources/images/BUILD.gn index d4b003f..6fb66cd 100644 --- a/ui/webui/resources/images/BUILD.gn +++ b/ui/webui/resources/images/BUILD.gn
@@ -50,6 +50,7 @@ if (!is_ios) { input_files += [ "chrome_logo_dark.svg", + "icon_arrow_back.svg", "icon_edit.svg", ] } @@ -63,7 +64,6 @@ "chevron_down.svg", "dark/arrow_down.svg", "dark/chevron_down.svg", - "icon_arrow_back.svg", "icon_arrow_drop_down_cr23.svg", "icon_arrow_drop_up_cr23.svg", "icon_bookmark.svg",
diff --git a/v8 b/v8 index 7d13261..5f0be56 160000 --- a/v8 +++ b/v8
@@ -1 +1 @@ -Subproject commit 7d1326123f37e10d3cf0a8ff6efc2d330e1f2b96 +Subproject commit 5f0be56704de0f9fd5ee6bb6a7a780976a5bfcb7