diff --git a/DEPS b/DEPS index a9c65e1a..c854ac67 100644 --- a/DEPS +++ b/DEPS
@@ -311,15 +311,15 @@ # 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': 'c90f939a79eab36ff08b04ae6a619c718b363b22', + 'v8_revision': 'cd2100108ffb2ae78e46934e7d29313f00aee271', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '19fb11b67c6f30f440112ca5d9725d1bed9b232e', + 'angle_revision': '2265e37bf9b3679c20748fb16b1823e9b0b621d8', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. - 'swiftshader_revision': 'ab3cb3f5416b4363f4d4f0e06b35ec8c3dc4714c', + 'swiftshader_revision': '3fdd375e1de1e81e5c9fe04a46c1870b78a55b43', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. @@ -378,7 +378,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': 'faaae893d5d2c4050d9ba741a2cbd70b4528c7fe', + 'catapult_revision': '98d333e8aebd9658a465e71e51214072b0ac205e', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -422,7 +422,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': '18bcdebc0256ad3d14bd1290163c98c84baddfa3', + 'dawn_revision': '72ac53e5fa8c7837750a3dfec350775da72e8aef', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -818,7 +818,7 @@ 'src/clank': { 'url': 'https://chrome-internal.googlesource.com/clank/internal/apps.git' + '@' + - 'aa0b389580b88beb9ef353b3a7bb11cfa3f8bac6', + '1ee22b1b79a2c01ad36b08b7b5a88beddf7a1b8d', 'condition': 'checkout_android and checkout_src_internal and not checkout_clank_via_src_internal', }, @@ -1002,7 +1002,7 @@ 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': '6BqL6l_VnAEp2_3Vdr-Rzeb2pDOCpufW6SLm8GKBIDQC', + 'version': 'B1v8mLuR3zWALlZp3ZYv3ydSWTsVvOj50O8odlD5gMMC', }, ], 'condition': 'checkout_android', @@ -1079,7 +1079,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_build_tools/manifest_merger', - 'version': 'bUREd_PkCqlp2ww6zmyOLGf0jhqgbnf6GT4V1xkAZ10C', + 'version': 'bfhl7B4_T6dP72d1sF-6RSeAQqwlw1qUx-FDEFh3sKIC', }, ], 'condition': 'checkout_android', @@ -1245,7 +1245,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '2c0a8c736a59044e4acc7be9e172343adc5c4310', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '77e64ae61ebda9e20f1eee461149e601c65b0a8f', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), @@ -1522,7 +1522,7 @@ }, 'src/third_party/libunwindstack': { - 'url': Var('chromium_git') + '/chromium/src/third_party/libunwindstack.git' + '@' + '8740b09bd1f8b81bdba92766afcb9df1d6a1f14e', + 'url': Var('chromium_git') + '/chromium/src/third_party/libunwindstack.git' + '@' + '4dbfa0e8c844c8e243b297bc185e54a99ff94f9e', 'condition': 'checkout_android', }, @@ -1666,7 +1666,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '7bd83a8f9af8604e932cd717fdd56e586c54d3a4', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '082982f29eab767cb18b2dfa7196b2774b3c65ee', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1811,7 +1811,7 @@ 'dep_type': 'cipd', }, - 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@2eb0a986e4f50b40c1fa1724564bc826c51a295b', + 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@37c23b92b3b6379ec1516466dffcbedebc301f20', 'src/third_party/vulkan_memory_allocator': Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907', @@ -1848,10 +1848,10 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'd1b65aa5a88f6efd900604dfcda840154e9f16e2', 'src/third_party/webgpu-cts/src': - Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '9ff9ec53c07190a7add65392239939961a064da5', + Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '8ced52dedfef6c91ef5ee84bf08af00bf879a17d', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '18b95681858717f66d7dfef20c53dbadcec8d4da', + Var('webrtc_git') + '/src.git' + '@' + 'f52cd2bb7305dbd781fe99e2c5a3929380f4fd8d', # 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. @@ -1921,7 +1921,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@4bd291c858980c53e3127bcb8a7076502f87d6a1', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@5f95aa179c7691f2def9340a7798b7fc07895c2b', 'condition': 'checkout_src_internal', },
diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 0af3237..6d90eb13 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py
@@ -1340,7 +1340,10 @@ ) | set('%s@skia-corp.google.com.iam.gserviceaccount.com' % s for s in ('chromium-internal-autoroll',) ) | set('%s@owners-cleanup-prod.google.com.iam.gserviceaccount.com' % s - for s in ('swarming-tasks',)) + for s in ('swarming-tasks',) + ) | set('%s@fuchsia-infra.iam.gserviceaccount.com' % s + for s in ('global-integration-try-builder', + 'global-integration-ci-builder')) _INVALID_GRD_FILE_LINE = [ (r'<file lang=.* path=.*', 'Path should come before lang in GRD files.')
diff --git a/ash/app_list/folder_image_unittest.cc b/ash/app_list/folder_image_unittest.cc index 255e18ba..d5c74a9 100644 --- a/ash/app_list/folder_image_unittest.cc +++ b/ash/app_list/folder_image_unittest.cc
@@ -119,9 +119,8 @@ INSTANTIATE_TEST_SUITE_P( All, FolderImageTest, - ::testing::Combine(::testing::Values(AppListConfigType::kLarge, - AppListConfigType::kMedium, - AppListConfigType::kSmall), + ::testing::Combine(::testing::Values(AppListConfigType::kRegular, + AppListConfigType::kDense), ::testing::Bool())); TEST_P(FolderImageTest, UpdateListTest) {
diff --git a/ash/app_list/model/app_list_folder_item.cc b/ash/app_list/model/app_list_folder_item.cc index 38292c8..73df26f 100644 --- a/ash/app_list/model/app_list_folder_item.cc +++ b/ash/app_list/model/app_list_folder_item.cc
@@ -26,13 +26,8 @@ // Item observers are added later in OnListItemAdded(). item_list_->AddObserver(this); - std::vector<AppListConfigType> configs; - if (features::IsProductivityLauncherEnabled()) { - configs = {AppListConfigType::kRegular, AppListConfigType::kDense}; - } else { - configs = {AppListConfigType::kLarge, AppListConfigType::kMedium, - AppListConfigType::kSmall}; - } + std::vector<AppListConfigType> configs = {AppListConfigType::kRegular, + AppListConfigType::kDense}; EnsureIconsForAvailableConfigTypes(configs, /*request_icon_update=*/false); config_provider_observation_.Observe(&AppListConfigProvider::Get()); set_is_folder(true);
diff --git a/ash/app_list/model/app_list_model_unittest.cc b/ash/app_list/model/app_list_model_unittest.cc index 0194be3..9e251f5 100644 --- a/ash/app_list/model/app_list_model_unittest.cc +++ b/ash/app_list/model/app_list_model_unittest.cc
@@ -270,67 +270,6 @@ model_->MergeItems(item0->id(), folder->id()); } -TEST_F(AppListModelFolderTest, NonSharedConfigIconGeneration) { - // The configs tested here are not used by ProductivityLauncher. This test - // can be deleted when ProductivityLauncher is the default. - base::test::ScopedFeatureList features; - features.InitAndDisableFeature(features::kProductivityLauncher); - - // Ensure any configs set by previous tests are cleared. - AppListConfigProvider::Get().ResetForTesting(); - - // Start with kLarge config available. - const AppListConfig* large_config = - AppListConfigProvider::Get().GetConfigForType(AppListConfigType::kLarge, - true); - ASSERT_TRUE(large_config); - - const size_t num_folder_apps = 5; - const size_t num_observed_apps = 4; - AppListFolderItem* folder = CreateFolderWithApps("folder1", num_folder_apps); - - // Verify that the folder has folder image for large config. - FolderImage* large_config_image = - folder->GetFolderImageForTesting(AppListConfigType::kLarge); - ASSERT_TRUE(large_config_image); - EXPECT_EQ(large_config->folder_unclipped_icon_size(), - large_config_image->icon().size()); - - // Verify that the folder is observing the app list item. - EXPECT_TRUE(ItemObservedByFolder( - folder, folder->item_list()->item_at(num_observed_apps - 1), - AppListConfigType::kLarge)); - EXPECT_FALSE(ItemObservedByFolder( - folder, folder->item_list()->item_at(num_observed_apps), - AppListConfigType::kLarge)); - - // Not medium folder image, as the config does not exist yet. - EXPECT_FALSE(folder->GetFolderImageForTesting(AppListConfigType::kMedium)); - - // Create medium config, and verify the folder image for medium config gets - // created. - const AppListConfig* medium_config = - AppListConfigProvider::Get().GetConfigForType(AppListConfigType::kMedium, - true); - FolderImage* medium_config_image = - folder->GetFolderImageForTesting(AppListConfigType::kMedium); - ASSERT_TRUE(medium_config_image); - EXPECT_EQ(medium_config->folder_unclipped_icon_size(), - medium_config_image->icon().size()); - - // Verify that the folder is observing the app list item. - EXPECT_TRUE(ItemObservedByFolder( - folder, folder->item_list()->item_at(num_observed_apps - 1), - AppListConfigType::kMedium)); - EXPECT_FALSE(ItemObservedByFolder( - folder, folder->item_list()->item_at(num_observed_apps), - AppListConfigType::kMedium)); - - EXPECT_FALSE(folder->GetFolderImageForTesting(AppListConfigType::kSmall)); - - AppListConfigProvider::Get().ResetForTesting(); -} - // Same test as above, but for ProductivityLauncher config types. TEST_F(AppListModelFolderTest, NonSharedConfigIconGenerationProductivityLauncher) {
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc index b978fa60a..a28c63a 100644 --- a/ash/app_list/views/apps_grid_view_unittest.cc +++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -62,6 +62,7 @@ #include "base/test/icu_test_util.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/metrics/user_action_tester.h" +#include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/window.h" @@ -723,6 +724,25 @@ INSTANTIATE_TEST_SUITE_P(All, AppsGridViewDragTest, testing::Bool()); +class AppsGridViewDragWithShelfPartyTest : public AppsGridViewDragTest { + public: + AppsGridViewDragWithShelfPartyTest() { + scoped_feature_list_.InitAndEnableFeature(features::kShelfParty); + } + AppsGridViewDragWithShelfPartyTest( + const AppsGridViewDragWithShelfPartyTest&) = delete; + AppsGridViewDragWithShelfPartyTest& operator=( + const AppsGridViewDragWithShelfPartyTest&) = delete; + ~AppsGridViewDragWithShelfPartyTest() override = default; + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +INSTANTIATE_TEST_SUITE_P(All, + AppsGridViewDragWithShelfPartyTest, + testing::Bool()); + // Test suite for clamshell mode, parameterized by RTL. class AppsGridViewClamshellTest : public AppsGridViewTest, public testing::WithParamInterface<bool> { @@ -3919,7 +3939,7 @@ EXPECT_TRUE(ShelfModel::Get()->items().empty()); } -TEST_P(AppsGridViewDragTest, DragAndPinItemToEmptyShelf) { +TEST_P(AppsGridViewDragWithShelfPartyTest, DragAndPinItemToEmptyShelf) { model_->PopulateApps(2); UpdateLayout();
diff --git a/ash/constants/quick_settings_catalogs.h b/ash/constants/quick_settings_catalogs.h index d9ce304..896e3e6 100644 --- a/ash/constants/quick_settings_catalogs.h +++ b/ash/constants/quick_settings_catalogs.h
@@ -29,7 +29,8 @@ kPowerRestartMenuButton = 13, kPowerSignoutMenuButton = 14, kPowerLockMenuButton = 15, - kMaxValue = kPowerLockMenuButton + kSupervisedButton = 16, + kMaxValue = kSupervisedButton }; // A catalog that registers all the features on the Quick Settings page. This
diff --git a/ash/public/cpp/app_list/app_list_config.cc b/ash/public/cpp/app_list/app_list_config.cc index 65462e6..4483f296 100644 --- a/ash/public/cpp/app_list/app_list_config.cc +++ b/ash/public/cpp/app_list/app_list_config.cc
@@ -27,25 +27,15 @@ // padding for the unclipped folder icon. int MinYScaleHeightAdjustmentForType(ash::AppListConfigType type) { switch (type) { - case ash::AppListConfigType::kLarge: case ash::AppListConfigType::kRegular: return 16; - case ash::AppListConfigType::kMedium: case ash::AppListConfigType::kDense: return 8; - case ash::AppListConfigType::kSmall: - return 4; } } int GridTileWidthForType(ash::AppListConfigType type) { switch (type) { - case ash::AppListConfigType::kLarge: - return 120; - case ash::AppListConfigType::kMedium: - return 88; - case ash::AppListConfigType::kSmall: - return 80; case ash::AppListConfigType::kRegular: return 96; case ash::AppListConfigType::kDense: @@ -55,38 +45,24 @@ int GridTileHeightForType(ash::AppListConfigType type) { switch (type) { - case ash::AppListConfigType::kLarge: case ash::AppListConfigType::kRegular: return 120; - case ash::AppListConfigType::kMedium: case ash::AppListConfigType::kDense: return 88; - case ash::AppListConfigType::kSmall: - return 80; } } int GridIconDimensionForType(ash::AppListConfigType type) { switch (type) { - case ash::AppListConfigType::kLarge: case ash::AppListConfigType::kRegular: return 64; - case ash::AppListConfigType::kMedium: case ash::AppListConfigType::kDense: return 48; - case ash::AppListConfigType::kSmall: - return 40; } } int GridTitleTopPaddingForType(ash::AppListConfigType type) { switch (type) { - case ash::AppListConfigType::kLarge: - return 92; - case ash::AppListConfigType::kMedium: - return 64; - case ash::AppListConfigType::kSmall: - return 56; case ash::AppListConfigType::kRegular: return 88; case ash::AppListConfigType::kDense: @@ -96,11 +72,6 @@ int GridTitleBottomPaddingForType(ash::AppListConfigType type) { switch (type) { - case ash::AppListConfigType::kLarge: - return 8; - case ash::AppListConfigType::kMedium: - case ash::AppListConfigType::kSmall: - return 6; case ash::AppListConfigType::kRegular: return 12; case ash::AppListConfigType::kDense: @@ -110,25 +81,16 @@ int GridTitleHorizontalPaddingForType(ash::AppListConfigType type) { switch (type) { - case ash::AppListConfigType::kLarge: case ash::AppListConfigType::kRegular: return 8; - case ash::AppListConfigType::kMedium: case ash::AppListConfigType::kDense: return 4; - case ash::AppListConfigType::kSmall: - return 0; } } +// TODO(crbug.com/1372228): Remove this function. int GridFocusDimensionForType(ash::AppListConfigType type) { switch (type) { - case ash::AppListConfigType::kLarge: - return 80; - case ash::AppListConfigType::kMedium: - return 64; - case ash::AppListConfigType::kSmall: - return 56; // Unused for ProductivityLauncher. case ash::AppListConfigType::kRegular: case ash::AppListConfigType::kDense: @@ -136,25 +98,10 @@ } } -int GridFocusCornerRadiusForType(ash::AppListConfigType type) { - switch (type) { - case ash::AppListConfigType::kLarge: - return 12; - case ash::AppListConfigType::kMedium: - case ash::AppListConfigType::kSmall: - case ash::AppListConfigType::kRegular: - case ash::AppListConfigType::kDense: - return 8; - } -} - int AppTitleMaxLineHeightForType(ash::AppListConfigType type) { switch (type) { - case ash::AppListConfigType::kLarge: case ash::AppListConfigType::kRegular: return 20; - case ash::AppListConfigType::kMedium: - case ash::AppListConfigType::kSmall: case ash::AppListConfigType::kDense: return 18; } @@ -162,15 +109,12 @@ gfx::FontList AppTitleFontForType(ash::AppListConfigType type) { ui::ResourceBundle::FontDetails details; - // TODO(https://crbug.com/1197600): Use Google Sans Text (medium weight) for - // ProductivityLauncher (kRegular, kDense) when that font is available. + // TODO(https://crbug.com/1197600): Use Google Sans Text (medium weight) when + // the font is available. switch (type) { - case ash::AppListConfigType::kLarge: case ash::AppListConfigType::kRegular: details.size_delta = 1; break; - case ash::AppListConfigType::kMedium: - case ash::AppListConfigType::kSmall: case ash::AppListConfigType::kDense: details.size_delta = 0; break; @@ -181,12 +125,6 @@ // See "App drag over folder" in go/cros-launcher-spec. int FolderUnclippedIconDimensionForType(ash::AppListConfigType type) { switch (type) { - case ash::AppListConfigType::kLarge: - return 88; - case ash::AppListConfigType::kMedium: - return 64; - case ash::AppListConfigType::kSmall: - return 56; case ash::AppListConfigType::kRegular: return 76; case ash::AppListConfigType::kDense: @@ -196,12 +134,6 @@ int FolderClippedIconDimensionForType(ash::AppListConfigType type) { switch (type) { - case ash::AppListConfigType::kLarge: - return 72; - case ash::AppListConfigType::kMedium: - return 56; - case ash::AppListConfigType::kSmall: - return 48; case ash::AppListConfigType::kRegular: return 60; case ash::AppListConfigType::kDense: @@ -211,29 +143,13 @@ int ItemIconInFolderIconDimensionForType(ash::AppListConfigType type) { switch (type) { - case ash::AppListConfigType::kLarge: case ash::AppListConfigType::kRegular: return 32; - case ash::AppListConfigType::kMedium: - return 28; - case ash::AppListConfigType::kSmall: case ash::AppListConfigType::kDense: return 24; } } -int ItemIconInFolderIconMarginForType(ash::AppListConfigType type) { - switch (type) { - case ash::AppListConfigType::kLarge: - case ash::AppListConfigType::kRegular: - case ash::AppListConfigType::kMedium: - case ash::AppListConfigType::kDense: - return 4; - case ash::AppListConfigType::kSmall: - return 2; - } -} - } // namespace SharedAppListConfig& SharedAppListConfig::instance() { @@ -279,7 +195,7 @@ grid_title_horizontal_padding_(GridTitleHorizontalPaddingForType(type)), grid_title_width_(grid_tile_width_), grid_focus_dimension_(GridFocusDimensionForType(type)), - grid_focus_corner_radius_(GridFocusCornerRadiusForType(type)), + grid_focus_corner_radius_(8), app_title_max_line_height_(AppTitleMaxLineHeightForType(type)), app_title_font_(AppTitleFontForType(type)), folder_bubble_radius_(FolderUnclippedIconDimensionForType(type) / 2), @@ -290,7 +206,7 @@ folder_background_radius_(12), item_icon_in_folder_icon_dimension_( ItemIconInFolderIconDimensionForType(type)), - item_icon_in_folder_icon_margin_(ItemIconInFolderIconMarginForType(type)), + item_icon_in_folder_icon_margin_(4), folder_dropping_circle_radius_(folder_bubble_radius_) {} AppListConfig::AppListConfig(const AppListConfig& base_config,
diff --git a/ash/public/cpp/app_list/app_list_config_provider.cc b/ash/public/cpp/app_list/app_list_config_provider.cc index 25eb630..7cb2a70 100644 --- a/ash/public/cpp/app_list/app_list_config_provider.cc +++ b/ash/public/cpp/app_list/app_list_config_provider.cc
@@ -23,29 +23,11 @@ // size. ash::AppListConfigType GetConfigTypeForDisplaySize( const gfx::Size& display_size) { - if (features::IsProductivityLauncherEnabled()) { - // Values from go/cros-launcher-spec - if (display_size.height() <= 675 || display_size.width() <= 675) - return AppListConfigType::kDense; + // Values from go/cros-launcher-spec + if (display_size.height() <= 675 || display_size.width() <= 675) + return AppListConfigType::kDense; - return AppListConfigType::kRegular; - } - - // Landscape: - if (display_size.width() > display_size.height()) { - if (display_size.width() >= 1200) - return ash::AppListConfigType::kLarge; - if (display_size.width() >= 960) - return ash::AppListConfigType::kMedium; - return ash::AppListConfigType::kSmall; - } - - // Portrait: - if (display_size.width() >= 768) - return ash::AppListConfigType::kLarge; - if (display_size.width() >= 600) - return ash::AppListConfigType::kMedium; - return ash::AppListConfigType::kSmall; + return AppListConfigType::kRegular; } } // namespace
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h index 2f37203c..6af57dd 100644 --- a/ash/public/cpp/app_list/app_list_types.h +++ b/ash/public/cpp/app_list/app_list_types.h
@@ -45,18 +45,10 @@ // App list config types supported by AppListConfig. enum class AppListConfigType { - // Legacy configs, chosen based on the size of the screen. - // Used when ProductivityLauncher is disabled. - kLarge, - kMedium, - kSmall, - // Config for tablet mode on typical size screens. - // Used when ProductivityLauncher is enabled. kRegular, // Config for clamshell mode. Also used for tablet mode on small screens. - // Used when ProductivityLauncher is enabled. kDense, };
diff --git a/ash/public/cpp/ash_view_ids.h b/ash/public/cpp/ash_view_ids.h index c5c434e..611b1ac 100644 --- a/ash/public/cpp/ash_view_ids.h +++ b/ash/public/cpp/ash_view_ids.h
@@ -44,6 +44,7 @@ VIEW_ID_QS_POWER_SIGNOUT_MENU_BUTTON, VIEW_ID_QS_SETTINGS_BUTTON, VIEW_ID_QS_SIGN_OUT_BUTTON, + VIEW_ID_QS_SUPERVISED_BUTTON, VIEW_ID_QS_USER_AVATAR_BUTTON, VIEW_ID_QS_VERSION_BUTTON, VIEW_ID_QS_MAX = VIEW_ID_QS_VERSION_BUTTON,
diff --git a/ash/resources/vector_icons/unified_menu_dark_mode.icon b/ash/resources/vector_icons/unified_menu_dark_mode.icon index 667abf487..148fe33 100644 --- a/ash/resources/vector_icons/unified_menu_dark_mode.icon +++ b/ash/resources/vector_icons/unified_menu_dark_mode.icon
@@ -2,28 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -CANVAS_DIMENSIONS, 40, -MOVE_TO, 20, 36, -R_CUBIC_TO, 8.84f, 0, 16, -7.16f, 16, -16, -CUBIC_TO_SHORTHAND, 28.84f, 4, 20, 4, -CUBIC_TO_SHORTHAND, 4, 11.16f, 4, 20, -R_CUBIC_TO, 0, 8.84f, 7.16f, 16, 16, 16, -CLOSE, -R_MOVE_TO, 0, -28.23f, -R_CUBIC_TO, 6.76f, 0, 12.24f, 5.48f, 12.24f, 12.24f, -CUBIC_TO_SHORTHAND, 26.76f, 32.24f, 20, 32.24f, -V_LINE_TO, 7.77f, -CLOSE - CANVAS_DIMENSIONS, 20, -MOVE_TO, 10, 18, -R_CUBIC_TO, 4.42f, 0, 8, -3.58f, 8, -8, -R_CUBIC_TO, 0, -4.42f, -3.58f, -8, -8, -8, -R_CUBIC_TO, -4.42f, 0, -8, 3.58f, -8, 8, -R_CUBIC_TO, 0, 4.42f, 3.58f, 8, 8, 8, +MOVE_TO, 2, 10, +R_ARC_TO, 8, 8, 0, 1, 1, 16, 0, +R_ARC_TO, 8, 8, 0, 0, 1, -16, 0, CLOSE, -R_MOVE_TO, 0, -14.12f, -R_CUBIC_TO, 3.38f, 0, 6.12f, 2.74f, 6.12f, 6.12f, -R_CUBIC_TO, 0, 3.38f, -2.74f, 6.12f, -6.12f, 6.12f, +R_MOVE_TO, 1.88f, 0, +ARC_TO, 6.12f, 6.12f, 0, 0, 0, 10, 16.12f, V_LINE_TO, 3.88f, +ARC_TO, 6.12f, 6.12f, 0, 0, 0, 3.88f, 10, CLOSE
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc index c417f718..73ad887 100644 --- a/ash/shelf/shelf_view.cc +++ b/ash/shelf/shelf_view.cc
@@ -1573,7 +1573,8 @@ } void ShelfView::MoveDragViewTo(int primary_axis_coordinate) { - if (visible_views_indices_.empty()) { + if (visible_views_indices_.empty() || + !IsItemPinned(model_->items()[visible_views_indices_.front()])) { DCHECK(model_->in_shelf_party()); if (shelf_->IsHorizontalAlignment()) { if (drag_view_->x() != app_icons_layout_offset_) @@ -1858,7 +1859,7 @@ if (!features::IsDragUnpinnedAppToPinEnabled()) return false; - if (visible_views_indices_.empty()) { + if (!base::Contains(visible_views_indices_, dragged_view_index)) { DCHECK(model_->in_shelf_party()); return false; } @@ -2654,6 +2655,9 @@ } void ShelfView::HandleShelfParty() { + if (!base::FeatureList::IsEnabled(features::kShelfParty)) + return; + UpdateShelfItemViewsVisibility(); PreferredSizeChanged(); AnimateToIdealBounds();
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc index b87835c..63211905 100644 --- a/ash/shelf/shelf_view_unittest.cc +++ b/ash/shelf/shelf_view_unittest.cc
@@ -3597,7 +3597,9 @@ std::pair<ShelfAlignment, ShelfAutoHideBehavior>> { public: ShelfPartyTest() - : ShelfViewTest(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {} + : ShelfViewTest(base::test::TaskEnvironment::TimeSource::MOCK_TIME) { + scoped_feature_list_.InitAndEnableFeature(features::kShelfParty); + } ShelfPartyTest(const ShelfPartyTest&) = delete; ShelfPartyTest& operator=(const ShelfPartyTest&) = delete; ~ShelfPartyTest() override = default; @@ -3607,6 +3609,9 @@ shelf_view_->shelf()->SetAlignment(GetParam().first); shelf_view_->shelf()->SetAutoHideBehavior(GetParam().second); } + + private: + base::test::ScopedFeatureList scoped_feature_list_; }; INSTANTIATE_TEST_SUITE_P(
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_tray.cc b/ash/system/accessibility/select_to_speak/select_to_speak_tray.cc index 9fe9c87..4ef5eab 100644 --- a/ash/system/accessibility/select_to_speak/select_to_speak_tray.cc +++ b/ash/system/accessibility/select_to_speak/select_to_speak_tray.cc
@@ -14,6 +14,7 @@ #include "ash/system/tray/tray_container.h" #include "ash/system/tray/tray_utils.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/metadata/metadata_impl_macros.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/views/border.h" #include "ui/views/controls/image_view.h" @@ -26,12 +27,6 @@ namespace { -// This constant must be kept the same as SELECT_TO_SPEAK_TRAY_CLASS_NAME in -// chrome/browser/resources/chromeos/accessibility/select_to_speak/ -// select_to_speak.js. -constexpr char kSelectToSpeakTrayClassName[] = - "tray/TrayBackgroundView/SelectToSpeakTray"; - gfx::ImageSkia GetImageOnCurrentSelectToSpeakStatus() { auto* shell = Shell::Get(); const SkColor color = @@ -54,18 +49,23 @@ SelectToSpeakTray::SelectToSpeakTray(Shelf* shelf, TrayBackgroundViewCatalogName catalog_name) - : TrayBackgroundView(shelf, catalog_name), icon_(new views::ImageView()) { + : TrayBackgroundView(shelf, catalog_name) { + SetPressedCallback(base::BindRepeating([](const ui::Event& event) { + Shell::Get()->accessibility_controller()->RequestSelectToSpeakStateChange(); + })); + const gfx::ImageSkia inactive_image = gfx::CreateVectorIcon( kSystemTraySelectToSpeakNewuiIcon, TrayIconColor(Shell::Get()->session_controller()->GetSessionState())); - icon_->SetImage(inactive_image); + auto icon = std::make_unique<views::ImageView>(); + icon->SetImage(inactive_image); const int vertical_padding = (kTrayItemSize - inactive_image.height()) / 2; const int horizontal_padding = (kTrayItemSize - inactive_image.width()) / 2; - icon_->SetBorder(views::CreateEmptyBorder( + icon->SetBorder(views::CreateEmptyBorder( gfx::Insets::VH(vertical_padding, horizontal_padding))); - icon_->SetTooltipText(l10n_util::GetStringUTF16( + icon->SetTooltipText(l10n_util::GetStringUTF16( IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SELECT_TO_SPEAK)); - tray_container()->AddChildView(icon_); + icon_ = tray_container()->AddChildView(std::move(icon)); // Observe the accessibility controller state changes to know when Select to // Speak state is updated or when it is disabled/enabled. @@ -91,15 +91,6 @@ IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SELECT_TO_SPEAK)); } -const char* SelectToSpeakTray::GetClassName() const { - return kSelectToSpeakTrayClassName; -} - -bool SelectToSpeakTray::PerformAction(const ui::Event& event) { - Shell::Get()->accessibility_controller()->RequestSelectToSpeakStateChange(); - return true; -} - void SelectToSpeakTray::OnThemeChanged() { TrayBackgroundView::OnThemeChanged(); UpdateIconOnColorChanges(); @@ -134,4 +125,7 @@ icon_->SetImage(GetImageOnCurrentSelectToSpeakStatus()); } +BEGIN_METADATA(SelectToSpeakTray, TrayBackgroundView); +END_METADATA + } // namespace ash
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_tray.h b/ash/system/accessibility/select_to_speak/select_to_speak_tray.h index e4f7df9..2f04cfe 100644 --- a/ash/system/accessibility/select_to_speak/select_to_speak_tray.h +++ b/ash/system/accessibility/select_to_speak/select_to_speak_tray.h
@@ -10,7 +10,7 @@ #include "ash/constants/tray_background_view_catalog.h" #include "ash/public/cpp/session/session_observer.h" #include "ash/system/tray/tray_background_view.h" -#include "ui/views/controls/image_view.h" +#include "ui/base/metadata/metadata_header_macros.h" namespace views { class ImageView; @@ -18,24 +18,24 @@ namespace ash { +class Shelf; + // A button in the tray that lets users start/stop Select-to-Speak. class ASH_EXPORT SelectToSpeakTray : public TrayBackgroundView, public AccessibilityObserver, public SessionObserver { public: - SelectToSpeakTray(Shelf* shelf, TrayBackgroundViewCatalogName catalog_name); + METADATA_HEADER(SelectToSpeakTray); + SelectToSpeakTray(Shelf* shelf, TrayBackgroundViewCatalogName catalog_name); SelectToSpeakTray(const SelectToSpeakTray&) = delete; SelectToSpeakTray& operator=(const SelectToSpeakTray&) = delete; - ~SelectToSpeakTray() override; // TrayBackgroundView: void Initialize() override; std::u16string GetAccessibleNameForTray() override; void HandleLocaleChange() override; - const char* GetClassName() const override; - bool PerformAction(const ui::Event& event) override; void OnThemeChanged() override; // The SelectToSpeakTray does not have a bubble, so these functions are // no-ops. @@ -59,8 +59,8 @@ // Updates icon if the color of the icon changes. void UpdateIconOnColorChanges(); - // Weak pointer, will be parented by TrayContainer for its lifetime. - views::ImageView* icon_; + // Owned by TrayContainer for its lifetime. + views::ImageView* icon_ = nullptr; ScopedSessionObserver session_observer_{this}; };
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_tray_unittest.cc b/ash/system/accessibility/select_to_speak/select_to_speak_tray_unittest.cc index afd6deab..8537635 100644 --- a/ash/system/accessibility/select_to_speak/select_to_speak_tray_unittest.cc +++ b/ash/system/accessibility/select_to_speak/select_to_speak_tray_unittest.cc
@@ -22,6 +22,7 @@ #include "ui/events/event.h" #include "ui/gfx/image/image_unittest_util.h" #include "ui/gfx/paint_vector_icon.h" +#include "ui/views/controls/image_view.h" #include "ui/views/controls/label.h" namespace ash {
diff --git a/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc b/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc index 9909921..b2e4b51 100644 --- a/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc +++ b/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc
@@ -233,7 +233,6 @@ SetMinSize(gfx::Size(0, kVersionButtonHeight)); } views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON); - views::InkDrop::Get(this)->SetBaseColor(GetInkDropBaseColor(channel_)); InstallRoundedCornerHighlightPathGenerator( this, GetVersionButtonInkDropCorners(allow_user_feedback)); } @@ -261,6 +260,7 @@ void OnThemeChanged() override { views::LabelButton::OnThemeChanged(); + views::InkDrop::Get(this)->SetBaseColor(GetInkDropBaseColor(channel_)); SetBackgroundAndFont(); }
diff --git a/ash/system/network/network_detailed_view_controller.cc b/ash/system/network/network_detailed_view_controller.cc index fc36427a1..0f2222bd 100644 --- a/ash/system/network/network_detailed_view_controller.cc +++ b/ash/system/network/network_detailed_view_controller.cc
@@ -171,10 +171,14 @@ return; } - // If the captive portal UI flag is enabled, the network is connected, and - // the network is in a portal or proxy state, the user is shown the portal - // signin. + // If the captive portal UI flag is enabled, the user is logged in, the + // network is connected, and the network is in a portal or proxy state, the + // user is shown the portal signin. We do not show portal sign in for user + // not logged in because it is the only way for the user to get to the + // network details page. if (features::IsCaptivePortalUI2022Enabled() && + Shell::Get()->session_controller()->login_status() != + LoginStatus::NOT_LOGGED_IN && chromeos::network_config::StateIsConnected(network->connection_state) && IsNetworkBehindPortalOrProxy(network->portal_state)) { RecordNetworkRowClickedAction(NetworkRowClickedAction::kOpenPortalSignin);
diff --git a/ash/system/unified/buttons.cc b/ash/system/unified/buttons.cc index c28669c2..0374865c 100644 --- a/ash/system/unified/buttons.cc +++ b/ash/system/unified/buttons.cc
@@ -27,11 +27,13 @@ #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/chromeos/devicetype_utils.h" #include "ui/compositor/layer.h" +#include "ui/gfx/canvas.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/views/accessibility/view_accessibility.h" #include "ui/views/animation/ink_drop.h" #include "ui/views/background.h" #include "ui/views/controls/button/button.h" +#include "ui/views/controls/highlight_path_generator.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/label.h" #include "ui/views/layout/box_layout.h" @@ -40,6 +42,12 @@ namespace { +// Constants used with QsRevamp. +constexpr int kManagedStateButtonHeight = 32; +constexpr int kManagedStateHighlightRadius = 16; +constexpr SkScalar kManagedStateCornerRadii[] = {16, 16, 16, 16, + 16, 16, 16, 16}; + // Helper function for getting ContentLayerColor. inline SkColor GetContentLayerColor(AshColorProvider::ContentLayerType type) { return AshColorProvider::Get()->GetContentLayerColor(type); @@ -225,21 +233,37 @@ views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), kUnifiedSystemInfoSpacing)); - label_ = AddChildView(std::make_unique<views::Label>()); + if (features::IsQsRevampEnabled()) { + // Image goes first. + image_ = AddChildView(std::make_unique<views::ImageView>()); + label_ = AddChildView(std::make_unique<views::Label>()); + layout_manager->set_minimum_cross_axis_size(kManagedStateButtonHeight); + layout_manager->set_main_axis_alignment( + views::BoxLayout::MainAxisAlignment::kCenter); + } else { + // Label goes first. + label_ = AddChildView(std::make_unique<views::Label>()); + image_ = AddChildView(std::make_unique<views::ImageView>()); + // Shrink the label if needed so the icon fits. + layout_manager->SetFlexForView(label_, 1); + } + label_->SetAutoColorReadabilityEnabled(false); label_->SetSubpixelRenderingEnabled(false); label_->SetText(l10n_util::GetStringUTF16(label_id)); - image_ = AddChildView(std::make_unique<views::ImageView>()); image_->SetPreferredSize( gfx::Size(kUnifiedSystemInfoHeight, kUnifiedSystemInfoHeight)); - // Shrink the label if needed so the icon fits. - layout_manager->SetFlexForView(label_, 1); - SetInstallFocusRingOnFocus(true); views::FocusRing::Get(this)->SetColorId(ui::kColorAshFocusRing); - views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::OFF); + if (features::IsQsRevampEnabled()) { + views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON); + views::InstallRoundRectHighlightPathGenerator(this, gfx::Insets(), + kManagedStateHighlightRadius); + } else { + views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::OFF); + } } views::View* ManagedStateView::GetTooltipHandlerForPoint( @@ -255,6 +279,27 @@ image_->SetImage(gfx::CreateVectorIcon( icon_, GetContentLayerColor( AshColorProvider::ContentLayerType::kIconColorSecondary))); + if (features::IsQsRevampEnabled()) { + const std::pair<SkColor, float> base_color_and_opacity = + AshColorProvider::Get()->GetInkDropBaseColorAndOpacity(); + views::InkDrop::Get(this)->SetBaseColor(base_color_and_opacity.first); + } +} + +void ManagedStateView::PaintButtonContents(gfx::Canvas* canvas) { + if (!features::IsQsRevampEnabled()) + return; + // Draw a button outline similar to ChannelIndicatorQuickSettingsView's + // VersionButton outline. + cc::PaintFlags flags; + flags.setColor(AshColorProvider::Get()->GetContentLayerColor( + ColorProvider::ContentLayerType::kSeparatorColor)); + flags.setStyle(cc::PaintFlags::kStroke_Style); + flags.setAntiAlias(true); + canvas->DrawPath( + SkPath().addRoundRect(gfx::RectToSkRect(GetLocalBounds()), + kManagedStateCornerRadii, SkPathDirection::kCW), + flags); } BEGIN_METADATA(ManagedStateView, views::Button) @@ -328,6 +373,10 @@ : base::UTF8ToUTF16(enterprise_domain_manager); managed_string = l10n_util::GetStringFUTF16(IDS_ASH_SHORT_MANAGED_BY, display_domain_manager); + if (features::IsQsRevampEnabled()) { + // When the managed string is short it is used as the button label. + label()->SetText(managed_string); + } } SetTooltipText(managed_string); } @@ -341,6 +390,7 @@ : ManagedStateView(PressedCallback(), IDS_ASH_STATUS_TRAY_SUPERVISED_LABEL, GetSupervisedUserIcon()) { + SetID(VIEW_ID_QS_SUPERVISED_BUTTON); bool visible = Shell::Get()->session_controller()->IsUserChild(); SetVisible(visible); if (visible) @@ -350,6 +400,7 @@ // to show a similar ui to enterprise managed accounts. Disable button // state for now. SetState(ButtonState::STATE_DISABLED); + views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::OFF); } BEGIN_METADATA(SupervisedUserView, ManagedStateView)
diff --git a/ash/system/unified/buttons.h b/ash/system/unified/buttons.h index 79769ec..565ecf332 100644 --- a/ash/system/unified/buttons.h +++ b/ash/system/unified/buttons.h
@@ -123,10 +123,13 @@ int label_id, const gfx::VectorIcon& icon); + views::Label* label() { return label_; } + private: // views::Button: views::View* GetTooltipHandlerForPoint(const gfx::Point& point) override; void OnThemeChanged() override; + void PaintButtonContents(gfx::Canvas* canvas) override; // Owned by views hierarchy. views::Label* label_ = nullptr;
diff --git a/ash/system/unified/quick_settings_header.cc b/ash/system/unified/quick_settings_header.cc index 1ac9e39..5f5debd 100644 --- a/ash/system/unified/quick_settings_header.cc +++ b/ash/system/unified/quick_settings_header.cc
@@ -8,12 +8,15 @@ #include "ash/constants/ash_features.h" #include "ash/public/cpp/system_tray_client.h" +#include "ash/resources/vector_icons/vector_icons.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "ash/shell_delegate.h" #include "ash/system/channel_indicator/channel_indicator_quick_settings_view.h" #include "ash/system/channel_indicator/channel_indicator_utils.h" #include "ash/system/model/system_tray_model.h" +#include "ash/system/unified/buttons.h" +#include "ash/system/unified/unified_system_tray_controller.h" #include "components/session_manager/session_manager_types.h" #include "ui/base/metadata/metadata_impl_macros.h" #include "ui/views/layout/box_layout.h" @@ -28,7 +31,8 @@ } // namespace -QuickSettingsHeader::QuickSettingsHeader() { +QuickSettingsHeader::QuickSettingsHeader( + UnifiedSystemTrayController* controller) { DCHECK(features::IsQsRevampEnabled()); auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>( @@ -37,6 +41,11 @@ layout->set_cross_axis_alignment( views::BoxLayout::CrossAxisAlignment::kStretch); + enterprise_managed_view_ = + AddChildView(std::make_unique<EnterpriseManagedView>(controller)); + + supervised_view_ = AddChildView(std::make_unique<SupervisedUserView>()); + // If the release track is not "stable" then show the channel indicator UI. auto channel = Shell::Get()->shell_delegate()->GetChannel(); if (channel_indicator_utils::IsDisplayableChannel(channel) && @@ -55,10 +64,13 @@ QuickSettingsHeader::~QuickSettingsHeader() = default; +void QuickSettingsHeader::ChildVisibilityChanged(views::View* child) { + UpdateVisibility(); +} + void QuickSettingsHeader::UpdateVisibility() { - // TODO(b/251724754): Update condition when enterprise management view is - // added. - bool should_show = !!channel_view_; + bool should_show = enterprise_managed_view_->GetVisible() || + supervised_view_->GetVisible() || !!channel_view_; SetVisible(should_show); }
diff --git a/ash/system/unified/quick_settings_header.h b/ash/system/unified/quick_settings_header.h index 00adaca2..58480b3 100644 --- a/ash/system/unified/quick_settings_header.h +++ b/ash/system/unified/quick_settings_header.h
@@ -11,6 +11,9 @@ namespace ash { class ChannelIndicatorQuickSettingsView; +class EnterpriseManagedView; +class SupervisedUserView; +class UnifiedSystemTrayController; // The header view shown at the top of the `QuickSettingsView`. Contains an // optional "Managed by" button and an optional release channel indicator. Sets @@ -19,11 +22,14 @@ public: METADATA_HEADER(QuickSettingsHeader); - QuickSettingsHeader(); + explicit QuickSettingsHeader(UnifiedSystemTrayController* controller); QuickSettingsHeader(const QuickSettingsHeader&) = delete; QuickSettingsHeader& operator=(const QuickSettingsHeader&) = delete; ~QuickSettingsHeader() override; + // views::View: + void ChildVisibilityChanged(views::View* child) override; + ChannelIndicatorQuickSettingsView* channel_view_for_test() { return channel_view_; } @@ -33,6 +39,9 @@ // invisible so it does not consume any space. void UpdateVisibility(); + // Owned by views hierarchy. + EnterpriseManagedView* enterprise_managed_view_ = nullptr; + SupervisedUserView* supervised_view_ = nullptr; ChannelIndicatorQuickSettingsView* channel_view_ = nullptr; };
diff --git a/ash/system/unified/quick_settings_header_unittest.cc b/ash/system/unified/quick_settings_header_unittest.cc index 91c689e0..7059fef 100644 --- a/ash/system/unified/quick_settings_header_unittest.cc +++ b/ash/system/unified/quick_settings_header_unittest.cc
@@ -7,12 +7,25 @@ #include <memory> #include "ash/constants/ash_features.h" +#include "ash/public/cpp/ash_view_ids.h" +#include "ash/session/session_controller_impl.h" +#include "ash/shell.h" +#include "ash/system/model/enterprise_domain_model.h" +#include "ash/system/model/system_tray_model.h" +#include "ash/system/unified/unified_system_tray_controller.h" +#include "ash/system/unified/unified_system_tray_model.h" #include "ash/test/ash_test_base.h" #include "ash/test_shell_delegate.h" #include "base/test/scoped_feature_list.h" +#include "components/user_manager/user_type.h" #include "components/version_info/channel.h" namespace ash { +namespace { + +EnterpriseDomainModel* GetEnterpriseDomainModel() { + return Shell::Get()->system_tray_model()->enterprise_domain(); +} class QuickSettingsHeaderTest : public NoSessionAshTestBase { public: @@ -25,43 +38,152 @@ // Install a test delegate to allow overriding channel version. auto delegate = std::make_unique<TestShellDelegate>(); test_shell_delegate_ = delegate.get(); - AshTestBase::SetUp(std::move(delegate)); + NoSessionAshTestBase::SetUp(std::move(delegate)); + + model_ = base::MakeRefCounted<UnifiedSystemTrayModel>(nullptr); + controller_ = std::make_unique<UnifiedSystemTrayController>(model_.get()); + } + + void TearDown() override { + header_.reset(); + controller_.reset(); + model_.reset(); + NoSessionAshTestBase::TearDown(); + } + + // Creates the object under test. Not part of SetUp() because sometimes tests + // need to setup the shell delegate or login before creating the header. + void CreateQuickSettingsHeader() { + header_ = std::make_unique<QuickSettingsHeader>(controller_.get()); + } + + views::View* GetManagedButton() { + return header_->GetViewByID(VIEW_ID_QS_MANAGED_BUTTON); + } + + views::View* GetSupervisedButton() { + return header_->GetViewByID(VIEW_ID_QS_SUPERVISED_BUTTON); } base::test::ScopedFeatureList feature_list_; TestShellDelegate* test_shell_delegate_ = nullptr; + scoped_refptr<UnifiedSystemTrayModel> model_; + std::unique_ptr<UnifiedSystemTrayController> controller_; + std::unique_ptr<QuickSettingsHeader> header_; }; TEST_F(QuickSettingsHeaderTest, HiddenByDefaultBeforeLogin) { - QuickSettingsHeader header; + CreateQuickSettingsHeader(); + + EXPECT_FALSE(GetManagedButton()->GetVisible()); + EXPECT_FALSE(GetSupervisedButton()->GetVisible()); // By default, channel view is not created. - EXPECT_FALSE(header.channel_view_for_test()); + EXPECT_FALSE(header_->channel_view_for_test()); // Since no views are created, the header is hidden. - EXPECT_FALSE(header.GetVisible()); + EXPECT_FALSE(header_->GetVisible()); } TEST_F(QuickSettingsHeaderTest, DoesNotShowChannelViewBeforeLogin) { test_shell_delegate_->set_channel(version_info::Channel::BETA); - QuickSettingsHeader header; + CreateQuickSettingsHeader(); - EXPECT_FALSE(header.channel_view_for_test()); - EXPECT_FALSE(header.GetVisible()); + EXPECT_FALSE(header_->channel_view_for_test()); + EXPECT_FALSE(header_->GetVisible()); } TEST_F(QuickSettingsHeaderTest, ShowsChannelViewAfterLogin) { test_shell_delegate_->set_channel(version_info::Channel::BETA); SimulateUserLogin("user@gmail.com"); - QuickSettingsHeader header; + CreateQuickSettingsHeader(); // Channel view is created. - EXPECT_TRUE(header.channel_view_for_test()); + EXPECT_TRUE(header_->channel_view_for_test()); // Header is shown. - EXPECT_TRUE(header.GetVisible()); + EXPECT_TRUE(header_->GetVisible()); } +TEST_F(QuickSettingsHeaderTest, EnterpriseManagedDeviceVisible) { + CreateQuickSettingsHeader(); + + // Simulate enterprise information becoming available. + GetEnterpriseDomainModel()->SetDeviceEnterpriseInfo( + DeviceEnterpriseInfo{"example.com", /*active_directory_managed=*/false, + ManagementDeviceMode::kChromeEnterprise}); + + EXPECT_TRUE(GetManagedButton()->GetVisible()); + EXPECT_EQ(GetManagedButton()->GetTooltipText({}), u"Managed by example.com"); + EXPECT_TRUE(header_->GetVisible()); +} + +TEST_F(QuickSettingsHeaderTest, EnterpriseManagedActiveDirectoryVisible) { + CreateQuickSettingsHeader(); + + // Simulate enterprise information becoming available. + GetEnterpriseDomainModel()->SetDeviceEnterpriseInfo( + DeviceEnterpriseInfo{"", /*active_directory_managed=*/true, + ManagementDeviceMode::kChromeEnterprise}); + + EXPECT_TRUE(GetManagedButton()->GetVisible()); + EXPECT_EQ(GetManagedButton()->GetTooltipText({}), + u"This Chrome device is enterprise managed"); + EXPECT_TRUE(header_->GetVisible()); +} + +TEST_F(QuickSettingsHeaderTest, EnterpriseManagedAccountVisible) { + CreateQuickSettingsHeader(); + + // Simulate enterprise information becoming available. + GetEnterpriseDomainModel()->SetEnterpriseAccountDomainInfo("example.com"); + + EXPECT_TRUE(GetManagedButton()->GetVisible()); + EXPECT_EQ(GetManagedButton()->GetTooltipText({}), u"Managed by example.com"); + EXPECT_TRUE(header_->GetVisible()); +} + +TEST_F(QuickSettingsHeaderTest, BothChannelAndEnterpriseVisible) { + test_shell_delegate_->set_channel(version_info::Channel::BETA); + GetEnterpriseDomainModel()->SetDeviceEnterpriseInfo( + DeviceEnterpriseInfo{"example.com", /*active_directory_managed=*/false, + ManagementDeviceMode::kChromeEnterprise}); + SimulateUserLogin("user@gmail.com"); + + CreateQuickSettingsHeader(); + + EXPECT_TRUE(GetManagedButton()->GetVisible()); + EXPECT_TRUE(header_->channel_view_for_test()); + EXPECT_TRUE(header_->GetVisible()); +} + +TEST_F(QuickSettingsHeaderTest, ChildVisible) { + CreateQuickSettingsHeader(); + + // Before login the supervised user view is invisible. + EXPECT_FALSE(GetSupervisedButton()->GetVisible()); + + // Simulate supervised user logging in. + SessionControllerImpl* session = Shell::Get()->session_controller(); + TestSessionControllerClient* client = GetSessionControllerClient(); + client->Reset(); + client->AddUserSession("child@test.com", user_manager::USER_TYPE_CHILD); + client->SetSessionState(session_manager::SessionState::ACTIVE); + UserSession user_session = *session->GetUserSession(0); + user_session.custodian_email = "parent@test.com"; + session->UpdateUserSession(std::move(user_session)); + + // Recreate the header after login. + CreateQuickSettingsHeader(); + + // Now the supervised user view is visible. + EXPECT_TRUE(GetSupervisedButton()->GetVisible()); + EXPECT_EQ(GetSupervisedButton()->GetTooltipText({}), + u"Account managed by parent@test.com"); + EXPECT_TRUE(header_->GetVisible()); +} + +} // namespace } // namespace ash
diff --git a/ash/system/unified/quick_settings_view.cc b/ash/system/unified/quick_settings_view.cc index 815534e..912f28d 100644 --- a/ash/system/unified/quick_settings_view.cc +++ b/ash/system/unified/quick_settings_view.cc
@@ -140,7 +140,7 @@ AddChildView(std::make_unique<SystemTrayContainer>()); header_ = system_tray_container_->AddChildView( - std::make_unique<QuickSettingsHeader>()); + std::make_unique<QuickSettingsHeader>(controller_)); feature_tiles_container_ = system_tray_container_->AddChildView( std::make_unique<FeatureTilesContainerView>(controller_)); page_indicator_view_ = system_tray_container_->AddChildView(
diff --git a/ash/system/unified/unified_system_info_view_unittest.cc b/ash/system/unified/unified_system_info_view_unittest.cc index ee7a3fe..bd183a3 100644 --- a/ash/system/unified/unified_system_info_view_unittest.cc +++ b/ash/system/unified/unified_system_info_view_unittest.cc
@@ -27,6 +27,8 @@ // - Whether the release track UI feature is enabled, and // - Whether the release track is a value other than "stable" // The release track UI only shows if both conditions are met. +// +// NOTE: For QsRevamp, see similar tests in QuickSettingsHeaderTest. class UnifiedSystemInfoViewTest : public AshTestBase, public testing::WithParamInterface<std::tuple<bool, bool>> {
diff --git a/ash/webui/common/resources/quick_unlock/pin_keyboard.html b/ash/webui/common/resources/quick_unlock/pin_keyboard.html index bedf3a0..18d2ddc 100644 --- a/ash/webui/common/resources/quick_unlock/pin_keyboard.html +++ b/ash/webui/common/resources/quick_unlock/pin_keyboard.html
@@ -144,9 +144,7 @@ outline: 0; position: relative; text-align: center; - width: 200px; - - @apply --pin-keyboard-pin-input-style; + width: var(--pin-keyboard-pin-input-width, 200px); } #pinInput[make-contrast] {
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc index f54a767..df5500f 100644 --- a/ash/wm/desks/desks_unittests.cc +++ b/ash/wm/desks/desks_unittests.cc
@@ -79,9 +79,12 @@ #include "ash/wm/workspace_controller.h" #include "base/containers/contains.h" #include "base/containers/cxx20_erase.h" +#include "base/functional/callback_forward.h" #include "base/ranges/algorithm.h" +#include "base/scoped_observation.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/simple_test_clock.h" @@ -99,6 +102,7 @@ #include "ui/aura/client/window_parenting_client.h" #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/window.h" +#include "ui/aura/window_observer.h" #include "ui/base/clipboard/clipboard_buffer.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/base/ime/ash/fake_ime_keyboard.h" @@ -2523,10 +2527,11 @@ void SetUp() override { DesksTest::SetUp(); - // Enter tablet mode. Avoid TabletModeController::OnGetSwitchStates() from - // disabling tablet mode. - base::RunLoop().RunUntilIdle(); - TabletModeControllerTestApi().EnterTabletMode(); + // Enter tablet mode after detaching all mouse devices, as this is needed + // when running these tests on an actual workstation. + TabletModeControllerTestApi tablet_mode_test_api; + tablet_mode_test_api.DetachAllMice(); + tablet_mode_test_api.EnterTabletMode(); } SplitViewController* split_view_controller() { @@ -2534,6 +2539,77 @@ } }; +// Triggers a callback when the visibility of an observed window changes for the +// first time since creation of this observer. +class WindowVisibilityObserver : public aura::WindowObserver { + public: + WindowVisibilityObserver(aura::Window* window, base::OnceClosure callback) + : on_visibility_changed_callback_(std::move(callback)) { + DCHECK(on_visibility_changed_callback_); + observer_.Observe(window); + } + WindowVisibilityObserver(const WindowVisibilityObserver&) = delete; + WindowVisibilityObserver& operator=(const WindowVisibilityObserver&) = delete; + ~WindowVisibilityObserver() override = default; + + // aura::WindowObserver: + void OnWindowVisibilityChanged(aura::Window* window, bool visible) override { + if (observer_.IsObservingSource(window) && on_visibility_changed_callback_) + std::move(on_visibility_changed_callback_).Run(); + } + + void OnWindowDestroying(aura::Window* window) override { + DCHECK(observer_.IsObservingSource(window)); + observer_.Reset(); + } + + private: + base::ScopedObservation<aura::Window, aura::WindowObserver> observer_{this}; + + base::OnceClosure on_visibility_changed_callback_; +}; + +// Regression test for https://crbug.com/1368587. +TEST_P(TabletModeDesksTest, CantDestroyBackdropWhileHiding) { + auto* controller = DesksController::Get(); + ASSERT_EQ(1u, controller->desks().size()); + const Desk* desk_1 = controller->desks()[0].get(); + + auto window = CreateAppWindow(gfx::Rect(0, 0, 250, 100)); + wm::ActivateWindow(window.get()); + EXPECT_EQ(window.get(), window_util::GetActiveWindow()); + + // Verify that there is a visible backdrop for the app window on the current + // desk. + auto* desk_1_backdrop_controller = + GetDeskBackdropController(desk_1, Shell::GetPrimaryRootWindow()); + auto* backdrop_window = desk_1_backdrop_controller->backdrop_window(); + ASSERT_TRUE(backdrop_window); + EXPECT_TRUE(backdrop_window->IsVisible()); + + // Intercept the visibility change notification of the backdrop (which will + // happen when we enter overview below), and hide the app window. This + // triggers an update of the backdrop while still in the process of hiding + // it. Now that the single app window available is hidden, the backdrop + // controller will try to destroy the backdrop. This should be prevented as + // we don't allow `BackdropController::Hide()` to be called recursively. + auto* app_window = window.get(); + WindowVisibilityObserver observer{ + backdrop_window, + base::BindLambdaForTesting([app_window, backdrop_window]() { + app_window->Hide(); + EXPECT_FALSE(backdrop_window->IsVisible()); + })}; + + // Enter overview and expect that the backdrop is still present for desk_1 but + // hidden. + auto* overview_controller = Shell::Get()->overview_controller(); + EnterOverview(); + EXPECT_TRUE(overview_controller->InOverviewSession()); + ASSERT_TRUE(desk_1_backdrop_controller->backdrop_window()); + EXPECT_FALSE(desk_1_backdrop_controller->backdrop_window()->IsVisible()); +} + TEST_P(TabletModeDesksTest, Backdrops) { auto* controller = DesksController::Get(); NewDesk();
diff --git a/ash/wm/workspace/backdrop_controller.cc b/ash/wm/workspace/backdrop_controller.cc index 504450f0..fd91414 100644 --- a/ash/wm/workspace/backdrop_controller.cc +++ b/ash/wm/workspace/backdrop_controller.cc
@@ -306,9 +306,8 @@ continue; // No need to check the visibility or the activateability of the window if - // this is an inactive desk's container. - if (!desks_util::IsDeskContainer(container_) || - desks_util::IsActiveDeskContainer(container_)) { + // this is not a desk container. + if (desks_util::IsDeskContainer(container_)) { if (!window->layer()->GetTargetVisibility()) continue; @@ -558,10 +557,12 @@ } void BackdropController::Hide(bool destroy, bool animate) { - if (!backdrop_) + if (!backdrop_ || is_hiding_backdrop_) return; DCHECK(backdrop_window_); + base::AutoReset<bool> lock(&is_hiding_backdrop_, true); + const aura::Window::Windows windows = container_->children(); auto window_iter = base::ranges::find(windows, backdrop_window_); ++window_iter;
diff --git a/ash/wm/workspace/backdrop_controller.h b/ash/wm/workspace/backdrop_controller.h index 8e98168..277c77a 100644 --- a/ash/wm/workspace/backdrop_controller.h +++ b/ash/wm/workspace/backdrop_controller.h
@@ -183,6 +183,11 @@ // in overview mode. bool pause_update_ = false; + // If true, we're inside the stack of `Hide()`. This is used to avoid + // recursively calling `Hide()` which may lead to destroying the backdrop + // widget while it's still being hidden. https://crbug.com/1368587. + bool is_hiding_backdrop_ = false; + base::ScopedMultiSourceObservation<WindowBackdrop, WindowBackdrop::Observer> window_backdrop_observations_{this};
diff --git a/base/test/scoped_feature_list.cc b/base/test/scoped_feature_list.cc index 26fdd561..0fb2f67 100644 --- a/base/test/scoped_feature_list.cc +++ b/base/test/scoped_feature_list.cc
@@ -287,15 +287,14 @@ } // namespace -ScopedFeatureList::FeatureAndParams::FeatureAndParams( - const Feature& feature, - const FieldTrialParams& params) +FeatureRefAndParams::FeatureRefAndParams(const Feature& feature, + const FieldTrialParams& params) : feature(feature), params(params) {} -ScopedFeatureList::FeatureAndParams::~FeatureAndParams() = default; +FeatureRefAndParams::FeatureRefAndParams(const FeatureRefAndParams& other) = + default; -ScopedFeatureList::FeatureAndParams::FeatureAndParams( - const FeatureAndParams& other) = default; +FeatureRefAndParams::~FeatureRefAndParams() = default; ScopedFeatureList::ScopedFeatureList() = default; @@ -438,7 +437,7 @@ void ScopedFeatureList::InitWithFeaturesImpl( const std::vector<FeatureRef>& enabled_features, - const std::vector<FeatureAndParams>& enabled_features_and_params, + const std::vector<FeatureRefAndParams>& enabled_features_and_params, const std::vector<FeatureRef>& disabled_features, bool keep_existing_states) { DCHECK(!init_called_); @@ -485,7 +484,7 @@ } void ScopedFeatureList::InitWithFeaturesAndParameters( - const std::vector<FeatureAndParams>& enabled_features, + const std::vector<FeatureRefAndParams>& enabled_features, const std::vector<FeatureRef>& disabled_features) { InitWithFeaturesImpl({}, enabled_features, disabled_features); }
diff --git a/base/test/scoped_feature_list.h b/base/test/scoped_feature_list.h index 64699d31..6d9bfa8 100644 --- a/base/test/scoped_feature_list.h +++ b/base/test/scoped_feature_list.h
@@ -21,6 +21,20 @@ namespace base::test { +// A reference to a base::Feature and field trial params that should be force +// enabled and overwritten for test purposes. +struct FeatureRefAndParams { + FeatureRefAndParams(const Feature& feature ABSL_ATTRIBUTE_LIFETIME_BOUND, + const FieldTrialParams& params); + + FeatureRefAndParams(const FeatureRefAndParams& other); + + ~FeatureRefAndParams(); + + const Feature& feature; + const FieldTrialParams params; +}; + // A lightweight wrapper for a reference to a base::Feature. Allows lists of // features to be enabled/disabled to be easily passed without actually copying // the underlying base::Feature. Actual C++ references do not work well for this @@ -67,21 +81,6 @@ struct Features; struct FeatureWithStudyGroup; - // TODO(https://crbug.com/1370851): Temporary "alias" to allow incremental - // migration. - // A reference to a base::Feature and field trial params that should be force - // enabled and overwritten for test purposes. - struct FeatureAndParams { - FeatureAndParams(const Feature& feature ABSL_ATTRIBUTE_LIFETIME_BOUND, - const FieldTrialParams& params); - ~FeatureAndParams(); - - FeatureAndParams(const FeatureAndParams& other); - - const Feature& feature; - const FieldTrialParams params; - }; - // Constructs the instance in a non-initialized state. ScopedFeatureList(); @@ -158,7 +157,7 @@ // Note: This creates a scoped global field trial list if there is not // currently one. void InitWithFeaturesAndParameters( - const std::vector<FeatureAndParams>& enabled_features, + const std::vector<FeatureRefAndParams>& enabled_features, const std::vector<FeatureRef>& disabled_features); // Initializes and registers a FeatureList instance based on the current @@ -183,7 +182,7 @@ // empty). void InitWithFeaturesImpl( const std::vector<FeatureRef>& enabled_features, - const std::vector<FeatureAndParams>& enabled_features_and_params, + const std::vector<FeatureRefAndParams>& enabled_features_and_params, const std::vector<FeatureRef>& disabled_features, bool keep_existing_states = true); @@ -209,8 +208,6 @@ std::unique_ptr<base::FieldTrialList> field_trial_list_; }; -using FeatureRefAndParams = ScopedFeatureList::FeatureAndParams; - } // namespace base::test #endif // BASE_TEST_SCOPED_FEATURE_LIST_H_
diff --git a/build/android/test_runner.py b/build/android/test_runner.py index ff776fbd..096569b 100755 --- a/build/android/test_runner.py +++ b/build/android/test_runner.py
@@ -1108,9 +1108,10 @@ flakiness_server=getattr(args, 'flakiness_dashboard_server', None)) - if iteration_results.GetNotPass(): - _LogRerunStatement(iteration_results.GetNotPass(), - args.wrapper_script_args) + failed_tests = (iteration_results.GetNotPass() - + iteration_results.GetSkip()) + if failed_tests: + _LogRerunStatement(failed_tests, args.wrapper_script_args) if args.break_on_failure and not iteration_results.DidRunPass(): break @@ -1198,7 +1199,13 @@ 'test filter file.') return - test_filter_file = os.path.join(os.path.relpath(constants.GetOutDirectory()), + output_directory = constants.GetOutDirectory() + if not os.path.exists(output_directory): + logging.error('Output directory not found. Unable to generate failing ' + 'test filter file.') + return + + test_filter_file = os.path.join(os.path.relpath(output_directory), _RERUN_FAILED_TESTS_FILE) arg_list = shlex.split(wrapper_arg_str) if wrapper_arg_str else sys.argv index = 0
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index f272b18..177a8d4 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn
@@ -611,7 +611,7 @@ # turned off we need the !is_nacl clause and the (is_nacl && is_clang) # clause, above. cflags_c += [ "-std=c11" ] - if (is_mac) { + if (is_apple) { # TODO(crbug.com/1284275): Switch to C++20 on all platforms. cflags_cc += [ "-std=c++20" ] } else {
diff --git a/chrome/VERSION b/chrome/VERSION index efa76680..08982a32 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=109 MINOR=0 -BUILD=5360 +BUILD=5361 PATCH=0
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java index fdb43df..5fe3849 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
@@ -103,6 +103,18 @@ new BooleanCachedFieldTrialParameter(ChromeFeatureList.GRID_TAB_SWITCHER_FOR_TABLETS, DELAY_GTS_CREATION_PARAM, true); + // Field trial parameter for enabling folio for tab strip redesign. + private static final String TAB_STRIP_REDESIGN_ENABLE_FOLIO_PARAM = "enable_folio"; + public static final BooleanCachedFieldTrialParameter TAB_STRIP_REDESIGN_ENABLE_FOLIO = + new BooleanCachedFieldTrialParameter(ChromeFeatureList.TAB_STRIP_REDESIGN, + TAB_STRIP_REDESIGN_ENABLE_FOLIO_PARAM, false); + + // Field trial parameter for enabling detached for tab strip redesign. + private static final String TAB_STRIP_REDESIGN_ENABLE_DETACHED_PARAM = "enable_detached"; + public static final BooleanCachedFieldTrialParameter TAB_STRIP_REDESIGN_ENABLE_DETACHED = + new BooleanCachedFieldTrialParameter(ChromeFeatureList.TAB_STRIP_REDESIGN, + TAB_STRIP_REDESIGN_ENABLE_DETACHED_PARAM, false); + // Field trial parameter for defining tab width for tab strip improvements. private static final String TAB_STRIP_IMPROVEMENTS_TAB_WIDTH_PARAM = "min_tab_width"; public static final DoubleCachedFieldTrialParameter TAB_STRIP_TAB_WIDTH = @@ -285,6 +297,20 @@ return ENABLE_LAUNCH_POLISH.getValue(); } + /** + * @return Whether Folio for tab strip redesign is enabled. + */ + public static boolean isTabStripFolioEnabled() { + return TAB_STRIP_REDESIGN_ENABLE_FOLIO.getValue(); + } + + /** + * @return Whether Detached for tab strip redesign is enabled. + */ + public static boolean isTabStripDetachedEnabled() { + return TAB_STRIP_REDESIGN_ENABLE_DETACHED.getValue(); + } + private static Float sTabMinWidthForTesting; /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java index 7b02d0c..927bb9a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
@@ -200,6 +200,8 @@ add(TabUiFeatureUtilities.GRID_TAB_SWITCHER_FOR_TABLETS_POLISH); add(TabUiFeatureUtilities.TAB_STRIP_TAB_WIDTH); add(TabUiFeatureUtilities.ENABLE_TAB_SELECTION_EDITOR_V2_SHARE); + add(TabUiFeatureUtilities.TAB_STRIP_REDESIGN_ENABLE_FOLIO); + add(TabUiFeatureUtilities.TAB_STRIP_REDESIGN_ENABLE_DETACHED); add(VersionNumberGetter.MIN_SDK_VERSION); } };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java index b86b224..7811843 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -1111,7 +1111,7 @@ mActivity, mActivityLifecycleDispatcher, mProfileSupplier); PriceTrackingButtonController priceTrackingButtonController = new PriceTrackingButtonController(mActivityTabProvider, - mModalDialogManagerSupplier.get(), getBottomSheetController(), + mModalDialogManagerSupplier.get(), AppCompatResources.getDrawable( mActivity, R.drawable.price_tracking_disabled), mTabBookmarkerSupplier);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java index 324a04fb..43f6f804 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinatorTest.java
@@ -99,6 +99,7 @@ // default or removed if the flag is removed. ChromeFeatureList.SYNC_ANDROID_LIMIT_NTP_PROMO_IMPRESSIONS, }) +@Features.EnableFeatures({ChromeFeatureList.FEED_CLIENT_GOOD_VISITS}) public class FeedSurfaceCoordinatorTest { private static final @SurfaceType int SURFACE_TYPE = SurfaceType.NEW_TAB_PAGE; private static final long SURFACE_CREATION_TIME_NS = 1234L;
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index c09c3d50..d978f4a 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -5061,9 +5061,12 @@ <message name="IDS_EXTENSIONS_TOOLBAR_ACTION_HOVER_CARD_FOOTER_DESCRIPTION_EXTENSION_REQUESTS_ACESSS" desc="The description in the footer of the card that shows up on mouse hover of a toolbar action when the extension requests access to the current site."> Click this extension's icon to read & change <ph name="HOST">$1<ex>google.com</ex></ph> </message> - <message name="IDS_EXTENSIONS_TOOLBAR_ACTION_HOVER_CARD_FOOTER_POLICY_LABEL_TEXT" desc="The text in the footer of the card that shows up on mouse hover of a toolbar action when extension was pinned by the administrator."> + <message name="IDS_EXTENSIONS_TOOLBAR_ACTION_HOVER_CARD_FOOTER_POLICY_LABEL_PINNED_TEXT" desc="The text in the footer of the card that shows up on mouse hover of a toolbar action when extension was pinned by the administrator."> Pinned by your administrator </message> + <message name="IDS_EXTENSIONS_TOOLBAR_ACTION_HOVER_CARD_FOOTER_POLICY_LABEL_INSTALLED_TEXT" desc="The text in the footer of the card that shows up on mouse hover of a toolbar action when extension was installed by the administrator."> + Installed by your administrator + </message> <if expr="not use_titlecase"> <message name="IDS_EXTENSIONS_CONTEXT_MENU_CANT_ACCESS_PAGE" desc="The label in an extension's context menu indicating the extension cannot access the current page. (sentence case)">
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_TOOLBAR_ACTION_HOVER_CARD_FOOTER_POLICY_LABEL_INSTALLED_TEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_TOOLBAR_ACTION_HOVER_CARD_FOOTER_POLICY_LABEL_INSTALLED_TEXT.png.sha1 new file mode 100644 index 0000000..30df743 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_TOOLBAR_ACTION_HOVER_CARD_FOOTER_POLICY_LABEL_INSTALLED_TEXT.png.sha1
@@ -0,0 +1 @@ +4e23789d21aed3dd15a26da2856cb4f860e9ebb4 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_TOOLBAR_ACTION_HOVER_CARD_FOOTER_POLICY_LABEL_TEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_TOOLBAR_ACTION_HOVER_CARD_FOOTER_POLICY_LABEL_PINNED_TEXT.png.sha1 similarity index 100% rename from chrome/app/generated_resources_grd/IDS_EXTENSIONS_TOOLBAR_ACTION_HOVER_CARD_FOOTER_POLICY_LABEL_TEXT.png.sha1 rename to chrome/app/generated_resources_grd/IDS_EXTENSIONS_TOOLBAR_ACTION_HOVER_CARD_FOOTER_POLICY_LABEL_PINNED_TEXT.png.sha1
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 708b17e..5939bee 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -3300,6 +3300,20 @@ #endif // BUILDFLAG(IS_ANDROID) #if BUILDFLAG(IS_ANDROID) +const FeatureEntry::FeatureParam kTabStripRedesignFolio[] = { + {"enable_folio", "true"}}; +const FeatureEntry::FeatureParam kTabStripRedesignDetached[] = { + {"enable_detached", "true"}}; + +const FeatureEntry::FeatureVariation kTabStripRedesignVariations[] = { + {"Folio", kTabStripRedesignFolio, std::size(kTabStripRedesignFolio), + nullptr}, + {"Detached", kTabStripRedesignDetached, + std::size(kTabStripRedesignDetached), nullptr}, +}; +#endif // BUILDFLAG(IS_ANDROID) + +#if BUILDFLAG(IS_ANDROID) constexpr FeatureEntry::FeatureParam kUpmAndroidShadowSyncingUsers[] = { {password_manager::features::kUpmExperimentVariationParam.name, password_manager::features::kUpmExperimentVariationOption[1].name}}; @@ -6466,7 +6480,9 @@ {"enable-tab-strip-redesign", flag_descriptions::kTabStripRedesignAndroidName, flag_descriptions::kTabStripRedesignAndroidDescription, kOsAndroid, - FEATURE_VALUE_TYPE(chrome::android::kTabStripRedesign)}, + FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kTabStripRedesign, + kTabStripRedesignVariations, + "TabStripRedesignAndroid")}, {"enable-conditional-tabstrip", flag_descriptions::kConditionalTabStripAndroidName,
diff --git a/chrome/browser/ash/accessibility/dictation_browsertest.cc b/chrome/browser/ash/accessibility/dictation_browsertest.cc index cee6d93..59e47a71 100644 --- a/chrome/browser/ash/accessibility/dictation_browsertest.cc +++ b/chrome/browser/ash/accessibility/dictation_browsertest.cc
@@ -1712,13 +1712,11 @@ DictationPumpkinInstallTest, ::testing::Values(speech::SpeechRecognitionType::kOnDevice)); -// TODO(crbug.com/1368843): Test is flaky on MSAN builds. -#if defined(MEMORY_SANITIZER) -#define MAYBE_WaitForInstall DISABLED_WaitForInstall -#else -#define MAYBE_WaitForInstall WaitForInstall -#endif -IN_PROC_BROWSER_TEST_P(DictationPumpkinInstallTest, MAYBE_WaitForInstall) { +// TODO(crbug.com/1368843): Test is flaky on MSAN builds. This test is +// temporarily disabled to allow the SandboxedPumpkinTagger prototype to be +// landed. It will be re-enabled when we can support Pumpkin from C++ tests +// here: https://crrev.com/c/3938318 +IN_PROC_BROWSER_TEST_P(DictationPumpkinInstallTest, DISABLED_WaitForInstall) { // Dictation will request a Pumpkin install when it starts up. Wait for // the install to succeed. WaitForInstallToSucceed();
diff --git a/chrome/browser/ash/net/network_diagnostics/network_diagnostics_util_unittest.cc b/chrome/browser/ash/net/network_diagnostics/network_diagnostics_util_unittest.cc index 137d318..d2691e0 100644 --- a/chrome/browser/ash/net/network_diagnostics/network_diagnostics_util_unittest.cc +++ b/chrome/browser/ash/net/network_diagnostics/network_diagnostics_util_unittest.cc
@@ -7,7 +7,6 @@ #include <string> #include <vector> -#include "base/ranges/algorithm.h" #include "base/strings/string_util.h" #include "testing/gtest/include/gtest/gtest.h" @@ -38,7 +37,8 @@ util::GetRandomHosts(num_hosts, prefix_length); // Ensure |random_hosts| has unique entries. std::sort(random_hosts.begin(), random_hosts.end()); - EXPECT_TRUE(base::ranges::adjacent_find(random_hosts) == random_hosts.end()); + EXPECT_TRUE(std::adjacent_find(random_hosts.begin(), random_hosts.end()) == + random_hosts.end()); } TEST(NetworkDiagnosticsUtilTest, TestGetRandomHostsWithScheme) { @@ -48,7 +48,8 @@ util::GetRandomHostsWithScheme(num_hosts, prefix_length, kHttpsScheme); // Ensure |random_hosts| has unique entries. std::sort(random_hosts.begin(), random_hosts.end()); - EXPECT_TRUE(base::ranges::adjacent_find(random_hosts) == random_hosts.end()); + EXPECT_TRUE(std::adjacent_find(random_hosts.begin(), random_hosts.end()) == + random_hosts.end()); // Ensure hosts in |random_hosts| start with |kHttpsScheme|. for (const auto& host : random_hosts) { EXPECT_TRUE(host.rfind(kHttpsScheme, 0) == 0); @@ -64,7 +65,8 @@ kHttpsScheme); // Ensure |random_hosts| has unique entries. std::sort(random_hosts.begin(), random_hosts.end()); - EXPECT_TRUE(base::ranges::adjacent_find(random_hosts) == random_hosts.end()); + EXPECT_TRUE(std::adjacent_find(random_hosts.begin(), random_hosts.end()) == + random_hosts.end()); // Ensure: // (1) hosts in |random_hosts| start with |kHttpsScheme|. // (2) hosts in |random_hosts| end with |kGenerate204Path|.
diff --git a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingButtonController.java b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingButtonController.java index cdc5f12..7daba3d 100644 --- a/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingButtonController.java +++ b/chrome/browser/commerce/price_tracking/android/java/src/org/chromium/chrome/browser/price_tracking/PriceTrackingButtonController.java
@@ -15,10 +15,6 @@ import org.chromium.chrome.browser.toolbar.BaseButtonDataProvider; import org.chromium.chrome.browser.toolbar.adaptive.AdaptiveToolbarFeatures.AdaptiveToolbarButtonVariant; import org.chromium.chrome.browser.user_education.IPHCommandBuilder; -import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; -import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState; -import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver; -import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver; import org.chromium.components.feature_engagement.FeatureConstants; import org.chromium.ui.modaldialog.ModalDialogManager; @@ -28,29 +24,17 @@ */ public class PriceTrackingButtonController extends BaseButtonDataProvider { private final Supplier<TabBookmarker> mTabBookmarkerSupplier; - private final BottomSheetController mBottomSheetController; - private final BottomSheetObserver mBottomSheetObserver; /** Constructor. */ public PriceTrackingButtonController(ObservableSupplier<Tab> tabSupplier, - ModalDialogManager modalDialogManager, BottomSheetController bottomSheetController, - Drawable buttonDrawable, Supplier<TabBookmarker> tabBookmarkerSupplier) { + ModalDialogManager modalDialogManager, Drawable buttonDrawable, + Supplier<TabBookmarker> tabBookmarkerSupplier) { super(tabSupplier, modalDialogManager, buttonDrawable, R.string.enable_price_tracking_menu_item, /* actionChipLabelResId= */ R.string.enable_price_tracking_menu_item, /*supportsTinting=*/true, /*iphCommandBuilder*/ null, AdaptiveToolbarButtonVariant.PRICE_TRACKING); mTabBookmarkerSupplier = tabBookmarkerSupplier; - mBottomSheetController = bottomSheetController; - - mBottomSheetObserver = new EmptyBottomSheetObserver() { - @Override - public void onSheetStateChanged(int newState, int reason) { - mButtonData.setEnabled(newState == SheetState.HIDDEN); - notifyObservers(mButtonData.canShow()); - } - }; - mBottomSheetController.addObserver(mBottomSheetObserver); } @Override
diff --git a/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceTrackingButtonControllerUnitTest.java b/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceTrackingButtonControllerUnitTest.java deleted file mode 100644 index aff052f..0000000 --- a/chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceTrackingButtonControllerUnitTest.java +++ /dev/null
@@ -1,184 +0,0 @@ -// Copyright 2022 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.price_tracking; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.Activity; -import android.content.res.Resources; -import android.graphics.drawable.Drawable; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestRule; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.robolectric.Robolectric; -import org.robolectric.annotation.Config; - -import org.chromium.base.FeatureList; -import org.chromium.base.supplier.ObservableSupplier; -import org.chromium.base.supplier.Supplier; -import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.bookmarks.TabBookmarker; -import org.chromium.chrome.browser.flags.ChromeFeatureList; -import org.chromium.chrome.browser.tab.Tab; -import org.chromium.chrome.browser.toolbar.ButtonData; -import org.chromium.chrome.browser.toolbar.ButtonDataProvider; -import org.chromium.chrome.test.util.browser.Features; -import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; -import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState; -import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason; -import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver; -import org.chromium.ui.modaldialog.ModalDialogManager; - -/** - * Unit test for {@link PriceTrackingButtonController}. - */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) -@Features.EnableFeatures({ChromeFeatureList.CONTEXTUAL_PAGE_ACTION_PRICE_TRACKING, - ChromeFeatureList.ADAPTIVE_BUTTON_IN_TOP_TOOLBAR_CUSTOMIZATION_V2}) -public class PriceTrackingButtonControllerUnitTest { - @Rule - public TestRule mFeaturesProcessor = new Features.JUnitProcessor(); - - private Activity mActivity; - @Mock - private Tab mMockTab; - @Mock - private ObservableSupplier<Tab> mMockTabSupplier; - @Mock - private Supplier<TabBookmarker> mMockTabBookmarkerSupplier; - @Mock - private TabBookmarker mMockTabBookmarker; - @Mock - private BottomSheetController mMockBottomSheetController; - @Mock - private ModalDialogManager mMockModalDialogManager; - @Captor - private ArgumentCaptor<BottomSheetObserver> mBottomSheetObserverCaptor; - - @Before - public void setUp() { - mActivity = Robolectric.setupActivity(Activity.class); - mActivity.setTheme(R.style.Theme_BrowserUI_DayNight); - - MockitoAnnotations.initMocks(this); - - when(mMockTab.getContext()).thenReturn(mActivity); - when(mMockTabSupplier.get()).thenReturn(mMockTab); - when(mMockTabBookmarkerSupplier.get()).thenReturn(mMockTabBookmarker); - } - - @Test - public void testButtonData_QuietVariation() { - PriceTrackingButtonController priceTrackingButtonController = - new PriceTrackingButtonController(mMockTabSupplier, mMockModalDialogManager, - mMockBottomSheetController, mock(Drawable.class), - mMockTabBookmarkerSupplier); - ButtonData buttonData = priceTrackingButtonController.get(mMockTab); - - // Quiet variation uses an IPHCommandBuilder to highlight the action. - Assert.assertNotNull(buttonData.getButtonSpec().getIPHCommandBuilder()); - Assert.assertEquals( - Resources.ID_NULL, buttonData.getButtonSpec().getActionChipLabelResId()); - } - - @Test - public void testButtonData_ActionChipVariation() { - FeatureList.TestValues testValues = new FeatureList.TestValues(); - testValues.addFeatureFlagOverride(ChromeFeatureList.CONTEXTUAL_PAGE_ACTIONS, true); - testValues.addFieldTrialParamOverride( - ChromeFeatureList.CONTEXTUAL_PAGE_ACTIONS, "action_chip", "true"); - testValues.addFeatureFlagOverride( - ChromeFeatureList.ADAPTIVE_BUTTON_IN_TOP_TOOLBAR_CUSTOMIZATION_V2, true); - FeatureList.setTestValues(testValues); - - PriceTrackingButtonController priceTrackingButtonController = - new PriceTrackingButtonController(mMockTabSupplier, mMockModalDialogManager, - mMockBottomSheetController, mock(Drawable.class), - mMockTabBookmarkerSupplier); - ButtonData buttonData = priceTrackingButtonController.get(mMockTab); - - // Action chip variation sets a string resource as action chip label. IPH is not used. - Assert.assertNull(buttonData.getButtonSpec().getIPHCommandBuilder()); - Assert.assertNotEquals( - Resources.ID_NULL, buttonData.getButtonSpec().getActionChipLabelResId()); - } - - @Test - public void testPriceTrackingButtonClick() { - PriceTrackingButtonController priceTrackingButtonController = - new PriceTrackingButtonController(mMockTabSupplier, mMockModalDialogManager, - mMockBottomSheetController, mock(Drawable.class), - mMockTabBookmarkerSupplier); - ButtonData buttonData = priceTrackingButtonController.get(mMockTab); - - buttonData.getButtonSpec().getOnClickListener().onClick(null); - - verify(mMockTabBookmarker).startOrModifyPriceTracking(mMockTab); - } - - @Test - public void testPriceTrackingButton_IsDisabledWhenBottomSheetAppears() { - PriceTrackingButtonController priceTrackingButtonController = - new PriceTrackingButtonController(mMockTabSupplier, mMockModalDialogManager, - mMockBottomSheetController, mock(Drawable.class), - mMockTabBookmarkerSupplier); - ButtonDataProvider.ButtonDataObserver buttonDataObserver = - Mockito.mock(ButtonDataProvider.ButtonDataObserver.class); - priceTrackingButtonController.addObserver(buttonDataObserver); - ButtonData buttonData = priceTrackingButtonController.get(mMockTab); - - // The controller should have registered an observer to listen to bottom sheet events. - verify(mMockBottomSheetController).addObserver(mBottomSheetObserverCaptor.capture()); - - mBottomSheetObserverCaptor.getValue().onSheetStateChanged( - SheetState.FULL, StateChangeReason.NONE); - - Assert.assertFalse(buttonData.isEnabled()); - verify(buttonDataObserver).buttonDataChanged(true); - } - - @Test - public void testPriceTrackingButton_IsReenabledWhenBottomSheetDismissed() { - PriceTrackingButtonController priceTrackingButtonController = - new PriceTrackingButtonController(mMockTabSupplier, mMockModalDialogManager, - mMockBottomSheetController, mock(Drawable.class), - mMockTabBookmarkerSupplier); - - ButtonDataProvider.ButtonDataObserver buttonDataObserver = - Mockito.mock(ButtonDataProvider.ButtonDataObserver.class); - priceTrackingButtonController.addObserver(buttonDataObserver); - ButtonData buttonData = priceTrackingButtonController.get(mMockTab); - - // The controller should have registered an observer to listen to bottom sheet events. - verify(mMockBottomSheetController).addObserver(mBottomSheetObserverCaptor.capture()); - - // Show bottom sheet to disable button. - mBottomSheetObserverCaptor.getValue().onSheetStateChanged( - SheetState.FULL, StateChangeReason.NONE); - - // Close the bottom sheet, button should be enabled again. - mBottomSheetObserverCaptor.getValue().onSheetStateChanged( - SheetState.HIDDEN, StateChangeReason.NONE); - - // After the bottom sheet is closed the button should be enabled. - Assert.assertTrue(buttonData.isEnabled()); - // We should have notified of changes twice (when disabled and when enabled again). - verify(buttonDataObserver, times(2)).buttonDataChanged(true); - } -}
diff --git a/chrome/browser/commerce/price_tracking/android/test_java_sources.gni b/chrome/browser/commerce/price_tracking/android/test_java_sources.gni index 665fa0f1..f54d7a7 100644 --- a/chrome/browser/commerce/price_tracking/android/test_java_sources.gni +++ b/chrome/browser/commerce/price_tracking/android/test_java_sources.gni
@@ -9,6 +9,5 @@ price_tracking_junit_test_java_sources = [ "//chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceDropNotifierUnitTest.java", - "//chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceTrackingButtonControllerUnitTest.java", "//chrome/browser/commerce/price_tracking/android/javatests/src/org/chromium/chrome/browser/price_tracking/PriceTrackingNotificationBridgeUnitTest.java", ]
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc index 552e13c9..851e0037 100644 --- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc +++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
@@ -185,6 +185,12 @@ "CancelledByUser", false); } + // Ask the binary upload service to cancel requests if it can. + auto cancel = std::make_unique<BinaryUploadService::CancelRequests>( + data_.settings.cloud_or_local_settings); + cancel->set_user_action_id(user_action_id_); + GetBinaryUploadService()->MaybeCancelRequests(std::move(cancel)); + // Make sure to reject everything. FillAllResultsWith(false); RunCallback();
diff --git a/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_client.cc b/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_client.cc index 49bf52b..6a322ac0 100644 --- a/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_client.cc +++ b/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_client.cc
@@ -34,6 +34,7 @@ int FakeContentAnalysisSdkClient::CancelRequests( const content_analysis::sdk::ContentAnalysisCancelRequests& cancel) { + cancel_ = cancel; return cancel_status_; } @@ -42,6 +43,11 @@ return request_; } +const content_analysis::sdk::ContentAnalysisCancelRequests& +FakeContentAnalysisSdkClient::GetCancelRequests() { + return cancel_; +} + void FakeContentAnalysisSdkClient::SetAckStatus(int status) { ack_status_ = status; }
diff --git a/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_client.h b/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_client.h index 30aa7ad6..506f518b 100644 --- a/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_client.h +++ b/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_client.h
@@ -29,6 +29,10 @@ // Get the latest request client receives. const content_analysis::sdk::ContentAnalysisRequest& GetRequest(); + // Get the latest cancel requests receives. + const content_analysis::sdk::ContentAnalysisCancelRequests& + GetCancelRequests(); + // Configure response acknowledgement status. void SetAckStatus(int status); @@ -46,6 +50,7 @@ content_analysis::sdk::Client::Config config_; content_analysis::sdk::ContentAnalysisResponse response_; content_analysis::sdk::ContentAnalysisRequest request_; + content_analysis::sdk::ContentAnalysisCancelRequests cancel_; int send_status_ = 0; int ack_status_ = 0; int cancel_status_ = 0;
diff --git a/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_manager.cc b/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_manager.cc index 93ff3662..8bcafed5 100644 --- a/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_manager.cc +++ b/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_manager.cc
@@ -30,6 +30,10 @@ ack_status_ = status; } +void FakeContentAnalysisSdkManager::SetClientCancelStatus(int status) { + cancel_status_ = status; +} + std::unique_ptr<content_analysis::sdk::Client> FakeContentAnalysisSdkManager::CreateClient( const content_analysis::sdk::Client::Config& config) { @@ -37,6 +41,7 @@ client->SetSendStatus(send_status_); client->SetSendResponse(response_); client->SetAckStatus(ack_status_); + client->SetCancelStatus(cancel_status_); fake_clients_.insert(std::make_pair(std::move(config), client.get())); return client;
diff --git a/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_manager.h b/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_manager.h index 9c3858e0c..7b893fa0 100644 --- a/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_manager.h +++ b/chrome/browser/enterprise/connectors/analysis/fake_content_analysis_sdk_manager.h
@@ -31,12 +31,15 @@ void SetClientAckStatus(int status); + void SetClientCancelStatus(int status); + FakeContentAnalysisSdkClient* GetFakeClient( const content_analysis::sdk::Client::Config& config); private: int send_status_ = 0; int ack_status_ = 0; + int cancel_status_ = 0; content_analysis::sdk::ContentAnalysisResponse response_; constexpr static auto CompareConfig =
diff --git a/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.cc b/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.cc index 62244f8..b463bbc 100644 --- a/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.cc +++ b/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.cc
@@ -34,6 +34,14 @@ ack->cloud_or_local_settings().user_specific()}; } +// Build a content analysis SDK client config based on the cancel requests being +// sent. +content_analysis::sdk::Client::Config SDKConfigFromCancel( + const safe_browsing::BinaryUploadService::CancelRequests* cancel) { + return {cancel->cloud_or_local_settings().local_path(), + cancel->cloud_or_local_settings().user_specific()}; +} + // Convert enterprise connector ContentAnalysisRequest into the SDK equivalent. // SDK ContentAnalysisRequest is a strict subset of the enterprise connector // version, therefore the function should always work. @@ -92,7 +100,15 @@ return wrapped->client()->Acknowledge(sdk_ack); } -void HandleAckResponse( +int SendCancelToSDK( + scoped_refptr<ContentAnalysisSdkManager::WrappedClient> wrapped, + content_analysis::sdk::ContentAnalysisCancelRequests sdk_cancel) { + base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, + base::BlockingType::MAY_BLOCK); + return wrapped->client()->CancelRequests(sdk_cancel); +} + +void HandleAckOrCancelResponse( scoped_refptr<ContentAnalysisSdkManager::WrappedClient> wrapped, int status) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -114,7 +130,24 @@ FROM_HERE, {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, base::BindOnce(&SendAckToSDK, wrapped, std::move(sdk_ack)), - base::BindOnce(&HandleAckResponse, wrapped)); + base::BindOnce(&HandleAckOrCancelResponse, wrapped)); +} + +void DoSendCancel( + scoped_refptr<ContentAnalysisSdkManager::WrappedClient> wrapped, + std::unique_ptr<safe_browsing::BinaryUploadService::CancelRequests> + cancel) { + if (!wrapped || !wrapped->client()) + return; + + content_analysis::sdk::ContentAnalysisCancelRequests sdk_cancel; + sdk_cancel.set_user_action_id(cancel->get_user_action_id()); + + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, + {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, + base::BindOnce(&SendCancelToSDK, wrapped, std::move(sdk_cancel)), + base::BindOnce(&HandleAckOrCancelResponse, wrapped)); } // Sends a request to the local agent and waits for a response. @@ -201,6 +234,35 @@ std::move(ack)); } +void LocalBinaryUploadService::MaybeCancelRequests( + std::unique_ptr<CancelRequests> cancel) { + // Cancel all active requests. If the agent returns a response for any, + // they will be ignored. + for (auto it = active_requests_.begin(); it != active_requests_.end();) { + if (it->second.request->user_action_id() == cancel->get_user_action_id()) { + it = active_requests_.erase(it); + } else { + ++it; + } + } + + // Cancel all pending requests. + for (auto it = pending_requests_.begin(); it != pending_requests_.end();) { + if (it->request->user_action_id() == cancel->get_user_action_id()) { + it = pending_requests_.erase(it); + } else { + ++it; + } + } + + // Tell agent to cancel requests. This is a best effort only on the part of + // the agent. + auto* cancel_ptr = cancel.get(); + DoSendCancel(ContentAnalysisSdkManager::Get()->GetClient( + SDKConfigFromCancel(cancel_ptr)), + std::move(cancel)); +} + void LocalBinaryUploadService::DoLocalContentAnalysis(RequestKey key, Result result, Request::Data data) {
diff --git a/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.h b/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.h index a229f31..4dc6b78 100644 --- a/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.h +++ b/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service.h
@@ -69,6 +69,7 @@ // Send the given file contents to local partners for deep scanning. void MaybeUploadForDeepScanning(std::unique_ptr<Request> request) override; void MaybeAcknowledge(std::unique_ptr<Ack> ack) override; + void MaybeCancelRequests(std::unique_ptr<CancelRequests> cancel) override; size_t GetActiveRequestCountForTesting() const { return active_requests_.size();
diff --git a/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service_unittest.cc b/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service_unittest.cc index 38a9555..09eb670 100644 --- a/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service_unittest.cc +++ b/chrome/browser/enterprise/connectors/analysis/local_binary_upload_service_unittest.cc
@@ -23,6 +23,8 @@ using ::testing::Return; using ::testing::SaveArg; +constexpr char kFakeUserActionId[] = "1234567890"; + class MockRequest : public BinaryUploadService::Request { public: MockRequest(BinaryUploadService::ContentAnalysisCallback callback, @@ -332,4 +334,65 @@ EXPECT_EQ(BinaryUploadService::Result::UPLOAD_FAILURE, result); } +TEST_F(LocalBinaryUploadServiceTest, CancelRequests) { + LocalAnalysisSettings local; + local.local_path = "local_system_path"; + local.user_specific = false; + + CloudOrLocalAnalysisSettings cloud_or_local(local); + LocalBinaryUploadService lbus; + + // Add one more request than the max number of concurrent active requests. + // The remaining one should be pending. + LocalAnalysisSettings settings(local); + for (size_t i = 0; i < LocalBinaryUploadService::kMaxActiveCount + 1; ++i) { + auto request = std::make_unique<MockRequest>(base::DoNothing(), settings); + request->set_user_action_id(kFakeUserActionId); + lbus.MaybeUploadForDeepScanning(std::move(request)); + } + + EXPECT_EQ(LocalBinaryUploadService::kMaxActiveCount, + lbus.GetActiveRequestCountForTesting()); + EXPECT_EQ(1u, lbus.GetPendingRequestCountForTesting()); + + auto cr = std::make_unique<LocalBinaryUploadService::CancelRequests>( + cloud_or_local); + cr->set_user_action_id(kFakeUserActionId); + lbus.MaybeCancelRequests(std::move(cr)); + + EXPECT_EQ(0u, lbus.GetActiveRequestCountForTesting()); + EXPECT_EQ(0u, lbus.GetPendingRequestCountForTesting()); + + task_environment_.RunUntilIdle(); + + FakeContentAnalysisSdkClient* fake_client_ptr = + fake_sdk_manager_.GetFakeClient({local.local_path, local.user_specific}); + EXPECT_EQ(kFakeUserActionId, + fake_client_ptr->GetCancelRequests().user_action_id()); +} + +TEST_F(LocalBinaryUploadServiceTest, + ClientDestroyedWhenCancelStatusIsAbnormal) { + fake_sdk_manager_.SetClientCancelStatus(-1); + + LocalAnalysisSettings local; + local.local_path = "local_system_path"; + local.user_specific = false; + + CloudOrLocalAnalysisSettings cloud_or_local(local); + content_analysis::sdk::Client::Config config{local.local_path, + local.user_specific}; + LocalBinaryUploadService lbus; + + auto cr = std::make_unique<LocalBinaryUploadService::CancelRequests>( + cloud_or_local); + cr->set_user_action_id("1234567890"); + lbus.MaybeCancelRequests(std::move(cr)); + + EXPECT_TRUE(fake_sdk_manager_.HasClientForTesting(config)); + + task_environment_.RunUntilIdle(); + + EXPECT_FALSE(fake_sdk_manager_.HasClientForTesting(config)); +} } // namespace enterprise_connectors
diff --git a/chrome/browser/extensions/api/developer_private/inspectable_views_finder.cc b/chrome/browser/extensions/api/developer_private/inspectable_views_finder.cc index 3a1b53ab..b73cc3ca 100644 --- a/chrome/browser/extensions/api/developer_private/inspectable_views_finder.cc +++ b/chrome/browser/extensions/api/developer_private/inspectable_views_finder.cc
@@ -181,7 +181,7 @@ // committed (or visible) url yet. In this case, use the initial url. if (url.is_empty()) { ExtensionHost* extension_host = - process_manager->GetExtensionHostForRenderFrameHost(host); + process_manager->GetBackgroundHostForRenderFrameHost(host); if (extension_host) url = extension_host->initial_url(); }
diff --git a/chrome/browser/feed/android/feed_stream.cc b/chrome/browser/feed/android/feed_stream.cc index 35ea31a1..576204e6 100644 --- a/chrome/browser/feed/android/feed_stream.cc +++ b/chrome/browser/feed/android/feed_stream.cc
@@ -12,6 +12,7 @@ #include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "base/strings/string_piece.h" +#include "base/time/time.h" #include "chrome/browser/feed/android/feed_reliability_logging_bridge.h" #include "chrome/browser/feed/android/jni_headers/FeedStream_jni.h" #include "chrome/browser/feed/android/jni_translation.h" @@ -330,5 +331,15 @@ (static_cast<StreamKind>(stream_kind))); } +void FeedStream::ReportContentSliceVisibleTimeForGoodVisits( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj, + jlong elapsed_ms) { + if (!feed_stream_api_) + return; + feed_stream_api_->ReportContentSliceVisibleTimeForGoodVisits( + base::Milliseconds(elapsed_ms)); +} + } // namespace android } // namespace feed
diff --git a/chrome/browser/feed/android/feed_stream.h b/chrome/browser/feed/android/feed_stream.h index 42feee3f..9608b300 100644 --- a/chrome/browser/feed/android/feed_stream.h +++ b/chrome/browser/feed/android/feed_stream.h
@@ -141,6 +141,11 @@ const base::android::JavaParamRef<jobject>& obj, jint stream_kind); + void ReportContentSliceVisibleTimeForGoodVisits( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj, + jlong elapsed_ms); + private: base::android::ScopedJavaGlobalRef<jobject> java_ref_; raw_ptr<FeedApi> feed_stream_api_;
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java index fff6883f..2df54ae 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTracker.java
@@ -4,7 +4,9 @@ package org.chromium.chrome.browser.feed; +import android.app.Activity; import android.graphics.Rect; +import android.os.SystemClock; import android.view.View; import android.view.ViewTreeObserver; @@ -13,6 +15,7 @@ import androidx.annotation.VisibleForTesting; import androidx.recyclerview.widget.RecyclerView; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.xsurface.ListLayoutHelper; import java.util.ArrayList; @@ -26,6 +29,8 @@ public class FeedSliceViewTracker implements ViewTreeObserver.OnPreDrawListener { private static final String TAG = "FeedSliceViewTracker"; private static final float DEFAULT_VIEW_LOG_THRESHOLD = .66f; + private static final float GOOD_VISITS_EXPOSURE_THRESHOLD = 0.5f; + private static final float GOOD_VISITS_COVERAGE_THRESHOLD = 0.25f; private class VisibilityObserver { final float mVisibilityThreshold; @@ -37,6 +42,7 @@ } } + private final Activity mActivity; @Nullable private RecyclerView mRootView; @Nullable @@ -51,28 +57,50 @@ // changes. Each item in the waicther list consists of the view threshold percentage and the // callback. private HashMap<String, ArrayList<VisibilityObserver>> mWatchedSliceMap = new HashMap<>(); + private boolean mTrackTimeForGoodVisits; + // Thresholds for counting a view as visible for calculating time spent in feed for good visits. + private float mGoodVisitExposureThreshold; + private float mGoodVisitCoverageThreshold; + // Timestamp for keeping track of time spent in feed for good visits. + private long mLastGoodVisibleTime; /** Notified the first time slices are visible */ public interface Observer { // Invoked the first time a slice is 66% visible. void sliceVisible(String sliceId); + // Invoked any time at least one slice is X% exposed and all visible content slices cover Y% + // of the viewport (see Good Visits threshold params). + void reportContentSliceVisibleTime(long elapsedMs); // Invoked when feed content is first visible. This can happens as soon as an xsurface view // is partially visible. void feedContentVisible(); } - public FeedSliceViewTracker(@NonNull RecyclerView rootView, + public FeedSliceViewTracker(@NonNull RecyclerView rootView, @NonNull Activity activity, @NonNull NtpListContentManager contentManager, @Nullable ListLayoutHelper layoutHelper, @NonNull Observer observer) { + mActivity = activity; mRootView = rootView; mContentManager = contentManager; mLayoutHelper = layoutHelper; mObserver = observer; + if (ChromeFeatureList.isEnabled(ChromeFeatureList.FEED_CLIENT_GOOD_VISITS)) { + mTrackTimeForGoodVisits = true; + mGoodVisitExposureThreshold = + (float) ChromeFeatureList.getFieldTrialParamByFeatureAsDouble( + ChromeFeatureList.FEED_CLIENT_GOOD_VISITS, "slice_exposure_threshold", + GOOD_VISITS_EXPOSURE_THRESHOLD); + mGoodVisitCoverageThreshold = + (float) ChromeFeatureList.getFieldTrialParamByFeatureAsDouble( + ChromeFeatureList.FEED_CLIENT_GOOD_VISITS, "slice_coverage_threshold", + GOOD_VISITS_COVERAGE_THRESHOLD); + } } /** Attaches the tracker to the root view. */ public void bind() { mRootView.getViewTreeObserver().addOnPreDrawListener(this); + mLastGoodVisibleTime = 0L; } /** Detaches the tracker from the view. */ @@ -80,6 +108,7 @@ if (mRootView != null && mRootView.getViewTreeObserver().isAlive()) { mRootView.getViewTreeObserver().removeOnPreDrawListener(this); } + reportTimeForGoodVisitsIfNeeded(); } /** Stop observing rootView. Prevents further calls to observer. */ @@ -146,6 +175,7 @@ int firstPosition = mLayoutHelper.findFirstVisibleItemPosition(); int lastPosition = mLayoutHelper.findLastVisibleItemPosition(); + boolean countTimeForGoodVisits = false; for (int i = firstPosition; i <= lastPosition && i < mContentManager.getItemCount() && i >= 0; ++i) { String contentKey = mContentManager.getContent(i).getKey(); @@ -184,6 +214,12 @@ } } + if (mTrackTimeForGoodVisits) { + countTimeForGoodVisits = countTimeForGoodVisits + || isViewVisible(childView, mGoodVisitExposureThreshold) + || isViewCoveringViewport(childView, mGoodVisitCoverageThreshold); + } + if (mContentKeysVisible.contains(contentKey) || !isViewVisible(childView, DEFAULT_VIEW_LOG_THRESHOLD)) { continue; @@ -192,16 +228,53 @@ mContentKeysVisible.add(contentKey); mObserver.sliceVisible(contentKey); } + + if (mTrackTimeForGoodVisits) { + reportTimeForGoodVisitsIfNeeded(); + if (countTimeForGoodVisits) { + mLastGoodVisibleTime = SystemClock.elapsedRealtime(); + } + } + return true; } + private void reportTimeForGoodVisitsIfNeeded() { + // Report elapsed time since we last saw that content was visible enough. + if (mLastGoodVisibleTime != 0L) { + mObserver.reportContentSliceVisibleTime( + SystemClock.elapsedRealtime() - mLastGoodVisibleTime); + mLastGoodVisibleTime = 0L; + } + } + @VisibleForTesting boolean isViewVisible(View childView, float threshold) { - Rect rect = new Rect(0, 0, childView.getWidth(), childView.getHeight()); - int viewArea = rect.width() * rect.height(); + int viewArea = getViewArea(childView); if (viewArea <= 0) return false; - if (!mRootView.getChildVisibleRect(childView, rect, null)) return false; - int visibleArea = rect.width() * rect.height(); - return (float) visibleArea / viewArea >= threshold; + return (float) getVisibleArea(childView) / viewArea >= threshold; + } + + @VisibleForTesting + boolean isViewCoveringViewport(View childView, float threshold) { + int viewportArea = getViewportArea(); + if (viewportArea <= 0) return false; + return (float) getVisibleArea(childView) / viewportArea >= threshold; + } + + private int getViewArea(View childView) { + return childView.getWidth() * childView.getHeight(); + } + + private int getViewportArea() { + Rect viewport = new Rect(); + mActivity.getWindow().getDecorView().getWindowVisibleDisplayFrame(viewport); + return viewport.width() * viewport.height(); + } + + private int getVisibleArea(View childView) { + Rect rect = new Rect(); + if (!mRootView.getChildVisibleRect(childView, rect, null)) return 0; + return rect.width() * rect.height(); } }
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTrackerTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTrackerTest.java index 41ddd29..b9bc09b 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTrackerTest.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedSliceViewTrackerTest.java
@@ -9,21 +9,27 @@ import static org.mockito.AdditionalMatchers.leq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyFloat; +import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.Activity; import android.graphics.Rect; import android.view.View; import android.view.ViewTreeObserver; +import android.view.Window; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.test.filters.SmallTest; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -35,15 +41,19 @@ import org.mockito.stubbing.Answer; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowLog; +import org.robolectric.shadows.ShadowSystemClock; +import org.chromium.base.FeatureList; import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.xsurface.ListLayoutHelper; import java.util.Arrays; +import java.util.concurrent.TimeUnit; /** Unit tests for {@link FeedSliceViewTracker}. */ @RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) +@Config(manifest = Config.NONE, shadows = {ShadowSystemClock.class}) public class FeedSliceViewTrackerTest { // Mocking dependencies that are always present, but using a real FeedListContentManager. @Mock @@ -56,6 +66,12 @@ ListLayoutHelper mLayoutHelper; @Mock ViewTreeObserver mViewTreeObserver; + @Mock + Activity mActivity; + @Mock + Window mWindow; + @Mock + View mDecorView; NtpListContentManager mContentManager; FeedSliceViewTracker mTracker; @@ -74,20 +90,31 @@ @Before public void setUp() { + FeatureList.TestValues testValues = new FeatureList.TestValues(); + testValues.addFeatureFlagOverride(ChromeFeatureList.FEED_CLIENT_GOOD_VISITS, true); + FeatureList.setTestValues(testValues); + ShadowLog.stream = System.out; MockitoAnnotations.initMocks(this); mContentManager = new NtpListContentManager(); doReturn(mLayoutManager).when(mParentView).getLayoutManager(); doReturn(mViewTreeObserver).when(mParentView).getViewTreeObserver(); - mTracker = Mockito.spy( - new FeedSliceViewTracker(mParentView, mContentManager, mLayoutHelper, mObserver)); + doReturn(mWindow).when(mActivity).getWindow(); + doReturn(mDecorView).when(mWindow).getDecorView(); + mTracker = Mockito.spy(new FeedSliceViewTracker( + mParentView, mActivity, mContentManager, mLayoutHelper, mObserver)); + } + + @After + public void tearDown() { + ShadowSystemClock.reset(); } @Test @SmallTest public void testIsItemVisible_JustEnoughnViewport() { mockViewDimensions(mChildA, 10, 10); - mockGetChildVisibleRect(mChildA, 0, 7); + mockGetChildVisibleRect(mChildA, 0, 0, 10, 7); Assert.assertTrue(mTracker.isViewVisible(mChildA, 0.66f)); } @@ -95,7 +122,7 @@ @SmallTest public void testIsItemVisible_NotEnoughnViewport() { mockViewDimensions(mChildA, 10, 10); - mockGetChildVisibleRect(mChildA, 0, 6); + mockGetChildVisibleRect(mChildA, 0, 0, 10, 6); Assert.assertFalse(mTracker.isViewVisible(mChildA, 0.66f)); } @@ -103,7 +130,7 @@ @SmallTest public void testIsItemVisible_ZeroAreaInViewport() { mockViewDimensions(mChildA, 10, 10); - mockGetChildVisibleRect(mChildA, 0, 0); + mockGetChildVisibleRect(mChildA, 0, 0, 0, 0); Assert.assertFalse(mTracker.isViewVisible(mChildA, 0.66f)); } @@ -119,12 +146,48 @@ @SmallTest public void testIsItemVisible_ZeroArea() { mockViewDimensions(mChildA, 0, 0); - mockGetChildVisibleRect(mChildA, 0, 0); + mockGetChildVisibleRect(mChildA, 0, 0, 0, 0); Assert.assertFalse(mTracker.isViewVisible(mChildA, 0.66f)); } @Test @SmallTest + public void testIsItemCoveringViewport_JustEnough() { + mockViewDimensions(mChildA, 100, 100); + mockGetChildVisibleRect(mChildA, 0, 0, 100, 26); + mockViewportRect(0, 0, 100, 100); + Assert.assertTrue(mTracker.isViewCoveringViewport(mChildA, 0.25f)); + } + + @Test + @SmallTest + public void testIsViewCoveringViewport_NotEnough() { + mockViewDimensions(mChildA, 100, 100); + mockGetChildVisibleRect(mChildA, 0, 0, 100, 24); + mockViewportRect(0, 0, 100, 100); + Assert.assertFalse(mTracker.isViewCoveringViewport(mChildA, 0.25f)); + } + + @Test + @SmallTest + public void testIsContentCoveringViewport_ZeroArea() { + mockViewDimensions(mChildA, 0, 0); + mockGetChildVisibleRect(mChildA, 0, 0, 0, 0); + mockViewportRect(0, 0, 100, 100); + Assert.assertFalse(mTracker.isViewCoveringViewport(mChildA, 0.25f)); + } + + @Test + @SmallTest + public void testIsContentCoveringViewport_NoViewport() { + mockViewDimensions(mChildA, 100, 100); + mockGetChildVisibleRect(mChildA, 0, 0, 100, 26); + mockViewportRect(0, 0, 0, 0); + Assert.assertFalse(mTracker.isViewCoveringViewport(mChildA, 0.25f)); + } + + @Test + @SmallTest public void testOnPreDraw_BothVisibleAreReportedExactlyOnce() { mContentManager.addContents(0, Arrays.asList(new NtpListContentManager.FeedContent[] { @@ -305,18 +368,159 @@ assertTrue(mChildBVisibleRunnable2Called); } - void mockViewDimensions(View view, int width, int height) { - when(view.getWidth()).thenReturn(10); - when(view.getHeight()).thenReturn(10); + @Test + @SmallTest + public void testReportContentVisibleTime_visibleAndCovering() { + mContentManager.addContents(0, + Arrays.asList(new NtpListContentManager.FeedContent[] { + new NtpListContentManager.NativeViewContent(0, "c/key1", mChildA), + new NtpListContentManager.NativeViewContent(0, "c/key2", mChildB), + })); + doReturn(0).when(mLayoutHelper).findFirstVisibleItemPosition(); + doReturn(1).when(mLayoutHelper).findLastVisibleItemPosition(); + doReturn(mChildA).when(mLayoutManager).findViewByPosition(eq(0)); + doReturn(mChildB).when(mLayoutManager).findViewByPosition(eq(1)); + + // Not visible or covering: no time reported. + doReturn(false).when(mTracker).isViewVisible(eq(mChildA), anyFloat()); + doReturn(false).when(mTracker).isViewCoveringViewport(eq(mChildA), anyFloat()); + mTracker.onPreDraw(); + advanceByMs(1L); + mTracker.onPreDraw(); + verify(mObserver, never()).reportContentSliceVisibleTime(anyLong()); + + // Visible enough; time is reported. + doReturn(true).when(mTracker).isViewVisible(eq(mChildA), anyFloat()); + doReturn(false).when(mTracker).isViewCoveringViewport(eq(mChildA), anyFloat()); + mTracker.onPreDraw(); + advanceByMs(1L); + mTracker.onPreDraw(); + verify(mObserver, times(1)).reportContentSliceVisibleTime(eq(1L)); + reset(mObserver); + + // Covering enough; time is reported. + doReturn(false).when(mTracker).isViewVisible(eq(mChildA), anyFloat()); + doReturn(true).when(mTracker).isViewCoveringViewport(eq(mChildA), anyFloat()); + advanceByMs(1L); + mTracker.onPreDraw(); + verify(mObserver, times(1)).reportContentSliceVisibleTime(eq(1L)); + reset(mObserver); + + // Visible enough and covering enough: report some time spent in feed. + doReturn(true).when(mTracker).isViewVisible(eq(mChildA), anyFloat()); + doReturn(true).when(mTracker).isViewCoveringViewport(eq(mChildA), anyFloat()); + advanceByMs(1L); + mTracker.onPreDraw(); + verify(mObserver, times(1)).reportContentSliceVisibleTime(eq(1L)); } - void mockGetChildVisibleRect(View child, int rectTop, int rectBottom) { + @Test + @SmallTest + public void testReportContentVisibleTime_testSmallCardsCoveringEnough() { + mContentManager.addContents(0, + Arrays.asList(new NtpListContentManager.FeedContent[] { + new NtpListContentManager.NativeViewContent(0, "c/key1", mChildA), + new NtpListContentManager.NativeViewContent(0, "c/key2", mChildB), + })); + doReturn(0).when(mLayoutHelper).findFirstVisibleItemPosition(); + doReturn(1).when(mLayoutHelper).findLastVisibleItemPosition(); + doReturn(mChildA).when(mLayoutManager).findViewByPosition(eq(0)); + doReturn(mChildB).when(mLayoutManager).findViewByPosition(eq(1)); + + // Views are completely exposed so time is tracked. + mockViewportRect(0, 0, 100, 100); + mockViewDimensions(mChildA, 100, 15); + mockGetChildVisibleRect(mChildA, 0, 0, 100, 15); + mockViewDimensions(mChildB, 100, 15); + mockGetChildVisibleRect(mChildB, 0, 15, 100, 30); + + mTracker.onPreDraw(); + advanceByMs(1L); + mTracker.onPreDraw(); + verify(mObserver, times(1)).reportContentSliceVisibleTime(eq(1L)); + } + + @Test + @SmallTest + public void testReportContentVisibleTime_testBigCardCoveringEnough() { + mContentManager.addContents(0, + Arrays.asList(new NtpListContentManager.FeedContent[] { + new NtpListContentManager.NativeViewContent(0, "c/key1", mChildA), + })); + doReturn(0).when(mLayoutHelper).findFirstVisibleItemPosition(); + doReturn(0).when(mLayoutHelper).findLastVisibleItemPosition(); + doReturn(mChildA).when(mLayoutManager).findViewByPosition(eq(0)); + + // View is completely exposed and covers 30% of the viewport in total. + mockViewportRect(0, 0, 100, 100); + mockViewDimensions(mChildA, 100, 26); + mockGetChildVisibleRect(mChildA, 0, 0, 100, 26); + + mTracker.onPreDraw(); + advanceByMs(1L); + mTracker.onPreDraw(); + verify(mObserver, times(1)).reportContentSliceVisibleTime(eq(1L)); + } + + @Test + @SmallTest + public void testReportContentVisibleTime_testBigCardExposedEnough() { + mContentManager.addContents(0, + Arrays.asList(new NtpListContentManager.FeedContent[] { + new NtpListContentManager.NativeViewContent(0, "c/key1", mChildA), + })); + doReturn(0).when(mLayoutHelper).findFirstVisibleItemPosition(); + doReturn(0).when(mLayoutHelper).findLastVisibleItemPosition(); + doReturn(mChildA).when(mLayoutManager).findViewByPosition(eq(0)); + + // View is completely exposed but only covers 22% of the viewport. + mockViewportRect(0, 0, 100, 100); + mockViewDimensions(mChildA, 100, 22); + mockGetChildVisibleRect(mChildA, 0, 0, 100, 22); + + mTracker.onPreDraw(); + advanceByMs(1L); + mTracker.onPreDraw(); + verify(mObserver, times(1)).reportContentSliceVisibleTime(eq(1L)); + } + + @Test + @SmallTest + public void testReportContentVisibleTime_testReportTimeOnUnbind() { + mContentManager.addContents(0, + Arrays.asList(new NtpListContentManager.FeedContent[] { + new NtpListContentManager.NativeViewContent(0, "c/key1", mChildA), + })); + doReturn(0).when(mLayoutHelper).findFirstVisibleItemPosition(); + doReturn(0).when(mLayoutHelper).findLastVisibleItemPosition(); + doReturn(mChildA).when(mLayoutManager).findViewByPosition(eq(0)); + + // View is completely exposed but only covers 22% of the viewport. + mockViewportRect(0, 0, 100, 100); + mockViewDimensions(mChildA, 100, 22); + mockGetChildVisibleRect(mChildA, 0, 0, 100, 22); + + mTracker.onPreDraw(); + advanceByMs(1L); + mTracker.unbind(); + verify(mObserver, times(1)).reportContentSliceVisibleTime(eq(1L)); + } + + void mockViewDimensions(View view, int width, int height) { + when(view.getWidth()).thenReturn(width); + when(view.getHeight()).thenReturn(height); + } + + void mockGetChildVisibleRect( + View child, int rectLeft, int rectTop, int rectRight, int rectBottom) { doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) { Rect rect = (Rect) invocation.getArguments()[1]; rect.top = rectTop; rect.bottom = rectBottom; + rect.left = rectLeft; + rect.right = rectRight; return true; } }) @@ -335,6 +539,18 @@ .getChildVisibleRect(eq(child), any(), any()); } + void mockViewportRect(int left, int top, int right, int bottom) { + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) { + ((Rect) invocation.getArguments()[0]).set(new Rect(left, top, right, bottom)); + return null; + } + }) + .when(mDecorView) + .getWindowVisibleDisplayFrame(any()); + } + void clearVisibleRunnableCalledStates() { mChildAVisibleRunnable1Called = false; mChildAVisibleRunnable2Called = false; @@ -342,4 +558,8 @@ mChildBVisibleRunnable1Called = false; mChildBVisibleRunnable2Called = false; } + + void advanceByMs(long ms) { + ShadowSystemClock.advanceBy(ms, TimeUnit.MILLISECONDS); + } }
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java index c61f247..65511e30 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStream.java
@@ -718,7 +718,7 @@ mScrollStateToRestore = savedInstanceState; manager.setHandlers(mHandlersMap); - mSliceViewTracker = new FeedSliceViewTracker(rootView, manager, + mSliceViewTracker = new FeedSliceViewTracker(rootView, mActivity, manager, renderer.getListLayoutHelper(), new FeedStream.ViewTrackerObserver()); mSliceViewTracker.bind(); @@ -1273,6 +1273,11 @@ FeedStreamJni.get().reportSliceViewed(mNativeFeedStream, FeedStream.this, sliceId); } @Override + public void reportContentSliceVisibleTime(long elapsedMs) { + FeedStreamJni.get().reportContentSliceVisibleTimeForGoodVisits( + mNativeFeedStream, FeedStream.this, elapsedMs); + } + @Override public void feedContentVisible() { FeedStreamJni.get().reportFeedViewed(mNativeFeedStream, FeedStream.this); } @@ -1369,5 +1374,7 @@ void resetInfoCardStates(long nativeFeedStream, FeedStream caller, int type); void invalidateContentCacheFor( long nativeFeedStream, FeedStream caller, @StreamType int feedToInvalidate); + void reportContentSliceVisibleTimeForGoodVisits( + long nativeFeedStream, FeedStream caller, long elapsedMs); } }
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java index 842064477..59d9012 100644 --- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java +++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedStreamTest.java
@@ -166,6 +166,7 @@ Map<String, Boolean> overrides = new ArrayMap<>(); overrides.put(ChromeFeatureList.FEED_LOADING_PLACEHOLDER, feedLoadingPlaceholderOn); overrides.put(ChromeFeatureList.WEB_FEED_ONBOARDING, onboardingOn); + overrides.put(ChromeFeatureList.FEED_CLIENT_GOOD_VISITS, true); FeatureList.setTestFeatures(overrides); }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 75c5e56..c49525a 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -1879,6 +1879,11 @@ "expiry_milestone": 120 }, { + "name": "enable-compromised-passwords-muting", + "owners": [ "noemies@google.com", "tmartino" ], + "expiry_milestone": 112 + }, + { "name": "enable-conditional-tabstrip", "owners": [ "memex-team@google.com" ], "expiry_milestone": 90 @@ -5914,27 +5919,27 @@ { "name": "request-desktop-site-additions", "owners": [ "shuyng@google.com", "twellington", "clank-app-team@google.com" ], - "expiry_milestone": 108 + "expiry_milestone": 112 }, { "name": "request-desktop-site-defaults", "owners": [ "aishwaryarj", "twellington", "clank-app-team@google.com" ], - "expiry_milestone": 108 + "expiry_milestone": 110 }, { "name": "request-desktop-site-defaults-downgrade", "owners": [ "aishwaryarj", "twellington", "clank-app-team@google.com" ], - "expiry_milestone": 108 + "expiry_milestone": 110 }, { "name": "request-desktop-site-exceptions", "owners": [ "shuyng@google.com", "twellington", "clank-app-team@google.com" ], - "expiry_milestone": 108 + "expiry_milestone": 110 }, { "name": "request-desktop-site-exceptions-downgrade", "owners": [ "aishwaryarj", "twellington", "clank-app-team@google.com" ], - "expiry_milestone": 108 + "expiry_milestone": 110 }, { "name": "request-desktop-site-for-tablets", @@ -6219,7 +6224,7 @@ { "name": "shopping-list", "owners": [ "mdjones", "chrome-shopping-eng@google.com" ], - "expiry_milestone": 108 + "expiry_milestone": 110 }, { "name": "show-autofill-type-predictions",
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc index aa9beec..0b991ec3 100644 --- a/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -140,6 +140,7 @@ &feature_engagement::kUseClientConfigIPH, &feature_guide::features::kFeatureNotificationGuide, &feature_guide::features::kSkipCheckForLowEngagedUsers, + &feed::kClientGoodVisits, &feed::kFeedBackToTop, &feed::kFeedClearImageMemoryCache, &feed::kFeedHeaderStickToTop,
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java index 0f29b42..c50e572 100644 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -360,6 +360,7 @@ public static final String FEED_BACK_TO_TOP = "FeedBackToTop"; public static final String FEED_CLEAR_IMAGE_MEMORY_CACHE = "FeedClearImageMemoryCache"; public static final String FEED_HEADER_STICK_TO_TOP = "FeedHeaderStickToTop"; + public static final String FEED_CLIENT_GOOD_VISITS = "FeedClientGoodVisits"; public static final String FEED_IMAGE_MEMORY_CACHE_SIZE_PERCENTAGE = "FeedImageMemoryCacheSizePercentage"; public static final String FEED_INTERACTIVE_REFRESH = "FeedInteractiveRefresh";
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn index d6f622a..ef30dd2 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/BUILD.gn
@@ -53,7 +53,9 @@ "dictation/metrics_utils.js", "dictation/parse/input_text_strategy.js", "dictation/parse/parse_strategy.js", + "dictation/parse/pumpkin/pumpkin_constants.js", "dictation/parse/pumpkin_parse_strategy.js", + "dictation/parse/sandboxed_pumpkin_tagger.js", "dictation/parse/simple_parse_strategy.js", "dictation/parse/speech_parser.js", "dictation/ui_controller.js", @@ -69,7 +71,10 @@ testonly = true assert(enable_extensions) - deps = [ ":accessibility_common_extjs_tests" ] + deps = [ + ":accessibility_common_extjs_tests", + ":pumpkin_test_files", + ] data = [ "$root_out_dir/chrome_100_percent.pak", @@ -99,6 +104,7 @@ "dictation/locale_info_test.js", "dictation/macros/dictation_macros_test.js", "dictation/parse/dictation_parse_test.js", + "dictation/parse/dictation_pumpkin_parse_test.js", "magnifier/magnifier_test.js", ] gen_include_files = [ @@ -234,7 +240,9 @@ sources = [ "dictation/parse/input_text_strategy.js", "dictation/parse/parse_strategy.js", + "dictation/parse/pumpkin/pumpkin_constants.js", "dictation/parse/pumpkin_parse_strategy.js", + "dictation/parse/sandboxed_pumpkin_tagger.js", "dictation/parse/simple_parse_strategy.js", "dictation/parse/speech_parser.js", ] @@ -243,6 +251,7 @@ ":dictation_locale_info", ":dictation_macros", ] + externs_list = [ "$externs_path/accessibility_private.js" ] } js_library("dictation_metrics") { @@ -270,3 +279,25 @@ js_library("dictation_locale_info") { sources = [ "dictation/locale_info.js" ] } + +action("pumpkin_test_files") { + testonly = true + + pumpkin_output_dir = "$accessibility_common_dir/dictation/parse/pumpkin" + script = "dictation/parse/pumpkin/unpack_pumpkin.py" + sources = [ "dictation/parse/pumpkin/pumpkin-2.0.tar.xz" ] + files_to_extract = [ + "$pumpkin_output_dir/js_pumpkin_tagger_bin.js", + "$pumpkin_output_dir/tagger_wasm_main.js", + "$pumpkin_output_dir/tagger_wasm_main.wasm", + "$pumpkin_output_dir/en_us/action_config.binarypb", + "$pumpkin_output_dir/en_us/pumpkin_config.binarypb", + ] + args = [ + "--dest-dir=resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin", + rebase_path("dictation/parse/pumpkin/pumpkin-2.0.tar.xz", root_build_dir), + string_join(",", files_to_extract), + pumpkin_output_dir, + ] + outputs = files_to_extract +}
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/dictation_pumpkin_parse_test.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/dictation_pumpkin_parse_test.js new file mode 100644 index 0000000..84cb77c --- /dev/null +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/dictation_pumpkin_parse_test.js
@@ -0,0 +1,145 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +GEN_INCLUDE(['../dictation_test_base.js']); + +/** A class that represents a test case for parsing text. */ +class ParseTestCase { + /** + * @param {string} text The text to be parsed + * @param {string|undefined} expectedMacroName The expected name of the + * resulting macro. + * @param {number|undefined} expectedRepeat The expected repeat value of the + * resulting macro. + * @constructor + */ + constructor(text, expectedMacroName, expectedRepeat) { + /** @type {string} */ + this.text = text; + /** @type {string|undefined} */ + this.expectedMacroName = expectedMacroName; + /** @type {number|undefined} */ + this.expectedRepeat = expectedRepeat; + } +} + +/** + * Dictation tests for speech parsing with Pumpkin. These tests do not use the + * live Pumpkin DLC, but instead use a local tar archive that mirrors the DLC. + * It's important that we keep the live DLC and the local tar archive in sync. + * SandboxedPumpkinTagger emits several logs during the initialization + * phase e.g. "Pumpkin module loaded". Setup this test so that it doesn't + * fail when something is logged to the console. + * TODO(https://crbug.com/1258190): Remove DictationE2ETestAllowConsole and + * override the message filter so that wasm console messages don't cause the + * test to fail. + */ +DictationPumpkinParseTest = class extends DictationE2ETestAllowConsole { + /** @override */ + async setUpDeferred() { + this.mockAccessibilityPrivate.enableFeatureForTest( + 'dictationPumpkinParsing', true); + await this.mockAccessibilityPrivate.initializePumpkinData(); + // Re-initialize PumpkinParseStrategy after mock Pumpkin data has been + // created. + this.getPumpkinParseStrategy().init_(); + await importModule( + 'SpeechParser', + '/accessibility_common/dictation/parse/speech_parser.js'); + + await super.setUpDeferred(); + } + + /** + * @return {!Promise} + * @private + */ + async waitForPumpkinParseStrategy_() { + const strategy = this.getPumpkinParseStrategy(); + // TODO(crbug.com/1258190): Consider adding an observer or callback and + // remove the polling below. + return new Promise(resolve => { + const intervalId = setInterval(() => { + if (strategy.pumpkinTaggerReady_) { + clearInterval(intervalId); + resolve(); + } + }, 300); + }); + } + + /** + * @param {!ParseTestCase} testCase + * @return {!Promise} + */ + async runParseTestCase(testCase) { + const expectedMacroName = testCase.expectedMacroName; + const expectedRepeat = testCase.expectedRepeat; + const macro = await this.getPumpkinParseStrategy().parse(testCase.text); + if (!macro) { + assertEquals(undefined, expectedMacroName); + assertEquals(undefined, expectedRepeat); + return; + } + + if (expectedMacroName) { + assertEquals(expectedMacroName, macro.getMacroNameString()); + } + if (expectedRepeat) { + assertEquals(expectedRepeat, macro.repeat_); + } + } +}; + +// Tests that we can use the SandboxedPumpkinTagger to convert speech into a +// macro. The text to macro mapping can be found in +// google3/chrome/chromeos/accessibility/dictation/grammars/\ +// dictation_en_us.patterns +AX_TEST_F('DictationPumpkinParseTest', 'Parse', async function() { + await this.waitForPumpkinParseStrategy_(); + + /** @type {!Array<!ParseTestCase>} */ + const testCases = [ + new ParseTestCase('Hello world'), + new ParseTestCase('dictate delete', 'INPUT_TEXT_VIEW'), + new ParseTestCase('backspace', 'DELETE_PREV_CHAR'), + new ParseTestCase('left one character', 'NAV_PREV_CHAR'), + new ParseTestCase('right one character', 'NAV_NEXT_CHAR'), + new ParseTestCase('up one line', 'NAV_PREV_LINE'), + new ParseTestCase('down one line', 'NAV_NEXT_LINE'), + new ParseTestCase('copy selected text', 'COPY_SELECTED_TEXT'), + new ParseTestCase('paste copied text', 'PASTE_TEXT'), + new ParseTestCase('cut highlighted text', 'CUT_SELECTED_TEXT'), + new ParseTestCase('undo that', 'UNDO_TEXT_EDIT'), + new ParseTestCase('redo that', 'REDO_ACTION'), + new ParseTestCase('select everything', 'SELECT_ALL_TEXT'), + new ParseTestCase('deselect selection', 'UNSELECT_TEXT'), + new ParseTestCase('what can I say', 'LIST_COMMANDS'), + new ParseTestCase('new line'), + ]; + + for (const test of testCases) { + await this.runParseTestCase(test); + } +}); + +// Tests that we can use the SandboxedPumpkinTagger to parse text and yield +// a RepeatableKeyPressMacro with a `repeat_` value greater than one. +AX_TEST_F( + 'DictationPumpkinParseTest', 'RepeatableKeyPressMacro', async function() { + await this.waitForPumpkinParseStrategy_(); + + /** @type {!Array<!ParseTestCase>} */ + const testCases = [ + new ParseTestCase('remove two characters', 'DELETE_PREV_CHAR', 2), + new ParseTestCase('left five characters', 'NAV_PREV_CHAR', 5), + ]; + + for (const test of testCases) { + await this.runParseTestCase(test); + } + }); + +// TODO(https://crbug.com/1258190): Add test cases for when Dictation is in +// another en-* locale (e.g. en-GB).
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin/pumpkin-2.0.tar.xz b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin/pumpkin-2.0.tar.xz new file mode 100644 index 0000000..33eff10381 --- /dev/null +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin/pumpkin-2.0.tar.xz Binary files differ
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin/pumpkin_constants.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin/pumpkin_constants.js new file mode 100644 index 0000000..25b20c3f --- /dev/null +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin/pumpkin_constants.js
@@ -0,0 +1,127 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview Defines constants used for Pumpkin. + */ + +/** + * The sandbox doesn't have access to extension APIs, so we need to keep a copy + * of the PumpkinData typedef. Copied from + * third_party/closure_compiler/externs/accessibility_private.js + * TODO(crbug.com/1258190): Consider creating a python script that would pull + * this definition in at build time. + * @typedef {{ + * js_pumpkin_tagger_bin_js: ArrayBuffer, + * tagger_wasm_main_js: ArrayBuffer, + * tagger_wasm_main_wasm: ArrayBuffer, + * en_us_action_config_binarypb: ArrayBuffer, + * en_us_pumpkin_config_binarypb: ArrayBuffer, + * fr_fr_action_config_binarypb: ArrayBuffer, + * fr_fr_pumpkin_config_binarypb: ArrayBuffer, + * it_it_action_config_binarypb: ArrayBuffer, + * it_it_pumpkin_config_binarypb: ArrayBuffer, + * de_de_action_config_binarypb: ArrayBuffer, + * de_de_pumpkin_config_binarypb: ArrayBuffer, + * es_es_action_config_binarypb: ArrayBuffer, + * es_es_pumpkin_config_binarypb: ArrayBuffer + * }} + */ +export let PumpkinData; + +/** + * The types of commands that can come from SandboxedPumpkinTagger. + * @enum {string} + */ +export const FromPumpkinTaggerCommand = { + READY: 'ready', + FULLY_INITIALIZED: 'fullyInitialized', + TAG_RESULTS: 'tagResults', +}; + +/** + * The types of commands that can be sent to SandboxedPumpkinTagger. + * @enum {string} + */ +export const ToPumpkinTaggerCommand = { + LOAD: 'load', + TAG: 'tagAndGetNBestHypotheses', +}; + +/** + * Defines the message data received from SandboxedPumpkinTagger. + * @typedef {{ + * results: (!Object|null|undefined), + * type: !FromPumpkinTaggerCommand, + * }} + */ +export let FromPumpkinTagger; + +/** + * Defines the message data sent to SandboxedPumpkinTagger. + * @typedef {{ + * locale: (!PumpkinLocale|undefined), + * numResults: (number|undefined), + * pumpkinData: (!PumpkinData|null|undefined), + * text: (string|undefined), + * type: !ToPumpkinTaggerCommand, + * }} + */ +export let ToPumpkinTagger; + +/** + * Supported Pumpkin locales. + * @enum {string} + */ +export const PumpkinLocale = { + EN_US: 'en_us', + FR_FR: 'fr_fr', + IT_IT: 'it_it', + DE_DE: 'de_de', + ES_ES: 'es_es', +}; + +/** + * Map from BCP-47 locale code (see dictation.cc) to directory name in + * dictation/parse/pumpkin/ for supported Pumpkin locales. + * TODO(crbug.com/1264544): Determine if all en* languages can be mapped to + * en_us. Possible locales are listed in dictation.cc, + * kWebSpeechSupportedLocales. + * TODO(https://crbug.com/1258190): Add mappings for other locales supported by + * Pumpkin. + * @const {!Object<string, PumpkinLocale>} + */ +export const SUPPORTED_LOCALES = { + 'en-US': PumpkinLocale.EN_US, + 'en-AU': PumpkinLocale.EN_US, + 'en-CA': PumpkinLocale.EN_US, + 'en-GB': PumpkinLocale.EN_US, + 'en-GH': PumpkinLocale.EN_US, + 'en-HK': PumpkinLocale.EN_US, + 'en-IN': PumpkinLocale.EN_US, + 'en-KE': PumpkinLocale.EN_US, + 'en-NG': PumpkinLocale.EN_US, + 'en-NZ': PumpkinLocale.EN_US, + 'en-PH': PumpkinLocale.EN_US, + 'en-PK': PumpkinLocale.EN_US, + 'en-SG': PumpkinLocale.EN_US, + 'en-TZ': PumpkinLocale.EN_US, + 'en-ZA': PumpkinLocale.EN_US, +}; + +/** + * PumpkinTagger Hypothesis argument names. These should match the variable + * argument placeholders in voiceaccess.patterns_template and the static strings + * defined in voiceaccess/utils/PumpkinUtils.java in google3. + * @enum {string} + */ +export const HypothesisArgumentName = { + SEM_TAG: 'SEM_TAG', + NUM_ARG: 'NUM_ARG', + OPEN_ENDED_TEXT: 'OPEN_ENDED_TEXT', +}; + +/** @const {string} */ +export const SANDBOXED_PUMPKIN_TAGGER_JS_FILE = + 'dictation/parse/sandboxed_pumpkin_tagger.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin/unpack_pumpkin.py b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin/unpack_pumpkin.py new file mode 100755 index 0000000..593337c8 --- /dev/null +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin/unpack_pumpkin.py
@@ -0,0 +1,76 @@ +#!/usr/bin/env python + +# Copyright 2022 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import optparse +import os +import shutil +import sys +import tarfile + +# A python script that unpacks the pumpkin tar archive by: +# 1. Extracting the tar into a temporary directory +# 2. Copying the file contents from the temporary directory to the destination +# directory +# 3. Removing the temporary directory +# +# We need to do the extraction indirectly because of: +# 1. how tarfile.extractall() works +# 2. how ninja determines dirty/stale objects +# +# tarfile.extractall() hits errors if the extracted file already exists. +# If we wanted to extract directly into the destination directory, we'd need to +# clear the directory first. However, removing and creating new directories +# within this script would change object timestamps without ninja's +# knowledge. This would cause ninja to always think the pumpkin test files are +# out of date, and thus this script would run each time there is a build +# request, even if there is no work necessary. This is all important because the +# CQ builds all targets, then triggers the same build again and asserts that +# it was a no-op. Without this indirect extraction, we'd fail the CQ every time. + +def main(): + parser = optparse.OptionParser(description=__doc__) + parser.usage = '%prog [options] <tar-file_path>' + parser.add_option( + '--dest-dir', + action='store', + metavar='DEST_DIR', + help='Destination directory for extracted files.') + options, args = parser.parse_args() + if len(args) < 1 or not options.dest_dir: + print( + 'Expected --dest-dir and the tar archive to unpack.', + file=sys.stderr) + print(str(args)) + sys.exit(1) + + tarArchive = args[0] + outputFiles = args[1].split(',') + pumpkinOutputDir = args[2]; + destDir = options.dest_dir + tempDir = os.path.join(destDir, 'temp') + + # Remove full path from each output file so that they're relative to + # pumpkinOutputDir. + for i in range(0, len(outputFiles)): + path = outputFiles[i] + outputFiles[i] = path.replace(pumpkinOutputDir, "") + + # Extract tar into temporary directory. + tar = tarfile.open(tarArchive) + tar.extractall(path=tempDir) + tar.close() + + # Copy file contents from tempDir to destDir. + for file in outputFiles: + source = tempDir + file + destination = destDir + file + shutil.copyfile(source, destination) + + # Remove temporary directory. + shutil.rmtree(tempDir) + +if __name__ == '__main__': + main()
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin/unzip_pumpkin.py b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin/unzip_pumpkin.py deleted file mode 100755 index d1b6a14..0000000 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin/unzip_pumpkin.py +++ /dev/null
@@ -1,44 +0,0 @@ -#!/usr/bin/env python - -# Copyright 2021 The Chromium Authors -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import io -import optparse -import sys -from zipfile import ZipFile - -# Unzips archive created with `zip -r ../pumpkin_files.zip *`. -# Archive structure: -# tagger_wasm_main.wasm -# tagger_wasm_main.js -# js_pumpkin_tagger-bin.js -# en_us/action_config.binarypb -# en_us/pumpkin_config.binarypb - -def UnzipPumpkinFiles(filename, output_dir): - with ZipFile(filename, 'r') as zf: - zf.extractall(path=output_dir); - -def main(): - parser = optparse.OptionParser(description=__doc__) - parser.usage = '%prog [options] <zip-file_path>' - - parser.add_option( - '--output-dir', - action='store', - metavar='OUTPUT_DIR', - help='Output directory for extracted files.') - options, args = parser.parse_args() - if len(args) < 1 or not options.output_dir: - print( - 'Expected --output-dir and the input filename to unzip.', - file=sys.stderr) - print(str(args)) - sys.exit(1) - - UnzipPumpkinFiles(args[0], options.output_dir) - -if __name__ == '__main__': - main() \ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin_parse_strategy.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin_parse_strategy.js index a3e63cf..f4082f2 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin_parse_strategy.js +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/pumpkin_parse_strategy.js
@@ -16,26 +16,26 @@ import * as RepeatableKeyPressMacro from '../macros/repeatable_key_press_macro.js'; import {ParseStrategy} from './parse_strategy.js'; - -const PumpkinData = chrome.accessibilityPrivate.PumpkinData; +import * as PumpkinConstants from './pumpkin/pumpkin_constants.js'; /** A parsing strategy that utilizes the Pumpkin semantic parser. */ export class PumpkinParseStrategy extends ParseStrategy { /** @param {!InputController} inputController */ constructor(inputController) { super(inputController); - - /** @private {speech.pumpkin.api.js.PumpkinTagger.PumpkinTagger} */ - this.pumpkinTagger_ = null; - - /** @private {?Promise} */ - this.pumpkinLoadingPromise_ = null; - /** * Whether or not the feature flag gating this object's logic is enabled. * @private {boolean} */ this.featureEnabled_ = false; + /** @private {?PumpkinConstants.PumpkinData} */ + this.pumpkinData_ = null; + /** @private {boolean} */ + this.pumpkinTaggerReady_ = false; + /** @private {Function} */ + this.tagResolver_ = null; + /** @private {?Worker} */ + this.worker_ = null; this.init_(); } @@ -46,7 +46,9 @@ .DICTATION_PUMPKIN_PARSING; chrome.accessibilityPrivate.isFeatureEnabled(pumpkinFeature, enabled => { this.featureEnabled_ = enabled; - if (!enabled) { + const pumpkinLocale = + PumpkinConstants.SUPPORTED_LOCALES[LocaleInfo.locale]; + if (!enabled || !pumpkinLocale) { return; } @@ -57,7 +59,7 @@ } /** - * @param {PumpkinData} data + * @param {PumpkinConstants.PumpkinData} data * @private */ onPumpkinInstalled_(data) { @@ -69,104 +71,75 @@ } for (const [key, value] of Object.entries(data)) { - if (value.byteLength === 0) { - console.warn(`Pumpkin data incomplete, missing data for ${key}`); - return; + if (!value || value.byteLength === 0) { + throw new Error(`Pumpkin data incomplete, missing data for ${key}`); } } - // TODO(akihiroota): Instantiate a sandboxed iframe for Pumpkin. - } - - /** - * Initializes Pumpkin by loading the required scripts and creating the - * PumpkinTagger object. - * @param {string} locale The locale in which to init Pumpkin actions. - * @return {!Promise<undefined>} - * @private - */ - async initPumpkin_(locale) { - if (this.pumpkinLoadingPromise_) { - // Already initializing. + const pumpkinLocale = PumpkinConstants.SUPPORTED_LOCALES[LocaleInfo.locale]; + if (!pumpkinLocale || !this.isEnabled()) { return; } - this.pumpkinLoadingPromise_ = - new Promise(async (pumpkinLoadResolve, pumpkinLoadReject) => { - // Check for objects defined by the Pumpkin WASM. - if (!goog || !goog['global'] || !goog['global']['Module']) { - await this.loadPumpkinScripts_(); - } - const success = await this.createPumpkinTagger_(locale); - if (success) { - pumpkinLoadResolve(); - } else { - pumpkinLoadReject(); - } + // Create SandboxedPumpkinTagger. + this.pumpkinTaggerReady_ = false; + this.pumpkinData_ = data; + + this.worker_ = new Worker( + PumpkinConstants.SANDBOXED_PUMPKIN_TAGGER_JS_FILE, {type: 'module'}); + this.worker_.onmessage = (message) => this.onMessage_(message); + } + + /** + * Called when the SandboxedPumpkinTagger posts a message to the background + * context. + * @param {!Event} message + * @private + */ + onMessage_(message) { + const command = + /** @type {!PumpkinConstants.FromPumpkinTagger} */ (message.data); + switch (command.type) { + case PumpkinConstants.FromPumpkinTaggerCommand.READY: + const pumpkinLocale = + PumpkinConstants.SUPPORTED_LOCALES[LocaleInfo.locale]; + if (!pumpkinLocale) { + throw new Error( + `Can't load SandboxedPumpkinTagger in an unsupported locale ${ + LocaleInfo.locale}`); + } + + this.sendToSandboxedPumpkinTagger_({ + type: PumpkinConstants.ToPumpkinTaggerCommand.LOAD, + locale: pumpkinLocale, + pumpkinData: this.pumpkinData_, }); - } - - /** - * Creates a PumpkinTagger from a config and action frame file for a - * particular locale. - * @param {string} locale The locale in which to init Pumpkin actions. - * @return {!Promise<boolean>} Whether the tagger was created successfully. - * @private - */ - async createPumpkinTagger_(locale) { - const pumpkinTagger = - new speech.pumpkin.api.js.PumpkinTagger.PumpkinTagger(); - try { - const path = `${PumpkinParseStrategy.PUMPKIN_DIR}${locale}/`; - let success = await pumpkinTagger.initializeFromPumpkinConfig( - `${path}${PumpkinParseStrategy.PUMPKIN_CONFIG_PROTO_SRC}`); - if (!success) { - console.warn('Failed to load PumpkinTagger from PumpkinConfig.'); - return false; - } - success = await pumpkinTagger.loadActionFrame( - `${path}${PumpkinParseStrategy.PUMPKIN_ACTION_CONFIG_PROTO_SRC}`); - if (!success) { - console.warn('Failed to load Pumpkin ActionConfig.'); - return false; - } - } catch (e) { - console.warn('Error initializing PumpkinTagger', e); - return false; + this.pumpkinData_ = null; + return; + case PumpkinConstants.FromPumpkinTaggerCommand.FULLY_INITIALIZED: + this.pumpkinTaggerReady_ = true; + return; + case PumpkinConstants.FromPumpkinTaggerCommand.TAG_RESULTS: + this.tagResolver_(command.results); + return; } - this.pumpkinTagger_ = pumpkinTagger; - return true; + + throw new Error( + `Unrecognized message received from SandboxedPumpkinTagger: ${ + command.type}`); } /** - * Loads the Pumpkin scripts javascript in to the document. - * @return {!Promise<undefined>} + * @param {!PumpkinConstants.ToPumpkinTagger} command * @private */ - async loadPumpkinScripts_() { - const pumpkinTaggerScript = - /** @type {!HTMLScriptElement} */ (document.createElement('script')); - pumpkinTaggerScript.src = PumpkinParseStrategy.PUMPKIN_TAGGER_SRC; - const taggerLoadPromise = new Promise((resolve, reject) => { - pumpkinTaggerScript.addEventListener('load', () => { - resolve(); - }); - }); - document.head.appendChild(pumpkinTaggerScript); - await taggerLoadPromise; + sendToSandboxedPumpkinTagger_(command) { + if (!this.worker_) { + throw new Error( + 'Worker not ready, cannot send message to SandboxedPumpkinTagger'); + } - const wasmModuleScript = - /** @type {!HTMLScriptElement} */ (document.createElement('script')); - wasmModuleScript.src = PumpkinParseStrategy.PUMPKIN_WASM_SRC; - const moduleLoadPromise = new Promise((resolve, reject) => { - goog['global']['Module'] = { - onRuntimeInitialized() { - resolve(); - }, - }; - }); - document.head.appendChild(wasmModuleScript); - await moduleLoadPromise; + this.worker_.postMessage(command); } /** @@ -187,21 +160,21 @@ for (let i = 0; i < numArgs; i++) { const argument = hypothesis.actionArgumentList[i]; // See Variable Argument Placeholders in voiceaccess.patterns_template. - if (argument.name === - PumpkinParseStrategy.HypothesisArgumentName.SEM_TAG) { + if (argument.name === PumpkinConstants.HypothesisArgumentName.SEM_TAG) { tag = MacroName[argument.value]; } else if ( - argument.name === - PumpkinParseStrategy.HypothesisArgumentName.NUM_ARG) { + argument.name === PumpkinConstants.HypothesisArgumentName.NUM_ARG) { repeat = argument.value; } else if ( argument.name === - PumpkinParseStrategy.HypothesisArgumentName.OPEN_ENDED_TEXT) { + PumpkinConstants.HypothesisArgumentName.OPEN_ENDED_TEXT) { text = argument.value; } } + // TODO(crbug.com/1362842) Add all macros under the DictationMoreCommands // to this switch statement. + // TODO(crbug.com/1258190): Add support for new macros here. switch (tag) { case MacroName.INPUT_TEXT_VIEW: return new InputTextViewMacro(text, this.getInputController()); @@ -241,85 +214,44 @@ } /** @override */ + refresh() { + const pumpkinLocale = PumpkinConstants.SUPPORTED_LOCALES[LocaleInfo.locale]; + this.enabled = Boolean(pumpkinLocale) && LocaleInfo.areCommandsSupported(); + // TODO(https://crbug.com/1258190): Re-initialize SandboxedPumpkinTagger + // if the locale changed. + } + + /** @override */ async parse(text) { - // Pumpkin load requires several async calls. If the request to parse - // comes before load is complete, wait for load. This happens during - // browser tests which may be fast enough to start sending speech text - // before callbacks with user prefs have completed. - if (this.pumpkinLoadingPromise_) { - await this.pumpkinLoadingPromise_; + if (!this.isEnabled() || !this.pumpkinTaggerReady_) { + return null; } - // Try to get results from Pumpkin. + this.tagResolver_ = null; + // Get results from Pumpkin. // TODO(crbug.com/1264544): Could increase the hypotheses count from 1 // when we are ready to implement disambiguation. - if (this.pumpkinTagger_) { - // Try to get results from Pumpkin. - // TODO(crbug.com/1264544): Could increase the hypotheses count from 1 - // when we are ready to implement disambiguation. - // TODO(crbug.com/1362842) Add logic to check whether - // DictationMoreCommands is enabled or not before - // running the macros hidden within that flag. - const taggerResults = - this.pumpkinTagger_.tagAndGetNBestHypotheses(text, 1); - if (taggerResults && taggerResults.hypothesisList.length > 0) { - const macro = - this.macroFromPumpkinHypothesis_(taggerResults.hypothesisList[0]); - if (macro) { - return macro; - } - } + // TODO(crbug.com/1362842) Add logic to check whether + // DictationMoreCommands is enabled or not before + // running the macros hidden within that flag. + this.sendToSandboxedPumpkinTagger_({ + type: PumpkinConstants.ToPumpkinTaggerCommand.TAG, + text, + numResults: 1, + }); + const taggerResults = await new Promise(resolve => { + this.tagResolver_ = resolve; + }); + + if (!taggerResults || taggerResults.hypothesisList.length === 0) { + return null; } - return null; + return this.macroFromPumpkinHypothesis_(taggerResults.hypothesisList[0]); + } + + /** @override */ + isEnabled() { + return this.enabled && this.featureEnabled_; } } - -/** - * PumpkinTagger Hypothesis argument names. These should match the variable - * argument placeholders in voiceaccess.patterns_template and the static strings - * defined in voiceaccess/utils/PumpkinUtils.java in google3. - * @enum {string} - */ -PumpkinParseStrategy.HypothesisArgumentName = { - SEM_TAG: 'SEM_TAG', - NUM_ARG: 'NUM_ARG', - OPEN_ENDED_TEXT: 'OPEN_ENDED_TEXT', -}; - -/** - * The pumpkin/ directory, relative to the accessibility common base directory. - * @type {string} - * @const - */ -PumpkinParseStrategy.PUMPKIN_DIR = 'dictation/parse/pumpkin/'; - -/** - * The path to the pumpkin tagger source file. - * @type {string} - * @const - */ -PumpkinParseStrategy.PUMPKIN_TAGGER_SRC = - PumpkinParseStrategy.PUMPKIN_DIR + 'js_pumpkin_tagger_bin.js'; - -/** - * The path to the pumpkin web assembly module source file. - * @type {string} - * @const - */ -PumpkinParseStrategy.PUMPKIN_WASM_SRC = - PumpkinParseStrategy.PUMPKIN_DIR + 'tagger_wasm_main.js'; - -/** - * The name of the pumpkin config binary proto file. - * @type {string} - * @const - */ -PumpkinParseStrategy.PUMPKIN_CONFIG_PROTO_SRC = 'pumpkin_config.binarypb'; - -/** - * The name of the pumpkin action config binary proto file. - * @type {string} - * @const - */ -PumpkinParseStrategy.PUMPKIN_ACTION_CONFIG_PROTO_SRC = 'action_config.binarypb';
diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/sandboxed_pumpkin_tagger.js b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/sandboxed_pumpkin_tagger.js new file mode 100644 index 0000000..fdb1d93a --- /dev/null +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/parse/sandboxed_pumpkin_tagger.js
@@ -0,0 +1,179 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import * as PumpkinConstants from './pumpkin/pumpkin_constants.js'; + +/** + * A class that unpacks and loads the pumpkin semantic parser. Runs in a + * web worker for security purposes. + */ +class SandboxedPumpkinTagger { + constructor() { + /** @private {?speech.pumpkin.api.js.PumpkinTagger.PumpkinTagger} */ + this.pumpkinTagger_ = null; + this.init_(); + } + + /** @private */ + init_() { + globalThis.addEventListener( + 'message', (message) => this.onMessage_(message)); + this.sendToBackground_( + {type: PumpkinConstants.FromPumpkinTaggerCommand.READY}); + } + + /** + * Called when the background context posts a message to + * SandboxedPumpkinTagger's web worker. + * @param {!Event} message + * @private + */ + onMessage_(message) { + const command = + /** @type {!PumpkinConstants.ToPumpkinTagger} */ (message.data); + switch (command.type) { + case PumpkinConstants.ToPumpkinTaggerCommand.LOAD: + const pumpkinData = + /** @type {!PumpkinConstants.PumpkinData} */ (command.pumpkinData); + const locale = + /** @type {!PumpkinConstants.PumpkinLocale} */ (command.locale); + this.load_(pumpkinData, locale); + return; + case PumpkinConstants.ToPumpkinTaggerCommand.TAG: + const text = /** @type {string} */ (command.text); + const numResults = /** @type {number} */ (command.numResults); + this.tagAndGetNBestHypotheses_(text, numResults); + return; + } + + throw new Error(`Unrecognized message received in SandboxedPumpkinTagger: ${ + command.type}`); + } + + /** + * @param {!PumpkinConstants.FromPumpkinTagger} command + * @private + */ + sendToBackground_(command) { + postMessage(command); + } + + /** + * @param {string} text + * @param {number} numResults + * @private + */ + tagAndGetNBestHypotheses_(text, numResults) { + const results = + this.pumpkinTagger_.tagAndGetNBestHypotheses(text, numResults); + this.sendToBackground_( + {type: PumpkinConstants.FromPumpkinTaggerCommand.TAG_RESULTS, results}); + } + + /** + * @param {!PumpkinConstants.PumpkinData} data + * @param {!PumpkinConstants.PumpkinLocale} locale + * @private + */ + async load_(data, locale) { + if (!data) { + throw new Error(`Can't load pumpkin tagger from empty data`); + } + + // Unpack the PumpkinTagger JS. + const pumpkinTaggerBytes = data.js_pumpkin_tagger_bin_js; + if (!pumpkinTaggerBytes) { + throw new Error(`Pumpkin tagger bytes must be valid`); + } + const pumpkinTaggerFile = new TextDecoder().decode(pumpkinTaggerBytes); + // Use indirect eval here to ensure the script works in the global scope. + const indirectEval = eval; + const pumpkinTaggerModule = indirectEval(pumpkinTaggerFile); + if (!pumpkinTaggerModule) { + throw new Error('Failed to eval pumpkin tagger file'); + } + /** + * Closure can't recognize pumpkinTaggerModule as a constructor, so suppress + * the error. + * @suppress {checkTypes} + */ + const pumpkinTagger = new pumpkinTaggerModule(); + + // The `taggerWasmJsFile` below expects that the corresponding .wasm file + // lives in the same directory as it. However, since none of these files + // live in the extension directory, we need to override the fetch method + // so that it returns the wasm file bytes from `data` when requested. + globalThis.fetch = async (fileName) => { + return new Promise(resolve => { + const response = new Response(null, { + ok: true, + status: 200, + }); + response.arrayBuffer = async () => { + return new Promise(resolve => { + resolve(data.tagger_wasm_main_wasm); + }); + }; + resolve(response); + }); + }; + + const taggerWasmBytes = data.tagger_wasm_main_js; + if (!taggerWasmBytes) { + throw new Error(`Pumpkin wasm bytes must be valid`); + } + const taggerWasmJsFile = new TextDecoder().decode(taggerWasmBytes); + // A promise that resolves once the web assembly module loads. + const wasmLoadPromise = new Promise((resolve) => { + goog['global']['Module'] = { + onRuntimeInitialized() { + resolve(); + }, + }; + }); + // Load the web assembly. + // Use indirect eval here to ensure the script works in the global scope. + indirectEval(taggerWasmJsFile); + await wasmLoadPromise; + + // Initialize from config files. + let pumpkinConfig; + let actionConfig; + switch (locale) { + case PumpkinConstants.PumpkinLocale.EN_US: + pumpkinConfig = data.en_us_pumpkin_config_binarypb; + actionConfig = data.en_us_action_config_binarypb; + break; + case PumpkinConstants.PumpkinLocale.FR_FR: + pumpkinConfig = data.fr_fr_pumpkin_config_binarypb; + actionConfig = data.fr_fr_action_config_binarypb; + break; + case PumpkinConstants.PumpkinLocale.IT_IT: + pumpkinConfig = data.it_it_pumpkin_config_binarypb; + actionConfig = data.it_it_action_config_binarypb; + break; + case PumpkinConstants.PumpkinLocale.DE_DE: + pumpkinConfig = data.de_de_pumpkin_config_binarypb; + actionConfig = data.de_de_action_config_binarypb; + break; + case PumpkinConstants.PumpkinLocale.ES_ES: + pumpkinConfig = data.es_es_pumpkin_config_binarypb; + actionConfig = data.es_es_action_config_binarypb; + break; + default: + throw new Error( + `Can't initialize Pumpkin in unsupported locale: ${locale}`); + } + + pumpkinTagger.initializeFromPumpkinConfig(pumpkinConfig); + pumpkinTagger.loadActionFrame(actionConfig); + + // Save the PumpkinTagger and notify the background context. + this.pumpkinTagger_ = pumpkinTagger; + this.sendToBackground_( + {type: PumpkinConstants.FromPumpkinTaggerCommand.FULLY_INITIALIZED}); + } +} + +new SandboxedPumpkinTagger();
diff --git a/chrome/browser/resources/chromeos/accessibility/common/testing/mock_accessibility_private.js b/chrome/browser/resources/chromeos/accessibility/common/testing/mock_accessibility_private.js index ae6602d..3f3460ef 100644 --- a/chrome/browser/resources/chromeos/accessibility/common/testing/mock_accessibility_private.js +++ b/chrome/browser/resources/chromeos/accessibility/common/testing/mock_accessibility_private.js
@@ -12,6 +12,17 @@ */ let SelectToSpeakPanelState; +/** + * @typedef {{ + * js_pumpkin_tagger_bin_js: !ArrayBuffer, + * tagger_wasm_main_js: !ArrayBuffer, + * tagger_wasm_main_wasm: !ArrayBuffer, + * en_us_action_config_binarypb: !ArrayBuffer, + * en_us_pumpkin_config_binarypb: !ArrayBuffer, + * }} + */ +let MockPumpkinData; + /* * A mock AccessibilityPrivate API for tests. */ @@ -48,6 +59,9 @@ /** @private {function<number, number>} */ this.boundsListener_ = null; + /** @private {?MockPumpkinData} */ + this.pumpkinData_ = null; + /** * @private {function(!chrome.accessibilityPrivate.SelectToSpeakPanelAction, * number=)} @@ -242,6 +256,11 @@ */ sendSyntheticKeyEvent(unused) {} + /** @return {?PumpkinData} */ + installPumpkinForDictation(callback) { + callback(MockAccessibilityPrivate.pumpkinData_); + } + // Methods for testing. // /** @@ -386,4 +405,34 @@ this.enabledFeatures_.delete(feature); } } + + /** @return {!Promise} */ + async initializePumpkinData() { + /** + * @param {string} file + * @return {!Promise<!ArrayBuffer>} + */ + const getFileBytes = async (file) => { + const response = await fetch(file); + if (response.status === 404) { + throw `Failed to fetch file: ${file}`; + } + + return await response.arrayBuffer(); + }; + + const data = {}; + const pumpkinDir = '../../accessibility_common/dictation/parse/pumpkin'; + data.js_pumpkin_tagger_bin_js = + await getFileBytes(`${pumpkinDir}/js_pumpkin_tagger_bin.js`); + data.tagger_wasm_main_js = + await getFileBytes(`${pumpkinDir}/tagger_wasm_main.js`); + data.tagger_wasm_main_wasm = + await getFileBytes(`${pumpkinDir}/tagger_wasm_main.wasm`); + data.en_us_action_config_binarypb = + await getFileBytes(`${pumpkinDir}/en_us/action_config.binarypb`); + data.en_us_pumpkin_config_binarypb = + await getFileBytes(`${pumpkinDir}/en_us/pumpkin_config.binarypb`); + MockAccessibilityPrivate.pumpkinData_ = data; + } }
diff --git a/chrome/browser/resources/chromeos/accessibility/select_to_speak/ui_manager.js b/chrome/browser/resources/chromeos/accessibility/select_to_speak/ui_manager.js index f352deb..35ab707d 100644 --- a/chrome/browser/resources/chromeos/accessibility/select_to_speak/ui_manager.js +++ b/chrome/browser/resources/chromeos/accessibility/select_to_speak/ui_manager.js
@@ -16,11 +16,9 @@ const SelectToSpeakPanelAction = chrome.accessibilityPrivate.SelectToSpeakPanelAction; -// This must be the same as in -// ash/system/accessibility/select_to_speak/select_to_speak_tray.cc: -// ash::kSelectToSpeakTrayClassName. -export const SELECT_TO_SPEAK_TRAY_CLASS_NAME = - 'tray/TrayBackgroundView/SelectToSpeakTray'; +// This must match the name of view class that implements the SelectToSpeakTray: +// ash/system/accessibility/select_to_speak/select_to_speak_tray.h +export const SELECT_TO_SPEAK_TRAY_CLASS_NAME = 'SelectToSpeakTray'; // This must match the name of view class that implements the menu view: // ash/system/accessibility/select_to_speak/select_to_speak_menu_view.h
diff --git a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html index 8afc8c9..9be0997 100644 --- a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html +++ b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html
@@ -17,6 +17,21 @@ #networkState[connected] { color: var(--cros-text-color-positive); } + + #networkState[warning] { + color: var(--cros-text-color-warning); + } + + .signin-button { + margin-inline-end: 8px; + padding: 8px 16px 8px 8px; + } + + .signin-icon { + background-color: var(--text-color); + margin-inline-end: 4px; + margin-inline-start: 0; + } </style> <!-- Title section: Icon + name + connection state. --> @@ -29,9 +44,18 @@ [[getNameText_(managedProperties_)]] </div> <div id="networkState" class="title flex" - connected$="[[isConnectedState_(managedProperties_)]]"> + connected$="[[showConnectedState_(managedProperties_)]]" + warning$="[[showRestrictedConnectivity_(managedProperties_)]]"> [[getStateText_(managedProperties_)]] </div> + <template is="dom-if" if="[[isCaptivePortalUI2022Enabled_]]"> + <cr-button class="signin-button" id="signinButton" on-click="onSigninTap_" + hidden$="[[!showSignin_(managedProperties_)]]" + disabled="[[disableSignin_(managedProperties_, disabled_)]]"> + <div class="signin-icon cr-icon icon-external"></div> + $i18n{networkButtonSignin} + </cr-button> + </template> <cr-button on-click="onForgetTap_" hidden$="[[!showForget_(managedProperties_)]]" disabled="[[disabled_]]">
diff --git a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js index 751ce6b..d1245b2 100644 --- a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js +++ b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js
@@ -18,16 +18,16 @@ import 'chrome://resources/cr_elements/cr_shared_style.css.js'; import './strings.m.js'; +import {I18nBehavior} from 'chrome://resources/ash/common/i18n_behavior.js'; import {isActiveSim} from 'chrome://resources/ash/common/network/cellular_utils.js'; import {CrPolicyNetworkBehaviorMojo} from 'chrome://resources/ash/common/network/cr_policy_network_behavior_mojo.js'; import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js'; import {NetworkListenerBehavior} from 'chrome://resources/ash/common/network/network_listener_behavior.js'; import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js'; -import {I18nBehavior} from 'chrome://resources/ash/common/i18n_behavior.js'; import {assert} from 'chrome://resources/js/assert.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; import {ApnProperties, ConfigProperties, CrosNetworkConfigRemote, GlobalPolicy, IPConfigProperties, ManagedProperties, NetworkStateProperties, ProxySettings, StartConnectResult} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js'; -import {ConnectionStateType, NetworkType, OncSource} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js'; +import {ConnectionStateType, NetworkType, OncSource, PortalState} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js'; import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {InternetDetailDialogBrowserProxy, InternetDetailDialogBrowserProxyImpl} from './internet_detail_dialog_browser_proxy.js'; @@ -76,6 +76,17 @@ loadTimeData.getBoolean('showTechnologyBadge'); }, }, + /** + * Return true if captivePortalUI2022 feature flag is enabled. + * @private + */ + isCaptivePortalUI2022Enabled_: { + type: Boolean, + value() { + return loadTimeData.valueExists('captivePortalUI2022') && + loadTimeData.getBoolean('captivePortalUI2022'); + }, + }, /** * Whether network configuration properties sections should be shown. The @@ -314,6 +325,20 @@ if (!managedProperties) { return ''; } + + if (this.isCaptivePortalUI2022Enabled_ && + OncMojo.connectionStateIsConnected(managedProperties.connectionState)) { + if (this.isPortalState_(managedProperties.portalState)) { + return this.i18n('networkListItemSignIn'); + } + if (managedProperties.portalState === PortalState.kPortalSuspected) { + return this.i18n('networkListItemConnectedLimited'); + } + if (managedProperties.portalState === PortalState.kNoInternet) { + return this.i18n('networkListItemConnectedNoConnectivity'); + } + } + return this.i18n( OncMojo.getConnectionStateString(managedProperties.connectionState)); }, @@ -328,13 +353,61 @@ }, /** - * @param {!ManagedProperties} managedProperties + * @param {!ManagedProperties|undefined} managedProperties * @return {boolean} True if the network is connected. * @private */ isConnectedState_(managedProperties) { - return OncMojo.connectionStateIsConnected( - managedProperties.connectionState); + return !!managedProperties && + OncMojo.connectionStateIsConnected(managedProperties.connectionState); + }, + + /** + * @param {!ManagedProperties|undefined} + * managedProperties + * @return {boolean} True if the network is restricted. + * @private + */ + isRestrictedConnectivity_(managedProperties) { + return !!managedProperties && + OncMojo.isRestrictedConnectivity(managedProperties.portalState); + }, + + /** + * @param {!ManagedProperties|undefined} + * managedProperties + * @return {boolean} True if the network is connected to have connected color + * for state. + * @private + */ + showConnectedState_(managedProperties) { + // Only check that state is connected if feature flag is disabled. + if (!this.isCaptivePortalUI2022Enabled_) { + return this.isConnectedState_(managedProperties); + } + + return this.isConnectedState_(managedProperties) && + !this.isRestrictedConnectivity_(managedProperties); + }, + + /** + * @param {!ManagedProperties|undefined} + * managedProperties + * @return {boolean} True if the network is restricted to have warning color + * for state. + * @private + */ + showRestrictedConnectivity_(managedProperties) { + // Do not show warning color if feature flag is disabled. + if (!this.isCaptivePortalUI2022Enabled_) { + return false; + } + if (!managedProperties) { + return false; + } + // State must be connected and restricted. + return this.isConnectedState_(managedProperties) && + this.isRestrictedConnectivity_(managedProperties); }, /** @@ -410,6 +483,50 @@ }, /** + * @param {!ManagedProperties|undefined} + * managedProperties + * @return {boolean} + * @private + */ + showSignin_(managedProperties) { + if (!this.isCaptivePortalUI2022Enabled_) { + return false; + } + if (!managedProperties) { + return false; + } + if (OncMojo.connectionStateIsConnected(managedProperties.connectionState) && + this.isPortalState_(managedProperties.portalState)) { + return true; + } + return false; + }, + + /** + * @param {!ManagedProperties} managedProperties + * @return {boolean} + * @private + */ + disableSignin_(managedProperties) { + if (!this.isCaptivePortalUI2022Enabled_) { + return true; + } + if (this.disabled_ || !managedProperties) { + return true; + } + if (!OncMojo.connectionStateIsConnected( + managedProperties.connectionState)) { + return true; + } + return !this.isPortalState_(managedProperties.portalState); + }, + + /** @private */ + onSigninTap_() { + this.browserProxy_.showPortalSignin(this.guid); + }, + + /** * @param {!ManagedProperties} managedProperties * @return {string} * @private @@ -662,4 +779,15 @@ // the dialog's inputs should be disabled. return OncMojo.deviceIsInhibited(this.deviceState_); }, + + /** + * Return true if portalState is either kPortal or kProxyAuthRequired. + * @param {!PortalState} portalState + * @return {boolean} + * @private + */ + isPortalState_(portalState) { + return portalState === PortalState.kPortal || + portalState === PortalState.kProxyAuthRequired; + }, });
diff --git a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog_browser_proxy.js b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog_browser_proxy.js index cf49d628..59ad1de8 100644 --- a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog_browser_proxy.js +++ b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog_browser_proxy.js
@@ -21,6 +21,12 @@ * Signals C++ that the dialog is closed. */ closeDialog() {} + + /** + * Shows the Portal Signin. + * @param {string} guid + */ + showPortalSignin(guid) {} } /** @@ -33,6 +39,11 @@ } /** @override */ + showPortalSignin(guid) { + chrome.send('showPortalSignin', [guid]); + } + + /** @override */ closeDialog() { chrome.send('dialogClose'); }
diff --git a/chrome/browser/resources/chromeos/login/components/security_token_pin.html b/chrome/browser/resources/chromeos/login/components/security_token_pin.html index 05f7c3cc..e754142 100644 --- a/chrome/browser/resources/chromeos/login/components/security_token_pin.html +++ b/chrome/browser/resources/chromeos/login/components/security_token_pin.html
@@ -11,9 +11,7 @@ } #pinKeyboard { - --pin-keyboard-pin-input-style: { - width: 192px; - }; + --pin-keyboard-pin-input-width: 192px; --pin-keyboard-input-letter-spacing: 13px; --pin-keyboard-number-color: var(--cros-text-color-primary); --cr-icon-button-margin-start: 5px;
diff --git a/chrome/browser/resources/new_tab_page/lens_form.html b/chrome/browser/resources/new_tab_page/lens_form.html index 7cb403ff..fc914058 100644 --- a/chrome/browser/resources/new_tab_page/lens_form.html +++ b/chrome/browser/resources/new_tab_page/lens_form.html
@@ -15,4 +15,9 @@ accept="[[supportedFileTypes_]]" on-change="handleFileInputChange_"></input> </form> + <form id="urlForm" + action="[[uploadUrlAction_]]" + method="GET"> + <input name="url" value="[[uploadUrl_]]"></input> + </form> </div>
diff --git a/chrome/browser/resources/new_tab_page/lens_form.ts b/chrome/browser/resources/new_tab_page/lens_form.ts index a67f68a1..6536071 100644 --- a/chrome/browser/resources/new_tab_page/lens_form.ts +++ b/chrome/browser/resources/new_tab_page/lens_form.ts
@@ -7,9 +7,15 @@ import {getTemplate} from './lens_form.html.js'; /** Lens service endpoint for the Upload by File action. */ -const UPLOAD_FILE_ACTION = 'https://lens.google.com/upload'; +const UPLOAD_FILE_ACTION: string = 'https://lens.google.com/upload'; -const SUPPORTED_FILE_TYPES = [ +/** Lens service endpoint for the Upload by URL action. */ +const UPLOAD_BY_URL_ACTION: string = 'https://lens.google.com/uploadbyurl'; + +/** Max length for encoded input URL. */ +const MAX_URL_LENGTH: number = 2000; + +const SUPPORTED_FILE_TYPES: string[] = [ 'image/bmp', 'image/heic', 'image/heif', @@ -21,7 +27,7 @@ ]; /** Maximum file size support by Lens in bytes. */ -const MAX_FILE_SIZE_BYTES = 20 * 1024 * 1024; // 20MB +const MAX_FILE_SIZE_BYTES: number = 20 * 1024 * 1024; // 20MB export enum LensErrorType { // The user attempted to upload multiple files at the same time. @@ -32,12 +38,19 @@ FILE_TYPE, // The user provided a file that is too large. FILE_SIZE, + // The user provided a url with an invalid or missing scheme. + INVALID_SCHEME, + // The user provided a string that does not parse to a valid url. + INVALID_URL, + // The user provided a string that was too long. + LENGTH_TOO_GREAT, } export interface LensFormElement { $: { fileForm: HTMLFormElement, fileInput: HTMLInputElement, + urlForm: HTMLFormElement, }; } @@ -62,9 +75,17 @@ readOnly: true, value: UPLOAD_FILE_ACTION, }, + uploadUrlAction_: { + type: String, + readOnly: true, + value: UPLOAD_BY_URL_ACTION, + }, + uploadUrl_: String, }; } + private uploadUrl_: string = ''; + openSystemFilePicker() { this.$.fileInput.click(); } @@ -105,10 +126,39 @@ dataTransfer.items.add(file); this.$.fileInput.files = dataTransfer.files; - this.dispatchEvent(new Event('loading')); + this.dispatchLoading_(); this.$.fileForm.submit(); } + submitUrl(urlString: string) { + if (!urlString.startsWith('http://') && !urlString.startsWith('https://')) { + this.dispatchError_(LensErrorType.INVALID_SCHEME); + return; + } + + let encodedUri: string; + try { + encodedUri = encodeURI(urlString); + new URL(urlString); // Throws an error if fails to parse. + } catch (e) { + this.dispatchError_(LensErrorType.INVALID_URL); + return; + } + + if (encodedUri.length > MAX_URL_LENGTH) { + this.dispatchError_(LensErrorType.LENGTH_TOO_GREAT); + return; + } + + this.uploadUrl_ = encodedUri; + this.dispatchLoading_(); + this.$.urlForm.submit(); + } + + private dispatchLoading_() { + this.dispatchEvent(new Event('loading')); + } + private dispatchError_(errorType: LensErrorType) { this.dispatchEvent(new CustomEvent('error', { bubbles: false,
diff --git a/chrome/browser/resources/new_tab_page/lens_upload_dialog.html b/chrome/browser/resources/new_tab_page/lens_upload_dialog.html index aa0247f..78c0486 100644 --- a/chrome/browser/resources/new_tab_page/lens_upload_dialog.html +++ b/chrome/browser/resources/new_tab_page/lens_upload_dialog.html
@@ -284,8 +284,13 @@ <div id="inputContainer"> <input id="inputBox" autocomplete="false" autocorrect="false" placeholder="$i18n{lensSearchUploadDialogTextPlaceholder}" - text="text"> - <div id="inputSubmit" tabindex="0" role="button"> + text="text" + value="{{uploadUrl_::input}}" + on-keydown="onUrlKeyDown_"> + <div id="inputSubmit" + tabindex="0" + role="button" + on-click="onSubmitUrl_"> $i18n{lensSearchUploadDialogSearchButtonLabel} </div> </div>
diff --git a/chrome/browser/resources/new_tab_page/lens_upload_dialog.ts b/chrome/browser/resources/new_tab_page/lens_upload_dialog.ts index ffda4a0..2e3bf2726 100644 --- a/chrome/browser/resources/new_tab_page/lens_upload_dialog.ts +++ b/chrome/browser/resources/new_tab_page/lens_upload_dialog.ts
@@ -73,12 +73,16 @@ computed: `computeIsOffline_(dialogState_)`, reflectToAttribute: true, }, + uploadUrl_: { + type: String, + }, }; } private outsideClickHandler_: (event: MouseEvent) => void; private dialogState_ = DialogState.HIDDEN; private outsideClickHandlerAttached_ = false; + private uploadUrl_: string = ''; private computeIsHidden_(dialogState: DialogState): boolean { return dialogState === DialogState.HIDDEN; @@ -176,6 +180,20 @@ private handleFormError_(_event: CustomEvent<LensErrorType>) { // TODO(crbug.com/1367506): Implement error state. } + + private onUrlKeyDown_(event: KeyboardEvent) { + if (event.key === 'Enter') { + event.preventDefault(); + this.onSubmitUrl_(); + } + } + + private onSubmitUrl_() { + const url = this.uploadUrl_.trim(); + if (url.length > 0) { + this.$.lensForm.submitUrl(url); + } + } } declare global { interface HTMLElementTagNameMap {
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn index 4a27d27..97414f2 100644 --- a/chrome/browser/resources/settings/chromeos/BUILD.gn +++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -116,6 +116,7 @@ "//tools/typescript/definitions/quick_unlock_private.d.ts", "//tools/typescript/definitions/runtime.d.ts", "//tools/typescript/definitions/settings_private.d.ts", + "//tools/typescript/definitions/system_display.d.ts", "//tools/typescript/definitions/tabs.d.ts", ] root_dir = "$target_gen_dir/$preprocessed_ts_folder"
diff --git a/chrome/browser/resources/settings/chromeos/device_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/device_page/BUILD.gn index 34bb216..bae20152 100644 --- a/chrome/browser/resources/settings/chromeos/device_page/BUILD.gn +++ b/chrome/browser/resources/settings/chromeos/device_page/BUILD.gn
@@ -14,9 +14,6 @@ ":audio", ":cros_audio_config", ":device_page_browser_proxy", - ":display", - ":display_layout", - ":display_overscan_dialog", ":drag_behavior", ":keyboard", ":layout_behavior", @@ -35,6 +32,8 @@ "../..:router", "//ash/webui/common/resources:i18n_behavior", "//chromeos/ash/components/audio/public/mojom:mojom_webui_js", + "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", + "//ui/webui/resources/js:cr.m", ] externs_list = @@ -53,37 +52,6 @@ ] } -js_library("display") { - deps = [ - "..:os_route", - "..:route_observer_behavior", - "../..:router", - "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", - "//ui/webui/resources/js:cr.m", - ] - externs_list = - chrome_extension_public_externs + [ - "$externs_path/settings_private.js", - "$externs_path/system_display.js", - "//ui/webui/resources/cr_elements/cr_slider/cr_slider_externs.js", - "../settings_controls_types.js", - ] -} - -js_library("display_layout") { - deps = [ - ":drag_behavior", - ":layout_behavior", - ] -} - -js_library("display_overscan_dialog") { - deps = [ - ":display", - "//ui/webui/resources/js:cr.m", - ] -} - js_library("drag_behavior") { deps = [ "//ui/webui/resources/js:assert", @@ -137,6 +105,7 @@ "../..:router", "//ash/webui/common/resources:focus_without_ink_js", "//ash/webui/common/resources:web_ui_listener_behavior", + "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", "//ui/webui/resources/js:assert", "//ui/webui/resources/js:load_time_data.m", ] @@ -147,6 +116,7 @@ ":device_page_browser_proxy", "//ash/webui/common/resources:i18n_behavior", "//ash/webui/common/resources:web_ui_listener_behavior", + "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", ] } @@ -154,15 +124,14 @@ deps = [ "..:prefs_behavior", "//ash/webui/common/resources:web_ui_listener_behavior", + "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", ] + externs_list = chrome_extension_public_externs } html_to_js("web_components") { js_files = [ "audio.js", - "display_layout.js", - "display.js", - "display_overscan_dialog.js", "keyboard.js", "power.js", "storage_external_entry.js",
diff --git a/chrome/browser/resources/settings/chromeos/device_page/display.html b/chrome/browser/resources/settings/chromeos/device_page/display.html index 418b3da6..b626eb8 100644 --- a/chrome/browser/resources/settings/chromeos/device_page/display.html +++ b/chrome/browser/resources/settings/chromeos/device_page/display.html
@@ -121,7 +121,9 @@ <div id="displayScreenTitle" class="start" aria-hidden="true"> $i18n{displayScreenTitle} </div> - <select class="md-select" on-change="updatePrimaryDisplay_" + <select id="primaryDisplaySelect" + class="md-select" + on-change="updatePrimaryDisplay_" aria-labelledby="displayScreenTitle" value="[[getDisplaySelectMenuIndex_( selectedDisplay, primaryDisplayId)]]"> @@ -239,7 +241,8 @@ icon-aria-label="$i18n{displayOrientation}"> </cr-policy-pref-indicator> </template> - <select class="md-select" + <select id="orientationSelect" + class="md-select" value="[[selectedDisplay.rotation]]" aria-labelledby="displayOrientation" on-change="onOrientationChange_"
diff --git a/chrome/browser/resources/settings/chromeos/device_page/display.js b/chrome/browser/resources/settings/chromeos/device_page/display.ts similarity index 68% rename from chrome/browser/resources/settings/chromeos/device_page/display.js rename to chrome/browser/resources/settings/chromeos/device_page/display.ts index 330c819..d6a5dd0 100644 --- a/chrome/browser/resources/settings/chromeos/device_page/display.js +++ b/chrome/browser/resources/settings/chromeos/device_page/display.ts
@@ -24,74 +24,83 @@ import 'chrome://resources/cr_elements/cr_slider/cr_slider.js'; import 'chrome://resources/cr_elements/cr_shared_style.css.js'; -import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/ash/common/i18n_behavior.js'; -import {assert} from 'chrome://resources/js/assert.js'; import {focusWithoutInk} from 'chrome://resources/ash/common/focus_without_ink_js.js'; +import {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js'; +import {CrSliderElement, SliderTick} from 'chrome://resources/cr_elements/cr_slider/cr_slider.js'; +import {I18nMixin, I18nMixinInterface} from 'chrome://resources/cr_elements/i18n_mixin.js'; +import {assert} from 'chrome://resources/js/assert_ts.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; -import {flush, html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {flush, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {DropdownMenuOptionList} from '../../controls/settings_dropdown_menu.js'; +import {SettingsSliderElement} from '../../controls/settings_slider.js'; import {Setting} from '../../mojom-webui/setting.mojom-webui.js'; -import {Route} from '../../router.js'; +import {Route, RouteObserverMixin, RouteObserverMixinInterface} from '../../router.js'; +import {assertExists, cast, castExists} from '../assert_extras.js'; import {DeepLinkingBehavior, DeepLinkingBehaviorInterface} from '../deep_linking_behavior.js'; import {routes} from '../os_route.js'; import {PrefsBehavior, PrefsBehaviorInterface} from '../prefs_behavior.js'; -import {RouteObserverBehavior, RouteObserverBehaviorInterface} from '../route_observer_behavior.js'; import {DevicePageBrowserProxy, DevicePageBrowserProxyImpl, getDisplayApi} from './device_page_browser_proxy.js'; +import {getTemplate} from './display.html.js'; +import {SettingsDisplayOverscanDialogElement} from './display_overscan_dialog.js'; -/** - * @typedef {{ - * value: (!{ - * recommended: (boolean|undefined), - * external_width: (number|undefined), - * external_height: (number|undefined), - * external_use_native: (boolean|undefined), - * external_scale_percentage: (number|undefined), - * internal_scale_percentage: (number|undefined) - * }|null) - * }} - */ -let DisplayResolutionPrefObject; +type DisplayLayout = chrome.system.display.DisplayLayout; +type DisplayMode = chrome.system.display.DisplayMode; +type DisplayProperties = chrome.system.display.DisplayProperties; +type DisplayUnitInfo = chrome.system.display.DisplayUnitInfo; +type GetInfoFlags = chrome.system.display.GetInfoFlags; +type MirrorModeInfo = chrome.system.display.MirrorModeInfo; +const MirrorMode = chrome.system.display.MirrorMode; + +interface DisplayResolutionPrefObject { + value: { + recommended?: boolean, + external_width?: number, + external_height?: number, + external_use_native?: boolean, + external_scale_percentage?: number, + internal_scale_percentage?: number, + }|null; +} /** * The types of Night Light automatic schedule. The values of the enum values * are synced with the pref "prefs.ash.night_light.schedule_type". - * @enum {number} */ -const NightLightScheduleType = { - NEVER: 0, - SUNSET_TO_SUNRISE: 1, - CUSTOM: 2, -}; +enum NightLightScheduleType { + NEVER = 0, + SUNSET_TO_SUNRISE = 1, + CUSTOM = 2, +} -/** - * @constructor - * @extends {PolymerElement} - * @implements {DeepLinkingBehaviorInterface} - * @implements {I18nBehaviorInterface} - * @implements {PrefsBehaviorInterface} - * @implements {RouteObserverBehaviorInterface} - */ -const SettingsDisplayElementBase = mixinBehaviors( - [DeepLinkingBehavior, I18nBehavior, PrefsBehavior, RouteObserverBehavior], - PolymerElement); +interface SettingsDisplayElement { + $: { + displayOverscan: SettingsDisplayOverscanDialogElement, + displaySizeSlider: SettingsSliderElement, + }; +} -/** @polymer */ +const SettingsDisplayElementBase = + mixinBehaviors( + [DeepLinkingBehavior, PrefsBehavior], + RouteObserverMixin(I18nMixin(PolymerElement))) as { + new (): PolymerElement & DeepLinkingBehaviorInterface & + I18nMixinInterface & PrefsBehaviorInterface & + RouteObserverMixinInterface, + }; + class SettingsDisplayElement extends SettingsDisplayElementBase { static get is() { return 'settings-display'; } static get template() { - return html`{__html_template__}`; + return getTemplate(); } static get properties() { return { - /** - * @type {!chrome.settingsPrivate.PrefObject} - * @private - */ selectedModePref_: { type: Object, value() { @@ -103,10 +112,6 @@ }, }, - /** - * @type {!chrome.settingsPrivate.PrefObject} - * @private - */ selectedZoomPref_: { type: Object, value() { @@ -118,16 +123,8 @@ }, }, - /** - * Array of displays. - * @type {!Array<!chrome.system.display.DisplayUnitInfo>} - */ displays: Array, - /** - * Array of display layouts. - * @type {!Array<!chrome.system.display.DisplayLayout>} - */ layouts: Array, /** @@ -139,7 +136,6 @@ /** Primary display id */ primaryDisplayId: String, - /** @type {!chrome.system.display.DisplayUnitInfo|undefined} */ selectedDisplay: Object, /** Id passed to the overscan dialog. */ @@ -151,28 +147,24 @@ /** Ids for mirroring destination displays. */ mirroringDestinationIds: Array, - /** @private {!Array<number>} Mode index values for slider. */ + /** Mode index values for slider. */ modeValues_: Array, /** - * @private {!Array<SliderTick>} Display zoom slider tick - * values. + * Display zoom slider tick values. */ zoomValues_: Array, - /** @private {!DropdownMenuOptionList} */ displayModeList_: { type: Array, value: [], }, - /** @private {!DropdownMenuOptionList} */ refreshRateList_: { type: Array, value: [], }, - /** @private */ unifiedDesktopAvailable_: { type: Boolean, value() { @@ -180,7 +172,6 @@ }, }, - /** @private */ ambientColorAvailable_: { type: Boolean, value() { @@ -188,7 +179,6 @@ }, }, - /** @private */ listAllDisplayModes_: { type: Boolean, value() { @@ -196,16 +186,11 @@ }, }, - /** @private */ unifiedDesktopMode_: { type: Boolean, value: false, }, - /** - * @type {!chrome.settingsPrivate.PrefObject} - * @private - */ selectedParentModePref_: { type: Object, value: function() { @@ -217,7 +202,6 @@ }, }, - /** @private */ scheduleTypesList_: { type: Array, value() { @@ -239,28 +223,22 @@ }, }, - /** @private */ shouldOpenCustomScheduleCollapse_: { type: Boolean, value: false, }, - /** @private */ nightLightScheduleSubLabel_: String, - /** @private */ logicalResolutionText_: String, - /** @private {!Array<string>} */ displayTabNames_: Array, - /** @private */ selectedTab_: Number, /** * Contains the settingId of any deep link that wasn't able to be shown, * null otherwise. - * @private {?Setting} */ pendingSettingId_: { type: Number, @@ -269,7 +247,6 @@ /** * Used by DeepLinkingBehavior to focus this page's deep links. - * @type {!Set<!Setting>} */ supportedSettingIds: { type: Object, @@ -288,7 +265,6 @@ Setting.kDisplayOverscan, ]), }, - }; } @@ -304,37 +280,61 @@ ]; } - /** @override */ + displayIds: string; + displays: DisplayUnitInfo[]; + layouts: DisplayLayout[]; + mirroringDestinationIds: string[]; + overscanDisplayId: string; + primaryDisplayId: string; + selectedDisplay?: DisplayUnitInfo; + private browserProxy_: DevicePageBrowserProxy; + private currentRoute_: Route|null; + private currentSelectedModeIndex_: number; + private currentSelectedParentModeIndex_: number; + private displayChangedListener_: EventListener|null; + private displayModeList_: DropdownMenuOptionList; + private displayTabNames_: string[]; + private invalidDisplayId_: string; + private listAllDisplayModes_: boolean; + private logicalResolutionText_: string; + private modeToParentModeMap_: Map<number, number>; + private modeValues_: number[]; + private nightLightScheduleSubLabel_: string; + private parentModeToRefreshRateMap_: Map<number, DropdownMenuOptionList>; + private pendingSettingId_: Setting|null; + private refreshRateList_: DropdownMenuOptionList; + private selectedModePref_: chrome.settingsPrivate.PrefObject; + private selectedParentModePref_: chrome.settingsPrivate.PrefObject; + private selectedTab_: number; + private selectedZoomPref_: chrome.settingsPrivate.PrefObject; + private shouldOpenCustomScheduleCollapse_: boolean; + private unifiedDesktopMode_: boolean; + private zoomValues_: SliderTick[]; + constructor() { super(); /** * This represents the index of the mode with the highest refresh rate at * the current resolution. - * @private {number} */ this.currentSelectedParentModeIndex_ = -1; /** * This is the index of the currently selected mode. - * @private {number} Selected mode index received from chrome. + * Selected mode index received from chrome. */ this.currentSelectedModeIndex_ = -1; /** * Listener for chrome.system.display.onDisplayChanged events. - * @type {function(void)|undefined} - * @private */ - this.displayChangedListener_ = undefined; + this.displayChangedListener_ = null; - /** @private {string} */ this.invalidDisplayId_ = loadTimeData.getString('invalidDisplayId'); - /** @private {!Route|undefined} */ - this.currentRoute_ = undefined; + this.currentRoute_ = null; - /** @private {!DevicePageBrowserProxy} */ this.browserProxy_ = DevicePageBrowserProxyImpl.getInstance(); /** @@ -343,47 +343,39 @@ * display's mode list. Parent mode indexes represent the mode with the * highest refresh rate at a given resolution. There is 1 and only 1 * parentModeIndex for each possible resolution . - * @private {!Map<number, DropdownMenuOptionList>} */ this.parentModeToRefreshRateMap_ = new Map(); /** * Map containing an entry for each display mode mapping its modeIndex to * the corresponding parentModeIndex value. - * @private {!Map<number, number>} Mode index values for slider. + * Mode index values for slider. */ this.modeToParentModeMap_ = new Map(); } - /** @override */ - connectedCallback() { + override connectedCallback() { super.connectedCallback(); this.displayChangedListener_ = - this.displayChangedListener_ || (() => this.getDisplayInfo_()); + this.displayChangedListener_ || this.getDisplayInfo_.bind(this); getDisplayApi().onDisplayChanged.addListener(this.displayChangedListener_); this.getDisplayInfo_(); this.$.displaySizeSlider.updateValueInstantly = false; } - /** @override */ - disconnectedCallback() { + override disconnectedCallback() { super.disconnectedCallback(); getDisplayApi().onDisplayChanged.removeListener( - assert(this.displayChangedListener_)); + castExists(this.displayChangedListener_)); this.currentSelectedModeIndex_ = -1; this.currentSelectedParentModeIndex_ = -1; } - /** - * Overridden from DeepLinkingBehavior. - * @param {!Setting} settingId - * @return {boolean} - */ - beforeDeepLinkAttempt(settingId) { + override beforeDeepLinkAttempt(_settingId: Setting): boolean { if (!this.displays) { // On initial page load, displays will not be loaded and deep link // attempt will fail. Suppress warnings by exiting early and try again @@ -395,15 +387,11 @@ return true; } - /** - * @param {!Route} newRoute - * @param {!Route=} opt_oldRoute - */ - currentRouteChanged(newRoute, opt_oldRoute) { + override currentRouteChanged(newRoute: Route, oldRoute?: Route) { this.currentRoute_ = newRoute; // When navigating away from the page, deselect any selected display. - if (newRoute !== routes.DISPLAY && opt_oldRoute === routes.DISPLAY) { + if (newRoute !== routes.DISPLAY && oldRoute === routes.DISPLAY) { this.browserProxy_.highlightDisplay(this.invalidDisplayId_); return; } @@ -425,10 +413,8 @@ /** * Shows or hides the overscan dialog. - * @param {boolean} showOverscan - * @private */ - showOverscanDialog_(showOverscan) { + private showOverscanDialog_(showOverscan: boolean) { if (showOverscan) { this.$.displayOverscan.open(); this.$.displayOverscan.focus(); @@ -437,32 +423,28 @@ } } - /** @private */ - onDisplayIdsChanged_() { + private onDisplayIdsChanged_() { // Close any overscan dialog (which will cancel any overscan operation) // if displayIds changes. this.showOverscanDialog_(false); } - /** @private */ - getDisplayInfo_() { - /** @type {chrome.system.display.GetInfoFlags} */ const flags = { + private getDisplayInfo_() { + const flags: GetInfoFlags = { singleUnified: true, }; getDisplayApi().getInfo( - flags, displays => this.displayInfoFetched_(displays)); + flags, + (displays: DisplayUnitInfo[]) => this.displayInfoFetched_(displays)); } - /** - * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays - * @private - */ - displayInfoFetched_(displays) { + private displayInfoFetched_(displays: DisplayUnitInfo[]) { if (!displays.length) { return; } getDisplayApi().getDisplayLayout( - layouts => this.displayLayoutFetched_(displays, layouts)); + (layouts: DisplayLayout[]) => + this.displayLayoutFetched_(displays, layouts)); if (this.isMirrored_(displays)) { this.mirroringDestinationIds = displays[0].mirroringDestinationIds; } else { @@ -470,12 +452,8 @@ } } - /** - * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays - * @param {!Array<!chrome.system.display.DisplayLayout>} layouts - * @private - */ - displayLayoutFetched_(displays, layouts) { + private displayLayoutFetched_( + displays: DisplayUnitInfo[], layouts: DisplayLayout[]) { this.layouts = layouts; this.displays = displays; this.displayTabNames_ = displays.map(({name}) => name); @@ -483,12 +461,10 @@ } /** - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @return {number} The index of the currently selected mode of the + * @return The index of the currently selected mode of the * |selectedDisplay|. If the display has no modes, returns 0. - * @private */ - getSelectedModeIndex_(selectedDisplay) { + private getSelectedModeIndex_(selectedDisplay: DisplayUnitInfo): number { for (let i = 0; i < selectedDisplay.modes.length; ++i) { if (selectedDisplay.modes[i].isSelected) { return i; @@ -497,69 +473,54 @@ return 0; } - /** - * Checks if the given device policy is enabled. - * @param {DisplayResolutionPrefObject} policyPref - * @return {boolean} - * @private - */ - isDevicePolicyEnabled_(policyPref) { + private isDevicePolicyEnabled_(policyPref: DisplayResolutionPrefObject): + boolean { return policyPref !== undefined && policyPref.value !== null; } - /** - * Checks if display resolution is managed by device policy. - * @param {DisplayResolutionPrefObject} resolutionPref - * @return {boolean} - * @private - */ - isDisplayResolutionManagedByPolicy_(resolutionPref) { + private isDisplayResolutionManagedByPolicy_( + resolutionPref: DisplayResolutionPrefObject): boolean { return this.isDevicePolicyEnabled_(resolutionPref) && - (resolutionPref.value.external_use_native !== undefined || - (resolutionPref.value.external_width !== undefined && - resolutionPref.value.external_height !== undefined)); + (resolutionPref.value!.external_use_native !== undefined || + (resolutionPref.value!.external_width !== undefined && + resolutionPref.value!.external_height !== undefined)); } /** * Checks if display resolution is managed by policy and the policy * is mandatory. - * @param {DisplayResolutionPrefObject} resolutionPref - * @return {boolean} - * @private */ - isDisplayResolutionMandatory_(resolutionPref) { + private isDisplayResolutionMandatory_( + resolutionPref: DisplayResolutionPrefObject): boolean { return this.isDisplayResolutionManagedByPolicy_(resolutionPref) && - !resolutionPref.value.recommended; + !resolutionPref.value!.recommended; } /** * Checks if display scale factor is managed by device policy. - * @param {chrome.system.display.DisplayUnitInfo} selectedDisplay - * @param {DisplayResolutionPrefObject} resolutionPref - * @return {boolean} - * @private */ - isDisplayScaleManagedByPolicy_(selectedDisplay, resolutionPref) { + private isDisplayScaleManagedByPolicy_( + selectedDisplay: DisplayUnitInfo, + resolutionPref: DisplayResolutionPrefObject): boolean { if (!this.isDevicePolicyEnabled_(resolutionPref) || !selectedDisplay) { return false; } if (selectedDisplay.isInternal) { - return resolutionPref.value.internal_scale_percentage !== undefined; + return resolutionPref.value!.internal_scale_percentage !== undefined; } - return resolutionPref.value.external_scale_percentage !== undefined; + return resolutionPref.value!.external_scale_percentage !== undefined; } /** * Checks if display scale factor is managed by policy and the policy * is mandatory. - * @param {DisplayResolutionPrefObject} resolutionPref - * @return {boolean} - * @private */ - isDisplayScaleMandatory_(selectedDisplay, resolutionPref) { + private isDisplayScaleMandatory_( + selectedDisplay: DisplayUnitInfo, + resolutionPref: DisplayResolutionPrefObject): boolean { return this.isDisplayScaleManagedByPolicy_( selectedDisplay, resolutionPref) && - !resolutionPref.value.recommended; + !resolutionPref.value!.recommended; } @@ -568,12 +529,10 @@ * contain entries representing a combined resolution + refresh rate. * Only one parse*DisplayModes_ method must be called, depending on the * state of |listAllDisplayModes_|. - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @private */ - parseCompoundDisplayModes_(selectedDisplay) { + private parseCompoundDisplayModes_(selectedDisplay: DisplayUnitInfo) { assert(!this.listAllDisplayModes_); - const optionList = []; + const optionList: DropdownMenuOptionList = []; for (let i = 0; i < selectedDisplay.modes.length; ++i) { const mode = selectedDisplay.modes[i]; @@ -596,11 +555,9 @@ * height => refreshRate => modeIndex. modeIndex is the index of the * resolution + refreshRate combination in |selectedDisplay|'s mode list. * This is used to traverse all possible display modes in ascending order. - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @return {!Map<number, Map<number, Map<number, number>>>} - * @private */ - createModeMap_(selectedDisplay) { + private createModeMap_(selectedDisplay: DisplayUnitInfo): + Map<number, Map<number, Map<number, number>>> { const modes = new Map(); for (let i = 0; i < selectedDisplay.modes.length; ++i) { const mode = selectedDisplay.modes[i]; @@ -635,10 +592,8 @@ * selected, and other possible refresh rates at that resolution are shown * in a dropdown. Only one parse*DisplayModes_ method must be called, * depending on the state of |listAllDisplayModes_|. - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @private */ - parseSplitDisplayModes_(selectedDisplay) { + private parseSplitDisplayModes_(selectedDisplay: DisplayUnitInfo) { assert(this.listAllDisplayModes_); // Clear the mappings before recalculating. this.modeToParentModeMap_ = new Map(); @@ -653,13 +608,13 @@ const widthsArr = Array.from(modes.keys()).sort(); for (let i = 0; i < widthsArr.length; i++) { const width = widthsArr[i]; - const heightsMap = modes.get(width); + const heightsMap = modes.get(width)!; const heightArr = Array.from(heightsMap.keys()); for (let j = 0; j < heightArr.length; j++) { // The highest/first refresh rate for each width/height pair // (resolution) is the default and therefore the "parent" mode. const height = heightArr[j]; - const refreshRates = heightsMap.get(height); + const refreshRates = heightsMap.get(height)!; const parentModeIndex = this.getParentModeIndex_(refreshRates); this.addResolution_(parentModeIndex, width, height); @@ -670,7 +625,7 @@ const refreshRatesArr = Array.from(refreshRates.keys()); for (let k = 0; k < refreshRatesArr.length; k++) { const rate = refreshRatesArr[k]; - const modeIndex = refreshRates.get(rate); + const modeIndex = refreshRates.get(rate)!; const isInterlaced = selectedDisplay.modes[modeIndex].isInterlaced; this.addRefreshRate_(parentModeIndex, modeIndex, rate, isInterlaced); @@ -682,7 +637,7 @@ for (let i = 0; i < selectedDisplay.modes.length; i++) { const mode = selectedDisplay.modes[i]; const parentModeIndex = - this.getParentModeIndex_(modes.get(mode.width).get(mode.height)); + this.getParentModeIndex_(modes.get(mode.width)!.get(mode.height)!); this.modeToParentModeMap_.set(i, parentModeIndex); } assert(this.modeToParentModeMap_.size === selectedDisplay.modes.length); @@ -694,24 +649,21 @@ /** * Picks the appropriate parent mode from a refresh rate -> mode index map. * Currently this chooses the mode with the highest refresh rate. - * @param {Map<number,number>} refreshRates each possible refresh rate + * @param refreshRates each possible refresh rate * mapped to the corresponding mode index. - * @private */ - getParentModeIndex_(refreshRates) { + private getParentModeIndex_(refreshRates: Map<number, number>) { const maxRefreshRate = Math.max(...refreshRates.keys()); - return refreshRates.get(maxRefreshRate); + // maxRefreshRate always exists as a key + return refreshRates.get(maxRefreshRate)!; } /** * Adds a an entry in |displayModeList_| for the resolution represented by * |width| and |height| and possible |refreshRates|. - * @param {number} parentModeIndex - * @param {number} width - * @param {number} height - * @private */ - addResolution_(parentModeIndex, width, height) { + private addResolution_( + parentModeIndex: number, width: number, height: number) { assert(this.listAllDisplayModes_); // Add an entry in the outer map for |parentModeIndex|. The inner @@ -733,13 +685,10 @@ /** * Adds a an entry in |parentModeToRefreshRateMap_| for the refresh rate * represented by |rate|. - * @param {number} parentModeIndex - * @param {number} modeIndex - * @param {number} rate - * @param {boolean|undefined} isInterlaced - * @private */ - addRefreshRate_(parentModeIndex, modeIndex, rate, isInterlaced) { + private addRefreshRate_( + parentModeIndex: number, modeIndex: number, rate: number, + isInterlaced?: boolean) { assert(this.listAllDisplayModes_); // Truncate at two decimal places for display. If the refresh rate @@ -754,7 +703,7 @@ const refreshRateOption = this.i18n(id, refreshRate.toString()); - this.parentModeToRefreshRateMap_.get(parentModeIndex).push({ + this.parentModeToRefreshRateMap_.get(parentModeIndex)!.push({ name: refreshRateOption, value: modeIndex, }); @@ -763,10 +712,9 @@ /** * Sorts |displayModeList_| in descending order. First order sort is width, * second order sort is height. - * @private */ - sortResolutionList_() { - const getWidthFromResolutionString = function(str) { + private sortResolutionList_() { + const getWidthFromResolutionString = function(str: string) { return Number(str.substr(0, str.indexOf(' '))); }; @@ -784,10 +732,8 @@ * refresh rate combo. If |listAllDisplayModes_| is on, resolution and * refresh rate are parsed into separate dropdowns and * |parentModeToRefreshRateMap_| + |modeToParentModeMap_| are populated. - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @private */ - updateDisplayModeStructures_(selectedDisplay) { + private updateDisplayModeStructures_(selectedDisplay: DisplayUnitInfo) { if (this.listAllDisplayModes_) { this.parseSplitDisplayModes_(selectedDisplay); } else { @@ -798,11 +744,8 @@ /** * Returns a value from |zoomValues_| that is closest to the display zoom * percentage currently selected for the |selectedDisplay|. - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @return {number} - * @private */ - getSelectedDisplayZoom_(selectedDisplay) { + private getSelectedDisplayZoom_(selectedDisplay: DisplayUnitInfo): number { const selectedZoom = selectedDisplay.displayZoomFactor; let closestMatch = this.zoomValues_[0].value; let minimumDiff = Math.abs(closestMatch - selectedZoom); @@ -815,16 +758,14 @@ } } - return /** @type {number} */ (closestMatch); + return closestMatch; } /** * Given the display with the current display mode, this function lists all * the display zoom values and their labels to be used by the slider. - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @return {!Array<SliderTick>} */ - getZoomValues_(selectedDisplay) { + private getZoomValues_(selectedDisplay: DisplayUnitInfo): SliderTick[] { return selectedDisplay.availableDisplayZoomFactors.map(value => { const ariaValue = Math.round(value * 100); return { @@ -838,10 +779,8 @@ /** * We need to call this explicitly rather than relying on change events * so that we can control the update order. - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @private */ - setSelectedDisplay_(selectedDisplay) { + private setSelectedDisplay_(selectedDisplay: DisplayUnitInfo) { // |modeValues_| controls the resolution slider's tick values. Changing it // might trigger a change in the |selectedModePref_.value| if the number // of modes differs and the current mode index is out of range of the new @@ -878,9 +817,9 @@ // Now that everything is in sync, set the selected mode to its correct // value right before updating the pref. this.currentSelectedParentModeIndex_ = - this.modeToParentModeMap_.get(currentModeIndex); + this.modeToParentModeMap_.get(currentModeIndex)!; this.refreshRateList_ = this.parentModeToRefreshRateMap_.get( - this.currentSelectedParentModeIndex_); + this.currentSelectedParentModeIndex_)!; } else { this.currentSelectedParentModeIndex_ = currentModeIndex; } @@ -888,27 +827,20 @@ this.set( 'selectedParentModePref_.value', this.currentSelectedParentModeIndex_); - this.updateLogicalResolutionText_( - /** @type {number} */ (this.selectedZoomPref_.value)); + this.updateLogicalResolutionText_(this.selectedZoomPref_.value); } /** * Returns true if the resolution setting needs to be displayed. - * @param {!chrome.system.display.DisplayUnitInfo} display - * @return {boolean} - * @private */ - showDropDownResolutionSetting_(display) { + private showDropDownResolutionSetting_(display: DisplayUnitInfo): boolean { return !display.isInternal; } /** * Returns true if the refresh rate setting needs to be displayed. - * @param {!chrome.system.display.DisplayUnitInfo} display - * @return {boolean} - * @private */ - showRefreshRateSetting_(display) { + private showRefreshRateSetting_(display: DisplayUnitInfo): boolean { return this.listAllDisplayModes_ && this.showDropDownResolutionSetting_(display); } @@ -917,53 +849,37 @@ * Returns true if external touch devices are connected and the current * display is not an internal display. If the feature is not enabled via the * switch, this will return false. - * @param {!chrome.system.display.DisplayUnitInfo} display Display being - * checked for touch support. - * @return {boolean} - * @private + * @param display Display being checked for touch support. */ - showTouchCalibrationSetting_(display) { + private showTouchCalibrationSetting_(display: DisplayUnitInfo): boolean { return !display.isInternal && loadTimeData.getBoolean('enableTouchCalibrationSetting'); } /** * Returns true if the overscan setting should be shown for |display|. - * @param {!chrome.system.display.DisplayUnitInfo} display - * @return {boolean} - * @private */ - showOverscanSetting_(display) { + private showOverscanSetting_(display: DisplayUnitInfo): boolean { return !display.isInternal; } /** * Returns true if the ambient color setting should be shown for |display|. - * @param {boolean} ambientColorAvailable - * @param {chrome.system.display.DisplayUnitInfo} display - * @return {boolean} - * @private */ - showAmbientColorSetting_(ambientColorAvailable, display) { + private showAmbientColorSetting_( + ambientColorAvailable: boolean, display: DisplayUnitInfo): boolean { return ambientColorAvailable && display && display.isInternal; } - /** - * @return {boolean} - * @private - */ - hasMultipleDisplays_() { + private hasMultipleDisplays_(): boolean { return this.displays.length > 1; } /** * Returns false if the display select menu has to be hidden. - * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @return {boolean} - * @private */ - showDisplaySelectMenu_(displays, selectedDisplay) { + private showDisplaySelectMenu_( + displays: DisplayUnitInfo[], selectedDisplay: DisplayUnitInfo): boolean { if (selectedDisplay) { return displays.length > 1 && !selectedDisplay.isPrimary; } @@ -974,12 +890,10 @@ /** * Returns the select menu index indicating whether the display currently is * primary or extended. - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @param {string} primaryDisplayId - * @return {number} Returns 0 if the display is primary else returns 1. - * @private + * @return Returns 0 if the display is primary else returns 1. */ - getDisplaySelectMenuIndex_(selectedDisplay, primaryDisplayId) { + private getDisplaySelectMenuIndex_( + selectedDisplay: DisplayUnitInfo, primaryDisplayId: string): number { if (selectedDisplay && selectedDisplay.id === primaryDisplayId) { return 0; } @@ -988,22 +902,15 @@ /** * Returns the i18n string for the text to be used for mirroring settings. - * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays - * @return {string} i18n string for mirroring settings text. - * @private + * @return i18n string for mirroring settings text. */ - getDisplayMirrorText_(displays) { + private getDisplayMirrorText_(displays: DisplayUnitInfo[]): string { return this.i18n('displayMirror', displays[0].name); } - /** - * @param {boolean} unifiedDesktopAvailable - * @param {boolean} unifiedDesktopMode - * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays - * @return {boolean} - * @private - */ - showUnifiedDesktop_(unifiedDesktopAvailable, unifiedDesktopMode, displays) { + private showUnifiedDesktop_( + unifiedDesktopAvailable: boolean, unifiedDesktopMode: boolean, + displays: DisplayUnitInfo[]): boolean { if (displays === undefined) { return false; } @@ -1013,24 +920,14 @@ !this.isMirrored_(displays)); } - /** - * @param {boolean} unifiedDesktopMode - * @return {string} - * @private - */ - getUnifiedDesktopText_(unifiedDesktopMode) { + private getUnifiedDesktopText_(unifiedDesktopMode: boolean): string { return this.i18n( unifiedDesktopMode ? 'displayUnifiedDesktopOn' : 'displayUnifiedDesktopOff'); } - /** - * @param {boolean} unifiedDesktopMode - * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays - * @return {boolean} - * @private - */ - showMirror_(unifiedDesktopMode, displays) { + private showMirror_(unifiedDesktopMode: boolean, displays: DisplayUnitInfo[]): + boolean { if (displays === undefined) { return false; } @@ -1039,53 +936,30 @@ (!unifiedDesktopMode && displays.length > 1); } - /** - * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays - * @return {boolean} - * @private - */ - isMirrored_(displays) { + private isMirrored_(displays: DisplayUnitInfo[]): boolean { return displays !== undefined && displays.length > 0 && !!displays[0].mirroringSourceId; } - /** - * @param {!chrome.system.display.DisplayUnitInfo} display - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @return {boolean} - * @private - */ - isSelected_(display, selectedDisplay) { + private isSelected_( + display: DisplayUnitInfo, selectedDisplay: DisplayUnitInfo): boolean { return display.id === selectedDisplay.id; } - /** - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @return {boolean} - * @private - */ - enableSetResolution_(selectedDisplay) { + private enableSetResolution_(selectedDisplay: DisplayUnitInfo): boolean { return selectedDisplay.modes.length > 1; } - /** - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @return {boolean} - * @private - */ - enableDisplayZoomSlider_(selectedDisplay) { + private enableDisplayZoomSlider_(selectedDisplay: DisplayUnitInfo): boolean { return selectedDisplay.availableDisplayZoomFactors.length > 1; } /** * Returns true if the given mode is the best mode for the * |selectedDisplay|. - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @param {!chrome.system.display.DisplayMode} mode - * @return {boolean} - * @private */ - isBestMode_(selectedDisplay, mode) { + private isBestMode_(selectedDisplay: DisplayUnitInfo, mode: DisplayMode): + boolean { if (!selectedDisplay.isInternal) { return mode.isNative; } @@ -1093,29 +967,25 @@ // Things work differently for full HD devices(1080p). The best mode is // the one with 1.25 device scale factor and 0.8 ui scale. if (mode.heightInNativePixels === 1080) { - return Math.abs(mode.uiScale - 0.8) < 0.001 && + return Math.abs(mode.uiScale! - 0.8) < 0.001 && Math.abs(mode.deviceScaleFactor - 1.25) < 0.001; } return mode.uiScale === 1.0; } - /** - * @return {string} - * @private - */ - getResolutionText_() { + private getResolutionText_(): string { + assertExists(this.selectedDisplay); if (this.selectedDisplay.modes.length === 0 || this.currentSelectedModeIndex_ === -1) { - // If currentSelectedModeIndex_ == -1, selectedDisplay and + // If currentSelectedModeIndex_ is -1, selectedDisplay and // |selectedModePref_.value| are not in sync. return this.i18n( 'displayResolutionText', this.selectedDisplay.bounds.width.toString(), this.selectedDisplay.bounds.height.toString()); } - const mode = this.selectedDisplay.modes[ - /** @type {number} */ (this.selectedModePref_.value)]; - assert(mode); + const mode = + castExists(this.selectedDisplay.modes[this.selectedModePref_.value]); const widthStr = mode.width.toString(); const heightStr = mode.height.toString(); if (this.isBestMode_(this.selectedDisplay, mode)) { @@ -1129,11 +999,10 @@ /** * Updates the logical resolution text to be used for the display size * section - * @param {number} zoomFactor Current zoom factor applied on the selected - * display. - * @private + * @param zoomFactor Current zoom factor applied on the selected display. */ - updateLogicalResolutionText_(zoomFactor) { + private updateLogicalResolutionText_(zoomFactor: number) { + assertExists(this.selectedDisplay); if (!this.selectedDisplay.isInternal) { this.logicalResolutionText_ = ''; return; @@ -1167,9 +1036,9 @@ * Logical Resolution Text. Returns true if the longer edge of the * display's native pixels is different than the longer edge of the * display's current bounds. - * @private */ - shouldSwapLogicalResolutionText_() { + private shouldSwapLogicalResolutionText_() { + assertExists(this.selectedDisplay); const mode = this.selectedDisplay.modes[this.currentSelectedModeIndex_]; const bounds = this.selectedDisplay.bounds; @@ -1177,30 +1046,27 @@ mode.widthInNativePixels > mode.heightInNativePixels; } - /** * Handles the event where the display size slider is being dragged, i.e. * the mouse or tap has not been released. - * @private */ - onDisplaySizeSliderDrag_() { + private onDisplaySizeSliderDrag_() { if (!this.selectedDisplay) { return; } - const sliderValue = - this.$.displaySizeSlider.shadowRoot.querySelector('#slider').value; - const zoomFactor = this.$.displaySizeSlider.ticks[sliderValue].value; - this.updateLogicalResolutionText_( - /** @type {number} */ (zoomFactor)); + const slider = castExists( + this.$.displaySizeSlider.shadowRoot!.querySelector<CrSliderElement>( + '#slider')); + const zoomFactor = + (this.$.displaySizeSlider.ticks as SliderTick[])[slider.value].value; + this.updateLogicalResolutionText_(zoomFactor); } /** - * @param {!CustomEvent<string>} e |e.detail| is the id of the selected - * display. - * @private + * @param e |e.detail| is the id of the selected display. */ - onSelectDisplay_(e) { + private onSelectDisplay_(e: CustomEvent<string>) { const id = e.detail; for (let i = 0; i < this.displays.length; ++i) { const display = this.displays[i]; @@ -1213,9 +1079,8 @@ } } - /** @private */ - onSelectDisplayTab_() { - const {selected} = this.shadowRoot.querySelector('cr-tabs'); + private onSelectDisplayTab_() { + const {selected} = castExists(this.shadowRoot!.querySelector('cr-tabs')); if (this.selectedTab_ !== selected) { this.setSelectedDisplay_(this.displays[selected]); } @@ -1223,31 +1088,26 @@ /** * Handles event when a touch calibration option is selected. - * @param {!Event} e - * @private */ - onTouchCalibrationTap_(e) { - getDisplayApi().showNativeTouchCalibration(this.selectedDisplay.id); + private onTouchCalibrationTap_() { + getDisplayApi().showNativeTouchCalibration(this.selectedDisplay!.id); } /** * Handles the event when an option from display select menu is selected. - * @param {!{target: !HTMLSelectElement}} e - * @private */ - updatePrimaryDisplay_(e) { - /** @type {number} */ const PRIMARY_DISP_IDX = 0; + private updatePrimaryDisplay_(e: Event) { if (!this.selectedDisplay) { return; } if (this.selectedDisplay.id === this.primaryDisplayId) { return; } - if (!e.target.value) { + if (!(e.target as HTMLSelectElement).value) { return; } - /** @type {!chrome.system.display.DisplayProperties} */ const properties = { + const properties: DisplayProperties = { isPrimary: true, }; getDisplayApi().setDisplayProperties( @@ -1258,10 +1118,8 @@ /** * Handles a change in the |selectedParentModePref| value triggered via the * observer. - * @param {number} newModeIndex The new index value - * @private */ - onSelectedParentModeChange_(newModeIndex) { + private onSelectedParentModeChange_(newModeIndex: number) { if (this.currentSelectedParentModeIndex_ === newModeIndex) { return; } @@ -1279,10 +1137,8 @@ /** * Returns True if a new parentMode has been set and we have received an * update from Chrome. - * @return {boolean} - * @private */ - hasNewParentModeBeenSet() { + private hasNewParentModeBeenSet(): boolean { if (this.currentSelectedParentModeIndex_ === -1) { return false; } @@ -1294,10 +1150,8 @@ /** * Returns True if a new mode has been set and we have received an update * from Chrome. - * @return {boolean} - * @private */ - hasNewModeBeenSet() { + private hasNewModeBeenSet(): boolean { if (this.currentSelectedModeIndex_ === -1) { return false; } @@ -1312,10 +1166,8 @@ /** * Handles a change in |selectedModePref| triggered via the observer. - * @param {number} newModeIndex The new index value - * @private */ - onSelectedModeChange_(newModeIndex) { + private onSelectedModeChange_(newModeIndex: number) { // We want to ignore all value changes to the pref due to the slider being // dragged. See http://crbug/845712 for more info. if (this.currentSelectedModeIndex_ === newModeIndex) { @@ -1327,13 +1179,14 @@ // update from Chrome and the mode differs from the current mode. return; } - /** @type {!chrome.system.display.DisplayProperties} */ const properties = { - displayMode: this.selectedDisplay.modes[ - /** @type {number} */ (this.selectedModePref_.value)], + + assertExists(this.selectedDisplay); + const properties: DisplayProperties = { + displayMode: this.selectedDisplay.modes[this.selectedModePref_.value], }; - this.refreshRateList_ = this.parentModeToRefreshRateMap_.get( - /** @type {number} */ (this.selectedParentModePref_.value)); + this.refreshRateList_ = castExists(this.parentModeToRefreshRateMap_.get( + this.selectedParentModePref_.value)); getDisplayApi().setDisplayProperties( this.selectedDisplay.id, properties, () => this.setPropertiesCallback_()); @@ -1343,16 +1196,14 @@ * Triggerend when the display size slider changes its value. This only * occurs when the value is committed (i.e. not while the slider is being * dragged). - * @private */ - onSelectedZoomChange_() { + private onSelectedZoomChange_() { if (this.currentSelectedModeIndex_ === -1 || !this.selectedDisplay) { return; } - /** @type {!chrome.system.display.DisplayProperties} */ const properties = { - displayZoomFactor: - /** @type {number} */ (this.selectedZoomPref_.value), + const properties: DisplayProperties = { + displayZoomFactor: this.selectedZoomPref_.value, }; getDisplayApi().setDisplayProperties( @@ -1363,25 +1214,20 @@ /** * Returns whether the option "Auto-rotate" is one of the shown options in * the rotation drop-down menu. - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @return {boolean|undefined} - * @private */ - showAutoRotateOption_(selectedDisplay) { + private showAutoRotateOption_(selectedDisplay: DisplayUnitInfo): boolean + |undefined { return selectedDisplay.isAutoRotationAllowed; } - /** - * @param {!Event} event - * @private - */ - onOrientationChange_(event) { - const target = /** @type {!HTMLSelectElement} */ (event.target); - const value = /** @type {number} */ (parseInt(target.value, 10)); + private onOrientationChange_(event: Event) { + const select = cast(event.target, HTMLSelectElement); + const value = parseInt(select.value, 10); + assertExists(this.selectedDisplay); assert(value !== -1 || this.selectedDisplay.isAutoRotationAllowed); - /** @type {!chrome.system.display.DisplayProperties} */ const properties = { + const properties: DisplayProperties = { rotation: value, }; getDisplayApi().setDisplayProperties( @@ -1389,17 +1235,14 @@ () => this.setPropertiesCallback_()); } - /** @private */ - onMirroredTap_(event) { + private onMirroredTap_(event: Event) { // Blur the control so that when the transition animation completes and // the UI is focused, the control does not receive focus. crbug.com/785070 - event.target.blur(); + (event.currentTarget as CrCheckboxElement).blur(); - /** @type {!chrome.system.display.MirrorModeInfo} */ - const mirrorModeInfo = { - mode: this.isMirrored_(this.displays) ? - chrome.system.display.MirrorMode.OFF : - chrome.system.display.MirrorMode.NORMAL, + const mirrorModeInfo: MirrorModeInfo = { + mode: this.isMirrored_(this.displays) ? MirrorMode.OFF : + MirrorMode.NORMAL, }; getDisplayApi().setMirrorMode(mirrorModeInfo, () => { const error = chrome.runtime.lastError; @@ -1409,35 +1252,28 @@ }); } - /** @private */ - onUnifiedDesktopTap_() { - /** @type {!chrome.system.display.DisplayProperties} */ const properties = { + private onUnifiedDesktopTap_() { + const properties: DisplayProperties = { isUnified: !this.unifiedDesktopMode_, }; getDisplayApi().setDisplayProperties( this.primaryDisplayId, properties, () => this.setPropertiesCallback_()); } - /** - * @param {!Event} e - * @private - */ - onOverscanTap_(e) { + private onOverscanTap_(e: Event) { e.preventDefault(); - this.overscanDisplayId = this.selectedDisplay.id; + this.overscanDisplayId = this.selectedDisplay!.id; this.showOverscanDialog_(true); } - /** @private */ - onCloseOverscanDialog_() { - focusWithoutInk(assert(this.shadowRoot.querySelector('#overscan'))); + private onCloseOverscanDialog_() { + focusWithoutInk(castExists(this.shadowRoot!.getElementById('overscan'))); } - /** @private */ - updateDisplayInfo_() { + private updateDisplayInfo_() { let displayIds = ''; - let primaryDisplay = undefined; - let selectedDisplay = undefined; + let primaryDisplay: DisplayUnitInfo|undefined = undefined; + let selectedDisplay: DisplayUnitInfo|undefined = undefined; for (let i = 0; i < this.displays.length; ++i) { const display = this.displays[i]; if (displayIds) { @@ -1471,8 +1307,7 @@ }); } - /** @private */ - setPropertiesCallback_() { + private setPropertiesCallback_() { if (chrome.runtime.lastError) { console.error( 'setDisplayProperties Error: ' + chrome.runtime.lastError.message); @@ -1483,9 +1318,8 @@ * Invoked when the status of Night Light or its schedule type are changed, * in order to update the schedule settings, such as whether to show the * custom schedule slider, and the schedule sub label. - * @private */ - updateNightLightScheduleSettings_() { + private updateNightLightScheduleSettings_() { const scheduleType = this.getPref('ash.night_light.schedule_type').value; this.shouldOpenCustomScheduleCollapse_ = scheduleType === NightLightScheduleType.CUSTOM; @@ -1500,21 +1334,16 @@ } } - /** - * @return {boolean} - * @private - */ - shouldShowArrangementSection_() { + private shouldShowArrangementSection_(): boolean { if (!this.displays) { return false; } return this.hasMultipleDisplays_() || this.isMirrored_(this.displays); } - /** @private */ - onDisplaysChanged_() { + private onDisplaysChanged_() { flush(); - const displayLayout = this.shadowRoot.querySelector('#displayLayout'); + const displayLayout = this.shadowRoot!.querySelector('display-layout'); if (displayLayout) { displayLayout.updateDisplays( this.displays, this.layouts, this.mirroringDestinationIds); @@ -1522,4 +1351,10 @@ } } +declare global { + interface HTMLElementTagNameMap { + 'settings-display': SettingsDisplayElement; + } +} + customElements.define(SettingsDisplayElement.is, SettingsDisplayElement);
diff --git a/chrome/browser/resources/settings/chromeos/device_page/display_layout.js b/chrome/browser/resources/settings/chromeos/device_page/display_layout.ts similarity index 66% rename from chrome/browser/resources/settings/chromeos/device_page/display_layout.js rename to chrome/browser/resources/settings/chromeos/device_page/display_layout.ts index 832c88d..046d3469 100644 --- a/chrome/browser/resources/settings/chromeos/device_page/display_layout.js +++ b/chrome/browser/resources/settings/chromeos/device_page/display_layout.ts
@@ -13,60 +13,70 @@ import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; import {IronResizableBehavior} from 'chrome://resources/polymer/v3_0/iron-resizable-behavior/iron-resizable-behavior.js'; -import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; + +import {castExists} from '../assert_extras.js'; import {DevicePageBrowserProxy, DevicePageBrowserProxyImpl} from './device_page_browser_proxy.js'; +import {getTemplate} from './display_layout.html.js'; import {DragBehavior, DragBehaviorInterface, DragPosition} from './drag_behavior.js'; import {LayoutBehavior, LayoutBehaviorInterface} from './layout_behavior.js'; +type DisplayUnitInfo = chrome.system.display.DisplayUnitInfo; +type DisplayLayout = chrome.system.display.DisplayLayout; +type Bounds = chrome.system.display.Bounds; + /** * Container for DisplayUnitInfo. Mostly here to make the DisplaySelectEvent * typedef more readable. - * @typedef {{item: !chrome.system.display.DisplayUnitInfo}} */ -let InfoItem; +interface InfoItem { + item: DisplayUnitInfo; +} /** * Required member fields for events which select displays. - * @typedef {{model: !InfoItem, target: !HTMLDivElement}} */ -let DisplaySelectEvent; +interface DisplaySelectEvent { + model: InfoItem; + target: HTMLElement; +} -/** @type {number} */ const MIN_VISUAL_SCALE = .01; +const MIN_VISUAL_SCALE = .01; -/** - * @constructor - * @extends {PolymerElement} - * @implements {DragBehaviorInterface} - * @implements {LayoutBehaviorInterface} - */ -const DisplayLayoutElementBase = mixinBehaviors( - [IronResizableBehavior, DragBehavior, LayoutBehavior], PolymerElement); +interface DisplayLayoutElement { + $: { + displayArea: HTMLElement, + }; +} -/** @polymer */ +const DisplayLayoutElementBase = + mixinBehaviors( + [IronResizableBehavior, DragBehavior, LayoutBehavior], + PolymerElement) as { + new (): PolymerElement & DragBehaviorInterface & LayoutBehaviorInterface, + }; + class DisplayLayoutElement extends DisplayLayoutElementBase { static get is() { return 'display-layout'; } static get template() { - return html`{__html_template__}`; + return getTemplate(); } static get properties() { return { /** * Array of displays. - * @type {!Array<!chrome.system.display.DisplayUnitInfo>} */ displays: Array, - /** @type {!chrome.system.display.DisplayUnitInfo|undefined} */ selectedDisplay: Object, /** * The ratio of the display area div (in px) to DisplayUnitInfo.bounds. - * @type {number} */ visualScale: { type: Number, @@ -75,44 +85,47 @@ /** * Ids for mirroring destination displays. - * @type {!Array<string>|undefined} - * @private */ mirroringDestinationIds_: Array, }; } - /** @override */ + displays: DisplayUnitInfo[]; + selectedDisplay?: DisplayUnitInfo; + visualScale: number; + private allowDisplayAlignmentApi_: boolean; + private browserProxy_: DevicePageBrowserProxy; + private hasDragStarted_: boolean; + private invalidDisplayId_: string; + private lastDragCoordinates_: {x: number, y: number}|null; + private mirroringDestinationIds_: string[]; + private visualOffset_: {left: number, top: number}; + constructor() { super(); - /** @private {!{left: number, top: number}} */ this.visualOffset_ = {left: 0, top: 0}; /** * Stores the previous coordinates of a display once dragging starts. Used * to calculate the delta during each step of the drag. Null when there is * no drag in progress. - * @private {?{x: number, y: number}} */ this.lastDragCoordinates_ = null; - /** @private {!DevicePageBrowserProxy} */ this.browserProxy_ = DevicePageBrowserProxyImpl.getInstance(); - /** @private {boolean} */ this.allowDisplayAlignmentApi_ = loadTimeData.getBoolean('allowDisplayAlignmentApi'); - /** @private {string} */ this.invalidDisplayId_ = loadTimeData.getString('invalidDisplayId'); - /** @private {boolean} */ this.hasDragStarted_ = false; + + this.mirroringDestinationIds_ = []; } - /** @override */ - disconnectedCallback() { + override disconnectedCallback() { super.disconnectedCallback(); this.initializeDrag(false); @@ -121,11 +134,10 @@ /** * Called explicitly when |this.displays| and their associated |this.layouts| * have been fetched from chrome. - * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays - * @param {!Array<!chrome.system.display.DisplayLayout>} layouts - * @param {!Array<string>} mirroringDestinationIds */ - updateDisplays(displays, layouts, mirroringDestinationIds) { + updateDisplays( + displays: DisplayUnitInfo[], layouts: DisplayLayout[], + mirroringDestinationIds: string[]) { this.displays = displays; this.layouts = layouts; this.mirroringDestinationIds_ = mirroringDestinationIds; @@ -152,10 +164,9 @@ * Calculates the visual offset and scale for the display area * (i.e. the ratio of the display area div size to the area required to * contain the DisplayUnitInfo bounding boxes). - * @return {boolean} Whether the calculation was successful. - * @private + * @return Whether the calculation was successful. */ - calculateVisualScale_() { + private calculateVisualScale_(): boolean { const displayAreaDiv = this.$.displayArea; if (!displayAreaDiv || !displayAreaDiv.offsetWidth || !this.displays || !this.displays.length) { @@ -211,21 +222,15 @@ return true; } - /** - * @param {string} id - * @param {!chrome.system.display.Bounds} displayBounds - * @param {number} visualScale - * @param {number=} opt_offset - * @return {string} The style string for the div. - * @private - */ - getDivStyle_(id, displayBounds, visualScale, opt_offset) { + private getDivStyle_( + id: string, _displayBounds: Bounds, _visualScale: number, + offset?: number): string { // This matches the size of the box-shadow or border in CSS. - /** @type {number} */ const BORDER = 1; - /** @type {number} */ const MARGIN = 4; - /** @type {number} */ const OFFSET = opt_offset || 0; - /** @type {number} */ const PADDING = 3; - const bounds = this.getCalculatedDisplayBounds(id, true /* notest */); + const BORDER = 1; + const MARGIN = 4; + const OFFSET = offset || 0; + const PADDING = 3; + const bounds = this.getCalculatedDisplayBounds(id, /* notest */ true); if (!bounds) { return ''; } @@ -241,17 +246,9 @@ ' left: ' + left + 'px; top: ' + top + 'px'; } - /** - * @param {number} mirroringDestinationIndex - * @param {number} mirroringDestinationDisplayNum - * @param {!Array<!chrome.system.display.DisplayUnitInfo>} displays - * @param {number} visualScale - * @return {string} The style string for the mirror div. - * @private - */ - getMirrorDivStyle_( - mirroringDestinationIndex, mirroringDestinationDisplayNum, displays, - visualScale) { + private getMirrorDivStyle_( + mirroringDestinationIndex: number, mirroringDestinationDisplayNum: number, + displays: DisplayUnitInfo[], visualScale: number): string { // All destination displays have the same bounds as the mirroring source // display, but we add a little offset to each destination display's bounds // so that they can be distinguished from each other in the layout. @@ -260,57 +257,29 @@ (mirroringDestinationDisplayNum - mirroringDestinationIndex) * -4); } - /** - * @param {!chrome.system.display.DisplayUnitInfo} display - * @param {!chrome.system.display.DisplayUnitInfo} selectedDisplay - * @return {boolean} - * @private - */ - isSelected_(display, selectedDisplay) { + private isSelected_( + display: DisplayUnitInfo, selectedDisplay: DisplayUnitInfo): boolean { return display.id === selectedDisplay.id; } - focusSelectedDisplay_() { - if (!this.selectedDisplay) { - return; - } - const children = Array.from(this.$.displayArea.children); - const selected = - children.find(display => display.id === '_' + this.selectedDisplay.id); - if (selected) { - selected.focus(); - } - } - - /** - * @param {!DisplaySelectEvent} e - * @private - */ - onSelectDisplayTap_(e) { - const selectDisplayEvent = new CustomEvent( - 'select-display', {composed: true, detail: e.model.item.id}); + private dispatchSelectDisplayEvent_(displayId: DisplayUnitInfo['id']) { + const selectDisplayEvent = + new CustomEvent('select-display', {composed: true, detail: displayId}); this.dispatchEvent(selectDisplayEvent); - // Force active in case the selected display was clicked. - // TODO(dpapad): Ask @stevenjb, why are we setting 'active' on a div? - e.target.active = true; } - /** - * @param {!DisplaySelectEvent} e - * @private - */ - onFocus_(e) { - const selectDisplayEvent = new CustomEvent( - 'select-display', {composed: true, detail: e.model.item.id}); - this.dispatchEvent(selectDisplayEvent); - this.focusSelectedDisplay_(); + private onSelectDisplayTap_(e: DisplaySelectEvent) { + this.dispatchSelectDisplayEvent_(e.model.item.id); + // Keep focused display in-sync with clicked display + e.target.focus(); } - /** - * @param {string} id - * @param {?DragPosition} amount - */ - onDrag_(id, amount) { + private onFocus_(e: DisplaySelectEvent) { + this.dispatchSelectDisplayEvent_(e.model.item.id); + e.target.focus(); + } + + private onDrag_(id: string, amount: DragPosition|null) { id = id.substr(1); // Skip prefix let newBounds; @@ -323,16 +292,12 @@ } else { this.browserProxy_.highlightDisplay(id); // Make sure the dragged display is also selected. - if (id !== this.selectedDisplay.id) { - const selectDisplayEvent = - new CustomEvent('select-display', {composed: true, detail: id}); - this.dispatchEvent(selectDisplayEvent); + if (id !== this.selectedDisplay!.id) { + this.dispatchSelectDisplayEvent_(id); } const calculatedBounds = this.getCalculatedDisplayBounds(id); - newBounds = - /** @type {chrome.system.display.Bounds} */ ( - Object.assign({}, calculatedBounds)); + newBounds = {...calculatedBounds}; newBounds.left += Math.round(amount.x / this.visualScale); newBounds.top += Math.round(amount.y / this.visualScale); @@ -367,10 +332,16 @@ this.visualOffset_.left + Math.round(newBounds.left * this.visualScale); const top = this.visualOffset_.top + Math.round(newBounds.top * this.visualScale); - const div = this.shadowRoot.querySelector('#_' + id); + const div = castExists(this.shadowRoot!.getElementById(`_${id}`)); div.style.left = '' + left + 'px'; div.style.top = '' + top + 'px'; - this.focusSelectedDisplay_(); + div.focus(); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'display-layout': DisplayLayoutElement; } }
diff --git a/chrome/browser/resources/settings/chromeos/device_page/display_overscan_dialog.js b/chrome/browser/resources/settings/chromeos/device_page/display_overscan_dialog.ts similarity index 76% rename from chrome/browser/resources/settings/chromeos/device_page/display_overscan_dialog.js rename to chrome/browser/resources/settings/chromeos/device_page/display_overscan_dialog.ts index 6d68a8e..6a4a4d58 100644 --- a/chrome/browser/resources/settings/chromeos/device_page/display_overscan_dialog.js +++ b/chrome/browser/resources/settings/chromeos/device_page/display_overscan_dialog.ts
@@ -16,18 +16,27 @@ import '../os_icons.js'; import '../../settings_shared.css.js'; -import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js'; +import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {getDisplayApi} from './device_page_browser_proxy.js'; +import {getTemplate} from './display_overscan_dialog.html.js'; -/** @polymer */ -class SettingsDisplayOverscanDialogElement extends PolymerElement { +type Insets = chrome.system.display.Insets; + +export interface SettingsDisplayOverscanDialogElement { + $: { + dialog: CrDialogElement, + }; +} + +export class SettingsDisplayOverscanDialogElement extends PolymerElement { static get is() { return 'settings-display-overscan-dialog'; } static get template() { - return html`{__html_template__}`; + return getTemplate(); } static get properties() { @@ -46,26 +55,27 @@ }; } + displayId: string; + private committed_: boolean; + private keyHandler_: (event: KeyboardEvent) => void; + constructor() { super(); /** * Keyboard event handler for overscan adjustments. - * @type {?function(!Event)} - * @private */ - this.keyHandler_ = null; + this.keyHandler_ = this.handleKeyEvent_.bind(this); } open() { - this.keyHandler_ = this.handleKeyEvent_.bind(this); // We need to attach the event listener to |window|, not |this| so that // changing focus does not prevent key events from occurring. window.addEventListener('keydown', this.keyHandler_); this.committed_ = false; this.$.dialog.showModal(); // Don't focus 'reset' by default. 'Tab' will focus 'OK'. - this.shadowRoot.querySelector('#reset').blur(); + this.shadowRoot!.getElementById('reset')!.blur(); } close() { @@ -78,8 +88,7 @@ } } - /** @private */ - displayIdChanged_(newValue, oldValue) { + private displayIdChanged_(newValue: string, oldValue: string) { if (oldValue && !this.committed_) { getDisplayApi().overscanCalibrationReset(oldValue); getDisplayApi().overscanCalibrationComplete(oldValue); @@ -91,23 +100,17 @@ getDisplayApi().overscanCalibrationStart(newValue); } - /** @private */ - onResetTap_() { + private onResetTap_() { getDisplayApi().overscanCalibrationReset(this.displayId); } - /** @private */ - onSaveTap_() { + private onSaveTap_() { getDisplayApi().overscanCalibrationComplete(this.displayId); this.committed_ = true; this.close(); } - /** - * @param {!Event} event - * @private - */ - handleKeyEvent_(event) { + private handleKeyEvent_(event: KeyboardEvent) { if (event.altKey || event.ctrlKey || event.metaKey) { return; } @@ -147,13 +150,8 @@ event.preventDefault(); } - /** - * @param {number} x - * @param {number} y - * @private - */ - move_(x, y) { - /** @type {!chrome.system.display.Insets} */ const delta = { + private move_(x: number, y: number) { + const delta: Insets = { left: x, top: y, right: x ? -x : 0, // negating 0 will produce a double. @@ -162,13 +160,8 @@ getDisplayApi().overscanCalibrationAdjust(this.displayId, delta); } - /** - * @param {number} x - * @param {number} y - * @private - */ - resize_(x, y) { - /** @type {!chrome.system.display.Insets} */ const delta = { + private resize_(x: number, y: number) { + const delta: Insets = { left: x, top: y, right: x, @@ -178,6 +171,12 @@ } } +declare global { + interface HTMLElementTagNameMap { + 'settings-display-overscan-dialog': SettingsDisplayOverscanDialogElement; + } +} + customElements.define( SettingsDisplayOverscanDialogElement.is, SettingsDisplayOverscanDialogElement);
diff --git a/chrome/browser/resources/settings/chromeos/ensure_lazy_loaded.js b/chrome/browser/resources/settings/chromeos/ensure_lazy_loaded.ts similarity index 70% rename from chrome/browser/resources/settings/chromeos/ensure_lazy_loaded.js rename to chrome/browser/resources/settings/chromeos/ensure_lazy_loaded.ts index 754b4aa6..2f05590 100644 --- a/chrome/browser/resources/settings/chromeos/ensure_lazy_loaded.js +++ b/chrome/browser/resources/settings/chromeos/ensure_lazy_loaded.ts
@@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -let lazyLoadPromise = null; +let lazyLoadPromise: Promise<CustomElementConstructor[]>|null = null; -/** @return {!Promise<void>} Resolves when the lazy load module is imported. */ -export function ensureLazyLoaded() { +/** @return Resolves when the lazy load module is imported. */ +export function ensureLazyLoaded(): Promise<CustomElementConstructor[]> { if (!lazyLoadPromise) { const script = document.createElement('script'); script.type = 'module'; @@ -22,7 +22,7 @@ ]; lazyLoadPromise = Promise.all( - lazyLoadPages.map(name => customElements.whenDefined(name))); + lazyLoadPages.map((name) => customElements.whenDefined(name))); } return lazyLoadPromise; }
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.gni b/chrome/browser/resources/settings/chromeos/os_settings.gni index dc1b84c..44f3289 100644 --- a/chrome/browser/resources/settings/chromeos/os_settings.gni +++ b/chrome/browser/resources/settings/chromeos/os_settings.gni
@@ -12,6 +12,9 @@ "chromeos/date_time_page/timezone_selector.ts", "chromeos/date_time_page/timezone_subpage.ts", "chromeos/device_page/device_page.ts", + "chromeos/device_page/display.ts", + "chromeos/device_page/display_layout.ts", + "chromeos/device_page/display_overscan_dialog.ts", "chromeos/device_page/pointers.ts", "chromeos/device_page/stylus.ts", "chromeos/google_assistant_page/google_assistant_page.ts", @@ -132,7 +135,7 @@ "chromeos/device_page/device_page_browser_proxy.js", "chromeos/device_page/drag_behavior.js", "chromeos/device_page/layout_behavior.js", - "chromeos/ensure_lazy_loaded.js", + "chromeos/ensure_lazy_loaded.ts", "chromeos/find_shortcut_behavior.js", "chromeos/global_scroll_target_behavior.js", "chromeos/google_assistant_page/google_assistant_browser_proxy.ts", @@ -254,9 +257,6 @@ "chromeos/crostini_page/crostini_port_forwarding_add_port_dialog.js", "chromeos/crostini_page/crostini_subpage.js", "chromeos/device_page/audio.js", - "chromeos/device_page/display.js", - "chromeos/device_page/display_layout.js", - "chromeos/device_page/display_overscan_dialog.js", "chromeos/device_page/keyboard.js", "chromeos/device_page/power.js", "chromeos/device_page/storage.js",
diff --git a/chrome/browser/safe_browsing/chrome_ui_manager_delegate.cc b/chrome/browser/safe_browsing/chrome_ui_manager_delegate.cc index 570b888c..9cc64ca 100644 --- a/chrome/browser/safe_browsing/chrome_ui_manager_delegate.cc +++ b/chrome/browser/safe_browsing/chrome_ui_manager_delegate.cc
@@ -67,7 +67,7 @@ return false; extensions::ExtensionHost* extension_host = - extension_manager->GetExtensionHostForRenderFrameHost( + extension_manager->GetBackgroundHostForRenderFrameHost( web_contents->GetPrimaryMainFrame()); return extension_host != nullptr; #else
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc index 3dde5ee..42cad625 100644 --- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc +++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
@@ -316,6 +316,17 @@ ack_.set_final_action(final_action); } +BinaryUploadService::CancelRequests::CancelRequests( + enterprise_connectors::CloudOrLocalAnalysisSettings settings) + : cloud_or_local_settings_(std::move(settings)) {} + +BinaryUploadService::CancelRequests::~CancelRequests() = default; + +void BinaryUploadService::CancelRequests::set_user_action_id( + const std::string& user_action_id) { + user_action_id_ = user_action_id; +} + // static BinaryUploadService* BinaryUploadService::GetForProfile( Profile* profile,
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h index bcc01205..90927a0 100644 --- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h +++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
@@ -242,6 +242,36 @@ cloud_or_local_settings_; }; + // A class to encapsulate requests to cancel. Any request that match the + // given criteria is canceled. This is best effort only, in some cases + // requests may have already started and can no longer be canceled. + class CancelRequests { + public: + explicit CancelRequests( + enterprise_connectors::CloudOrLocalAnalysisSettings settings); + virtual ~CancelRequests(); + CancelRequests(const CancelRequests&) = delete; + CancelRequests& operator=(const CancelRequests&) = delete; + CancelRequests(CancelRequests&&) = delete; + CancelRequests& operator=(CancelRequests&&) = delete; + + void set_user_action_id(const std::string& user_action_id); + const std::string& get_user_action_id() const { return user_action_id_; } + + const enterprise_connectors::CloudOrLocalAnalysisSettings& + cloud_or_local_settings() const { + return cloud_or_local_settings_; + } + + private: + std::string user_action_id_; + + // Settings used to determine how the request is used in the cloud or + // locally. + enterprise_connectors::CloudOrLocalAnalysisSettings + cloud_or_local_settings_; + }; + static BinaryUploadService* GetForProfile( Profile* profile, const enterprise_connectors::AnalysisSettings& settings); @@ -252,6 +282,11 @@ // Send an acknowledgement for the request with the given token. virtual void MaybeAcknowledge(std::unique_ptr<Ack> ack) = 0; + + // Cancel any requests that match the given criteria . This is a best effort + // approach only, since it is possible that requests have been started in a + // way that they are no longer cancelable. + virtual void MaybeCancelRequests(std::unique_ptr<CancelRequests> cancel) = 0; }; } // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.cc b/chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.cc index b41ffe37..5dff763 100644 --- a/chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.cc +++ b/chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.cc
@@ -221,6 +221,12 @@ // Nothing to do for cloud upload service. } +void CloudBinaryUploadService::MaybeCancelRequests( + std::unique_ptr<CancelRequests> cancel) { + // Nothing to do for cloud upload service. + // TODO(1374944): Might consider canceling requests in `request_queue_`. +} + void CloudBinaryUploadService::MaybeUploadForDeepScanningCallback( std::unique_ptr<CloudBinaryUploadService::Request> request, bool authorized) {
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.h b/chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.h index ead906e..bf49f044 100644 --- a/chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.h +++ b/chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.h
@@ -38,6 +38,7 @@ // authorized to upload data, otherwise queue the request. void MaybeUploadForDeepScanning(std::unique_ptr<Request> request) override; void MaybeAcknowledge(std::unique_ptr<Ack> ack) override; + void MaybeCancelRequests(std::unique_ptr<CancelRequests> cancel) override; // Indicates whether the DM token/Connector combination is allowed to upload // data.
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/test_binary_upload_service.h b/chrome/browser/safe_browsing/cloud_content_scanning/test_binary_upload_service.h index 7342ef7..57377a7 100644 --- a/chrome/browser/safe_browsing/cloud_content_scanning/test_binary_upload_service.h +++ b/chrome/browser/safe_browsing/cloud_content_scanning/test_binary_upload_service.h
@@ -20,6 +20,7 @@ void MaybeUploadForDeepScanning(std::unique_ptr<Request> request) override; void MaybeAcknowledge(std::unique_ptr<Ack> ack) override {} + void MaybeCancelRequests(std::unique_ptr<CancelRequests> cancel) override {} void SetResponse(Result result, enterprise_connectors::ContentAnalysisResponse response);
diff --git a/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc b/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc index 3f884737..f14ff7ab 100644 --- a/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc +++ b/chrome/browser/safe_browsing/download_protection/deep_scanning_request_unittest.cc
@@ -144,6 +144,8 @@ ASSERT_TRUE(base::Contains(requests_tokens_, ack->ack().request_token())); } + void MaybeCancelRequests(std::unique_ptr<CancelRequests> cancel) override {} + void SetResponse(const base::FilePath& path, BinaryUploadService::Result result, enterprise_connectors::ContentAnalysisResponse response) {
diff --git a/chrome/browser/safe_browsing/download_protection/download_request_maker.cc b/chrome/browser/safe_browsing/download_protection/download_request_maker.cc index eaabbe4..565c2a6 100644 --- a/chrome/browser/safe_browsing/download_protection/download_request_maker.cc +++ b/chrome/browser/safe_browsing/download_protection/download_request_maker.cc
@@ -30,6 +30,11 @@ namespace { +// The version of this client supporting tailored warnings. +// Please update the description of TailoredInfo field in csd.proto when +// changing this value. +constexpr int kTailoredWarningVersion = 1; + DownloadRequestMaker::TabUrls TabUrlsFromWebContents( content::WebContents* web_contents) { DownloadRequestMaker::TabUrls result; @@ -162,6 +167,8 @@ request_->set_locale(g_browser_process->GetApplicationLocale()); request_->set_file_basename(target_file_path_.BaseName().AsUTF8Unsafe()); + PopulateTailoredInfo(); + file_analyzer_->Start( target_file_path_, full_path_, base::BindOnce(&DownloadRequestMaker::OnFileFeatureExtractionDone, @@ -256,4 +263,10 @@ std::move(callback_).Run(std::move(request_)); } +void DownloadRequestMaker::PopulateTailoredInfo() { + ClientDownloadRequest::TailoredInfo tailored_info; + tailored_info.set_version(kTailoredWarningVersion); + *request_->mutable_tailored_info() = tailored_info; +} + } // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/download_protection/download_request_maker.h b/chrome/browser/safe_browsing/download_protection/download_request_maker.h index 09c6941d..8cde2125 100644 --- a/chrome/browser/safe_browsing/download_protection/download_request_maker.h +++ b/chrome/browser/safe_browsing/download_protection/download_request_maker.h
@@ -74,6 +74,9 @@ // Callback when the history service has retrieved the tab redirects. void OnGotTabRedirects(history::RedirectList redirect_list); + // Populates the tailored info field for tailored warnings. + void PopulateTailoredInfo(); + raw_ptr<content::BrowserContext> browser_context_; std::unique_ptr<ClientDownloadRequest> request_; const scoped_refptr<BinaryFeatureExtractor> binary_feature_extractor_;
diff --git a/chrome/browser/safe_browsing/download_protection/download_request_maker_unittest.cc b/chrome/browser/safe_browsing/download_protection/download_request_maker_unittest.cc index e88eb48..c69bade 100644 --- a/chrome/browser/safe_browsing/download_protection/download_request_maker_unittest.cc +++ b/chrome/browser/safe_browsing/download_protection/download_request_maker_unittest.cc
@@ -351,6 +351,41 @@ ChromeUserPopulation::ENHANCED_PROTECTION); } +TEST_F(DownloadRequestMakerTest, PopulateTailoredInfo) { + base::RunLoop run_loop; + base::FilePath tmp_path(FILE_PATH_LITERAL("temp_path")); + + DownloadRequestMaker request_maker( + mock_feature_extractor_, &profile_, DownloadRequestMaker::TabUrls(), + /*target_file_path=*/base::FilePath(), tmp_path, + /*source_url=*/GURL(), + /*sha256_hash=*/"", + /*length=*/0, + /*resources=*/std::vector<ClientDownloadRequest::Resource>(), + /*is_user_initiated=*/true, + /*referrer_chain_data=*/nullptr); + + EXPECT_CALL(*mock_feature_extractor_, CheckSignature(tmp_path, _)) + .WillOnce(Return()); + EXPECT_CALL(*mock_feature_extractor_, ExtractImageFeatures(tmp_path, _, _, _)) + .WillRepeatedly(Return(true)); + + std::unique_ptr<ClientDownloadRequest> request; + request_maker.Start(base::BindOnce( + [](base::RunLoop* run_loop, + std::unique_ptr<ClientDownloadRequest>* request_target, + std::unique_ptr<ClientDownloadRequest> request) { + run_loop->Quit(); + *request_target = std::move(request); + }, + &run_loop, &request)); + + run_loop.Run(); + + ASSERT_NE(request, nullptr); + EXPECT_EQ(request->tailored_info().version(), 1); +} + TEST_F(DownloadRequestMakerTest, PopulatesFileBasename) { base::RunLoop run_loop; base::FilePath tmp_path(FILE_PATH_LITERAL("temp_path"));
diff --git a/chrome/browser/ui/ash/system_tray_client_impl_browsertest.cc b/chrome/browser/ui/ash/system_tray_client_impl_browsertest.cc index 4c0dd3c..0937216 100644 --- a/chrome/browser/ui/ash/system_tray_client_impl_browsertest.cc +++ b/chrome/browser/ui/ash/system_tray_client_impl_browsertest.cc
@@ -14,6 +14,7 @@ #include "ash/system/model/system_tray_model.h" #include "base/i18n/time_formatting.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" #include "chrome/browser/apps/app_service/app_service_proxy.h" #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" #include "chrome/browser/ash/login/lock/screen_locker_tester.h" @@ -52,6 +53,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test.h" #include "content/public/test/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" #include "ui/base/l10n/l10n_util.h" #include "ui/chromeos/devicetype_utils.h" #include "url/gurl.h" @@ -59,7 +61,7 @@ using ::ash::ProfileHelper; using user_manager::UserManager; -using SystemTrayClientEnterpriseTest = policy::DevicePolicyCrosBrowserTest; +namespace { const char kManager[] = "admin@example.com"; const char16_t kManager16[] = u"admin@example.com"; @@ -68,7 +70,25 @@ const char kManagedUser[] = "user@example.com"; const char kManagedGaiaID[] = "33333"; -IN_PROC_BROWSER_TEST_F(SystemTrayClientEnterpriseTest, TrayEnterprise) { +} // namespace + +// Parameterized by feature QsRevamp. +class SystemTrayClientEnterpriseTest + : public policy::DevicePolicyCrosBrowserTest, + public testing::WithParamInterface<bool> { + public: + SystemTrayClientEnterpriseTest() { + feature_list_.InitWithFeatureState(ash::features::kQsRevamp, GetParam()); + } + + base::test::ScopedFeatureList feature_list_; +}; + +INSTANTIATE_TEST_SUITE_P(QsRevamp, + SystemTrayClientEnterpriseTest, + testing::Bool()); + +IN_PROC_BROWSER_TEST_P(SystemTrayClientEnterpriseTest, TrayEnterprise) { auto test_api = ash::SystemTrayTestApi::Create(); // Managed devices show an item in the menu. @@ -304,19 +324,19 @@ EXPECT_TRUE(tray_test_api->Is24HourClock()); } -class SystemTrayClientEnterpriseAccountTest : public ash::LoginManagerTest { +// Parameterized by feature QsRevamp. +class SystemTrayClientEnterpriseAccountTest + : public ash::LoginManagerTest, + public testing::WithParamInterface<bool> { protected: - SystemTrayClientEnterpriseAccountTest() : LoginManagerTest() { + SystemTrayClientEnterpriseAccountTest() { + feature_list_.InitWithFeatureState(ash::features::kQsRevamp, GetParam()); std::unique_ptr<ash::ScopedUserPolicyUpdate> scoped_user_policy_update = user_policy_mixin_.RequestPolicyUpdate(); scoped_user_policy_update->policy_data()->set_managed_by(kManager); } - SystemTrayClientEnterpriseAccountTest( - const SystemTrayClientEnterpriseAccountTest&) = delete; - SystemTrayClientEnterpriseAccountTest& operator=( - const SystemTrayClientEnterpriseAccountTest&) = delete; - ~SystemTrayClientEnterpriseAccountTest() override = default; + base::test::ScopedFeatureList feature_list_; const ash::LoginManagerMixin::TestUserInfo unmanaged_user_{ AccountId::FromUserEmailGaiaId(kNewUser, kNewGaiaID)}; const ash::LoginManagerMixin::TestUserInfo managed_user_{ @@ -327,7 +347,11 @@ {managed_user_, unmanaged_user_}}; }; -IN_PROC_BROWSER_TEST_F(SystemTrayClientEnterpriseAccountTest, +INSTANTIATE_TEST_SUITE_P(QsRevamp, + SystemTrayClientEnterpriseAccountTest, + testing::Bool()); + +IN_PROC_BROWSER_TEST_P(SystemTrayClientEnterpriseAccountTest, TrayEnterpriseManagedAccount) { auto test_api = ash::SystemTrayTestApi::Create(); @@ -361,7 +385,7 @@ test_api->GetBubbleViewTooltip(ash::VIEW_ID_QS_MANAGED_BUTTON)); } -IN_PROC_BROWSER_TEST_F(SystemTrayClientEnterpriseAccountTest, +IN_PROC_BROWSER_TEST_P(SystemTrayClientEnterpriseAccountTest, TrayEnterpriseUnmanagedAccount) { auto test_api = ash::SystemTrayTestApi::Create(); @@ -381,19 +405,18 @@ SystemTrayClientEnterpriseSessionRestoreTest() { login_mixin_.set_session_restore_enabled(); } - SystemTrayClientEnterpriseSessionRestoreTest( - const SystemTrayClientEnterpriseSessionRestoreTest&) = delete; - SystemTrayClientEnterpriseSessionRestoreTest& operator=( - const SystemTrayClientEnterpriseSessionRestoreTest&) = delete; - ~SystemTrayClientEnterpriseSessionRestoreTest() override = default; }; -IN_PROC_BROWSER_TEST_F(SystemTrayClientEnterpriseSessionRestoreTest, +INSTANTIATE_TEST_SUITE_P(QsRevamp, + SystemTrayClientEnterpriseSessionRestoreTest, + testing::Bool()); + +IN_PROC_BROWSER_TEST_P(SystemTrayClientEnterpriseSessionRestoreTest, PRE_SessionRestore) { LoginUser(managed_user_.account_id); } -IN_PROC_BROWSER_TEST_F(SystemTrayClientEnterpriseSessionRestoreTest, +IN_PROC_BROWSER_TEST_P(SystemTrayClientEnterpriseSessionRestoreTest, SessionRestore) { auto test_api = ash::SystemTrayTestApi::Create();
diff --git a/chrome/browser/ui/extensions/extension_action_view_controller.cc b/chrome/browser/ui/extensions/extension_action_view_controller.cc index 4f01922..7618f97 100644 --- a/chrome/browser/ui/extensions/extension_action_view_controller.cc +++ b/chrome/browser/ui/extensions/extension_action_view_controller.cc
@@ -428,6 +428,24 @@ } } +ToolbarActionViewController::HoverCardPolicyState +ExtensionActionViewController::GetHoverCardPolicyState() const { + // An extension pinned by admin is also installed by admin. Thus, + // "pinned by admin" has preference. + auto* const model = ToolbarActionsModel::Get(browser_->profile()); + if (model->IsActionForcePinned(GetId())) + return HoverCardPolicyState::kPinnedByAdmin; + + scoped_refptr<const extensions::Extension> extension = + extensions::ExtensionRegistry::Get(browser_->profile()) + ->enabled_extensions() + .GetByID(GetId()); + if (extensions::Manifest::IsPolicyLocation(extension->location())) + return HoverCardPolicyState::kInstalledByAdmin; + + return HoverCardPolicyState::kNone; +} + bool ExtensionActionViewController::CanHandleAccelerators() const { if (!ExtensionIsValid()) return false;
diff --git a/chrome/browser/ui/extensions/extension_action_view_controller.h b/chrome/browser/ui/extensions/extension_action_view_controller.h index 8bb5494..10ba8365 100644 --- a/chrome/browser/ui/extensions/extension_action_view_controller.h +++ b/chrome/browser/ui/extensions/extension_action_view_controller.h
@@ -72,6 +72,8 @@ std::u16string GetTooltip(content::WebContents* web_contents) const override; ToolbarActionViewController::HoverCardState GetHoverCardState( content::WebContents* web_contents) const override; + ToolbarActionViewController::HoverCardPolicyState GetHoverCardPolicyState() + const override; extensions::SitePermissionsHelper::SiteInteraction GetSiteInteraction( content::WebContents* web_contents) const override; bool IsEnabled(content::WebContents* web_contents) const override;
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc index 09144bcb..1a3e5076 100644 --- a/chrome/browser/ui/tabs/tab_strip_model.cc +++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -1038,7 +1038,7 @@ // Ensure that the indices are nonempty, sorted, and unique. DCHECK_GT(indices.size(), 0u); DCHECK(base::ranges::is_sorted(indices)); - DCHECK(base::ranges::adjacent_find(indices) == indices.end()); + DCHECK(std::adjacent_find(indices.begin(), indices.end()) == indices.end()); // The odds of |new_group| colliding with an existing group are astronomically // low. If there is a collision, a DCHECK will fail in |AddToNewGroupImpl()|, @@ -1058,7 +1058,7 @@ // Ensure that the indices are sorted and unique. DCHECK(base::ranges::is_sorted(indices)); - DCHECK(base::ranges::adjacent_find(indices) == indices.end()); + DCHECK(std::adjacent_find(indices.begin(), indices.end()) == indices.end()); CHECK(ContainsIndex(*(indices.begin()))); CHECK(ContainsIndex(*(indices.rbegin())));
diff --git a/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.cc b/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.cc index 70b6721..856bd6f7 100644 --- a/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.cc +++ b/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.cc
@@ -6,11 +6,9 @@ #include <string> -#include "base/strings/utf_string_conversions.h" #include "chrome/browser/extensions/site_permissions_helper.h" #include "chrome/browser/ui/toolbar/toolbar_action_view_delegate.h" #include "ui/gfx/image/image.h" -#include "ui/gfx/image/image_skia.h" TestToolbarActionViewController::TestToolbarActionViewController( const std::string& id) @@ -58,6 +56,11 @@ kExtensionDoesNotWantAccess; } +ToolbarActionViewController::HoverCardPolicyState +TestToolbarActionViewController::GetHoverCardPolicyState() const { + return ToolbarActionViewController::HoverCardPolicyState::kNone; +} + bool TestToolbarActionViewController::IsEnabled( content::WebContents* web_contents) const { return is_enabled_;
diff --git a/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.h b/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.h index 75e0d3c571..d297bca 100644 --- a/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.h +++ b/chrome/browser/ui/toolbar/test_toolbar_action_view_controller.h
@@ -33,6 +33,8 @@ std::u16string GetTooltip(content::WebContents* web_contents) const override; ToolbarActionViewController::HoverCardState GetHoverCardState( content::WebContents* web_contents) const override; + ToolbarActionViewController::HoverCardPolicyState GetHoverCardPolicyState() + const override; bool IsEnabled(content::WebContents* web_contents) const override; bool IsShowingPopup() const override; bool IsRequestingSiteAccess(
diff --git a/chrome/browser/ui/toolbar/toolbar_action_view_controller.h b/chrome/browser/ui/toolbar/toolbar_action_view_controller.h index 598ea8cf..607082a 100644 --- a/chrome/browser/ui/toolbar/toolbar_action_view_controller.h +++ b/chrome/browser/ui/toolbar/toolbar_action_view_controller.h
@@ -62,7 +62,9 @@ kMaxValue = kRequestAccessButton, }; - // Hover card states for a toolbar action view. + // Site access state for the toolbar action view's hover card. + // TODO(emiliapaz): Change the enum name to reflect "site acesss" state, or + // bundle both enums in one struct. enum class HoverCardState { // All extensions are allowed on the current site by the user. kAllExtensionsAllowed, @@ -80,6 +82,17 @@ kExtensionDoesNotWantAccess, }; + // Policy state for the toolbar action view's hover card. + enum class HoverCardPolicyState { + kNone, + + // Extension is force pinned by administrator. + kPinnedByAdmin, + + // Extension if force installed by administrator. + kInstalledByAdmin + }; + virtual ~ToolbarActionViewController() = default; // Returns the unique ID of this particular action. For extensions, this is @@ -111,6 +124,8 @@ virtual HoverCardState GetHoverCardState( content::WebContents* web_contents) const = 0; + virtual HoverCardPolicyState GetHoverCardPolicyState() const = 0; + // Returns true if the action should be enabled on the given |web_contents|. virtual bool IsEnabled(content::WebContents* web_contents) const = 0;
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc index b6f9d4d..fd78b66 100644 --- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc +++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
@@ -129,8 +129,7 @@ : nullptr), display_mode_(display_mode), action_hover_card_controller_( - std::make_unique<ToolbarActionHoverCardController>(browser->profile(), - this)) { + std::make_unique<ToolbarActionHoverCardController>(this)) { // The container shouldn't show unless / until we have extensions available. SetVisible(false);
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_interactive_uitest.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_interactive_uitest.cc index de52759..eae19cf 100644 --- a/chrome/browser/ui/views/extensions/extensions_toolbar_interactive_uitest.cc +++ b/chrome/browser/ui/views/extensions/extensions_toolbar_interactive_uitest.cc
@@ -16,9 +16,9 @@ #include "chrome/browser/ui/views/toolbar/toolbar_view.h" #include "chrome/common/chrome_paths.h" #include "extensions/browser/extension_system.h" +#include "extensions/common/extension_builder.h" #include "extensions/test/test_extension_dir.h" #include "net/dns/mock_host_resolver.h" -#include "ui/events/base_event_utils.h" #include "ui/views/layout/animating_layout_manager_test_util.h" #include "ui/views/view_utils.h" @@ -45,7 +45,18 @@ // Allow it to finish laying out appropriately. auto* container = GetExtensionsToolbarContainer(); container->GetWidget()->LayoutRootViewIfNecessary(); + return extension; +} +scoped_refptr<const extensions::Extension> +ExtensionsToolbarUITest::ForceInstallExtension(const std::string& name) { + scoped_refptr<const extensions::Extension> extension = + extensions::ExtensionBuilder("extension") + .SetLocation(extensions::mojom::ManifestLocation::kExternalPolicy) + .Build(); + extensions::ExtensionSystem::Get(browser()->profile()) + ->extension_service() + ->AddExtension(extension.get()); return extension; }
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_interactive_uitest.h b/chrome/browser/ui/views/extensions/extensions_toolbar_interactive_uitest.h index fba08495..271c6c2 100644 --- a/chrome/browser/ui/views/extensions/extensions_toolbar_interactive_uitest.h +++ b/chrome/browser/ui/views/extensions/extensions_toolbar_interactive_uitest.h
@@ -55,6 +55,9 @@ const std::string& path, bool allow_incognito = false); + scoped_refptr<const extensions::Extension> ForceInstallExtension( + const std::string& name); + // Loads and returns a extension given a `name`. scoped_refptr<const extensions::Extension> InstallExtension( const std::string& name);
diff --git a/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view_browsertest.cc b/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view_browsertest.cc index 621c7a1..31e2d57e 100644 --- a/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view_browsertest.cc +++ b/chrome/browser/ui/views/performance_controls/high_efficiency_chip_view_browsertest.cc
@@ -65,7 +65,7 @@ return promo_controller; } - PageActionIconView* GetHighEfficiencyChipView() { + PageActionIconView* GetPageActionIconView() { BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser()); return browser_view->GetLocationBarView() @@ -73,10 +73,9 @@ ->GetIconView(PageActionIconType::kHighEfficiency); } - void ClickHighEfficiencyChip() { + void PressButton(views::Button* button) { views::test::InteractionTestUtilSimulatorViews::PressButton( - GetHighEfficiencyChipView(), - ui::test::InteractionTestUtil::InputType::kMouse); + button, ui::test::InteractionTestUtil::InputType::kMouse); } void SetTabDiscardState(bool is_discarded) { @@ -101,31 +100,8 @@ waiter.WaitIfNeededAndGet(); } - user_education::HelpBubbleView* GetHelpBubbleView() { - return GetFeaturePromoController() - ->promo_bubble_for_testing() - ->AsA<user_education::HelpBubbleViews>() - ->bubble_view(); - } - - void ClickIPHCancelButton() { - views::test::WidgetDestroyedWaiter waiter(GetHelpBubbleView()->GetWidget()); - views::test::InteractionTestUtilSimulatorViews::PressButton( - GetHelpBubbleView()->GetDefaultButtonForTesting(), - ui::test::InteractionTestUtil::InputType::kMouse); - waiter.Wait(); - } - - void ClickIPHSettingsButton() { - views::test::WidgetDestroyedWaiter waiter(GetHelpBubbleView()->GetWidget()); - views::test::InteractionTestUtilSimulatorViews::PressButton( - GetHelpBubbleView()->GetNonDefaultButtonForTesting(0), - ui::test::InteractionTestUtil::InputType::kMouse); - waiter.Wait(); - } - views::InkDropState GetInkDropState() { - return views::InkDrop::Get(GetHighEfficiencyChipView()) + return views::InkDrop::Get(GetPageActionIconView()) ->GetInkDrop() ->GetTargetInkDropState(); } @@ -135,13 +111,17 @@ }; IN_PROC_BROWSER_TEST_F(HighEfficiencyChipViewBrowserTest, - NavigatesOnIPHSettingsLinkClicked) { + PromoCustomActionClicked) { auto lock = BrowserFeaturePromoController::BlockActiveWindowCheckForTesting(); + auto* const promo_controller = GetFeaturePromoController(); EXPECT_FALSE(GetFeaturePromoController()->IsPromoActive( feature_engagement::kIPHHighEfficiencyInfoModeFeature)); SetTabDiscardState(true); + PageActionIconView* icon = GetPageActionIconView(); + EXPECT_TRUE(icon->GetVisible()); + WaitForIPHToShow(); EXPECT_TRUE(GetFeaturePromoController()->IsPromoActive( @@ -149,7 +129,11 @@ content::TestNavigationObserver navigation_observer( browser()->tab_strip_model()->GetWebContentsAt(0)); - ClickIPHSettingsButton(); + auto* promo_bubble = promo_controller->promo_bubble_for_testing() + ->AsA<user_education::HelpBubbleViews>() + ->bubble_view(); + auto* custom_action_button = promo_bubble->GetNonDefaultButtonForTesting(0); + PressButton(custom_action_button); navigation_observer.Wait(); GURL expected(chrome::kChromeUIPerformanceSettingsURL); @@ -157,40 +141,56 @@ } IN_PROC_BROWSER_TEST_F(HighEfficiencyChipViewBrowserTest, - PromoDismissesOnCancelClick) { + PromoDismissesOnChipClick) { auto lock = BrowserFeaturePromoController::BlockActiveWindowCheckForTesting(); SetTabDiscardState(true); + PageActionIconView* icon = GetPageActionIconView(); WaitForIPHToShow(); EXPECT_TRUE(GetFeaturePromoController()->IsPromoActive( feature_engagement::kIPHHighEfficiencyInfoModeFeature)); - ClickHighEfficiencyChip(); + PressButton(icon); // Expect the bubble to be open and the promo to be closed. EXPECT_FALSE(GetFeaturePromoController()->IsPromoActive( feature_engagement::kIPHHighEfficiencyInfoModeFeature)); - EXPECT_NE(GetHighEfficiencyChipView()->GetBubble(), nullptr); + EXPECT_NE(icon->GetBubble(), nullptr); } IN_PROC_BROWSER_TEST_F(HighEfficiencyChipViewBrowserTest, ShowAndHideInkDropWithPromo) { auto lock = BrowserFeaturePromoController::BlockActiveWindowCheckForTesting(); + auto* const promo_controller = GetFeaturePromoController(); EXPECT_FALSE(GetFeaturePromoController()->IsPromoActive( feature_engagement::kIPHHighEfficiencyInfoModeFeature)); SetTabDiscardState(true); + PageActionIconView* icon = GetPageActionIconView(); + EXPECT_TRUE(icon->GetVisible()); + WaitForIPHToShow(); EXPECT_TRUE(GetFeaturePromoController()->IsPromoActive( feature_engagement::kIPHHighEfficiencyInfoModeFeature)); + EXPECT_EQ(GetInkDropState(), views::InkDropState::ACTIVATED); - ClickIPHCancelButton(); + auto* promo_bubble = promo_controller->promo_bubble_for_testing() + ->AsA<user_education::HelpBubbleViews>() + ->bubble_view(); - EXPECT_FALSE(GetFeaturePromoController()->IsPromoActive( + views::test::WidgetDestroyedWaiter waiter(promo_bubble->GetWidget()); + auto* default_action_button = promo_bubble->GetDefaultButtonForTesting(); + PressButton(default_action_button); + waiter.Wait(); + + EXPECT_FALSE(browser()->window()->IsFeaturePromoActive( feature_engagement::kIPHHighEfficiencyInfoModeFeature)); - EXPECT_TRUE(GetInkDropState() == views::InkDropState::DEACTIVATED); + + views::InkDropState current_state = GetInkDropState(); + EXPECT_TRUE(current_state == views::InkDropState::HIDDEN || + current_state == views::InkDropState::DEACTIVATED); }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.cc b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.cc index 0c1d438..9977d686 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.cc
@@ -108,6 +108,25 @@ return l10n_util::GetStringFUTF16(title_id, host); } +std::u16string GetFootnotePolicyText( + ToolbarActionViewController::HoverCardPolicyState state) { + int text_id = -1; + switch (state) { + case ToolbarActionViewController::HoverCardPolicyState::kPinnedByAdmin: + text_id = + IDS_EXTENSIONS_TOOLBAR_ACTION_HOVER_CARD_FOOTER_POLICY_LABEL_PINNED_TEXT; + break; + case ToolbarActionViewController::HoverCardPolicyState::kInstalledByAdmin: + text_id = + IDS_EXTENSIONS_TOOLBAR_ACTION_HOVER_CARD_FOOTER_POLICY_LABEL_INSTALLED_TEXT; + break; + case ToolbarActionViewController::HoverCardPolicyState::kNone: + NOTREACHED(); + break; + } + return l10n_util::GetStringUTF16(text_id); +} + // Label that renders its background in a solid color. Placed in front of a // normal label either by being later in the draw order or on a layer, it can // be used to animate a fade-out. @@ -244,8 +263,6 @@ policy_label_ = AddChildView( std::make_unique<FadeLabel>(views::style::CONTEXT_DIALOG_BODY_TEXT)); - policy_label_->SetText(l10n_util::GetStringUTF16( - IDS_EXTENSIONS_TOOLBAR_ACTION_HOVER_CARD_FOOTER_POLICY_LABEL_TEXT)); // Separator doesn't need margin to span the dialog width. auto style_label = [](FadeLabel* label) { @@ -272,12 +289,16 @@ color_provider->GetColor(ui::kColorBubbleFooterBorder))); } - void UpdateContent(ToolbarActionViewController::HoverCardState state, - bool show_policy_label, - std::u16string host) { - bool show_site_access_labels = state != + void UpdateContent( + ToolbarActionViewController::HoverCardState site_access_state, + ToolbarActionViewController::HoverCardPolicyState policy_state, + std::u16string host) { + bool show_site_access_labels = site_access_state != ToolbarActionViewController::HoverCardState:: kExtensionDoesNotWantAccess; + bool show_policy_label = + policy_state != + ToolbarActionViewController::HoverCardPolicyState::kNone; bool footer_visible = show_site_access_labels || show_policy_label; SetVisible(footer_visible); @@ -290,9 +311,13 @@ separator_->SetVisible(show_site_access_labels && show_policy_label); if (show_site_access_labels) { - title_label_->SetText(GetFootnoteTitle(state)); - description_label_->SetText(GetFootnoteDescription(state, host)); + title_label_->SetText(GetFootnoteTitle(site_access_state)); + description_label_->SetText( + GetFootnoteDescription(site_access_state, host)); } + + if (show_policy_label) + policy_label_->SetText(GetFootnotePolicyText(policy_state)); } void SetFade(double percent) { @@ -317,12 +342,10 @@ // ---------------------------------------------------------- ToolbarActionHoverCardBubbleView::ToolbarActionHoverCardBubbleView( - ToolbarActionView* action_view, - Profile* profile) + ToolbarActionView* action_view) : BubbleDialogDelegateView(action_view, views::BubbleBorder::TOP_LEFT, - views::BubbleBorder::STANDARD_SHADOW), - model_(ToolbarActionsModel::Get(profile)) { + views::BubbleBorder::STANDARD_SHADOW) { DCHECK(base::FeatureList::IsEnabled( extensions_features::kExtensionsMenuAccessControl)); @@ -401,7 +424,7 @@ title_label_->SetText(action_controller->GetActionName()); footnote_view_->UpdateContent( action_controller->GetHoverCardState(web_contents), - model_->IsActionForcePinned(action_controller->GetId()), + action_controller->GetHoverCardPolicyState(), GetCurrentHost(web_contents)); }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.h b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.h index 320e4db..82c3ff3c 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.h +++ b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.h
@@ -18,8 +18,7 @@ : public views::BubbleDialogDelegateView { public: METADATA_HEADER(ToolbarActionHoverCardBubbleView); - explicit ToolbarActionHoverCardBubbleView(ToolbarActionView* action_view, - Profile* profile); + explicit ToolbarActionHoverCardBubbleView(ToolbarActionView* action_view); ToolbarActionHoverCardBubbleView(const ToolbarActionHoverCardBubbleView&) = delete; ToolbarActionHoverCardBubbleView& operator=( @@ -52,9 +51,6 @@ // views::BubbleDialogDelegateView: void OnThemeChanged() override; - // The associated ToolbarActionsModel. Not owned. - raw_ptr<ToolbarActionsModel> model_; - raw_ptr<FadeLabel> title_label_ = nullptr; raw_ptr<FootnoteView> footnote_view_ = nullptr;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view_interactive_uitest.cc index 4c38814..b03688832 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view_interactive_uitest.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view_interactive_uitest.cc
@@ -210,20 +210,19 @@ WidgetUpdatedWhenHoveringBetweenActionViews) { ASSERT_TRUE(embedded_test_server()->Start()); - // Add two extensions with no host permissions, and two with them. - auto simple_extension_A = InstallExtension("Simple extension A"); - auto simple_extension_B = InstallExtension("Simple extension B"); - auto extension_with_permissions_A = InstallExtensionWithHostPermissions( - "Extension with host permissions A", "<all_urls>"); - auto extension_with_permissions_B = InstallExtensionWithHostPermissions( - "Extension with host permissions B", "<all_urls>"); + // Install four extensions with different policy and site access permissions + // to test all the possible footnote combinations. + auto simple_extension = InstallExtension("Extension A"); + auto force_installed_extension = ForceInstallExtension("Extension B"); + auto extension_with_host_permissions = + InstallExtensionWithHostPermissions("Extension C", "<all_urls>"); + auto force_pinned_extension_with_host_permissions = + InstallExtensionWithHostPermissions("Extension D", "<all_urls>"); - // Pin extensions "A" and force pin extensions "B" in order to test all - // possible footer combinations. - PinExtension(simple_extension_A->id()); - ForcePinExtension(simple_extension_B->id()); - PinExtension(extension_with_permissions_A->id()); - ForcePinExtension(extension_with_permissions_B->id()); + PinExtension(simple_extension->id()); + PinExtension(force_installed_extension->id()); + PinExtension(extension_with_host_permissions->id()); + ForcePinExtension(force_pinned_extension_with_host_permissions->id()); auto action_views = GetVisibleToolbarActionViews(); ASSERT_EQ(action_views.size(), 4u); @@ -235,31 +234,32 @@ // Hover over the simple extension pinned by the user. // Verify card anchors to its action, and it contains the extension's name and // no footnote. - ToolbarActionView* simple_action_A = - GetExtensionsToolbarContainer()->GetViewForId(simple_extension_A->id()); - HoverMouseOverActionView(simple_action_A); + ToolbarActionView* simple_action = + GetExtensionsToolbarContainer()->GetViewForId(simple_extension->id()); + HoverMouseOverActionView(simple_action); views::Widget* const widget = hover_card()->GetWidget(); views::test::WidgetVisibleWaiter(widget).Wait(); ASSERT_TRUE(widget); EXPECT_TRUE(widget->IsVisible()); - EXPECT_EQ(hover_card()->GetAnchorView(), simple_action_A); + EXPECT_EQ(hover_card()->GetAnchorView(), simple_action); EXPECT_EQ(hover_card()->GetTitleTextForTesting(), - simple_action_A->view_controller()->GetActionName()); + simple_action->view_controller()->GetActionName()); EXPECT_FALSE(hover_card()->IsFooterVisible()); - // Hover over the simple extension pinned by policy. + // Hover over the extension installed by policy and pinned by the user. // Verify card anchors to its action using the same widget, because it // transitions from one action view to the other, and it contains contains the // extension's name and a footnote with only policy label. - ToolbarActionView* simple_action_B = - GetExtensionsToolbarContainer()->GetViewForId(simple_extension_B->id()); - HoverMouseOverActionView(simple_action_B); + ToolbarActionView* force_installed_action = + GetExtensionsToolbarContainer()->GetViewForId( + force_installed_extension->id()); + HoverMouseOverActionView(force_installed_action); views::test::WidgetVisibleWaiter(widget).Wait(); ASSERT_TRUE(widget); EXPECT_TRUE(widget->IsVisible()); - EXPECT_EQ(hover_card()->GetAnchorView(), simple_action_B); + EXPECT_EQ(hover_card()->GetAnchorView(), force_installed_action); EXPECT_EQ(hover_card()->GetTitleTextForTesting(), - simple_action_B->view_controller()->GetActionName()); + force_installed_action->view_controller()->GetActionName()); EXPECT_TRUE(hover_card()->IsFooterVisible()); EXPECT_FALSE(hover_card()->IsFooterTitleLabelVisible()); EXPECT_FALSE(hover_card()->IsFooterDescriptionLabelVisible()); @@ -270,37 +270,39 @@ // Verify card anchors to its action using the same widget, and it contains // contains the extension's name and a footnote with only title and // description labels. - ToolbarActionView* action_with_permissions_A = + ToolbarActionView* action_with_host_permissions = GetExtensionsToolbarContainer()->GetViewForId( - extension_with_permissions_A->id()); - HoverMouseOverActionView(action_with_permissions_A); + extension_with_host_permissions->id()); + HoverMouseOverActionView(action_with_host_permissions); views::test::WidgetVisibleWaiter(widget).Wait(); ASSERT_TRUE(widget); EXPECT_TRUE(widget->IsVisible()); - EXPECT_EQ(hover_card()->GetAnchorView(), action_with_permissions_A); + EXPECT_EQ(hover_card()->GetAnchorView(), action_with_host_permissions); EXPECT_EQ(hover_card()->GetTitleTextForTesting(), - action_with_permissions_A->view_controller()->GetActionName()); + action_with_host_permissions->view_controller()->GetActionName()); EXPECT_TRUE(hover_card()->IsFooterVisible()); EXPECT_TRUE(hover_card()->IsFooterTitleLabelVisible()); EXPECT_TRUE(hover_card()->IsFooterDescriptionLabelVisible()); EXPECT_FALSE(hover_card()->IsFooterPolicyLabelVisible()); EXPECT_FALSE(hover_card()->IsFooterSeparatorVisible()); - // Hover over the extension with host permission pinned by policy. - // Verify card anchors to its action using the same widget, and it contains - // contains the extension's name and a footnote with both title and + // Hover over the extension with host permission installed and pinned by + // policy. Verify card anchors to its action using the same widget, and it + // contains contains the extension's name and a footnote with both title and // description labels, and policy label. Since all labels are visible, // separator should also be visible to distinct between them. - ToolbarActionView* action_with_permissions_B = + ToolbarActionView* force_pinned_action_with_host_permissions = GetExtensionsToolbarContainer()->GetViewForId( - extension_with_permissions_B->id()); - HoverMouseOverActionView(action_with_permissions_B); + force_pinned_extension_with_host_permissions->id()); + HoverMouseOverActionView(force_pinned_action_with_host_permissions); views::test::WidgetVisibleWaiter(widget).Wait(); ASSERT_TRUE(widget); EXPECT_TRUE(widget->IsVisible()); - EXPECT_EQ(hover_card()->GetAnchorView(), action_with_permissions_B); + EXPECT_EQ(hover_card()->GetAnchorView(), + force_pinned_action_with_host_permissions); EXPECT_EQ(hover_card()->GetTitleTextForTesting(), - action_with_permissions_B->view_controller()->GetActionName()); + force_pinned_action_with_host_permissions->view_controller() + ->GetActionName()); EXPECT_TRUE(hover_card()->IsFooterVisible()); EXPECT_TRUE(hover_card()->IsFooterTitleLabelVisible()); EXPECT_TRUE(hover_card()->IsFooterDescriptionLabelVisible());
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_controller.cc b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_controller.cc index 4930157..af963b5 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_controller.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_controller.cc
@@ -10,7 +10,6 @@ #include "base/memory/raw_ptr.h" #include "base/time/time.h" #include "build/build_config.h" -#include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/toolbar/toolbar_action_hover_card_types.h" #include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h" #include "chrome/browser/ui/views/toolbar/toolbar_action_hover_card_bubble_view.h" @@ -71,9 +70,8 @@ // ToolbarActionHoverCardController ToolbarActionHoverCardController::ToolbarActionHoverCardController( - Profile* profile, ExtensionsToolbarContainer* extensions_container) - : profile_(profile), extensions_container_(extensions_container) {} + : extensions_container_(extensions_container) {} ToolbarActionHoverCardController::~ToolbarActionHoverCardController() = default; @@ -214,7 +212,7 @@ ToolbarActionView* action_view) { DCHECK(action_view); - hover_card_ = new ToolbarActionHoverCardBubbleView(action_view, profile_); + hover_card_ = new ToolbarActionHoverCardBubbleView(action_view); hover_card_observation_.Observe(hover_card_.get()); event_sniffer_ = std::make_unique<EventSniffer>(this);
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_controller.h b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_controller.h index 18d8ef7..b40704a 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_controller.h +++ b/chrome/browser/ui/views/toolbar/toolbar_action_hover_card_controller.h
@@ -22,13 +22,11 @@ class ToolbarActionHoverCardBubbleView; class ExtensionsToolbarContainer; class ToolbarActionView; -class Profile; // Controls how hover cards are shown and hidden for toolbar actions. class ToolbarActionHoverCardController : public views::ViewObserver { public: explicit ToolbarActionHoverCardController( - Profile* profile, ExtensionsToolbarContainer* extensions_container); ~ToolbarActionHoverCardController() override; @@ -74,8 +72,6 @@ void OnViewVisibilityChanged(views::View* observed_view, views::View* starting_view) override; - raw_ptr<Profile> profile_; - // Timestamp of the last time the hover card is hidden by the mouse leaving // the tab strip. This is used for reshowing the hover card without delay if // the mouse reenters within a given amount of time.
diff --git a/chrome/browser/ui/webui/chromeos/internet_detail_dialog.cc b/chrome/browser/ui/webui/chromeos/internet_detail_dialog.cc index cdccff2..9de3a76 100644 --- a/chrome/browser/ui/webui/chromeos/internet_detail_dialog.cc +++ b/chrome/browser/ui/webui/chromeos/internet_detail_dialog.cc
@@ -16,6 +16,7 @@ #include "chrome/grit/generated_resources.h" #include "chrome/grit/internet_detail_dialog_resources.h" #include "chrome/grit/internet_detail_dialog_resources_map.h" +#include "chromeos/ash/components/network/network_connect.h" #include "chromeos/ash/components/network/network_handler.h" #include "chromeos/ash/components/network/network_state.h" #include "chromeos/ash/components/network/network_state_handler.h" @@ -25,6 +26,7 @@ #include "components/strings/grit/components_strings.h" #include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui_data_source.h" +#include "content/public/browser/web_ui_message_handler.h" #include "ui/base/l10n/l10n_util.h" #include "ui/chromeos/strings/grit/ui_chromeos_strings.h" #include "ui/chromeos/strings/network_element_localized_strings_provider.h" @@ -53,6 +55,7 @@ {"networkButtonConnect", IDS_SETTINGS_INTERNET_BUTTON_CONNECT}, {"networkButtonDisconnect", IDS_SETTINGS_INTERNET_BUTTON_DISCONNECT}, {"networkButtonForget", IDS_SETTINGS_INTERNET_BUTTON_FORGET}, + {"networkButtonSignin", IDS_SETTINGS_INTERNET_BUTTON_SIGNIN}, {"networkIPAddress", IDS_SETTINGS_INTERNET_NETWORK_IP_ADDRESS}, {"networkSectionNetwork", IDS_SETTINGS_INTERNET_NETWORK_SECTION_NETWORK}, {"networkSectionProxy", IDS_SETTINGS_INTERNET_NETWORK_SECTION_PROXY}, @@ -72,6 +75,29 @@ : network.name(); } +class PortalNetworkMessageHandler : public content::WebUIMessageHandler { + public: + PortalNetworkMessageHandler() = default; + ~PortalNetworkMessageHandler() override = default; + + void RegisterMessages() override { + web_ui()->RegisterMessageCallback( + "showPortalSignin", + base::BindRepeating(&PortalNetworkMessageHandler::ShowPortalSignin, + base::Unretained(this))); + } + + private: + void ShowPortalSignin(const base::Value::List& args) { + if (args.size() < 1 || !args[0].is_string()) { + NOTREACHED() << "Invalid args for: ShowPortalSignin"; + return; + } + const std::string& guid = args[0].GetString(); + ash::NetworkConnect::Get()->ShowPortalSignin(guid); + } +}; + } // namespace // static @@ -138,11 +164,15 @@ InternetDetailDialogUI::InternetDetailDialogUI(content::WebUI* web_ui) : ui::MojoWebDialogUI(web_ui) { + web_ui->AddMessageHandler(std::make_unique<PortalNetworkMessageHandler>()); + content::WebUIDataSource* source = content::WebUIDataSource::Create( chrome::kChromeUIInternetDetailDialogHost); source->DisableTrustedTypesCSP(); source->AddBoolean("showTechnologyBadge", !ash::features::IsSeparateNetworkIconsEnabled()); + source->AddBoolean("captivePortalUI2022", + ash::features::IsCaptivePortalUI2022Enabled()); cellular_setup::AddNonStringLoadTimeData(source); AddInternetStrings(source); source->AddLocalizedString("title", IDS_SETTINGS_INTERNET_DETAIL);
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc index f9c1bb4..0144ed2 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -540,6 +540,7 @@ theme_service_(ThemeServiceFactory::GetForProfile(profile_)), ntp_custom_background_service_( NtpCustomBackgroundServiceFactory::GetForProfile(profile_)), + web_contents_(web_ui->GetWebContents()), // We initialize navigation_start_time_ to a reasonable value to account // for the unlikely case where the NewTabPageHandler is created before we // received the DidStartNavigation event. @@ -583,18 +584,22 @@ // background image available as soon as the page loads to prevent a potential // white flicker. + // Load time data is cached across page reloads. Listen for theme changes so + // that theme info is up-to-date when reloading. + native_theme_observation_.Observe(ui::NativeTheme::GetInstanceForNativeUi()); + theme_service_observation_.Observe(theme_service_.get()); ntp_custom_background_service_observation_.Observe( ntp_custom_background_service_.get()); // Create and register customize chrome entry on unified side panel if (customize_chrome::IsSidePanelEnabled()) { auto* customize_chrome_tab_helper = - CustomizeChromeTabHelper::FromWebContents(web_contents()); + CustomizeChromeTabHelper::FromWebContents(web_contents_); customize_chrome_tab_helper->CreateAndRegisterEntry(); } // Populates the load time data with basic info. - OnColorProviderChanged(); + OnThemeChanged(); OnCustomBackgroundImageUpdated(); OnLoad(); } @@ -605,7 +610,7 @@ // Deregister customize chrome entry on unified side panel if (customize_chrome::IsSidePanelEnabled()) { auto* customize_chrome_tab_helper = - CustomizeChromeTabHelper::FromWebContents(web_contents()); + CustomizeChromeTabHelper::FromWebContents(web_contents_); customize_chrome_tab_helper->DeregisterEntry(); } } @@ -674,7 +679,7 @@ void NewTabPageUI::BindInterface( mojo::PendingReceiver<realbox::mojom::PageHandler> pending_page_handler) { realbox_handler_ = std::make_unique<RealboxHandler>( - std::move(pending_page_handler), profile_, web_contents()); + std::move(pending_page_handler), profile_, web_contents_); } void NewTabPageUI::BindInterface( @@ -719,7 +724,7 @@ void NewTabPageUI::BindInterface( mojo::PendingReceiver<photos::mojom::PhotosHandler> pending_receiver) { photos_handler_ = std::make_unique<PhotosHandler>(std::move(pending_receiver), - profile_, web_contents()); + profile_, web_contents_); } void NewTabPageUI::BindInterface( @@ -739,7 +744,7 @@ mojo::PendingReceiver<chrome_cart::mojom::CartHandler> pending_page_handler) { cart_handler_ = std::make_unique<CartHandler>(std::move(pending_page_handler), - profile_, web_contents()); + profile_, web_contents_); } void NewTabPageUI::CreatePageHandler( @@ -750,7 +755,7 @@ page_handler_ = std::make_unique<NewTabPageHandler>( std::move(pending_page_handler), std::move(pending_page), profile_, ntp_custom_background_service_, theme_service_, - LogoServiceFactory::GetForProfile(profile_), web_contents(), + LogoServiceFactory::GetForProfile(profile_), web_contents_, navigation_start_time_); } @@ -760,7 +765,7 @@ mojo::PendingReceiver<customize_themes::mojom::CustomizeThemesHandler> pending_handler) { customize_themes_handler_ = std::make_unique<ChromeCustomizeThemesHandler>( - std::move(pending_client), std::move(pending_handler), web_contents(), + std::move(pending_client), std::move(pending_handler), web_contents_, profile_); } @@ -784,17 +789,19 @@ DCHECK(pending_page.is_valid()); most_visited_page_handler_ = std::make_unique<MostVisitedHandler>( std::move(pending_page_handler), std::move(pending_page), profile_, - web_contents(), GURL(chrome::kChromeUINewTabPageURL), + web_contents_, GURL(chrome::kChromeUINewTabPageURL), navigation_start_time_); most_visited_page_handler_->EnableCustomLinks(IsCustomLinksEnabled()); most_visited_page_handler_->SetShortcutsVisible(IsShortcutsVisible()); } -void NewTabPageUI::OnColorProviderChanged() { - if (!web_contents()) - return; +void NewTabPageUI::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) { + OnThemeChanged(); +} + +void NewTabPageUI::OnThemeChanged() { base::Value::Dict update; - const ui::ColorProvider& color_provider = web_contents()->GetColorProvider(); + const ui::ColorProvider& color_provider = web_contents_->GetColorProvider(); auto background_color = color_provider.GetColor(kColorNewTabPageBackground); update.Set("backgroundColor", skia::SkColorToHexString(background_color)); content::WebUIDataSource::Update(profile_, chrome::kChromeUINewTabPageHost,
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h index b75e53f..371b26d 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
@@ -21,6 +21,7 @@ #include "chrome/browser/search/background/ntp_custom_background_service.h" #include "chrome/browser/search/background/ntp_custom_background_service_observer.h" #include "chrome/browser/themes/theme_service.h" +#include "chrome/browser/themes/theme_service_observer.h" #include "chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom.h" #include "chrome/browser/ui/webui/realbox/realbox.mojom-forward.h" #include "components/prefs/pref_change_registrar.h" @@ -30,6 +31,7 @@ #include "mojo/public/cpp/bindings/receiver.h" #include "ui/base/resource/resource_scale_factor.h" #include "ui/native_theme/native_theme.h" +#include "ui/native_theme/native_theme_observer.h" #include "ui/webui/mojo_web_ui_controller.h" #include "ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom.h" #include "ui/webui/resources/cr_components/customize_themes/customize_themes.mojom.h" @@ -41,6 +43,7 @@ namespace content { class NavigationHandle; +class WebContents; class WebUI; } // namespace content @@ -73,6 +76,8 @@ public customize_themes::mojom::CustomizeThemesHandlerFactory, public most_visited::mojom::MostVisitedPageHandlerFactory, public browser_command::mojom::CommandHandlerFactory, + public ui::NativeThemeObserver, + public ThemeServiceObserver, public NtpCustomBackgroundServiceObserver, content::WebContentsObserver { public: @@ -187,6 +192,12 @@ mojo::PendingReceiver<most_visited::mojom::MostVisitedPageHandler> pending_page_handler) override; + // ui::NativeThemeObserver: + void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override; + + // ThemeServiceObserver: + void OnThemeChanged() override; + // NtpCustomBackgroundServiceObserver: void OnCustomBackgroundImageUpdated() override; void OnNtpCustomBackgroundServiceShuttingDown() override; @@ -194,7 +205,6 @@ // content::WebContentsObserver: void DidStartNavigation( content::NavigationHandle* navigation_handle) override; - void OnColorProviderChanged() override; bool IsCustomLinksEnabled() const; bool IsShortcutsVisible() const; @@ -228,9 +238,14 @@ raw_ptr<Profile> profile_; raw_ptr<ThemeService> theme_service_; raw_ptr<NtpCustomBackgroundService> ntp_custom_background_service_; + base::ScopedObservation<ui::NativeTheme, ui::NativeThemeObserver> + native_theme_observation_{this}; + base::ScopedObservation<ThemeService, ThemeServiceObserver> + theme_service_observation_{this}; base::ScopedObservation<NtpCustomBackgroundService, NtpCustomBackgroundServiceObserver> ntp_custom_background_service_observation_{this}; + raw_ptr<content::WebContents> web_contents_; // Time the NTP started loading. Used for logging the WebUI NTP's load // performance. base::Time navigation_start_time_;
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn index 3d2d0d8..bef9088e 100644 --- a/chrome/browser/web_applications/BUILD.gn +++ b/chrome/browser/web_applications/BUILD.gn
@@ -99,6 +99,8 @@ "manifest_update_manager.h", "manifest_update_task.cc", "manifest_update_task.h", + "manifest_update_utils.cc", + "manifest_update_utils.h", "os_integration/os_integration_manager.cc", "os_integration/os_integration_manager.h", "os_integration/os_integration_sub_manager.h",
diff --git a/chrome/browser/web_applications/commands/install_isolated_app_command.cc b/chrome/browser/web_applications/commands/install_isolated_app_command.cc index 537b376b6..cb6691a 100644 --- a/chrome/browser/web_applications/commands/install_isolated_app_command.cc +++ b/chrome/browser/web_applications/commands/install_isolated_app_command.cc
@@ -44,6 +44,9 @@ namespace web_app { +constexpr static char kGeneratedInstallPagePath[] = + "/.well-known/_generated_install_page.html"; + namespace { bool IsUrlLoadingResultSuccess(WebAppUrlLoader::Result result) { @@ -61,7 +64,7 @@ } // namespace InstallIsolatedAppCommand::InstallIsolatedAppCommand( - const GURL& url, + const IsolatedWebAppUrlInfo& isolation_info, const IsolationData& isolation_data, std::unique_ptr<content::WebContents> web_contents, std::unique_ptr<WebAppUrlLoader> url_loader, @@ -70,8 +73,8 @@ InstallIsolatedAppCommandError>)> callback) : lock_(std::make_unique<AppLock>( - base::flat_set<AppId>{GenerateAppId("", GURL{url})})), - url_(url), + base::flat_set<AppId>{isolation_info.app_id()})), + isolation_info_(isolation_info), isolation_data_(isolation_data), web_contents_(std::move(web_contents)), url_loader_(std::move(url_loader)), @@ -80,9 +83,6 @@ DETACH_FROM_SEQUENCE(sequence_checker_); DCHECK(web_contents_ != nullptr); - DCHECK(url_loader_ != nullptr); - - DCHECK(url_.is_valid()); DCHECK(!callback.is_null()); callback_ = @@ -110,7 +110,6 @@ } void InstallIsolatedAppCommand::LoadUrl() { - DCHECK(url_.is_valid()); DCHECK(web_contents_ != nullptr); // |web_app::IsolatedWebAppURLLoaderFactory| uses the isolation data in order @@ -119,7 +118,9 @@ IsolatedWebAppPendingInstallInfo::FromWebContents(*web_contents_) .set_isolation_data(isolation_data_); - url_loader_->LoadUrl(url_, web_contents_.get(), + GURL install_page_url = + isolation_info_.origin().GetURL().Resolve(kGeneratedInstallPagePath); + url_loader_->LoadUrl(install_page_url, web_contents_.get(), WebAppUrlLoader::UrlComparison::kIgnoreQueryParamsAndRef, base::BindOnce(&InstallIsolatedAppCommand::OnLoadUrl, weak_factory_.GetWeakPtr())); @@ -183,7 +184,7 @@ info.manifest_id = ""; - url::Origin origin = url::Origin::Create(url_); + url::Origin origin = isolation_info_.origin(); if (manifest.scope != origin.GetURL()) { return base::unexpected{ base::StrCat({"Scope should resolve to the origin. scope: ",
diff --git a/chrome/browser/web_applications/commands/install_isolated_app_command.h b/chrome/browser/web_applications/commands/install_isolated_app_command.h index 24732f6..a3c454f 100644 --- a/chrome/browser/web_applications/commands/install_isolated_app_command.h +++ b/chrome/browser/web_applications/commands/install_isolated_app_command.h
@@ -16,6 +16,7 @@ #include "base/types/expected.h" #include "base/values.h" #include "chrome/browser/web_applications/commands/web_app_command.h" +#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h" #include "chrome/browser/web_applications/os_integration/os_integration_manager.h" #include "chrome/browser/web_applications/web_app_id.h" #include "chrome/browser/web_applications/web_app_install_info.h" @@ -56,14 +57,19 @@ // re-using web contents. class InstallIsolatedAppCommand : public WebAppCommand { public: - // |application_url| is the url for the app to be installed. The url must be - // valid. + // + // |isolation_info| holds the origin information of the app. It is + // randomly generated for dev-proxy and the public key of signed bundle. It is + // guarantee to be valid. + // + // |isolation_data| holds information about the + // mode(dev-mod-proxy/signed-bundle) and the source. // // |callback| must be not null. // // The `id` in the application's manifest must equal "/". explicit InstallIsolatedAppCommand( - const GURL& application_url, + const IsolatedWebAppUrlInfo& isolation_info, const IsolationData& isolation_data, std::unique_ptr<content::WebContents> web_contents, std::unique_ptr<WebAppUrlLoader> url_loader, @@ -123,7 +129,7 @@ std::unique_ptr<AppLock> lock_; - GURL url_; + IsolatedWebAppUrlInfo isolation_info_; IsolationData isolation_data_; std::unique_ptr<content::WebContents> web_contents_;
diff --git a/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc b/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc index c9cc942727..907c78c5 100644 --- a/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc +++ b/chrome/browser/web_applications/commands/install_isolated_app_command_unittest.cc
@@ -14,6 +14,7 @@ #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/check_op.h" #include "base/files/file_path.h" #include "base/functional/callback_forward.h" #include "base/functional/callback_helpers.h" @@ -25,6 +26,7 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/test_future.h" #include "base/types/expected.h" +#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h" #include "chrome/browser/web_applications/isolated_web_apps/pending_install_info.h" #include "chrome/browser/web_applications/isolation_data.h" #include "chrome/browser/web_applications/locks/lock.h" @@ -42,6 +44,7 @@ #include "chrome/browser/web_applications/web_app_install_utils.h" #include "chrome/browser/web_applications/web_app_url_loader.h" #include "chrome/test/base/testing_profile.h" +#include "components/web_package/signed_web_bundles/signed_web_bundle_id.h" #include "components/webapps/browser/install_result_code.h" #include "components/webapps/browser/installable/installable_metrics.h" #include "content/public/browser/web_contents.h" @@ -84,30 +87,40 @@ using ::testing::VariantWith; using ::testing::WithArg; -IsolationData CreateDefaultIsolationData( +IsolatedWebAppUrlInfo CreateRandomIsolatedWebAppUrlInfo() { + web_package::SignedWebBundleId signed_web_bundle_id = + web_package::SignedWebBundleId::CreateRandomForDevelopment(); + base::expected<IsolatedWebAppUrlInfo, std::string> url_info = + IsolatedWebAppUrlInfo::CreateFromSignedWebBundleId(signed_web_bundle_id); + if (!url_info.has_value()) { + CHECK(false) << "Failed to create testing web app url info: " + << url_info.error(); + } + return url_info.value(); +} + +IsolationData CreateIsolationDataDevProxy( base::StringPiece dev_mode_proxy_url = "http://default-proxy-url.org/") { return IsolationData{IsolationData::DevModeProxy{ .proxy_url = std::string(dev_mode_proxy_url)}}; } -blink::mojom::ManifestPtr CreateDefaultManifest( - base::StringPiece application_url) { +blink::mojom::ManifestPtr CreateDefaultManifest(const GURL& application_url) { auto manifest = blink::mojom::Manifest::New(); manifest->id = u""; - manifest->scope = GURL{application_url}.Resolve("/"); - manifest->start_url = - GURL{application_url}.Resolve("/testing-start-url.html"); + manifest->scope = application_url.Resolve("/"); + manifest->start_url = application_url.Resolve("/testing-start-url.html"); manifest->display = DisplayMode::kStandalone; manifest->short_name = u"test short manifest name"; return manifest; } -GURL CreateDefaultManifestURL(base::StringPiece application_url) { - return GURL{application_url}.Resolve("/manifest.webmanifest"); +GURL CreateDefaultManifestURL(const GURL& application_url) { + return application_url.Resolve("/manifest.webmanifest"); } auto ReturnManifest(const blink::mojom::ManifestPtr& manifest, - GURL manifest_url, + const GURL& manifest_url, bool is_installable = true) { constexpr int kCallbackArgumentIndex = 2; @@ -124,7 +137,7 @@ } std::unique_ptr<MockDataRetriever> CreateDefaultDataRetriever( - base::StringPiece application_url) { + const GURL& application_url) { std::unique_ptr<MockDataRetriever> fake_data_retriever = std::make_unique<NiceMock<MockDataRetriever>>(); @@ -180,7 +193,7 @@ } struct Parameters { - std::string url; + IsolatedWebAppUrlInfo url_info; std::unique_ptr<WebAppUrlLoader> url_loader; std::unique_ptr<content::WebContents> web_contents; absl::optional<IsolationData> isolation_data; @@ -203,40 +216,46 @@ } auto command = CreateCommand( - parameters.url, std::move(web_contents), - parameters.isolation_data.value_or( - CreateDefaultIsolationData(parameters.url)), + parameters.url_info, std::move(web_contents), parameters.isolation_data, std::move(parameters.url_loader), test_future.GetCallback()); + command->SetDataRetrieverForTesting( data_retriever != nullptr ? std::move(data_retriever) - : CreateDefaultDataRetriever(parameters.url)); + : CreateDefaultDataRetriever( + parameters.url_info.origin().GetURL())); ScheduleCommand(std::move(command)); return test_future.Get(); } std::unique_ptr<InstallIsolatedAppCommand> CreateCommand( - base::StringPiece url, + const IsolatedWebAppUrlInfo& url_info, std::unique_ptr<content::WebContents> web_contents, - const IsolationData& isolation_data, + absl::optional<IsolationData> isolation_data, std::unique_ptr<WebAppUrlLoader> url_loader, base::OnceCallback<void(base::expected<InstallIsolatedAppCommandSuccess, InstallIsolatedAppCommandError>)> callback) { - const GURL application_url{url}; - DCHECK(application_url.is_valid()); + if (!isolation_data.has_value()) { + isolation_data = CreateIsolationDataDevProxy(); + } return std::make_unique<InstallIsolatedAppCommand>( - application_url, isolation_data, std::move(web_contents), + url_info, isolation_data.value(), std::move(web_contents), std::move(url_loader), *install_finalizer_, std::move(callback)); } base::expected<InstallIsolatedAppCommandSuccess, InstallIsolatedAppCommandError> - ExecuteCommandWithManifest(base::StringPiece application_url, - const blink::mojom::ManifestPtr& manifest) { + ExecuteCommandWithManifest(const IsolatedWebAppUrlInfo& url_info, + const blink::mojom::ManifestPtr& manifest, + absl::optional<IsolationData> isolation_data = + absl::optional<IsolationData>()) { + GURL application_url = url_info.origin().GetURL(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{application_url}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); std::unique_ptr<MockDataRetriever> fake_data_retriever = CreateDefaultDataRetriever(application_url); @@ -245,12 +264,10 @@ .WillByDefault(ReturnManifest( manifest, CreateDefaultManifestURL(application_url))); - return ExecuteCommand( - { - .url = std::string(application_url), - .url_loader = std::move(url_loader), - }, - std::move(fake_data_retriever)); + return ExecuteCommand({.url_info = url_info, + .url_loader = std::move(url_loader), + .isolation_data = isolation_data}, + std::move(fake_data_retriever)); } TestingProfile* profile() const { return profile_.get(); } @@ -324,86 +341,83 @@ TEST_F(InstallIsolatedAppCommandTest, ServiceWorkerIsNotRequiredForInstallation) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{"http://test-url-example.com"}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); std::unique_ptr<MockDataRetriever> fake_data_retriever = - CreateDefaultDataRetriever("http://test-url-example.com"); + CreateDefaultDataRetriever(url_info.origin().GetURL()); EXPECT_CALL(*fake_data_retriever, CheckInstallabilityAndRetrieveManifest( _, /*bypass_service_worker_check=*/IsTrue(), _)) .WillOnce(ReturnManifest( - CreateDefaultManifest("http://test-url-example.com"), - CreateDefaultManifestURL("http://test-url-example.com"))); + // IsolatedWebAppUrlLoaderFactory is responsible for resolving + // isolated-app:// schema requests. + CreateDefaultManifest(url_info.origin().GetURL()), + CreateDefaultManifestURL(url_info.origin().GetURL()))); EXPECT_THAT(ExecuteCommand( Parameters{ - .url = "http://test-url-example.com", + .url_info = url_info, .url_loader = std::move(url_loader), }, std::move(fake_data_retriever)), IsInstallationOk()); } -TEST_F(InstallIsolatedAppCommandTest, CommandCanBeExecutedSuccesfully) { - auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{"http://test-url-example.com"}, - WebAppUrlLoader::Result::kUrlLoaded); - - EXPECT_THAT(ExecuteCommand(Parameters{ - .url = "http://test-url-example.com", - .url_loader = std::move(url_loader), - }), - IsInstallationOk()); -} - TEST_F(InstallIsolatedAppCommandTest, PropagateErrorWhenURLLoaderFails) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); url_loader->SetNextLoadUrlResult( - GURL{"http://test-url-example.com"}, + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), WebAppUrlLoader::Result::kFailedErrorPageLoaded); - EXPECT_THAT(ExecuteCommand(Parameters{ - .url = "http://test-url-example.com", - .url_loader = std::move(url_loader), - }), + EXPECT_THAT(ExecuteCommand(Parameters{.url_info = url_info, + .url_loader = std::move(url_loader)}), IsInstallationError(HasSubstr("Error during URL loading: "))); } TEST_F(InstallIsolatedAppCommandTest, PropagateErrorWhenURLLoaderFailsWithDestroyedWebContentsError) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); url_loader->SetNextLoadUrlResult( - GURL{"http://test-url-example.com"}, + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), WebAppUrlLoaderResult::kFailedWebContentsDestroyed); - EXPECT_THAT(ExecuteCommand(Parameters{ - .url = "http://test-url-example.com", - .url_loader = std::move(url_loader), - }), + EXPECT_THAT(ExecuteCommand(Parameters{.url_info = url_info, + .url_loader = std::move(url_loader)}), IsInstallationError(HasSubstr( "Error during URL loading: FailedWebContentsDestroyed"))); } TEST_F(InstallIsolatedAppCommandTest, URLLoaderIsCalledWithURLgivenToTheInstallCommand) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{"http://another-test-url-example.com"}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); - EXPECT_THAT(ExecuteCommand(Parameters{ - .url = "http://another-test-url-example.com", - .url_loader = std::move(url_loader), - }), + EXPECT_THAT(ExecuteCommand(Parameters{.url_info = url_info, + .url_loader = std::move(url_loader)}), IsInstallationOk()); } TEST_F(InstallIsolatedAppCommandTest, URLLoaderIgnoresQueryParameters) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{"http://test-url-example.com"}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); absl::optional<WebAppUrlLoader::UrlComparison> last_url_comparison = absl::nullopt; @@ -413,10 +427,8 @@ last_url_comparison = url_comparison; })); - EXPECT_THAT(ExecuteCommand(Parameters{ - .url = "http://test-url-example.com", - .url_loader = std::move(url_loader), - }), + EXPECT_THAT(ExecuteCommand(Parameters{.url_info = url_info, + .url_loader = std::move(url_loader)}), IsInstallationOk()); EXPECT_THAT( @@ -426,16 +438,18 @@ TEST_F(InstallIsolatedAppCommandTest, InstallationFailsWhenFinalizerReturnNotInstallableError) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{"http://test-url-example.com"}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); install_finalizer().SetNextFinalizeInstallResult( - GenerateAppIdFromUnhashed("http://testing-unused-app-id.com/"), - webapps::InstallResultCode::kNotInstallable); + url_info.app_id(), webapps::InstallResultCode::kNotInstallable); EXPECT_THAT(ExecuteCommand(Parameters{ - .url = "http://test-url-example.com", + .url_info = url_info, .url_loader = std::move(url_loader), }), IsInstallationError( @@ -444,16 +458,18 @@ TEST_F(InstallIsolatedAppCommandTest, InstallationFailsWhenFinalizerReturnInstallURLLoadTimeOut) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{"http://test-url-example.com"}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); install_finalizer().SetNextFinalizeInstallResult( - GenerateAppIdFromUnhashed("http://testing-unused-app-id.com/"), - webapps::InstallResultCode::kInstallURLLoadTimeOut); + url_info.app_id(), webapps::InstallResultCode::kInstallURLLoadTimeOut); EXPECT_THAT(ExecuteCommand(Parameters{ - .url = "http://test-url-example.com", + .url_info = url_info, .url_loader = std::move(url_loader), }), IsInstallationError(HasSubstr( @@ -462,36 +478,36 @@ TEST_F(InstallIsolatedAppCommandTest, InstallationSucceedesWhenFinalizerReturnSuccessNewInstall) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{"http://test-url-example.com"}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); install_finalizer().SetNextFinalizeInstallResult( - GenerateAppIdFromUnhashed("http://testing-unused-app-id.com/"), - webapps::InstallResultCode::kSuccessNewInstall); + url_info.app_id(), webapps::InstallResultCode::kSuccessNewInstall); - EXPECT_THAT(ExecuteCommand(Parameters{ - .url = "http://test-url-example.com", - .url_loader = std::move(url_loader), - }), + EXPECT_THAT(ExecuteCommand(Parameters{.url_info = url_info, + .url_loader = std::move(url_loader)}), IsInstallationOk()); } TEST_F(InstallIsolatedAppCommandTest, InstallationFinalizedWithIsolatedAppDevInstallInstallSource) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{"http://test-url-example.com"}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); std::unique_ptr<MockDataRetriever> fake_data_retriever = - CreateDefaultDataRetriever("http://test-url-example.com"); + CreateDefaultDataRetriever(url_info.origin().GetURL()); - EXPECT_THAT(ExecuteCommand( - Parameters{ - .url = "http://test-url-example.com", - .url_loader = std::move(url_loader), - }, - std::move(fake_data_retriever)), + EXPECT_THAT(ExecuteCommand(Parameters{.url_info = url_info, + .url_loader = std::move(url_loader)}, + std::move(fake_data_retriever)), IsInstallationOk()); using FinalizeOptions = WebAppInstallFinalizer::FinalizeOptions; @@ -507,12 +523,15 @@ TEST_F(InstallIsolatedAppCommandTest, InstallationFailsWhenAppIsNotInstallable) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{"http://test-url-example.com"}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); std::unique_ptr<MockDataRetriever> fake_data_retriever = - CreateDefaultDataRetriever("http://test-url-example.com"); + CreateDefaultDataRetriever(url_info.origin().GetURL()); ON_CALL(*fake_data_retriever, CheckInstallabilityAndRetrieveManifest) .WillByDefault( @@ -521,10 +540,7 @@ /*is_installable=*/false)); EXPECT_THAT(ExecuteCommand( - { - .url = "http://test-url-example.com", - .url_loader = std::move(url_loader), - }, + {.url_info = url_info, .url_loader = std::move(url_loader)}, std::move(fake_data_retriever)), IsInstallationError(HasSubstr("App is not installable"))); } @@ -534,36 +550,39 @@ InstallIsolatedAppCommandError>> test_future; + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto command = CreateCommand( - "http://test-app-id.com/", + url_info, content::WebContents::Create( content::WebContents::CreateParams(profile())), - CreateDefaultIsolationData(), std::make_unique<TestWebAppUrlLoader>(), + CreateIsolationDataDevProxy(), std::make_unique<TestWebAppUrlLoader>(), test_future.GetCallback()); - EXPECT_THAT(command->lock(), - AllOf(Property(&Lock::type, Eq(Lock::Type::kApp)), - Property(&Lock::app_ids, - UnorderedElementsAre(GenerateAppIdFromUnhashed( - "http://test-app-id.com/"))))); + EXPECT_THAT( + command->lock(), + AllOf(Property(&Lock::type, Eq(Lock::Type::kApp)), + Property(&Lock::app_ids, UnorderedElementsAre(url_info.app_id())))); } TEST_F(InstallIsolatedAppCommandTest, InstallationFailsWhenAppIsInstallableButManifestIsNull) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{"http://test-url-example.com"}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); std::unique_ptr<MockDataRetriever> fake_data_retriever = - CreateDefaultDataRetriever("http://test-url-example.com"); + CreateDefaultDataRetriever(url_info.origin().GetURL()); ON_CALL(*fake_data_retriever, CheckInstallabilityAndRetrieveManifest) .WillByDefault(ReturnManifest( /*manifest=*/nullptr, - CreateDefaultManifestURL("http://test-url-example.com"))); + CreateDefaultManifestURL(url_info.origin().GetURL()))); EXPECT_THAT(ExecuteCommand( Parameters{ - .url = "http://test-url-example.com", + .url_info = url_info, .url_loader = std::move(url_loader), }, std::move(fake_data_retriever)), @@ -571,14 +590,15 @@ } TEST_F(InstallIsolatedAppCommandTest, IsolationDataSentToFinalizer) { - std::string url("http://test-url-example.com/"); - + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{url}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); EXPECT_THAT(ExecuteCommand(Parameters{ - .url = url, + .url_info = url_info, .url_loader = std::move(url_loader), .isolation_data = IsolationData{IsolationData::DevModeProxy{ .proxy_url = "http://some-testing-proxy-url.com/"}}, @@ -599,42 +619,42 @@ TEST_F(InstallIsolatedAppCommandManifestTest, InstallationFailsWhenManifestHasNoId) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); blink::mojom::ManifestPtr manifest = - CreateDefaultManifest("http://manifest-test-url.com"); + CreateDefaultManifest(url_info.origin().GetURL()); manifest->id = absl::nullopt; EXPECT_THAT( - ExecuteCommandWithManifest("http://manifest-test-url.com", - manifest.Clone()), + ExecuteCommandWithManifest(url_info, manifest.Clone()), IsInstallationError(HasSubstr( "Manifest `id` is not present. manifest_url: " + - CreateDefaultManifestURL("http://manifest-test-url.com").spec()))); + CreateDefaultManifestURL(url_info.origin().GetURL()).spec()))); EXPECT_THAT(install_finalizer().web_app_info(), IsNull()); } TEST_F(InstallIsolatedAppCommandManifestTest, FailsWhenManifestIdHasInvalidUTF8Character) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); blink::mojom::ManifestPtr manifest = - CreateDefaultManifest("http://manifest-test-url.com"); + CreateDefaultManifest(url_info.origin().GetURL()); char16_t invalid_utf8_chars = {0xD801}; manifest->id = std::u16string{invalid_utf8_chars}; - EXPECT_THAT(ExecuteCommandWithManifest("http://manifest-test-url.com", - manifest.Clone()), + EXPECT_THAT(ExecuteCommandWithManifest(url_info, manifest.Clone()), IsInstallationError(HasSubstr( "Failed to convert manifest `id` from UTF16 to UTF8"))); } TEST_F(InstallIsolatedAppCommandManifestTest, PassesManifestIdToFinalizerWhenManifestIdIsEmpty) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); blink::mojom::ManifestPtr manifest = - CreateDefaultManifest("http://manifest-test-url.com"); + CreateDefaultManifest(url_info.origin().GetURL()); manifest->id = u""; - EXPECT_THAT(ExecuteCommandWithManifest("http://manifest-test-url.com", - manifest.Clone()), + EXPECT_THAT(ExecuteCommandWithManifest(url_info, manifest.Clone()), IsInstallationOk()); EXPECT_THAT(install_finalizer().web_app_info(), @@ -643,52 +663,53 @@ } TEST_F(InstallIsolatedAppCommandManifestTest, FailsWhenManifestIdIsNotEmpty) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); blink::mojom::ManifestPtr manifest = - CreateDefaultManifest("http://manifest-test-url.com"); + CreateDefaultManifest(url_info.origin().GetURL()); manifest->id = u"test-manifest-id"; - EXPECT_THAT(ExecuteCommandWithManifest("http://manifest-test-url.com", - manifest.Clone()), + EXPECT_THAT(ExecuteCommandWithManifest(url_info, manifest.Clone()), IsInstallationError(HasSubstr(R"(Manifest `id` must be "/")"))); + ; EXPECT_THAT(install_finalizer().web_app_info(), IsNull()); } TEST_F(InstallIsolatedAppCommandManifestTest, FailsWhenManifestScopeIsNotSlash) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); blink::mojom::ManifestPtr manifest = - CreateDefaultManifest("http://manifest-test-url.com"); + CreateDefaultManifest(url_info.origin().GetURL()); - manifest->scope = GURL{"http://manifest-test-url.com"}.Resolve("/scope"); + manifest->scope = url_info.origin().GetURL().Resolve("/scope"); EXPECT_THAT( - ExecuteCommandWithManifest("http://manifest-test-url.com", - manifest.Clone()), + ExecuteCommandWithManifest(url_info, manifest.Clone()), IsInstallationError(HasSubstr("Scope should resolve to the origin"))); EXPECT_THAT(install_finalizer().web_app_info(), IsNull()); } TEST_F(InstallIsolatedAppCommandManifestTest, PassesManifestScopeToFinalizerWhenManifestScopeIsSlash) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); blink::mojom::ManifestPtr manifest = - CreateDefaultManifest("http://manifest-test-url.com"); - manifest->scope = GURL{"http://manifest-test-url.com"}.Resolve("/"); + CreateDefaultManifest(url_info.origin().GetURL()); + manifest->scope = url_info.origin().GetURL().Resolve("/"); - EXPECT_THAT(ExecuteCommandWithManifest("http://manifest-test-url.com", - manifest.Clone()), + EXPECT_THAT(ExecuteCommandWithManifest(url_info, manifest.Clone()), IsInstallationOk()); - EXPECT_THAT(install_finalizer().web_app_info(), - Pointee(Field(&WebAppInstallInfo::scope, - GURL{"http://manifest-test-url.com/"}))); + EXPECT_THAT( + install_finalizer().web_app_info(), + Pointee(Field(&WebAppInstallInfo::scope, url_info.origin().GetURL()))); } TEST_F(InstallIsolatedAppCommandManifestTest, PassesManifestNameAsTitle) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); blink::mojom::ManifestPtr manifest = - CreateDefaultManifest("http://manifest-test-url.com"); + CreateDefaultManifest(url_info.origin().GetURL()); manifest->name = u"test application name"; - EXPECT_THAT(ExecuteCommandWithManifest("http://manifest-test-url.com", - manifest.Clone()), + EXPECT_THAT(ExecuteCommandWithManifest(url_info, manifest.Clone()), IsInstallationOk()); EXPECT_THAT( @@ -698,13 +719,14 @@ TEST_F(InstallIsolatedAppCommandManifestTest, UseShortNameAsTitleWhenNameIsNotPresent) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); + blink::mojom::ManifestPtr manifest = - CreateDefaultManifest("http://manifest-test-url.com"); + CreateDefaultManifest(url_info.origin().GetURL()); manifest->name = absl::nullopt; manifest->short_name = u"test short name"; - EXPECT_THAT(ExecuteCommandWithManifest("http://manifest-test-url.com", - manifest.Clone()), + EXPECT_THAT(ExecuteCommandWithManifest(url_info, manifest.Clone()), IsInstallationOk()); EXPECT_THAT(install_finalizer().web_app_info(), @@ -713,13 +735,13 @@ TEST_F(InstallIsolatedAppCommandManifestTest, UseShortNameAsTitleWhenNameIsEmpty) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); blink::mojom::ManifestPtr manifest = - CreateDefaultManifest("http://manifest-test-url.com"); + CreateDefaultManifest(url_info.origin().GetURL()); manifest->name = u""; manifest->short_name = u"other test short name"; - EXPECT_THAT(ExecuteCommandWithManifest("http://manifest-test-url.com", - manifest.Clone()), + EXPECT_THAT(ExecuteCommandWithManifest(url_info, manifest.Clone()), IsInstallationOk()); EXPECT_THAT( @@ -729,13 +751,13 @@ TEST_F(InstallIsolatedAppCommandManifestTest, TitleIsmptyWhenNameAndShortNameAreNotPresent) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); blink::mojom::ManifestPtr manifest = - CreateDefaultManifest("http://manifest-test-url.com"); + CreateDefaultManifest(url_info.origin().GetURL()); manifest->name = absl::nullopt; manifest->short_name = absl::nullopt; - EXPECT_THAT(ExecuteCommandWithManifest("http://manifest-test-url.com", - manifest.Clone()), + EXPECT_THAT(ExecuteCommandWithManifest(url_info, manifest.Clone()), IsInstallationOk()); EXPECT_THAT(install_finalizer().web_app_info(), @@ -744,9 +766,9 @@ class InstallIsolatedAppCommandManifestIconsTest : public InstallIsolatedAppCommandManifestTest { + public: protected: - static constexpr base::StringPiece kSomeTestApplicationUrl = - "http://manifest-test-url.com"; + GURL kSomeTestApplicationUrl = GURL("http://manifest-test-url.com"); void SetUp() override { InstallIsolatedAppCommandManifestTest::SetUp(); } blink::mojom::ManifestPtr CreateManifest() const { @@ -777,7 +799,7 @@ return bitmap; } -blink::Manifest::ImageResource CreateImageResource(GURL image_src) { +blink::Manifest::ImageResource CreateImageResource(const GURL& image_src) { blink::Manifest::ImageResource image; image.type = u"image/png"; image.sizes.push_back(gfx::Size{kImageSize, kImageSize}); @@ -789,14 +811,18 @@ } TEST_F(InstallIsolatedAppCommandManifestIconsTest, ManifestIconIsDownloaded) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); + kSomeTestApplicationUrl = url_info.origin().GetURL(); + GURL img_url = url_info.origin().GetURL().Resolve("icon.png"); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{kSomeTestApplicationUrl}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + kSomeTestApplicationUrl.Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); blink::mojom::ManifestPtr manifest = CreateManifest(); - manifest->icons = { - CreateImageResource(GURL{"http://test-icon-url.com/icon.png"})}; + manifest->icons = {CreateImageResource(img_url)}; std::unique_ptr<MockDataRetriever> fake_data_retriever = CreateFakeDataRetriever(manifest.Clone()); @@ -806,28 +832,23 @@ manifest, CreateDefaultManifestURL(kSomeTestApplicationUrl))); std::map<GURL, std::vector<SkBitmap>> icons = {{ - GURL{"http://test-icon-url.com/icon.png"}, + img_url, {CreateTestBitmap(SK_ColorRED)}, }}; using HttpStatusCode = int; std::map<GURL, HttpStatusCode> http_result = { - {GURL{"http://test-icon-url.com/icon.png"}, net::HttpStatusCode::HTTP_OK}, + {img_url, net::HttpStatusCode::HTTP_OK}, }; - EXPECT_CALL( - *fake_data_retriever, - GetIcons(_, - UnorderedElementsAre(GURL{"http://test-icon-url.com/icon.png"}), - /*skip_page_favicons=*/true, IsNotNullCallback())) + EXPECT_CALL(*fake_data_retriever, + GetIcons(_, UnorderedElementsAre(img_url), + /*skip_page_favicons=*/true, IsNotNullCallback())) .WillOnce(RunOnceCallback<3>(IconsDownloadedResult::kCompleted, std::move(icons), http_result)); EXPECT_THAT(ExecuteCommand( - { - .url = std::string(kSomeTestApplicationUrl), - .url_loader = std::move(url_loader), - }, + {.url_info = url_info, .url_loader = std::move(url_loader)}, std::move(fake_data_retriever)), IsInstallationOk()); @@ -844,14 +865,18 @@ TEST_F(InstallIsolatedAppCommandManifestIconsTest, InstallationFailsWhenIconDownloadingFails) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); + kSomeTestApplicationUrl = url_info.origin().GetURL(); + GURL img_url = url_info.origin().GetURL().Resolve("icon.png"); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{kSomeTestApplicationUrl}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + kSomeTestApplicationUrl.Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); blink::mojom::ManifestPtr manifest = CreateManifest(); - manifest->icons = { - CreateImageResource(GURL{"http://test-icon-url.com/icon.png"})}; + manifest->icons = {CreateImageResource(img_url)}; std::unique_ptr<MockDataRetriever> fake_data_retriever = CreateFakeDataRetriever(manifest.Clone()); @@ -870,19 +895,19 @@ std::move(icons), http_result)); EXPECT_THAT(ExecuteCommand( - { - .url = std::string(kSomeTestApplicationUrl), - .url_loader = std::move(url_loader), - }, + {.url_info = url_info, .url_loader = std::move(url_loader)}, std::move(fake_data_retriever)), IsInstallationError(HasSubstr( "Error during icon downloading: AbortedDueToFailure"))); } TEST_F(InstallIsolatedAppCommandTest, SetDevModeIsolationDataBeforeUrlLoading) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{"http://test-url-example.com"}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); absl::optional<IsolationData> isolation_data = absl::nullopt; url_loader->TrackLoadUrlCalls(base::BindLambdaForTesting( @@ -894,7 +919,7 @@ })); EXPECT_THAT(ExecuteCommand({ - .url = "http://test-url-example.com", + .url_info = url_info, .url_loader = std::move(url_loader), .isolation_data = IsolationData{IsolationData::DevModeProxy{ .proxy_url = "http://some-testing-proxy-url.com/"}}, @@ -911,9 +936,12 @@ TEST_F(InstallIsolatedAppCommandTest, SetInstalledBundleIsolationDataBeforeUrlLoading) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{"http://test-url-example.com"}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); absl::optional<IsolationData> isolation_data = absl::nullopt; url_loader->TrackLoadUrlCalls(base::BindLambdaForTesting( @@ -926,7 +954,7 @@ EXPECT_THAT( ExecuteCommand({ - .url = "http://test-url-example.com", + .url_info = url_info, .url_loader = std::move(url_loader), .isolation_data = IsolationData{IsolationData::InstalledBundle{ .path = base::FilePath{FILE_PATH_LITERAL( @@ -947,16 +975,17 @@ TEST_F(InstallIsolatedAppCommandMetricsTest, ReportSuccessWhenFinishedSuccessfully) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{"http://test-url-example.com"}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); base::HistogramTester histogram_tester; - EXPECT_THAT(ExecuteCommand(Parameters{ - .url = "http://test-url-example.com", - .url_loader = std::move(url_loader), - }), + EXPECT_THAT(ExecuteCommand(Parameters{.url_info = url_info, + .url_loader = std::move(url_loader)}), IsInstallationOk()); EXPECT_THAT(histogram_tester.GetAllSamples("WebApp.Install.Result"), @@ -964,17 +993,17 @@ } TEST_F(InstallIsolatedAppCommandMetricsTest, ReportErrorWhenUrlLoaderFails) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); url_loader->SetNextLoadUrlResult( - GURL{"http://test-url-example.com"}, + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), WebAppUrlLoader::Result::kFailedErrorPageLoaded); base::HistogramTester histogram_tester; - EXPECT_THAT(ExecuteCommand(Parameters{ - .url = "http://test-url-example.com", - .url_loader = std::move(url_loader), - }), + EXPECT_THAT(ExecuteCommand(Parameters{.url_info = url_info, + .url_loader = std::move(url_loader)}), IsInstallationError()); EXPECT_THAT(histogram_tester.GetAllSamples("WebApp.Install.Result"), @@ -983,12 +1012,15 @@ TEST_F(InstallIsolatedAppCommandMetricsTest, ReportFailureWhenAppIsNotInstallable) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{"http://test-url-example.com"}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); std::unique_ptr<MockDataRetriever> fake_data_retriever = - CreateDefaultDataRetriever("http://test-url-example.com"); + CreateDefaultDataRetriever(url_info.origin().GetURL()); ON_CALL(*fake_data_retriever, CheckInstallabilityAndRetrieveManifest) .WillByDefault( @@ -999,10 +1031,7 @@ base::HistogramTester histogram_tester; EXPECT_THAT(ExecuteCommand( - { - .url = "http://test-url-example.com", - .url_loader = std::move(url_loader), - }, + {.url_info = url_info, .url_loader = std::move(url_loader)}, std::move(fake_data_retriever)), IsInstallationError()); @@ -1011,26 +1040,26 @@ } TEST_F(InstallIsolatedAppCommandMetricsTest, ReportFailureWhenManifestIsNull) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); auto url_loader = std::make_unique<TestWebAppUrlLoader>(); - url_loader->SetNextLoadUrlResult(GURL{"http://test-url-example.com"}, - WebAppUrlLoader::Result::kUrlLoaded); + url_loader->SetNextLoadUrlResult( + url_info.origin().GetURL().Resolve( + ".well-known/_generated_install_page.html"), + WebAppUrlLoader::Result::kUrlLoaded); std::unique_ptr<MockDataRetriever> fake_data_retriever = - CreateDefaultDataRetriever("http://test-url-example.com"); + CreateDefaultDataRetriever(url_info.origin().GetURL()); ON_CALL(*fake_data_retriever, CheckInstallabilityAndRetrieveManifest) .WillByDefault(ReturnManifest( /*manifest=*/nullptr, - CreateDefaultManifestURL("http://test-url-example.com"), + CreateDefaultManifestURL(url_info.origin().GetURL()), /*is_installable=*/false)); base::HistogramTester histogram_tester; EXPECT_THAT(ExecuteCommand( - { - .url = "http://test-url-example.com", - .url_loader = std::move(url_loader), - }, + {.url_info = url_info, .url_loader = std::move(url_loader)}, std::move(fake_data_retriever)), IsInstallationError()); @@ -1040,14 +1069,14 @@ TEST_F(InstallIsolatedAppCommandMetricsTest, ReportFailureWhenManifestIdIsNotEmpty) { + IsolatedWebAppUrlInfo url_info = CreateRandomIsolatedWebAppUrlInfo(); blink::mojom::ManifestPtr manifest = - CreateDefaultManifest("http://manifest-test-url.com"); + CreateDefaultManifest(url_info.origin().GetURL()); manifest->id = u"test manifest id"; base::HistogramTester histogram_tester; - EXPECT_THAT(ExecuteCommandWithManifest("http://manifest-test-url.com", - manifest.Clone()), + EXPECT_THAT(ExecuteCommandWithManifest(url_info, manifest.Clone()), IsInstallationError()); EXPECT_THAT(histogram_tester.GetAllSamples("WebApp.Install.Result"), BucketsAre(base::Bucket(false, 1)));
diff --git a/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line.cc b/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line.cc index 7fecf75..25a0fc1 100644 --- a/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line.cc +++ b/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line.cc
@@ -12,18 +12,22 @@ #include "base/callback.h" #include "base/callback_helpers.h" #include "base/command_line.h" +#include "base/files/file_util.h" #include "base/functional/callback_forward.h" #include "base/functional/callback_helpers.h" #include "base/no_destructor.h" #include "base/strings/strcat.h" #include "base/strings/string_piece.h" +#include "base/types/expected.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/web_applications/commands/install_isolated_app_command.h" +#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h" #include "chrome/browser/web_applications/isolation_data.h" #include "chrome/browser/web_applications/web_app_command_manager.h" #include "chrome/browser/web_applications/web_app_provider.h" #include "chrome/browser/web_applications/web_app_url_loader.h" #include "chrome/common/chrome_switches.h" +#include "components/web_package/signed_web_bundles/signed_web_bundle_id.h" #include "components/webapps/browser/installable/installable_manager.h" #include "content/public/browser/web_contents.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -61,27 +65,23 @@ DCHECK(url.is_valid()); DCHECK(!callback.is_null()); + // TODO(zelin): move random generation up to MaybeInstallAppFromCommandLine() + web_package::SignedWebBundleId random_signed_web_bundle_id = + web_package::SignedWebBundleId::CreateRandomForDevelopment(); + base::expected<IsolatedWebAppUrlInfo, std::string> url_info = + IsolatedWebAppUrlInfo::CreateFromSignedWebBundleId( + random_signed_web_bundle_id); + DCHECK(url_info.has_value()); + provider.command_manager().ScheduleCommand( std::make_unique<InstallIsolatedAppCommand>( - url, isolation_data, CreateWebContents(profile), + url_info.value(), isolation_data, CreateWebContents(profile), std::make_unique<WebAppUrlLoader>(), provider.install_finalizer(), base::BindOnce(&ReportInstallationResult).Then(std::move(callback)))); } -base::OnceClosure& GetNextDoneCallbackInstance() { - static base::NoDestructor<base::OnceClosure> kInstance{base::NullCallback()}; - return *kInstance; -} - -} // namespace - -void SetNextInstallationDoneCallbackForTesting( // IN-TEST - base::OnceClosure done_callback) { - GetNextDoneCallbackInstance() = std::move(done_callback); -} - base::expected<absl::optional<IsolationData>, std::string> -GetIsolationDataFromCommandLine(const base::CommandLine& command_line) { +GetProxyUrlFromCommandLine(const base::CommandLine& command_line) { std::string switch_value = command_line.GetSwitchValueASCII(switches::kInstallIsolatedWebAppFromUrl); @@ -100,6 +100,60 @@ return IsolationData{IsolationData::DevModeProxy{.proxy_url = url.spec()}}; } +base::expected<absl::optional<IsolationData>, std::string> +GetBundlePathFromCommandLine(const base::CommandLine& command_line) { + base::FilePath switch_value = + command_line.GetSwitchValuePath(switches::kInstallIsolatedWebAppFromFile); + + if (switch_value.empty()) { + return absl::nullopt; + } + + base::FilePath absolute_path = base::MakeAbsoluteFilePath(switch_value); + + if (!base::PathExists(absolute_path) || + base::DirectoryExists(absolute_path)) { + return base::unexpected(base::StrCat( + {"Invalid path provided to --", switches::kInstallIsolatedWebAppFromUrl, + " flag: '", absolute_path.AsUTF8Unsafe(), "'"})); + } + + return IsolationData{IsolationData::DevModeBundle{.path = absolute_path}}; +} + +base::OnceClosure& GetNextDoneCallbackInstance() { + static base::NoDestructor<base::OnceClosure> kInstance{base::NullCallback()}; + return *kInstance; +} + +} // namespace + +void SetNextInstallationDoneCallbackForTesting( // IN-TEST + base::OnceClosure done_callback) { + GetNextDoneCallbackInstance() = std::move(done_callback); +} + +base::expected<absl::optional<IsolationData>, std::string> +GetIsolationDataFromCommandLine(const base::CommandLine& command_line) { + base::expected<absl::optional<IsolationData>, std::string> proxy_url = + GetProxyUrlFromCommandLine(command_line); + base::expected<absl::optional<IsolationData>, std::string> bundle_path = + GetBundlePathFromCommandLine(command_line); + + // Return an error if both flags are set. + bool was_proxy_url_set = !proxy_url.has_value() || proxy_url->has_value(); + bool was_bundle_path_set = + !bundle_path.has_value() || bundle_path->has_value(); + if (was_proxy_url_set && was_bundle_path_set) { + return base::unexpected( + base::StrCat({"--", switches::kInstallIsolatedWebAppFromUrl, " and --", + switches::kInstallIsolatedWebAppFromFile, + " cannot both be provided."})); + } + + return was_proxy_url_set ? proxy_url : bundle_path; +} + void MaybeInstallAppFromCommandLine(const base::CommandLine& command_line, Profile& profile) { base::OnceClosure& next_done_callback = GetNextDoneCallbackInstance();
diff --git a/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line_browsertest.cc index 346f478..cbe37a4 100644 --- a/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line_browsertest.cc +++ b/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line_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 <vector> #include "chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line.h" #include "base/command_line.h" @@ -12,12 +13,14 @@ #include "base/test/bind.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h" #include "chrome/browser/web_applications/web_app.h" #include "chrome/browser/web_applications/web_app_helpers.h" #include "chrome/browser/web_applications/web_app_id.h" #include "chrome/browser/web_applications/web_app_provider.h" #include "chrome/browser/web_applications/web_app_registrar.h" #include "chrome/test/base/in_process_browser_test.h" +#include "content/public/common/content_features.h" #include "content/public/test/browser_test.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "url/gurl.h" @@ -30,6 +33,7 @@ : public InProcessBrowserTest { protected: void SetUp() override { + scoped_feature_list_.InitAndEnableFeature(features::kIsolatedWebApps); embedded_test_server()->AddDefaultHandlers( GetChromeTestDataDir().AppendASCII("web_apps/simple_isolated_app")); ASSERT_TRUE(embedded_test_server()->Start()); @@ -63,15 +67,21 @@ private: base::OneShotEvent is_installation_done_; + base::test::ScopedFeatureList scoped_feature_list_; }; +// TODO(zelin): enable this browser test IN_PROC_BROWSER_TEST_F(InstallIsolatedAppFromCommandLineBrowserTest, - AppFromCommandLineIsInstalled) { + DISABLED_AppFromCommandLineIsInstalled) { WaitForInstallation(); - const AppId app_id = GenerateAppId("", GetAppUrl()); + ASSERT_THAT(GetWebAppRegistrar().CountUserInstalledApps(), 1); - ASSERT_THAT(GetWebAppRegistrar().IsInstalled(app_id), IsTrue()); + std::vector<AppId> all_apps = GetWebAppRegistrar().GetAppIds(); + + ASSERT_THAT(all_apps.size(), 1); + + AppId app_id = all_apps.front(); const WebApp* web_app = GetWebAppRegistrar().GetAppById(app_id); EXPECT_THAT(web_app->isolation_data().has_value(), IsTrue());
diff --git a/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line_unittest.cc index a6a36c35..0dea4f4 100644 --- a/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line_unittest.cc +++ b/chrome/browser/web_applications/isolated_web_apps/install_isolated_app_from_command_line_unittest.cc
@@ -6,6 +6,9 @@ #include "base/callback.h" #include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/path_service.h" #include "base/strings/string_piece.h" #include "base/strings/string_piece_forward.h" #include "base/test/bind.h" @@ -67,10 +70,62 @@ return true; } -base::CommandLine CreateDefaultCommandLine(base::StringPiece flag_value) { +MATCHER_P(IsDevModeBundle, + bundle_path, + std::string(negation ? "isn't " : "Dev Mode bundle at: \"") + + bundle_path.AsUTF8Unsafe() + '"') { + if (!arg.has_value() || !arg.value().has_value()) { + DescribeOptionalIsolationData(result_listener, arg); + return false; + } + const IsolationData::DevModeBundle* bundle = + absl::get_if<IsolationData::DevModeBundle>(&arg.value().value().content); + if (bundle == nullptr || bundle->path != bundle_path) { + DescribeOptionalIsolationData(result_listener, arg); + return false; + } + return true; +} + +// Sets the current working directory to a location that contains a file. +// The working directory is restored when the object is destroyed. +class ScopedWorkingDirectoryWithFile { + public: + ScopedWorkingDirectoryWithFile() { + // Rather than creating a temporary directory and file, just use the + // current binary, which we know will always exist. + CHECK(base::GetCurrentDirectory(&original_working_directory_)); + CHECK(base::PathService::Get(base::FILE_EXE, &executable_path_)); + CHECK(base::SetCurrentDirectory(executable_path_.DirName())); + } + + ~ScopedWorkingDirectoryWithFile() { + CHECK(base::SetCurrentDirectory(original_working_directory_)); + } + + base::FilePath existing_file_path() { return executable_path_; } + + base::FilePath existing_file_name() { return executable_path_.BaseName(); } + + base::FilePath directory() { return executable_path_.DirName(); } + + private: + base::FilePath original_working_directory_; + base::FilePath executable_path_; +}; + +base::CommandLine CreateCommandLine( + absl::optional<base::StringPiece> proxy_flag_value, + absl::optional<base::FilePath> bundle_flag_value) { base::CommandLine command_line{base::CommandLine::NoProgram::NO_PROGRAM}; - command_line.AppendSwitchASCII("install-isolated-web-app-from-url", - flag_value); + if (proxy_flag_value.has_value()) { + command_line.AppendSwitchASCII("install-isolated-web-app-from-url", + proxy_flag_value.value()); + } + if (bundle_flag_value.has_value()) { + command_line.AppendSwitchPath("install-isolated-web-app-from-file", + bundle_flag_value.value()); + } return command_line; } @@ -80,36 +135,138 @@ }; TEST_F(InstallIsolatedAppFromCommandLineFlagTest, - InstallsAppFromCommandLineFlag) { + NoInstallationWhenProxyFlagAbsentAndBundleFlagAbsent) { EXPECT_THAT(GetIsolationDataFromCommandLine( - CreateDefaultCommandLine("http://example.com")), - IsDevModeProxy("http://example.com")); -} - -TEST_F(InstallIsolatedAppFromCommandLineFlagTest, - InstallsDifferentAppFromCommandLineFlag) { - EXPECT_THAT(GetIsolationDataFromCommandLine( - CreateDefaultCommandLine("http://different-example.com")), - IsDevModeProxy("http://different-example.com")); -} - -TEST_F(InstallIsolatedAppFromCommandLineFlagTest, NoneForInvalidUrls) { - EXPECT_THAT( - GetIsolationDataFromCommandLine(CreateDefaultCommandLine("badurl")), - HasErrorWithSubstr("Invalid URL")); -} - -TEST_F(InstallIsolatedAppFromCommandLineFlagTest, - DoNotCallInstallationWhenFlagIsEmpty) { - EXPECT_THAT(GetIsolationDataFromCommandLine(CreateDefaultCommandLine("")), + CreateCommandLine(absl::nullopt, absl::nullopt)), HasNoValue()); } TEST_F(InstallIsolatedAppFromCommandLineFlagTest, - DoNotCallInstallationWhenFlagIsNotPresent) { - const base::CommandLine command_line{ - base::CommandLine::NoProgram::NO_PROGRAM}; - EXPECT_THAT(GetIsolationDataFromCommandLine(command_line), HasNoValue()); + NoInstallationWhenProxyFlagAbsentAndBundleFlagEmpty) { + EXPECT_THAT(GetIsolationDataFromCommandLine(CreateCommandLine( + absl::nullopt, base::FilePath::FromUTF8Unsafe(""))), + HasNoValue()); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + ErrorWhenProxyFlagAbsentAndBundleFlagInvalid) { + EXPECT_THAT(GetIsolationDataFromCommandLine(CreateCommandLine( + absl::nullopt, + base::FilePath::FromUTF8Unsafe("does_not_exist.wbn)"))), + HasErrorWithSubstr("Invalid path provided")); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + ErrorWhenProxyFlagAbsentAndBundleFlagIsDirectory) { + ScopedWorkingDirectoryWithFile cwd; + EXPECT_THAT(GetIsolationDataFromCommandLine( + CreateCommandLine(absl::nullopt, cwd.directory())), + HasErrorWithSubstr("Invalid path provided")); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + InstallsAppWhenProxyFlagAbsentAndBundleFlagValid) { + ScopedWorkingDirectoryWithFile cwd; + EXPECT_THAT(GetIsolationDataFromCommandLine( + CreateCommandLine(absl::nullopt, cwd.existing_file_name())), + IsDevModeBundle(cwd.existing_file_path())); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + InstallsAppWhenProxyFlagAbsentAndBundleFlagValidAndAbsolute) { + ScopedWorkingDirectoryWithFile cwd; + EXPECT_THAT(GetIsolationDataFromCommandLine( + CreateCommandLine(absl::nullopt, cwd.existing_file_path())), + IsDevModeBundle(cwd.existing_file_path())); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + NoInstallationWhenProxyFlagEmptyAndBundleFlagAbsent) { + EXPECT_THAT( + GetIsolationDataFromCommandLine(CreateCommandLine("", absl::nullopt)), + HasNoValue()); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + NoInstallationWhenProxyFlagEmptyAndBundleFlagEmpty) { + EXPECT_THAT(GetIsolationDataFromCommandLine( + CreateCommandLine("", base::FilePath::FromUTF8Unsafe(""))), + HasNoValue()); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + ErrorWhenProxyFlagEmptyAndBundleFlagInvalid) { + EXPECT_THAT(GetIsolationDataFromCommandLine(CreateCommandLine( + "", base::FilePath::FromUTF8Unsafe("does_not_exist.wbn"))), + HasErrorWithSubstr("Invalid path provided")); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + InstallsAppWhenProxyFlagEmptyAndBundleFlagValid) { + ScopedWorkingDirectoryWithFile cwd; + EXPECT_THAT(GetIsolationDataFromCommandLine( + CreateCommandLine("", cwd.existing_file_name())), + IsDevModeBundle(cwd.existing_file_path())); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + ErrorWhenProxyFlagInvalidAndBundleFlagAbsent) { + EXPECT_THAT(GetIsolationDataFromCommandLine( + CreateCommandLine("invalid", absl::nullopt)), + HasErrorWithSubstr("Invalid URL")); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + ErrorWhenProxyFlagInvalidAndBundleFlagEmpty) { + EXPECT_THAT(GetIsolationDataFromCommandLine(CreateCommandLine( + "invalid", base::FilePath::FromUTF8Unsafe(""))), + HasErrorWithSubstr("Invalid URL")); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + ErrorWhenProxyFlagInvalidAndBundleFlagInvalid) { + EXPECT_THAT( + GetIsolationDataFromCommandLine(CreateCommandLine( + "invalid", base::FilePath::FromUTF8Unsafe("does_not_exist.wbn"))), + HasErrorWithSubstr("cannot both be provided")); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + ErrorWhenProxyFlagInvalidAndBundleFlagValid) { + ScopedWorkingDirectoryWithFile cwd; + EXPECT_THAT(GetIsolationDataFromCommandLine( + CreateCommandLine("invalid", cwd.existing_file_name())), + HasErrorWithSubstr("cannot both be provided")); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + InstallsAppWhenProxyFlagValidAndBundleFlagAbsent) { + EXPECT_THAT(GetIsolationDataFromCommandLine( + CreateCommandLine("http://example.com", absl::nullopt)), + IsDevModeProxy("http://example.com")); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + InstallsAppWhenProxyFlagValidAndBundleFlagEmpty) { + EXPECT_THAT(GetIsolationDataFromCommandLine(CreateCommandLine( + "http://example.com", base::FilePath::FromUTF8Unsafe(""))), + IsDevModeProxy("http://example.com")); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + ErrorWhenProxyFlagValidAndBundleFlagInvalid) { + EXPECT_THAT(GetIsolationDataFromCommandLine(CreateCommandLine( + "http://example.com", + base::FilePath::FromUTF8Unsafe("does_not_exist.wbn"))), + HasErrorWithSubstr("cannot both be provided")); +} + +TEST_F(InstallIsolatedAppFromCommandLineFlagTest, + ErrorWhenProxyFlagValidAndBundleFlagValid) { + ScopedWorkingDirectoryWithFile cwd; + EXPECT_THAT(GetIsolationDataFromCommandLine(CreateCommandLine( + "http://example.com", cwd.existing_file_name())), + HasErrorWithSubstr("cannot both be provided")); } } // namespace
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory.cc index cee7da70..e1537c4 100644 --- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory.cc +++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory.cc
@@ -42,6 +42,7 @@ #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/resource_request.h" #include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/mojom/fetch_api.mojom.h" #include "services/network/public/mojom/url_loader.mojom.h" #include "services/network/public/mojom/url_loader_completion_status.mojom.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" @@ -454,8 +455,11 @@ GURL proxy_url = proxy_url_base.Resolve(resource_request.url.path()); // Create a new ResourceRequest with the proxy URL. - network::ResourceRequest proxy_request(resource_request); + network::ResourceRequest proxy_request; proxy_request.url = proxy_url; + proxy_request.method = net::HttpRequestHeaders::kGetMethod; + // Don't send cookies or HTTP authentication to the proxy server. + proxy_request.credentials_mode = network::mojom::CredentialsMode::kOmit; content::StoragePartition* storage_partition = profile_->GetStoragePartition( url_info.storage_partition_config(profile_), /*can_create=*/false);
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_unittest.cc index 4f818ec..702a62d 100644 --- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_unittest.cc +++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_loader_factory_unittest.cc
@@ -33,6 +33,7 @@ #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h" #include "services/network/public/cpp/resource_request.h" +#include "services/network/public/mojom/fetch_api.mojom.h" #include "services/network/public/mojom/url_response_head.mojom.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -103,18 +104,25 @@ : interceptor_(base::BindRepeating(&ScopedUrlHandler::Intercept, base::Unretained(this))) {} - absl::optional<GURL> intercepted_url() const { return intercepted_url_; } + absl::optional<network::ResourceRequest> request() const { return request_; } + + absl::optional<GURL> intercepted_url() const { + if (request_.has_value()) { + return request_->url; + } + return absl::nullopt; + } private: bool Intercept(content::URLLoaderInterceptor::RequestParams* params) { - intercepted_url_ = params->url_request.url; + request_ = params->url_request; content::URLLoaderInterceptor::WriteResponse( "HTTP/1.1 200 OK\n", "test body", params->client.get()); return true; } content::URLLoaderInterceptor interceptor_; - absl::optional<GURL> intercepted_url_; + absl::optional<network::ResourceRequest> request_; }; } // namespace @@ -505,6 +513,24 @@ Eq(GURL("http://example.com/foo/bar.html"))); } +TEST_F(IsolatedWebAppURLLoaderFactoryTest, ProxyUrlRemovesOriginalRequestData) { + RegisterWebApp(CreateIsolatedWebApp(kAppStartUrl, + IsolationData{IsolationData::DevModeProxy{ + .proxy_url = "http://example.com"}})); + + CreateFactory(); + + auto request = std::make_unique<network::ResourceRequest>(); + request->url = GURL("isolated-app://" + kWebBundleId + "/foo/bar.html"); + CreateLoaderAndRun(std::move(request)); + + ASSERT_THAT(url_handler().intercepted_url(), + Eq(GURL("http://example.com/foo/bar.html"))); + EXPECT_THAT(url_handler().request()->credentials_mode, + Eq(network::mojom::CredentialsMode::kOmit)); + EXPECT_THAT(url_handler().request()->request_initiator, Eq(absl::nullopt)); +} + TEST_F(IsolatedWebAppURLLoaderFactoryTest, DoNotReturnGeneratedPageWhenNotInstallingApplication) { RegisterWebApp(CreateIsolatedWebApp(kAppStartUrl,
diff --git a/chrome/browser/web_applications/manifest_update_manager.cc b/chrome/browser/web_applications/manifest_update_manager.cc index df0d3bf..f13cad22 100644 --- a/chrome/browser/web_applications/manifest_update_manager.cc +++ b/chrome/browser/web_applications/manifest_update_manager.cc
@@ -16,6 +16,8 @@ #include "base/memory/weak_ptr.h" #include "base/metrics/histogram_functions.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" +#include "chrome/browser/web_applications/manifest_update_task.h" +#include "chrome/browser/web_applications/manifest_update_utils.h" #include "chrome/browser/web_applications/os_integration/os_integration_manager.h" #include "chrome/browser/web_applications/web_app_constants.h" #include "chrome/browser/web_applications/web_app_system_web_app_delegate_map_utils.h"
diff --git a/chrome/browser/web_applications/manifest_update_manager.h b/chrome/browser/web_applications/manifest_update_manager.h index 2c2adf4..93f7356 100644 --- a/chrome/browser/web_applications/manifest_update_manager.h +++ b/chrome/browser/web_applications/manifest_update_manager.h
@@ -16,6 +16,7 @@ #include "chrome/browser/ash/system_web_apps/types/system_web_app_delegate_map.h" #include "chrome/browser/web_applications/app_registrar_observer.h" #include "chrome/browser/web_applications/manifest_update_task.h" +#include "chrome/browser/web_applications/manifest_update_utils.h" #include "chrome/browser/web_applications/web_app_id.h" #include "chrome/browser/web_applications/web_app_install_manager.h" #include "chrome/browser/web_applications/web_app_install_manager_observer.h"
diff --git a/chrome/browser/web_applications/manifest_update_task.cc b/chrome/browser/web_applications/manifest_update_task.cc index a5e0c65d..30f64a4 100644 --- a/chrome/browser/web_applications/manifest_update_task.cc +++ b/chrome/browser/web_applications/manifest_update_task.cc
@@ -125,16 +125,6 @@ } // Some apps, such as pre-installed apps, have been vetted and are therefore -// considered safe and permitted to update their names. -bool AllowUnpromptedNameUpdate(const AppId& app_id, - const WebAppRegistrar& registrar) { - const WebApp* web_app = registrar.GetAppById(app_id); - if (!web_app) - return false; - return CanWebAppUpdateIdentity(web_app); -} - -// Some apps, such as pre-installed apps, have been vetted and are therefore // considered safe and permitted to update their icon. For others, the feature // flag needs to be on. bool AllowUnpromptedIconUpdate(const AppId& app_id, @@ -146,30 +136,6 @@ base::FeatureList::IsEnabled(features::kWebAppManifestIconUpdating); } -bool NeedsAppIdentityUpdateDialog(bool title_changing, - bool icons_changing, - const AppId& app_id, - const WebAppRegistrar& registrar) { - // Shortcut apps can trigger the update check (https://crbug.com/1366600) on - // subsequent runs of the app, if the user changed the title of the app when - // creating the shortcut. But we should never run the App Identity dialog for - // shortcut apps. Also, ideally we should just use IsShortcutApp here instead - // of checking the install source, but as per https://crbug.com/1368592 there - // is a bug with that where it returns the wrong thing for Shortcut apps that - // specify `scope`. - if (registrar.IsShortcutApp(app_id) || - registrar.GetAppInstallSourceForMetrics(app_id) == - webapps::WebappInstallSource::MENU_CREATE_SHORTCUT) { - return false; - } - - if (title_changing && !AllowUnpromptedNameUpdate(app_id, registrar)) - return true; - if (icons_changing && !AllowUnpromptedIconUpdate(app_id, registrar)) - return true; - return false; -} - } // namespace IconDiff HaveIconBitmapsChanged( @@ -281,7 +247,7 @@ DestroySelf(ManifestUpdateResult::kWebContentsDestroyed); return; } - stage_ = Stage::kPendingInstallableData; + stage_ = ManifestUpdateStage::kPendingInstallableData; webapps::InstallableParams params; params.valid_primary_icon = true; params.valid_manifest = true; @@ -305,7 +271,7 @@ DestroySelf(ManifestUpdateResult::kWebContentsDestroyed); return; } - DCHECK_EQ(stage_, Stage::kPendingInstallableData); + DCHECK_EQ(stage_, ManifestUpdateStage::kPendingInstallableData); if (!data.NoBlockingErrors()) { DestroySelf(ManifestUpdateResult::kAppNotEligible); @@ -442,9 +408,9 @@ DestroySelf(ManifestUpdateResult::kWebContentsDestroyed); return; } - DCHECK(stage_ == Stage::kPendingInstallableData || - stage_ == Stage::kPendingAppIdentityCheck); - stage_ = Stage::kPendingWindowsClosed; + DCHECK(stage_ == ManifestUpdateStage::kPendingInstallableData || + stage_ == ManifestUpdateStage::kPendingAppIdentityCheck); + stage_ = ManifestUpdateStage::kPendingWindowsClosed; Profile* profile = Profile::FromBrowserContext(web_contents_.get()->GetBrowserContext()); @@ -475,8 +441,8 @@ DestroySelf(ManifestUpdateResult::kWebContentsDestroyed); return; } - DCHECK_EQ(stage_, Stage::kPendingInstallableData); - stage_ = Stage::kPendingIconDownload; + DCHECK_EQ(stage_, ManifestUpdateStage::kPendingInstallableData); + stage_ = ManifestUpdateStage::kPendingIconDownload; DCHECK(install_info_.has_value()); base::flat_set<GURL> icon_urls = GetValidIconUrlsToDownload(*install_info_); @@ -500,7 +466,7 @@ DestroySelf(ManifestUpdateResult::kWebContentsDestroyed); return; } - DCHECK_EQ(stage_, Stage::kPendingIconDownload); + DCHECK_EQ(stage_, ManifestUpdateStage::kPendingIconDownload); // TODO(crbug.com/1238622): Report `result` and `icons_http_results` in // internals. @@ -516,7 +482,7 @@ RecordDownloadedIconsHttpResultsCodeClass( "WebApp.Icon.HttpStatusCodeClassOnUpdate", result, icons_http_results); - stage_ = Stage::kPendingIconReadFromDisk; + stage_ = ManifestUpdateStage::kPendingIconReadFromDisk; icon_manager_.ReadAllIcons( app_id_, base::BindOnce(&ManifestUpdateTask::OnAllIconsRead, AsWeakPtr(), std::move(icons_map))); @@ -531,7 +497,7 @@ DestroySelf(ManifestUpdateResult::kWebContentsDestroyed); return; } - DCHECK_EQ(stage_, Stage::kPendingIconReadFromDisk); + DCHECK_EQ(stage_, ManifestUpdateStage::kPendingIconReadFromDisk); if (disk_icon_bitmaps.empty()) { DestroySelf(ManifestUpdateResult::kIconReadFromDiskFailed); @@ -539,7 +505,7 @@ } DCHECK(install_info_.has_value()); - stage_ = Stage::kPendingAppIdentityCheck; + stage_ = ManifestUpdateStage::kPendingAppIdentityCheck; // These calls populate the |install_info_| with all icon bitmap // data. If this data does not match what we already have on disk, then an @@ -677,7 +643,7 @@ DestroySelf(ManifestUpdateResult::kWebContentsDestroyed); return; } - DCHECK_EQ(stage_, Stage::kPendingAppIdentityCheck); + DCHECK_EQ(stage_, ManifestUpdateStage::kPendingAppIdentityCheck); app_identity_update_allowed_ = app_identity_update_allowed == AppIdentityUpdate::kAllowed; @@ -716,7 +682,7 @@ DestroySelf(ManifestUpdateResult::kWebContentsDestroyed); return; } - DCHECK_EQ(stage_, Stage::kPendingAppIdentityCheck); + DCHECK_EQ(stage_, ManifestUpdateStage::kPendingAppIdentityCheck); DCHECK(install_info_.has_value()); @@ -765,8 +731,8 @@ } void ManifestUpdateTask::NoManifestUpdateRequired() { - DCHECK_EQ(stage_, Stage::kPendingAppIdentityCheck); - stage_ = Stage::kPendingAssociationsUpdate; + DCHECK_EQ(stage_, ManifestUpdateStage::kPendingAppIdentityCheck); + stage_ = ManifestUpdateStage::kPendingAssociationsUpdate; if (!IsUpdateNeededForWebAppOriginAssociations()) { DestroySelf(ManifestUpdateResult::kAppUpToDate); @@ -780,13 +746,13 @@ } void ManifestUpdateTask::OnWebAppOriginAssociationsUpdated(bool success) { - DCHECK_EQ(stage_, Stage::kPendingAssociationsUpdate); + DCHECK_EQ(stage_, ManifestUpdateStage::kPendingAssociationsUpdate); success ? DestroySelf(ManifestUpdateResult::kAppAssociationsUpdated) : DestroySelf(ManifestUpdateResult::kAppAssociationsUpdateFailed); } void ManifestUpdateTask::OnAllAppWindowsClosed() { - DCHECK_EQ(stage_, Stage::kPendingWindowsClosed); + DCHECK_EQ(stage_, ManifestUpdateStage::kPendingWindowsClosed); DCHECK(install_info_.has_value()); @@ -801,7 +767,7 @@ // Preserve the user's choice of form factor to open the app with. install_info_->user_display_mode = registrar_.GetAppUserDisplayMode(app_id_); - stage_ = Stage::kPendingInstallation; + stage_ = ManifestUpdateStage::kPendingInstallation; install_finalizer_.FinalizeUpdate( *install_info_, @@ -811,7 +777,7 @@ void ManifestUpdateTask::OnInstallationComplete(const AppId& app_id, webapps::InstallResultCode code, OsHooksErrors os_hooks_errors) { - DCHECK_EQ(stage_, Stage::kPendingInstallation); + DCHECK_EQ(stage_, ManifestUpdateStage::kPendingInstallation); if (!IsSuccess(code)) { DestroySelf(ManifestUpdateResult::kAppUpdateFailed);
diff --git a/chrome/browser/web_applications/manifest_update_task.h b/chrome/browser/web_applications/manifest_update_task.h index bd8432d3..5b1c783 100644 --- a/chrome/browser/web_applications/manifest_update_task.h +++ b/chrome/browser/web_applications/manifest_update_task.h
@@ -9,6 +9,7 @@ #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h" +#include "chrome/browser/web_applications/manifest_update_utils.h" #include "chrome/browser/web_applications/web_app_icon_downloader.h" #include "chrome/browser/web_applications/web_app_icon_manager.h" #include "chrome/browser/web_applications/web_app_id.h" @@ -38,26 +39,6 @@ struct IconDiff; -// This enum is recorded by UMA, the numeric values must not change. -enum ManifestUpdateResult { - kNoAppInScope = 0, - kThrottled = 1, - kWebContentsDestroyed = 2, - kAppUninstalling = 3, - kAppIsPlaceholder = 4, - kAppUpToDate = 5, - kAppNotEligible = 6, - kAppUpdateFailed = 7, - kAppUpdated = 8, - kAppIsSystemWebApp = 9, - kIconDownloadFailed = 10, - kIconReadFromDiskFailed = 11, - kAppIdMismatch = 12, - kAppAssociationsUpdateFailed = 13, - kAppAssociationsUpdated = 14, - kMaxValue = kAppAssociationsUpdated, -}; - enum IconDiffResult : uint32_t { NO_CHANGE_DETECTED = 0, @@ -184,16 +165,6 @@ void Start(); private: - enum class Stage { - kPendingInstallableData, - kPendingIconDownload, - kPendingIconReadFromDisk, - kPendingAppIdentityCheck, - kPendingWindowsClosed, - kPendingMaybeReadExistingIcons, - kPendingInstallation, - kPendingAssociationsUpdate, - }; // We perform this check for the following Stages: // kPendingInstallableData // kPendingIconDownload @@ -237,7 +208,7 @@ OsIntegrationManager& os_integration_manager_; raw_ptr<WebAppSyncBridge> sync_bridge_ = nullptr; - Stage stage_; + ManifestUpdateStage stage_; absl::optional<WebAppInstallInfo> install_info_; absl::optional<WebAppIconDownloader> icon_downloader_;
diff --git a/chrome/browser/web_applications/manifest_update_utils.cc b/chrome/browser/web_applications/manifest_update_utils.cc new file mode 100644 index 0000000..84251f6e --- /dev/null +++ b/chrome/browser/web_applications/manifest_update_utils.cc
@@ -0,0 +1,225 @@ + +// Copyright 2022 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/web_applications/manifest_update_utils.h" + +#include <ostream> +#include <string> + +#include "base/feature_list.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/web_applications/web_app.h" +#include "chrome/browser/web_applications/web_app_id.h" +#include "chrome/browser/web_applications/web_app_install_utils.h" +#include "chrome/browser/web_applications/web_app_registrar.h" +#include "chrome/common/chrome_features.h" +#include "third_party/blink/public/common/features.h" + +namespace web_app { + +namespace { + +// Some apps, such as pre-installed apps, have been vetted and are therefore +// considered safe and permitted to update their icon. For others, the +// feature flag needs to be on. +bool AllowUnpromptedIconUpdate(const AppId& app_id, + const WebAppRegistrar& registrar) { + const WebApp* web_app = registrar.GetAppById(app_id); + if (!web_app) + return false; + return CanWebAppUpdateIdentity(web_app) || + base::FeatureList::IsEnabled(features::kWebAppManifestIconUpdating); +} + +} // namespace + +std::ostream& operator<<(std::ostream& os, ManifestUpdateResult result) { + switch (result) { + case ManifestUpdateResult::kNoAppInScope: + return os << "kNoAppInScope"; + case ManifestUpdateResult::kThrottled: + return os << "kThrottled"; + case ManifestUpdateResult::kWebContentsDestroyed: + return os << "kWebContentsDestroyed"; + case ManifestUpdateResult::kAppUninstalling: + return os << "kAppUninstalling"; + case ManifestUpdateResult::kAppIsPlaceholder: + return os << "kAppIsPlaceholder"; + case ManifestUpdateResult::kAppUpToDate: + return os << "kAppUpToDate"; + case ManifestUpdateResult::kAppNotEligible: + return os << "kAppNotEligible"; + case ManifestUpdateResult::kAppUpdateFailed: + return os << "kAppUpdateFailed"; + case ManifestUpdateResult::kAppUpdated: + return os << "kAppUpdated"; + case ManifestUpdateResult::kAppIsSystemWebApp: + return os << "kAppIsSystemWebApp"; + case ManifestUpdateResult::kIconDownloadFailed: + return os << "kIconDownloadFailed"; + case ManifestUpdateResult::kIconReadFromDiskFailed: + return os << "kIconReadFromDiskFailed"; + case ManifestUpdateResult::kAppIdMismatch: + return os << "kAppIdMismatch"; + case ManifestUpdateResult::kAppAssociationsUpdateFailed: + return os << "kAppAssociationsUpdateFailed"; + case ManifestUpdateResult::kAppAssociationsUpdated: + return os << "kAppAssociationsUpdated"; + } +} + +std::ostream& operator<<(std::ostream& os, ManifestUpdateStage stage) { + switch (stage) { + case ManifestUpdateStage::kPendingInstallableData: + return os << "kPendingInstallableData"; + case ManifestUpdateStage::kPendingIconDownload: + return os << "kPendingIconDownload"; + case ManifestUpdateStage::kPendingIconReadFromDisk: + return os << "kPendingIconReadFromDisk"; + case ManifestUpdateStage::kPendingAppIdentityCheck: + return os << "kPendingAppIdentityCheck"; + case ManifestUpdateStage::kPendingMaybeReadExistingIcons: + return os << "kPendingMaybeReadExistingIcons"; + case ManifestUpdateStage::kPendingAssociationsUpdate: + return os << "kPendingAssociationsUpdate"; + case ManifestUpdateStage::kPendingWindowsClosed: + return os << "kPendingWindowsClosed"; + case ManifestUpdateStage::kPendingInstallation: + return os << "kPendingInstallation"; + } +} + +bool AllowUnpromptedNameUpdate(const AppId& app_id, + const WebAppRegistrar& registrar) { + const WebApp* web_app = registrar.GetAppById(app_id); + if (!web_app) + return false; + return CanWebAppUpdateIdentity(web_app); +} + +bool NeedsAppIdentityUpdateDialog(bool title_changing, + bool icons_changing, + const AppId& app_id, + const WebAppRegistrar& registrar) { + // Shortcut apps can trigger the update check (https://crbug.com/1366600) + // on subsequent runs of the app, if the user changed the title of the app + // when creating the shortcut. But we should never run the App Identity dialog + // for shortcut apps. Also, ideally we should just use IsShortcutApp here + // instead of checking the install source, but as per + // https://crbug.com/1368592 there is a bug with that where it returns the + // wrong thing for Shortcut apps that specify `scope`. + if (registrar.IsShortcutApp(app_id) || + registrar.GetAppInstallSourceForMetrics(app_id) == + webapps::WebappInstallSource::MENU_CREATE_SHORTCUT) { + return false; + } + if (title_changing && !AllowUnpromptedNameUpdate(app_id, registrar)) + return true; + if (icons_changing && !AllowUnpromptedIconUpdate(app_id, registrar)) + return true; + return false; +} + +bool IsUpdateNeededForManifest(const AppId& app_id, + const WebAppInstallInfo& install_info, + const WebAppRegistrar& registrar) { + const WebApp* app = registrar.GetAppById(app_id); + DCHECK(app); + + // TODO(crbug.com/1259777): Check whether translations have been updated. + bool title_changing = + install_info.title != base::UTF8ToUTF16(app->untranslated_name()); + bool icons_changing = install_info.manifest_icons != app->manifest_icons(); + if (!NeedsAppIdentityUpdateDialog(title_changing, icons_changing, app_id, + registrar)) { + if (title_changing && AllowUnpromptedNameUpdate(app_id, registrar)) { + return true; + } + if (icons_changing && AllowUnpromptedIconUpdate(app_id, registrar)) { + return true; + } + } + + // Allows updating start_url and manifest_id. Both fields are allowed to + // change as long as the app_id generated from them doesn't change. + { + if (install_info.manifest_id != app->manifest_id()) + return true; + if (install_info.start_url != app->start_url()) + return true; + } + + if (install_info.theme_color != app->theme_color()) + return true; + + if (install_info.scope != app->scope()) + return true; + + if (install_info.display_mode != app->display_mode()) + return true; + + if (install_info.display_override != app->display_mode_override()) + return true; + + if (install_info.shortcuts_menu_item_infos != + app->shortcuts_menu_item_infos()) { + return true; + } + + if (install_info.share_target != app->share_target()) + return true; + + if (install_info.protocol_handlers != app->protocol_handlers()) + return true; + + if (install_info.url_handlers != app->url_handlers()) + return true; + + if (base::FeatureList::IsEnabled( + blink::features::kWebAppManifestLockScreen) && + install_info.lock_screen_start_url != app->lock_screen_start_url()) { + return true; + } + + if (install_info.note_taking_new_note_url != + app->note_taking_new_note_url()) { + return true; + } + + if (install_info.capture_links != app->capture_links()) + return true; + + if (app->file_handlers() != install_info.file_handlers) + return true; + + if (install_info.background_color != app->background_color()) + return true; + + if (install_info.dark_mode_theme_color != app->dark_mode_theme_color()) { + return true; + } + + if (install_info.dark_mode_background_color != + app->dark_mode_background_color()) { + return true; + } + + if (install_info.manifest_url != app->manifest_url()) + return true; + + if (install_info.launch_handler != app->launch_handler()) + return true; + + if (install_info.permissions_policy != app->permissions_policy()) + return true; + + // TODO(crbug.com/897314): Check changes to tab_strip field once icons are + // stored. + // TODO(crbug.com/1212849): Handle changes to is_storage_isolated. + // TODO(crbug.com/926083): Check more manifest fields. + return false; +} + +} // namespace web_app
diff --git a/chrome/browser/web_applications/manifest_update_utils.h b/chrome/browser/web_applications/manifest_update_utils.h new file mode 100644 index 0000000..804ec06 --- /dev/null +++ b/chrome/browser/web_applications/manifest_update_utils.h
@@ -0,0 +1,71 @@ +// Copyright 2022 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_WEB_APPLICATIONS_MANIFEST_UPDATE_UTILS_H_ +#define CHROME_BROWSER_WEB_APPLICATIONS_MANIFEST_UPDATE_UTILS_H_ + +#include <iosfwd> +#include <string> + +#include "chrome/browser/web_applications/web_app_id.h" +#include "chrome/browser/web_applications/web_app_install_info.h" + +namespace web_app { + +class WebAppRegistrar; + +// This enum is recorded by UMA, the numeric values must not change. +enum ManifestUpdateResult { + kNoAppInScope = 0, + kThrottled = 1, + kWebContentsDestroyed = 2, + kAppUninstalling = 3, + kAppIsPlaceholder = 4, + kAppUpToDate = 5, + kAppNotEligible = 6, + kAppUpdateFailed = 7, + kAppUpdated = 8, + kAppIsSystemWebApp = 9, + kIconDownloadFailed = 10, + kIconReadFromDiskFailed = 11, + kAppIdMismatch = 12, + kAppAssociationsUpdateFailed = 13, + kAppAssociationsUpdated = 14, + kMaxValue = kAppAssociationsUpdated, +}; + +std::ostream& operator<<(std::ostream& os, ManifestUpdateResult result); + +enum ManifestUpdateStage { + kPendingInstallableData = 0, + kPendingIconDownload = 1, + kPendingIconReadFromDisk = 2, + kPendingAppIdentityCheck = 3, + kPendingMaybeReadExistingIcons = 4, + kPendingAssociationsUpdate = 5, + kPendingWindowsClosed = 6, + kPendingInstallation = 7, +}; + +std::ostream& operator<<(std::ostream& os, ManifestUpdateStage stage); + +// Some apps, such as pre-installed apps, have been vetted and are therefore +// considered safe and permitted to update their names. +bool AllowUnpromptedNameUpdate(const AppId& app_id, + const WebAppRegistrar& registrar); + +bool NeedsAppIdentityUpdateDialog(bool title_changing, + bool icons_changing, + const AppId& app_id, + const WebAppRegistrar& registrar); + +// Checks if a manifest update is required by reading the web_app fields and +// comparing it with the passed install_info. +bool IsUpdateNeededForManifest(const AppId& app_id, + const WebAppInstallInfo& install_info, + const WebAppRegistrar& registrar); + +} // namespace web_app + +#endif // CHROME_BROWSER_WEB_APPLICATIONS_MANIFEST_UPDATE_UTILS_H_
diff --git a/chrome/browser/webauthn/chrome_webauthn_autofill_interactive_uitest.cc b/chrome/browser/webauthn/chrome_webauthn_autofill_interactive_uitest.cc new file mode 100644 index 0000000..0721f39 --- /dev/null +++ b/chrome/browser/webauthn/chrome_webauthn_autofill_interactive_uitest.cc
@@ -0,0 +1,316 @@ +// Copyright 2022 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 <vector> + +#include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/run_loop.h" +#include "base/test/scoped_feature_list.h" +#include "build/build_config.h" +#include "chrome/browser/password_manager/password_store_factory.h" +#include "chrome/browser/ssl/cert_verifier_browser_test.h" +#include "chrome/browser/ui/autofill/autofill_popup_controller_impl.h" +#include "chrome/browser/ui/autofill/chrome_autofill_client.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/autofill/core/browser/ui/popup_item_ids.h" +#include "components/autofill/core/browser/ui/popup_types.h" +#include "components/network_session_configurator/common/network_switches.h" +#include "components/password_manager/core/browser/password_store_interface.h" +#include "components/password_manager/core/browser/password_ui_utils.h" +#include "content/public/browser/authenticator_environment.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/common/content_features.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "device/fido/fido_transport_protocol.h" +#include "device/fido/virtual_ctap2_device.h" +#include "device/fido/virtual_fido_device_factory.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "ui/base/l10n/l10n_util.h" +#include "url/gurl.h" + +#if BUILDFLAG(IS_WIN) +#include "device/fido/win/fake_webauthn_api.h" +#endif // BUILDFLAG(IS_WIN) + +namespace { + +static constexpr uint8_t kCredentialID[] = {1, 2, 3, 4}; + +static constexpr char kConditionalUIRequest[] = R"((() => { +window.requestAbortController = new AbortController(); +navigator.credentials.get({ + signal: window.requestAbortController.signal, + mediation: 'conditional', + publicKey: { + challenge: new Uint8Array([1,2,3,4]), + timeout: 10000, + allowCredentials: [], + }}).then(c => window.domAutomationController.send('webauthn: OK'), + e => window.domAutomationController.send('error ' + e)); +})())"; + +// Autofill integration tests. This file contains end-to-end tests for +// integration between WebAuthn and Autofill. These tests are sensitive to focus +// changes, so they are interactive UI tests. + +// Base class for autofill integration tests, contains the actual test code but +// no setup. +class WebAuthnAutofillIntegrationTest : public CertVerifierBrowserTest { + public: + WebAuthnAutofillIntegrationTest() = default; + + WebAuthnAutofillIntegrationTest(const WebAuthnAutofillIntegrationTest&) = + delete; + WebAuthnAutofillIntegrationTest& operator=( + const WebAuthnAutofillIntegrationTest&) = delete; + + void SetUpCommandLine(base::CommandLine* command_line) override { + CertVerifierBrowserTest::SetUpCommandLine(command_line); + command_line->AppendSwitch(switches::kIgnoreCertificateErrors); + } + + void SetUp() override { + ASSERT_TRUE(https_server_.InitializeAndListen()); + CertVerifierBrowserTest::SetUp(); + } + + void SetUpOnMainThread() override { + CertVerifierBrowserTest::SetUpOnMainThread(); + + https_server_.ServeFilesFromSourceDirectory(GetChromeTestDataDir()); + https_server_.StartAcceptingConnections(); + host_resolver()->AddRule("*", "127.0.0.1"); + + // Allowlist all certs for the HTTPS server. + auto cert = https_server_.GetCertificate(); + net::CertVerifyResult verify_result; + verify_result.cert_status = 0; + verify_result.verified_cert = cert; + mock_cert_verifier()->AddResultForCert(cert.get(), verify_result, net::OK); + + // Save a credential to the password store. This will let us wait on the + // popup to appear after aborting the request. + password_manager::PasswordStoreInterface* password_store = + PasswordStoreFactory::GetForProfile(browser()->profile(), + ServiceAccessType::EXPLICIT_ACCESS) + .get(); + password_manager::PasswordForm signin_form; + GURL url = https_server_.GetURL("www.example.com", "/"); + signin_form.signon_realm = url.spec(); + signin_form.url = url; + signin_form.action = url; + signin_form.username_value = u"remilia"; + signin_form.password_value = u"shouldbeusingapasskeyinstead"; + base::RunLoop run_loop; + password_store->AddLogin(signin_form, run_loop.QuitClosure()); + + ASSERT_TRUE(ui_test_utils::NavigateToURL( + browser(), + https_server_.GetURL("www.example.com", + "/webauthn_conditional_mediation.html"))); + } + + void RunSelectAccountTest() { + // Make sure input events cannot close the autofill popup. + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + autofill::ChromeAutofillClient* autofill_client = + autofill::ChromeAutofillClient::FromWebContents(web_contents); + autofill_client->KeepPopupOpenForTesting(); + + // Execute the Conditional UI request. + content::DOMMessageQueue message_queue(web_contents); + content::ExecuteScriptAsync(web_contents, kConditionalUIRequest); + + // Interact with the username field until the popup shows up. This has the + // effect of waiting for the browser to send the renderer the password + // information, and waiting for the UI to render. + base::WeakPtr<autofill::AutofillPopupController> popup_controller; + while (!popup_controller) { + content::SimulateMouseClickOrTapElementWithId(web_contents, "username"); + popup_controller = autofill_client->popup_controller_for_testing(); + } + + // Find the webauthn credential on the suggestions list. + auto suggestions = popup_controller->GetSuggestions(); + size_t suggestion_index; + autofill::Suggestion webauthn_entry; + for (suggestion_index = 0; suggestion_index < suggestions.size(); + ++suggestion_index) { + if (suggestions[suggestion_index].frontend_id == + autofill::PopupItemId::POPUP_ITEM_ID_WEBAUTHN_CREDENTIAL) { + webauthn_entry = suggestions[suggestion_index]; + break; + } + } + ASSERT_LT(suggestion_index, suggestions.size()) + << "WebAuthn entry not found"; + EXPECT_EQ(webauthn_entry.main_text.value, u"flandre"); + EXPECT_EQ(webauthn_entry.labels.at(0).at(0).value, + l10n_util::GetStringUTF16( + password_manager::GetPlatformAuthenticatorLabel())); + EXPECT_EQ(webauthn_entry.icon, "globeIcon"); + + // Click the credential. + popup_controller->AcceptSuggestion(suggestion_index); + std::string result; + ASSERT_TRUE(message_queue.WaitForMessage(&result)); + EXPECT_EQ(result, "\"webauthn: OK\""); + } + + void RunAbortTest() { + // Make sure input events cannot close the autofill popup. + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + autofill::ChromeAutofillClient* autofill_client = + autofill::ChromeAutofillClient::FromWebContents(web_contents); + autofill_client->KeepPopupOpenForTesting(); + + // Execute the Conditional UI request. + content::DOMMessageQueue message_queue(web_contents); + content::ExecuteScriptAsync(web_contents, kConditionalUIRequest); + + // Interact with the username field until the popup shows up. This has the + // effect of waiting for the browser to send the renderer the password + // information, and waiting for the UI to render. + base::WeakPtr<autofill::AutofillPopupController> popup_controller; + while (!popup_controller) { + content::SimulateMouseClickOrTapElementWithId(web_contents, "username"); + popup_controller = autofill_client->popup_controller_for_testing(); + } + + // Find the webauthn credential on the suggestions list. + auto suggestions = popup_controller->GetSuggestions(); + size_t suggestion_index; + autofill::Suggestion webauthn_entry; + for (suggestion_index = 0; suggestion_index < suggestions.size(); + ++suggestion_index) { + if (suggestions[suggestion_index].frontend_id == + autofill::PopupItemId::POPUP_ITEM_ID_WEBAUTHN_CREDENTIAL) { + webauthn_entry = suggestions[suggestion_index]; + break; + } + } + ASSERT_LT(suggestion_index, suggestions.size()) + << "WebAuthn entry not found"; + EXPECT_EQ(webauthn_entry.main_text.value, u"flandre"); + EXPECT_EQ(webauthn_entry.labels.at(0).at(0).value, + l10n_util::GetStringUTF16( + password_manager::GetPlatformAuthenticatorLabel())); + EXPECT_EQ(webauthn_entry.icon, "globeIcon"); + + // Abort the request. + content::ExecuteScriptAsync(web_contents, + "window.requestAbortController.abort()"); + std::string result; + ASSERT_TRUE(message_queue.WaitForMessage(&result)); + EXPECT_EQ(result, "\"error AbortError: signal is aborted without reason\""); + + // The popup may have gone away while waiting. If not, make sure it's gone. + if (popup_controller) { + popup_controller->Hide(autofill::PopupHidingReason::kUserAborted); + } + + // Interact with the username field. Since there is still a saved password, + // the popup should eventually show up. + while (!popup_controller) { + content::SimulateMouseClickOrTapElementWithId(web_contents, "username"); + popup_controller = autofill_client->popup_controller_for_testing(); + } + for (const auto& suggestion : popup_controller->GetSuggestions()) { + EXPECT_NE(suggestion.frontend_id, + autofill::POPUP_ITEM_ID_WEBAUTHN_CREDENTIAL); + EXPECT_NE(suggestion.frontend_id, + autofill::POPUP_ITEM_ID_WEBAUTHN_SIGN_IN_WITH_ANOTHER_DEVICE); + } + } + + base::test::ScopedFeatureList scoped_feature_list_{ + features::kWebAuthConditionalUI}; + raw_ptr<device::test::VirtualFidoDeviceFactory> virtual_device_factory_; + net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS}; +}; + +// Autofill integration test using the devtools virtual environment. +class WebAuthnDevtoolsAutofillIntegrationTest + : public WebAuthnAutofillIntegrationTest { + public: + void SetUpOnMainThread() override { + WebAuthnAutofillIntegrationTest::SetUpOnMainThread(); + + // Set up a fake virtual device. + auto virtual_device_factory = + std::make_unique<device::test::VirtualFidoDeviceFactory>(); + virtual_device_factory->SetTransport( + device::FidoTransportProtocol::kInternal); + virtual_device_factory_ = virtual_device_factory.get(); + virtual_device_factory->mutable_state()->InjectResidentKey( + kCredentialID, "www.example.com", std::vector<uint8_t>{5, 6, 7, 8}, + "flandre", "Flandre Scarlet"); + virtual_device_factory->mutable_state()->fingerprints_enrolled = true; + device::VirtualCtap2Device::Config config; + config.resident_key_support = true; + config.internal_uv_support = true; + virtual_device_factory->SetCtap2Config(std::move(config)); + content::AuthenticatorEnvironment::GetInstance() + ->ReplaceDefaultDiscoveryFactoryForTesting( + std::move(virtual_device_factory)); + } +}; + +IN_PROC_BROWSER_TEST_F(WebAuthnDevtoolsAutofillIntegrationTest, SelectAccount) { + RunSelectAccountTest(); +} + +IN_PROC_BROWSER_TEST_F(WebAuthnDevtoolsAutofillIntegrationTest, Abort) { + RunAbortTest(); +} + +#if BUILDFLAG(IS_WIN) +// Autofill integration test using the Windows fake API. +class WebAuthnWindowsAutofillIntegrationTest + : public WebAuthnAutofillIntegrationTest { + public: + void SetUpOnMainThread() override { + WebAuthnAutofillIntegrationTest::SetUpOnMainThread(); + + // Set up the fake Windows platform authenticator. + fake_webauthn_api_ = std::make_unique<device::FakeWinWebAuthnApi>(); + fake_webauthn_api_->set_version(WEBAUTHN_API_VERSION_4); + fake_webauthn_api_->set_is_uvpaa(true); + fake_webauthn_api_->set_supports_silent_discovery(true); + device::PublicKeyCredentialUserEntity user({1, 2, 3, 4}, "flandre", + "Flandre Scarlet"); + device::PublicKeyCredentialRpEntity rp("www.example.com"); + fake_webauthn_api_->InjectDiscoverableCredential( + kCredentialID, std::move(rp), std::move(user)); + + // Inject the fake Windows platform authenticator. + auto device_factory = + std::make_unique<device::test::VirtualFidoDeviceFactory>(); + device_factory->set_win_webauthn_api(fake_webauthn_api_.get()); + content::AuthenticatorEnvironment::GetInstance() + ->ReplaceDefaultDiscoveryFactoryForTesting(std::move(device_factory)); + } + + protected: + std::unique_ptr<device::FakeWinWebAuthnApi> fake_webauthn_api_; +}; + +IN_PROC_BROWSER_TEST_F(WebAuthnWindowsAutofillIntegrationTest, SelectAccount) { + RunSelectAccountTest(); +} + +IN_PROC_BROWSER_TEST_F(WebAuthnWindowsAutofillIntegrationTest, Abort) { + RunAbortTest(); +} +#endif // BUILDFLAG(IS_WIN) + +} // namespace
diff --git a/chrome/browser/webauthn/chrome_webauthn_mac_browsertest.mm b/chrome/browser/webauthn/chrome_webauthn_autofill_mac_interactive_uitest.mm similarity index 100% rename from chrome/browser/webauthn/chrome_webauthn_mac_browsertest.mm rename to chrome/browser/webauthn/chrome_webauthn_autofill_mac_interactive_uitest.mm
diff --git a/chrome/browser/webauthn/chrome_webauthn_browsertest.cc b/chrome/browser/webauthn/chrome_webauthn_browsertest.cc index 05a8b2cb..24d56669 100644 --- a/chrome/browser/webauthn/chrome_webauthn_browsertest.cc +++ b/chrome/browser/webauthn/chrome_webauthn_browsertest.cc
@@ -19,21 +19,13 @@ #include "chrome/browser/extensions/install_verifier.h" #include "chrome/browser/extensions/test_extension_system.h" #include "chrome/browser/password_manager/chrome_webauthn_credentials_delegate.h" -#include "chrome/browser/password_manager/password_store_factory.h" #include "chrome/browser/ssl/cert_verifier_browser_test.h" -#include "chrome/browser/ui/autofill/autofill_popup_controller_impl.h" -#include "chrome/browser/ui/autofill/chrome_autofill_client.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/webauthn/authenticator_request_dialog_model.h" #include "chrome/browser/webauthn/chrome_authenticator_request_delegate.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" -#include "components/autofill/core/browser/ui/popup_item_ids.h" -#include "components/autofill/core/browser/ui/popup_types.h" #include "components/network_session_configurator/common/network_switches.h" -#include "components/password_manager/core/browser/password_form_manager.h" -#include "components/password_manager/core/browser/password_store_interface.h" -#include "components/password_manager/core/browser/password_ui_utils.h" #include "content/public/browser/authenticator_environment.h" #include "content/public/browser/render_frame_host.h" #include "content/public/common/content_features.h" @@ -42,14 +34,12 @@ #include "device/fido/cable/cable_discovery_data.h" #include "device/fido/features.h" #include "device/fido/fido_transport_protocol.h" -#include "device/fido/public_key_credential_user_entity.h" #include "device/fido/virtual_ctap2_device.h" #include "device/fido/virtual_fido_device.h" #include "device/fido/virtual_fido_device_factory.h" #include "extensions/common/extension_builder.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/embedded_test_server.h" -#include "ui/base/l10n/l10n_util.h" #include "url/gurl.h" #if BUILDFLAG(IS_WIN) @@ -345,231 +335,6 @@ EXPECT_EQ(observer_->accounts_.at(0), "01020304"); } -// Autofill integration tests -------------------------------------------------- - -// Base class for autofill integration tests, contains the actual test code but -// no setup. -class WebAuthnAutofillIntegrationTest : public WebAuthnBrowserTest { - protected: - void SetUpOnMainThread() override { - WebAuthnBrowserTest::SetUpOnMainThread(); - // Save a credential to the password store. This will let us wait on the - // popup to appear after aborting the request. - password_manager::PasswordStoreInterface* password_store = - PasswordStoreFactory::GetForProfile(browser()->profile(), - ServiceAccessType::EXPLICIT_ACCESS) - .get(); - password_manager::PasswordForm signin_form; - GURL url = https_server_.GetURL("www.example.com", "/"); - signin_form.signon_realm = url.spec(); - signin_form.url = url; - signin_form.action = url; - signin_form.username_value = u"remilia"; - signin_form.password_value = u"shouldbeusingapasskeyinstead"; - base::RunLoop run_loop; - password_store->AddLogin(signin_form, run_loop.QuitClosure()); - - ASSERT_TRUE(ui_test_utils::NavigateToURL( - browser(), - https_server_.GetURL("www.example.com", - "/webauthn_conditional_mediation.html"))); - } - - void RunSelectAccountTest() { - // Make sure input events cannot close the autofill popup. - content::WebContents* web_contents = - browser()->tab_strip_model()->GetActiveWebContents(); - autofill::ChromeAutofillClient* autofill_client = - autofill::ChromeAutofillClient::FromWebContents(web_contents); - autofill_client->KeepPopupOpenForTesting(); - - // Execute the Conditional UI request. - content::DOMMessageQueue message_queue(web_contents); - content::ExecuteScriptAsync(web_contents, kConditionalUIRequest); - - // Interact with the username field until the popup shows up. This has the - // effect of waiting for the browser to send the renderer the password - // information, and waiting for the UI to render. - base::WeakPtr<autofill::AutofillPopupController> popup_controller; - while (!popup_controller) { - content::SimulateMouseClickOrTapElementWithId(web_contents, "username"); - popup_controller = autofill_client->popup_controller_for_testing(); - } - - // Find the webauthn credential on the suggestions list. - auto suggestions = popup_controller->GetSuggestions(); - size_t suggestion_index; - autofill::Suggestion webauthn_entry; - for (suggestion_index = 0; suggestion_index < suggestions.size(); - ++suggestion_index) { - if (suggestions[suggestion_index].frontend_id == - autofill::PopupItemId::POPUP_ITEM_ID_WEBAUTHN_CREDENTIAL) { - webauthn_entry = suggestions[suggestion_index]; - break; - } - } - ASSERT_LT(suggestion_index, suggestions.size()) - << "WebAuthn entry not found"; - EXPECT_EQ(webauthn_entry.main_text.value, u"flandre"); - EXPECT_EQ(webauthn_entry.labels.at(0).at(0).value, - l10n_util::GetStringUTF16( - password_manager::GetPlatformAuthenticatorLabel())); - EXPECT_EQ(webauthn_entry.icon, "globeIcon"); - - // Click the credential. - popup_controller->AcceptSuggestion(suggestion_index); - std::string result; - ASSERT_TRUE(message_queue.WaitForMessage(&result)); - EXPECT_EQ(result, "\"webauthn: OK\""); - } - - void RunAbortTest() { - // Make sure input events cannot close the autofill popup. - content::WebContents* web_contents = - browser()->tab_strip_model()->GetActiveWebContents(); - autofill::ChromeAutofillClient* autofill_client = - autofill::ChromeAutofillClient::FromWebContents(web_contents); - autofill_client->KeepPopupOpenForTesting(); - - // Execute the Conditional UI request. - content::DOMMessageQueue message_queue(web_contents); - content::ExecuteScriptAsync(web_contents, kConditionalUIRequest); - - // Interact with the username field until the popup shows up. This has the - // effect of waiting for the browser to send the renderer the password - // information, and waiting for the UI to render. - base::WeakPtr<autofill::AutofillPopupController> popup_controller; - while (!popup_controller) { - content::SimulateMouseClickOrTapElementWithId(web_contents, "username"); - popup_controller = autofill_client->popup_controller_for_testing(); - } - - // Find the webauthn credential on the suggestions list. - auto suggestions = popup_controller->GetSuggestions(); - size_t suggestion_index; - autofill::Suggestion webauthn_entry; - for (suggestion_index = 0; suggestion_index < suggestions.size(); - ++suggestion_index) { - if (suggestions[suggestion_index].frontend_id == - autofill::PopupItemId::POPUP_ITEM_ID_WEBAUTHN_CREDENTIAL) { - webauthn_entry = suggestions[suggestion_index]; - break; - } - } - ASSERT_LT(suggestion_index, suggestions.size()) - << "WebAuthn entry not found"; - EXPECT_EQ(webauthn_entry.main_text.value, u"flandre"); - EXPECT_EQ(webauthn_entry.labels.at(0).at(0).value, - l10n_util::GetStringUTF16( - password_manager::GetPlatformAuthenticatorLabel())); - EXPECT_EQ(webauthn_entry.icon, "globeIcon"); - - // Abort the request. - content::ExecuteScriptAsync(web_contents, - "window.requestAbortController.abort()"); - std::string result; - ASSERT_TRUE(message_queue.WaitForMessage(&result)); - EXPECT_EQ(result, "\"error AbortError: signal is aborted without reason\""); - - // The popup may have gone away while waiting. If not, make sure it's gone. - if (popup_controller) { - popup_controller->Hide(autofill::PopupHidingReason::kUserAborted); - } - - // Interact with the username field. Since there is still a saved password, - // the popup should eventually show up. - while (!popup_controller) { - content::SimulateMouseClickOrTapElementWithId(web_contents, "username"); - popup_controller = autofill_client->popup_controller_for_testing(); - } - for (const auto& suggestion : popup_controller->GetSuggestions()) { - EXPECT_NE(suggestion.frontend_id, - autofill::POPUP_ITEM_ID_WEBAUTHN_CREDENTIAL); - EXPECT_NE(suggestion.frontend_id, - autofill::POPUP_ITEM_ID_WEBAUTHN_SIGN_IN_WITH_ANOTHER_DEVICE); - } - } - - base::test::ScopedFeatureList scoped_feature_list_{ - features::kWebAuthConditionalUI}; - raw_ptr<device::test::VirtualFidoDeviceFactory> virtual_device_factory_; -}; - -// Autofill integration test using the devtools virtual environment. -class WebAuthnDevtoolsAutofillIntegrationTest - : public WebAuthnAutofillIntegrationTest { - public: - void SetUpOnMainThread() override { - WebAuthnAutofillIntegrationTest::SetUpOnMainThread(); - - // Set up a fake virtual device. - auto virtual_device_factory = - std::make_unique<device::test::VirtualFidoDeviceFactory>(); - virtual_device_factory->SetTransport( - device::FidoTransportProtocol::kInternal); - virtual_device_factory_ = virtual_device_factory.get(); - virtual_device_factory->mutable_state()->InjectResidentKey( - kCredentialID, "www.example.com", std::vector<uint8_t>{5, 6, 7, 8}, - "flandre", "Flandre Scarlet"); - virtual_device_factory->mutable_state()->fingerprints_enrolled = true; - device::VirtualCtap2Device::Config config; - config.resident_key_support = true; - config.internal_uv_support = true; - virtual_device_factory->SetCtap2Config(std::move(config)); - content::AuthenticatorEnvironment::GetInstance() - ->ReplaceDefaultDiscoveryFactoryForTesting( - std::move(virtual_device_factory)); - } -}; - -IN_PROC_BROWSER_TEST_F(WebAuthnDevtoolsAutofillIntegrationTest, SelectAccount) { - RunSelectAccountTest(); -} - -IN_PROC_BROWSER_TEST_F(WebAuthnDevtoolsAutofillIntegrationTest, Abort) { - RunAbortTest(); -} - -#if BUILDFLAG(IS_WIN) -// Autofill integration test using the Windows fake API. -class WebAuthnWindowsAutofillIntegrationTest - : public WebAuthnAutofillIntegrationTest { - public: - void SetUpOnMainThread() override { - WebAuthnAutofillIntegrationTest::SetUpOnMainThread(); - - // Set up the fake Windows platform authenticator. - fake_webauthn_api_ = std::make_unique<device::FakeWinWebAuthnApi>(); - fake_webauthn_api_->set_version(WEBAUTHN_API_VERSION_4); - fake_webauthn_api_->set_is_uvpaa(true); - fake_webauthn_api_->set_supports_silent_discovery(true); - device::PublicKeyCredentialUserEntity user({1, 2, 3, 4}, "flandre", - "Flandre Scarlet"); - device::PublicKeyCredentialRpEntity rp("www.example.com"); - fake_webauthn_api_->InjectDiscoverableCredential( - kCredentialID, std::move(rp), std::move(user)); - - // Inject the fake Windows platform authenticator. - auto device_factory = - std::make_unique<device::test::VirtualFidoDeviceFactory>(); - device_factory->set_win_webauthn_api(fake_webauthn_api_.get()); - content::AuthenticatorEnvironment::GetInstance() - ->ReplaceDefaultDiscoveryFactoryForTesting(std::move(device_factory)); - } - - protected: - std::unique_ptr<device::FakeWinWebAuthnApi> fake_webauthn_api_; -}; - -IN_PROC_BROWSER_TEST_F(WebAuthnWindowsAutofillIntegrationTest, SelectAccount) { - RunSelectAccountTest(); -} - -IN_PROC_BROWSER_TEST_F(WebAuthnWindowsAutofillIntegrationTest, Abort) { - RunAbortTest(); -} -#endif // BUILDFLAG(IS_WIN) - // WebAuthnCableExtension exercises code paths where a server sends a caBLEv2 // extension in a get() request. class WebAuthnCableExtension : public WebAuthnBrowserTest {
diff --git a/chrome/browser/win/conflicts/installed_applications_unittest.cc b/chrome/browser/win/conflicts/installed_applications_unittest.cc index ae494e1..f1b6d7c 100644 --- a/chrome/browser/win/conflicts/installed_applications_unittest.cc +++ b/chrome/browser/win/conflicts/installed_applications_unittest.cc
@@ -4,9 +4,9 @@ #include "chrome/browser/win/conflicts/installed_applications.h" +#include <algorithm> #include <map> -#include "base/ranges/algorithm.h" #include "base/strings/stringprintf.h" #include "base/test/test_reg_util_win.h" #include "base/win/registry.h" @@ -397,10 +397,13 @@ auto applications = installed_applications().applications_; std::sort(std::begin(applications), std::end(applications)); EXPECT_EQ(std::end(applications), - base::ranges::adjacent_find( - applications, std::equal<>(), [](const auto& app) { - return std::tie(app.name, app.registry_root, - app.registry_key_path, - app.registry_wow64_access); - })); + std::adjacent_find(std::begin(applications), std::end(applications), + [](const auto& lhs, const auto& rhs) { + return std::tie(lhs.name, lhs.registry_root, + lhs.registry_key_path, + lhs.registry_wow64_access) == + std::tie(rhs.name, rhs.registry_root, + rhs.registry_key_path, + rhs.registry_wow64_access); + })); }
diff --git a/chrome/browser/win/conflicts/module_blocklist_cache_util_unittest.cc b/chrome/browser/win/conflicts/module_blocklist_cache_util_unittest.cc index 2db9d15..0af2cdd09 100644 --- a/chrome/browser/win/conflicts/module_blocklist_cache_util_unittest.cc +++ b/chrome/browser/win/conflicts/module_blocklist_cache_util_unittest.cc
@@ -52,8 +52,8 @@ // Sort the entries and make sure each module is unique. std::sort(entries.begin(), entries.end(), internal::ModuleLess()); - CHECK(base::ranges::adjacent_find(entries, internal::ModuleEqual()) == - entries.end()); + CHECK(std::adjacent_find(entries.begin(), entries.end(), + internal::ModuleEqual()) == entries.end()); return entries; }
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index 883d8a80..05dad11 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-main-1665770398-17d4f7bea9fcc7d745ba4f47c559f834a80ec320.profdata +chrome-linux-main-1665791948-e3778de99e281f8d497aab292bd2b3edfe866c10.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 324fd7d..37e1d7f0 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1665770398-c2ca4d7140879c329be52f41540880b88c5e6fea.profdata +chrome-mac-arm-main-1665791948-da8d820eb239ddbf7cce7167780fef3b1a569374.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index b704373..2d34063 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1665770398-e5491636ea29ef2e4a2a0d4123cb4c9b39977e3f.profdata +chrome-win32-main-1665780980-268de8ccceb67cb8a6186d4a4e093edcc58b176b.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index c0e6f11..b24b1809 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1665770398-60ea39db78e2e9990263a15c0195420700de507e.profdata +chrome-win64-main-1665780980-352bf7bac339e5f02e7759c28ffa6e66ed59b582.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 4d0f889..fd48499 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -2616,7 +2616,6 @@ "../browser/ui/test/test_browser_dialog_mac.h", "../browser/ui/test/test_browser_dialog_mac.mm", "../browser/ui/views/frame/browser_non_client_frame_view_mac_browsertest.mm", - "../browser/webauthn/chrome_webauthn_mac_browsertest.mm", "../browser/webshare/mac/sharing_service_operation_browsertest.cc", "../common/mac/app_mode_chrome_locator_browsertest.mm", "../common/profiler/thread_profiler_browsertest.cc", @@ -9491,6 +9490,7 @@ "../browser/ui/webui/settings/settings_interactive_uitest.cc", "../browser/webapps/web_app_offline_browsertest.cc", "../browser/webauth_interactive_uitest.cc", + "../browser/webauthn/chrome_webauthn_autofill_interactive_uitest.cc", "//ui/base/clipboard/clipboard_unittest.cc", "base/interactive_ui_tests_main.cc", ] @@ -9963,6 +9963,7 @@ "../browser/ui/cocoa/status_bubble_mac_interactive_uitest.mm", "../browser/ui/cocoa/tab_contents/web_contents_view_mac_interactive_uitest.mm", "../browser/ui/find_bar/find_bar_platform_helper_mac_interactive_uitest.mm", + "../browser/webauthn/chrome_webauthn_autofill_mac_interactive_uitest.mm", ] sources -= [
diff --git a/chrome/test/data/webui/chromeos/internet_detail_dialog_test.js b/chrome/test/data/webui/chromeos/internet_detail_dialog_test.js index 1f7e39c4..bf1cf33 100644 --- a/chrome/test/data/webui/chromeos/internet_detail_dialog_test.js +++ b/chrome/test/data/webui/chromeos/internet_detail_dialog_test.js
@@ -8,7 +8,7 @@ import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js'; import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js'; import {CrosNetworkConfigRemote, InhibitReason} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js'; -import {ConnectionStateType, DeviceStateType, NetworkType, OncSource} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js'; +import {ConnectionStateType, DeviceStateType, NetworkType, OncSource, PortalState} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js'; import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {FakeNetworkConfig} from 'chrome://test/chromeos/fake_network_config_mojom.js'; import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js'; @@ -18,7 +18,8 @@ constructor() { super([ 'getDialogArguments', - 'dialogClose', + 'closeDialog', + 'showPortalSignin', ]); } @@ -28,7 +29,10 @@ } /** @override */ - dialogClose() {} + closeDialog() {} + + /** @override */ + showPortalSignin() {} } suite('internet-detail-dialog', () => { @@ -104,6 +108,116 @@ return element; } + suite('captive portal ui updates', () => { + function getButton(buttonId) { + const button = + internetDetailDialog.shadowRoot.querySelector(`#${buttonId}`); + assertTrue(!!button); + return button; + } + + test('WiFi in a portal portalState', function() { + mojoApi_.setNetworkTypeEnabledState(NetworkType.kWiFi, true); + const wifiNetwork = getManagedProperties(NetworkType.kWiFi, 'wifi_user'); + wifiNetwork.source = OncSource.kUser; + wifiNetwork.connectable = true; + wifiNetwork.connectionState = ConnectionStateType.kPortal; + wifiNetwork.portalState = PortalState.kPortal; + + mojoApi_.setManagedPropertiesForTest(wifiNetwork); + init(); + internetDetailDialog.isCaptivePortalUI2022Enabled_ = true; + return flushAsync().then(() => { + const networkStateText = + internetDetailDialog.shadowRoot.querySelector(`#networkState`); + assertTrue(networkStateText.hasAttribute('warning')); + assertEquals( + networkStateText.textContent.trim(), + internetDetailDialog.i18n('networkListItemSignIn')); + const signinButton = getButton('signinButton'); + assertTrue(!!signinButton); + assertFalse(signinButton.hasAttribute('hidden')); + assertFalse(signinButton.disabled); + }); + }); + + test('WiFi in a no internet portalState', function() { + mojoApi_.setNetworkTypeEnabledState(NetworkType.kWiFi, true); + const wifiNetwork = getManagedProperties(NetworkType.kWiFi, 'wifi_user'); + wifiNetwork.source = OncSource.kUser; + wifiNetwork.connectable = true; + wifiNetwork.connectionState = ConnectionStateType.kPortal; + wifiNetwork.portalState = PortalState.kNoInternet; + + mojoApi_.setManagedPropertiesForTest(wifiNetwork); + init(); + internetDetailDialog.isCaptivePortalUI2022Enabled_ = true; + return flushAsync().then(() => { + const networkStateText = + internetDetailDialog.shadowRoot.querySelector(`#networkState`); + assertTrue(networkStateText.hasAttribute('warning')); + assertEquals( + networkStateText.textContent.trim(), + internetDetailDialog.i18n( + 'networkListItemConnectedNoConnectivity')); + const signinButton = getButton('signinButton'); + assertTrue(!!signinButton); + assertTrue(signinButton.hasAttribute('hidden')); + assertTrue(signinButton.disabled); + }); + }); + + test('WiFi in a proxy-auth portalState', function() { + mojoApi_.setNetworkTypeEnabledState(NetworkType.kWiFi, true); + const wifiNetwork = getManagedProperties(NetworkType.kWiFi, 'wifi_user'); + wifiNetwork.source = OncSource.kUser; + wifiNetwork.connectable = true; + wifiNetwork.connectionState = ConnectionStateType.kPortal; + wifiNetwork.portalState = PortalState.kProxyAuthRequired; + + mojoApi_.setManagedPropertiesForTest(wifiNetwork); + init(); + internetDetailDialog.isCaptivePortalUI2022Enabled_ = true; + return flushAsync().then(() => { + const networkStateText = + internetDetailDialog.shadowRoot.querySelector(`#networkState`); + assertTrue(networkStateText.hasAttribute('warning')); + assertEquals( + networkStateText.textContent.trim(), + internetDetailDialog.i18n('networkListItemSignIn')); + const signinButton = getButton('signinButton'); + assertTrue(!!signinButton); + assertFalse(signinButton.hasAttribute('hidden')); + assertFalse(signinButton.disabled); + }); + }); + + test('WiFi in a portal portalState and feature flag disabled', function() { + mojoApi_.setNetworkTypeEnabledState(NetworkType.kWiFi, true); + const wifiNetwork = getManagedProperties(NetworkType.kWiFi, 'wifi_user'); + wifiNetwork.source = OncSource.kUser; + wifiNetwork.connectable = true; + wifiNetwork.connectionState = ConnectionStateType.kPortal; + wifiNetwork.portalState = PortalState.kPortal; + + mojoApi_.setManagedPropertiesForTest(wifiNetwork); + init(); + internetDetailDialog.isCaptivePortalUI2022Enabled_ = false; + return flushAsync().then(() => { + const networkStateText = + internetDetailDialog.shadowRoot.querySelector(`#networkState`); + assertTrue(networkStateText.hasAttribute('connected')); + assertEquals( + networkStateText.textContent.trim(), + internetDetailDialog.i18n('OncConnected')); + const signinButton = + internetDetailDialog.shadowRoot.querySelector(`#signinButton`); + // Button does not exist because feature flag is disabled. + assertTrue(!signinButton); + }); + }); + }); + test('Network not on active sim, hide configurations', async () => { await setupCellularNetwork(/*isPrimary=*/ false, /*isInhibited=*/ false);
diff --git a/chrome/test/data/webui/new_tab_page/lens_form_test.ts b/chrome/test/data/webui/new_tab_page/lens_form_test.ts index 0f4e135b3..1b1e0b8 100644 --- a/chrome/test/data/webui/new_tab_page/lens_form_test.ts +++ b/chrome/test/data/webui/new_tab_page/lens_form_test.ts
@@ -12,6 +12,7 @@ let lensForm: LensFormElement; let fileFormSubmitted = false; + let urlFormSubmitted = false; let lastError: LensErrorType|null = null; let loading = false; @@ -23,6 +24,10 @@ fileFormSubmitted = true; }; + lensForm.$.urlForm.submit = () => { + urlFormSubmitted = true; + }; + lensForm.addEventListener('error', (e: Event) => { const event = e as CustomEvent<LensErrorType>; lastError = event.detail; @@ -35,6 +40,7 @@ teardown(() => { fileFormSubmitted = false; + urlFormSubmitted = false; lastError = null; loading = false; }); @@ -99,6 +105,85 @@ assertFalse(loading); }); + test('submit url with valid http should submit', async () => { + // Arrange. + const url = 'http://www.example.com/dog.jpg'; + + // Act. + lensForm.submitUrl(url); + + // Assert. + assertTrue(urlFormSubmitted); + assertTrue(loading); + }); + + test('submit url with valid https should submit', async () => { + // Arrange. + const url = 'https://www.example.com/dog.jpg'; + + // Act. + lensForm.submitUrl(url); + + // Assert. + assertTrue(urlFormSubmitted); + assertTrue(loading); + }); + + test( + 'submit url with empty scheme should fail with invalid scheme error', + async () => { + // Arrange. + const url = 'www.example.com/dog.jpg'; + + // Act. + lensForm.submitUrl(url); + + // Assert. + assertFalse(urlFormSubmitted); + assertEquals(LensErrorType.INVALID_SCHEME, lastError); + }); + + test( + 'submit url with invalid scheme should fail with invalid scheme error', + async () => { + // Arrange. + const url = 'file://www.example.com/dog.jpg'; + + // Act. + lensForm.submitUrl(url); + + // Assert. + assertFalse(urlFormSubmitted); + assertEquals(LensErrorType.INVALID_SCHEME, lastError); + }); + + test('submit invalid url should fail with invalid url error', async () => { + // Arrange. + const url = 'http://www.example.com/\uD800.jpg'; + + // Act. + lensForm.submitUrl(url); + + // Assert. + assertFalse(urlFormSubmitted); + assertEquals(LensErrorType.INVALID_URL, lastError); + }); + + test('submit long url should fail with length too great error', async () => { + // Arrange. + let longString = 'http://www.example.com/dog.jpg?a='; + for (let i = 0; i < 2000; i++) { + longString += 'x'; + } + + // Act. + lensForm.submitUrl(longString); + + // Assert. + assertFalse(urlFormSubmitted); + assertEquals(LensErrorType.LENGTH_TOO_GREAT, lastError); + }); + function dispatchFileInputChangeWithDataTransfer(dataTransfer: DataTransfer) { lensForm.$.fileInput.files = dataTransfer.files; lensForm.$.fileInput.dispatchEvent(new Event('change'));
diff --git a/chrome/test/data/webui/new_tab_page/lens_upload_dialog_test.ts b/chrome/test/data/webui/new_tab_page/lens_upload_dialog_test.ts index 054ff35d..cff6413 100644 --- a/chrome/test/data/webui/new_tab_page/lens_upload_dialog_test.ts +++ b/chrome/test/data/webui/new_tab_page/lens_upload_dialog_test.ts
@@ -7,7 +7,7 @@ import {LensUploadDialogElement} from 'chrome://new-tab-page/lazy_load.js'; import {WindowProxy} from 'chrome://new-tab-page/new_tab_page.js'; -import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; +import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js'; import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js'; @@ -19,6 +19,9 @@ let outsideClickTarget: HTMLDivElement; let windowProxy: TestBrowserProxy; + let submitUrlCalled = false; + let submittedUrl: string|null = null; + setup(() => { document.body.innerHTML = ''; windowProxy = installMock(WindowProxy); @@ -42,6 +45,16 @@ uploadDialog = document.createElement('ntp-lens-upload-dialog'); wrapperElement.appendChild(uploadDialog); + + uploadDialog.$.lensForm.submitUrl = (url: string) => { + submitUrlCalled = true; + submittedUrl = url; + }; + }); + + teardown(() => { + submitUrlCalled = false; + submittedUrl = null; }); test('hidden be default', () => { @@ -121,4 +134,63 @@ // Assert. assertFalse(uploadDialog.hasAttribute('is-offline_')); }); + + test('submit url does not submit with empty url', async () => { + // Arrange. + uploadDialog.openDialog(); + await waitAfterNextRender(uploadDialog); + + // Act. + clickInputSubmit(); + + // Assert. + assertFalse(submitUrlCalled); + }); + + test( + 'submit valid url by clicking submit button should submit ', async () => { + // Arrange. + const url = 'http://google.com/image.png'; + uploadDialog.openDialog(); + await waitAfterNextRender(uploadDialog); + + // Act. + setInputBoxValue(url); + clickInputSubmit(); + + // Assert. + assertTrue(submitUrlCalled); + assertEquals(url, submittedUrl); + }); + + test('submit valid url by typing enter should submit ', async () => { + // Arrange. + const url = 'http://google.com/image.png'; + uploadDialog.openDialog(); + await waitAfterNextRender(uploadDialog); + + // Act. + setInputBoxValue(url); + getInputBox().dispatchEvent(new KeyboardEvent('keydown', {key: 'Enter'})); + + // Assert. + assertTrue(submitUrlCalled); + assertEquals(url, submittedUrl); + }); + + function getInputBox(): HTMLInputElement { + return uploadDialog.shadowRoot!.querySelector('#inputBox')!; + } + + function setInputBoxValue(value: string) { + const inputBox = getInputBox(); + inputBox.value = value; + inputBox.dispatchEvent(new InputEvent('input')); + } + + function clickInputSubmit() { + const inputSubmit = + uploadDialog.shadowRoot!.querySelector('#inputSubmit') as HTMLElement; + inputSubmit.click(); + } });
diff --git a/chrome/test/data/webui/settings/chromeos/device_page_tests.js b/chrome/test/data/webui/settings/chromeos/device_page_tests.js index 240d22a..81d52bc 100644 --- a/chrome/test/data/webui/settings/chromeos/device_page_tests.js +++ b/chrome/test/data/webui/settings/chromeos/device_page_tests.js
@@ -1269,9 +1269,22 @@ displayDiv.click(); assertEquals( displayPage.displays[1].id, displayPage.selectedDisplay.id); + flush(); - displayPage.updatePrimaryDisplay_({target: {value: '0'}}); - displayPage.onOrientationChange_({target: {value: '90'}}); + const primaryDisplaySelect = + displayPage.shadowRoot.getElementById('primaryDisplaySelect'); + assertTrue(!!primaryDisplaySelect); + primaryDisplaySelect.value = '0'; + primaryDisplaySelect.dispatchEvent(new CustomEvent('change')); + flush(); + + const orientationSelect = + displayPage.shadowRoot.getElementById('orientationSelect'); + assertTrue(!!orientationSelect); + orientationSelect.value = '90'; + orientationSelect.dispatchEvent(new CustomEvent('change')); + flush(); + fakeSystemDisplay.onDisplayChanged.callListeners(); return Promise.all([ @@ -1294,7 +1307,12 @@ assertEquals(90, displayPage.displays[1].rotation); // Mirror the displays. - displayPage.onMirroredTap_({target: {blur: function() {}}}); + const displayMirrorCheckbox = + displayPage.shadowRoot.getElementById('displayMirrorCheckbox'); + assertTrue(!!displayMirrorCheckbox); + displayMirrorCheckbox.click(); + flush(); + fakeSystemDisplay.onDisplayChanged.callListeners(); return Promise.all([
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js index 0e07062..65efc07 100644 --- a/chrome/test/data/webui/settings/cr_settings_browsertest.js +++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -757,7 +757,6 @@ mocha.run(); }); - var CrSettingsReviewNotificationPermissionsTest = class extends CrSettingsBrowserTest { /** @override */ @@ -775,9 +774,12 @@ } }; -TEST_F('CrSettingsReviewNotificationPermissionsTest', 'All', function() { - mocha.run(); -}); +// Failing on buildbots. http://crbug.com/1374908 +TEST_F( + 'CrSettingsReviewNotificationPermissionsTest', + 'DISABLED_CrSettingsReviewNotificationPermissionsTest', function() { + mocha.run(); + }); [['AppearanceFontsPage', 'appearance_fonts_page_test.js'], [
diff --git a/chromeos/ash/components/audio/BUILD.gn b/chromeos/ash/components/audio/BUILD.gn index 2f66848..2fec89d 100644 --- a/chromeos/ash/components/audio/BUILD.gn +++ b/chromeos/ash/components/audio/BUILD.gn
@@ -77,6 +77,7 @@ "audio_device_selection_generated_unittest.cc", "audio_device_selection_test_base.cc", "audio_device_selection_test_base.h", + "audio_device_selection_unittest.cc", "audio_devices_pref_handler_impl_unittest.cc", "cras_audio_handler_unittest.cc", "cros_audio_config_impl_unittest.cc",
diff --git a/chromeos/ash/components/audio/audio_device.h b/chromeos/ash/components/audio/audio_device.h index 3fe3bceb..8b585e46 100644 --- a/chromeos/ash/components/audio/audio_device.h +++ b/chromeos/ash/components/audio/audio_device.h
@@ -38,9 +38,9 @@ }; // Default value of user priority preference. -const uint32_t kUserPriorityNone = 0; +const int kUserPriorityNone = 0; // Min value of user priority preference. -const uint32_t kUserPriorityMin = 1; +const int kUserPriorityMin = 1; struct COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_AUDIO) AudioDevice { AudioDevice();
diff --git a/chromeos/ash/components/audio/audio_device_selection_unittest.cc b/chromeos/ash/components/audio/audio_device_selection_unittest.cc new file mode 100644 index 0000000..1dec1d7 --- /dev/null +++ b/chromeos/ash/components/audio/audio_device_selection_unittest.cc
@@ -0,0 +1,103 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/ash/components/audio/audio_device_selection_test_base.h" + +#include "base/test/metrics/user_action_tester.h" +#include "testing/gtest/include/gtest/gtest.h" + +const char* kInputSwitched = "StatusArea_Audio_SwitchInputDevice"; +const char* kOutputSwitched = "StatusArea_Audio_SwitchOutputDevice"; +const char* kInputOverridden = "StatusArea_Audio_AutoInputSelectionOverridden"; +const char* kOutputOverridden = + "StatusArea_Audio_AutoOutputSelectionOverridden"; + +namespace ash { +namespace { + +class AudioDeviceSelectionTest : public AudioDeviceSelectionTestBase {}; + +TEST_F(AudioDeviceSelectionTest, PlugUnplugMetricAction) { + AudioNode input1 = NewInputNode("USB"); + AudioNode input2 = NewInputNode("USB"); + AudioNode output3 = NewOutputNode("USB"); + AudioNode output4 = NewOutputNode("USB"); + + { + base::UserActionTester actions; + Plug(input1); + Plug(output3); + ASSERT_EQ(ActiveInputNodeId(), input1.id); + ASSERT_EQ(ActiveOutputNodeId(), output3.id); + Plug(input2); + Plug(output4); + ASSERT_EQ(ActiveInputNodeId(), input2.id); + ASSERT_EQ(ActiveOutputNodeId(), output4.id); + // Automatic switches should not generate events. + EXPECT_EQ(actions.GetActionCount(kInputSwitched), 0); + EXPECT_EQ(actions.GetActionCount(kOutputSwitched), 0); + EXPECT_EQ(actions.GetActionCount(kInputOverridden), 0); + EXPECT_EQ(actions.GetActionCount(kOutputOverridden), 0); + } + + { + base::UserActionTester actions; + Select(input1); + ASSERT_EQ(ActiveInputNodeId(), input1.id); + ASSERT_EQ(ActiveOutputNodeId(), output4.id); + EXPECT_EQ(actions.GetActionCount(kInputSwitched), 1); + EXPECT_EQ(actions.GetActionCount(kOutputSwitched), 0); + EXPECT_EQ(actions.GetActionCount(kInputOverridden), 1); + EXPECT_EQ(actions.GetActionCount(kOutputOverridden), 0); + } + + { + base::UserActionTester actions; + Select(output3); + ASSERT_EQ(ActiveInputNodeId(), input1.id); + ASSERT_EQ(ActiveOutputNodeId(), output3.id); + EXPECT_EQ(actions.GetActionCount(kInputSwitched), 0); + EXPECT_EQ(actions.GetActionCount(kOutputSwitched), 1); + EXPECT_EQ(actions.GetActionCount(kInputOverridden), 0); + EXPECT_EQ(actions.GetActionCount(kOutputOverridden), 1); + } + + { + base::UserActionTester actions; + Select(input2); + Select(output4); + ASSERT_EQ(ActiveInputNodeId(), input2.id); + ASSERT_EQ(ActiveOutputNodeId(), output4.id); + EXPECT_EQ(actions.GetActionCount(kInputSwitched), 1); + EXPECT_EQ(actions.GetActionCount(kOutputSwitched), 1); + // Switching back and forth should not be counted. + EXPECT_EQ(actions.GetActionCount(kInputOverridden), 0); + EXPECT_EQ(actions.GetActionCount(kOutputOverridden), 0); + } + + { + base::UserActionTester actions; + Unplug(input1); + Plug(input1); + ASSERT_EQ(ActiveInputNodeId(), input2.id); + Select(input1); + EXPECT_EQ(actions.GetActionCount(kInputSwitched), 1); + // Switching after the system decides to do nothing, should be counted. + EXPECT_EQ(actions.GetActionCount(kInputOverridden), 1); + } + + { + base::UserActionTester actions; + Unplug(output3); + Plug(output3); + ASSERT_EQ(ActiveOutputNodeId(), output4.id); + Select(output3); + EXPECT_EQ(actions.GetActionCount(kOutputSwitched), 1); + // Switching after the system decides to do nothing, should be counted. + EXPECT_EQ(actions.GetActionCount(kOutputOverridden), 1); + } +} + +} // namespace +} // namespace ash
diff --git a/chromeos/ash/components/audio/audio_devices_pref_handler.h b/chromeos/ash/components/audio/audio_devices_pref_handler.h index 6d8e86e..8e0e619 100644 --- a/chromeos/ash/components/audio/audio_devices_pref_handler.h +++ b/chromeos/ash/components/audio/audio_devices_pref_handler.h
@@ -67,7 +67,7 @@ virtual void SetUserPriorityHigherThan(const AudioDevice& target, const AudioDevice& base) = 0; // Reads the user priority from prefs. - virtual int32_t GetUserPriority(const AudioDevice& device) = 0; + virtual int GetUserPriority(const AudioDevice& device) = 0; // Reads the audio output allowed value from prefs. virtual bool GetAudioOutputAllowedValue() const = 0;
diff --git a/chromeos/ash/components/audio/audio_devices_pref_handler_impl.cc b/chromeos/ash/components/audio/audio_devices_pref_handler_impl.cc index 6b9927b..9b050dc1 100644 --- a/chromeos/ash/components/audio/audio_devices_pref_handler_impl.cc +++ b/chromeos/ash/components/audio/audio_devices_pref_handler_impl.cc
@@ -266,7 +266,7 @@ } } -int32_t AudioDevicesPrefHandlerImpl::GetUserPriority( +int AudioDevicesPrefHandlerImpl::GetUserPriority( const AudioDevice& device) { if (device.is_input) { return input_device_user_priority_settings_
diff --git a/chromeos/ash/components/audio/audio_devices_pref_handler_impl.h b/chromeos/ash/components/audio/audio_devices_pref_handler_impl.h index 8ada10b..e410ffa 100644 --- a/chromeos/ash/components/audio/audio_devices_pref_handler_impl.h +++ b/chromeos/ash/components/audio/audio_devices_pref_handler_impl.h
@@ -48,7 +48,7 @@ void SetUserPriorityHigherThan(const AudioDevice& target, const AudioDevice& base) override; - int32_t GetUserPriority(const AudioDevice& device) override; + int GetUserPriority(const AudioDevice& device) override; bool GetNoiseCancellationState() override; void SetNoiseCancellationState(bool noise_cancellation_state) override;
diff --git a/chromeos/ash/components/audio/audio_devices_pref_handler_impl_unittest.cc b/chromeos/ash/components/audio/audio_devices_pref_handler_impl_unittest.cc index 8d866a5..7f3d8b0 100644 --- a/chromeos/ash/components/audio/audio_devices_pref_handler_impl_unittest.cc +++ b/chromeos/ash/components/audio/audio_devices_pref_handler_impl_unittest.cc
@@ -179,7 +179,7 @@ : audio_pref_handler_->GetOutputVolumeValue(&device); } - double GetUserPriority(const AudioDevice& device) { + int GetUserPriority(const AudioDevice& device) { return audio_pref_handler_->GetUserPriority(device); }
diff --git a/chromeos/ash/components/audio/audio_devices_pref_handler_stub.cc b/chromeos/ash/components/audio/audio_devices_pref_handler_stub.cc index e139b3a7..fe64a02 100644 --- a/chromeos/ash/components/audio/audio_devices_pref_handler_stub.cc +++ b/chromeos/ash/components/audio/audio_devices_pref_handler_stub.cc
@@ -96,7 +96,7 @@ } } -int32_t AudioDevicesPrefHandlerStub::GetUserPriority( +int AudioDevicesPrefHandlerStub::GetUserPriority( const AudioDevice& device) { if (user_priority_map_.find(device.stable_device_id) == user_priority_map_.end())
diff --git a/chromeos/ash/components/audio/cras_audio_handler.cc b/chromeos/ash/components/audio/cras_audio_handler.cc index cd4c36a..75ce55f2 100644 --- a/chromeos/ash/components/audio/cras_audio_handler.cc +++ b/chromeos/ash/components/audio/cras_audio_handler.cc
@@ -786,11 +786,28 @@ if (active_device.is_input) { base::RecordAction( base::UserMetricsAction("StatusArea_Audio_SwitchInputDevice")); + if (!input_device_selected_by_user_) { + base::RecordAction(base::UserMetricsAction( + "StatusArea_Audio_AutoInputSelectionOverridden")); + } } else { base::RecordAction( base::UserMetricsAction("StatusArea_Audio_SwitchOutputDevice")); + if (!output_device_selected_by_user_) { + base::RecordAction(base::UserMetricsAction( + "StatusArea_Audio_AutoOutputSelectionOverridden")); + } } } + + // Update *_selected_by_user_. + // Including to unset it when selected by priority or by camera. + if (active_device.is_input) { + input_device_selected_by_user_ = activate_by == ACTIVATE_BY_USER; + } else { + output_device_selected_by_user_ = activate_by == ACTIVATE_BY_USER; + } + if (active_device.is_input) CrasAudioClient::Get()->SetActiveInputNode(active_device.id); else @@ -1535,7 +1552,7 @@ void CrasAudioHandler::HandleNonHotplugNodesChange( bool is_input, - const AudioDevicePriorityQueue& hotplug_nodes, + const AudioDevicePriorityQueue& hotplug_devices, bool has_device_change, bool has_device_removed, bool active_device_removed) { @@ -1546,7 +1563,7 @@ if (!has_device_change && has_current_active_node) return; - if (hotplug_nodes.empty()) { + if (hotplug_devices.empty()) { if (has_device_removed) { if (!active_device_removed && has_current_active_node) { // Removed a non-active device, keep the current active device. @@ -1746,22 +1763,27 @@ void CrasAudioHandler::UpdateDevicesAndSwitchActive( const AudioNodeList& nodes) { - AudioDevicePriorityQueue hotplug_output_nodes; - AudioDevicePriorityQueue hotplug_input_nodes; + AudioDevicePriorityQueue hotplug_output_devices; + AudioDevicePriorityQueue hotplug_input_devices; bool has_output_removed = false; bool has_input_removed = false; bool active_output_removed = false; bool active_input_removed = false; bool output_devices_changed = - HasDeviceChange(nodes, false, &hotplug_output_nodes, &has_output_removed, - &active_output_removed); + HasDeviceChange(nodes, false, &hotplug_output_devices, + &has_output_removed, &active_output_removed); bool input_devices_changed = - HasDeviceChange(nodes, true, &hotplug_input_nodes, &has_input_removed, + HasDeviceChange(nodes, true, &hotplug_input_devices, &has_input_removed, &active_input_removed); + std::vector<AudioDevice> devices; + devices.reserve(nodes.size()); + for (AudioNode node : nodes) { + devices.push_back(ConvertAudioNodeWithModifiedPriority(node)); + } + // Updates the display_rotation to the internal speaker when it's added. - for (auto node : nodes) { - AudioDevice device = ConvertAudioNodeWithModifiedPriority(node); + for (AudioDevice device : devices) { DeviceStatus status = CheckDeviceStatus(device); if (status == NEW_DEVICE && device.type == AudioDeviceType::kInternalSpeaker) { @@ -1778,8 +1800,7 @@ while (!output_devices_pq_.empty()) output_devices_pq_.pop(); - for (size_t i = 0; i < nodes.size(); ++i) { - AudioDevice device = ConvertAudioNodeWithModifiedPriority(nodes[i]); + for (AudioDevice device : devices) { audio_devices_[device.id] = device; if (!has_alternative_input_ && device.is_input && device.IsExternalDevice()) { @@ -1797,12 +1818,12 @@ } // Handle output device changes. - HandleAudioDeviceChange(false, output_devices_pq_, hotplug_output_nodes, + HandleAudioDeviceChange(false, output_devices_pq_, hotplug_output_devices, output_devices_changed, has_output_removed, active_output_removed); // Handle input device changes. - HandleAudioDeviceChange(true, input_devices_pq_, hotplug_input_nodes, + HandleAudioDeviceChange(true, input_devices_pq_, hotplug_input_devices, input_devices_changed, has_input_removed, active_input_removed); @@ -1820,13 +1841,24 @@ void CrasAudioHandler::HandleAudioDeviceChange( bool is_input, const AudioDevicePriorityQueue& devices_pq, - const AudioDevicePriorityQueue& hotplug_nodes, + const AudioDevicePriorityQueue& hotplug_devices, bool has_device_change, bool has_device_removed, bool active_device_removed) { uint64_t& active_node_id = is_input ? active_input_node_id_ : active_output_node_id_; + if (has_device_change) { + // Mark device selected by the system, including when the algorithm + // does nothing ultimately. We still treat not switching the device + // as a decision of the algorithm. + if (is_input) { + input_device_selected_by_user_ = false; + } else { + output_device_selected_by_user_ = false; + } + } + // No audio devices found. if (devices_pq.empty()) { VLOG(1) << "No " << (is_input ? "input" : "output") << " devices found"; @@ -1842,12 +1874,13 @@ if (!active_device || !active_device->active) active_node_id = 0; - if (!active_node_id || hotplug_nodes.empty() || hotplug_nodes.size() > 1) { - HandleNonHotplugNodesChange(is_input, hotplug_nodes, has_device_change, + if (!active_node_id || hotplug_devices.empty() || + hotplug_devices.size() > 1) { + HandleNonHotplugNodesChange(is_input, hotplug_devices, has_device_change, has_device_removed, active_device_removed); } else { // Typical user hotplug case. - HandleHotPlugDevice(hotplug_nodes.top(), devices_pq); + HandleHotPlugDevice(hotplug_devices.top(), devices_pq); } }
diff --git a/chromeos/ash/components/audio/cras_audio_handler.h b/chromeos/ash/components/audio/cras_audio_handler.h index cb4cad5..c7d9379 100644 --- a/chromeos/ash/components/audio/cras_audio_handler.h +++ b/chromeos/ash/components/audio/cras_audio_handler.h
@@ -814,6 +814,10 @@ // In this case, input mute changes will be disabled. bool input_muted_by_microphone_mute_switch_ = false; + // Whether the audio device was selected by user, to track user overrides + bool input_device_selected_by_user_ = false; + bool output_device_selected_by_user_ = false; + // Task runner of browser main thread. All member variables should be accessed // on this thread. scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
diff --git a/chromeos/components/cdm_factory_daemon/cdm_factory_daemon_proxy_lacros.cc b/chromeos/components/cdm_factory_daemon/cdm_factory_daemon_proxy_lacros.cc index ee5538c..84df9f42 100644 --- a/chromeos/components/cdm_factory_daemon/cdm_factory_daemon_proxy_lacros.cc +++ b/chromeos/components/cdm_factory_daemon/cdm_factory_daemon_proxy_lacros.cc
@@ -94,13 +94,14 @@ return; } - if (!LacrosService::Get()->IsBrowserCdmFactoryAvailable()) { + auto* service = LacrosService::Get(); + if (!service || !service->IsBrowserCdmFactoryAvailable()) { std::move(callback).Run(); return; } // For Lacros, we connect to the ash-chrome browser process which will proxy // the connection to the daemon. - LacrosService::Get()->BindBrowserCdmFactory( + service->BindBrowserCdmFactory( mojo::GenericPendingReceiver(ash_remote_.BindNewPipeAndPassReceiver())); std::move(callback).Run(); return;
diff --git a/chromeos/services/machine_learning/cpp/lacros/service_connection_lacros.cc b/chromeos/services/machine_learning/cpp/lacros/service_connection_lacros.cc index 8b6858f55..49f01111 100644 --- a/chromeos/services/machine_learning/cpp/lacros/service_connection_lacros.cc +++ b/chromeos/services/machine_learning/cpp/lacros/service_connection_lacros.cc
@@ -43,19 +43,24 @@ chromeos::machine_learning::mojom::MachineLearningService& ServiceConnectionLacros::GetMachineLearningService() { + // TODO(crbug.com/1374564): Determine whether it is safe to assume + // LacrosService is always available here. + auto* service = chromeos::LacrosService::Get(); + DCHECK(service); mojo::Remote<chromeos::machine_learning::mojom::MachineLearningService>& - machine_learning_service_remote = - chromeos::LacrosService::Get() - ->GetRemote< - chromeos::machine_learning::mojom::MachineLearningService>(); + machine_learning_service_remote = service->GetRemote< + chromeos::machine_learning::mojom::MachineLearningService>(); return *machine_learning_service_remote.get(); } void ServiceConnectionLacros::BindMachineLearningService( mojo::PendingReceiver< chromeos::machine_learning::mojom::MachineLearningService> receiver) { - chromeos::LacrosService::Get()->BindMachineLearningService( - std::move(receiver)); + // TODO(crbug.com/1374564): Determine whether it is safe to assume + // LacrosService is always available here. + auto* service = chromeos::LacrosService::Get(); + DCHECK(service); + service->BindMachineLearningService(std::move(receiver)); } void ServiceConnectionLacros::Initialize() {}
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni index edd4e1f6..0dac43fb 100644 --- a/chromeos/tast_control.gni +++ b/chromeos/tast_control.gni
@@ -294,6 +294,21 @@ # b/249879125 "launcher.SearchBuiltInApps.tablet_mode", + # http://b/250566486 + "lacros.Basic", + "lacros.ShelfLaunch", + "lacros.ShelfLaunch.primary", + "lacros.AppLauncherLaunch", + "lacros.AudioPinnedStream.play", + "mlservice.WebHandwritingRecognitionNotSupported.lacros", + "platform.PerfettoChromeConsumer.lacros", + "power.SmartDim.lacros", + + # https://crbug.com/1371407 and http://b/250566486. + "lacros.AudioPinnedStream.record", + "lacros.AudioPlay", + "lacros.AudioRecord", + # https://crbug.com/1356506 "inputs.PhysicalKeyboardCantoneseTyping", "inputs.PhysicalKeyboardCantoneseTyping.lacros",
diff --git a/components/feed/core/v2/feed_stream.cc b/components/feed/core/v2/feed_stream.cc index 38e882f..b475e65 100644 --- a/components/feed/core/v2/feed_stream.cc +++ b/components/feed/core/v2/feed_stream.cc
@@ -1399,6 +1399,12 @@ info_card_tracker_.ResetState(info_card_type); } +void FeedStream::ReportContentSliceVisibleTimeForGoodVisits( + base::TimeDelta elapsed) { + metrics_reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + elapsed); +} + void FeedStream::SetContentOrder(const StreamType& stream_type, ContentOrder content_order) { if (!stream_type.IsWebFeed()) {
diff --git a/components/feed/core/v2/feed_stream.h b/components/feed/core/v2/feed_stream.h index 6c4cbcbf..3d78ecb 100644 --- a/components/feed/core/v2/feed_stream.h +++ b/components/feed/core/v2/feed_stream.h
@@ -178,6 +178,8 @@ int info_card_type) override; void ResetInfoCardStates(const StreamType& stream_type, int info_card_type) override; + void ReportContentSliceVisibleTimeForGoodVisits( + base::TimeDelta elapsed) override; base::Time GetLastFetchTime(const StreamType& stream_type) override; void SetContentOrder(const StreamType& stream_type, ContentOrder content_order) override;
diff --git a/components/feed/core/v2/feedstore_util.cc b/components/feed/core/v2/feedstore_util.cc index 59acdf7d..c5f2c30a 100644 --- a/components/feed/core/v2/feedstore_util.cc +++ b/components/feed/core/v2/feedstore_util.cc
@@ -24,17 +24,22 @@ DCHECK(stream_type.IsChannelFeed()); std::string encoding; base::Base64Encode(stream_type.GetWebFeedId(), &encoding); - return encoding; + return std::string(kChannelStreamKeyPrefix) + encoding; } -StreamType StreamTypeFromKey(std::string key) { +StreamType StreamTypeFromKey(base::StringPiece key) { if (key == kForYouStreamKey) return StreamType(feed::StreamKind::kForYou); if (key == kFollowStreamKey) return StreamType(feed::StreamKind::kFollowing); - std::string channel_key; - if (base::Base64Decode(key, &channel_key)) - return StreamType(feed::StreamKind::kChannel, channel_key); + if (base::StartsWith(key, kChannelStreamKeyPrefix, + base::CompareCase::SENSITIVE)) { + std::string channel_key; + if (base::Base64Decode(key.substr(kChannelStreamKeyPrefix.size()), + &channel_key)) { + return StreamType(feed::StreamKind::kChannel, channel_key); + } + } return {}; }
diff --git a/components/feed/core/v2/feedstore_util.h b/components/feed/core/v2/feedstore_util.h index 7c61b4c..0a987ade 100644 --- a/components/feed/core/v2/feedstore_util.h +++ b/components/feed/core/v2/feedstore_util.h
@@ -22,9 +22,10 @@ const char kForYouStreamKey[] = "i"; const char kFollowStreamKey[] = "w"; +constexpr base::StringPiece kChannelStreamKeyPrefix = "c"; std::string StreamKey(const feed::StreamType& stream_type); -feed::StreamType StreamTypeFromKey(std::string key); +feed::StreamType StreamTypeFromKey(base::StringPiece key); /////////////////////////////////////////////////// // Functions that operate on feedstore proto types.
diff --git a/components/feed/core/v2/feedstore_util_unittest.cc b/components/feed/core/v2/feedstore_util_unittest.cc index 35707055..119dcc8 100644 --- a/components/feed/core/v2/feedstore_util_unittest.cc +++ b/components/feed/core/v2/feedstore_util_unittest.cc
@@ -88,6 +88,7 @@ EXPECT_TRUE(StreamTypeFromKey(StreamKey(following)).IsWebFeed()); EXPECT_TRUE(StreamTypeFromKey(StreamKey(for_you)).IsForYou()); + EXPECT_EQ(StreamTypeFromKey("z"), StreamType()); } } // namespace
diff --git a/components/feed/core/v2/metrics_reporter.cc b/components/feed/core/v2/metrics_reporter.cc index 941d52e2..a31c99db 100644 --- a/components/feed/core/v2/metrics_reporter.cc +++ b/components/feed/core/v2/metrics_reporter.cc
@@ -738,7 +738,7 @@ } } -void MetricsReporter::ReportStableContentSliceVisibilityTime( +void MetricsReporter::ReportStableContentSliceVisibilityTimeForGoodVisits( base::TimeDelta delta) { if (good_visit_state_) good_visit_state_->AddTimeInFeed(delta);
diff --git a/components/feed/core/v2/metrics_reporter.h b/components/feed/core/v2/metrics_reporter.h index 0a0d17a..6713013 100644 --- a/components/feed/core/v2/metrics_reporter.h +++ b/components/feed/core/v2/metrics_reporter.h
@@ -77,10 +77,10 @@ void PageLoaded(); void OtherUserAction(const StreamType& stream_type, FeedUserActionType action_type); - // Report a period of time during which at least one content slice was >50% - // visible and covered >25% of the viewport. - // TODO(iwells): Call this. - void ReportStableContentSliceVisibilityTime(base::TimeDelta delta); + // Report a period of time during which at least one content slice was visible + // enough or covering enough of the viewport. + void ReportStableContentSliceVisibilityTimeForGoodVisits( + base::TimeDelta delta); // Indicates the user scrolled the feed by |distance_dp| and then stopped // scrolling.
diff --git a/components/feed/core/v2/metrics_reporter_unittest.cc b/components/feed/core/v2/metrics_reporter_unittest.cc index 24a583f..c3633d8 100644 --- a/components/feed/core/v2/metrics_reporter_unittest.cc +++ b/components/feed/core/v2/metrics_reporter_unittest.cc
@@ -1370,22 +1370,26 @@ TEST_F(MetricsReporterTest, GoodVisit_Scroll_GoodTimeSpentInFeed) { reporter_->StreamScrolled(StreamType(StreamKind::kForYou), 1); - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(30)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(30)); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 0); // Passing a minute in the feed should log a Good Visit since a scroll // happened. - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(30)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(30)); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 1); } TEST_F(MetricsReporterTest, GoodVisit_GoodTimeSpentInFeed_Scroll) { - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(30)); - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(30)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(30)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(30)); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 0); @@ -1400,22 +1404,25 @@ TEST_F(MetricsReporterTest, GoodVisit_SmallTimesDroppped) { // Reach 59.9 seconds and a scroll. - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(30)); - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(29) + - base::Milliseconds(900)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(30)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(29) + base::Milliseconds(900)); reporter_->StreamScrolled(StreamType(StreamKind::kForYou), 1); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 0); // Ignore less than half a second. - reporter_->ReportStableContentSliceVisibilityTime(base::Milliseconds(200)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Milliseconds(200)); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 0); // More than half a second counts. - reporter_->ReportStableContentSliceVisibilityTime(base::Milliseconds(501)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Milliseconds(501)); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 1); @@ -1424,14 +1431,17 @@ TEST_F(MetricsReporterTest, GoodVisit_LargeTimesCapped) { reporter_->StreamScrolled(StreamType(StreamKind::kForYou), 1); // Capped to 30 seconds. - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(61)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(61)); // 59 seconds so far. - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(29)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(29)); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 0); - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(2)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(2)); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 1); @@ -1443,20 +1453,23 @@ kClientGoodVisits, {{"min_stable_content_slice_visibility_time", "200ms"}}); // Reach 59.9 seconds and a scroll. - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(30)); - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(29) + - base::Milliseconds(900)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(30)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(29) + base::Milliseconds(900)); reporter_->StreamScrolled(StreamType(StreamKind::kForYou), 1); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 0); - reporter_->ReportStableContentSliceVisibilityTime(base::Milliseconds(150)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Milliseconds(150)); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 0); - reporter_->ReportStableContentSliceVisibilityTime(base::Milliseconds(201)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Milliseconds(201)); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 1); @@ -1468,14 +1481,17 @@ kClientGoodVisits, {{"max_stable_content_slice_visibility_time", "40s"}}); reporter_->StreamScrolled(StreamType(StreamKind::kForYou), 1); // Capped to 40 seconds. - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(61)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(61)); // 59 seconds so far. - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(19)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(19)); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 0); - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(2)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(2)); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 1); @@ -1563,11 +1579,13 @@ kClientGoodVisits, {{"good_time_in_feed", "45s"}}); reporter_->StreamScrolled(StreamType(StreamKind::kForYou), 1); - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(30)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(30)); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 0); - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(15)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(15)); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 1); @@ -1638,8 +1656,10 @@ task_environment_.FastForwardBy(base::Minutes(5)); reporter_->StreamScrolled(StreamType(StreamKind::kForYou), 1); - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(30)); - reporter_->ReportStableContentSliceVisibilityTime(base::Seconds(30)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(30)); + reporter_->ReportStableContentSliceVisibilityTimeForGoodVisits( + base::Seconds(30)); histogram_.ExpectBucketCount( "ContentSuggestions.Feed.AllFeeds.EngagementType", FeedEngagementType::kGoodVisit, 0);
diff --git a/components/feed/core/v2/public/feed_api.h b/components/feed/core/v2/public/feed_api.h index 0a0c32f..e77af81 100644 --- a/components/feed/core/v2/public/feed_api.h +++ b/components/feed/core/v2/public/feed_api.h
@@ -207,6 +207,12 @@ // Resets all the states of the info card. virtual void ResetInfoCardStates(const StreamType& stream_type, int info_card_type) = 0; + // Report a period of time for which at least one content slice is visible + // enough or at least one content slice covers enough of the viewport. See the + // slice_exposure_threshold and slice_coverage_threshold feature params for + // what counts as visible enough and covering enough. + virtual void ReportContentSliceVisibleTimeForGoodVisits( + base::TimeDelta elapsed) = 0; // The following methods are used for the internals page.
diff --git a/components/feed/core/v2/public/test/stub_feed_api.h b/components/feed/core/v2/public/test/stub_feed_api.h index 75ec307..102961c 100644 --- a/components/feed/core/v2/public/test/stub_feed_api.h +++ b/components/feed/core/v2/public/test/stub_feed_api.h
@@ -99,6 +99,8 @@ int info_card_type) override {} void ResetInfoCardStates(const StreamType& stream_type, int info_card_type) override {} + void ReportContentSliceVisibleTimeForGoodVisits( + base::TimeDelta elapsed) override {} DebugStreamData GetDebugStreamData() override; void ForceRefreshForDebugging(const StreamType& stream_type) override {} std::string DumpStateForDebugging() override;
diff --git a/components/feed/feed_feature_list.cc b/components/feed/feed_feature_list.cc index 527974c..b40d02ba 100644 --- a/components/feed/feed_feature_list.cc +++ b/components/feed/feed_feature_list.cc
@@ -190,4 +190,10 @@ &kClientGoodVisits, "max_stable_content_slice_visibility_time", base::Seconds(30)}; +const base::FeatureParam<double> kSliceVisibleExposureThreshold{ + &kClientGoodVisits, "slice_exposure_threshold", 0.5f}; + +const base::FeatureParam<double> kSliceVisibleCoverageThreshold{ + &kClientGoodVisits, "slice_coverage_threshold", 0.25f}; + } // namespace feed
diff --git a/components/feed/feed_feature_list.h b/components/feed/feed_feature_list.h index edce9f13..a3be964 100644 --- a/components/feed/feed_feature_list.h +++ b/components/feed/feed_feature_list.h
@@ -164,6 +164,11 @@ // viewport-stable feed viewing to this time. extern const base::FeatureParam<base::TimeDelta> kMaxStableContentSliceVisibilityTime; +// Minimum slice exposure needed for counting time in feed for good visits. +extern const base::FeatureParam<double> kSliceVisibleExposureThreshold; +// Minimum slice coverage of viewport needed for counting time in feed for good +// visits. +extern const base::FeatureParam<double> kSliceVisibleCoverageThreshold; } // namespace feed
diff --git a/components/history/core/browser/expire_history_backend.cc b/components/history/core/browser/expire_history_backend.cc index 6a286cad..785d36e 100644 --- a/components/history/core/browser/expire_history_backend.cc +++ b/components/history/core/browser/expire_history_backend.cc
@@ -6,6 +6,7 @@ #include <stddef.h> +#include <algorithm> #include <functional> #include <limits> #include <memory> @@ -20,7 +21,6 @@ #include "base/location.h" #include "base/metrics/histogram_macros.h" #include "base/numerics/safe_conversions.h" -#include "base/ranges/algorithm.h" #include "base/task/sequenced_task_runner.h" #include "build/build_config.h" #include "components/favicon/core/favicon_database.h" @@ -262,8 +262,10 @@ // `times` must be in reverse chronological order and have no // duplicates, i.e. each member must be earlier than the one before // it. - DCHECK(base::ranges::adjacent_find(times, std::less_equal<base::Time>()) == - times.end()); + DCHECK( + std::adjacent_find( + times.begin(), times.end(), std::less_equal<base::Time>()) == + times.end()); if (!main_db_) return;
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc index 16f9333..4c4ff67a 100644 --- a/components/omnibox/browser/autocomplete_controller.cc +++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -912,8 +912,6 @@ } } - const auto last_result_for_logging = result_.GetMatchDedupComparators(); - if (regenerate_result) result_.Reset(); @@ -971,13 +969,6 @@ result_.Validate(); #endif // DCHECK_IS_ON() - // Will log metrics for how many matches changed. Will also log timing metrics - // for the current request if it's complete; otherwise, will just update - // timestamps of when the last update changing any or the default suggestion - // occurred. - metrics_.OnUpdateResult(last_result_for_logging, - result_.GetMatchDedupComparators()); - // Below are all annotations after the match list is ready. // Only produce Pedals for the default focus case (not on focus or on delete). @@ -1220,10 +1211,19 @@ } void AutocompleteController::NotifyChanged() { - // `CopyFrom()` does a vector copy, and `NotifyChanged()` is called a lot, so - // guard the copy to measure performance regressions. + // Will log metrics for how many matches changed. Will also log timing metrics + // for the current request if it's complete; otherwise, will just update + // timestamps of when the last update changed any or the default suggestion. + metrics_.OnNotifyChanged(last_result_for_logging_, + result_.GetMatchDedupComparators()); + + // `NotifyChanged()` is called a lot, so guard the copies so performance + // differences between them are also measured. if (DebouncingEnabled()) published_result_.CopyFrom(result_); + + last_result_for_logging_ = result_.GetMatchDedupComparators(); + for (Observer& obs : observers_) obs.OnResultChanged(this, notify_changed_default_match_); notify_changed_debouncer_.CancelRequest();
diff --git a/components/omnibox/browser/autocomplete_controller.h b/components/omnibox/browser/autocomplete_controller.h index 1336ae72..2879e61 100644 --- a/components/omnibox/browser/autocomplete_controller.h +++ b/components/omnibox/browser/autocomplete_controller.h
@@ -379,6 +379,10 @@ // empty and unused. AutocompleteResult published_result_; + // Used for logging the changes between updates. + std::vector<AutocompleteResult::MatchDedupComparator> + last_result_for_logging_; + // The most recent time the default match (inline match) changed. This may // be earlier than the most recent keystroke if the recent keystrokes didn't // change the suggested match in the omnibox. (For instance, if
diff --git a/components/omnibox/browser/autocomplete_controller_metrics.cc b/components/omnibox/browser/autocomplete_controller_metrics.cc index 9509ef3..534c917 100644 --- a/components/omnibox/browser/autocomplete_controller_metrics.cc +++ b/components/omnibox/browser/autocomplete_controller_metrics.cc
@@ -25,9 +25,13 @@ last_default_change_time_ = start_time_; } -void AutocompleteControllerMetrics::OnUpdateResult( +void AutocompleteControllerMetrics::OnNotifyChanged( std::vector<AutocompleteResult::MatchDedupComparator> last_result, std::vector<AutocompleteResult::MatchDedupComparator> new_result) { + // Only log metrics for async requests. + if (controller_.input().omit_asynchronous_matches()) + return; + // If results are empty then the omnibox is likely closed, and clearing old // results won't be user visible. E.g., this occurs when opening a new tab // while the popup was open. @@ -48,14 +52,11 @@ LogSuggestionChangeInAnyPositionMetrics(any_match_changed_or_removed); // Log suggestion finalization times. + // This handles logging as soon as the final update occurs, while `OnStop()` + // handles the case where the final update never occurs because of + // interruptions. - // Only log suggestion finalization metrics for async requests. This handles - // logging as soon as the final update occurs, while `OnStop()` handles the - // case where the final update never occurs because of interruptions. - // TODO(manukh): Consider adding this filter to the above metrics as well. - if (controller_.input().omit_asynchronous_matches()) - return; - // E.g., suggestion deletion can call `OnUpdateResult()` after the controller + // E.g., suggestion deletion can call `OnNotifyChanged()` after the controller // is done and finalization metrics have been logged. They shouldn't be // re-logged. if (logged_finalization_metrics_) @@ -80,6 +81,13 @@ void AutocompleteControllerMetrics::OnProviderUpdate( const AutocompleteProvider& provider) const { + // Only log metrics for async requests. This will likely never happen, since + // `OnProviderUpdate()` is only called by async providers (but not necessarily + // async'ly, see the comments in + // `AutocompleteController::OnProviderUpdate()`). + if (controller_.input().omit_asynchronous_matches()) + return; + // Some async providers may produce multiple updates. Only log the final async // update. if (provider.done()) @@ -87,6 +95,10 @@ } void AutocompleteControllerMetrics::OnStop() { + // Only log metrics for async requests. + if (controller_.input().omit_asynchronous_matches()) + return; + // Done providers should already be logged by `OnProviderUpdate()`. for (const auto& provider : controller_.providers()) { if (!provider->done()) { @@ -95,7 +107,7 @@ } } - // If the controller is done, `OnUpdateResult()` should have already logged + // If the controller is done, `OnNotifyChanged()` should have already logged // finalization metrics. This case, i.e. `OnStop()` invoked even though the // controller is done, is possible because 1) `OnStart()` calls `OnStop()` // and 2) `AutocompleteController::stop_timer_` may fire after the controller @@ -133,7 +145,7 @@ const std::string& name, bool completed, const base::TimeTicks end_time) const { - const auto name_prefix = "Omnibox.AsyncAutocompletionTime." + name; + const auto name_prefix = "Omnibox.AsyncAutocompletionTime2." + name; const auto elapsed_time = end_time - start_time_; // These metrics are logged up to about 40 times per omnibox keystroke. But // use UMA functions as the names are dynamic. @@ -146,7 +158,7 @@ void AutocompleteControllerMetrics::LogSuggestionChangeIndexMetrics( size_t change_index) const { - std::string name = "Omnibox.MatchStability.MatchChangeIndex"; + std::string name = "Omnibox.MatchStability2.MatchChangeIndex"; size_t max = AutocompleteResult::kMaxAutocompletePositionValue; // These metrics are logged up to about 50 times per omnibox keystroke, so use // UMA macros for efficiency. @@ -159,7 +171,7 @@ void AutocompleteControllerMetrics::LogSuggestionChangeInAnyPositionMetrics( bool changed) const { - std::string name = "Omnibox.MatchStability.MatchChangeInAnyPosition"; + std::string name = "Omnibox.MatchStability2.MatchChangeInAnyPosition"; // These metrics are logged up to about 5 times per omnibox keystroke, so // use UMA macros for efficiency. if (controller_.in_start())
diff --git a/components/omnibox/browser/autocomplete_controller_metrics.h b/components/omnibox/browser/autocomplete_controller_metrics.h index 57030668..642c26ef 100644 --- a/components/omnibox/browser/autocomplete_controller_metrics.h +++ b/components/omnibox/browser/autocomplete_controller_metrics.h
@@ -48,12 +48,12 @@ // updated for the new request. void OnStart(); - // Called when `AutocompleteController::UpdateResult()` is called. Will log + // Called when `AutocompleteController::NotifyChanged()` is called. Will log // metrics on how many suggestions changed with this update. If the controller // is done, will also log suggestion finalization metrics; otherwise, future // calls to `OnProviderUpdate()`, `OnStop()`, or `OnStart()` will log // suggestion finalization metrics. - void OnUpdateResult( + void OnNotifyChanged( std::vector<AutocompleteResult::MatchDedupComparator> last_result, std::vector<AutocompleteResult::MatchDedupComparator> new_result);
diff --git a/components/omnibox/browser/autocomplete_controller_metrics_unittest.cc b/components/omnibox/browser/autocomplete_controller_metrics_unittest.cc index 441c9d05..5872e77 100644 --- a/components/omnibox/browser/autocomplete_controller_metrics_unittest.cc +++ b/components/omnibox/browser/autocomplete_controller_metrics_unittest.cc
@@ -57,7 +57,7 @@ controller_.in_start_ = true; controller_.done_ = sync_results_only; task_environment_.FastForwardBy(base::Milliseconds(sync_milliseconds)); - metrics_.OnUpdateResult(old_results, sync_results); + metrics_.OnNotifyChanged(old_results, sync_results); controller_.in_start_ = false; } @@ -82,7 +82,7 @@ // Convenience method to check the buckets of a single metric. void ExpectMetrics(const std::string metric_name, std::vector<base::Bucket> expected_buckets) { - const std::string prefix = "Omnibox.AsyncAutocompletionTime."; + const std::string prefix = "Omnibox.AsyncAutocompletionTime2."; EXPECT_THAT( histogram_tester_->GetAllSamples(prefix + metric_name), ElementsAreArray(expected_buckets.data(), expected_buckets.size())) @@ -182,14 +182,14 @@ SimulateStart(false, 1, {}, {{}}); // 1st async update. task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({}, {{}}); + metrics_.OnNotifyChanged({}, {{}}); // 2nd async update. task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({}, {{}}); + metrics_.OnNotifyChanged({}, {{}}); // Last async update. task_environment_.FastForwardBy(base::Milliseconds(1)); controller_.done_ = true; - metrics_.OnUpdateResult({}, {{}}); + metrics_.OnNotifyChanged({}, {{}}); ExpectSingleCountSuggestionFinalizationMetrics(4, 4, 4, true); StopAndExpectNoSuggestionFinalizationMetrics(); } @@ -200,14 +200,14 @@ SimulateStart(false, 1, {{}}, {{}}); // 1st async update. task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); // 2nd async update. task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); // Last async update. task_environment_.FastForwardBy(base::Milliseconds(1)); controller_.done_ = true; - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); ExpectSingleCountSuggestionFinalizationMetrics(4, 0, 0, true); StopAndExpectNoSuggestionFinalizationMetrics(); } @@ -219,14 +219,14 @@ SimulateStart(false, 1, {{}}, {{}}); // 1st async update. task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); // 2nd async update. task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); // Last async update. task_environment_.FastForwardBy(base::Milliseconds(1)); controller_.done_ = true; - metrics_.OnUpdateResult({}, {{}}); + metrics_.OnNotifyChanged({}, {{}}); ExpectSingleCountSuggestionFinalizationMetrics(4, 4, 4, true); StopAndExpectNoSuggestionFinalizationMetrics(); } @@ -238,14 +238,14 @@ SimulateStart(false, 1, {{}}, {{}}); // 1st async update. task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({}, {{}}); + metrics_.OnNotifyChanged({}, {{}}); // 2nd async update. task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); // Last async update. task_environment_.FastForwardBy(base::Milliseconds(1)); controller_.done_ = true; - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); ExpectSingleCountSuggestionFinalizationMetrics(4, 2, 2, true); StopAndExpectNoSuggestionFinalizationMetrics(); } @@ -256,14 +256,14 @@ SimulateStart(false, 1, {}, {{}}); // 1st async update. task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); // 2nd async update. task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); // Last async update. task_environment_.FastForwardBy(base::Milliseconds(1)); controller_.done_ = true; - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); ExpectSingleCountSuggestionFinalizationMetrics(4, 1, 1, true); StopAndExpectNoSuggestionFinalizationMetrics(); } @@ -277,7 +277,7 @@ SimulateStart(false, 1, {}, {{}}); // 1st async update. task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); // Stop timer. task_environment_.FastForwardBy(base::Milliseconds(1)); metrics_.OnStop(); @@ -291,7 +291,7 @@ SimulateStart(false, 1, {}, {{}}); // 1 async update for 1st input. task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({}, {{}}); + metrics_.OnNotifyChanged({}, {{}}); ExpectNoSuggestionFinalizationMetrics(); // Interrupted by 2nd input. The log should include the time until // interruption. @@ -306,12 +306,12 @@ // 1 async update for 3rd input. Controller completes with the 2nd update. ResetHistogramTester(); task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({}, {{}}); + metrics_.OnNotifyChanged({}, {{}}); ExpectNoSuggestionFinalizationMetrics(); // 2nd and last async update for 3rd input. controller_.done_ = true; task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({}, {{}}); + metrics_.OnNotifyChanged({}, {{}}); ExpectSingleCountSuggestionFinalizationMetrics(3, 3, 3, true); StopAndExpectNoSuggestionFinalizationMetrics(); } @@ -322,11 +322,11 @@ SimulateStart(false, 1, {}, {{}}); // 1st async update with non-default match changed. task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({{}}, {{}, {}}); + metrics_.OnNotifyChanged({{}}, {{}, {}}); // 2nd async update with no matches changed. task_environment_.FastForwardBy(base::Milliseconds(1)); controller_.done_ = true; - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); ExpectSingleCountSuggestionFinalizationMetrics(3, 2, 1, true); StopAndExpectNoSuggestionFinalizationMetrics(); } @@ -346,7 +346,7 @@ metrics_.OnProviderUpdate(*async_provider_done_sync); ExpectProviderMetrics(async_provider_done_sync->GetName(), 1, true); controller_.done_ = false; - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); controller_.in_start_ = false; ExpectNoSuggestionFinalizationMetrics(); ResetHistogramTester(); @@ -355,7 +355,7 @@ task_environment_.FastForwardBy(base::Milliseconds(1)); metrics_.OnProviderUpdate(*async_provider_done_not_last); ExpectProviderMetrics(async_provider_done_not_last->GetName(), 2, true); - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); ExpectNoSuggestionFinalizationMetrics(); ResetHistogramTester(); @@ -364,7 +364,7 @@ metrics_.OnProviderUpdate(*async_provider_done_last); controller_.done_ = true; ExpectProviderMetrics(async_provider_done_last->GetName(), 3, true); - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); ExpectSingleCountSuggestionFinalizationMetrics(3, 0, 0, true); StopAndExpectNoSuggestionFinalizationMetrics(); @@ -385,20 +385,20 @@ metrics_.OnProviderUpdate(*provider); controller_.done_ = false; task_environment_.FastForwardBy(base::Milliseconds(1)); - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); controller_.in_start_ = false; // 1st async update without completion. task_environment_.FastForwardBy(base::Milliseconds(1)); metrics_.OnProviderUpdate(*provider); - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); // Last async update with completion. task_environment_.FastForwardBy(base::Milliseconds(1)); provider->done_ = true; controller_.done_ = true; metrics_.OnProviderUpdate(*provider); - metrics_.OnUpdateResult({{}}, {{}}); + metrics_.OnNotifyChanged({{}}, {{}}); ExpectProviderMetrics(provider->GetName(), 3, true); ExpectSingleCountSuggestionFinalizationMetrics(3, 0, 0, true); @@ -451,90 +451,93 @@ // Verify logging to the Async* histograms. controller_.in_start_ = false; - metrics_.OnUpdateResult(first_result, second_result); + metrics_.OnNotifyChanged(first_result, second_result); // Expect the default match, third match, and last two matches to be logged // as changed, and nothing else. EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeIndex.Async"), + "Omnibox.MatchStability2.MatchChangeIndex.Async"), testing::ElementsAre(base::Bucket(0, 1), base::Bucket(2, 1), base::Bucket(3, 1), base::Bucket(4, 1))); // Expect that we log that at least one of the matches has changed. EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeInAnyPosition.Async"), + "Omnibox.MatchStability2.MatchChangeInAnyPosition.Async"), testing::ElementsAre(base::Bucket(1, 1))); // Expect that we don't log async updates to the sync histograms. EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeIndex.CrossInput"), + "Omnibox.MatchStability2.MatchChangeIndex.CrossInput"), testing::ElementsAre()); - EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeInAnyPosition.CrossInput"), - testing::ElementsAre()); + EXPECT_THAT( + histogram_tester_->GetAllSamples( + "Omnibox.MatchStability2.MatchChangeInAnyPosition.CrossInput"), + testing::ElementsAre()); // Verify the unsliced histograms. EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeIndex"), + "Omnibox.MatchStability2.MatchChangeIndex"), testing::ElementsAre(base::Bucket(0, 1), base::Bucket(2, 1), base::Bucket(3, 1), base::Bucket(4, 1))); EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeInAnyPosition"), + "Omnibox.MatchStability2.MatchChangeInAnyPosition"), testing::ElementsAre(base::Bucket(1, 1))); ResetHistogramTester(); // Verify logging to the CrossInput* histograms. controller_.in_start_ = true; - metrics_.OnUpdateResult(first_result, second_result); + metrics_.OnNotifyChanged(first_result, second_result); // Expect the default match, third match, and last two matches to be logged // as changed, and nothing else. EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeIndex.CrossInput"), + "Omnibox.MatchStability2.MatchChangeIndex.CrossInput"), testing::ElementsAre(base::Bucket(0, 1), base::Bucket(2, 1), base::Bucket(3, 1), base::Bucket(4, 1))); // Expect that we log that at least one of the matches has changed. - EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeInAnyPosition.CrossInput"), - testing::ElementsAre(base::Bucket(1, 1))); + EXPECT_THAT( + histogram_tester_->GetAllSamples( + "Omnibox.MatchStability2.MatchChangeInAnyPosition.CrossInput"), + testing::ElementsAre(base::Bucket(1, 1))); // Expect that we don't log sync updates to the async histograms. EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeIndex.Async"), + "Omnibox.MatchStability2.MatchChangeIndex.Async"), testing::ElementsAre()); EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeInAnyPosition.Async"), + "Omnibox.MatchStability2.MatchChangeInAnyPosition.Async"), testing::ElementsAre()); // Verify the unsliced histograms. EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeIndex"), + "Omnibox.MatchStability2.MatchChangeIndex"), testing::ElementsAre(base::Bucket(0, 1), base::Bucket(2, 1), base::Bucket(3, 1), base::Bucket(4, 1))); // Expect that we log that at least one of the matches has changed. EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeInAnyPosition"), + "Omnibox.MatchStability2.MatchChangeInAnyPosition"), testing::ElementsAre(base::Bucket(1, 1))); ResetHistogramTester(); // Verify no logging when appending matches. controller_.in_start_ = false; - metrics_.OnUpdateResult(second_result, third_result); + metrics_.OnNotifyChanged(second_result, third_result); controller_.in_start_ = true; - metrics_.OnUpdateResult(second_result, third_result); + metrics_.OnNotifyChanged(second_result, third_result); // Expect no changes logged; expect 1 false logged to // *MatchChangedInAnyPosition. EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeIndex.Async"), + "Omnibox.MatchStability2.MatchChangeIndex.Async"), testing::ElementsAre()); EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeInAnyPosition.Async"), + "Omnibox.MatchStability2.MatchChangeInAnyPosition.Async"), testing::ElementsAre(base::Bucket(0, 1))); EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeIndex.CrossInput"), + "Omnibox.MatchStability2.MatchChangeIndex.CrossInput"), testing::ElementsAre()); - EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeInAnyPosition.CrossInput"), - testing::ElementsAre(base::Bucket(0, 1))); + EXPECT_THAT( + histogram_tester_->GetAllSamples( + "Omnibox.MatchStability2.MatchChangeInAnyPosition.CrossInput"), + testing::ElementsAre(base::Bucket(0, 1))); // Verify the unsliced histograms. EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeIndex"), + "Omnibox.MatchStability2.MatchChangeIndex"), testing::ElementsAre()); EXPECT_THAT(histogram_tester_->GetAllSamples( - "Omnibox.MatchStability.MatchChangeInAnyPosition"), + "Omnibox.MatchStability2.MatchChangeInAnyPosition"), testing::ElementsAre(base::Bucket(0, 2))); ResetHistogramTester(); }
diff --git a/components/omnibox/browser/in_memory_url_index_unittest.cc b/components/omnibox/browser/in_memory_url_index_unittest.cc index 6347206..f7cdcbd 100644 --- a/components/omnibox/browser/in_memory_url_index_unittest.cc +++ b/components/omnibox/browser/in_memory_url_index_unittest.cc
@@ -7,6 +7,7 @@ #include <stddef.h> #include <stdint.h> +#include <algorithm> #include <fstream> #include <memory> #include <numeric> @@ -20,7 +21,6 @@ #include "base/i18n/case_conversion.h" #include "base/memory/raw_ptr.h" #include "base/path_service.h" -#include "base/ranges/algorithm.h" #include "base/run_loop.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" @@ -723,8 +723,9 @@ // Each next group should fill almost everything, while the previous group // should occupy what's left. - auto* error_position = base::ranges::adjacent_find( - item_groups, [&](const ItemGroup& previous, const ItemGroup& current) { + auto* error_position = std::adjacent_find( + std::begin(item_groups), std::end(item_groups), + [&](const ItemGroup& previous, const ItemGroup& current) { auto ids = GetHistoryIdsUpTo(current.max_id); EXPECT_TRUE(GetPrivateData()->TrimHistoryIdsPool(&ids));
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc index f52bf17..8900cdb 100644 --- a/components/omnibox/browser/omnibox_field_trial.cc +++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -218,20 +218,6 @@ return base::Milliseconds(1500); } -base::Time OmniboxFieldTrial::GetLocalHistoryZeroSuggestAgeThreshold() { - std::string param_value = base::GetFieldTrialParamValueByFeature( - omnibox::kOmniboxLocalZeroSuggestAgeThreshold, - OmniboxFieldTrial::kOmniboxLocalZeroSuggestAgeThresholdParam); - - // If the field trial param is not found or cannot be parsed to an unsigned - // integer, return the default value. - unsigned int param_value_as_int = 0; - if (!base::StringToUint(param_value, ¶m_value_as_int)) { - param_value_as_int = 60; - } - return (base::Time::Now() - base::Days(param_value_as_int)); -} - bool OmniboxFieldTrial::ShortcutsScoringMaxRelevance( OmniboxEventProto::PageClassification current_page_classification, int* max_relevance) { @@ -717,9 +703,6 @@ OmniboxFieldTrial::kMaxNumHQPUrlsIndexedAtStartupOnNonLowEndDevicesParam[] = "MaxNumHQPUrlsIndexedAtStartupOnNonLowEndDevices"; -const char OmniboxFieldTrial::kOmniboxLocalZeroSuggestAgeThresholdParam[] = - "OmniboxLocalZeroSuggestAgeThreshold"; - const char OmniboxFieldTrial::kMaxZeroSuggestMatchesParam[] = "MaxZeroSuggestMatches"; const char OmniboxFieldTrial::kOmniboxMaxURLMatchesParam[] = @@ -822,6 +805,21 @@ } } +// Determines the age threshold in days for local zero-prefix suggestions. +const base::FeatureParam<int> kOmniboxLocalZeroSuggestAgeThresholdParam( + &omnibox::kOmniboxLocalZeroSuggestAgeThreshold, + "OmniboxLocalZeroSuggestAgeThreshold", +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) + 60); +#else + 90); +#endif + +base::Time GetLocalHistoryZeroSuggestAgeThreshold() { + return (base::Time::Now() - + base::Days(kOmniboxLocalZeroSuggestAgeThresholdParam.Get())); +} + const base::FeatureParam<bool> kZeroSuggestIgnoreDuplicateVisits( &omnibox::kLocalHistorySuggestRevamp, "ZeroSuggestIgnoreDuplicateVisits", @@ -829,7 +827,11 @@ const base::FeatureParam<bool> kPrefixSuggestIgnoreDuplicateVisits( &omnibox::kLocalHistorySuggestRevamp, "PrefixSuggestIgnoreDuplicateVisits", +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) false); +#else + true); +#endif // Short bookmarks.
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h index 530fb59..5eb9e0f 100644 --- a/components/omnibox/browser/omnibox_field_trial.h +++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -149,13 +149,6 @@ base::TimeDelta StopTimerFieldTrialDuration(); // --------------------------------------------------------- -// For the OmniboxLocalZeroSuggestAgeThreshold field trial. - -// Returns the age threshold since the last visit in order to consider a -// normalized keyword search term as a zero-prefix suggestion. -base::Time GetLocalHistoryZeroSuggestAgeThreshold(); - -// --------------------------------------------------------- // For the ShortcutsScoringMaxRelevance experiment that's part of the // bundled omnibox field trial. @@ -444,11 +437,6 @@ extern const char kMaxNumHQPUrlsIndexedAtStartupOnLowEndDevicesParam[]; extern const char kMaxNumHQPUrlsIndexedAtStartupOnNonLowEndDevicesParam[]; -// Parameter name determining the age threshold for local zero-prefix -// suggestions. The value of this parameter should be parsable as an unsigned -// integer, which will be used to specify the age threshold in days. -extern const char kOmniboxLocalZeroSuggestAgeThresholdParam[]; - // Parameter names used by num suggestion experiments. extern const char kMaxZeroSuggestMatchesParam[]; extern const char kOmniboxMaxURLMatchesParam[]; @@ -540,6 +528,13 @@ bool IsZeroSuggestPrefetchingEnabledInContext( metrics::OmniboxEventProto::PageClassification page_classification); +// Determines the age threshold in days for local zero-prefix suggestions. +extern const base::FeatureParam<int> kOmniboxLocalZeroSuggestAgeThresholdParam; + +// Returns the age threshold since the last visit in order to consider a +// normalized keyword search term as a zero-prefix suggestion. +base::Time GetLocalHistoryZeroSuggestAgeThreshold(); + // Whether duplicative visits should be ignored for local history zero-suggest. // A duplicative visit is a visit to the same search term in an interval smaller // than kAutocompleteDuplicateVisitIntervalThreshold.
diff --git a/components/omnibox/browser/omnibox_field_trial_unittest.cc b/components/omnibox/browser/omnibox_field_trial_unittest.cc index 71fdf1d..8dd7f0b 100644 --- a/components/omnibox/browser/omnibox_field_trial_unittest.cc +++ b/components/omnibox/browser/omnibox_field_trial_unittest.cc
@@ -357,24 +357,24 @@ TEST_F(OmniboxFieldTrialTest, LocalZeroSuggestAgeThreshold) { base::test::ScopedFeatureList scoped_feature_list_; + // Verify the default value. +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) + const int expected_age_threshold_days = 60; +#else + const int expected_age_threshold_days = 90; +#endif + base::Time age_threshold = + OmniboxFieldTrial::GetLocalHistoryZeroSuggestAgeThreshold(); + EXPECT_EQ(expected_age_threshold_days, + base::TimeDelta(base::Time::Now() - age_threshold).InDays()); + // The default value can be overridden. scoped_feature_list_.InitAndEnableFeatureWithParameters( omnibox::kOmniboxLocalZeroSuggestAgeThreshold, - {{OmniboxFieldTrial::kOmniboxLocalZeroSuggestAgeThresholdParam, "3"}}); - base::Time age_threshold = - OmniboxFieldTrial::GetLocalHistoryZeroSuggestAgeThreshold(); - EXPECT_EQ(3, base::TimeDelta(base::Time::Now() - age_threshold).InDays()); - - // If the age threshold is not parsable to an unsigned integer, the default - // value is used. - scoped_feature_list_.Reset(); - scoped_feature_list_.InitAndEnableFeatureWithParameters( - omnibox::kOmniboxLocalZeroSuggestAgeThreshold, - {{OmniboxFieldTrial::kOmniboxLocalZeroSuggestAgeThresholdParam, "j"}}); - const int expected_age_threshold_days = 60; + {{OmniboxFieldTrial::kOmniboxLocalZeroSuggestAgeThresholdParam.name, + "3"}}); age_threshold = OmniboxFieldTrial::GetLocalHistoryZeroSuggestAgeThreshold(); - EXPECT_EQ(expected_age_threshold_days, - base::TimeDelta(base::Time::Now() - age_threshold).InDays()); + EXPECT_EQ(3, base::TimeDelta(base::Time::Now() - age_threshold).InDays()); } TEST_F(OmniboxFieldTrialTest, HUPNewScoringFieldTrial) {
diff --git a/components/omnibox/browser/scored_history_match.cc b/components/omnibox/browser/scored_history_match.cc index bb2c625f..f853e91d 100644 --- a/components/omnibox/browser/scored_history_match.cc +++ b/components/omnibox/browser/scored_history_match.cc
@@ -6,13 +6,13 @@ #include <math.h> +#include <algorithm> #include <utility> #include <vector> #include "base/check_op.h" #include "base/no_destructor.h" #include "base/numerics/safe_conversions.h" -#include "base/ranges/algorithm.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" @@ -634,8 +634,11 @@ auto visits_end = visits.begin() + std::min(visits.size(), max_visits_to_score_); // Visits should be in newest to oldest order. - DCHECK(base::ranges::adjacent_find(visits.begin(), visits_end, std::less<>(), - &history::VisitInfo::first) == visits_end); + DCHECK(std::adjacent_find( + visits.begin(), visits_end, + [](const history::VisitInfo& a, const history::VisitInfo& b) { + return a.first < b.first; + }) == visits_end); for (auto i = visits.begin(); i != visits_end; ++i) { const bool is_page_transition_typed = ui::PageTransitionCoreTypeIs(i->second, ui::PAGE_TRANSITION_TYPED);
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc index 422e38c9..7195553 100644 --- a/components/omnibox/common/omnibox_features.cc +++ b/components/omnibox/common/omnibox_features.cc
@@ -187,7 +187,7 @@ // zero-prefix and prefix suggestions. BASE_FEATURE(kLocalHistorySuggestRevamp, "LocalHistorySuggestRevamp", - base::FEATURE_DISABLED_BY_DEFAULT); + enabled_by_default_desktop_only); // Enables local history zero-prefix suggestions in every context in which the // remote zero-prefix suggestions are enabled. @@ -197,12 +197,12 @@ // Used to adjust the age threshold since the last visit in order to consider a // normalized keyword search term as a zero-prefix suggestion. If disabled, the -// default value of 60 days for Desktop and 7 days for Android and iOS is used. +// default value of 90 days for Desktop and 60 days for Android and iOS is used. // If enabled, the age threshold is determined by this feature's companion // parameter, OmniboxFieldTrial::kOmniboxLocalZeroSuggestAgeThresholdParam. BASE_FEATURE(kOmniboxLocalZeroSuggestAgeThreshold, "OmniboxLocalZeroSuggestAgeThreshold", - base::FEATURE_DISABLED_BY_DEFAULT); + enabled_by_default_desktop_only); // Mainly used to enable sending INTERACTION_CLOBBER focus type for zero-prefix // requests with an empty input on Web/SRP on Mobile. Enabled by default on
diff --git a/components/password_manager/core/browser/generation/password_generator.cc b/components/password_manager/core/browser/generation/password_generator.cc index d6544e0f..5aec52d 100644 --- a/components/password_manager/core/browser/generation/password_generator.cc +++ b/components/password_manager/core/browser/generation/password_generator.cc
@@ -4,6 +4,7 @@ #include "components/password_manager/core/browser/generation/password_generator.h" +#include <algorithm> #include <limits> #include <map> #include <utility> @@ -11,7 +12,6 @@ #include "base/check.h" #include "base/rand_util.h" -#include "base/ranges/algorithm.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/proto/password_requirements.pb.h" @@ -80,9 +80,10 @@ // sequences of '-' or '_' that are joined into long strokes on the screen // in many fonts. bool IsDifficultToRead(const std::u16string& password) { - return base::ranges::adjacent_find(password, [](auto a, auto b) { - return a == b && (a == '-' || a == '_'); - }) != password.end(); + return std::adjacent_find(password.begin(), password.end(), + [](auto a, auto b) { + return a == b && (a == '-' || a == '_'); + }) != password.end(); } // Generates a password according to |spec| and tries to maximze the entropy
diff --git a/components/performance_manager/v8_memory/v8_context_tracker_internal.cc b/components/performance_manager/v8_memory/v8_context_tracker_internal.cc index 043b8af4..f4d7acd 100644 --- a/components/performance_manager/v8_memory/v8_context_tracker_internal.cc +++ b/components/performance_manager/v8_memory/v8_context_tracker_internal.cc
@@ -135,7 +135,7 @@ static_cast<bool>(description.execution_context_token)); if (execution_context_data) { DCHECK_EQ(execution_context_data->GetToken(), - description.execution_context_token.value()); + *description.execution_context_token); // These must be same process. DCHECK_EQ(process_data, execution_context_data->process_data());
diff --git a/components/translate/core/browser/translate_language_list.cc b/components/translate/core/browser/translate_language_list.cc index 1e1753e..088cc236 100644 --- a/components/translate/core/browser/translate_language_list.cc +++ b/components/translate/core/browser/translate_language_list.cc
@@ -6,6 +6,7 @@ #include <stddef.h> +#include <algorithm> #include <iterator> #include "base/bind.h" @@ -13,7 +14,6 @@ #include "base/json/json_reader.h" #include "base/lazy_instance.h" #include "base/notreached.h" -#include "base/ranges/algorithm.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/values.h" @@ -185,7 +185,8 @@ DCHECK( std::is_sorted(supported_languages_.begin(), supported_languages_.end())); DCHECK(supported_languages_.end() == - base::ranges::adjacent_find(supported_languages_)); + std::adjacent_find(supported_languages_.begin(), + supported_languages_.end())); if (update_is_disabled) return; @@ -361,7 +362,8 @@ DCHECK( std::is_sorted(supported_languages_.begin(), supported_languages_.end())); DCHECK(supported_languages_.end() == - base::ranges::adjacent_find(supported_languages_)); + std::adjacent_find(supported_languages_.begin(), + supported_languages_.end())); NotifyEvent(__LINE__, base::JoinString(supported_languages_, ", ")); return true;
diff --git a/components/url_rewrite/common/url_loader_throttle.cc b/components/url_rewrite/common/url_loader_throttle.cc index 24889d46..09aff972 100644 --- a/components/url_rewrite/common/url_loader_throttle.cc +++ b/components/url_rewrite/common/url_loader_throttle.cc
@@ -200,19 +200,12 @@ void URLLoaderThrottle::ApplyRule(network::ResourceRequest* request, const mojom::UrlRequestRulePtr& rule) { - if (!RuleFiltersMatchUrl(request->url, rule)) + // Prevent applying rules on redirect navigations. + if (request->navigation_redirect_chain.size() > 1u) return; - // Prevent applying rules more than once when redirected. - for (const auto& url : request->navigation_redirect_chain) { - // Last element in redirect chain is the current navigation. - if (&url == &request->navigation_redirect_chain.back()) { - continue; - } - if (RuleFiltersMatchUrl(url, rule)) { - return; - } - } + if (!RuleFiltersMatchUrl(request->url, rule)) + return; for (const auto& rewrite : rule->actions) ApplyRewrite(request, rewrite);
diff --git a/components/url_rewrite/common/url_loader_throttle_unittest.cc b/components/url_rewrite/common/url_loader_throttle_unittest.cc index 9e677fbc..d1d706d 100644 --- a/components/url_rewrite/common/url_loader_throttle_unittest.cc +++ b/components/url_rewrite/common/url_loader_throttle_unittest.cc
@@ -35,7 +35,6 @@ std::vector<std::string> cors_exempt_headers) { return base::BindLambdaForTesting( [cors_exempt_headers](base::StringPiece header) { - LOG(INFO) << "HEADER: " << header; for (const auto& exempt_header : cors_exempt_headers) { if (base::EqualsCaseInsensitiveASCII(header, exempt_header)) { return true; @@ -212,12 +211,11 @@ EXPECT_EQ(request.url, GURL(kUrlWithQueryString)); } -// Tests URL replacement rules applies when redirecting from a different host. +// Tests URL replacement rules do not apply when redirecting. TEST_F(URLLoaderThrottleTest, RedirectsFromDifferentHost) { constexpr char kAppendQueryString[] = "foo=1&bar=2"; constexpr char kBaseUrl1[] = "http://a.com"; constexpr char kBaseUrl2[] = "http://b.com"; - constexpr char kUrl2WithQueryString[] = "http://b.com?foo=1&bar=2"; mojom::UrlRequestRewriteAppendToQueryPtr append_query = mojom::UrlRequestRewriteAppendToQuery::New(kAppendQueryString); @@ -246,7 +244,7 @@ request.url = GURL(kBaseUrl2); request.navigation_redirect_chain = {GURL(kBaseUrl1), GURL(kBaseUrl2)}; throttle.WillStartRequest(&request, &defer); - EXPECT_EQ(request.url, GURL(kUrl2WithQueryString)); + EXPECT_EQ(request.url, GURL(kBaseUrl2)); } // Tests URL replacement rules do not apply more than once when redirecting to a @@ -278,7 +276,6 @@ network::ResourceRequest request; request.url = GURL(kBaseUrl1); - request.navigation_redirect_chain = {GURL(kBaseUrl1)}; throttle.WillStartRequest(&request, &defer); EXPECT_EQ(request.url, GURL(kUrl1WithQueryString)); @@ -288,50 +285,6 @@ EXPECT_EQ(request.url, GURL(kBaseUrl2)); } -// Tests URL replacement rules do not apply more than once when redirecting to a -// different host then back to the same host. -TEST_F(URLLoaderThrottleTest, RedirectsToDifferentHostThenBack) { - constexpr char kAppendQueryString[] = "foo=1&bar=2"; - constexpr char kBaseUrl1[] = "http://a.com"; - constexpr char kBaseUrl2[] = "http://b.com"; - constexpr char kUrl1WithQueryString[] = "http://a.com?foo=1&bar=2"; - - mojom::UrlRequestRewriteAppendToQueryPtr append_query = - mojom::UrlRequestRewriteAppendToQuery::New(kAppendQueryString); - std::vector<mojom::UrlRequestActionPtr> actions; - actions.push_back( - mojom::UrlRequestAction::NewAppendToQuery(std::move(append_query))); - - mojom::UrlRequestRulePtr rule = mojom::UrlRequestRule::New(); - rule->hosts_filter = absl::optional<std::vector<std::string>>({"*.a.com"}); - rule->actions = std::move(actions); - - mojom::UrlRequestRewriteRulesPtr rules = mojom::UrlRequestRewriteRules::New(); - rules->rules.push_back(std::move(rule)); - - URLLoaderThrottle throttle( - base::MakeRefCounted<UrlRequestRewriteRules>(std::move(rules)), - CreateCorsExemptHeadersCallback({})); - bool defer = false; - - network::ResourceRequest request; - request.url = GURL(kBaseUrl1); - request.navigation_redirect_chain = {GURL(kBaseUrl1)}; - throttle.WillStartRequest(&request, &defer); - EXPECT_EQ(request.url, GURL(kUrl1WithQueryString)); - - request.url = GURL(kBaseUrl2); - request.navigation_redirect_chain = {GURL(kBaseUrl1), GURL(kBaseUrl2)}; - throttle.WillStartRequest(&request, &defer); - EXPECT_EQ(request.url, GURL(kBaseUrl2)); - - request.url = GURL(kBaseUrl1); - request.navigation_redirect_chain = {GURL(kBaseUrl1), GURL(kBaseUrl2), - GURL(kBaseUrl1)}; - throttle.WillStartRequest(&request, &defer); - EXPECT_EQ(request.url, GURL(kBaseUrl1)); -} - class TestThrottleDelegate : public blink::URLLoaderThrottle::Delegate { public: TestThrottleDelegate() = default;
diff --git a/components/viz/common/quads/render_pass_io.cc b/components/viz/common/quads/render_pass_io.cc index c0d34bf..7e58782 100644 --- a/components/viz/common/quads/render_pass_io.cc +++ b/components/viz/common/quads/render_pass_io.cc
@@ -242,21 +242,19 @@ return list; } -bool FloatArrayFromList(const base::Value& list, +bool FloatArrayFromList(const base::Value::List& list, size_t expected_count, float* data) { DCHECK(data); DCHECK_LT(0u, expected_count); - if (!list.is_list()) - return false; - size_t count = list.GetList().size(); + size_t count = list.size(); if (count != expected_count) return false; std::vector<double> double_data(count); for (size_t ii = 0; ii < count; ++ii) { - if (!list.GetList()[ii].is_double()) + if (!list[ii].is_double()) return false; - double_data[ii] = list.GetList()[ii].GetDouble(); + double_data[ii] = list[ii].GetDouble(); } for (size_t ii = 0; ii < count; ++ii) data[ii] = static_cast<float>(double_data[ii]); @@ -604,7 +602,7 @@ const base::Value* drop_shadow_offset = dict.FindDictKey("drop_shadow_offset"); const std::string* image_filter = dict.FindStringKey("image_filter"); - const base::Value* matrix = dict.FindListKey("matrix"); + const base::Value::List* matrix = dict.GetDict().FindList("matrix"); absl::optional<int> zoom_inset = dict.FindIntKey("zoom_inset"); const base::Value* shape = dict.FindListKey("shape"); absl::optional<int> blur_tile_mode = dict.FindIntKey("blur_tile_mode"); @@ -868,7 +866,7 @@ return FloatArrayToList(data); } -bool Matrix3x3FromList(const base::Value& list, skcms_Matrix3x3* mat) { +bool Matrix3x3FromList(const base::Value::List& list, skcms_Matrix3x3* mat) { DCHECK(mat); return FloatArrayFromList(list, 9u, reinterpret_cast<float*>(mat->vals)); } @@ -885,7 +883,7 @@ return FloatArrayToList(data); } -bool TransferFunctionFromList(const base::Value& list, +bool TransferFunctionFromList(const base::Value::List& list, skcms_TransferFunction* fn) { DCHECK(fn); float data[7]; @@ -945,8 +943,8 @@ bool uses_custom_primary_matrix = primary_id == static_cast<uint8_t>(gfx::ColorSpace::PrimaryID::CUSTOM); if (uses_custom_primary_matrix) { - const base::Value* custom_primary_matrix = - dict.FindListKey("custom_primary_matrix"); + const base::Value::List* custom_primary_matrix = + dict.GetDict().FindList("custom_primary_matrix"); if (!custom_primary_matrix || !Matrix3x3FromList(*custom_primary_matrix, &t_custom_primary_matrix)) { return false; @@ -959,8 +957,8 @@ transfer_id == static_cast<uint8_t>(gfx::ColorSpace::TransferID::CUSTOM_HDR); if (uses_custom_transfer_params) { - const base::Value* custom_transfer_params = - dict.FindListKey("custom_transfer_params"); + const base::Value::List* custom_transfer_params = + dict.GetDict().FindList("custom_transfer_params"); if (!custom_transfer_params || !TransferFunctionFromList(*custom_transfer_params, &t_custom_transfer_params)) { @@ -1514,7 +1512,7 @@ dict.FindBool("premultiplied_alpha"); const base::Value::Dict* uv_top_left = dict.FindDict("uv_top_left"); const base::Value::Dict* uv_bottom_right = dict.FindDict("uv_bottom_right"); - const base::Value* vertex_opacity = dict_value.FindListKey("vertex_opacity"); + const base::Value::List* vertex_opacity = dict.FindList("vertex_opacity"); const base::Value::Dict* damage_rect = dict.FindDict("damage_rect"); absl::optional<bool> y_flipped = dict.FindBool("y_flipped"); absl::optional<bool> nearest_neighbor = dict.FindBool("nearest_neighbor");
diff --git a/components/viz/service/display_embedder/output_presenter_gl.cc b/components/viz/service/display_embedder/output_presenter_gl.cc index 5da24c2..cfda8221 100644 --- a/components/viz/service/display_embedder/output_presenter_gl.cc +++ b/components/viz/service/display_embedder/output_presenter_gl.cc
@@ -60,7 +60,7 @@ int GetPresentCount() const final; void OnContextLost() final; - gl::GLImage* GetGLImage(std::unique_ptr<gfx::GpuFence>* fence); + gl::OverlayImage GetOverlayImage(std::unique_ptr<gfx::GpuFence>* fence); const gfx::ColorSpace& color_space() { DCHECK(overlay_representation_); @@ -102,13 +102,17 @@ overlay_representation_->OnContextLost(); } -gl::GLImage* PresenterImageGL::GetGLImage( +gl::OverlayImage PresenterImageGL::GetOverlayImage( std::unique_ptr<gfx::GpuFence>* fence) { DCHECK(scoped_overlay_read_access_); if (fence) { *fence = TakeGpuFence(scoped_overlay_read_access_->TakeAcquireFence()); } +#if defined(USE_OZONE) + return scoped_overlay_read_access_->GetNativePixmap(); +#else return scoped_overlay_read_access_->gl_image(); +#endif } } // namespace @@ -319,7 +323,7 @@ std::unique_ptr<gfx::GpuFence> fence; auto* presenter_image = static_cast<PresenterImageGL*>(image); // If the submitted_image() is being scheduled, we don't new a new fence. - auto* gl_image = presenter_image->GetGLImage( + gl::OverlayImage overlay_image = presenter_image->GetOverlayImage( (is_submitted || !gl_surface_->SupportsPlaneGpuFences()) ? nullptr : &fence); @@ -330,7 +334,7 @@ // overlays, damage should be added to OutputSurfaceOverlayPlane and passed in // here. gl_surface_->ScheduleOverlayPlane( - gl_image, std::move(fence), + std::move(overlay_image), std::move(fence), gfx::OverlayPlaneData( kPlaneZOrder, plane.transform, plane.display_rect, plane.uv_rect, plane.enable_blending, @@ -359,12 +363,17 @@ const OutputPresenter::OverlayPlaneCandidate& overlay_plane_candidate, ScopedOverlayAccess* access, std::unique_ptr<gfx::GpuFence> acquire_fence) { -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE) || defined(USE_OZONE) // Note that |overlay_plane_candidate| has different types on different // platforms. On Android and Ozone it is an OverlayCandidate, on Windows it is // a DCLayerOverlay, and on macOS it is a CALayeroverlay. - auto* gl_image = access ? access->gl_image() : nullptr; #if BUILDFLAG(IS_ANDROID) || defined(USE_OZONE) +#if defined(USE_OZONE) + // TODO(crbug.com/1366808): Add ScopedOverlayAccess::GetOverlayImage() that + // works on all platforms. + gl::OverlayImage overlay_image = access ? access->GetNativePixmap() : nullptr; +#else + auto* overlay_image = access ? access->gl_image() : nullptr; +#endif // TODO(msisov): Once shared image factory allows creating a non backed // images and ScheduleOverlayPlane does not rely on GLImage, remove the if // condition that checks if this is a solid color overlay plane. @@ -374,7 +383,7 @@ // may have a protocol that asks Wayland compositor to create a solid color // buffer for a client. OverlayProcessorDelegated decides if a solid color // overlay is an overlay candidate and should be scheduled. - if (gl_image || overlay_plane_candidate.is_solid_color) { + if (overlay_image || overlay_plane_candidate.is_solid_color) { #if DCHECK_IS_ON() if (overlay_plane_candidate.is_solid_color) { LOG_IF(FATAL, !overlay_plane_candidate.color.has_value()) @@ -400,7 +409,7 @@ } gl_surface_->ScheduleOverlayPlane( - gl_image, std::move(acquire_fence), + std::move(overlay_image), std::move(acquire_fence), gfx::OverlayPlaneData( overlay_plane_candidate.plane_z_order, absl::get<gfx::OverlayTransform>(overlay_plane_candidate.transform), @@ -416,6 +425,7 @@ overlay_plane_candidate.clip_rect)); } #elif BUILDFLAG(IS_APPLE) + auto* gl_image = access ? access->gl_image() : nullptr; gl_surface_->ScheduleCALayer(ui::CARendererLayerParams( overlay_plane_candidate.shared_state->is_clipped, gfx::ToEnclosingRect(overlay_plane_candidate.shared_state->clip_rect), @@ -429,7 +439,6 @@ overlay_plane_candidate.filter, overlay_plane_candidate.hdr_metadata, overlay_plane_candidate.protected_video_type)); #endif -#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE) || defined(USE_OZONE) } #if BUILDFLAG(IS_MAC)
diff --git a/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc b/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc index 3bdebb50..9842b32 100644 --- a/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc +++ b/components/viz/service/display_embedder/skia_output_device_buffer_queue.cc
@@ -87,12 +87,16 @@ } bool IsInUseByWindowServer() const { +#if BUILDFLAG(IS_MAC) if (!scoped_read_access_) return false; auto* gl_image = scoped_read_access_->gl_image(); if (!gl_image) return false; return gl_image->IsInUseByWindowServer(); +#else + return false; +#endif } void Ref() { ++ref_; } @@ -331,12 +335,11 @@ return nullptr; } - // Fuchsia does not provide a GLImage overlay. -#if BUILDFLAG(IS_FUCHSIA) +#if defined(IS_OZONE) const bool needs_gl_image = false; #else const bool needs_gl_image = true; -#endif // BUILDFLAG(IS_FUCHSIA) +#endif // defined(IS_OZONE) // TODO(penghuang): do not depend on GLImage. auto shared_image_access =
diff --git a/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc b/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc index 7e55967..886e9cb 100644 --- a/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc +++ b/components/viz/service/display_embedder/skia_output_device_buffer_queue_unittest.cc
@@ -219,7 +219,7 @@ } bool ScheduleOverlayPlane( - gl::GLImage* image, + gl::OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) override { return true;
diff --git a/components/zucchini/imposed_ensemble_matcher.cc b/components/zucchini/imposed_ensemble_matcher.cc index 09c09f0..36afad6 100644 --- a/components/zucchini/imposed_ensemble_matcher.cc +++ b/components/zucchini/imposed_ensemble_matcher.cc
@@ -4,12 +4,12 @@ #include "components/zucchini/imposed_ensemble_matcher.h" +#include <algorithm> #include <sstream> #include <utility> #include "base/bind.h" #include "base/logging.h" -#include "base/ranges/algorithm.h" #include "components/zucchini/io_utils.h" namespace zucchini { @@ -67,8 +67,9 @@ }); // Check for overlaps in "new" file. - if (base::ranges::adjacent_find( - matches_, [](const ElementMatch& match1, const ElementMatch& match2) { + if (std::adjacent_find( + matches_.begin(), matches_.end(), + [](const ElementMatch& match1, const ElementMatch& match2) { return match1.new_element.hi() > match2.new_element.lo(); }) != matches_.end()) { return kOverlapInNew;
diff --git a/content/browser/android/navigation_handle_proxy.cc b/content/browser/android/navigation_handle_proxy.cc index 4cdec4a3..60cfc74 100644 --- a/content/browser/android/navigation_handle_proxy.cc +++ b/content/browser/android/navigation_handle_proxy.cc
@@ -104,20 +104,4 @@ Java_NavigationHandle_release(env, java_navigation_handle_); } -// Called from Java. -void NavigationHandleProxy::SetRequestHeader( - JNIEnv* env, - const JavaParamRef<jstring>& name, - const JavaParamRef<jstring>& value) { - cpp_navigation_handle_->SetRequestHeader(ConvertJavaStringToUTF8(name), - ConvertJavaStringToUTF8(value)); -} - -// Called from Java. -void NavigationHandleProxy::RemoveRequestHeader( - JNIEnv* env, - const JavaParamRef<jstring>& name) { - cpp_navigation_handle_->RemoveRequestHeader(ConvertJavaStringToUTF8(name)); -} - } // namespace content
diff --git a/content/browser/android/navigation_handle_proxy.h b/content/browser/android/navigation_handle_proxy.h index 1d0894e..ad8cd4e 100644 --- a/content/browser/android/navigation_handle_proxy.h +++ b/content/browser/android/navigation_handle_proxy.h
@@ -35,15 +35,6 @@ void DidRedirect(); void DidFinish(); - // Called from Java. - void SetRequestHeader(JNIEnv* env, - const base::android::JavaParamRef<jstring>& name, - const base::android::JavaParamRef<jstring>& value); - - // Called from Java. - void RemoveRequestHeader(JNIEnv* env, - const base::android::JavaParamRef<jstring>& name); - private: base::android::ScopedJavaGlobalRef<jobject> java_navigation_handle_; raw_ptr<NavigationHandle> cpp_navigation_handle_ = nullptr;
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc index d8cd60b..860e005 100644 --- a/content/browser/web_contents/web_contents_impl_browsertest.cc +++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -18,7 +18,6 @@ #include "base/memory/raw_ptr.h" #include "base/memory/ref_counted.h" #include "base/path_service.h" -#include "base/ranges/algorithm.h" #include "base/run_loop.h" #include "base/strings/pattern.h" #include "base/strings/stringprintf.h" @@ -1005,8 +1004,8 @@ const std::vector<double>& progresses = delegate->progresses; // All updates should be in order ... - if (base::ranges::adjacent_find(progresses, std::greater<>()) != - progresses.end()) { + if (std::adjacent_find(progresses.begin(), progresses.end(), + std::greater<>()) != progresses.end()) { ADD_FAILURE() << "Progress values should be in order: " << ::testing::PrintToString(progresses); } @@ -1026,8 +1025,8 @@ const std::vector<double>& progresses = delegate->progresses; // All updates should be in order ... - if (base::ranges::adjacent_find(progresses, std::greater<>()) != - progresses.end()) { + if (std::adjacent_find(progresses.begin(), progresses.end(), + std::greater<>()) != progresses.end()) { ADD_FAILURE() << "Progress values should be in order: " << ::testing::PrintToString(progresses); }
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc index 0ab9f47..42e9767f 100644 --- a/content/browser/webid/federated_auth_request_impl.cc +++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -300,6 +300,10 @@ url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS)); } +std::string FormatOriginForDisplay(const url::Origin& origin) { + return FormatUrlForDisplay(origin.GetURL()); +} + bool ShouldFailIfNotSignedInWithIdp( const GURL& idp_url, FederatedIdentitySharingPermissionContextDelegate* @@ -837,8 +841,7 @@ if (!pending_idps_.empty()) return; - std::string rp_url_for_display = - FormatUrlForDisplay(rp_web_contents->GetLastCommittedURL()); + std::string rp_url_for_display = FormatOriginForDisplay(GetEmbeddingOrigin()); std::vector<IdentityProviderData> idp_data_for_display; for (const auto& idp : idp_order_) { @@ -848,8 +851,7 @@ absl::optional<std::string> iframe_url_for_display = absl::nullopt; if (IsFedCmIframeSupportEnabled() && show_iframe_requester_) { - iframe_url_for_display = - FormatUrlForDisplay(render_frame_host().GetLastCommittedURL()); + iframe_url_for_display = FormatOriginForDisplay(origin()); } request_dialog_controller_->ShowAccountsDialog( @@ -892,10 +894,8 @@ DCHECK(render_frame_host().GetMainFrame()->IsInPrimaryMainFrame()); request_dialog_controller_->ShowFailureDialog( - rp_web_contents, - FormatUrlForDisplay(rp_web_contents->GetLastCommittedURL()), - FormatUrlForDisplay(idp_url), - FormatUrlForDisplay(render_frame_host().GetLastCommittedURL()), + rp_web_contents, FormatOriginForDisplay(GetEmbeddingOrigin()), + FormatUrlForDisplay(idp_url), FormatOriginForDisplay(origin()), base::BindOnce( &FederatedAuthRequestImpl::OnDismissFailureDialog, weak_ptr_factory_.GetWeakPtr(), FederatedAuthRequestResult::kError,
diff --git a/content/browser/webui/shared_resources_data_source.cc b/content/browser/webui/shared_resources_data_source.cc index 3bdad44..e98656f7 100644 --- a/content/browser/webui/shared_resources_data_source.cc +++ b/content/browser/webui/shared_resources_data_source.cc
@@ -32,22 +32,16 @@ namespace { const std::set<int> GetContentResourceIds() { - return std::set<int>{ - IDR_GEOMETRY_MOJOM_WEBUI_JS, - IDR_IMAGE_MOJOM_WEBUI_JS, - IDR_ORIGIN_MOJO_WEBUI_JS, - IDR_RANGE_MOJOM_WEBUI_JS, - IDR_TOKEN_MOJO_WEBUI_JS, - IDR_UI_WINDOW_OPEN_DISPOSITION_MOJO_WEBUI_JS, - IDR_URL_MOJOM_WEBUI_JS, - IDR_VULKAN_INFO_MOJO_JS, - IDR_VULKAN_TYPES_MOJO_JS, + return std::set<int> { + IDR_GEOMETRY_MOJOM_WEBUI_JS, IDR_IMAGE_MOJOM_WEBUI_JS, + IDR_ORIGIN_MOJO_WEBUI_JS, IDR_RANGE_MOJOM_WEBUI_JS, + IDR_TOKEN_MOJO_WEBUI_JS, IDR_UI_WINDOW_OPEN_DISPOSITION_MOJO_WEBUI_JS, + IDR_URL_MOJOM_WEBUI_JS, IDR_VULKAN_INFO_MOJO_JS, + IDR_VULKAN_TYPES_MOJO_JS, #if BUILDFLAG(IS_CHROMEOS_ASH) - IDR_ORIGIN_MOJO_JS, - IDR_UI_WINDOW_OPEN_DISPOSITION_MOJO_JS, - IDR_UNGUESSABLE_TOKEN_MOJO_JS, - IDR_URL_MOJO_JS, + IDR_ORIGIN_MOJO_JS, IDR_UI_WINDOW_OPEN_DISPOSITION_MOJO_JS, + IDR_UNGUESSABLE_TOKEN_MOJO_JS, IDR_URL_MOJO_JS, #endif // !BUILDFLAG(IS_CHROMEOS_ASH) }; }
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java b/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java index d4e4394..34c29460 100644 --- a/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java +++ b/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java
@@ -8,7 +8,6 @@ import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.NativeMethods; import org.chromium.net.NetError; import org.chromium.ui.base.PageTransition; import org.chromium.url.GURL; @@ -244,25 +243,6 @@ } /** - * Set request's header. If the header is already present, its value is overwritten. When - * modified during a navigation start, the headers will be applied to the initial network - * request. When modified during a redirect, the headers will be applied to the redirected - * request. - */ - public void setRequestHeader(String headerName, String headerValue) { - NavigationHandleJni.get().setRequestHeader( - mNativeNavigationHandleProxy, headerName, headerValue); - } - - /** - * Remove a request's header. If the header is not present, it has no effect. Must be called - * during a redirect. - */ - public void removeRequestHeader(String headerName) { - NavigationHandleJni.get().removeRequestHeader(mNativeNavigationHandleProxy, headerName); - } - - /** * Get the Origin that initiated this navigation. May be null in the case of navigations * originating from the browser. */ @@ -311,11 +291,4 @@ public boolean isReload() { return mIsReload; } - - @NativeMethods - interface Natives { - void setRequestHeader( - long nativeNavigationHandleProxy, String headerName, String headerValue); - void removeRequestHeader(long nativeNavigationHandleProxy, String headerName); - } }
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc index 0ca55f1..e8a63ee 100644 --- a/content/public/common/content_features.cc +++ b/content/public/common/content_features.cc
@@ -1226,12 +1226,6 @@ "WebAssemblyLazyCompilation", base::FEATURE_DISABLED_BY_DEFAULT); -// Enable WebAssembly SIMD. -// https://github.com/WebAssembly/Simd -BASE_FEATURE(kWebAssemblySimd, - "WebAssemblySimd", - base::FEATURE_ENABLED_BY_DEFAULT); - // Enable WebAssembly tiering (Liftoff -> TurboFan). BASE_FEATURE(kWebAssemblyTiering, "WebAssemblyTiering",
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h index e10cdf16..844f254 100644 --- a/content/public/common/content_features.h +++ b/content/public/common/content_features.h
@@ -262,7 +262,6 @@ kEnableExperimentalWebAssemblyStackSwitching); #endif // defined(ARCH_CPU_X86_64) CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebAssemblyLazyCompilation); -CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebAssemblySimd); CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebAssemblyTiering); CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebAssemblyTrapHandler); CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebAuthConditionalUI);
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate.cc b/content/renderer/media/renderer_webmediaplayer_delegate.cc index 6e1d4b4..66ae4755 100644 --- a/content/renderer/media/renderer_webmediaplayer_delegate.cc +++ b/content/renderer/media/renderer_webmediaplayer_delegate.cc
@@ -191,7 +191,7 @@ const bool is_shown = visibility_state == blink::mojom::PageVisibilityState::kVisible || visibility_state == blink::mojom::PageVisibilityState::kHiddenButPainting; - if (is_shown_ == is_shown) + if (is_shown_.has_value() && *is_shown_ == is_shown) return; is_shown_ = is_shown;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 2e135b98..ff94305 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -4,6 +4,7 @@ #include "content/renderer/render_frame_impl.h" +#include <algorithm> #include <map> #include <memory> #include <string> @@ -33,7 +34,6 @@ #include "base/metrics/histogram_macros.h" #include "base/observer_list.h" #include "base/process/process.h" -#include "base/ranges/algorithm.h" #include "base/run_loop.h" #include "base/strings/strcat.h" #include "base/strings/string_piece.h" @@ -726,7 +726,8 @@ DCHECK(std::is_sorted(params_.digests_of_uris_to_skip.begin(), params_.digests_of_uris_to_skip.end())); // URLs are not duplicated. - DCHECK(base::ranges::adjacent_find(params_.digests_of_uris_to_skip) == + DCHECK(std::adjacent_find(params_.digests_of_uris_to_skip.begin(), + params_.digests_of_uris_to_skip.end()) == params_.digests_of_uris_to_skip.end()); }
diff --git a/content/renderer/render_process_impl.cc b/content/renderer/render_process_impl.cc index f2bdea3..f3b4c9c 100644 --- a/content/renderer/render_process_impl.cc +++ b/content/renderer/render_process_impl.cc
@@ -174,10 +174,6 @@ SetV8FlagIfNotFeature(features::kWebAssemblyLazyCompilation, "--no-wasm-lazy-compilation"); - SetV8FlagIfFeature(features::kWebAssemblySimd, "--experimental-wasm-simd"); - SetV8FlagIfNotFeature(features::kWebAssemblySimd, - "--no-experimental-wasm-simd"); - constexpr char kImportAssertionsFlag[] = "--harmony-import-assertions"; v8::V8::SetFlagsFromString(kImportAssertionsFlag, sizeof(kImportAssertionsFlag));
diff --git a/content/test/gpu/gpu_tests/trace_integration_test.py b/content/test/gpu/gpu_tests/trace_integration_test.py index c5ff251..9edab82 100644 --- a/content/test/gpu/gpu_tests/trace_integration_test.py +++ b/content/test/gpu/gpu_tests/trace_integration_test.py
@@ -2,6 +2,10 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# TODO(dawn:549) Move WebGPU caching tests to a separate module to trim file. +# pylint: disable=too-many-lines + +from enum import Enum import logging import os import posixpath @@ -132,6 +136,20 @@ _PROFILE_TYPE_KEY = 'profile_type' +class _TraceTestOrigin(Enum): + """Enum type for different origin types when navigating to URLs. + + The default enum DEFAULT resolves URLs using the explicit localhost IP, + i.e. 127.0.0.1. LOCALHOST resolves URLs using 'localhost' instead. This is + useful when we want the navigations to hit the same resource but appear to + be from a different origin. + + As an implementation detail, the values of the enums correspond to the name of + the SeriallyExecutedBrowserTestCase instance functions to get the URLs.""" + DEFAULT = 'UrlOfStaticFilePath' + LOCALHOST = 'LocalhostUrlOfStaticFilePath' + + class _TraceTestArguments(): """Struct-like object for passing trace test arguments instead of dicts.""" @@ -143,7 +161,8 @@ finish_js_condition: str, success_eval_func: str, other_args: dict, - restart_browser: bool = True): + restart_browser: bool = True, + origin: _TraceTestOrigin = _TraceTestOrigin.DEFAULT): self.browser_args = browser_args self.category = category self.test_harness_script = test_harness_script @@ -151,25 +170,53 @@ self.success_eval_func = success_eval_func self.other_args = other_args self.restart_browser = restart_browser + self.origin = origin class _CacheTraceTestArguments(): """Struct-like object for passing persistent cache trace test arguments. Cache trace tests consist of a series of normal trace test invocations that - are necessary in order to verify the expected caching behaviors.""" + are necessary in order to verify the expected caching behaviors. The tests + start with a first load page which is generally used to populate cache + entries. If |test_renavigation| is true, the same browser that opened the + first load page is navigated to each cache page in |cache_pages| and the cache + conditions are verified. Then, regardless of the value of |test_renavigation|, + we iterate across the |cache_pages| again and restart the browser for each + page using a new clean user data directory that is seeded with the contents + from the first load page, and verify the cache conditions. The seeding just + copies all files in the user data directory from the first load over, see + *BrowserFinder classes for more details on the seeding: + //third_party/catapult/telemetry/telemetry/internal/backends/chrome/ + + The renavigation tests are suitable when we are verifying for cache hits since + no new entries should be generated in the |cache_pages|. However, they are not + suitable for cache miss cases because each |cache_page| may cause entries to + be written to the cache, thereby causing subsequent |cache_pages| to see cache + hits when we actually expect them to be misses. Note this is not a problem + for the restarted browser case because each browser restart seeds a new + temporary directory with only the contents after the first load page.""" def __init__( # pylint: disable=too-many-arguments - self, browser_args: List[str], category: str, test_harness_script: str, - finish_js_condition: str, first_load_eval_func: str, - cache_hit_eval_func: str, cache_hit_pages: List[str]): + self, + browser_args: List[str], + category: str, + test_harness_script: str, + finish_js_condition: str, + first_load_eval_func: str, + cache_eval_func: str, + cache_pages: List[str], + cache_page_origin: _TraceTestOrigin = _TraceTestOrigin.DEFAULT, + test_renavigation: bool = True): self.browser_args = browser_args self.category = category self.test_harness_script = test_harness_script self.finish_js_condition = finish_js_condition self.first_load_eval_func = first_load_eval_func - self.cache_hit_eval_func = cache_hit_eval_func - self.cache_hit_pages = cache_hit_pages + self.cache_eval_func = cache_eval_func + self.cache_pages = cache_pages + self.cache_page_origin = cache_page_origin + self.test_renavigation = test_renavigation def GenerateFirstLoadTest(self) -> _TraceTestArguments: """Returns the trace test arguments for the first load cache test.""" @@ -186,27 +233,30 @@ ) -> Generator[Tuple[str, _TraceTestArguments], None, None]: """Returns a generator for all cache hit trace tests. - First pass of tests just do a re-navigation, second pass should restarts - with a seeded profile directory. + First pass of tests just do a re-navigation, second pass restarts with a + seeded profile directory. """ - for cache_hit_page in self.cache_hit_pages: + if self.test_renavigation: + for cache_hit_page in self.cache_pages: + yield (posixpath.join(gpu_data_relative_path, cache_hit_page), + _TraceTestArguments(browser_args=self.browser_args, + category=self.category, + test_harness_script=self.test_harness_script, + finish_js_condition=self.finish_js_condition, + success_eval_func=self.cache_eval_func, + other_args=cache_args, + restart_browser=False, + origin=self.cache_page_origin)) + for cache_hit_page in self.cache_pages: yield (posixpath.join(gpu_data_relative_path, cache_hit_page), _TraceTestArguments(browser_args=self.browser_args, category=self.category, test_harness_script=self.test_harness_script, finish_js_condition=self.finish_js_condition, - success_eval_func=self.cache_hit_eval_func, + success_eval_func=self.cache_eval_func, other_args=cache_args, - restart_browser=False)) - for cache_hit_page in self.cache_hit_pages: - yield (posixpath.join(gpu_data_relative_path, cache_hit_page), - _TraceTestArguments(browser_args=self.browser_args, - category=self.category, - test_harness_script=self.test_harness_script, - finish_js_condition=self.finish_js_condition, - success_eval_func=self.cache_hit_eval_func, - other_args=cache_args, - restart_browser=True)) + restart_browser=True, + origin=self.cache_page_origin)) class TraceIntegrationTest(gpu_integration_test.GpuIntegrationTest): @@ -292,14 +342,14 @@ # # The following tests are caching tests that do not render to canvas and so # are not a part of the pixel tests suite. Each tuple represents: - # (test_name, first_load_url, cache_hit_pages) + # (test_name, first_load_url, cache_pages) # # test_name: Name of the test. # first_load_url: The first URL that is loaded and when cache entries should # be written. This URL determines the number of expected cache entries to # expect in following loads. - # cache_hit_pages: List of URLs that should be both re-navigated to, and - # reloaded in a restarted browser to expect cache hits from disk. + # cache_pages: List of URLs that should be both re-navigated and/or + # reloaded in a restarted browser to expect some cache condition. webgpu_cache_test_browser_args = [ cba.ENABLE_UNSAFE_WEBGPU, cba.ENABLE_EXPERIMENTAL_WEB_PLATFORM_FEATURES, @@ -311,7 +361,7 @@ # WebGPU load and reload caching tests. # These tests load the |first_load_url|, records the number of cache # entries written, then both re-navigates and restarts the browser for - # each subsequence |cache_hit_pages| and verifies that the number of cache + # each subsequence |cache_pages| and verifies that the number of cache # hits is at least equal to the number of cache entries written before. webgpu_caching_tests: List[Tuple[str, str, List[str]]] = [ ('RenderPipelineMainThread', 'webgpu-caching.html?testId=render-test', [ @@ -370,7 +420,7 @@ 'webgpu-caching.html?testId=compute-test-async&worker=true' ]), ] - for (name, first_load_page, cache_hit_pages) in webgpu_caching_tests: + for (name, first_load_page, cache_pages) in webgpu_caching_tests: yield ('WebGPUCachingTraceTest_' + name, posixpath.join(gpu_data_relative_path, first_load_page), [ _CacheTraceTestArguments( @@ -379,15 +429,15 @@ test_harness_script=basic_test_harness_script, finish_js_condition='domAutomationController._finished', first_load_eval_func='CheckWebGPUFirstLoadCache', - cache_hit_eval_func='CheckWebGPUCacheHits', - cache_hit_pages=cache_hit_pages) + cache_eval_func='CheckWebGPUCacheHits', + cache_pages=cache_pages) ]) # WebGPU incognito mode caching tests # These tests load the |first_load_url| (which runs the same WebGPU code # multiple times) in incognito mode, verifies that the pages had some # in-memory cache hits, then both re-navigates and restarts the browser - # for each subsequence |cache_hit_pages| and verifies that the number of + # for each subsequence |cache_pages| and verifies that the number of # cache hits is 0 since the in-memory cache should be purged. webgpu_incognito_caching_tests: List[Tuple[str, str, List[str]]] = [ ('RenderPipelineIncognito', @@ -405,8 +455,7 @@ 'webgpu-caching.html?testId=compute-test-async&worker=true' ]), ] - for (name, first_load_page, - cache_hit_pages) in webgpu_incognito_caching_tests: + for (name, first_load_page, cache_pages) in webgpu_incognito_caching_tests: yield ('WebGPUCachingTraceTest_' + name, posixpath.join(gpu_data_relative_path, first_load_page), [ _CacheTraceTestArguments( @@ -416,8 +465,44 @@ test_harness_script=basic_test_harness_script, finish_js_condition='domAutomationController._finished', first_load_eval_func='CheckWebGPUCacheHits', - cache_hit_eval_func='CheckNoWebGPUCacheHits', - cache_hit_pages=cache_hit_pages) + cache_eval_func='CheckNoWebGPUCacheHits', + cache_pages=cache_pages) + ]) + + # WebGPU different origin caching tests + # These tests load the |first_load_url| on the default origin, making sure + # that the load populates on-disk entries. The tests then restart the + # browser for subsequent |cache_pages| on localhost origin and + # verifies that there are no cache hits. + webgpu_origin_caching_tests: List[Tuple[str, str, List[str]]] = [ + ('RenderPipelineDifferentOrigins', + 'webgpu-caching.html?testId=render-test', [ + 'webgpu-caching.html?testId=render-test', + 'webgpu-caching.html?testId=render-test-async', + 'webgpu-caching.html?testId=render-test&worker=true', + 'webgpu-caching.html?testId=render-test-async&worker=true' + ]), + ('ComputePipelineDifferentOrigins', + 'webgpu-caching.html?testId=compute-test', [ + 'webgpu-caching.html?testId=compute-test', + 'webgpu-caching.html?testId=compute-test-async', + 'webgpu-caching.html?testId=compute-test&worker=true', + 'webgpu-caching.html?testId=compute-test-async&worker=true' + ]), + ] + for (name, first_load_page, cache_pages) in webgpu_origin_caching_tests: + yield ('WebGPUCachingTraceTest_' + name, + posixpath.join(gpu_data_relative_path, first_load_page), [ + _CacheTraceTestArguments( + browser_args=webgpu_cache_test_browser_args, + category='gpu', + test_harness_script=basic_test_harness_script, + finish_js_condition='domAutomationController._finished', + first_load_eval_func='CheckWebGPUFirstLoadCache', + cache_eval_func='CheckNoWebGPUCacheHits', + cache_pages=cache_pages, + cache_page_origin=_TraceTestOrigin.LOCALHOST, + test_renavigation=False) ]) def _RunActualGpuTraceTest(self, @@ -443,7 +528,7 @@ tab.browser.platform.tracing_controller.StartTracing(config, 60) # Perform page navigation. - url = self.UrlOfStaticFilePath(test_path) + url = getattr(self, args.origin.value)(test_path) tab.Navigate(url, script_to_evaluate_on_commit=args.test_harness_script) try: @@ -485,7 +570,7 @@ profile_dir=cache_profile_dir.name, profile_type='exact') - # Generate and run the cache hit tests using the seeding cache dir. + # Generate and run the cache hit tests using the seeded cache dir. for (hit_path, trace_params) in params.GenerateCacheHitTests(results): self._RunActualGpuTraceTest(hit_path, trace_params,
diff --git a/device/bluetooth/floss/bluetooth_adapter_floss.cc b/device/bluetooth/floss/bluetooth_adapter_floss.cc index fc92c83..e05cf56 100644 --- a/device/bluetooth/floss/bluetooth_adapter_floss.cc +++ b/device/bluetooth/floss/bluetooth_adapter_floss.cc
@@ -579,6 +579,8 @@ DCHECK(FlossDBusManager::Get()); DCHECK(IsPresent()); + BLUETOOTH_LOG(EVENT) << __func__ << device_found; + auto device_floss = base::WrapUnique(new BluetoothDeviceFloss( this, device_found, ui_task_runner_, socket_thread_)); @@ -603,9 +605,27 @@ base::BindOnce(&BluetoothAdapterFloss::OnGetConnectionState, weak_ptr_factory_.GetWeakPtr(), device_found), device_found); + return; } - BLUETOOTH_LOG(EVENT) << __func__ << device_found; + BluetoothDeviceFloss* device = + static_cast<BluetoothDeviceFloss*>(devices_[canonical_address].get()); + if (UpdateDevice(device, device_floss.get())) { + for (auto& observer : observers_) + observer.DeviceChanged(this, device); + } +} + +bool BluetoothAdapterFloss::UpdateDevice(BluetoothDeviceFloss* device, + BluetoothDeviceFloss* new_device) { + bool updated = false; + + if (new_device->GetName() && device->GetName() != new_device->GetName()) { + device->SetName(new_device->GetName().value_or("")); + updated = true; + } + + return updated; } void BluetoothAdapterFloss::AdapterClearedDevice(
diff --git a/device/bluetooth/floss/bluetooth_adapter_floss.h b/device/bluetooth/floss/bluetooth_adapter_floss.h index 3beb2fd0..2880e8e 100644 --- a/device/bluetooth/floss/bluetooth_adapter_floss.h +++ b/device/bluetooth/floss/bluetooth_adapter_floss.h
@@ -195,6 +195,8 @@ void PopulateInitialDevices(); void ClearAllDevices(); + bool UpdateDevice(BluetoothDeviceFloss* device, + BluetoothDeviceFloss* new_device); // floss::FlossAdapterClient::Observer override. void DiscoverableChanged(bool discoverable) override;
diff --git a/device/bluetooth/floss/bluetooth_floss_unittest.cc b/device/bluetooth/floss/bluetooth_floss_unittest.cc index bab13efa6..dee7eea3 100644 --- a/device/bluetooth/floss/bluetooth_floss_unittest.cc +++ b/device/bluetooth/floss/bluetooth_floss_unittest.cc
@@ -556,6 +556,16 @@ EXPECT_TRUE(same_bonded_device != nullptr); } +TEST_F(BluetoothFlossTest, UpdatesDeviceName) { + InitializeAdapter(); + DiscoverDevices(); + + BluetoothDevice* device = + adapter_->GetDevice(FakeFlossAdapterClient::kClassicAddress); + ASSERT_TRUE(device != nullptr); + EXPECT_EQ(device->GetName(), FakeFlossAdapterClient::kClassicName); +} + #if BUILDFLAG(IS_CHROMEOS) TEST_F(BluetoothFlossTest, StartLowEnergyScanSessions) { InitializeAdapter();
diff --git a/device/bluetooth/floss/fake_floss_adapter_client.cc b/device/bluetooth/floss/fake_floss_adapter_client.cc index db875a18..34b205b 100644 --- a/device/bluetooth/floss/fake_floss_adapter_client.cc +++ b/device/bluetooth/floss/fake_floss_adapter_client.cc
@@ -29,6 +29,8 @@ const char FakeFlossAdapterClient::kKeyboardAddress[] = "aa:aa:aa:aa:aa:aa"; const char FakeFlossAdapterClient::kPhoneAddress[] = "bb:bb:bb:bb:bb:bb"; const char FakeFlossAdapterClient::kOldDeviceAddress[] = "cc:cc:cc:cc:cc:cc"; +const char FakeFlossAdapterClient::kClassicAddress[] = "dd:dd:dd:dd:dd:dd"; +const char FakeFlossAdapterClient::kClassicName[] = "Classic Device"; const uint32_t FakeFlossAdapterClient::kPasskey = 123456; const uint32_t FakeFlossAdapterClient::kHeadsetClassOfDevice = 2360344; @@ -53,6 +55,9 @@ observer.AdapterFoundDevice(FlossDeviceId({kKeyboardAddress, ""})); observer.AdapterFoundDevice(FlossDeviceId({kPhoneAddress, ""})); observer.AdapterFoundDevice(FlossDeviceId({kOldDeviceAddress, ""})); + // Simulate a device which sends its name later + observer.AdapterFoundDevice(FlossDeviceId({kClassicAddress, ""})); + observer.AdapterFoundDevice(FlossDeviceId({kClassicAddress, kClassicName})); } PostDelayedTask(base::BindOnce(std::move(callback), Void{}));
diff --git a/device/bluetooth/floss/fake_floss_adapter_client.h b/device/bluetooth/floss/fake_floss_adapter_client.h index 47610f6c..371b4ed 100644 --- a/device/bluetooth/floss/fake_floss_adapter_client.h +++ b/device/bluetooth/floss/fake_floss_adapter_client.h
@@ -27,6 +27,8 @@ static const char kKeyboardAddress[]; static const char kPhoneAddress[]; static const char kOldDeviceAddress[]; + static const char kClassicAddress[]; + static const char kClassicName[]; static const uint32_t kPasskey; static const uint32_t kHeadsetClassOfDevice;
diff --git a/docs/updater/dev_manual.md b/docs/updater/dev_manual.md index 1472c017..ee8fb28a 100644 --- a/docs/updater/dev_manual.md +++ b/docs/updater/dev_manual.md
@@ -56,6 +56,15 @@ ``` .\tools\mb\mb.bat run --swarmed --no-default-dimensions -d pool chromium.win.uac -d os Windows-10 .\out\Default updater_tests_system -- --gtest_filter=*Install* ``` +* `mb` can schedule tests in the pools managed by different swarming servers. + The default server is + [chromium-swarm.appspot.com](https://chromium-swarm.appspot.com/botlist?k=pool). + To schedule tests to pools managed by + [chrome-swarming.appspot.com](https://chrome-swarming.appspot.com/botlist?k=pool), + for example `chrome.tests`, add `--internal` flag in the command line: + ``` + tools/mb/mb run -v --swarmed --internal --no-default-dimensions -d pool chrome.tests -d os Windows-10 out/WinDefault updater_tests + ``` * If your test introduces dependency on a new app on macOS, you need to let `mb` tool know so it can correctly figure out the dependency. Example: https://crrev.com/c/3470143.
diff --git a/extensions/browser/process_manager.cc b/extensions/browser/process_manager.cc index ae89ae7..f5ea02d4 100644 --- a/extensions/browser/process_manager.cc +++ b/extensions/browser/process_manager.cc
@@ -425,8 +425,11 @@ return nullptr; } -ExtensionHost* ProcessManager::GetExtensionHostForRenderFrameHost( +ExtensionHost* ProcessManager::GetBackgroundHostForRenderFrameHost( content::RenderFrameHost* render_frame_host) { + if (!render_frame_host->IsInPrimaryMainFrame()) + return nullptr; + content::WebContents* web_contents = content::WebContents::FromRenderFrameHost(render_frame_host); for (ExtensionHost* extension_host : background_hosts_) {
diff --git a/extensions/browser/process_manager.h b/extensions/browser/process_manager.h index 7528a5b7..14aa6af 100644 --- a/extensions/browser/process_manager.h +++ b/extensions/browser/process_manager.h
@@ -120,12 +120,9 @@ ExtensionHost* GetBackgroundHostForExtension(const std::string& extension_id); // Returns the background page ExtensionHost for the given - // |render_frame_host|, if |render_frame_host| is within the extension's - // background. Note that this will return the background page host for - // iframes embedded in the background page, even if they are not extension - // frames. - // TODO(https://crbug.com/1340001): Make these "gotchas" less subtle. - ExtensionHost* GetExtensionHostForRenderFrameHost( + // |render_frame_host|, if |render_frame_host| is in primary main frame and + // within the extension's background. + ExtensionHost* GetBackgroundHostForRenderFrameHost( content::RenderFrameHost* render_frame_host); // Returns true if the (lazy) background host for the given extension has
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc b/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc index 00cb394..435bb3b 100644 --- a/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc +++ b/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc
@@ -313,6 +313,7 @@ backend_texture_(size.width(), size.height(), CreateGrVkImageInfo(image_.get())), + promise_texture_(SkPromiseImageTexture::Make(backend_texture_)), command_pool_(command_pool), use_separate_gl_texture_(use_separate_gl_texture) {}
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_backing.h b/gpu/command_buffer/service/shared_image/external_vk_image_backing.h index 7ab3065..f7a7d5e 100644 --- a/gpu/command_buffer/service/shared_image/external_vk_image_backing.h +++ b/gpu/command_buffer/service/shared_image/external_vk_image_backing.h
@@ -21,6 +21,7 @@ #include "gpu/command_buffer/service/shared_memory_region_wrapper.h" #include "gpu/command_buffer/service/texture_manager.h" #include "gpu/vulkan/vulkan_device_queue.h" +#include "third_party/skia/include/core/SkPromiseImageTexture.h" #include "ui/gfx/gpu_memory_buffer.h" namespace gpu { @@ -76,6 +77,9 @@ SharedContextState* context_state() const { return context_state_.get(); } const GrBackendTexture& backend_texture() const { return backend_texture_; } + sk_sp<SkPromiseImageTexture> promise_texture() const { + return promise_texture_; + } VulkanImage* image() const { return image_.get(); } const scoped_refptr<gles2::TexturePassthrough>& GetTexturePassthrough() const { @@ -189,6 +193,7 @@ scoped_refptr<SharedContextState> context_state_; std::unique_ptr<VulkanImage> image_; GrBackendTexture backend_texture_; + sk_sp<SkPromiseImageTexture> promise_texture_; const raw_ptr<VulkanCommandPool> command_pool_; const bool use_separate_gl_texture_;
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_skia_representation.cc b/gpu/command_buffer/service/shared_image/external_vk_image_skia_representation.cc index fa58b9bf..bcbbed0 100644 --- a/gpu/command_buffer/service/shared_image/external_vk_image_skia_representation.cc +++ b/gpu/command_buffer/service/shared_image/external_vk_image_skia_representation.cc
@@ -27,7 +27,8 @@ ~ExternalVkImageSkiaImageRepresentation() { DCHECK_EQ(access_mode_, kNone) << "Previous access hasn't end yet."; DCHECK(!end_access_semaphore_); - backing_impl()->context_state()->EraseCachedSkSurface(this); + backing_impl()->context_state()->EraseCachedSkSurface( + backing_impl()->promise_texture().get()); } sk_sp<SkSurface> ExternalVkImageSkiaImageRepresentation::BeginWriteAccess( @@ -54,7 +55,8 @@ return nullptr; } - auto surface = backing_impl()->context_state()->GetCachedSkSurface(this); + auto surface = backing_impl()->context_state()->GetCachedSkSurface( + promise_texture.get()); // If surface properties are different from the last access, then we cannot // reuse the cached SkSurface. @@ -68,11 +70,13 @@ backing_impl()->color_space().ToSkColorSpace(), &surface_props); if (!surface) { LOG(ERROR) << "MakeFromBackendTexture() failed."; - backing_impl()->context_state()->EraseCachedSkSurface(this); + backing_impl()->context_state()->EraseCachedSkSurface( + promise_texture.get()); return nullptr; } surface_msaa_count_ = final_msaa_count; - backing_impl()->context_state()->CacheSkSurface(this, surface); + backing_impl()->context_state()->CacheSkSurface(promise_texture.get(), + surface); } [[maybe_unused]] int count = surface->getCanvas()->save(); @@ -130,7 +134,8 @@ if (surface) { surface->getCanvas()->restoreToCount(1); surface = nullptr; - DCHECK(backing_impl()->context_state()->CachedSkSurfaceIsUnique(this)); + DCHECK(backing_impl()->context_state()->CachedSkSurfaceIsUnique( + backing_impl()->promise_texture().get())); } EndAccess(false /* readonly */); access_mode_ = kNone; @@ -209,7 +214,7 @@ end_semaphores->back().initVulkan(end_access_semaphore_.GetVkSemaphore()); } - return SkPromiseImageTexture::Make(backing_impl()->backend_texture()); + return backing_impl()->promise_texture(); } void ExternalVkImageSkiaImageRepresentation::EndAccess(bool readonly) {
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc index e7794567..23c9265 100644 --- a/gpu/command_buffer/service/webgpu_decoder_impl.cc +++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -1992,14 +1992,13 @@ base::UnguessableToken::Deserialize(high, low); blink::WebGPUExecutionContextToken execution_context_token; switch (type) { - case blink::WebGPUExecutionContextToken::Base::template TypeIndex< - blink::DocumentToken>::kValue: { + case blink::WebGPUExecutionContextToken::IndexOf<blink::DocumentToken>(): { execution_context_token = blink::WebGPUExecutionContextToken( blink::DocumentToken(unguessable_token)); break; } - case blink::WebGPUExecutionContextToken::Base::template TypeIndex< - blink::DedicatedWorkerToken>::kValue: { + case blink::WebGPUExecutionContextToken::IndexOf< + blink::DedicatedWorkerToken>(): { execution_context_token = blink::WebGPUExecutionContextToken( blink::DedicatedWorkerToken(unguessable_token)); break;
diff --git a/gpu/config/software_rendering_list.json b/gpu/config/software_rendering_list.json index 20ea3b55..e7d662d 100644 --- a/gpu/config/software_rendering_list.json +++ b/gpu/config/software_rendering_list.json
@@ -1689,6 +1689,22 @@ "features": [ "all" ] + }, + { + "id": 178, + "description": "Intel IronLake only shows black in hardware decoded H264 [b/202962575, b/233244020]", + "os": { + "type": "chromeos" + }, + "intel_gpu_generation": { + "op": "=", + "value": "5" + }, + "driver_vendor": "Mesa", + "gl_vendor": "Intel.*", + "features": [ + "accelerated_video_decode" + ] } ] }
diff --git a/gpu/ipc/service/image_transport_surface_overlay_mac.h b/gpu/ipc/service/image_transport_surface_overlay_mac.h index 8d1b4e5..54df9cd 100644 --- a/gpu/ipc/service/image_transport_surface_overlay_mac.h +++ b/gpu/ipc/service/image_transport_surface_overlay_mac.h
@@ -92,7 +92,7 @@ gl::GLSurfaceFormat GetFormat() override; bool OnMakeCurrent(gl::GLContext* context) override; bool ScheduleOverlayPlane( - gl::GLImage* image, + gl::OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) override; bool ScheduleCALayer(const ui::CARendererLayerParams& params) override; @@ -187,7 +187,7 @@ gl::GLSurfaceFormat GetFormat() override; bool OnMakeCurrent(gl::GLContext* context) override; bool ScheduleOverlayPlane( - gl::GLImage* image, + gl::OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) override; bool ScheduleCALayer(const ui::CARendererLayerParams& params) override;
diff --git a/gpu/ipc/service/image_transport_surface_overlay_mac.mm b/gpu/ipc/service/image_transport_surface_overlay_mac.mm index 622ae164..6944ef9d 100644 --- a/gpu/ipc/service/image_transport_surface_overlay_mac.mm +++ b/gpu/ipc/service/image_transport_surface_overlay_mac.mm
@@ -270,7 +270,7 @@ } bool ImageTransportSurfaceOverlayMac::ScheduleOverlayPlane( - gl::GLImage* image, + gl::OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) { if (overlay_plane_data.plane_transform != gfx::OVERLAY_TRANSFORM_NONE) { @@ -607,7 +607,7 @@ } bool ImageTransportSurfaceOverlayMacEGL::ScheduleOverlayPlane( - gl::GLImage* image, + gl::OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) { if (overlay_plane_data.plane_transform != gfx::OVERLAY_TRANSFORM_NONE) {
diff --git a/infra/config/generated/builders/reclient/ios-simulator reclient staging untrusted/properties.json b/infra/config/generated/builders/reclient/ios-simulator reclient staging untrusted/properties.json index 2045994..777bbb3 100644 --- a/infra/config/generated/builders/reclient/ios-simulator reclient staging untrusted/properties.json +++ b/infra/config/generated/builders/reclient/ios-simulator reclient staging untrusted/properties.json
@@ -45,6 +45,9 @@ } }, "$build/reclient": { + "bootstrap_env": { + "GLOG_vmodule": "bridge*=2" + }, "instance": "rbe-chromium-untrusted", "metrics_project": "chromium-reclient-metrics" },
diff --git a/infra/config/generated/builders/reclient/ios-simulator reclient staging/properties.json b/infra/config/generated/builders/reclient/ios-simulator reclient staging/properties.json index 4e570ca..c9df000 100644 --- a/infra/config/generated/builders/reclient/ios-simulator reclient staging/properties.json +++ b/infra/config/generated/builders/reclient/ios-simulator reclient staging/properties.json
@@ -45,6 +45,9 @@ } }, "$build/reclient": { + "bootstrap_env": { + "GLOG_vmodule": "bridge*=2" + }, "instance": "rbe-chromium-trusted", "metrics_project": "chromium-reclient-metrics" },
diff --git a/infra/config/generated/builders/reclient/ios-simulator reclient test untrusted/properties.json b/infra/config/generated/builders/reclient/ios-simulator reclient test untrusted/properties.json index 334ae5e..ce61745 100644 --- a/infra/config/generated/builders/reclient/ios-simulator reclient test untrusted/properties.json +++ b/infra/config/generated/builders/reclient/ios-simulator reclient test untrusted/properties.json
@@ -45,6 +45,10 @@ } }, "$build/reclient": { + "bootstrap_env": { + "GLOG_vmodule": "bridge*=2", + "RBE_ip_timeout": "-1s" + }, "instance": "rbe-chromium-untrusted-test", "metrics_project": "chromium-reclient-metrics" },
diff --git a/infra/config/generated/builders/reclient/ios-simulator reclient test/properties.json b/infra/config/generated/builders/reclient/ios-simulator reclient test/properties.json index 177dd52..03a24d3f 100644 --- a/infra/config/generated/builders/reclient/ios-simulator reclient test/properties.json +++ b/infra/config/generated/builders/reclient/ios-simulator reclient test/properties.json
@@ -45,6 +45,10 @@ } }, "$build/reclient": { + "bootstrap_env": { + "GLOG_vmodule": "bridge*=2", + "RBE_ip_timeout": "-1s" + }, "instance": "rbe-chromium-trusted-test", "metrics_project": "chromium-reclient-metrics" },
diff --git a/infra/config/generated/builders/reclient/mac-arm64-rel reclient staging untrusted/properties.json b/infra/config/generated/builders/reclient/mac-arm64-rel reclient staging untrusted/properties.json index 052d39a..a3eaca6b 100644 --- a/infra/config/generated/builders/reclient/mac-arm64-rel reclient staging untrusted/properties.json +++ b/infra/config/generated/builders/reclient/mac-arm64-rel reclient staging untrusted/properties.json
@@ -44,6 +44,9 @@ } }, "$build/reclient": { + "bootstrap_env": { + "GLOG_vmodule": "bridge*=2" + }, "instance": "rbe-chromium-untrusted", "metrics_project": "chromium-reclient-metrics" },
diff --git a/infra/config/generated/builders/reclient/mac-arm64-rel reclient staging/properties.json b/infra/config/generated/builders/reclient/mac-arm64-rel reclient staging/properties.json index 059d42c3..62488d6 100644 --- a/infra/config/generated/builders/reclient/mac-arm64-rel reclient staging/properties.json +++ b/infra/config/generated/builders/reclient/mac-arm64-rel reclient staging/properties.json
@@ -44,6 +44,9 @@ } }, "$build/reclient": { + "bootstrap_env": { + "GLOG_vmodule": "bridge*=2" + }, "instance": "rbe-chromium-trusted", "metrics_project": "chromium-reclient-metrics" },
diff --git a/infra/config/generated/builders/reclient/mac-arm64-rel reclient test untrusted/properties.json b/infra/config/generated/builders/reclient/mac-arm64-rel reclient test untrusted/properties.json index b94ed01..1698692 100644 --- a/infra/config/generated/builders/reclient/mac-arm64-rel reclient test untrusted/properties.json +++ b/infra/config/generated/builders/reclient/mac-arm64-rel reclient test untrusted/properties.json
@@ -44,6 +44,10 @@ } }, "$build/reclient": { + "bootstrap_env": { + "GLOG_vmodule": "bridge*=2", + "RBE_ip_timeout": "-1s" + }, "instance": "rbe-chromium-untrusted-test", "metrics_project": "chromium-reclient-metrics" },
diff --git a/infra/config/generated/builders/reclient/mac-arm64-rel reclient test/properties.json b/infra/config/generated/builders/reclient/mac-arm64-rel reclient test/properties.json index f97086c..5c5eac4c 100644 --- a/infra/config/generated/builders/reclient/mac-arm64-rel reclient test/properties.json +++ b/infra/config/generated/builders/reclient/mac-arm64-rel reclient test/properties.json
@@ -44,6 +44,10 @@ } }, "$build/reclient": { + "bootstrap_env": { + "GLOG_vmodule": "bridge*=2", + "RBE_ip_timeout": "-1s" + }, "instance": "rbe-chromium-trusted-test", "metrics_project": "chromium-reclient-metrics" },
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg index d471cf29..97a5f79 100644 --- a/infra/config/generated/luci/cr-buildbucket.cfg +++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -3872,6 +3872,9 @@ ' "use_luci_auth": true' ' },' ' "$build/reclient": {' + ' "bootstrap_env": {' + ' "RBE_experimental_goma_deps_cache": "true"' + ' },' ' "cache_silo": "Comparison Android CQ - cache siloed",' ' "instance": "rbe-chromium-untrusted-test",' ' "jobs": 300,' @@ -3962,6 +3965,9 @@ ' "use_luci_auth": true' ' },' ' "$build/reclient": {' + ' "bootstrap_env": {' + ' "RBE_experimental_goma_deps_cache": "true"' + ' },' ' "cache_silo": "Comparison Linux - cache siloed",' ' "instance": "rbe-chromium-trusted",' ' "jobs": 250,' @@ -4051,6 +4057,9 @@ ' "use_luci_auth": true' ' },' ' "$build/reclient": {' + ' "bootstrap_env": {' + ' "RBE_experimental_goma_deps_cache": "true"' + ' },' ' "cache_silo": "Comparison Linux CQ - cache siloed",' ' "instance": "rbe-chromium-untrusted-test",' ' "jobs": 150,' @@ -4139,7 +4148,9 @@ ' },' ' "$build/reclient": {' ' "bootstrap_env": {' - ' "GLOG_vmodule": "bridge*=2"' + ' "GLOG_vmodule": "bridge*=2",' + ' "RBE_experimental_goma_deps_cache": "true",' + ' "RBE_ip_timeout": "-1s"' ' },' ' "cache_silo": "Comparison Mac - cache siloed",' ' "instance": "rbe-chromium-trusted-test",' @@ -4229,7 +4240,8 @@ ' },' ' "$build/reclient": {' ' "bootstrap_env": {' - ' "GLOG_vmodule": "bridge*=2"' + ' "GLOG_vmodule": "bridge*=2",' + ' "RBE_experimental_goma_deps_cache": "true"' ' },' ' "cache_silo": "Comparison Mac CQ - cache siloed",' ' "instance": "rbe-chromium-untrusted-test",' @@ -4319,7 +4331,8 @@ ' },' ' "$build/reclient": {' ' "bootstrap_env": {' - ' "GLOG_vmodule": "bridge*=2"' + ' "GLOG_vmodule": "bridge*=2",' + ' "RBE_experimental_goma_deps_cache": "true"' ' },' ' "cache_silo": "Comparison Mac - cache siloed",' ' "instance": "rbe-chromium-trusted-test",' @@ -4408,7 +4421,8 @@ ' },' ' "$build/reclient": {' ' "bootstrap_env": {' - ' "GLOG_vmodule": "bridge*=2"' + ' "GLOG_vmodule": "bridge*=2",' + ' "RBE_experimental_goma_deps_cache": "true"' ' },' ' "cache_silo": "Comparison Mac - cache siloed",' ' "instance": "rbe-chromium-trusted-test",' @@ -4588,6 +4602,9 @@ ' "use_luci_auth": true' ' },' ' "$build/reclient": {' + ' "bootstrap_env": {' + ' "RBE_experimental_goma_deps_cache": "true"' + ' },' ' "cache_silo": "Comparison Simple Chrome CQ - cache siloed",' ' "instance": "rbe-chromium-untrusted-test",' ' "jobs": 300,' @@ -4678,6 +4695,9 @@ ' "use_luci_auth": true' ' },' ' "$build/reclient": {' + ' "bootstrap_env": {' + ' "RBE_experimental_goma_deps_cache": "true"' + ' },' ' "cache_silo": "Comparison Windows 8 cores - cache siloed",' ' "instance": "rbe-chromium-trusted",' ' "jobs": 80,' @@ -4767,99 +4787,10 @@ ' "use_luci_auth": true' ' },' ' "$build/reclient": {' - ' "cache_silo": "Comparison Windows - cache siloed",' - ' "instance": "rbe-chromium-trusted",' - ' "jobs": 250,' - ' "metrics_project": "chromium-reclient-metrics"' - ' },' - ' "$recipe_engine/resultdb/test_presentation": {' - ' "column_keys": [],' - ' "grouping_keys": [' - ' "status",' - ' "v.test_suite"' - ' ]' - ' },' - ' "builder_group": "chromium.fyi",' - ' "recipe": "reclient_goma_comparison"' - '}' - priority: 35 - execution_timeout_secs: 21600 - build_numbers: YES - service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com" - experiments { - key: "chromium_swarming.expose_merge_script_failures" - value: 10 - } - experiments { - key: "luci.buildbucket.omit_python2" - value: 100 - } - experiments { - key: "luci.recipes.use_python3" - value: 100 - } - resultdb { - enable: true - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "ci_test_results" - test_results {} - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "gpu_ci_test_results" - test_results { - predicate { - test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+" - } - } - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "blink_web_tests_ci_test_results" - test_results { - predicate { - test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*blink_wpt_tests/.+)" - } - } - } - history_options { - use_invocation_timestamp: true - } - } - } - builders { - name: "Comparison Windows (reclient) (reproxy cache)" - swarming_host: "chromium-swarm.appspot.com" - dimensions: "builderless:1" - dimensions: "cores:32" - dimensions: "cpu:x86-64" - dimensions: "free_space:high" - dimensions: "os:Windows-10" - dimensions: "pool:luci.chromium.ci" - dimensions: "ssd:0" - exe { - cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" - cipd_version: "refs/heads/main" - cmd: "luciexe" - } - properties: - '{' - ' "$build/goma": {' - ' "enable_ats": true,' - ' "jobs": 250,' - ' "rpc_extra_params": "?prod",' - ' "server_host": "goma.chromium.org",' - ' "use_luci_auth": true' - ' },' - ' "$build/reclient": {' ' "bootstrap_env": {' ' "RBE_experimental_goma_deps_cache": "true"' ' },' - ' "cache_silo": "Comparison Windows (reproxy cache) - cache siloed",' + ' "cache_silo": "Comparison Windows - cache siloed",' ' "instance": "rbe-chromium-trusted",' ' "jobs": 250,' ' "metrics_project": "chromium-reclient-metrics"' @@ -4948,6 +4879,9 @@ ' "use_luci_auth": true' ' },' ' "$build/reclient": {' + ' "bootstrap_env": {' + ' "RBE_experimental_goma_deps_cache": "true"' + ' },' ' "cache_silo": "Comparison Windows CQ - cache siloed",' ' "instance": "rbe-chromium-untrusted-test",' ' "jobs": 300,' @@ -5035,6 +4969,9 @@ ' "use_luci_auth": true' ' },' ' "$build/reclient": {' + ' "bootstrap_env": {' + ' "RBE_experimental_goma_deps_cache": "true"' + ' },' ' "cache_silo": "Comparison ios - cache siloed",' ' "instance": "rbe-chromium-trusted-test",' ' "jobs": 250,' @@ -5127,6 +5064,9 @@ ' "use_luci_auth": true' ' },' ' "$build/reclient": {' + ' "bootstrap_env": {' + ' "RBE_experimental_goma_deps_cache": "true"' + ' },' ' "cache_silo": "Comparison ios CQ - cache siloed",' ' "instance": "rbe-chromium-untrusted-test",' ' "jobs": 150,'
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg index 13a148a..d684fb0 100644 --- a/infra/config/generated/luci/luci-milo.cfg +++ b/infra/config/generated/luci/luci-milo.cfg
@@ -8687,11 +8687,6 @@ category: "win|cq" short_name: "re" } - builders { - name: "buildbucket/luci.chromium.ci/Comparison Windows (reclient) (reproxy cache)" - category: "win|expcache" - short_name: "re" - } header { oncalls { name: "Chromium"
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg index bff57767..76bd5c89 100644 --- a/infra/config/generated/luci/luci-scheduler.cfg +++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -839,16 +839,6 @@ } } job { - id: "Comparison Windows (reclient) (reproxy cache)" - realm: "ci" - acl_sets: "ci" - buildbucket { - server: "cr-buildbucket.appspot.com" - bucket: "ci" - builder: "Comparison Windows (reclient) (reproxy cache)" - } -} -job { id: "Comparison Windows (reclient)(CQ)" realm: "ci" acl_sets: "ci" @@ -7274,7 +7264,6 @@ triggers: "Comparison Simple Chrome (reclient)(CQ)" triggers: "Comparison Windows (8 cores) (reclient)" triggers: "Comparison Windows (reclient)" - triggers: "Comparison Windows (reclient) (reproxy cache)" triggers: "Comparison Windows (reclient)(CQ)" triggers: "Comparison ios (reclient)" triggers: "Comparison ios (reclient)(CQ)"
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star index ed4a4fe..0c2bfed 100644 --- a/infra/config/subprojects/chromium/ci/chromium.fyi.star +++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -1098,6 +1098,9 @@ reclient_instance = reclient.instance.DEFAULT_TRUSTED, reclient_jobs = 250, os = os.LINUX_DEFAULT, + reclient_bootstrap_env = { + "RBE_experimental_goma_deps_cache": "true", + }, ) ci.builder( @@ -1116,7 +1119,9 @@ os = os.MAC_DEFAULT, cores = None, reclient_bootstrap_env = { + "RBE_ip_timeout": "-1s", "GLOG_vmodule": "bridge*=2", + "RBE_experimental_goma_deps_cache": "true", }, ) @@ -1137,6 +1142,7 @@ cores = None, reclient_bootstrap_env = { "GLOG_vmodule": "bridge*=2", + "RBE_experimental_goma_deps_cache": "true", }, ) @@ -1158,6 +1164,7 @@ cpu = cpu.ARM64, reclient_bootstrap_env = { "GLOG_vmodule": "bridge*=2", + "RBE_experimental_goma_deps_cache": "true", }, ) @@ -1176,6 +1183,9 @@ reclient_jobs = 80, os = os.WINDOWS_DEFAULT, free_space = builders.free_space.high, + reclient_bootstrap_env = { + "RBE_experimental_goma_deps_cache": "true", + }, ) ci.builder( @@ -1194,27 +1204,9 @@ reclient_jobs = 250, os = os.WINDOWS_DEFAULT, free_space = builders.free_space.high, -) - -ci.builder( - name = "Comparison Windows (reclient) (reproxy cache)", - builderless = True, - console_view_entry = consoles.console_view_entry( - category = "win|expcache", - short_name = "re", - ), - cores = 32, - goma_jobs = 250, - executable = "recipe:reclient_goma_comparison", - execution_timeout = 6 * time.hour, - reclient_cache_silo = "Comparison Windows (reproxy cache) - cache siloed", - reclient_instance = reclient.instance.DEFAULT_TRUSTED, - reclient_jobs = 250, reclient_bootstrap_env = { "RBE_experimental_goma_deps_cache": "true", }, - os = os.WINDOWS_DEFAULT, - free_space = builders.free_space.high, ) ci.builder( @@ -1249,6 +1241,9 @@ os = os.MAC_DEFAULT, cores = None, xcode = xcode.x14main, + reclient_bootstrap_env = { + "RBE_experimental_goma_deps_cache": "true", + }, ) ci.builder( @@ -1270,6 +1265,9 @@ os = os.LINUX_DEFAULT, cores = 32, ssd = True, + reclient_bootstrap_env = { + "RBE_experimental_goma_deps_cache": "true", + }, ) ci.builder( @@ -1291,6 +1289,9 @@ os = os.LINUX_DEFAULT, cores = 16, ssd = True, + reclient_bootstrap_env = { + "RBE_experimental_goma_deps_cache": "true", + }, ) ci.builder( @@ -1315,6 +1316,7 @@ cores = None, reclient_bootstrap_env = { "GLOG_vmodule": "bridge*=2", + "RBE_experimental_goma_deps_cache": "true", }, ) @@ -1339,6 +1341,9 @@ os = os.WINDOWS_DEFAULT, ssd = True, cores = 32, + reclient_bootstrap_env = { + "RBE_experimental_goma_deps_cache": "true", + }, ) ci.builder( @@ -1361,6 +1366,9 @@ os = os.LINUX_DEFAULT, cores = 32, ssd = True, + reclient_bootstrap_env = { + "RBE_experimental_goma_deps_cache": "true", + }, ) ci.builder( @@ -1384,6 +1392,9 @@ cores = None, ssd = True, xcode = xcode.x14main, + reclient_bootstrap_env = { + "RBE_experimental_goma_deps_cache": "true", + }, ) # Build Perf builders use CQ reclient instance and high reclient jobs/cores and
diff --git a/infra/config/subprojects/reclient/reclient.star b/infra/config/subprojects/reclient/reclient.star index 579856a..e1b9cdd 100644 --- a/infra/config/subprojects/reclient/reclient.star +++ b/infra/config/subprojects/reclient/reclient.star
@@ -308,6 +308,10 @@ cores = None, xcode = xcode.x13main, priority = 35, + reclient_bootstrap_env = { + "RBE_ip_timeout": "-1s", + "GLOG_vmodule": "bridge*=2", + }, ) fyi_reclient_staging_builder( @@ -332,6 +336,9 @@ cores = None, xcode = xcode.x13main, priority = 35, + reclient_bootstrap_env = { + "GLOG_vmodule": "bridge*=2", + }, ) fyi_reclient_staging_builder( @@ -355,6 +362,9 @@ builderless = True, cores = None, priority = 35, + reclient_bootstrap_env = { + "GLOG_vmodule": "bridge*=2", + }, ) fyi_reclient_test_builder( @@ -378,4 +388,8 @@ builderless = True, cores = None, priority = 35, + reclient_bootstrap_env = { + "RBE_ip_timeout": "-1s", + "GLOG_vmodule": "bridge*=2", + }, )
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm index f778419..3661a0e 100644 --- a/ios/chrome/browser/flags/about_flags.mm +++ b/ios/chrome/browser/flags/about_flags.mm
@@ -1272,6 +1272,11 @@ flag_descriptions::kEnableRefineDataSourceReloadReportingDescription, flags_ui::kOsIos, FEATURE_VALUE_TYPE(kEnableRefineDataSourceReloadReporting)}, + {"enable-compromised-passwords-muting", + flag_descriptions::kEnableCompromisedPasswordsMutingName, + flag_descriptions::kEnableCompromisedPasswordsMutingDescription, + flags_ui::kOsIos, + FEATURE_VALUE_TYPE(password_manager::features::kMuteCompromisedPasswords)}, }; 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 8aea288..b56636e 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -259,6 +259,12 @@ const char kEnableCheckVisibilityOnAttentionLogStartDescription[] = "Enable checking feed visibility on attention log start."; +const char kEnableCompromisedPasswordsMutingName[] = + "Enable the muting of compromised passwords in the Password Manager"; +const char kEnableCompromisedPasswordsMutingDescription[] = + "Enable the compromised password alert mutings in Password Manager to be " + "respected in the app."; + const char kEnableDiscoverFeedDiscoFeedEndpointName[] = "Enable discover feed discofeed"; const char kEnableDiscoverFeedDiscoFeedEndpointDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h index b0afa820..46dd7ed 100644 --- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h +++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -198,6 +198,11 @@ extern const char kEnableCheckVisibilityOnAttentionLogStartName[]; extern const char kEnableCheckVisibilityOnAttentionLogStartDescription[]; +// Title and description for the flag to enable the muting of compromised +// passwords in the Password Manager. +extern const char kEnableCompromisedPasswordsMutingName[]; +extern const char kEnableCompromisedPasswordsMutingDescription[]; + // Title and description for the flag to enable the sync promotion on top of the // discover feed. extern const char kEnableDiscoverFeedTopSyncPromoName[];
diff --git a/ios/chrome/browser/follow/BUILD.gn b/ios/chrome/browser/follow/BUILD.gn index a7ffbfc..d4f6e66c 100644 --- a/ios/chrome/browser/follow/BUILD.gn +++ b/ios/chrome/browser/follow/BUILD.gn
@@ -67,6 +67,8 @@ "//ios/chrome/browser/follow:enums", "//ios/chrome/browser/follow:utils", "//ios/chrome/browser/history", + "//ios/chrome/browser/ntp:features", + "//ios/chrome/browser/signin", "//ios/chrome/browser/url", "//ios/web/public", "//ios/web/public/js_messaging",
diff --git a/ios/chrome/browser/follow/follow_tab_helper.mm b/ios/chrome/browser/follow/follow_tab_helper.mm index 69e61e1..ff397922 100644 --- a/ios/chrome/browser/follow/follow_tab_helper.mm +++ b/ios/chrome/browser/follow/follow_tab_helper.mm
@@ -27,6 +27,9 @@ #import "ios/chrome/browser/follow/follow_service_factory.h" #import "ios/chrome/browser/follow/follow_util.h" #import "ios/chrome/browser/history/history_service_factory.h" +#import "ios/chrome/browser/ntp/features.h" +#import "ios/chrome/browser/signin/authentication_service.h" +#import "ios/chrome/browser/signin/authentication_service_factory.h" #import "ios/chrome/browser/url/url_util.h" #import "ios/chrome/grit/ios_strings.h" #import "ios/web/public/js_messaging/web_frame.h" @@ -104,9 +107,23 @@ void FollowTabHelper::PageLoaded( web::WebState* web_state, web::PageLoadCompletionStatus load_completion_status) { - // TODO(crbug.com/1340154): move the checking to `follow_iph_presenter_` - // (FollowIPHCoordinator), so this class won't need to access browser_state - // anymore, which brings convinience to testing. + // Do not show follow IPH if the user is not signed in. + ChromeBrowserState* browserState = + ChromeBrowserState::FromBrowserState(web_state->GetBrowserState()); + AuthenticationService* authenticationService = + AuthenticationServiceFactory::GetForBrowserState(browserState); + if (!authenticationService || !authenticationService->GetPrimaryIdentity( + signin::ConsentLevel::kSignin)) { + return; + } + + // Do not show Follow IPH if it is disabled. + if (!base::FeatureList::IsEnabled( + feature_engagement::kIPHFollowWhileBrowsingFeature)) { + return; + } + + DCHECK(IsWebChannelsEnabled()); // Record when the page was successfully loaded. Computing whether the // IPH needs to be displayed is done asynchronously and the time used @@ -114,8 +131,8 @@ // displayed. const base::Time page_load_time = base::Time::Now(); - // Do not update follow menu option and do not show IPH when browsing non - // http,https URLs and Chrome URLs, such as NTP, flags, version, sad tab, etc. + // Do not show IPH when browsing non http, https URLs and Chrome URLs, such as + // NTP, flags, version, sad tab, etc. const GURL& url = web_state->GetVisibleURL(); if (UrlHasChromeScheme(url) || !url.SchemeIsHTTPOrHTTPS()) { return; @@ -146,13 +163,6 @@ WebPageURLs* web_page_urls) { DCHECK(web_state_); - // Update follow menu option if needed. - if (follow_menu_updater_ && should_update_follow_item_) { - UpdateFollowMenuItemWithURL(web_page_urls); - } - - // Show follow in-product help (IPH) if eligible. - // Don't show follow in-product help (IPH) if there's no presenter. Ex. // follow_iph_presenter_ is nil when link preview page is loaded. if (!follow_iph_presenter_) {
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory/branding_view_controller.mm b/ios/chrome/browser/ui/autofill/form_input_accessory/branding_view_controller.mm index 7c28937..fa215f52 100644 --- a/ios/chrome/browser/ui/autofill/form_input_accessory/branding_view_controller.mm +++ b/ios/chrome/browser/ui/autofill/form_input_accessory/branding_view_controller.mm
@@ -57,8 +57,6 @@ NOTREACHED(); break; } - UIImage* logo = [[UIImage imageNamed:logoName] - imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom]; if (@available(iOS 15.0, *)) { UIButtonConfiguration* buttonConfig = @@ -69,13 +67,11 @@ } else { button.imageEdgeInsets = UIEdgeInsetsMake(0, kLeadingInset, 0, 0); } - [button setImage:logo forState:UIControlStateNormal]; - [button setImage:logo forState:UIControlStateHighlighted]; button.accessibilityIdentifier = kBrandingButtonAXId; - button.imageView.contentMode = UIViewContentModeScaleAspectFit; button.isAccessibilityElement = NO; // Prevents VoiceOver users from tap. button.translatesAutoresizingMaskIntoConstraints = NO; self.view = button; + [self configureBrandingWithImageName:logoName]; // Adds keyboard popup listener to show animation when keyboard is fully // settled. @@ -86,6 +82,22 @@ object:nil]; } +#pragma mark - UITraitEnvironment + +// UIImages with rendering mode UIImageRenderingModeAlwaysOriginal do not +// automatically respond when the user interface mode changes. To fix this, the +// view controller should get notified on these changes and update the image +// accordingly. Similar issue and fix as crbug.com/998090. +- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection { + [super traitCollectionDidChange:previousTraitCollection]; + if (self.traitCollection.userInterfaceStyle != + previousTraitCollection.userInterfaceStyle && + autofill::features::GetAutofillBrandingType() == + autofill::features::AutofillBrandingType::kMonotone) { + [self configureBrandingWithImageName:@"monotone_branding_icon"]; + } +} + #pragma mark - Accessors - (BOOL)shouldAnimate { @@ -109,6 +121,17 @@ #pragma mark - Private +// Add the branding image with the correct size, and make sure it persists +// across different button states. +- (void)configureBrandingWithImageName:(NSString*)name { + UIImage* logo = [[UIImage imageNamed:name] + imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; + UIButton* button = (UIButton*)self.view; + [button setImage:logo forState:UIControlStateNormal]; + [button setImage:logo forState:UIControlStateHighlighted]; + button.imageView.contentMode = UIViewContentModeScaleAspectFit; +} + // Check if the branding icon is visible and should perform an animation, and do // so if it should. - (void)onKeyboardAnimationComplete {
diff --git a/mojo/core/ipcz_driver/transport.cc b/mojo/core/ipcz_driver/transport.cc index ebf2e9a1..a2227ea 100644 --- a/mojo/core/ipcz_driver/transport.cc +++ b/mojo/core/ipcz_driver/transport.cc
@@ -101,8 +101,10 @@ #if BUILDFLAG(IS_WIN) // Encodes a Windows HANDLE value for transmission within a serialized driver // object payload. See documentation on HandleOwner above for general notes -// about how handles are communicated over IPC on Windows. -void EncodeHandle(PlatformHandle& handle, +// about how handles are communicated over IPC on Windows. Returns true on +// success, with the encoded handle value in `out_handle`. Returns false if +// handle duplication failed. +bool EncodeHandle(PlatformHandle& handle, const base::Process& remote_process, HandleOwner handle_owner, HANDLE& out_handle) { @@ -112,7 +114,7 @@ // be sufficiently privileged and equipped to duplicate such handles to // itself. out_handle = handle.ReleaseHandle(); - return; + return true; } // To encode a handle that already belongs to the recipient, we must first @@ -121,10 +123,9 @@ // handle to the remote process. DCHECK_EQ(handle_owner, HandleOwner::kRecipient); DCHECK(remote_process.IsValid()); - BOOL result = ::DuplicateHandle( - ::GetCurrentProcess(), handle.ReleaseHandle(), remote_process.Handle(), - &out_handle, 0, FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); - DCHECK(result); + return ::DuplicateHandle(::GetCurrentProcess(), handle.ReleaseHandle(), + remote_process.Handle(), &out_handle, 0, FALSE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); } // Decodes a Windows HANDLE value from a transmission containing a serialized @@ -398,17 +399,18 @@ return IPCZ_RESULT_INVALID_ARGUMENT; } + bool ok = true; for (size_t i = 0; i < object_num_handles; ++i) { #if BUILDFLAG(IS_WIN) - EncodeHandle(platform_handles[i], remote_process_, handle_owner, - handle_data[i]); + ok &= EncodeHandle(platform_handles[i], remote_process_, handle_owner, + handle_data[i]); #else handles[i] = TransmissiblePlatformHandle::ReleaseAsHandle( base::MakeRefCounted<TransmissiblePlatformHandle>( std::move(platform_handles[i]))); #endif } - return IPCZ_RESULT_OK; + return ok ? IPCZ_RESULT_OK : IPCZ_RESULT_INVALID_ARGUMENT; } IpczResult Transport::DeserializeObject(
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc index 2446869d..b794d96e 100644 --- a/net/url_request/url_request.cc +++ b/net/url_request/url_request.cc
@@ -12,7 +12,6 @@ #include "base/compiler_specific.h" #include "base/metrics/histogram_macros.h" #include "base/rand_util.h" -#include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/lock.h" #include "base/threading/thread_task_runner_handle.h" @@ -47,9 +46,6 @@ #include "url/gurl.h" #include "url/origin.h" -using base::Time; -using std::string; - namespace net { namespace { @@ -225,8 +221,8 @@ return upload_data_stream_.get() != nullptr; } -void URLRequest::SetExtraRequestHeaderByName(const string& name, - const string& value, +void URLRequest::SetExtraRequestHeaderByName(base::StringPiece name, + base::StringPiece value, bool overwrite) { DCHECK(!is_pending_ || is_redirecting_); if (overwrite) { @@ -236,7 +232,7 @@ } } -void URLRequest::RemoveRequestHeaderByName(const string& name) { +void URLRequest::RemoveRequestHeaderByName(base::StringPiece name) { DCHECK(!is_pending_ || is_redirecting_); extra_request_headers_.RemoveHeader(name); } @@ -319,9 +315,8 @@ return base::Value(std::move(dict)); } -void URLRequest::LogBlockedBy(const char* blocked_by) { - DCHECK(blocked_by); - DCHECK_GT(strlen(blocked_by), 0u); +void URLRequest::LogBlockedBy(base::StringPiece blocked_by) { + DCHECK(!blocked_by.empty()); // Only log information to NetLog during startup and certain deferring calls // to delegates. For all reads but the first, do nothing. @@ -329,14 +324,14 @@ return; LogUnblocked(); - blocked_by_ = blocked_by; + blocked_by_ = std::string(blocked_by); use_blocked_by_as_load_param_ = false; net_log_.BeginEventWithStringParams(NetLogEventType::DELEGATE_INFO, "delegate_blocked_by", blocked_by_); } -void URLRequest::LogAndReportBlockedBy(const char* source) { +void URLRequest::LogAndReportBlockedBy(base::StringPiece source) { LogBlockedBy(source); use_blocked_by_as_load_param_ = true; } @@ -368,8 +363,8 @@ return UploadProgress(); } -void URLRequest::GetResponseHeaderByName(const string& name, - string* value) const { +void URLRequest::GetResponseHeaderByName(base::StringPiece name, + std::string* value) const { DCHECK(value); if (response_info_.headers.get()) { response_info_.headers->GetNormalizedHeader(name, value); @@ -409,12 +404,12 @@ return job_->GetTransactionRemoteEndpoint(endpoint); } -void URLRequest::GetMimeType(string* mime_type) const { +void URLRequest::GetMimeType(std::string* mime_type) const { DCHECK(job_.get()); job_->GetMimeType(mime_type); } -void URLRequest::GetCharset(string* charset) const { +void URLRequest::GetCharset(std::string* charset) const { DCHECK(job_.get()); job_->GetCharset(charset); } @@ -512,13 +507,13 @@ } #endif -void URLRequest::SetReferrer(const std::string& referrer) { +void URLRequest::SetReferrer(base::StringPiece referrer) { DCHECK(!is_pending_); GURL referrer_url(referrer); if (referrer_url.is_valid()) { referrer_ = referrer_url.GetAsReferrer().spec(); } else { - referrer_ = referrer; + referrer_ = std::string(referrer); } }
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h index 778a965..8d820e7 100644 --- a/net/url_request/url_request.h +++ b/net/url_request/url_request.h
@@ -14,7 +14,7 @@ #include "base/containers/flat_set.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" -#include "base/strings/string_piece_forward.h" +#include "base/strings/string_piece.h" #include "base/supports_user_data.h" #include "base/threading/thread_checker.h" #include "base/time/time.h" @@ -375,7 +375,7 @@ // the request is started. The referrer URL may be suppressed or changed // during the course of the request, for example because of a referrer policy // set with set_referrer_policy(). - void SetReferrer(const std::string& referrer); + void SetReferrer(base::StringPiece referrer); // The referrer policy to apply when updating the referrer during redirects. // The referrer policy may only be changed before Start() is called. Any @@ -406,10 +406,10 @@ // Set or remove a extra request header. These methods may only be called // before Start() is called, or between receiving a redirect and trying to // follow it. - void SetExtraRequestHeaderByName(const std::string& name, - const std::string& value, + void SetExtraRequestHeaderByName(base::StringPiece name, + base::StringPiece value, bool overwrite); - void RemoveRequestHeaderByName(const std::string& name); + void RemoveRequestHeaderByName(base::StringPiece name); // Sets all extra request headers. Any extra request headers set by other // methods are overwritten by this method. This method may only be called @@ -450,15 +450,13 @@ // Logs information about the what external object currently blocking the // request. LogUnblocked must be called before resuming the request. This // can be called multiple times in a row either with or without calling - // LogUnblocked between calls. |blocked_by| must not be NULL or have length - // 0. - void LogBlockedBy(const char* blocked_by); + // LogUnblocked between calls. |blocked_by| must not be empty. + void LogBlockedBy(base::StringPiece blocked_by); // Just like LogBlockedBy, but also makes GetLoadState return source as the // |param| in the value returned by GetLoadState. Calling LogUnblocked or - // LogBlockedBy will clear the load param. |blocked_by| must not be NULL or - // have length 0. - void LogAndReportBlockedBy(const char* blocked_by); + // LogBlockedBy will clear the load param. |blocked_by| must not be empty. + void LogAndReportBlockedBy(base::StringPiece blocked_by); // Logs that the request is no longer blocked by the last caller to // LogBlockedBy. @@ -473,7 +471,7 @@ // that appear more than once in the response are coalesced, with values // separated by commas (per RFC 2616). This will not work with cookies since // comma can be used in cookie values. - void GetResponseHeaderByName(const std::string& name, + void GetResponseHeaderByName(base::StringPiece name, std::string* value) const; // The time when |this| was constructed. @@ -811,8 +809,8 @@ return expected_response_checksum_; } - void set_expected_response_checksum(const std::string& checksum) { - expected_response_checksum_ = checksum; + void set_expected_response_checksum(base::StringPiece checksum) { + expected_response_checksum_ = std::string(checksum); } static bool DefaultCanUseCookies();
diff --git a/services/device/generic_sensor/platform_sensor_fusion.cc b/services/device/generic_sensor/platform_sensor_fusion.cc index d08aa3b..c8f2560a 100644 --- a/services/device/generic_sensor/platform_sensor_fusion.cc +++ b/services/device/generic_sensor/platform_sensor_fusion.cc
@@ -4,6 +4,8 @@ #include "services/device/generic_sensor/platform_sensor_fusion.h" +#include <algorithm> + #include "base/bind.h" #include "base/check.h" #include "base/memory/raw_ptr.h" @@ -45,7 +47,7 @@ const auto& types = fusion_algorithm_->source_types(); DCHECK(!types.empty()); // Make sure there are no dups. - DCHECK(base::ranges::adjacent_find(types) == types.end()); + DCHECK(std::adjacent_find(types.begin(), types.end()) == types.end()); DCHECK(result_callback_); DCHECK(reading_buffer_); DCHECK(provider_);
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json index 18bebcc..07957d2 100644 --- a/testing/buildbot/chromium.android.json +++ b/testing/buildbot/chromium.android.json
@@ -28470,6 +28470,77 @@ }, { "args": [ + "--use-persistent-shell", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--avd-config=../../tools/android/avd/proto/generic_android24.textpb", + "--git-revision=${got_revision}" + ], + "ci_only": true, + "experiment_percentage": 100, + "isolate_profile_data": true, + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "chrome_public_persistent_shell_test_apk" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "chrome_public_persistent_shell_test_apk", + "precommit_args": [ + "--gerrit-issue=${patch_issue}", + "--gerrit-patchset=${patch_set}", + "--buildbucket-id=${buildbucket_build_id}" + ], + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "cpu": "x86-64", + "device_os": null, + "device_type": null, + "machine_type": "n1-standard-4|e2-standard-4", + "os": "Ubuntu-18.04", + "pool": "chromium.tests.avd" + } + ], + "named_caches": [ + { + "name": "generic_android25", + "path": ".android_emulator/generic_android25" + } + ], + "optional_dimensions": { + "60": [ + { + "caches": "generic_android25" + } + ] + }, + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 25 + }, + "test": "chrome_public_test_apk", + "test_id_prefix": "ninja://chrome/android:chrome_public_test_apk/" + }, + { + "args": [ "--gtest_filter=org.chromium.chrome.browser.contextualsearch.ContextualSearchManagerTest.test*ExternalNavigationWithUserGesture*:org.chromium.shape_detection.*", "--gs-results-bucket=chromium-result-details", "--recover-devices", @@ -35445,6 +35516,61 @@ }, { "args": [ + "--use-persistent-shell", + "--gs-results-bucket=chromium-result-details", + "--recover-devices", + "--git-revision=${got_revision}" + ], + "ci_only": true, + "experiment_percentage": 100, + "merge": { + "args": [ + "--bucket", + "chromium-result-details", + "--test-name", + "chrome_public_persistent_shell_test_apk" + ], + "script": "//build/android/pylib/results/presentation/test_results_presentation.py" + }, + "name": "chrome_public_persistent_shell_test_apk", + "precommit_args": [ + "--gerrit-issue=${patch_issue}", + "--gerrit-patchset=${patch_set}", + "--buildbucket-id=${buildbucket_build_id}" + ], + "resultdb": { + "enable": true, + "has_native_resultdb_integration": true + }, + "swarming": { + "can_use_on_swarming_builders": true, + "dimension_sets": [ + { + "device_os": "PQ3A.190801.002", + "device_os_flavor": "google", + "device_os_type": "userdebug", + "device_type": "walleye", + "os": "Android" + } + ], + "output_links": [ + { + "link": [ + "https://luci-logdog.appspot.com/v/?s", + "=android%2Fswarming%2Flogcats%2F", + "${TASK_ID}%2F%2B%2Funified_logcats" + ], + "name": "shard #${SHARD_INDEX} logcats" + } + ], + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 19 + }, + "test": "chrome_public_test_apk", + "test_id_prefix": "ninja://chrome/android:chrome_public_test_apk/" + }, + { + "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", "--git-revision=${got_revision}"
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl index d56e89c..598e595 100644 --- a/testing/buildbot/test_suite_exceptions.pyl +++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1234,6 +1234,16 @@ }, }, # TODO(crbug/1239300): Remove when experiment is done. + 'chrome_public_persistent_shell_test_apk': { + 'modifications': { + 'android-nougat-x86-rel': { + 'swarming': { + 'shards': 25, + }, + }, + }, + }, + # TODO(crbug/1239300): Remove when experiment is done. 'chrome_public_persistent_shell_unit_test_apk': { 'modifications': { 'android-nougat-x86-rel': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index d1e2793..4de501c 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -461,19 +461,33 @@ # TODO(crbug/1239300): Remove when persistent_shell experiment is over. 'chrome_persistent_shell_tests': { - 'chrome_public_persistent_shell_unit_test_apk': { - 'args': [ - '--use-persistent-shell', - ], - 'ci_only': True, - 'test': 'chrome_public_unit_test_apk', - 'experiment_percentage': 100, - 'swarming': { - 'shards': 2, - }, - 'mixins': [ - 'skia_gold_test', - ], + 'chrome_public_persistent_shell_test_apk': { + 'args': [ + '--use-persistent-shell', + ], + 'ci_only': True, + 'test': 'chrome_public_test_apk', + 'experiment_percentage': 100, + 'swarming': { + 'shards': 19, + }, + 'mixins': [ + 'skia_gold_test', + ], + }, + 'chrome_public_persistent_shell_unit_test_apk': { + 'args': [ + '--use-persistent-shell', + ], + 'ci_only': True, + 'test': 'chrome_public_unit_test_apk', + 'experiment_percentage': 100, + 'swarming': { + 'shards': 2, + }, + 'mixins': [ + 'skia_gold_test', + ], } },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index f7c1742..2ff6d15 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -7305,20 +7305,15 @@ "params": { "EntitySuggestionsReduceLatencyDecoderTimeout": "405", "MaxZeroSuggestMatches": "10", - "OmniboxLocalZeroSuggestAgeThreshold": "90", "OmniboxMaxURLMatches": "7", - "PrefixSuggestIgnoreDuplicateVisits": "true", - "UIMaxAutocompleteMatches": "8", - "ZeroSuggestIgnoreDuplicateVisits": "false" + "UIMaxAutocompleteMatches": "8" }, "enable_features": [ - "LocalHistorySuggestRevamp", "OmniboxBlurWithEscape", "OmniboxClosePopupWithEscape", "OmniboxDocumentProviderAso", "OmniboxEntitySuggestionsReduceLatency", "OmniboxKeywordSearchButton", - "OmniboxLocalZeroSuggestAgeThreshold", "OmniboxMaxURLMatches", "OmniboxMaxZeroSuggestMatches", "OmniboxPreserveLongerShortcutsText",
diff --git a/third_party/blink/common/tokens/multi_token_unittest.cc b/third_party/blink/common/tokens/multi_token_unittest.cc index ec02723..294afbc 100644 --- a/third_party/blink/common/tokens/multi_token_unittest.cc +++ b/third_party/blink/common/tokens/multi_token_unittest.cc
@@ -4,8 +4,6 @@ #include "third_party/blink/public/common/tokens/multi_token.h" -#include <algorithm> - #include "base/types/token_type.h" #include "base/unguessable_token.h" #include "testing/gtest/include/gtest/gtest.h" @@ -16,59 +14,12 @@ using BarToken = base::TokenType<class BarTokenTag>; using BazToken = base::TokenType<class BazTokenTag>; -// Test MultiTokenVariantCount. -static_assert(internal::MultiTokenVariantCount<FooToken, BarToken>::kValue == 2, - "unexpected count"); -static_assert( - internal::MultiTokenVariantCount<FooToken, BarToken, BazToken>::kValue == 3, - "unexpected count"); +static_assert(internal::IsBaseTokenTypeV<FooToken>); +static_assert(!internal::IsBaseTokenTypeV<int>); -// Test MultiTokenTypeRepeated. -static_assert(!internal::MultiTokenTypeRepeated<FooToken>::kValue, - "unexpected repeated value"); -static_assert(!internal::MultiTokenTypeRepeated<FooToken, FooToken>::kValue, - "unexpected repeated value"); -static_assert( - !internal::MultiTokenTypeRepeated<FooToken, FooToken, BarToken>::kValue, - "unexpected repeated value"); -static_assert( - internal::MultiTokenTypeRepeated<FooToken, FooToken, BarToken, FooToken>:: - kValue, - "unexpected repeated value"); -static_assert( - internal::MultiTokenTypeRepeated<FooToken, BarToken, FooToken, FooToken>:: - kValue, - "unexpected repeated value"); - -// Test MultiTokenAnyTypeRepeated. -static_assert(!internal::MultiTokenAnyTypeRepeated<FooToken>::kValue, - "unexpected any repeated value"); -static_assert(!internal::MultiTokenAnyTypeRepeated<FooToken, BarToken>::kValue, - "unexpected any repeated value"); -static_assert( - !internal::MultiTokenAnyTypeRepeated<FooToken, BarToken, BazToken>::kValue, - "unexpected any repeated value"); -static_assert( - internal::MultiTokenAnyTypeRepeated<FooToken, BarToken, FooToken>::kValue, - "unexpected any repeated value"); -static_assert( - internal::MultiTokenAnyTypeRepeated<FooToken, BarToken, BarToken>::kValue, - "unexpected any repeated value"); - -// Test MultiTokenVariantIsTokenType. -static_assert(internal::MultiTokenVariantIsTokenType<FooToken>::kValue, - "unexpected is token type value"); -static_assert(!internal::MultiTokenVariantIsTokenType<int>::kValue, - "unexpected is token type value"); - -// Test MultiTokenAllVariantsAreTokenType. -static_assert( - internal::MultiTokenAllVariantsAreTokenType<FooToken, BarToken>::kValue, - "unexpected all variants are token type value"); -static_assert(!internal::MultiTokenAllVariantsAreTokenType<FooToken, - BarToken, - int>::kValue, - "unexpected all variants are token type value"); +static_assert(internal::AreAllUnique<int>); +static_assert(!internal::AreAllUnique<int, int>); +static_assert(!internal::AreAllUnique<int, char, int>); using FooBarToken = MultiToken<FooToken, BarToken>; using FooBarBazToken = MultiToken<FooToken, BarToken, BazToken>; @@ -121,4 +72,10 @@ EXPECT_EQ(token2.GetAs<BarToken>(), token3.GetAs<BarToken>()); } +TEST(MultiTokenTest, IndexOf) { + static_assert(FooBarBazToken::IndexOf<FooToken>() == 0); + static_assert(FooBarBazToken::IndexOf<BarToken>() == 1); + static_assert(FooBarBazToken::IndexOf<BazToken>() == 2); +} + } // namespace blink
diff --git a/third_party/blink/public/common/tokens/multi_token.h b/third_party/blink/public/common/tokens/multi_token.h index 857f648..d1e7c71 100644 --- a/third_party/blink/public/common/tokens/multi_token.h +++ b/third_party/blink/public/common/tokens/multi_token.h
@@ -13,143 +13,111 @@ #include <type_traits> #include "base/unguessable_token.h" +#include "third_party/abseil-cpp/absl/types/variant.h" #include "third_party/blink/public/common/tokens/multi_token_internal.h" namespace blink { -// Defines MultiToken, which is effectively a variant over 2 or more -// instances of base::TokenType. +// `MultiToken<Tokens...>` is a variant of 2 or more token types. Each token +// type must be an instantiation of `base::TokenType`, and each token type must +// be unique within `Tokens...`. Unlike `base::UnguessableToken`, a `MultiToken` +// is always valid: there is no null state. Default constructing a `MultiToken` +// will create a `MultiToken` containing an instance of the first token type in +// `Tokens`. // -// A MultiToken<..> emulates a token like interface. When default constructed -// it will construct itself as an instance of |TokenVariant0|. Additionally it -// offers the following functions allowing casting and querying token types at -// runtime: +// Usage: // -// // Determines whether this token stores an instance of a TokenType. -// bool Is<TokenType>() const; +// using CowToken = base::TokenType<class CowTokenTag>; +// using GoatToken = base::TokenType<class GoatTokenTag>; +// using UngulateToken = blink::MultiToken<CowToken, GoatToken>; // -// // Extracts the stored token in its original type. The stored token must -// // be of the provided type otherwise this will explode at runtime. -// const TokenType& GetAs<TokenType>() const; +// void TeleportCow(const CowToken&); +// void TeleportGoat(const GoatToken&); // -// A variant must have at least 2 valid input types, but can have arbitrarily -// many. They must all be distinct, and they must all be instances of -// base::TokenType. -template <typename TokenVariant0, - typename TokenVariant1, - typename... TokenVariants> -class MultiToken : public internal::MultiTokenBase<TokenVariant0, - TokenVariant1, - TokenVariants...> { +// void TeleportUngulate(const UngulateToken& token) { +// if (token.Is<CowToken>()) { +// TeleportCow(token.Get<CowToken>()); +// } else if (token.Is<GoatToken>()) { +// TeleportGoat(token.Get<GoatToken>()); +// } +// CHECK(false); // Not reachable. +// } +template <typename... Tokens> +class MultiToken { + static_assert(sizeof...(Tokens) > 1); + static_assert(std::conjunction_v<internal::IsBaseTokenType<Tokens>...>); + static_assert(internal::AreAllUnique<Tokens...>); + + template <typename T> + using EnableIfIsSupportedToken = + internal::EnableIfIsSupportedToken<T, Tokens...>; + public: - using Base = - internal::MultiTokenBase<TokenVariant0, TokenVariant1, TokenVariants...>; + using Storage = absl::variant<Tokens...>; - // The total number of types. - static const uint32_t kVariantCount = Base::VariantCount::kValue; - - // Default constructor. The resulting token will be a valid token of type - // TokenVariant0. + // A default constructed token will hold a default-constructed instance (i.e. + // randomly initialised) of the first token type in `Tokens...`. MultiToken() = default; - // Copy constructors. - MultiToken(const MultiToken& other) = default; - template <typename InputTokenType, - typename = typename std::enable_if< - Base::template ValidType<InputTokenType>::kValue>::type> + template <typename T, EnableIfIsSupportedToken<T> = 0> // NOLINTNEXTLINE(google-explicit-constructor) - MultiToken(const InputTokenType& input_token) - : value_(input_token.value()), - variant_index_(Base::template TypeIndex<InputTokenType>::kValue) {} + MultiToken(const T& token) : storage_(token) {} + MultiToken(const MultiToken&) = default; + + template <typename T, EnableIfIsSupportedToken<T> = 0> + MultiToken& operator=(const T& token) { + storage_ = token; + return *this; + } + MultiToken& operator=(const MultiToken&) = default; ~MultiToken() = default; - // Assignment operators. - MultiToken& operator=(const MultiToken& other) = default; - template <typename InputTokenType, - typename = typename std::enable_if< - Base::template ValidType<InputTokenType>::kValue>::type> - MultiToken& operator=(const InputTokenType& input_token) { - value_ = input_token.value(); - variant_index_ = Base::template TypeIndex<InputTokenType>::kValue; - return *this; - } - - const base::UnguessableToken& value() const { return value_; } - uint32_t variant_index() const { return variant_index_; } - std::string ToString() const { return value().ToString(); } - - // Type checking. - template <typename InputTokenType, - typename = typename std::enable_if< - Base::template ValidType<InputTokenType>::kValue>::type> + // Returns true iff `this` currently holds a token of type `T`. + template <typename T, EnableIfIsSupportedToken<T> = 0> bool Is() const { - return variant_index_ == Base::template TypeIndex<InputTokenType>::kValue; + return absl::holds_alternative<T>(storage_); } - // Type conversion. Allows extracting the underlying token type. This should - // only be called for the actual type that is stored in this token. This can - // be checked by calling "Is<>" first. - template <typename InputTokenType, - typename = typename std::enable_if< - Base::template ValidType<InputTokenType>::kValue>::type> - InputTokenType GetAs() const { - CHECK(Is<InputTokenType>()) << "invalid token type cast"; - // Type-punning via casting is undefined behaviour, so we return by value. - return InputTokenType(value_); + // Returns `T` if `this` currently holds a token of type `T`; otherwise, + // crashes. + template <typename T, EnableIfIsSupportedToken<T> = 0> + const T& GetAs() const { + return absl::get<T>(storage_); } - // Comparison with untyped tokens. Only compares the token value, ignoring the - // type. - int Compare(const base::UnguessableToken& other) const { - return Base::CompareImpl(value_, other); - } - bool operator<(const base::UnguessableToken& other) const { - return Compare(other) == -1; - } - bool operator==(const base::UnguessableToken& other) const { - return Compare(other) == 0; - } - bool operator!=(const base::UnguessableToken& other) const { - return Compare(other) != 0; + // Comparison operators + friend bool operator<(const MultiToken& lhs, const MultiToken& rhs) { + return lhs.storage_ < rhs.storage_; } - // Comparison with other MultiTokens. Compares by token, then type. - int Compare(const MultiToken& other) const { - return Base::CompareImpl(std::tie(value_, variant_index_), - std::tie(other.value_, other.variant_index_)); + friend bool operator==(const MultiToken& lhs, const MultiToken& rhs) { + return lhs.storage_ == rhs.storage_; } - bool operator<(const MultiToken& other) const { return Compare(other) == -1; } - bool operator==(const MultiToken& other) const { return Compare(other) == 0; } - bool operator!=(const MultiToken& other) const { return Compare(other) != 0; } - // Comparison with individual typed tokens. Compares by token, then type. - template <typename InputTokenType, - typename = typename std::enable_if< - Base::template ValidType<InputTokenType>::kValue>::type> - int Compare(const InputTokenType& other) const { - static constexpr uint32_t kInputTokenTypeIndex = - Base::template TypeIndex<InputTokenType>::kValue; - return Base::CompareImpl(std::tie(value_, variant_index_), - std::tie(other.value(), kInputTokenTypeIndex)); + friend bool operator!=(const MultiToken& lhs, const MultiToken& rhs) { + return !(lhs == rhs); } - template <typename InputTokenType, - typename = typename std::enable_if< - Base::template ValidType<InputTokenType>::kValue>::type> - bool operator<(const InputTokenType& other) const { - return Compare(other) == -1; + + template <typename T, EnableIfIsSupportedToken<T> = 0> + friend bool operator==(const MultiToken& lhs, const T& rhs) { + return absl::holds_alternative<T>(lhs.storage_) && + absl::get<T>(lhs.storage_) == rhs; } - template <typename InputTokenType, - typename = typename std::enable_if< - Base::template ValidType<InputTokenType>::kValue>::type> - bool operator==(const InputTokenType& other) const { - return Compare(other) == 0; + + template <typename T, EnableIfIsSupportedToken<T> = 0> + friend bool operator==(const T& lhs, const MultiToken& rhs) { + return rhs == lhs; } - template <typename InputTokenType, - typename = typename std::enable_if< - Base::template ValidType<InputTokenType>::kValue>::type> - bool operator!=(const InputTokenType& other) const { - return Compare(other) != 0; + + template <typename T, EnableIfIsSupportedToken<T> = 0> + friend bool operator!=(const MultiToken& lhs, const T& rhs) { + return !(lhs == rhs); + } + + template <typename T, EnableIfIsSupportedToken<T> = 0> + friend bool operator!=(const T& lhs, const MultiToken& rhs) { + return !(lhs == rhs); } // Hash functor for use in unordered containers. @@ -157,18 +125,57 @@ using argument_type = MultiToken; using result_type = size_t; result_type operator()(const MultiToken& token) const { - return base::UnguessableTokenHash()(token.value_); + return base::UnguessableTokenHash()(token.value()); } }; - private: - // The underlying untyped token value. This will *never* be null initialized. - base::UnguessableToken value_ = base::UnguessableToken::Create(); + // Prefer the above helpers where possible. These methods are primarily useful + // for serialization/deserialization. - // The index of the variant type that is currently stored in this token. - uint32_t variant_index_ = 0; + // Returns the underlying `base::UnguessableToken` of the currently held + // token. + const base::UnguessableToken& value() const; + + // 0-based index of the currently held token's type, based on its position in + // `Tokens...`. + uint32_t variant_index() const { + return static_cast<uint32_t>(storage_.index()); + } + + // Returns the 0-based index that a token of type `T` would have if it were + // currently held. + template <typename T, EnableIfIsSupportedToken<T> = 0> + static constexpr size_t IndexOf() { + return absl::variant<Tag<Tokens>...>(Tag<T>()).index(); + } + + // Equivalent to `value().ToString()`. + std::string ToString() const; + + private: + // Helper struct for IndexOf(); a `base::TokenType` is never usable as a + // literal type but a Tag<base::TokenType> is. + template <typename T> + struct Tag {}; + + Storage storage_; }; +template <typename... Tokens> +const base::UnguessableToken& MultiToken<Tokens...>::value() const { + return absl::visit( + [](const auto& token) -> const base::UnguessableToken& { + return token.value(); + }, + storage_); +} + +template <typename... Tokens> +std::string MultiToken<Tokens...>::ToString() const { + return absl::visit([](const auto& token) { return token.ToString(); }, + storage_); +} + } // namespace blink #endif // THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_MULTI_TOKEN_H_
diff --git a/third_party/blink/public/common/tokens/multi_token_internal.h b/third_party/blink/public/common/tokens/multi_token_internal.h index 65f05364..8a1a06c 100644 --- a/third_party/blink/public/common/tokens/multi_token_internal.h +++ b/third_party/blink/public/common/tokens/multi_token_internal.h
@@ -8,223 +8,33 @@ #ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_MULTI_TOKEN_INTERNAL_H_ #define THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_MULTI_TOKEN_INTERNAL_H_ -#include <algorithm> -#include <cstring> #include <type_traits> #include "base/types/token_type.h" -#include "base/unguessable_token.h" -namespace blink { +namespace blink::internal { -namespace internal { +template <typename T> +struct IsBaseTokenType : std::false_type {}; -//////////////////////////////////////////////////////////////////////////////// -// MultiTokenVariantCount -// -// Counts the number of token types. +template <typename T> +struct IsBaseTokenType<base::TokenType<T>> : std::true_type {}; -template <typename... VariantTypes> -struct MultiTokenVariantCount; +template <typename T> +inline constexpr bool IsBaseTokenTypeV = IsBaseTokenType<T>::value; -// Recursive case. -template <typename FirstVariantType, typename... OtherVariantTypes> -struct MultiTokenVariantCount<FirstVariantType, OtherVariantTypes...> { - // Deliberately use uint32_t here so as not to incur an extra 4 bytes of - // overhead on 64-bit systems, as this is the same type used by the - // |variant_index_|. - static constexpr uint32_t kValue = - 1 + MultiTokenVariantCount<OtherVariantTypes...>::kValue; -}; - -// Base case. +template <typename... Types> +bool AreAllUnique; template <> -struct MultiTokenVariantCount<> { - static constexpr uint32_t kValue = 0; -}; +inline constexpr bool AreAllUnique<> = true; +template <typename T, typename... Ts> +inline constexpr bool AreAllUnique<T, Ts...> = + (!std::is_same_v<T, Ts> && ...) && AreAllUnique<Ts...>; -//////////////////////////////////////////////////////////////////////////////// -// MultiTokenVariantIsTokenType -// -// Ensures if a QueryType is a a base::TokenType<>. +template <typename T, typename... Types> +using EnableIfIsSupportedToken = + std::enable_if_t<(std::is_same_v<T, Types> || ...), int>; -// Default case. -template <typename QueryType> -struct MultiTokenVariantIsTokenType { - static constexpr bool kValue = false; -}; - -// Specialization for base::TokenType<>. -template <typename TokenTypeTag> -struct MultiTokenVariantIsTokenType<::base::TokenType<TokenTypeTag>> { - static constexpr bool kValue = true; - - // We expect an identical layout, which allows us to reinterpret_cast between - // types. The spec does not guarantee this, but sane compilers do. Thankfully - // we can check whether or not the compiler is sane (and if the behaviour is - // safe) at compile-time. - static_assert( - sizeof(::base::TokenType<TokenTypeTag>) == - sizeof(::base::UnguessableToken), - "base::TokenType must have the same sizeof as base::UnguessableToken"); - static_assert( - alignof(::base::TokenType<TokenTypeTag>) == - alignof(::base::UnguessableToken), - "base::TokenType must have the same alignof as base::UnguessableToken"); -}; - -//////////////////////////////////////////////////////////////////////////////// -// MultiTokenAllVariantsAreTokenType -// -// Ensures that all variants are of type base::TokenType. - -template <typename... VariantTypes> -struct MultiTokenAllVariantsAreTokenType; - -// Recursive case. -template <typename FirstVariantType, typename... OtherVariantTypes> -struct MultiTokenAllVariantsAreTokenType<FirstVariantType, - OtherVariantTypes...> { - static constexpr bool kValue = - MultiTokenVariantIsTokenType<FirstVariantType>::kValue && - MultiTokenAllVariantsAreTokenType<OtherVariantTypes...>::kValue; -}; - -// Base case. -template <> -struct MultiTokenAllVariantsAreTokenType<> { - static constexpr bool kValue = true; -}; - -//////////////////////////////////////////////////////////////////////////////// -// MultiTokenTypeRepeated -// -// Determines if a QueryType is repeated in a variadic list of types. - -template <typename QueryType, typename... VariantTypes> -struct MultiTokenTypeRepeated; - -// Recursive case. -template <typename QueryType, - typename FirstVariantType, - typename... OtherVariantTypes> -struct MultiTokenTypeRepeated<QueryType, - FirstVariantType, - OtherVariantTypes...> { - static constexpr size_t kCount = - (std::is_same<QueryType, FirstVariantType>::value ? 1 : 0) + - MultiTokenTypeRepeated<QueryType, OtherVariantTypes...>::kCount; - static constexpr bool kValue = kCount > 1; -}; - -// Base case. -template <typename QueryType> -struct MultiTokenTypeRepeated<QueryType> { - static constexpr size_t kCount = 0; - static constexpr bool kValue = false; -}; - -//////////////////////////////////////////////////////////////////////////////// -// MultiTokenAnyTypeRepeated -// -// Determines if any type is repeated in a variadic list of types. - -template <typename... VariantTypes> -struct MultiTokenAnyTypeRepeated; - -// Recursive case. -template <typename FirstVariantType, typename... OtherVariantTypes> -struct MultiTokenAnyTypeRepeated<FirstVariantType, OtherVariantTypes...> { - static constexpr bool kValue = - MultiTokenTypeRepeated<FirstVariantType, - FirstVariantType, - OtherVariantTypes...>::kValue || - MultiTokenAnyTypeRepeated<OtherVariantTypes...>::kValue; -}; - -// Base case. -template <> -struct MultiTokenAnyTypeRepeated<> { - static constexpr bool kValue = false; -}; - -//////////////////////////////////////////////////////////////////////////////// -// MultiTokenTypeIndex -// -// Returns the index of a QueryType from a variadic list of N types, or N if the -// QueryType is not found in the list. - -template <typename QueryType, typename... VariantTypes> -struct MultiTokenTypeIndex; - -// Recursive case. -template <typename QueryType, - typename FirstVariantType, - typename... OtherVariantTypes> -struct MultiTokenTypeIndex<QueryType, FirstVariantType, OtherVariantTypes...> { - static constexpr size_t kValue = - (std::is_same<QueryType, FirstVariantType>::value - ? 0 - : (1 + - MultiTokenTypeIndex<QueryType, OtherVariantTypes...>::kValue)); -}; - -// Base case. -template <typename QueryType> -struct MultiTokenTypeIndex<QueryType> { - static constexpr size_t kValue = 0; -}; - -//////////////////////////////////////////////////////////////////////////////// -// MultiTokenBase -// -// Base class that brings helper structs into a single namespace for -// convenience. -template <typename... TokenVariants> -class MultiTokenBase { - public: - // Ensures that no types are repeated, as that's non-sensical. - using AnyRepeated = internal::MultiTokenAnyTypeRepeated<TokenVariants...>; - static_assert(!AnyRepeated::kValue, "input types must not be repeated"); - - // Ensures that all variants are instances of base::TokenType. - using AllVariantsAreTokenType = - internal::MultiTokenAllVariantsAreTokenType<TokenVariants...>; - static_assert(AllVariantsAreTokenType::kValue, - "input types must be instances of base::TokenType"); - - // Counts the number of variants. - using VariantCount = internal::MultiTokenVariantCount<TokenVariants...>; - - // For determining the index of a type. Used to assign an integer ID to a - // type, as a kind of untyped enum. - template <typename QueryType> - struct TypeIndex - : public internal::MultiTokenTypeIndex<QueryType, TokenVariants...> {}; - - // For determining if a type is valid for this variant. Useful in enable_if - // statements. - template <typename QueryType> - struct ValidType { - static constexpr bool kValue = - TypeIndex<QueryType>::kValue != VariantCount::kValue; - }; - - // Helper comparator. Compares underlying types using only < and == to - // return -1, 0, or 1 depending on their relative values. - template <typename InputType> - static int CompareImpl(const InputType& lhs, const InputType& rhs) { - if (lhs < rhs) - return -1; - if (lhs == rhs) - return 0; - DCHECK(rhs < lhs); - return 1; - } -}; - -} // namespace internal - -} // namespace blink +} // namespace blink::internal #endif // THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_MULTI_TOKEN_INTERNAL_H_
diff --git a/third_party/blink/public/platform/web_navigation_body_loader.h b/third_party/blink/public/platform/web_navigation_body_loader.h index 5db2104..68628252 100644 --- a/third_party/blink/public/platform/web_navigation_body_loader.h +++ b/third_party/blink/public/platform/web_navigation_body_loader.h
@@ -14,6 +14,7 @@ #include "third_party/blink/public/platform/web_common.h" #include "third_party/blink/public/platform/web_loader_freeze_mode.h" #include "third_party/blink/public/platform/web_url_error.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" namespace blink { @@ -54,6 +55,16 @@ int64_t total_decoded_body_length, bool should_report_corb_blocking, const absl::optional<WebURLError>& error) = 0; + + // The client can return a ProcessBackgroundDataCallback which will be + // called on a background thread with the decoded data. The returned + // callback will be called on a background thread with the same decoded data + // which will be given to DecodedBodyDataReceived(). + using ProcessBackgroundDataCallback = + WTF::CrossThreadRepeatingFunction<void(const WebString&)>; + virtual ProcessBackgroundDataCallback TakeProcessBackgroundDataCallback() { + return ProcessBackgroundDataCallback(); + } }; // This method fills navigation params related to the navigation request,
diff --git a/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc b/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc index 633f946..26fd2ec 100644 --- a/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc +++ b/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc
@@ -45,7 +45,9 @@ options.reference_element_.value_or( document->ScrollingElementNoLayout()), options.direction_), - name_(options.name_) {} + name_(options.name_) { + SnapshotState(); +} bool CSSScrollTimeline::Matches(const Options& options) const { return (GetReferenceType() == options.reference_type_) &&
diff --git a/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc b/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc index 32bc954..6e03f60 100644 --- a/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc +++ b/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc
@@ -214,4 +214,27 @@ GetDocumentAnimations().GetUnvalidatedTimelinesForTesting().size()); } +TEST_F(CSSScrollTimelineTest, DocumentScrollerInQuirksMode) { + GetDocument().SetCompatibilityMode(Document::kQuirksMode); + + SetBodyInnerHTML(R"HTML( + <style> + @keyframes anim { + from { z-index: 100; } + to { z-index: 100; } + } + #element { + animation: anim 10s forwards scroll(root); + } + </style> + <div id=element></div> + )HTML"); + + Element* element = GetDocument().getElementById("element"); + ASSERT_TRUE(element); + + EXPECT_EQ(100, element->GetComputedStyle()->ZIndex()); + // Don't crash. +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css/css_view_timeline.cc b/third_party/blink/renderer/core/animation/css/css_view_timeline.cc index 8eb78d67..a4ff48b 100644 --- a/third_party/blink/renderer/core/animation/css/css_view_timeline.cc +++ b/third_party/blink/renderer/core/animation/css/css_view_timeline.cc
@@ -20,7 +20,9 @@ : ViewTimeline(document, options.subject_, options.direction_, - options.inset_) {} + options.inset_) { + SnapshotState(); +} bool CSSViewTimeline::Matches(const Options& options) const { return (subject() == options.subject_) &&
diff --git a/third_party/blink/renderer/core/dom/document_encoding_data.h b/third_party/blink/renderer/core/dom/document_encoding_data.h index cebf5a9d..f7608637 100644 --- a/third_party/blink/renderer/core/dom/document_encoding_data.h +++ b/third_party/blink/renderer/core/dom/document_encoding_data.h
@@ -31,6 +31,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_DOCUMENT_ENCODING_DATA_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_DOCUMENT_ENCODING_DATA_H_ +#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/text/text_encoding.h" @@ -38,7 +39,7 @@ class TextResourceDecoder; struct WebEncodingData; -class DocumentEncodingData { +class CORE_EXPORT DocumentEncodingData { DISALLOW_NEW(); public:
diff --git a/third_party/blink/renderer/core/dom/document_parser.h b/third_party/blink/renderer/core/dom/document_parser.h index 3745b27..6aec948 100644 --- a/third_party/blink/renderer/core/dom/document_parser.h +++ b/third_party/blink/renderer/core/dom/document_parser.h
@@ -25,12 +25,14 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_DOCUMENT_PARSER_H_ #include <memory> +#include "base/callback.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/dom/document_encoding_data.h" #include "third_party/blink/renderer/platform/bindings/name_client.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/wtf/forward.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" namespace blink { @@ -63,6 +65,11 @@ virtual void SetHasAppendedData() {} virtual void AppendDecodedData(const String& data, const DocumentEncodingData& encoding_data) {} + using BackgroundScanCallback = + WTF::CrossThreadRepeatingFunction<void(const String&)>; + virtual BackgroundScanCallback TakeBackgroundScanCallback() { + return BackgroundScanCallback(); + } // FIXME: append() should be private, but DocumentLoader and DOMPatchSupport // uses it for now.
diff --git a/third_party/blink/renderer/core/dom/scriptable_document_parser.cc b/third_party/blink/renderer/core/dom/scriptable_document_parser.cc index 850bc1b..8c8ac80 100644 --- a/third_party/blink/renderer/core/dom/scriptable_document_parser.cc +++ b/third_party/blink/renderer/core/dom/scriptable_document_parser.cc
@@ -67,6 +67,12 @@ return nullptr; } +bool ScriptableDocumentParser::HasInlineScriptStreamerForTesting( + const String& source) { + base::AutoLock lock(streamers_lock_); + return inline_script_streamers_.Contains(source); +} + void ScriptableDocumentParser::AddCSSTokenizer( const String& source, std::unique_ptr<CachedCSSTokenizer> tokenizer) {
diff --git a/third_party/blink/renderer/core/dom/scriptable_document_parser.h b/third_party/blink/renderer/core/dom/scriptable_document_parser.h index 5353377..0f90ee5c 100644 --- a/third_party/blink/renderer/core/dom/scriptable_document_parser.h +++ b/third_party/blink/renderer/core/dom/scriptable_document_parser.h
@@ -78,6 +78,7 @@ // The returned streamer is guaranteed to be correct for script text that // matches the passed in |source|. InlineScriptStreamer* TakeInlineScriptStreamer(const String& source); + bool HasInlineScriptStreamerForTesting(const String& source); // Adds a tokenizer for |source| which can be later retrieved with // TakeCSSTokenizer(). This may be called on any thread.
diff --git a/third_party/blink/renderer/core/dom/tree_ordered_map.cc b/third_party/blink/renderer/core/dom/tree_ordered_map.cc index 09ff639..70a6a1f 100644 --- a/third_party/blink/renderer/core/dom/tree_ordered_map.cc +++ b/third_party/blink/renderer/core/dom/tree_ordered_map.cc
@@ -62,7 +62,8 @@ inline bool KeyMatchesMapName(const AtomicString& key, const Element& element) { auto* html_map_element = DynamicTo<HTMLMapElement>(element); - return html_map_element && html_map_element->GetName() == key; + return html_map_element && (html_map_element->GetName() == key || + html_map_element->GetIdAttribute() == key); } inline bool KeyMatchesSlotName(const AtomicString& key,
diff --git a/third_party/blink/renderer/core/dom/tree_scope.cc b/third_party/blink/renderer/core/dom/tree_scope.cc index 216ff6cae..d367536 100644 --- a/third_party/blink/renderer/core/dom/tree_scope.cc +++ b/third_party/blink/renderer/core/dom/tree_scope.cc
@@ -178,20 +178,24 @@ void TreeScope::AddImageMap(HTMLMapElement& image_map) { const AtomicString& name = image_map.GetName(); - if (!name) + const AtomicString& id = image_map.GetIdAttribute(); + if (!name && !id) return; if (!image_maps_by_name_) image_maps_by_name_ = MakeGarbageCollected<TreeOrderedMap>(); - image_maps_by_name_->Add(name, image_map); + if (name) + image_maps_by_name_->Add(name, image_map); + if (id) + image_maps_by_name_->Add(id, image_map); } void TreeScope::RemoveImageMap(HTMLMapElement& image_map) { if (!image_maps_by_name_) return; - const AtomicString& name = image_map.GetName(); - if (!name) - return; - image_maps_by_name_->Remove(name, image_map); + if (const AtomicString& name = image_map.GetName()) + image_maps_by_name_->Remove(name, image_map); + if (const AtomicString& id = image_map.GetIdAttribute()) + image_maps_by_name_->Remove(id, image_map); } HTMLMapElement* TreeScope::GetImageMap(const String& url) const { @@ -203,6 +207,8 @@ if (hash_pos == kNotFound) return nullptr; String name = url.Substring(hash_pos + 1); + if (name.empty()) + return nullptr; return To<HTMLMapElement>( image_maps_by_name_->GetElementByMapName(AtomicString(name), *this)); }
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc index c9d4e71..b3e530f 100644 --- a/third_party/blink/renderer/core/frame/local_frame_view.cc +++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -336,6 +336,7 @@ visitor->Trace(lifecycle_observers_); visitor->Trace(fullscreen_video_elements_); visitor->Trace(pending_transform_updates_); + visitor->Trace(pending_opacity_updates_); } void LocalFrameView::ForAllChildViewsAndPlugins( @@ -5012,17 +5013,52 @@ return true; } -void LocalFrameView::UpdateAllPendingTransforms() { +bool LocalFrameView::UpdateAllPendingTransforms() { DCHECK(GetFrame().IsLocalRoot() || !IsAttached()); - ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) { + bool updated = false; + ForAllNonThrottledLocalFrameViews([&updated](LocalFrameView& frame_view) { if (frame_view.pending_transform_updates_) { for (const LayoutObject* object : *frame_view.pending_transform_updates_) { PaintPropertyTreeBuilder::DirectlyUpdateTransformMatrix(*object); + updated = true; } frame_view.pending_transform_updates_->clear(); } }); + return updated; } +void LocalFrameView::AddPendingOpacityUpdate(LayoutObject& object) { + if (!pending_opacity_updates_) { + pending_opacity_updates_ = + MakeGarbageCollected<HeapHashSet<Member<LayoutObject>>>(); + } + pending_opacity_updates_->insert(&object); +} + +bool LocalFrameView::RemovePendingOpacityUpdate(const LayoutObject& object) { + if (!pending_opacity_updates_) + return false; + auto it = pending_opacity_updates_->find(const_cast<LayoutObject*>(&object)); + if (it == pending_opacity_updates_->end()) + return false; + pending_opacity_updates_->erase(it); + return true; +} + +bool LocalFrameView::UpdateAllPendingOpacityUpdates() { + DCHECK(GetFrame().IsLocalRoot() || !IsAttached()); + bool updated = false; + ForAllNonThrottledLocalFrameViews([&updated](LocalFrameView& frame_view) { + if (frame_view.pending_opacity_updates_) { + for (const LayoutObject* object : *frame_view.pending_opacity_updates_) { + PaintPropertyTreeBuilder::DirectlyUpdateOpacityValue(*object); + updated = true; + } + frame_view.pending_opacity_updates_->clear(); + } + }); + return updated; +} } // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h index bb71479a..4846c14f58 100644 --- a/third_party/blink/renderer/core/frame/local_frame_view.h +++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -769,7 +769,11 @@ void AddPendingTransformUpdate(LayoutObject& object); bool RemovePendingTransformUpdate(const LayoutObject& object); - void UpdateAllPendingTransforms(); + bool UpdateAllPendingTransforms(); + + void AddPendingOpacityUpdate(LayoutObject& object); + bool RemovePendingOpacityUpdate(const LayoutObject& object); + bool UpdateAllPendingOpacityUpdates(); protected: void FrameRectsChanged(const gfx::Rect&) override; @@ -1176,7 +1180,9 @@ // possible, avoids needing to walk the tree to update them. See: // https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/core/paint/README.md#Transform-update-optimization // for more on the fast path + // TODO(yotha): unify these into one HeapHashMap. Member<HeapHashSet<Member<LayoutObject>>> pending_transform_updates_; + Member<HeapHashSet<Member<LayoutObject>>> pending_opacity_updates_; // TODO(1370937): Currently we don't yet know how to handle soft navigation // UKM reporting. This flag indicates that First Contentful Paint was reported
diff --git a/third_party/blink/renderer/core/html/html_map_element.cc b/third_party/blink/renderer/core/html/html_map_element.cc index 6f3d1200..ccfe1d9 100644 --- a/third_party/blink/renderer/core/html/html_map_element.cc +++ b/third_party/blink/renderer/core/html/html_map_element.cc
@@ -67,7 +67,8 @@ image_element.FastGetAttribute(html_names::kUsemapAttr) .GetString() .Substring(1); - if (use_map_name == name_) + if (!use_map_name.empty() && + (use_map_name == name_ || use_map_name == GetIdAttribute())) return &image_element; } @@ -75,24 +76,23 @@ } void HTMLMapElement::ParseAttribute(const AttributeModificationParams& params) { - // FIXME: This logic seems wrong for XML documents. - // Either the id or name will be used depending on the order the attributes - // are parsed. - + // To return the first image that matches usemap on name or id attributes, we + // need to track their values in the TreeScope. + // https://html.spec.whatwg.org/multipage/#image-map-processing-model if (params.name == html_names::kIdAttr || params.name == html_names::kNameAttr) { if (params.name == html_names::kIdAttr) { // Call base class so that hasID bit gets set. HTMLElement::ParseAttribute(params); - if (IsA<HTMLDocument>(GetDocument())) - return; } if (isConnected()) GetTreeScope().RemoveImageMap(*this); String map_name = params.new_value; if (map_name[0] == '#') map_name = map_name.Substring(1); - name_ = AtomicString(map_name); + // name_ is the parsed name attribute value that is not empty. + if (!map_name.empty() && params.name == html_names::kNameAttr) + name_ = AtomicString(map_name); if (isConnected()) GetTreeScope().AddImageMap(*this);
diff --git a/third_party/blink/renderer/core/html/parser/background_html_scanner_test.cc b/third_party/blink/renderer/core/html/parser/background_html_scanner_test.cc index 183873c..56c25fe 100644 --- a/third_party/blink/renderer/core/html/parser/background_html_scanner_test.cc +++ b/third_party/blink/renderer/core/html/parser/background_html_scanner_test.cc
@@ -114,10 +114,10 @@ .task_runner = task_runner_, .min_size = 0u, .enabled = true}, /*pretokenize_css_params=*/ OptimizationParams{ - .task_runner = task_runner_, .min_size = 0u, .enabled = true})); - preload_scanner.ScanInBackground( - "<script>foo</script>", GetDocument().ValidBaseElementURL(), + .task_runner = task_runner_, .min_size = 0u, .enabled = true}), CrossThreadBindRepeating([](std::unique_ptr<PendingPreloadData>) {})); + preload_scanner.ScanInBackground("<script>foo</script>", + GetDocument().ValidBaseElementURL()); FlushTaskRunner(); EXPECT_NE(parser->TakeInlineScriptStreamer("foo"), nullptr); }
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.cc b/third_party/blink/renderer/core/html/parser/html_document_parser.cc index 9afebbe..4a4265b 100644 --- a/third_party/blink/renderer/core/html/parser/html_document_parser.cc +++ b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
@@ -90,11 +90,19 @@ class ShouldCompleteScope; class AttemptToEndForbiddenScope; -bool ThreadedPreloadScannerEnabled() { +enum class FeatureResetMode { + kUseCached, + kResetForTesting, +}; + +bool ThreadedPreloadScannerEnabled( + FeatureResetMode reset_mode = FeatureResetMode::kUseCached) { // Cache the feature value since checking for each parser regresses some micro // benchmarks. - static const bool kEnabled = + static bool kEnabled = base::FeatureList::IsEnabled(features::kThreadedPreloadScanner); + if (reset_mode == FeatureResetMode::kResetForTesting) + kEnabled = base::FeatureList::IsEnabled(features::kThreadedPreloadScanner); return kEnabled; } @@ -106,11 +114,14 @@ return kEnabled; } -bool PrecompileInlineScriptsEnabled() { +bool PrecompileInlineScriptsEnabled( + FeatureResetMode reset_mode = FeatureResetMode::kUseCached) { // Cache the feature value since checking for each parser regresses some micro // benchmarks. - static const bool kEnabled = + static bool kEnabled = base::FeatureList::IsEnabled(features::kPrecompileInlineScripts); + if (reset_mode == FeatureResetMode::kResetForTesting) + kEnabled = base::FeatureList::IsEnabled(features::kPrecompileInlineScripts); return kEnabled; } @@ -627,7 +638,7 @@ preload_scanner_.reset(); insertion_preload_scanner_.reset(); background_script_scanner_.Reset(); - background_scanner_.Reset(); + background_scanner_.reset(); // Oilpan: HTMLTokenProducer may allocate a fair amount of memory. Destroy // it to ensure that memory is released. token_producer_.reset(); @@ -1364,6 +1375,20 @@ AttemptToRunDeferredScriptsAndEnd(); } +// static +void HTMLDocumentParser::ResetCachedFeaturesForTesting() { + ThreadedPreloadScannerEnabled(FeatureResetMode::kResetForTesting); + PrecompileInlineScriptsEnabled(FeatureResetMode::kResetForTesting); +} + +// static +void HTMLDocumentParser::FlushPreloadScannerThreadForTesting() { + base::RunLoop run_loop; + GetPreloadScannerThread()->GetTaskRunner()->PostTask(FROM_HERE, + run_loop.QuitClosure()); + run_loop.Run(); +} + void HTMLDocumentParser::ExecuteScriptsWaitingForResources() { TRACE_EVENT0("blink", "HTMLDocumentParser::ExecuteScriptsWaitingForResources"); @@ -1567,6 +1592,13 @@ have_seen_first_byte ? ".NonInitial" : ".Initial"}); } +DocumentParser::BackgroundScanCallback +HTMLDocumentParser::TakeBackgroundScanCallback() { + if (!background_scan_fn_) + return BackgroundScanCallback(); + return CrossThreadBindRepeating(std::move(background_scan_fn_), KURL()); +} + void HTMLDocumentParser::ScanInBackground(const String& source) { if (task_runner_state_->IsSynchronous() || !GetDocument()->Url().IsValid()) return; @@ -1580,17 +1612,29 @@ // is already available. DCHECK(!preload_scanner_); if (!background_scanner_) { + // See comment on NavigationBodyLoader::StartLoadingBodyInBackground() for + // details on how the preload scanner flow works when the body data is + // being loaded in the background. background_scanner_ = HTMLPreloadScanner::CreateBackground( - this, options_, GetPreloadScannerThread()->GetTaskRunner()); + this, options_, GetPreloadScannerThread()->GetTaskRunner(), + CrossThreadBindRepeating( + &HTMLDocumentParser::AddPreloadDataOnBackgroundThread, + WrapCrossThreadWeakPersistent(this), + GetDocument()->GetTaskRunner(TaskType::kInternalLoading))); + + background_scan_fn_ = CrossThreadBindRepeating( + [](base::WeakPtr<HTMLPreloadScanner> scanner, const KURL& url, + const String& data) { + PostCrossThreadTask( + *GetPreloadScannerThread()->GetTaskRunner(), FROM_HERE, + CrossThreadBindOnce(&HTMLPreloadScanner::ScanInBackground, + std::move(scanner), data, url)); + }, + background_scanner_->AsWeakPtr()); } - background_scanner_.AsyncCall(&HTMLPreloadScanner::ScanInBackground) - .WithArgs( - source, GetDocument()->ValidBaseElementURL(), - CrossThreadBindRepeating( - &HTMLDocumentParser::AddPreloadDataOnBackgroundThread, - WrapCrossThreadPersistent(this), - GetDocument()->GetTaskRunner(TaskType::kInternalLoading))); + if (background_scan_fn_) + background_scan_fn_.Run(GetDocument()->ValidBaseElementURL(), source); return; }
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.h b/third_party/blink/renderer/core/html/parser/html_document_parser.h index 3eb88c5e..f00f7ee 100644 --- a/third_party/blink/renderer/core/html/parser/html_document_parser.h +++ b/third_party/blink/renderer/core/html/parser/html_document_parser.h
@@ -126,6 +126,9 @@ void SetDecoder(std::unique_ptr<TextResourceDecoder>) final; void NotifyNoRemainingAsyncScripts() final; + static void ResetCachedFeaturesForTesting(); + static void FlushPreloadScannerThreadForTesting(); + protected: HTMLDocumentParser(HTMLDocument&, ParserSynchronizationPolicy, @@ -167,6 +170,7 @@ void DocumentElementAvailable() override; void CommitPreloadedData() override; void FlushPendingPreloads() override; + BackgroundScanCallback TakeBackgroundScanCallback() override; // HTMLParserScriptRunnerHost void NotifyScriptLoaded() final; @@ -250,7 +254,10 @@ // A scanner used only for input provided to the insert() method. std::unique_ptr<HTMLPreloadScanner> insertion_preload_scanner_; WTF::SequenceBound<BackgroundHTMLScanner> background_script_scanner_; - WTF::SequenceBound<HTMLPreloadScanner> background_scanner_; + HTMLPreloadScanner::BackgroundPtr background_scanner_; + using BackgroundScanFn = + WTF::CrossThreadRepeatingFunction<void(const KURL&, const String&)>; + BackgroundScanFn background_scan_fn_; scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner_;
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser_test.cc b/third_party/blink/renderer/core/html/parser/html_document_parser_test.cc index e626e1a..f1f6f11 100644 --- a/third_party/blink/renderer/core/html/parser/html_document_parser_test.cc +++ b/third_party/blink/renderer/core/html/parser/html_document_parser_test.cc
@@ -189,6 +189,62 @@ static_cast<DocumentParser*>(parser)->StopParsing(); } +class HTMLDocumentParserThreadedPreloadScannerTest : public PageTestBase { + protected: + HTMLDocumentParserThreadedPreloadScannerTest() { + scoped_feature_list_.InitWithFeatures( + {features::kThreadedPreloadScanner, features::kPrecompileInlineScripts}, + {}); + HTMLDocumentParser::ResetCachedFeaturesForTesting(); + } + + ~HTMLDocumentParserThreadedPreloadScannerTest() override { + scoped_feature_list_.Reset(); + HTMLDocumentParser::ResetCachedFeaturesForTesting(); + } + + void SetUp() override { + PageTestBase::SetUp(); + GetDocument().SetURL(KURL("https://example.test")); + } + + HTMLDocumentParser* CreateParser(HTMLDocument& document) { + return MakeGarbageCollected<HTMLDocumentParser>(document, + kAllowDeferredParsing); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +TEST_F(HTMLDocumentParserThreadedPreloadScannerTest, + TakeBackgroundScanCallback) { + auto& document = To<HTMLDocument>(GetDocument()); + HTMLDocumentParser* parser = CreateParser(document); + ScopedParserDetacher detacher(parser); + + // First append "foo" script which should be passed through to the scanner. + parser->AppendDecodedData("<script>foo</script>", DocumentEncodingData()); + HTMLDocumentParser::FlushPreloadScannerThreadForTesting(); + EXPECT_TRUE(parser->HasInlineScriptStreamerForTesting("foo")); + + // Now take the callback. + auto callback = + static_cast<DocumentParser*>(parser)->TakeBackgroundScanCallback(); + + // Append "bar" script which should not be passed to the scanner. + parser->AppendDecodedData("<script>bar</script>", DocumentEncodingData()); + HTMLDocumentParser::FlushPreloadScannerThreadForTesting(); + EXPECT_FALSE(parser->HasInlineScriptStreamerForTesting("bar")); + + // Append "baz" script to the callback which should be passed to the scanner. + callback.Run("<script>baz</script>"); + HTMLDocumentParser::FlushPreloadScannerThreadForTesting(); + EXPECT_TRUE(parser->HasInlineScriptStreamerForTesting("baz")); + + static_cast<DocumentParser*>(parser)->StopParsing(); +} + class HTMLDocumentParserProcessImmediatelyTest : public PageTestBase { protected: void SetUp() override {
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc index dcc7a2f..04722bd 100644 --- a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc +++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
@@ -1069,18 +1069,22 @@ } // static -WTF::SequenceBound<HTMLPreloadScanner> HTMLPreloadScanner::CreateBackground( +HTMLPreloadScanner::BackgroundPtr HTMLPreloadScanner::CreateBackground( HTMLDocumentParser* parser, HTMLParserOptions options, - scoped_refptr<base::SequencedTaskRunner> task_runner) { + scoped_refptr<base::SequencedTaskRunner> task_runner, + TakePreloadFn take_preload) { auto* document = parser->GetDocument(); - return WTF::SequenceBound<HTMLPreloadScanner>( - std::move(task_runner), std::make_unique<HTMLTokenizer>(options), - options.priority_hints_origin_trial_enabled, document->Url(), - std::make_unique<CachedDocumentParameters>(document), - MediaValuesCached::MediaValuesCachedData(*document), - TokenPreloadScanner::ScannerType::kMainDocument, - BackgroundHTMLScanner::ScriptTokenScanner::Create(parser)); + return BackgroundPtr( + new HTMLPreloadScanner( + std::make_unique<HTMLTokenizer>(options), + options.priority_hints_origin_trial_enabled, document->Url(), + std::make_unique<CachedDocumentParameters>(document), + MediaValuesCached::MediaValuesCachedData(*document), + TokenPreloadScanner::ScannerType::kMainDocument, + BackgroundHTMLScanner::ScriptTokenScanner::Create(parser), + std::move(take_preload)), + Deleter{task_runner}); } HTMLPreloadScanner::HTMLPreloadScanner( @@ -1091,14 +1095,16 @@ const MediaValuesCached::MediaValuesCachedData& media_values_cached_data, const TokenPreloadScanner::ScannerType scanner_type, std::unique_ptr<BackgroundHTMLScanner::ScriptTokenScanner> - script_token_scanner) + script_token_scanner, + TakePreloadFn take_preload) : scanner_(document_url, std::move(document_parameters), media_values_cached_data, scanner_type, priority_hints_origin_trial_enabled), tokenizer_(std::move(tokenizer)), - script_token_scanner_(std::move(script_token_scanner)) {} + script_token_scanner_(std::move(script_token_scanner)), + take_preload_(std::move(take_preload)) {} HTMLPreloadScanner::~HTMLPreloadScanner() = default; @@ -1107,8 +1113,7 @@ } std::unique_ptr<PendingPreloadData> HTMLPreloadScanner::Scan( - const KURL& starting_base_element_url, - const TakePreloadFn& take_preload) { + const KURL& starting_base_element_url) { auto pending_data = std::make_unique<PendingPreloadData>(); TRACE_EVENT1("blink", "HTMLPreloadScanner::scan", "source_length", @@ -1147,19 +1152,19 @@ return pending_data; } // Incrementally add preloads when scanning in the background. - if (take_preload && !pending_data->requests.empty()) { - take_preload.Run(std::move(pending_data)); + if (take_preload_ && !pending_data->requests.empty()) { + take_preload_.Run(std::move(pending_data)); pending_data = std::make_unique<PendingPreloadData>(); } } return pending_data; } -void HTMLPreloadScanner::ScanInBackground(const String& source, - const KURL& document_base_element_url, - const TakePreloadFn& take_preload) { +void HTMLPreloadScanner::ScanInBackground( + const String& source, + const KURL& document_base_element_url) { source_.Append(source); - take_preload.Run(Scan(document_base_element_url, take_preload)); + take_preload_.Run(Scan(document_base_element_url)); } CachedDocumentParameters::CachedDocumentParameters(Document* document) {
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner.h b/third_party/blink/renderer/core/html/parser/html_preload_scanner.h index 35a3d54..816d888a 100644 --- a/third_party/blink/renderer/core/html/parser/html_preload_scanner.h +++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner.h
@@ -31,6 +31,7 @@ #include <utility> #include "base/memory/ptr_util.h" +#include "base/memory/weak_ptr.h" #include "services/network/public/cpp/client_hints.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/renderer/core/core_export.h" @@ -157,7 +158,7 @@ PictureData picture_data_; size_t template_count_; std::unique_ptr<CachedDocumentParameters> document_parameters_; - Persistent<MediaValuesCached> media_values_; + CrossThreadPersistent<MediaValuesCached> media_values_; ScannerType scanner_type_; // TODO(domfarolino): Remove this once Priority Hints is no longer in Origin // Trial (see https://crbug.com/821464). This member exists because @@ -167,7 +168,8 @@ bool priority_hints_origin_trial_enabled_; }; -class CORE_EXPORT HTMLPreloadScanner { +class CORE_EXPORT HTMLPreloadScanner + : public base::SupportsWeakPtr<HTMLPreloadScanner> { USING_FAST_MALLOC(HTMLPreloadScanner); public: @@ -177,11 +179,23 @@ HTMLParserOptions options, TokenPreloadScanner::ScannerType scanner_type); + using TakePreloadFn = WTF::CrossThreadRepeatingFunction<void( + std::unique_ptr<PendingPreloadData>)>; + // Creates a HTMLPreloadScanner which will be bound to |task_runner|. - static WTF::SequenceBound<HTMLPreloadScanner> CreateBackground( + struct Deleter { + void operator()(const HTMLPreloadScanner* ptr) { + if (ptr) + task_runner_->DeleteSoon(FROM_HERE, ptr); + } + scoped_refptr<base::SequencedTaskRunner> task_runner_; + }; + using BackgroundPtr = std::unique_ptr<HTMLPreloadScanner, Deleter>; + static BackgroundPtr CreateBackground( HTMLDocumentParser* parser, HTMLParserOptions options, - scoped_refptr<base::SequencedTaskRunner> task_runner); + scoped_refptr<base::SequencedTaskRunner> task_runner, + TakePreloadFn take_preload); HTMLPreloadScanner(std::unique_ptr<HTMLTokenizer>, bool priority_hints_origin_trial_enabled, @@ -190,23 +204,20 @@ const MediaValuesCached::MediaValuesCachedData&, const TokenPreloadScanner::ScannerType, std::unique_ptr<BackgroundHTMLScanner::ScriptTokenScanner> - script_token_scanner); + script_token_scanner, + TakePreloadFn take_preload = TakePreloadFn()); HTMLPreloadScanner(const HTMLPreloadScanner&) = delete; HTMLPreloadScanner& operator=(const HTMLPreloadScanner&) = delete; ~HTMLPreloadScanner(); void AppendToEnd(const SegmentedString&); - using TakePreloadFn = WTF::CrossThreadRepeatingFunction<void( - std::unique_ptr<PendingPreloadData>)>; std::unique_ptr<PendingPreloadData> Scan( - const KURL& document_base_element_url, - const TakePreloadFn& take_preload = TakePreloadFn()); + const KURL& document_base_element_url); // Scans |source| and calls |take_preload| with the generated preload data. void ScanInBackground(const String& source, - const KURL& document_base_element_url, - const TakePreloadFn& take_preload); + const KURL& document_base_element_url); private: TokenPreloadScanner scanner_; @@ -215,6 +226,7 @@ std::unique_ptr<HTMLTokenizer> tokenizer_; std::unique_ptr<BackgroundHTMLScanner::ScriptTokenScanner> script_token_scanner_; + TakePreloadFn take_preload_; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc index 420886e..c76e4f18 100644 --- a/third_party/blink/renderer/core/layout/layout_object.cc +++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -3680,6 +3680,7 @@ if (GetFrameView()) { GetFrameView()->RemovePendingTransformUpdate(*this); + GetFrameView()->RemovePendingOpacityUpdate(*this); SetIsBackgroundAttachmentFixedObject(false); } }
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc index 11a93e18..f93fcdb 100644 --- a/third_party/blink/renderer/core/loader/document_loader.cc +++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -147,6 +147,7 @@ #include "third_party/blink/renderer/platform/weborigin/kurl.h" #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h" #include "third_party/blink/renderer/platform/weborigin/security_policy.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" #include "third_party/blink/renderer/platform/wtf/hash_map.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" #include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h" @@ -1029,6 +1030,17 @@ BodyDataReceivedImpl(body_data); } +DocumentLoader::ProcessBackgroundDataCallback +DocumentLoader::TakeProcessBackgroundDataCallback() { + auto callback = parser_->TakeBackgroundScanCallback(); + if (!callback) + return ProcessBackgroundDataCallback(); + return CrossThreadBindRepeating( + [](const DocumentParser::BackgroundScanCallback& callback, + const WebString& data) { callback.Run(data); }, + std::move(callback)); +} + void DocumentLoader::BodyDataReceivedImpl(BodyData& data) { TRACE_EVENT0("loading", "DocumentLoader::BodyDataReceived"); base::span<const char> encoded_data = data.EncodedData();
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h index 1e9fc28..26ad99b 100644 --- a/third_party/blink/renderer/core/loader/document_loader.h +++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -548,6 +548,7 @@ int64_t total_decoded_body_length, bool should_report_corb_blocking, const absl::optional<WebURLError>& error) override; + ProcessBackgroundDataCallback TakeProcessBackgroundDataCallback() override; void ApplyClientHintsConfig( const WebVector<network::mojom::WebClientHintsType>&
diff --git a/third_party/blink/renderer/core/paint/README.md b/third_party/blink/renderer/core/paint/README.md index c13720a..af393b2 100644 --- a/third_party/blink/renderer/core/paint/README.md +++ b/third_party/blink/renderer/core/paint/README.md
@@ -462,9 +462,9 @@ from its containing self-painting layer to this layer, assuming that this layer needs all paint phases that its container self-painting layer needs. -### Transform update optimization +### Property tree update optimization -In specific cases of a transform update, we can directly update the property +In some specific cases of style updates, we can directly update the property tree without needing to run the property tree builder (Which requires a layout tree walk). During `PaintLayer::StyleDidChange` we check if this update meets the requirements for a quick update, and if so we add it to a list of pending @@ -472,9 +472,13 @@ changes can't be detected correctly). The updates are executed later in `PrePaintTreeWalk::WalkTree` using the -`LocalFrameView::UpdateAllPendingTransforms`. If at some point during pre-paint -we reach a node that has a pending update, we mark that node as needs full -update, and remove the pending update from the list. +the designated functions (For example - +`LocalFrameView::UpdateAllPendingTransforms`). If at some point during +pre-paint we reach a node that has a pending update, we mark that node as needs +full update, and remove the pending update from the list. + +Current updates that are checked for an optimized update are transform updates +and opacity updates. ### Hit test information recording
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_test.cc b/third_party/blink/renderer/core/paint/compositing/compositing_test.cc index 56b5a04..c6e446e2 100644 --- a/third_party/blink/renderer/core/paint/compositing/compositing_test.cc +++ b/third_party/blink/renderer/core/paint/compositing/compositing_test.cc
@@ -981,6 +981,75 @@ EXPECT_FALSE(transform_node->transform_changed); } +// Same as the test above but for opacity changes +TEST_P(CompositingSimTest, FastPathOpacityUpdateFromStyle) { + InitializeWithHTML(R"HTML( + <!DOCTYPE html> + <style> + @keyframes animation { + 0% { opacity: 0.2; } + 100% { opacity: 0.8; } + } + #div { + opacity: 0.1; + width: 100px; + height: 100px; + /* + This causes the opacity to have an active animation, but because + the delay is so large, it will not have an effect for the duration + of this unit test. + */ + animation-name: animation; + animation-duration: 999s; + animation-delay: 999s; + } + </style> + <div id='div'></div> + )HTML"); + + Compositor().BeginFrame(); + + // Check the initial state of the blink effect node. + auto* div = GetElementById("div"); + auto* div_properties = + div->GetLayoutObject()->FirstFragment().PaintProperties(); + ASSERT_TRUE(div_properties); + EXPECT_NEAR(0.1, div_properties->Effect()->Opacity(), 0.001); + EXPECT_TRUE(div_properties->Effect()->HasActiveOpacityAnimation()); + EXPECT_FALSE(div->GetLayoutObject()->NeedsPaintPropertyUpdate()); + + // Check the initial state of the cc effect node. + auto* div_cc_layer = CcLayerByDOMElementId("div"); + auto effect_tree_index = div_cc_layer->effect_tree_index(); + const auto* effect_node = + GetPropertyTrees()->effect_tree().Node(effect_tree_index); + EXPECT_FALSE(effect_node->effect_changed); + EXPECT_FALSE(paint_artifact_compositor()->NeedsUpdate()); + EXPECT_NEAR(0.1, effect_node->opacity, 0.001); + + // Change the effect style and ensure the blink and cc effect nodes are + // not marked for a full update. + div->setAttribute(html_names::kStyleAttr, "opacity: 0.15"); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); + EXPECT_FALSE(div->GetLayoutObject()->NeedsPaintPropertyUpdate()); + EXPECT_FALSE(paint_artifact_compositor()->NeedsUpdate()); + + // Continue to run the lifecycle to paint and ensure that updates are + // performed. + UpdateAllLifecyclePhasesExceptPaint(); + EXPECT_NEAR(0.15, div_properties->Effect()->Opacity(), 0.001); + EXPECT_NEAR(0.15, effect_node->opacity, 0.001); + EXPECT_TRUE(effect_node->effect_changed); + EXPECT_FALSE(div->GetLayoutObject()->NeedsPaintPropertyUpdate()); + EXPECT_FALSE(paint_artifact_compositor()->NeedsUpdate()); + EXPECT_TRUE(effect_node->effect_changed); + + // After a frame the |opacity_changed| value should be reset. + Compositor().BeginFrame(); + EXPECT_FALSE(effect_node->effect_changed); +} + TEST_P(CompositingSimTest, DirectSVGTransformPropertyUpdate) { InitializeWithHTML(R"HTML( <!doctype html>
diff --git a/third_party/blink/renderer/core/paint/object_paint_properties.h b/third_party/blink/renderer/core/paint/object_paint_properties.h index d417496..7bc1c0b6 100644 --- a/third_party/blink/renderer/core/paint/object_paint_properties.h +++ b/third_party/blink/renderer/core/paint/object_paint_properties.h
@@ -321,6 +321,18 @@ std::move(transform_and_origin), animation_state); } + PaintPropertyChangeType DirectlyUpdateOpacity( + float opacity, + const EffectPaintPropertyNode::AnimationState& animation_state) { + // TODO(yotha): Remove this check once we make sure crbug.com/1370268 is + // fixed + DCHECK(effect_ != nullptr); + if (effect_ == nullptr) { + return PaintPropertyChangeType::kNodeAddedOrRemoved; + } + return effect_->DirectlyUpdateOpacity(opacity, animation_state); + } + private: // Return true if the property tree structure changes (an existing node was // deleted), and false otherwise. See the class-level comment ("update & clear
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc index 631eddb..3ebecf5 100644 --- a/third_party/blink/renderer/core/paint/paint_layer.cc +++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -2457,9 +2457,18 @@ } } + bool needs_full_opacity_update = diff.OpacityChanged(); + if (needs_full_opacity_update) { + if (PaintPropertyTreeBuilder::ScheduleDeferredOpacityNodeUpdate( + GetLayoutObject())) { + needs_full_opacity_update = false; + SetNeedsDescendantDependentFlagsUpdate(); + } + } + // See also |LayoutObject::SetStyle| which handles these invalidations if a // PaintLayer is not present. - if (needs_full_transform_update || diff.OpacityChanged() || + if (needs_full_transform_update || needs_full_opacity_update || diff.ZIndexChanged() || diff.FilterChanged() || diff.CssClipChanged() || diff.BlendModeChanged() || diff.MaskChanged() || diff.CompositingReasonsChanged()) {
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc index 1fae5b6..8379c36 100644 --- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc +++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -851,6 +851,24 @@ } } +static void DirectlyUpdateCcOpacity(const LayoutObject& object, + ObjectPaintProperties& properties, + PaintPropertyChangeType& change_type) { + if (change_type == PaintPropertyChangeType::kChangedOnlySimpleValues && + properties.Effect()->HasDirectCompositingReasons()) { + if (auto* paint_artifact_compositor = + object.GetFrameView()->GetPaintArtifactCompositor()) { + bool updated = + paint_artifact_compositor->DirectlyUpdateCompositedOpacityValue( + *properties.Effect()); + if (updated) { + change_type = PaintPropertyChangeType::kChangedOnlyCompositedValues; + properties.Effect()->CompositorSimpleValuesUpdated(); + } + } + } +} + // TODO(dbaron): Remove this function when we can remove the // BackfaceVisibilityInteropEnabled() check, and have the caller use // CompositingReason::kDirectReasonsForTransformProperty directly. @@ -1446,6 +1464,9 @@ void FragmentPaintPropertyTreeBuilder::UpdateEffect() { DCHECK(properties_); + // Since we're doing a full update, clear list of objects waiting for a + // deferred update + object_.GetFrameView()->RemovePendingOpacityUpdate(object_); const ComputedStyle& style = object_.StyleRef(); if (NeedsPaintPropertyUpdate()) { @@ -1557,25 +1578,7 @@ // If we have simple value change, which means opacity, we should try to // directly update it on the PaintArtifactCompositor in order to avoid // doing a full rebuild. - if (effective_change_type == - PaintPropertyChangeType::kChangedOnlySimpleValues && - properties_->Effect()->HasDirectCompositingReasons() && - // TODO(crbug.com/1253797): Due to the bug, we may create multiple - // cc effect nodes for one blink effect node of a fragmented object, - // and direct update would be incomplete in the case. - !object_.FirstFragment().NextFragment()) { - if (auto* paint_artifact_compositor = - object_.GetFrameView()->GetPaintArtifactCompositor()) { - bool updated = - paint_artifact_compositor->DirectlyUpdateCompositedOpacityValue( - *properties_->Effect()); - if (updated) { - effective_change_type = - PaintPropertyChangeType::kChangedOnlyCompositedValues; - properties_->Effect()->CompositorSimpleValuesUpdated(); - } - } - } + DirectlyUpdateCcOpacity(object_, *properties_, effective_change_type); OnUpdateEffect(effective_change_type); auto mask_direct_compositing_reasons = @@ -2957,6 +2960,8 @@ // optimized transform updates if (object_.GetFrameView()->RemovePendingTransformUpdate(object_)) object_.GetMutableForPainting().SetOnlyThisNeedsPaintPropertyUpdate(); + if (object_.GetFrameView()->RemovePendingOpacityUpdate(object_)) + object_.GetMutableForPainting().SetOnlyThisNeedsPaintPropertyUpdate(); if (box.Size() == box.PreviousSize()) return; @@ -4222,6 +4227,17 @@ return false; } +bool PaintPropertyTreeBuilder::ScheduleDeferredOpacityNodeUpdate( + LayoutObject& object) { + if (!base::FeatureList::IsEnabled(features::kFastPathPaintPropertyUpdates)) + return false; + if (CanDoDeferredOpacityNodeUpdate(object)) { + object.GetFrameView()->AddPendingOpacityUpdate(object); + return true; + } + return false; +} + // Fast-path for directly updating transforms. Returns true if successful. This // is similar to |FragmentPaintPropertyTreeBuilder::UpdateIndividualTransform|. void PaintPropertyTreeBuilder::DirectlyUpdateTransformMatrix( @@ -4280,6 +4296,34 @@ } } +void PaintPropertyTreeBuilder::DirectlyUpdateOpacityValue( + const LayoutObject& object) { + DCHECK(CanDoDeferredOpacityNodeUpdate(object)); + const ComputedStyle& style = object.StyleRef(); + + EffectPaintPropertyNode::AnimationState animation_state; + animation_state.is_running_opacity_animation_on_compositor = + style.IsRunningOpacityAnimationOnCompositor(); + animation_state.is_running_backdrop_filter_animation_on_compositor = + style.IsRunningBackdropFilterAnimationOnCompositor(); + + FragmentData* fragment_data = &object.GetMutableForPainting().FirstFragment(); + auto* properties = fragment_data->PaintProperties(); + auto effective_change_type = + properties->DirectlyUpdateOpacity(style.Opacity(), animation_state); + // If we have simple value change, which means opacity, we should try to + // directly update it on the PaintArtifactCompositor in order to avoid + // needing to run the property tree builder at all. + DirectlyUpdateCcOpacity(object, *properties, effective_change_type); + + if (effective_change_type >= + PaintPropertyChangeType::kChangedOnlySimpleValues) { + object.GetFrameView()->SetPaintArtifactCompositorNeedsUpdate( + PaintArtifactCompositorUpdateReason:: + kPaintPropertyTreeBuilderPaintPropertyChanged); + } +} + void PaintPropertyTreeBuilder::IssueInvalidationsAfterUpdate() { // We need to update property tree states of paint chunks. auto max_change = properties_changed_.Max(); @@ -4387,4 +4431,38 @@ return true; } +bool PaintPropertyTreeBuilder::CanDoDeferredOpacityNodeUpdate( + const LayoutObject& object) { + // If we already need a full update, do not do the direct update. + if (object.NeedsPaintPropertyUpdate() || + object.DescendantNeedsPaintPropertyUpdate()) { + return false; + } + + // In some cases where we need to remove the update, objects that are not + // boxes can cause a bug. (See SetNeedsPaintPropertyUpdateIfNeeded) + if (!object.IsBox()) + return false; + + // This fast path does not support iterating over each fragment, so do not + // run the fast path in the presence of fragmentation. + if (object.FirstFragment().NextFragment()) + return false; + + auto* properties = object.FirstFragment().PaintProperties(); + // Cannot directly update properties if they have not been created yet. + if (!properties || !properties->Effect()) + return false; + + // Descendant state depends on opacity being zero, so we can't do a direct + // update if it changes + bool old_opacity_is_zero = properties->Effect()->Opacity() == 0; + bool new_opacity_is_zero = object.Style()->Opacity() == 0; + if (old_opacity_is_zero != new_opacity_is_zero) { + return false; + } + + return true; +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.h b/third_party/blink/renderer/core/paint/paint_property_tree_builder.h index 0bcd685..66f00d2 100644 --- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.h +++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.h
@@ -371,8 +371,10 @@ } static void DirectlyUpdateTransformMatrix(const LayoutObject& object); + static void DirectlyUpdateOpacityValue(const LayoutObject& object); static bool ScheduleDeferredTransformNodeUpdate(LayoutObject& object); + static bool ScheduleDeferredOpacityNodeUpdate(LayoutObject& object); private: ALWAYS_INLINE void InitFragmentPaintProperties( @@ -410,6 +412,7 @@ bool IsInNGFragmentTraversal() const { return pre_paint_info_; } static bool CanDoDeferredTransformNodeUpdate(const LayoutObject& object); + static bool CanDoDeferredOpacityNodeUpdate(const LayoutObject& object); const LayoutObject& object_; NGPrePaintInfo* pre_paint_info_;
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc index 8642c9e..edd44c9 100644 --- a/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc +++ b/third_party/blink/renderer/core/paint/paint_property_tree_update_tests.cc
@@ -2000,4 +2000,25 @@ EXPECT_TRUE(GetPaintLayerByElementId("masked")->SelfNeedsRepaint()); } +TEST_P(PaintPropertyTreeUpdateTest, + DirectOpacityUpdateSkipsPropertyTreeBuilder) { + SetBodyInnerHTML(R"HTML( + <div id='div' style="opacity:0.5"></div> + )HTML"); + + auto* div_properties = PaintPropertiesForElement("div"); + ASSERT_TRUE(div_properties); + EXPECT_EQ(0.5, div_properties->Effect()->Opacity()); + auto* div = GetDocument().getElementById("div"); + EXPECT_FALSE(div->GetLayoutObject()->NeedsPaintPropertyUpdate()); + + div->setAttribute(html_names::kStyleAttr, "opacity:0.8"); + GetDocument().View()->UpdateLifecycleToLayoutClean( + DocumentUpdateReason::kTest); + EXPECT_FALSE(div->GetLayoutObject()->NeedsPaintPropertyUpdate()); + + UpdateAllLifecyclePhasesExceptPaint(); + EXPECT_NEAR(0.8, div_properties->Effect()->Opacity(), 0.001); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc index a6a0a23..2e1ad9a1 100644 --- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc +++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -78,6 +78,10 @@ ShowAllPropertyTrees(root_frame_view); #endif + if (root_frame_view.UpdateAllPendingTransforms() || + root_frame_view.UpdateAllPendingOpacityUpdates()) + needs_invalidate_chrome_client_ = true; + // If the page has anything changed, we need to inform the chrome client // so that the client will initiate repaint of the contents if needed (e.g. // when this page is embedded as a non-composited content of another page). @@ -85,7 +89,6 @@ if (auto* client = root_frame_view.GetChromeClient()) client->InvalidateContainer(); } - root_frame_view.UpdateAllPendingTransforms(); } void PrePaintTreeWalk::Walk(LocalFrameView& frame_view,
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc index 69a153a9..32df292 100644 --- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc +++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc
@@ -7,7 +7,6 @@ #include <hb.h> #include "base/logging.h" -#include "base/ranges/algorithm.h" #include "third_party/blink/renderer/platform/fonts/font.h" #include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h" #include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h" @@ -412,8 +411,9 @@ void Finish(unsigned from, unsigned to) { std::sort(cluster_starts_.begin(), cluster_starts_.end()); - DCHECK_EQ(base::ranges::adjacent_find(cluster_starts_), - cluster_starts_.end()); + DCHECK_EQ( + std::adjacent_find(cluster_starts_.begin(), cluster_starts_.end()), + cluster_starts_.end()); DVLOG(4) << " Cluster starts: " << base::make_span(cluster_starts_); if (!cluster_starts_.empty()) { // 'from' may point inside a cluster; the least seen index may be larger.
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc index c9fcebb1..55358e65 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -399,16 +399,12 @@ EnsureCompositorTransformNode(transform_node.Parent()->Unalias()); id = transform_tree_.Insert(cc::TransformNode(), parent_id); - // ScrollUnification creates the entire scroll tree and will already have done - // this. - if (!base::FeatureList::IsEnabled(features::kScrollUnification)) { - if (auto* scroll_translation_for_fixed = - transform_node.ScrollTranslationForFixed()) { - // Fixed-position can cause different topologies of the transform tree and - // the scroll tree. This ensures the ancestor scroll nodes of the scroll - // node for a descendant transform node below is created. - EnsureCompositorTransformNode(*scroll_translation_for_fixed); - } + if (auto* scroll_translation_for_fixed = + transform_node.ScrollTranslationForFixed()) { + // Fixed-position can cause different topologies of the transform tree and + // the scroll tree. This ensures the ancestor scroll nodes of the scroll + // node for a descendant transform node below is created. + EnsureCompositorTransformNode(*scroll_translation_for_fixed); } cc::TransformNode& compositor_node = *transform_tree_.Node(id);
diff --git a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc index 28a479bf..b23bab2 100644 --- a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc +++ b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc
@@ -42,18 +42,8 @@ } bool opacity_changed = opacity != other.opacity; bool opacity_change_is_simple = - opacity_changed && - // Opacity change is simple if - // - opacity doesn't change from or to 1, or - // - there was and is active opacity animation, or - // TODO(crbug.com/1285498): Optimize for will-change: opacity. - // The rule is because whether opacity is 1 affects whether the effect - // should create a render surface if there is no active opacity animation. - ((opacity != 1.f && other.opacity != 1.f) || - ((direct_compositing_reasons & - CompositingReason::kActiveOpacityAnimation) && - (other.direct_compositing_reasons & - CompositingReason::kActiveOpacityAnimation))); + IsOpacityChangeSimple(opacity, other.opacity, direct_compositing_reasons, + other.direct_compositing_reasons); if (opacity_changed && !opacity_change_is_simple) { DCHECK(!animation_state.is_running_opacity_animation_on_compositor); return PaintPropertyChangeType::kChangedOnlyValues; @@ -94,6 +84,19 @@ return PaintPropertyChangeType::kUnchanged; } +bool EffectPaintPropertyNode::State::IsOpacityChangeSimple( + float opacity, + float new_opacity, + CompositingReasons direct_compositing_reasons, + CompositingReasons new_direct_compositing_reasons) { + bool opacity_changed = opacity != new_opacity; + return opacity_changed && ((opacity != 1.f && new_opacity != 1.f) || + ((direct_compositing_reasons & + CompositingReason::kActiveOpacityAnimation) && + (new_direct_compositing_reasons & + CompositingReason::kActiveOpacityAnimation))); +} + const EffectPaintPropertyNode& EffectPaintPropertyNode::Root() { DEFINE_STATIC_REF(EffectPaintPropertyNode, root, base::AdoptRef(new EffectPaintPropertyNode( @@ -149,6 +152,40 @@ } } +PaintPropertyChangeType EffectPaintPropertyNode::State::ComputeOpacityChange( + float new_opacity, + const AnimationState& animation_state) const { + bool opacity_changed = opacity != new_opacity; + bool opacity_change_is_simple = State::IsOpacityChangeSimple( + opacity, new_opacity, direct_compositing_reasons, + direct_compositing_reasons); + if (opacity_changed && !opacity_change_is_simple) { + DCHECK(!animation_state.is_running_opacity_animation_on_compositor); + return PaintPropertyChangeType::kChangedOnlyValues; + } + + bool simple_values_changed = + opacity_change_is_simple && + !animation_state.is_running_opacity_animation_on_compositor; + if (simple_values_changed) { + return PaintPropertyChangeType::kChangedOnlySimpleValues; + } + if (opacity_changed) { + return PaintPropertyChangeType::kChangedOnlyCompositedValues; + } + return PaintPropertyChangeType::kUnchanged; +} + +PaintPropertyChangeType EffectPaintPropertyNode::DirectlyUpdateOpacity( + float opacity, + const AnimationState& animation_state) { + auto change = state_.ComputeOpacityChange(opacity, animation_state); + state_.opacity = opacity; + if (change != PaintPropertyChangeType::kUnchanged) + AddChanged(change); + return change; +} + gfx::RectF EffectPaintPropertyNode::MapRect(const gfx::RectF& rect) const { if (state_.filter.IsEmpty()) return rect;
diff --git a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h index e40a762..e98e719 100644 --- a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h +++ b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
@@ -132,6 +132,22 @@ PaintPropertyChangeType ComputeChange( const State& other, const AnimationState& animation_state) const; + + PaintPropertyChangeType ComputeOpacityChange( + float opacity, + const AnimationState& animation_state) const; + + // Opacity change is simple if + // - opacity doesn't change from or to 1, or + // - there was and is active opacity animation, or + // TODO(crbug.com/1285498): Optimize for will-change: opacity. + // The rule is because whether opacity is 1 affects whether the effect + // should create a render surface if there is no active opacity animation. + static bool IsOpacityChangeSimple( + float opacity, + float new_opacity, + CompositingReasons direct_compositing_reasons, + CompositingReasons new_direct_compositing_reasons); }; // This node is really a sentinel, and does not represent a real effect. @@ -157,6 +173,10 @@ return std::max(parent_changed, state_changed); } + PaintPropertyChangeType DirectlyUpdateOpacity( + float opacity, + const AnimationState& animation_state); + const EffectPaintPropertyNode& Unalias() const = delete; bool IsParentAlias() const = delete;
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader.cc index a94e202..82f2357 100644 --- a/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader.cc +++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader.cc
@@ -8,6 +8,7 @@ #include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_functions.h" #include "base/numerics/safe_conversions.h" +#include "base/run_loop.h" #include "base/strings/strcat.h" #include "base/trace_event/trace_event.h" #include "services/network/public/cpp/features.h" @@ -34,6 +35,12 @@ namespace blink { namespace { +bool ShouldSendDirectlyToPreloadScanner() { + static const base::FeatureParam<bool> kSendToScannerParam{ + &features::kThreadedBodyLoader, "send-to-scanner", true}; + return kSendToScannerParam.Get(); +} + // A chunk of data read by the OffThreadBodyReader. This will be created on a // background thread and processed on the main thread. struct DataChunk { @@ -130,11 +137,36 @@ return std::move(data_chunks_); } + void StoreProcessBackgroundDataCallback(Client* client) { + DCHECK(IsMainThread()); + if (background_callback_set_) + return; + + auto callback = client->TakeProcessBackgroundDataCallback(); + if (!callback) + return; + + background_callback_set_ = true; + + base::AutoLock lock(lock_); + process_background_data_callback_ = std::move(callback); + + // Process any existing data to make sure we don't miss any. + for (const auto& chunk : data_chunks_) + process_background_data_callback_.Run(chunk.decoded_data); + } + void Delete() const { DCHECK(IsMainThread()); reader_task_runner_->DeleteSoon(FROM_HERE, this); } + void FlushForTesting() { + base::RunLoop run_loop; + reader_task_runner_->PostTask(FROM_HERE, run_loop.QuitClosure()); + run_loop.Run(); + } + private: // BodyReader: bool ShouldContinueReading() override { @@ -185,6 +217,9 @@ bool post_task; { base::AutoLock lock(lock_); + if (decoded_data && process_background_data_callback_) + process_background_data_callback_.Run(decoded_data); + // If |data_chunks_| is not empty, there is already a task posted which // will consume the data, so no need to post another one. post_task = data_chunks_.empty(); @@ -214,6 +249,11 @@ bool has_seen_end_of_data_ = false; base::Lock lock_; + // This bool is used on the main thread to avoid locking when the callback has + // already been set. + bool background_callback_set_ = false; + Client::ProcessBackgroundDataCallback process_background_data_callback_ + GUARDED_BY(lock_); std::vector<DataChunk> data_chunks_ GUARDED_BY(lock_); }; @@ -269,7 +309,9 @@ task_runner_), resource_load_info_notifier_wrapper_( std::move(resource_load_info_notifier_wrapper)), - original_url_(original_url) {} + original_url_(original_url), + should_send_directly_to_preload_scanner_( + ShouldSendDirectlyToPreloadScanner()) {} NavigationBodyLoader::~NavigationBodyLoader() { if (!has_received_completion_ || !has_seen_end_of_data_) { @@ -360,6 +402,12 @@ should_keep_encoded_data)); } +void NavigationBodyLoader::FlushOffThreadBodyReaderForTesting() { + if (!off_thread_body_reader_) + return; + off_thread_body_reader_->FlushForTesting(); +} + void NavigationBodyLoader::BindURLLoaderAndContinue() { url_loader_.Bind(std::move(endpoints_->url_loader), task_runner_); url_loader_client_receiver_.Bind(std::move(endpoints_->url_loader_client), @@ -419,6 +467,9 @@ } } NotifyCompletionIfAppropriate(); + + if (weak_self && should_send_directly_to_preload_scanner_) + off_thread_body_reader_->StoreProcessBackgroundDataCallback(client_); } void NavigationBodyLoader::ReadFromDataPipe() {
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader.h b/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader.h index 72ec6573..a100fce 100644 --- a/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader.h +++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader.h
@@ -62,9 +62,25 @@ // callbacks will not be called until StartLoadingBody() is called. If // |should_keep_encoded_data| is true, the original data will be copied from // the background thread and passed to DecodedBodyDataReceived(). + // + // If the preload scanner is being used in the parser, the flow of data to the + // scanner will go like this: + // 1. NavigationBodyLoader calls DecodedBodyDataReceived(), which will + // end up getting forwarded to HTMLDocumentParser::Append(). + // 2. HTMLDocumentParser::Append() will cause the background preload + // scanner to be created and scan the initial data passed to Append(). + // 3. NavigationBodyLoader calls TakeProcessBackgroundDataCallback() + // which tells HTMLDocumentParser to stop sending data to the + // preload scanner in Append(). + // 4. NavigationBodyLoader will pass data directly to the callback + // taken from TakeProcessBackgroundDataCallback(), which avoids + // hitting the main thread at all. HTMLDocumentParser will still + // receive data through Append() calls. void StartLoadingBodyInBackground(std::unique_ptr<BodyTextDecoder> decoder, bool should_keep_encoded_data); + void FlushOffThreadBodyReaderForTesting(); + private: // The loading flow is outlined below. NavigationBodyLoader can be safely // deleted at any moment, and it will record cancelation stats, but will not @@ -176,6 +192,7 @@ using OffThreadBodyReaderPtr = std::unique_ptr<OffThreadBodyReader, OffThreadBodyReaderDeleter>; OffThreadBodyReaderPtr off_thread_body_reader_; + bool should_send_directly_to_preload_scanner_ = false; base::WeakPtrFactory<NavigationBodyLoader> weak_factory_{this}; };
diff --git a/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader_unittest.cc b/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader_unittest.cc index 75c6d1b2..e18fde5 100644 --- a/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader_unittest.cc +++ b/third_party/blink/renderer/platform/loader/fetch/url_loader/navigation_body_loader_unittest.cc
@@ -27,6 +27,7 @@ #include "third_party/blink/renderer/platform/loader/fetch/code_cache_host.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h" #include "third_party/blink/renderer/platform/weborigin/referrer.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" namespace blink { @@ -143,6 +144,10 @@ run_loop_->Quit(); } + ProcessBackgroundDataCallback TakeProcessBackgroundDataCallback() override { + return std::move(process_background_data_callback_); + } + void TakeActions() { if (!buffer_to_write_.empty()) { std::string buffer = buffer_to_write_; @@ -227,6 +232,7 @@ bool destroy_loader_ = false; std::string data_received_; absl::optional<WebURLError> error_; + ProcessBackgroundDataCallback process_background_data_callback_; }; TEST_F(NavigationBodyLoaderTest, SetDefersBeforeStart) { @@ -246,6 +252,41 @@ EXPECT_EQ("HELLO", TakeDataReceived()); } +TEST_F(NavigationBodyLoaderTest, ProcessBackgroundData) { + CreateBodyLoader(); + StartLoadingInBackground(); + // First flush data to the off thread reader. The background data callback + // should not see this since it is not set yet. + Write("hello"); + To<NavigationBodyLoader>(loader_.get())->FlushOffThreadBodyReaderForTesting(); + + String background_data = ""; + process_background_data_callback_ = CrossThreadBindRepeating( + [](String* background_data, const WebString& data) { + *background_data = *background_data + String(data); + }, + CrossThreadUnretained(&background_data)); + + ExpectDecodedDataReceived(); + StartLoading(); + Wait(); + EXPECT_EQ("HELLO", TakeDataReceived()); + EXPECT_EQ("", background_data); + + // Now write more data with the background data callback set. + ExpectDecodedDataReceived(); + Write("hello2"); + Wait(); + EXPECT_EQ("HELLO2", TakeDataReceived()); + EXPECT_EQ("HELLO2", background_data); + + ExpectDecodedDataReceived(); + Write("hello3"); + Wait(); + EXPECT_EQ("HELLO3", TakeDataReceived()); + EXPECT_EQ("HELLO2HELLO3", background_data); +} + TEST_F(NavigationBodyLoaderTest, DataReceived) { CreateBodyLoader(); StartLoading();
diff --git a/third_party/blink/renderer/platform/media/web_media_player_impl.cc b/third_party/blink/renderer/platform/media/web_media_player_impl.cc index 8287f504..791da6c 100644 --- a/third_party/blink/renderer/platform/media/web_media_player_impl.cc +++ b/third_party/blink/renderer/platform/media/web_media_player_impl.cc
@@ -2610,6 +2610,7 @@ DCHECK(main_task_runner_->BelongsToCurrentThread()); was_suspended_for_frame_closed_ = true; + UpdateBackgroundVideoOptimizationState(); UpdatePlayState(); }
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index ee01a0c..d2cd2cf 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -7239,6 +7239,8 @@ # Disable top flakes trigger step failure on builders crbug.com/1371949 [ Mac12 ] virtual/low-priority-script-loading/http/tests/devtools/network/resource-priority.js [ Failure Pass ] crbug.com/1371949 [ Mac11 ] virtual/low-priority-script-loading/http/tests/devtools/network/resource-priority.js [ Failure Pass ] +crbug.com/1374204 [ Linux ] inspector-protocol/debugger/updateCallFrameScopes.js [ Failure Pass ] +crbug.com/1374204 [ Mac ] inspector-protocol/debugger/updateCallFrameScopes.js [ Failure Pass ] # Sheriff 2022-10-07 crbug.com/1372556 [ Linux ] external/wpt/css/css-text/text-transform/text-transform-capitalize-* [ Failure Pass ] @@ -7276,8 +7278,6 @@ crbug.com/1374264 [ Mac ] external/wpt/screen-capture/permissions-policy-audio.https.sub.html [ Failure ] crbug.com/1374264 [ Mac ] external/wpt/screen-capture/permissions-policy-video.https.sub.html [ Failure ] -crbug.com/1372181 external/wpt/scroll-animations/css/scroll-timeline-default-iframe-print.html [ Failure ] - # Sheriff 2022-10-04 crbug.com/1145491 [ Mac ] virtual/controls-refresh-hc/fast/forms/color-scheme/media/video-overlay-menu.html [ Skip ] @@ -7286,3 +7286,392 @@ crbug.com/1373684 external/wpt/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child.tentative.sub.window.html [ Failure Pass Timeout ] crbug.com/1373684 external/wpt/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-special-cases.tentative.sub.window.html [ Failure Pass Timeout ] crbug.com/1373684 external/wpt/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild.tentative.sub.window.html [ Failure Pass Timeout ] + +#Temporarily disable tests until debugged and fixed 2022-10-05 +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/alpha-video-playback.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/color-profile-video-seek-object-fit.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/color-profile-video-seek.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/color-profile-video.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/controls-after-reload.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/controls-strict.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/controls-styling-strict.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/controls-styling.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/controls-without-preload.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-aspect-ratio.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-canvas-alpha.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-colorspace-yuv420.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-colorspace-yuv422.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-controls-rendering.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-display-toggle.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-object-fit-change.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-object-fit.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-replaces-poster.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-zoom-controls.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/controls/overflow-menu-focus.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/controls/video-overlay-cast-dark-rendering.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/controls/video-overlay-cast-light-rendering.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/track/track-cue-rendering-horizontal.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/media-controls-overflow-hidden.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/media-element-play-after-eos.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-canvas.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-seek-past-end-paused.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/W3C/video/networkState/networkState_during_progress.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/external/wpt/media-source/mediasource-changetype-play-implicit.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/external/wpt/media-source/mediasource-changetype-play.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/external/wpt/media-source/mediasource-config-change-webm-v-framesize.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/external/wpt/media-source/mediasource-duration.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/external/wpt/media-source/mediasource-seek-beyond-duration.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/external/wpt/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-duration-known-after-eos.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-loop.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-no-audio.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-seek-to-duration-with-playbackrate-zero.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/controls/controls-video-keynav.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/controls/paint-controls-webkit-appearance-none-custom-bg.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/controls/paint-controls-webkit-appearance-none.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/track/track-cue-rendering-rtl.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-currentTime-delay.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-layer-crash.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-persistence.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-size.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-source-error-no-candidate.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-source-type.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-src-skip-suspend-after-have-metadata.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/video-zoom.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/controls/captions-menu-always-visible.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/controls/overflow-menu-always-visible.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/controls/video-controls-with-cast-rendering.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/controls/volumechange-muted-attribute.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/track/track-css-matching-timestamps.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/track/track-css-matching.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/track/track-cue-rendering-position-auto-rtl.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/track/track-cue-rendering-position-auto.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/track/track-cue-rendering-ruby.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-frameserver/media/track/track-cue-rendering-snap-to-lines-not-set.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/alpha-video-playback.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/color-profile-video-seek-object-fit.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/color-profile-video-seek.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/color-profile-video.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls-after-reload.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls-strict.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls-styling-strict.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls-styling.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls-without-preload.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-aspect-ratio.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-canvas-alpha.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-colorspace-yuv420.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-colorspace-yuv422.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-controls-rendering.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-display-toggle.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-object-fit-change.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-object-fit.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-replaces-poster.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-zoom-controls.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/video-overlay-cast-dark-rendering.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/video-overlay-cast-light-rendering.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-horizontal.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-element-play-after-eos.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-canvas.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-seek-past-end-paused.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/networkState/networkState_during_progress.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-changetype-play-implicit.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-changetype-play.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-config-change-mp4-av-audio-bitrate.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-config-change-mp4-av-framesize.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-config-change-mp4-av-video-bitrate.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-config-change-mp4-v-framesize.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-config-change-webm-av-audio-bitrate.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-config-change-webm-av-framesize.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-config-change-webm-av-video-bitrate.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-config-change-webm-v-bitrate.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-config-change-webm-v-framerate.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-config-change-webm-v-framesize.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-duration.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-play-then-seek-back.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-play.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-redundant-seek.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-replay.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-seek-beyond-duration.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/dedicated-worker/mediasource-worker-handle-transfer.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/dedicated-worker/mediasource-worker-play-terminate-worker.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/dedicated-worker/mediasource-worker-play.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mse-for-webcodecs/tentative/mediasource-webcodecs-appendencodedchunks-play.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/event-attributes.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-currentTime-delay.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-currentTime-set.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-duration-known-after-eos.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-loop.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-no-audio.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-not-paused-while-looping.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-playbackrate.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-poster-after-playing.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-seek-to-duration-with-playbackrate-zero.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-seeking.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/controls-video-keynav.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/doubletap-to-jump-backwards-at-start.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/doubletap-to-jump-backwards.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/doubletap-to-jump-forwards-too-short.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/paint-controls-webkit-appearance-none-custom-bg.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/paint-controls-webkit-appearance-none.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/scrubbing-touch.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/scrubbing.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/time-update-after-unload.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/toggle-class-with-poster-frame.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/toggle-class-with-poster-image.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/toggle-class-with-state-fullscreen.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0005.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0006.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0009.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0011.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0014.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0024.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0035.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0036.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0037.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0038.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0039.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0051.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0052.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0053.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0054.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0055.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0059.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0072.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0078.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0079.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0085.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0086.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0087.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0088.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0089.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0090.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0091.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-css-cue-lifetime.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-css-matching-timestamps.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-css-matching.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-mutable-text.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-nothing-to-render.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-rtl.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-snap-to-lines-not-set.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-word-breaking.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/regions-webvtt/vtt-region-display.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/encrypted-media/encrypted-media-lifetime-reload.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/encrypted-media/encrypted-media-playback-before-setmediakeys.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/encrypted-media/encrypted-media-playback-encrypted-and-clear-sources.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/encrypted-media/encrypted-media-playback-multiple-sessions.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/encrypted-media/encrypted-media-playback-setmediakeys-after-src.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/encrypted-media/encrypted-media-playback-setmediakeys-before-src.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/encrypted-media/encrypted-media-playback-two-videos.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/encrypted-media/encrypted-media-setmediakeys-again-after-playback.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/encrypted-media/encrypted-media-setmediakeys-again-after-resetting-src.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/encrypted-media/encrypted-media-setmediakeys-at-same-time.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/encrypted-media/encrypted-media-waiting-for-a-key.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-seek-past-end-playing.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/video-controls-with-cast-rendering.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0004.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0017.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0080.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-fragments/TC0081.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-ruby.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-activesourcebuffers.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-append-buffer.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-appendbuffer-quota-exceeded.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-config-change-webm-a-bitrate.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-duration-boundaryconditions.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-errors.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-h264-play-starved.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-seek-during-pending-seek.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-sourcebuffer-mode.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/external/wpt/media-source/mediasource-timestamp-offset.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/audio-concurrent-supported.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/audio-constructor-preload.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/audio-constructor-src.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/audio-constructor.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/audio-controls-do-not-fade-out.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/audio-data-url.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/audio-delete-while-slider-thumb-clicked.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/audio-garbage-collect.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/audio-play-event.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/audio-repaint.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/audio-src-suspend-after-have-metadata.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/auto-play-in-sandbox-with-allow-scripts.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/autoplay-clears-autoplaying-flag.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/autoplay-document-move.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/autoplay-muted-conditions.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/autoplay-muted.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/autoplay-never-visible.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/autoplay-unmute-offscreen.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/autoplay-webapp-scope.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/autoplay-when-visible-multiple-times.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/autoplay-when-visible.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/autoplay-with-preload-none.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/autoplay.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/autoplay/document-user-activation.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/autoplay/muted-change-src.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/autoplay/muted-playsinline.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/autoplay/muted-volume-0.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls-drag-timebar.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls-right-click-on-timebar.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/accessibility-caption-button.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/accessibility-timeline-element.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/accessibility-timeline.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/captions-menu-always-visible.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/click-anywhere-to-play-pause.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/closed-captions-dynamic-update.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/closed-captions-on-off.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/closed-captions-single-track.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/closed-captions-switch-track.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/controls-cast-do-not-fade-out.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/controls-cast-overlay-slow-fade.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/controls-layout-in-different-size.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/doubletap-on-play-button.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/doubletap-to-toggle-fullscreen.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/immersive-doubletap.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/overflow-button-disabled-no-source.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/overflow-menu-always-visible.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/overflow-menu-hide-on-click-item.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/overflow-menu-hide-on-click-outside.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/overflow-menu-hide-on-click-panel.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/overflow-menu-hide-on-scroll.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/overflow-menu-keyboard-navigation.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/overflow-menu-pointer-selection.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/overflow-menu-toggle-class-for-animation.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/overflow-menu-visibility.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/overlay-play-button-tap-to-hide.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/playback-speed-menu.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/progress-bar-repaint-on-size-change.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/show-controls-on-touch.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/singletouch-on-play-button.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/slow-doubletap.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/tab-into-controls-after-touch.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/tap-to-hide-controls.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/text-track-menu-pointer-selection.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/text-track-menu-keyboard-navigation.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/toggle-class-with-state-source-reset.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/video-enter-exit-fullscreen-while-hovering-shows-controls.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/video-enter-exit-fullscreen-without-hovering-doesnt-show-controls.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/controls/volumechange-muted-attribute.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/gc-while-playing.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/gc-while-seeking.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-continues-playing-after-replace-source.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-controls-invalid-url.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-controls-overflow-hidden.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-controls-tap-show-controls-without-activating.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-document-audio-repaint.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-document-audio-size.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-ended.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-play-promise.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-src-suspend-after-have-enough-data.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-src-suspend-after-have-future-data.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-src-suspend-after-have-metadata.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-src-suspend-before-have-metadata.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/media-state.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/picture-in-picture/clear-after-pip.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/picture-in-picture/clear-after-request.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/picture-in-picture/clear-player-during-pip.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/picture-in-picture/controls/picture-in-picture-button.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/picture-in-picture/picture-in-picture-interstitial.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/picture-in-picture/picture-in-picture-interstitial-sizing.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/remove-from-document.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/css-cue-for-video-in-shadow-2.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/css-cue-for-video-in-shadow.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/cue-style-invalidation.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/regions-webvtt/vtt-region-dom-layout.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/text-track-selection-menu-add-track.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-css-all-cues.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-css-author-settings-override-user-settings.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-css-important-user-settings-override-author-settings.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-css-is-pseudo.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-css-matching-default.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-css-matching-lang.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-css-property-allowlist.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-css-user-settings-override-internal-settings.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-container-rendering-position.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-gc-wrapper.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-dynamic-controls.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-dynamic-style.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-font-with-zoom.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-mode-changed.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-negative-line-numbers.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-no-space.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-on-resize.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-overscan.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-position-auto-rtl.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-position-auto.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-tree-is-removed-properly.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-vertical.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-wider-than-controls.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-with-padding.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/track/track-cue-rendering-zero-dimension-crash.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-autoplay.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-black-bg-in-media-document.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-buffered-unknown-duration.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-controls-auto-hide-after-play-by-touch.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-controls-dont-show-on-focus-when-disabled.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-controls-hide-after-touch-on-control.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-controls-hide-on-move-outside-controls.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-controls-in-media-document.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-controls-labels.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-controls-overflow-menu-fullscreen-button.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-controls-show-on-focus.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-controls-toggling.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-controls-track-selection-menu.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-controls-visibility-multimodal-mouse-after-touch.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-controls-visibility-multimodal-touch-after-mouse.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-controls-zoomed.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-currentTime-before-have-metadata-media-fragment-uri.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-currentTime-before-have-metadata.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-dom-autoplay.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-double-seek-currentTime.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-layer-crash.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-pause-immediately.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-persistence.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-play-empty-events.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-positive-start-time-seek-after-start-time.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-positive-start-time-seek-before-start-time.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-poster-load-after-playing.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-preload.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-remove-insert-repaints.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-scales-in-media-document.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-seek-by-small-increment.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-size.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-source.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-src-skip-suspend-after-have-metadata.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-src-suspend-after-have-metadata.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-srcobject-mediastream-src-file.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-timeupdate-during-playback.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/video-zoom.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/viewport-in-standalone-media-document.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/audio/events/event_canplay.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/audio/events/event_canplaythrough.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/audio/events/event_loadeddata.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/audio/events/event_order_canplay_canplaythrough.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/audio/events/event_order_canplay_playing.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/audio/events/event_order_loadedmetadata_loadeddata.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/audio/events/event_play.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/audio/events/event_playing_manual.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/audio/events/event_playing.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/audio/events/event_timeupdate.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/audio/paused/paused_false_during_play.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/audio/readyState/readyState_during_canplay.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/audio/readyState/readyState_during_canplaythrough.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/audio/readyState/readyState_during_loadeddata.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/audio/readyState/readyState_during_playing.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/events/event_canplay.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/events/event_canplaythrough.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/events/event_loadeddata.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/events/event_order_canplay_canplaythrough.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/events/event_order_canplay_playing.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/events/event_order_loadedmetadata_loadeddata.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/events/event_order_loadstart_progress.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/events/event_play.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/events/event_playing_manual.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/events/event_playing.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/events/event_progress.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/events/event_timeupdate.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/paused/paused_false_during_play.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/readyState/readyState_during_canplay.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/readyState/readyState_during_canplaythrough.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/readyState/readyState_during_loadeddata.html [ Skip ] +crbug.com/1362106 [ Win ] virtual/media-foundation-for-clear-dcomp/media/W3C/video/readyState/readyState_during_playing.html [ Skip ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites index 267642c..4f46713 100644 --- a/third_party/blink/web_tests/VirtualTestSuites +++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -178,6 +178,23 @@ "--disable-composited-antialiasing"] }, { + "prefix": "media-foundation-for-clear-dcomp", + "platforms": ["Win"], + "bases": ["external/wpt/media-source", + "media"], + "args": ["--use-gpu-in-tests", + "--enable-features=MediaFoundationClearPlayback,MediaFoundationClearRendering:strategy/direct-composition"] + }, + { + "prefix": "media-foundation-for-clear-frameserver", + "platforms": ["Win"], + "bases": ["external/wpt/media-source", + "media"], + "args": ["--use-gpu-in-tests", + "--force-mfmediaengine-renderer", + "--enable-features=MediaFoundationClearRendering:strategy/frame-server"] + }, + { "prefix": "media-gpu-accelerated", "platforms": ["Linux", "Mac", "Win", "Fuchsia"], "bases": ["external/wpt/media-source",
diff --git a/third_party/blink/web_tests/external/wpt/.well-known/web-identity b/third_party/blink/web_tests/external/wpt/.well-known/web-identity index f3a99e2..836d87b7 100644 --- a/third_party/blink/web_tests/external/wpt/.well-known/web-identity +++ b/third_party/blink/web_tests/external/wpt/.well-known/web-identity
@@ -1,6 +1,13 @@ def main(request, response): - if not b"sec-fetch-dest" in request.headers or request.headers[b"sec-fetch-dest"] != b"webidentity": - return (500, [], "Missing Sec-Fetch-Dest header") + if len(request.cookies) > 0: + return (530, [], "Cookie should not be sent to manifest list endpoint") + if request.headers.get(b"Accept") != b"application/json": + return (531, [], "Wrong Accept") + if request.headers.get(b"Sec-Fetch-Dest") != b"webidentity": + return (532, [], "Wrong Sec-Fetch-Dest header") + if request.headers.get(b"Referer"): + return (533, [], "Should not have Referer") + config = request.server.config host = config.browser_host + ":" + str(config.ports["https"][0]) return """
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-logout-rps.https.html b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-logout-rps.https.html index 9a96777..51b1230 100644 --- a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-logout-rps.https.html +++ b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-logout-rps.https.html
@@ -7,9 +7,9 @@ <script src="/resources/testharnessreport.js"></script> <script type="module"> - import {fedcm_test} from './support/fedcm-mojojs-helper.js'; + import {fedcm_mojo_mock_test} from './support/fedcm-mojojs-helper.js'; - fedcm_test(async (t, mock) => { + fedcm_mojo_mock_test(async (t, mock) => { mock.logoutRpsReturn("kError"); return promise_rejects_dom(t, "NetworkError", IdentityCredential.logoutRPs([{ @@ -19,7 +19,7 @@ ); }, "IdentityCredential.logoutRPs() error."); - fedcm_test(async (t, mock) => { + fedcm_mojo_mock_test(async (t, mock) => { mock.logoutRpsReturn("kSuccess"); await IdentityCredential.logoutRPs([{ accountId: "1234", @@ -27,7 +27,7 @@ }]); }, "IdentityCredential.logoutRPs() success."); - fedcm_test(async (t, mock) => { + fedcm_mojo_mock_test(async (t, mock) => { return promise_rejects_dom(t, "NetworkError", IdentityCredential.logoutRPs([{ accountId: "1234",
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-network-requests.sub.https.html b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-network-requests.sub.https.html index 9c21ccec..12a3813a 100644 --- a/third_party/blink/web_tests/external/wpt/credential-management/fedcm-network-requests.sub.https.html +++ b/third_party/blink/web_tests/external/wpt/credential-management/fedcm-network-requests.sub.https.html
@@ -20,14 +20,20 @@ } }; -promise_test(async t => { - await set_fedcm_cookie(); +// Test wrapper which does FedCM-specific setup. +function fedcm_test(test_func) { + promise_test(async t => { + await set_fedcm_cookie(); + await test_func(t); + }); +} + +fedcm_test(async t => { const cred = await navigator.credentials.get(test_options); assert_equals(cred.token, "token"); }, "Successfully obtaining token should resolve the promise."); -promise_test(async t => { - await set_fedcm_cookie(); +fedcm_test(async t => { const first = navigator.credentials.get(test_options); const second = navigator.credentials.get(test_options); @@ -43,7 +49,7 @@ }, "When there's a pending request, a second `get` call should be rejected. "); -promise_test(async t => { +fedcm_test(async t => { const cred = navigator.credentials.get({ identity: { providers: [] @@ -52,7 +58,7 @@ return promise_rejects_js(t, TypeError, cred); }, "Reject when provider list is empty"); -promise_test(async t => { +fedcm_test(async t => { const cred = navigator.credentials.get({ identity: { providers: [{ @@ -64,7 +70,7 @@ return promise_rejects_js(t, TypeError, cred); }, "Reject when configURL is missing" ); -promise_test(async t => { +fedcm_test(async t => { const cred = navigator.credentials.get({ identity: { providers: [{ @@ -76,7 +82,7 @@ return promise_rejects_dom(t, "InvalidStateError", cred); }, "Reject when configURL is invalid"); -promise_test(async t => { +fedcm_test(async t => { const cred = navigator.credentials.get({ identity: { providers: [{ @@ -88,7 +94,7 @@ return promise_rejects_dom(t, "InvalidStateError", cred); }, "Reject when clientId is empty"); -promise_test(async t => { +fedcm_test(async t => { const cred = await navigator.credentials.get({ identity: { providers: [{ @@ -100,7 +106,7 @@ assert_equals(cred.token, "token"); }, "nonce is not required in FederatedIdentityProvider."); -promise_test(async t => { +fedcm_test(async t => { const cred = navigator.credentials.get({ identity: { providers: [{ @@ -111,7 +117,7 @@ return promise_rejects_js(t, TypeError, cred); }, "Reject when clientId is missing" ); -promise_test(async t => { +fedcm_test(async t => { let controller = new AbortController(); const cred = navigator.credentials.get({ identity: test_options.identity, @@ -121,9 +127,7 @@ return promise_rejects_dom(t, 'AbortError', cred); }, "Test the abort signal"); -promise_test(async t => { - await set_fedcm_cookie(); - +fedcm_test(async t => { let controller = new AbortController(); const first_cred = navigator.credentials.get({ identity: test_options.identity, @@ -136,7 +140,7 @@ assert_equals(second_cred.token, "token"); }, "Get after abort should work"); -promise_test(async t => { +fedcm_test(async t => { const cred = navigator.credentials.get({ identity: { providers: [{ @@ -148,14 +152,12 @@ return promise_rejects_dom(t, "NetworkError", cred); }, "Provider configURL should honor Content-Security-Policy."); -promise_test(async t => { - await set_fedcm_cookie(); +fedcm_test(async t => { const cred = await navigator.credentials.get(test_options); assert_equals(cred.token, 'token'); }, 'Test that COEP policy do not apply to FedCM requests'); -promise_test(async t => { - await set_fedcm_cookie(); +fedcm_test(async t => { const cred = navigator.credentials.get({ identity: { providers: [{
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/support/README.md b/third_party/blink/web_tests/external/wpt/credential-management/support/README.md index 902a7ca..6fb064c 100644 --- a/third_party/blink/web_tests/external/wpt/credential-management/support/README.md +++ b/third_party/blink/web_tests/external/wpt/credential-management/support/README.md
@@ -34,7 +34,7 @@ ## FedCM Testing -`fedcm-mojojs-helper.js` exposes `fedcm_test` which is a specialized +`fedcm-mojojs-helper.js` exposes `fedcm_mojo_mock_test` which is a specialized `promise_test` which comes pre-setup with the appropriate mocking infrastructure to emulate platform federated auth backend. The mock is passed to the test function as the second parameter. @@ -42,9 +42,9 @@ Example usage: ``` <script type="module"> - import {fedcm_test} from './support/fedcm-mojojs-helper.js'; + import {fedcm_mojo_mock_test} from './support/fedcm-mojojs-helper.js'; - fedcm_test(async (t, mock) => { + fedcm_mojo_mock_test(async (t, mock) => { mock.returnToken("a_token"); assert_equals("a_token", await navigator.credentials.get(options)); }, "Successfully obtaining a token using mock.");
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/support/accounts.py b/third_party/blink/web_tests/external/wpt/credential-management/support/accounts.py index e9828cf..f645dfda 100644 --- a/third_party/blink/web_tests/external/wpt/credential-management/support/accounts.py +++ b/third_party/blink/web_tests/external/wpt/credential-management/support/accounts.py
@@ -1,8 +1,13 @@ def main(request, response): - if not b"sec-fetch-dest" in request.headers or request.headers[b"sec-fetch-dest"] != b"webidentity": - return (500, [], "Missing Sec-Fetch-Dest header") - if not b"cookie" in request.cookies or request.cookies[b"cookie"].value != b"1": - return (500, [], "Missing cookie") + if request.cookies.get(b"cookie") != b"1": + return (530, [], "Missing cookie") + if request.headers.get(b"Accept") != b"application/json": + return (531, [], "Wrong Accept") + if request.headers.get(b"Sec-Fetch-Dest") != b"webidentity": + return (532, [], "Wrong Sec-Fetch-Dest header") + if request.headers.get(b"Referer"): + return (533, [], "Should not have Referer") + return """ { "accounts": [{
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/support/client_metadata.py b/third_party/blink/web_tests/external/wpt/credential-management/support/client_metadata.py index e9ae722..f08b2852 100644 --- a/third_party/blink/web_tests/external/wpt/credential-management/support/client_metadata.py +++ b/third_party/blink/web_tests/external/wpt/credential-management/support/client_metadata.py
@@ -1,8 +1,13 @@ def main(request, response): - if not b"sec-fetch-dest" in request.headers or request.headers[b"sec-fetch-dest"] != b"webidentity": - return (500, [], "Missing Sec-Fetch-Dest header") - if b"cookie" in request.cookies: - return (500, [], "Cookie should not be sent to this endpoint") + if len(request.cookies) > 0: + return (530, [], "Cookie should not be sent to this endpoint") + if request.headers.get(b"Accept") != b"application/json": + return (531, [], "Wrong Accept") + if request.headers.get(b"Sec-Fetch-Dest") != b"webidentity": + return (532, [], "Wrong Sec-Fetch-Dest header") + if not request.headers.get(b"Referer"): + return (533, [], "Missing Referer") + return """ { "privacy_policy_url": "https://privacypolicy.com"
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-mojojs-helper.js b/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-mojojs-helper.js index 428a2fc..40ab729 100644 --- a/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-mojojs-helper.js +++ b/third_party/blink/web_tests/external/wpt/credential-management/support/fedcm-mojojs-helper.js
@@ -5,7 +5,7 @@ import { MockFederatedAuthRequest } from './fedcm-mock.js'; -export function fedcm_test(test_func, name, exception, properties) { +export function fedcm_mojo_mock_test(test_func, name, exception, properties) { promise_test(async (t) => { assert_implements(navigator.credentials, 'missing navigator.credentials'); const mock = new MockFederatedAuthRequest();
diff --git a/third_party/blink/web_tests/external/wpt/credential-management/support/token.py b/third_party/blink/web_tests/external/wpt/credential-management/support/token.py index 1ce4348..2f5e807b 100644 --- a/third_party/blink/web_tests/external/wpt/credential-management/support/token.py +++ b/third_party/blink/web_tests/external/wpt/credential-management/support/token.py
@@ -1,6 +1,22 @@ def main(request, response): - if not b"sec-fetch-dest" in request.headers or request.headers[b"sec-fetch-dest"] != b"webidentity": - return (500, [], "Missing Sec-Fetch-Dest header") - if not b"cookie" in request.cookies or request.cookies[b"cookie"].value != b"1": - return (500, [], "Missing cookie") + if request.cookies.get(b"cookie") != b"1": + return (530, [], "Missing cookie") + if request.method != "POST": + return (531, [], "Method is not POST") + if request.headers.get(b"Content-Type") != b"application/x-www-form-urlencoded": + return (532, [], "Wrong Content-Type") + if request.headers.get(b"Accept") != b"application/json": + return (533, [], "Wrong Accept") + if request.headers.get(b"Sec-Fetch-Dest") != b"webidentity": + return (500, [], "Wrong Sec-Fetch-Dest header") + if not request.headers.get(b"Referer"): + return (534, [], "Missing Referer") + + if not request.POST.get(b"client_id"): + return (535, [], "Missing 'client_id' POST parameter") + if not request.POST.get(b"account_id"): + return (536, [], "Missing 'account_id' POST parameter") + if not request.POST.get(b"disclosure_text_shown"): + return (537, [], "Missing 'disclosure_text_shown' POST parameter") + return "{\"token\": \"token\"}"
diff --git a/third_party/blink/web_tests/external/wpt/css/compositing/opacity-and-transform-animation-crash.html b/third_party/blink/web_tests/external/wpt/css/compositing/opacity-and-transform-animation-crash.html new file mode 100644 index 0000000..294a823 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/compositing/opacity-and-transform-animation-crash.html
@@ -0,0 +1,27 @@ +<!DOCTYPE HTML> +<html class=test-wait> +<style type="text/css"> + @keyframes anim { + from { + opacity: 0.99; + } + to { + opacity: 0.9; + transform: scale(1, 1); + } + } +</style> +<div id="div" style="transform: scale(1, 10); animation: 50ms linear 0s anim;"></div> +<script> + function boom() { + div.style.transform = ''; + document.execCommand("SelectAll", false, ""); + requestAnimationFrame(function() { + requestAnimationFrame(function() { + document.documentElement.classList.remove('test-wait'); + }); + }); + } + setTimeout(boom, 50); +</script> +</html> \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/crashtests/scroll-tree-parent-construction.html b/third_party/blink/web_tests/external/wpt/css/css-position/crashtests/scroll-tree-parent-construction.html new file mode 100644 index 0000000..8a5bf542 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-position/crashtests/scroll-tree-parent-construction.html
@@ -0,0 +1,52 @@ +<!DOCTYPE HTML> +<html class="test-wait"> +<head> +<style> + #ifr { border: 5px solid #ddd; width: 200px; height: 150px; } +</style> +</head> +<body> +<iframe id=ifr srcdoc=" + <style> + body { margin: 0 } + * { box-sizing: border-box } + .c1 { width: 90px; height: 300px; } + .f1 { + position: fixed; + background: #ddf; + left: 30px; + top: 10px; + width: 120px; + height: 120px; + } + .s1 { + overflow: scroll; + margin: 10px; + height: 100px; + width: 100px; + border: 5px solid gray; + } + </style> + <div class=c1>AAA</div> + <div class=f1> + <div class=s1> + <div class=c1>AAA</div> + </div> + </div>"></iframe> +<script> + raf = async () => { + return new Promise(resolve => { + requestAnimationFrame(resolve); + }); + } + onload = async () => { + await raf(); + await raf(); + ifr.contentWindow.location.reload(); + for (let i = 0; i < 10; i++) + await raf(); + document.documentElement.className = ""; + }; +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference-expected.txt deleted file mode 100644 index 8f80ec1d..0000000 --- a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference-expected.txt +++ /dev/null
@@ -1,174 +0,0 @@ -This is a testharness.js-based test. -Found 168 tests; 143 PASS, 25 FAIL, 0 TIMEOUT, 0 NOTRUN. -PASS HTML (standards) IMG usemap="no-hash-name" -PASS HTML (standards) OBJECT usemap="no-hash-name" -PASS HTML (standards) IMG usemap="no-hash-id" -PASS HTML (standards) OBJECT usemap="no-hash-id" -PASS HTML (standards) IMG usemap="#hash-name" -PASS HTML (standards) OBJECT usemap="#hash-name" -FAIL HTML (standards) IMG usemap="#hash-id" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-hash-id... but got Element node <img src="/images/threecolors.png" usemap="#hash-id"></img> -PASS HTML (standards) OBJECT usemap="#hash-id" -PASS HTML (standards) IMG usemap="#non-map-with-this-name" -PASS HTML (standards) OBJECT usemap="#non-map-with-this-name" -FAIL HTML (standards) IMG usemap="#non-map-with-this-id" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-non-map... but got Element node <img src="/images/threecolors.png" usemap="#non-map-with-... -PASS HTML (standards) OBJECT usemap="#non-map-with-this-id" -PASS HTML (standards) IMG usemap="#two-maps-with-this-name" -PASS HTML (standards) OBJECT usemap="#two-maps-with-this-name" -FAIL HTML (standards) IMG usemap="#two-maps-with-this-id" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-two-map... but got Element node <img src="/images/threecolors.png" usemap="#two-maps-with... -PASS HTML (standards) OBJECT usemap="#two-maps-with-this-id" -PASS HTML (standards) IMG usemap="#two-maps-with-this-name-or-id" -PASS HTML (standards) OBJECT usemap="#two-maps-with-this-name-or-id" -FAIL HTML (standards) IMG usemap="#two-maps-with-this-id-or-name" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-two-map... but got Element node <area shape="rect" coords="0,0,99,50" href="#area-two-map... -PASS HTML (standards) OBJECT usemap="#two-maps-with-this-id-or-name" -FAIL HTML (standards) IMG usemap="hash-last#" assert_equals: expected Element node <img src="/images/threecolors.png" usemap="hash-last#"></... but got Element node <area shape="rect" coords="0,0,99,50" href="#area-empty-u... -PASS HTML (standards) OBJECT usemap="hash-last#" -PASS HTML (standards) IMG usemap="" -PASS HTML (standards) OBJECT usemap="" -FAIL HTML (standards) IMG usemap="#" assert_equals: expected Element node <img src="/images/threecolors.png" usemap="#"></img> but got Element node <area shape="rect" coords="0,0,99,50" href="#area-empty-u... -PASS HTML (standards) OBJECT usemap="#" -PASS HTML (standards) IMG usemap="# " -PASS HTML (standards) OBJECT usemap="# " -FAIL HTML (standards) IMG usemap="#\n" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-hash-LF... but got Element node <img src="/images/threecolors.png" usemap="# -"></img> -PASS HTML (standards) OBJECT usemap="#\n" -PASS HTML (standards) IMG usemap="#percent-escape-name-%41" -PASS HTML (standards) OBJECT usemap="#percent-escape-name-%41" -PASS HTML (standards) IMG usemap="#percent-escape-id-%41" -PASS HTML (standards) OBJECT usemap="#percent-escape-id-%41" -PASS HTML (standards) IMG usemap="#percent-escape-name-%42" -PASS HTML (standards) OBJECT usemap="#percent-escape-name-%42" -FAIL HTML (standards) IMG usemap="#percent-escape-id-%42" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-percent... but got Element node <img src="/images/threecolors.png" usemap="#percent-escap... -PASS HTML (standards) OBJECT usemap="#percent-escape-id-%42" -PASS HTML (standards) IMG usemap="# hash-space-name" -PASS HTML (standards) OBJECT usemap="# hash-space-name" -FAIL HTML (standards) IMG usemap="# hash-space-id" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-hash-sp... but got Element node <img src="/images/threecolors.png" usemap="# hash-space-i... -PASS HTML (standards) OBJECT usemap="# hash-space-id" -PASS HTML (standards) IMG usemap=" #space-before-hash-name" -PASS HTML (standards) OBJECT usemap=" #space-before-hash-name" -FAIL HTML (standards) IMG usemap=" #space-before-hash-id" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-space-b... but got Element node <img src="/images/threecolors.png" usemap=" #space-before... -PASS HTML (standards) OBJECT usemap=" #space-before-hash-id" -PASS HTML (standards) IMG usemap="http://example.org/#garbage-before-hash-name" -PASS HTML (standards) OBJECT usemap="http://example.org/#garbage-before-hash-name" -FAIL HTML (standards) IMG usemap="http://example.org/#garbage-before-hash-id" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-garbage... but got Element node <img src="/images/threecolors.png" usemap="http://example... -PASS HTML (standards) OBJECT usemap="http://example.org/#garbage-before-hash-id" -PASS HTML (standards) IMG usemap="#no-such-map" -PASS HTML (standards) OBJECT usemap="#no-such-map" -PASS HTML (standards) IMG usemap="#different-CASE-name" -PASS HTML (standards) OBJECT usemap="#different-CASE-name" -PASS HTML (standards) IMG usemap="#different-CASE-id" -PASS HTML (standards) OBJECT usemap="#different-CASE-id" -PASS HTML (quirks) IMG usemap="no-hash-name" -PASS HTML (quirks) OBJECT usemap="no-hash-name" -PASS HTML (quirks) IMG usemap="no-hash-id" -PASS HTML (quirks) OBJECT usemap="no-hash-id" -PASS HTML (quirks) IMG usemap="#hash-name" -PASS HTML (quirks) OBJECT usemap="#hash-name" -FAIL HTML (quirks) IMG usemap="#hash-id" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-hash-id... but got Element node <img src="/images/threecolors.png" usemap="#hash-id"></img> -PASS HTML (quirks) OBJECT usemap="#hash-id" -PASS HTML (quirks) IMG usemap="#non-map-with-this-name" -PASS HTML (quirks) OBJECT usemap="#non-map-with-this-name" -FAIL HTML (quirks) IMG usemap="#non-map-with-this-id" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-non-map... but got Element node <img src="/images/threecolors.png" usemap="#non-map-with-... -PASS HTML (quirks) OBJECT usemap="#non-map-with-this-id" -PASS HTML (quirks) IMG usemap="#two-maps-with-this-name" -PASS HTML (quirks) OBJECT usemap="#two-maps-with-this-name" -FAIL HTML (quirks) IMG usemap="#two-maps-with-this-id" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-two-map... but got Element node <img src="/images/threecolors.png" usemap="#two-maps-with... -PASS HTML (quirks) OBJECT usemap="#two-maps-with-this-id" -PASS HTML (quirks) IMG usemap="#two-maps-with-this-name-or-id" -PASS HTML (quirks) OBJECT usemap="#two-maps-with-this-name-or-id" -FAIL HTML (quirks) IMG usemap="#two-maps-with-this-id-or-name" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-two-map... but got Element node <area shape="rect" coords="0,0,99,50" href="#area-two-map... -PASS HTML (quirks) OBJECT usemap="#two-maps-with-this-id-or-name" -FAIL HTML (quirks) IMG usemap="hash-last#" assert_equals: expected Element node <img src="/images/threecolors.png" usemap="hash-last#"></... but got Element node <area shape="rect" coords="0,0,99,50" href="#area-empty-u... -PASS HTML (quirks) OBJECT usemap="hash-last#" -PASS HTML (quirks) IMG usemap="" -PASS HTML (quirks) OBJECT usemap="" -FAIL HTML (quirks) IMG usemap="#" assert_equals: expected Element node <img src="/images/threecolors.png" usemap="#"></img> but got Element node <area shape="rect" coords="0,0,99,50" href="#area-empty-u... -PASS HTML (quirks) OBJECT usemap="#" -PASS HTML (quirks) IMG usemap="# " -PASS HTML (quirks) OBJECT usemap="# " -FAIL HTML (quirks) IMG usemap="#\n" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-hash-LF... but got Element node <img src="/images/threecolors.png" usemap="# -"></img> -PASS HTML (quirks) OBJECT usemap="#\n" -PASS HTML (quirks) IMG usemap="#percent-escape-name-%41" -PASS HTML (quirks) OBJECT usemap="#percent-escape-name-%41" -PASS HTML (quirks) IMG usemap="#percent-escape-id-%41" -PASS HTML (quirks) OBJECT usemap="#percent-escape-id-%41" -PASS HTML (quirks) IMG usemap="#percent-escape-name-%42" -PASS HTML (quirks) OBJECT usemap="#percent-escape-name-%42" -FAIL HTML (quirks) IMG usemap="#percent-escape-id-%42" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-percent... but got Element node <img src="/images/threecolors.png" usemap="#percent-escap... -PASS HTML (quirks) OBJECT usemap="#percent-escape-id-%42" -PASS HTML (quirks) IMG usemap="# hash-space-name" -PASS HTML (quirks) OBJECT usemap="# hash-space-name" -FAIL HTML (quirks) IMG usemap="# hash-space-id" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-hash-sp... but got Element node <img src="/images/threecolors.png" usemap="# hash-space-i... -PASS HTML (quirks) OBJECT usemap="# hash-space-id" -PASS HTML (quirks) IMG usemap=" #space-before-hash-name" -PASS HTML (quirks) OBJECT usemap=" #space-before-hash-name" -FAIL HTML (quirks) IMG usemap=" #space-before-hash-id" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-space-b... but got Element node <img src="/images/threecolors.png" usemap=" #space-before... -PASS HTML (quirks) OBJECT usemap=" #space-before-hash-id" -PASS HTML (quirks) IMG usemap="http://example.org/#garbage-before-hash-name" -PASS HTML (quirks) OBJECT usemap="http://example.org/#garbage-before-hash-name" -FAIL HTML (quirks) IMG usemap="http://example.org/#garbage-before-hash-id" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-garbage... but got Element node <img src="/images/threecolors.png" usemap="http://example... -PASS HTML (quirks) OBJECT usemap="http://example.org/#garbage-before-hash-id" -PASS HTML (quirks) IMG usemap="#no-such-map" -PASS HTML (quirks) OBJECT usemap="#no-such-map" -PASS HTML (quirks) IMG usemap="#different-CASE-name" -PASS HTML (quirks) OBJECT usemap="#different-CASE-name" -PASS HTML (quirks) IMG usemap="#different-CASE-id" -PASS HTML (quirks) OBJECT usemap="#different-CASE-id" -PASS XHTML img usemap="no-hash-name" -PASS XHTML object usemap="no-hash-name" -PASS XHTML img usemap="no-hash-id" -PASS XHTML object usemap="no-hash-id" -PASS XHTML img usemap="#hash-name" -PASS XHTML object usemap="#hash-name" -PASS XHTML img usemap="#hash-id" -PASS XHTML object usemap="#hash-id" -PASS XHTML img usemap="#non-map-with-this-name" -PASS XHTML object usemap="#non-map-with-this-name" -FAIL XHTML img usemap="#non-map-with-this-id" assert_equals: expected Element node <area shape="rect" coords="0,0,99,50" href="#area-non-map... but got Element node <img src="/images/threecolors.png" usemap="#non-map-with-... -PASS XHTML object usemap="#non-map-with-this-id" -PASS XHTML img usemap="#two-maps-with-this-name" -PASS XHTML object usemap="#two-maps-with-this-name" -PASS XHTML img usemap="#two-maps-with-this-id" -PASS XHTML object usemap="#two-maps-with-this-id" -PASS XHTML img usemap="#two-maps-with-this-name-or-id" -PASS XHTML object usemap="#two-maps-with-this-name-or-id" -PASS XHTML img usemap="#two-maps-with-this-id-or-name" -PASS XHTML object usemap="#two-maps-with-this-id-or-name" -FAIL XHTML img usemap="hash-last#" assert_equals: expected Element node <img src="/images/threecolors.png" usemap="hash-last#"></... but got Element node <area shape="rect" coords="0,0,99,50" href="#area-empty-u... -PASS XHTML object usemap="hash-last#" -PASS XHTML img usemap="" -PASS XHTML object usemap="" -FAIL XHTML img usemap="#" assert_equals: expected Element node <img src="/images/threecolors.png" usemap="#"></img> but got Element node <area shape="rect" coords="0,0,99,50" href="#area-empty-u... -PASS XHTML object usemap="#" -PASS XHTML img usemap="# " -PASS XHTML object usemap="# " -PASS XHTML img usemap="#\n" -PASS XHTML object usemap="#\n" -PASS XHTML img usemap="#percent-escape-name-%41" -PASS XHTML object usemap="#percent-escape-name-%41" -PASS XHTML img usemap="#percent-escape-id-%41" -PASS XHTML object usemap="#percent-escape-id-%41" -PASS XHTML img usemap="#percent-escape-name-%42" -PASS XHTML object usemap="#percent-escape-name-%42" -PASS XHTML img usemap="#percent-escape-id-%42" -PASS XHTML object usemap="#percent-escape-id-%42" -PASS XHTML img usemap="# hash-space-name" -PASS XHTML object usemap="# hash-space-name" -PASS XHTML img usemap="# hash-space-id" -PASS XHTML object usemap="# hash-space-id" -PASS XHTML img usemap=" #space-before-hash-name" -PASS XHTML object usemap=" #space-before-hash-name" -PASS XHTML img usemap=" #space-before-hash-id" -PASS XHTML object usemap=" #space-before-hash-id" -PASS XHTML img usemap="http://example.org/#garbage-before-hash-name" -PASS XHTML object usemap="http://example.org/#garbage-before-hash-name" -PASS XHTML img usemap="http://example.org/#garbage-before-hash-id" -PASS XHTML object usemap="http://example.org/#garbage-before-hash-id" -PASS XHTML img usemap="#no-such-map" -PASS XHTML object usemap="#no-such-map" -PASS XHTML img usemap="#different-CASE-name" -PASS XHTML object usemap="#different-CASE-name" -PASS XHTML img usemap="#different-CASE-id" -PASS XHTML object usemap="#different-CASE-id" -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference-test-data.html b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference-test-data.html index 735ba8c..1fb71431 100644 --- a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference-test-data.html +++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference-test-data.html
@@ -54,7 +54,7 @@ <div data-expect="area-non-map-with-this-id"> <img src="/images/threecolors.png" usemap="#non-map-with-this-id" id="non-map-with-this-id"/> <object data="/images/threecolors.png" usemap="#non-map-with-this-id"></object> - <map id="non-map-with-this-name"> + <map id="non-map-with-this-id"> <area shape="rect" coords="0,0,99,50" href="#area-non-map-with-this-id"/> </map> </div>
diff --git a/third_party/blink/web_tests/external/wpt/lint.ignore b/third_party/blink/web_tests/external/wpt/lint.ignore index dd3f7b6..42cee94 100644 --- a/third_party/blink/web_tests/external/wpt/lint.ignore +++ b/third_party/blink/web_tests/external/wpt/lint.ignore
@@ -145,6 +145,7 @@ SET TIMEOUT: common/reftest-wait.js SET TIMEOUT: conformance-checkers/* SET TIMEOUT: content-security-policy/* +SET TIMEOUT: css/compositing/opacity-and-transform-animation-crash.html SET TIMEOUT: css/css-display/display-contents-shadow-dom-1.html SET TIMEOUT: css/CSS2/normal-flow/crashtests/block-in-inline-ax-crash.html SET TIMEOUT: css/selectors/selector-placeholder-shown-type-change-001.html
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html index 408f62f8..b31e402 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html
@@ -257,7 +257,6 @@ // Ensure that #main (an ancestor of the scroller) needs style recalc. main.style.background = 'lightgray'; sibling.style.scrollTimelineName = 'timeline'; - await waitForNextFrame(); assert_equals(getComputedStyle(target).translate, '100px'); main.remove(); @@ -302,7 +301,6 @@ scroller.style.scrollTimelineName = 'timeline'; target.style.animation = 'anim 10s linear timeline'; - await waitForNextFrame(); assert_equals(getComputedStyle(target).translate, '100px'); @@ -353,7 +351,6 @@ scroller.style.scrollTimelineName = 'timeline'; target.style.animation = 'anim 10s linear timeline'; - await waitForNextFrame(); assert_equals(getComputedStyle(target).translate, '100px');
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/progress-based-animation-animation-longhand-properties.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/progress-based-animation-animation-longhand-properties.tentative.html index 17604e5..43401aca 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/progress-based-animation-animation-longhand-properties.tentative.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/progress-based-animation-animation-longhand-properties.tentative.html
@@ -104,12 +104,10 @@ // Let animation become 50% in the 1st iteration. target.style.animationIterationCount = '2'; - await waitForNextFrame(); assert_equals(getComputedStyle(target).translate, '50px'); // Let animation become 0% in the 2nd iteration. target.style.animationIterationCount = '4'; - await waitForNextFrame(); assert_equals(getComputedStyle(target).translate, '0px'); }, 'animation-iteration-count'); @@ -219,7 +217,6 @@ await scrollTop(scroller, 20); // [0, 100]. target.style.animationDelay = '-5s'; - await waitForNextFrame(); assert_equals(getComputedStyle(target).translate, '60px'); }, 'animation-delay with a negative value'); @@ -237,7 +234,6 @@ assert_equals(getComputedStyle(target).translate, 'none'); target.style.animationFillMode = 'backwards'; - await waitForNextFrame(); assert_equals(getComputedStyle(target).translate, '0px'); }, 'animation-fill-mode');
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-default-iframe-print.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-default-iframe-print.html index 6349622..fc8e4f1 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-default-iframe-print.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-default-iframe-print.html
@@ -5,7 +5,6 @@ <link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-timeline"> <meta name="assert" content="CSS animation correctly updates values when using the default scroll() timeline"> <link rel="match" href="scroll-timeline-default-iframe-ref.html"> -<meta name="fuzzy" content="25;100"> <iframe id="target" width="400" height="400" srcdoc=' <html>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-document-scroller-quirks.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-document-scroller-quirks.html deleted file mode 100644 index ca40161..0000000 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-document-scroller-quirks.html +++ /dev/null
@@ -1,26 +0,0 @@ -<!-- Quirks mode --> -<title>Tests the document scroller in quirks mode</title> -<link rel="help" href="https://drafts.csswg.org/scroll-animations-1/#scroll-notation"> -<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1180575"> -<link rel="author" href="mailto:andruud@chromium.org"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/scroll-animations/scroll-timelines/testcommon.js"></script> -<script src="/css/css-animations/support/testcommon.js"></script> -<style> - @keyframes anim { - from { z-index: 100; } - to { z-index: 100; } - } - #element { - animation: anim forwards scroll(root); - } -</style> -<div id=element></div> - -<script> -promise_test(async () => { - await waitForNextFrame(); - assert_equals(getComputedStyle(element).zIndex, "100"); -}); -</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative.html index c827d10..7bf35cd 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative.html
@@ -71,7 +71,6 @@ // Verify that the computed style is as expected immediately after the // rule change took place. instantiate(async (element, expected) => { - await waitForNextFrame(); assert_equals(getComputedStyle(element).width, expected); }, description + ' [immediate]');
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-in-container-query.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-in-container-query.html index 0c216c7..f74fa59 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-in-container-query.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-in-container-query.html
@@ -60,9 +60,8 @@ assert_equals(getComputedStyle(element).backgroundColor, 'rgb(100, 100, 100)'); // This causes the timeline to be created. outer.style.width = '250px'; - // Check value with getComputedStyle immediately, which is the unanimated - // value since the scroll timeline is inactive before the next frame. - assert_equals(getComputedStyle(element).backgroundColor, 'rgb(0, 0, 0)'); + // Check value with getComputedStyle immediately. + assert_equals(getComputedStyle(element).backgroundColor, 'rgb(150, 150, 150)'); // Also check value after one frame. await waitForNextFrame(); assert_equals(getComputedStyle(element).backgroundColor, 'rgb(150, 150, 150)');
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-paused-animations.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-paused-animations.html index b7611fb..a8117fa 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-paused-animations.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-paused-animations.html
@@ -36,8 +36,6 @@ t.add_cleanup(resetScrollPosition); div.style.animation = 'anim 100s linear paused scroll(root)'; - await waitForNextFrame(); - const anim = div.getAnimations()[0]; await anim.ready; assert_percents_equal(anim.currentTime, 0, 'timeline time reset'); @@ -57,8 +55,6 @@ await waitForNextFrame(); div.style.animation = 'anim 100s linear forwards scroll(root)'; - await waitForNextFrame(); - const anim = div.getAnimations()[0]; await anim.ready; assert_percents_equal(anim.currentTime, 0, 'timeline time reset');
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-responsiveness-from-endpoint.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-responsiveness-from-endpoint.html index 180704ee..5555fd0 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-responsiveness-from-endpoint.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-responsiveness-from-endpoint.html
@@ -32,8 +32,6 @@ await waitForNextFrame(); div.style.animation = 'anim 100s linear scroll(root)'; - await waitForNextFrame(); - const anim = div.getAnimations()[0]; await anim.ready; assert_percents_equal(anim.timeline.currentTime, 0,
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-sibling-gcs.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-sibling-gcs.html index 8500b39..21d5b8c 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-sibling-gcs.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-sibling-gcs.html
@@ -42,7 +42,6 @@ // Unknown timeline, time held at zero. assert_equals(getComputedStyle(element).backgroundColor, 'rgb(100, 100, 100)'); scroller.style.scrollTimeline = 'timeline'; - await waitForNextFrame(); assert_equals(getComputedStyle(element).backgroundColor, 'rgb(150, 150, 150)'); }, 'Timelines appearing on preceding siblings are visible to getComputedStyle'); </script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-dynamic.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-dynamic.html index 545e735..483fa36c 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-dynamic.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-dynamic.html
@@ -63,17 +63,14 @@ // scrollTop=50 is 75% for div75. div75.classList.add('timeline'); - await waitForNextFrame(); assert_equals(getComputedStyle(target).zIndex, '75'); // scrollTop=50 is 25% for div25. div25.classList.add('timeline'); - await waitForNextFrame(); assert_equals(getComputedStyle(target).zIndex, '25'); // scrollTop=50 is before the timeline start for div_before. div_before.classList.add('timeline'); - await waitForNextFrame(); assert_equals(getComputedStyle(target).zIndex, '-1'); // Scroll to 25% (for div_before) to verify that we're linked to that // timeline. @@ -83,7 +80,6 @@ // Now we should be back to div25's timeline, although with the new // scrollTop=150, it's actually at 75%. div_before.classList.remove('timeline'); - await waitForNextFrame(); assert_equals(getComputedStyle(target).zIndex, '75'); }, 'Dynamically changing view-timeline-name'); </script> @@ -114,7 +110,6 @@ assert_equals(getComputedStyle(target).zIndex, '25'); timeline.style.viewTimelineAxis = 'horizontal'; - await waitForNextFrame(); assert_equals(getComputedStyle(target).zIndex, '10'); }, 'Dynamically changing view-timeline-axis'); </script> @@ -144,7 +139,6 @@ assert_equals(getComputedStyle(target).zIndex, '25'); timeline.style.viewTimelineInset = '0px 50px'; - await waitForNextFrame(); assert_equals(getComputedStyle(target).zIndex, '0'); }, 'Dynamically changing view-timeline-inset'); </script> @@ -173,4 +167,4 @@ timeline.style.display = 'none'; assert_equals(getComputedStyle(target).zIndex, '-1'); }, 'Element with view-timeline becoming display:none'); -</script> +</script> \ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/media-foundation-for-clear-dcomp/README.md b/third_party/blink/web_tests/virtual/media-foundation-for-clear-dcomp/README.md new file mode 100644 index 0000000..3aa5407 --- /dev/null +++ b/third_party/blink/web_tests/virtual/media-foundation-for-clear-dcomp/README.md
@@ -0,0 +1,5 @@ +This suite runs the tests with +--use-gpu-in-tests +--enable-features=MediaFoundationClearRendering:strategy/direct-composition + +Media Foundation for Clear Direct Composition (DComp) video rendering will be used for the test suite. \ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/media-foundation-for-clear-dcomp/media/README.txt b/third_party/blink/web_tests/virtual/media-foundation-for-clear-dcomp/media/README.txt new file mode 100644 index 0000000..3f995a81 --- /dev/null +++ b/third_party/blink/web_tests/virtual/media-foundation-for-clear-dcomp/media/README.txt
@@ -0,0 +1 @@ +This directory is dedicated for testing the "Media Foundation for Clear" feature. \ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/media-foundation-for-clear-frameserver/README.md b/third_party/blink/web_tests/virtual/media-foundation-for-clear-frameserver/README.md new file mode 100644 index 0000000..65965e4 --- /dev/null +++ b/third_party/blink/web_tests/virtual/media-foundation-for-clear-frameserver/README.md
@@ -0,0 +1,6 @@ +This suite runs the tests with +--use-gpu-in-tests +--force-mfmediaengine-renderer +--enable-features=MediaFoundationClearRendering:strategy/frame-server + +Media Foundation for Clear Frame Server mode video rendering will be used for the test suite. \ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/media-foundation-for-clear-frameserver/media/README.txt b/third_party/blink/web_tests/virtual/media-foundation-for-clear-frameserver/media/README.txt new file mode 100644 index 0000000..3f995a81 --- /dev/null +++ b/third_party/blink/web_tests/virtual/media-foundation-for-clear-frameserver/media/README.txt
@@ -0,0 +1 @@ +This directory is dedicated for testing the "Media Foundation for Clear" feature. \ No newline at end of file
diff --git a/third_party/instrumented_libraries/focal/BUILD.gn b/third_party/instrumented_libraries/focal/BUILD.gn index 6e0e36b..946b817 100644 --- a/third_party/instrumented_libraries/focal/BUILD.gn +++ b/third_party/instrumented_libraries/focal/BUILD.gn
@@ -239,6 +239,10 @@ instrumented_library("libatspi2.0-0") { build_method = "meson" extra_configure_flags = [ "-Dintrospection=no" ] + package_cflags = [ + "-Wno-incompatible-function-pointer-types", + "-Wno-implicit-function-declaration", + ] } instrumented_library("libavahi-client3") { @@ -440,6 +444,7 @@ ] msan_ignorelist = "ignorelists/msan/libglib2.0-0.txt" pre_build = "scripts/pre-build/libglib2.0-0.sh" + package_cflags = [ "-Wno-int-conversion" ] } instrumented_library("libgnutls30") { @@ -474,7 +479,11 @@ } instrumented_library("libgtk-3-0") { - package_cflags = [ "-Wno-return-type" ] + package_cflags = [ + "-Wno-implicit-function-declaration", + "-Wno-int-conversion", + "-Wno-return-type", + ] extra_configure_flags = [ "--disable-static", "--disable-introspection", @@ -536,7 +545,7 @@ "--enable-local", "--with-subdir=ldap", "--with-cyrus-sasl", - "--with-threads", + "--without-threads", "--with-gssapi", "--with-tls=gnutls", "--with-odbc=unixodbc", @@ -545,6 +554,11 @@ # Debian adds a custom patch that adds @VERSION_OPTION@, which must # be substituted before building. pre_build = "scripts/pre-build/dh_autoreconf.sh" + + package_cflags = [ + "-Wno-implicit-function-declaration", + "-Wno-implicit-int", + ] } instrumented_library("libnspr4") { @@ -610,7 +624,10 @@ instrumented_library("libsasl2-2") { build_method = "debian" pre_build = "scripts/pre-build/libsasl2-2.sh" - package_cflags = [ "-Wno-return-type" ] + package_cflags = [ + "-Wno-implicit-function-declaration", + "-Wno-return-type", + ] } instrumented_library("libsecret") { @@ -644,6 +661,7 @@ instrumented_library("libunity9") { extra_configure_flags = [ "--disable-static" ] pre_build = "scripts/pre-build/autogen.sh" + package_cflags = [ "-Wno-incompatible-function-pointer-types" ] } instrumented_library("libva2") {
diff --git a/third_party/instrumented_libraries/focal/scripts/install-build-deps.sh b/third_party/instrumented_libraries/focal/scripts/install-build-deps.sh index 6f93945..7dd684b 100755 --- a/third_party/instrumented_libraries/focal/scripts/install-build-deps.sh +++ b/third_party/instrumented_libraries/focal/scripts/install-build-deps.sh
@@ -40,6 +40,7 @@ gtk+3.0 \ ido \ jasper-initramfs \ +libappindicator3-1 \ libcap2 \ libdbusmenu \ libdbusmenu-gtk3-dev \ @@ -48,9 +49,10 @@ libidn \ libindicator \ libjpeg-turbo \ +libldap-2.4-2 \ libmicrohttpd \ libpng1.6 \ -libsasl2-dev \ +libsasl2-2 \ libunity \ libx11 \ libxau \
diff --git a/third_party/ipcz/src/ipcz/driver_object.cc b/third_party/ipcz/src/ipcz/driver_object.cc index 967b3b4..8dbec71 100644 --- a/third_party/ipcz/src/ipcz/driver_object.cc +++ b/third_party/ipcz/src/ipcz/driver_object.cc
@@ -86,7 +86,7 @@ return dimensions; } -void DriverObject::Serialize(const DriverTransport& transport, +bool DriverObject::Serialize(const DriverTransport& transport, absl::Span<uint8_t> data, absl::Span<IpczDriverHandle> handles) { size_t num_bytes = data.size(); @@ -94,8 +94,11 @@ IpczResult result = driver_->Serialize( handle_, transport.driver_object().handle(), IPCZ_NO_FLAGS, nullptr, data.data(), &num_bytes, handles.data(), &num_handles); - ABSL_ASSERT(result == IPCZ_RESULT_OK); - release(); + if (result == IPCZ_RESULT_OK) { + release(); + return true; + } + return false; } // static
diff --git a/third_party/ipcz/src/ipcz/driver_object.h b/third_party/ipcz/src/ipcz/driver_object.h index 44b0c68..eed6a9b 100644 --- a/third_party/ipcz/src/ipcz/driver_object.h +++ b/third_party/ipcz/src/ipcz/driver_object.h
@@ -58,7 +58,7 @@ // transmissible by the driver without further serialization. Must only be // called on valid objects which are known to be serializable and // transmissible over `transport`. - void Serialize(const DriverTransport& transport, + bool Serialize(const DriverTransport& transport, absl::Span<uint8_t> data, absl::Span<IpczDriverHandle> handles);
diff --git a/third_party/ipcz/src/ipcz/driver_transport.cc b/third_party/ipcz/src/ipcz/driver_transport.cc index 0103284..a8cb7a1 100644 --- a/third_party/ipcz/src/ipcz/driver_transport.cc +++ b/third_party/ipcz/src/ipcz/driver_transport.cc
@@ -102,7 +102,12 @@ IpczResult DriverTransport::Transmit(Message& message) { ABSL_ASSERT(message.CanTransmitOn(*this)); - message.Serialize(*this); + if (!message.Serialize(*this)) { + // If serialization fails despite the object appearing to be serializable, + // we have to assume the transport is in a dysfunctional state and will be + // torn down by the driver soon. Discard the transmission. + return IPCZ_RESULT_FAILED_PRECONDITION; + } const absl::Span<const uint8_t> data = message.data_view(); const absl::Span<const IpczDriverHandle> handles =
diff --git a/third_party/ipcz/src/ipcz/driver_transport_test.cc b/third_party/ipcz/src/ipcz/driver_transport_test.cc index e64dab37..79bb836 100644 --- a/third_party/ipcz/src/ipcz/driver_transport_test.cc +++ b/third_party/ipcz/src/ipcz/driver_transport_test.cc
@@ -166,5 +166,41 @@ EXPECT_CALL(driver(), Close(kTransport0, _, _)); } +TEST_F(DriverTransportTest, SerializationFailure) { + // Verifies that if a serializable object fails to serialize, it's properly + // destroyed by the transport and no transmission occurs. + constexpr IpczDriverHandle kTransport0 = 5; + constexpr IpczDriverHandle kTransport1 = 42; + auto [a, b] = CreateTransportPair(kTransport0, kTransport1); + + constexpr IpczDriverHandle kFakeObject = 12345678; + test::msg::MessageWithDriverObject message; + message.params().object = + message.AppendDriverObject(DriverObject(test::kMockDriver, kFakeObject)); + + EXPECT_CALL(driver(), Serialize(kFakeObject, kTransport0, _, _, _, _, _, _)) + .WillRepeatedly([&](IpczDriverHandle, IpczDriverHandle, uint32_t, + const void*, void* data, size_t* num_bytes, + IpczDriverHandle* handles, size_t* num_handles) { + if (!data && !handles) { + // Return valid outputs when ipcz is sizing the object. + *num_bytes = 1; + *num_handles = 1; + return IPCZ_RESULT_RESOURCE_EXHAUSTED; + } + + // Return error when serializing. + return IPCZ_RESULT_FAILED_PRECONDITION; + }); + + // The object handle should be closed upon transmission failure. + EXPECT_CALL(driver(), Close(kFakeObject, _, _)).Times(1); + + a->Transmit(message); + + EXPECT_CALL(driver(), Close(kTransport1, _, _)); + EXPECT_CALL(driver(), Close(kTransport0, _, _)); +} + } // namespace } // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/message.cc b/third_party/ipcz/src/ipcz/message.cc index fea6a45..b736842 100644 --- a/third_party/ipcz/src/ipcz/message.cc +++ b/third_party/ipcz/src/ipcz/message.cc
@@ -29,7 +29,7 @@ // transmissible handles emitted by the driver are appended to // `transmissible_handles`, with relevant index and count also stashed in the // DriverObjectData. -IpczResult SerializeDriverObject( +bool SerializeDriverObject( DriverObject object, const DriverTransport& transport, Message& message, @@ -38,7 +38,7 @@ if (!object.is_valid()) { // This is not a valid driver handle and it cannot be serialized. data.num_driver_handles = 0; - return IPCZ_RESULT_INVALID_ARGUMENT; + return false; } uint32_t driver_data_array = 0; @@ -60,10 +60,13 @@ dimensions.num_driver_handles); auto handles_view = absl::MakeSpan(transmissible_handles); - object.Serialize( - transport, driver_data, - handles_view.subspan(first_handle, dimensions.num_driver_handles)); - return IPCZ_RESULT_OK; + if (!object.Serialize( + transport, driver_data, + handles_view.subspan(first_handle, dimensions.num_driver_handles))) { + return false; + } + + return true; } // Returns `true` if and only if it will be safe to use GetArrayView() to access @@ -208,10 +211,10 @@ return true; } -void Message::Serialize(const DriverTransport& transport) { +bool Message::Serialize(const DriverTransport& transport) { ABSL_ASSERT(CanTransmitOn(transport)); if (driver_objects_.empty()) { - return; + return true; } const uint32_t array_offset = @@ -222,16 +225,19 @@ // handles attached. Since these objects are small, we inline some storage on // the stack to avoid some heap allocation in the most common cases. absl::InlinedVector<IpczDriverHandle, 2> transmissible_handles; + bool ok = true; for (size_t i = 0; i < driver_objects().size(); ++i) { internal::DriverObjectData data = {}; - const IpczResult result = - SerializeDriverObject(std::move(driver_objects()[i]), transport, *this, - data, transmissible_handles); - ABSL_ASSERT(result == IPCZ_RESULT_OK); + ok &= SerializeDriverObject(std::move(driver_objects()[i]), transport, + *this, data, transmissible_handles); GetArrayView<internal::DriverObjectData>(array_offset)[i] = data; } - transmissible_driver_handles_ = std::move(transmissible_handles); + if (ok) { + transmissible_driver_handles_ = std::move(transmissible_handles); + return true; + } + return false; } bool Message::DeserializeUnknownType(const DriverTransport::RawMessage& message,
diff --git a/third_party/ipcz/src/ipcz/message.h b/third_party/ipcz/src/ipcz/message.h index 879afcd..7187528e 100644 --- a/third_party/ipcz/src/ipcz/message.h +++ b/third_party/ipcz/src/ipcz/message.h
@@ -322,7 +322,7 @@ // NOTE: It is invalid to call this on a message for which // `CanTransmitOn(transport)` does not return true, and doing so results in // unspecified behavior. - void Serialize(const DriverTransport& transport); + bool Serialize(const DriverTransport& transport); // Validates and deserializes a Message of an unrecognized type. DriverObjects // are deserialized and much of the message structure is validated, but the
diff --git a/tools/cast3p/cast_core.version b/tools/cast3p/cast_core.version index 44fb13d9..1c4d2147 100644 --- a/tools/cast3p/cast_core.version +++ b/tools/cast3p/cast_core.version
@@ -1 +1 @@ -cast_20220930_0600_RC00 +cast_20220930_0600_RC03
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index d093ce1..8cfbb69 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -31229,6 +31229,24 @@ <description>Please enter the description of this user action.</description> </action> +<action name="StatusArea_Audio_AutoInputSelectionOverridden"> + <owner>aaronyu@google.com</owner> + <owner>judyhsiao@google.com</owner> + <owner>chromeos-audio-sw@google.com</owner> + <description> + The user overrode the automatically selected audio input device. + </description> +</action> + +<action name="StatusArea_Audio_AutoOutputSelectionOverridden"> + <owner>aaronyu@google.com</owner> + <owner>judyhsiao@google.com</owner> + <owner>chromeos-audio-sw@google.com</owner> + <description> + The user overrode the automatically selected audio output device. + </description> +</action> + <action name="StatusArea_Audio_CurrentInputDevice"> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> <description>Please enter the description of this user action.</description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index f8f36a345..2067f4c 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -83139,6 +83139,11 @@ <int value="9" label="CollapseButton"/> <int value="10" label="FeedBackButton"/> <int value="11" label="VersionButton"/> + <int value="12" label="PowerOffMenuButton"/> + <int value="13" label="PowerRestartMenuButton"/> + <int value="14" label="PowerSignoutMenuButton"/> + <int value="15" label="PowerLockMenuButton"/> + <int value="16" label="SupervisedButton"/> </enum> <enum name="QsFeatureCatalogName">
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml index 248d277a..426b0b42 100644 --- a/tools/metrics/histograms/metadata/chromeos/histograms.xml +++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -673,7 +673,7 @@ </histogram> <histogram name="ChromeOS.CWP.PSIMemPressure.{PType}" units="failures" - expires_after="2022-12-01"> + expires_after="2023-10-13"> <owner>raging@google.com</owner> <owner>chromeos-memory@google.com</owner> <summary>
diff --git a/tools/metrics/histograms/metadata/omnibox/histograms.xml b/tools/metrics/histograms/metadata/omnibox/histograms.xml index fd2e099..b886711f7 100644 --- a/tools/metrics/histograms/metadata/omnibox/histograms.xml +++ b/tools/metrics/histograms/metadata/omnibox/histograms.xml
@@ -168,7 +168,7 @@ </histogram> <histogram - name="Omnibox.AsyncAutocompletionTime.Provider.{Provider}{Completed}" + name="Omnibox.AsyncAutocompletionTime2.Provider.{Provider}{Completed}" units="ms" expires_after="2022-12-01"> <owner>manukh@chromium.org</owner> <owner>jdonnelly@chromium.org</owner> @@ -231,7 +231,7 @@ </token> </histogram> -<histogram name="Omnibox.AsyncAutocompletionTime.{Change}{Completed}" +<histogram name="Omnibox.AsyncAutocompletionTime2.{Change}{Completed}" units="ms" expires_after="2022-12-01"> <owner>manukh@chromium.org</owner> <owner>jdonnelly@chromium.org</owner> @@ -996,7 +996,7 @@ </histogram> <histogram - name="Omnibox.MatchStability.MatchChangeInAnyPosition{OmniboxAutocompleteUpdateSlice}" + name="Omnibox.MatchStability2.MatchChangeInAnyPosition{OmniboxAutocompleteUpdateSlice}" enum="BooleanChanged" expires_after="2022-12-04"> <owner>tommycli@chromium.org</owner> <owner>manukh@chromium.org</owner> @@ -1030,11 +1030,12 @@ Omnibox.Start.WantAsyncMatches, which will yield the number of match changes per keystroke or other user gesture. - There're the related Omnibox.MatchStability.InAnyPosition.* slices for + There're the related Omnibox.MatchStability2.InAnyPosition.* slices for tracking match instability for all, asynchronous, and synchronous updates. This histogram can be considered a boolean analogue to the - Omnibox.MatchStability.Index.* histogram which tracks which matches changed. + Omnibox.MatchStability2.Index.* histogram which tracks which matches + changed. {OmniboxAutocompleteUpdateSlice} </summary> @@ -1043,7 +1044,7 @@ </histogram> <histogram - name="Omnibox.MatchStability.MatchChangeIndex{OmniboxAutocompleteUpdateSlice}" + name="Omnibox.MatchStability2.MatchChangeIndex{OmniboxAutocompleteUpdateSlice}" units="position" expires_after="2022-12-04"> <owner>tommycli@chromium.org</owner> <owner>manukh@chromium.org</owner> @@ -1084,10 +1085,10 @@ Omnibox.Start.WantAsyncMatches, which will yield the number of match changes per keystroke or other user gesture. - There're the related Omnibox.MatchStability.Index.* slices for tracking + There're the related Omnibox.MatchStability2.Index.* slices for tracking match instability for all, asynchronous, and synchronous updates. - There's the related Omnibox.MatchStability.InAnyPosition histogram for + There's the related Omnibox.MatchStability2.InAnyPosition histogram for tracking whether any match changed per autocomplete update. {OmniboxAutocompleteUpdateSlice}
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index dc3fa53..aee5edd1 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": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux_arm64/49b4b5dcbc312d8d2c3751cf29238b8efeb4e494/trace_processor_shell" }, "win": { - "hash": "cd36010f4aaa8261c0ead7927e3b5b0c51b23680", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/7bd83a8f9af8604e932cd717fdd56e586c54d3a4/trace_processor_shell.exe" + "hash": "7131cf0f3a0b0858b4396c30e5f0c3327569e690", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/082982f29eab767cb18b2dfa7196b2774b3c65ee/trace_processor_shell.exe" }, "linux_arm": { "hash": "58893933be305d3bfe0a72ebebcacde2ac3ca893", @@ -21,8 +21,8 @@ "full_remote_path": "perfetto-luci-artifacts/v30.0/mac-arm64/trace_processor_shell" }, "linux": { - "hash": "9ec936285839034b556c7e91136aeda7c8a59150", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/7bd83a8f9af8604e932cd717fdd56e586c54d3a4/trace_processor_shell" + "hash": "75fa34af55fadc9e5df81463cae3e0dcc7637865", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/082982f29eab767cb18b2dfa7196b2774b3c65ee/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/tools/typescript/definitions/system_display.d.ts b/tools/typescript/definitions/system_display.d.ts new file mode 100644 index 0000000..f836365 --- /dev/null +++ b/tools/typescript/definitions/system_display.d.ts
@@ -0,0 +1,144 @@ +// Copyright 2022 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** @fileoverview Type definitions for chrome.system.display API. */ +// TODO(crbug.com/1203307): Auto-generate this file. + +declare namespace chrome { + export namespace system { + export namespace display { + /** + * @see https://developer.chrome.com/extensions/system.display#type-Bounds + */ + export interface Bounds { + left: number; + top: number; + width: number; + height: number; + } + + /** + * @see https://developer.chrome.com/extensions/system.display#type-Insets + */ + export interface Insets { + left: number; + top: number; + right: number; + bottom: number; + } + + /** + * @see https://developer.chrome.com/extensions/system.display#type-DisplayMode + */ + export interface DisplayMode { + width: number; + height: number; + widthInNativePixels: number; + heightInNativePixels: number; + uiScale?: number; + deviceScaleFactor: number; + refreshRate: number; + isNative: boolean; + isSelected: boolean; + isInterlaced?: boolean; + } + + /** + * @see https://developer.chrome.com/extensions/system.display#type-LayoutPosition + */ + export enum LayoutPosition { + TOP = 'top', + RIGHT = 'right', + BOTTOM = 'bottom', + LEFT = 'left', + } + + /** + * @see https://developer.chrome.com/extensions/system.display#type-DisplayLayout + */ + export interface DisplayLayout { + id: string; + parentId: string; + position: LayoutPosition; + offset: number; + } + + /** + * @see https://developer.chrome.com/extensions/system.display#type-Edid + */ + export interface Edid { + manufacturerId: string; + productId: string; + yearOfManufacture: number; + } + + /** + * @see https://developer.chrome.com/extensions/system.display#type-DisplayUnitInfo + */ + export interface DisplayUnitInfo { + id: string; + name: string; + edid?: Edid; + mirroringSourceId: string; + mirroringDestinationIds: string[]; + isPrimary: boolean; + isInternal: boolean; + isEnabled: boolean; + isUnified: boolean; + isAutoRotationAllowed?: boolean; + dpiX: number; + dpiY: number; + rotation: number; + bounds: Bounds; + overscan: Insets; + workArea: Bounds; + modes: DisplayMode[]; + hasTouchSupport: boolean; + hasAccelerometerSupport: boolean; + availableDisplayZoomFactors: number[]; + displayZoomFactor: number; + } + + /** + * @see https://developer.chrome.com/extensions/system.display#type-DisplayProperties + */ + export interface DisplayProperties { + isUnified?: boolean; + mirroringSourceId?: string; + isPrimary?: boolean; + overscan?: Insets; + rotation?: number; + boundsOriginX?: number; + boundsOriginY?: number; + displayMode?: DisplayMode; + displayZoomFactor?: number; + } + + /** + * @see https://developer.chrome.com/extensions/system.display#type-GetInfoFlags + */ + export interface GetInfoFlags { + singleUnified?: boolean; + } + + /** + * @see https://developer.chrome.com/extensions/system.display#type-MirrorMode + */ + export enum MirrorMode { + OFF = 'off', + NORMAL = 'normal', + MIXED = 'mixed', + } + + /** + * @see https://developer.chrome.com/extensions/system.display#type-MirrorModeInfo + */ + export interface MirrorModeInfo { + mode: MirrorMode; + mirroringSourceId?: string; + mirroringDestinationIds?: string[]; + } + } + } +}
diff --git a/ui/base/resource/resource_bundle_ios.mm b/ui/base/resource/resource_bundle_ios.mm index e7f29d2..ba3c71ae 100644 --- a/ui/base/resource/resource_bundle_ios.mm +++ b/ui/base/resource/resource_bundle_ios.mm
@@ -131,13 +131,10 @@ base::ScopedCFTypeRef<CGColorSpaceRef> color_space( CGColorSpaceCreateDeviceRGB()); base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate( - NULL, - target_size.width, - target_size.height, - 8, - target_size.width * 4, + NULL, target_size.width, target_size.height, 8, target_size.width * 4, color_space, - kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); + kCGImageAlphaPremultipliedFirst | + static_cast<CGImageAlphaInfo>(kCGBitmapByteOrder32Host))); CGRect target_rect = CGRectMake(0, 0, target_size.width, target_size.height);
diff --git a/ui/color/color_provider_manager.cc b/ui/color/color_provider_manager.cc index 97c8467a..2f6bf04 100644 --- a/ui/color/color_provider_manager.cc +++ b/ui/color/color_provider_manager.cc
@@ -23,13 +23,12 @@ namespace { -// Cache at most 5 ColorProviders to prevent unbounded storage from user_color. -constexpr size_t kCacheSize = 5; +// Estimated upper limit of what we should record for cache size. +constexpr int kCacheHistogramMax = 100; class GlobalManager : public ColorProviderManager { public: - explicit GlobalManager(size_t cache_size = kCacheSize) - : ColorProviderManager(cache_size) {} + GlobalManager() = default; GlobalManager(const GlobalManager&) = delete; GlobalManager& operator=(const GlobalManager&) = delete; ~GlobalManager() override = default; @@ -84,8 +83,7 @@ ColorProviderManager::Key::~Key() = default; -ColorProviderManager::ColorProviderManager(size_t cache_size) - : color_providers_(cache_size) { +ColorProviderManager::ColorProviderManager() { ResetColorProviderInitializerList(); } @@ -106,10 +104,10 @@ } // static -ColorProviderManager& ColorProviderManager::GetForTesting(size_t cache_size) { +ColorProviderManager& ColorProviderManager::GetForTesting() { absl::optional<GlobalManager>& manager = GetGlobalManager(); if (!manager.has_value()) - manager.emplace(cache_size); + manager.emplace(); return manager.value(); } @@ -126,7 +124,7 @@ void ColorProviderManager::ResetColorProviderCache() { if (!color_providers_.empty()) - color_providers_.Clear(); + color_providers_.clear(); } void ColorProviderManager::AppendColorProviderInitializer( @@ -139,7 +137,7 @@ } ColorProvider* ColorProviderManager::GetColorProviderFor(Key key) { - auto iter = color_providers_.Get(key); + auto iter = color_providers_.find(key); if (iter == color_providers_.end()) { auto provider = std::make_unique<ColorProvider>(); DCHECK(initializer_list_); @@ -147,9 +145,9 @@ initializer_list_->Notify(provider.get(), key); provider->GenerateColorMap(); - iter = color_providers_.Put(key, std::move(provider)); + iter = color_providers_.emplace(key, std::move(provider)).first; base::UmaHistogramExactLinear("Views.ColorProviderCacheSize", - color_providers_.size(), kCacheSize); + color_providers_.size(), kCacheHistogramMax); } ColorProvider* provider = iter->second.get(); DCHECK(provider);
diff --git a/ui/color/color_provider_manager.h b/ui/color/color_provider_manager.h index 3cb6105..b76df0f 100644 --- a/ui/color/color_provider_manager.h +++ b/ui/color/color_provider_manager.h
@@ -7,12 +7,11 @@ #include <memory> #include <tuple> -#include <vector> #include "base/callback.h" #include "base/callback_list.h" #include "base/component_export.h" -#include "base/containers/lru_cache.h" +#include "base/containers/flat_map.h" #include "base/memory/weak_ptr.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkColor.h" @@ -138,7 +137,7 @@ ColorProviderManager& operator=(const ColorProviderManager&) = delete; static ColorProviderManager& Get(); - static ColorProviderManager& GetForTesting(size_t cache_size = 1); + static ColorProviderManager& GetForTesting(); static void ResetForTesting(); // Resets the current `initializer_list_`. @@ -155,9 +154,7 @@ ColorProvider* GetColorProviderFor(Key key); protected: - // Creates a ColorProviderManager that stores at most |cache_size| - // ColorProviders. - explicit ColorProviderManager(size_t cache_size); + ColorProviderManager(); virtual ~ColorProviderManager(); private: @@ -167,7 +164,7 @@ // Holds the subscriptions for initializers in the `initializer_list_`. std::vector<base::CallbackListSubscription> initializer_subscriptions_; - base::LRUCache<Key, std::unique_ptr<ColorProvider>> color_providers_; + base::flat_map<Key, std::unique_ptr<ColorProvider>> color_providers_; }; } // namespace ui
diff --git a/ui/color/color_provider_manager_unittest.cc b/ui/color/color_provider_manager_unittest.cc index 2711f26..0878ba4 100644 --- a/ui/color/color_provider_manager_unittest.cc +++ b/ui/color/color_provider_manager_unittest.cc
@@ -37,14 +37,6 @@ ColorProviderManager::FrameType::kChromium, absl::nullopt, nullptr}); } -// Returns a Key where |color| is the user_color value. -ColorProviderManager::Key UserColorKey(SkColor color) { - return ColorProviderManager::Key( - ColorProviderManager::ColorMode::kLight, - ColorProviderManager::ContrastMode::kNormal, ui::SystemTheme::kDefault, - ColorProviderManager::FrameType::kChromium, color, nullptr); -} - class TestInitializerSupplier : public ColorProviderManager::InitializerSupplier { void AddColorMixers(ColorProvider* provider, @@ -125,46 +117,4 @@ EXPECT_LT(keys[0], keys[1]); } -TEST_F(ColorProviderManagerTest, CacheLimits) { - // Count each time colors are generated. - int counter = 0; - auto initializer = base::BindRepeating( - [](int* inc, ColorProvider* provider, const ColorProviderManager::Key&) { - provider->AddMixer()[kColorTest0] = {SK_ColorBLUE}; - (*inc)++; - }, - &counter); - - // Only keep 4 color providers. - ColorProviderManager& manager = ColorProviderManager::GetForTesting(4U); - manager.AppendColorProviderInitializer(initializer); - - // We need 5 keys to test this. - ColorProviderManager::Key keys[5] = { - UserColorKey(SK_ColorGRAY), UserColorKey(SK_ColorWHITE), - UserColorKey(SK_ColorRED), UserColorKey(SK_ColorBLUE), - UserColorKey(SK_ColorMAGENTA)}; - - for (const ColorProviderManager::Key& key : keys) { - manager.GetColorProviderFor(key); - } - // 5 requests for different keys yields 5 runs of the initializer. - EXPECT_EQ(5, counter); - - counter = 0; - // Magenta is the most recent so it should not result in an evaluation. - manager.GetColorProviderFor(keys[4]); - EXPECT_EQ(0, counter); - - // Gray should have been evicted so it causes an evaluation. - manager.GetColorProviderFor(keys[0]); - EXPECT_EQ(1, counter); - - counter = 0; - // The most recently used keys are grey, magenta, blue and red. Magenta should - // not result in an evaluation. - manager.GetColorProviderFor(keys[4]); - EXPECT_EQ(0, counter); -} - } // namespace ui
diff --git a/ui/gfx/image/image_ios.mm b/ui/gfx/image/image_ios.mm index 1709f7ae..685bf67 100644 --- a/ui/gfx/image/image_ios.mm +++ b/ui/gfx/image/image_ios.mm
@@ -35,7 +35,9 @@ 16, // height 8, // bitsPerComponent 0, // CG will calculate by default. - color_space, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); + color_space, + kCGImageAlphaPremultipliedFirst | + static_cast<CGImageAlphaInfo>(kCGBitmapByteOrder32Host))); CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0); CGContextFillRect(context, CGRectMake(0.0, 0.0, 16, 16)); base::ScopedCFTypeRef<CGImageRef> cg_image(
diff --git a/ui/gfx/image/image_ios_unittest.mm b/ui/gfx/image/image_ios_unittest.mm index abc094e..f7d8c49 100644 --- a/ui/gfx/image/image_ios_unittest.mm +++ b/ui/gfx/image/image_ios_unittest.mm
@@ -24,13 +24,10 @@ base::ScopedCFTypeRef<CGColorSpaceRef> color_space( CGColorSpaceCreateDeviceRGB()); base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate( - NULL, - target_size.width, - target_size.height, - 8, - target_size.width * 4, + NULL, target_size.width, target_size.height, 8, target_size.width * 4, color_space, - kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); + kCGImageAlphaPremultipliedFirst | + static_cast<CGImageAlphaInfo>(kCGBitmapByteOrder32Host))); CGRect target_rect = CGRectMake(0, 0, target_size.width, target_size.height);
diff --git a/ui/gfx/image/image_unittest_util_ios.mm b/ui/gfx/image/image_unittest_util_ios.mm index 9fd15449..a37b17f0 100644 --- a/ui/gfx/image/image_unittest_util_ios.mm +++ b/ui/gfx/image/image_unittest_util_ios.mm
@@ -22,13 +22,13 @@ base::ScopedCFTypeRef<CGColorSpaceRef> color_space( CGColorSpaceCreateDeviceRGB()); base::ScopedCFTypeRef<CGContextRef> bitmap_context(CGBitmapContextCreate( - /*data=*/ NULL, - /*width=*/ 1, - /*height=*/ 1, - /*bitsPerComponent=*/ 8, - /*bytesPerRow=*/ 4, - color_space, - kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); + /*data=*/NULL, + /*width=*/1, + /*height=*/1, + /*bitsPerComponent=*/8, + /*bytesPerRow=*/4, color_space, + kCGImageAlphaPremultipliedFirst | + static_cast<CGImageAlphaInfo>(kCGBitmapByteOrder32Host))); CGContextDrawImage(bitmap_context, CGRectMake(0, 0, 1, 1), pixel_image); // The CGBitmapContext has the same memory layout as SkColor, so we can just
diff --git a/ui/gl/gl_surface.cc b/ui/gl/gl_surface.cc index 6cae225..b9b136acf 100644 --- a/ui/gl/gl_surface.cc +++ b/ui/gl/gl_surface.cc
@@ -157,7 +157,7 @@ void GLSurface::SetVSyncEnabled(bool enabled) {} bool GLSurface::ScheduleOverlayPlane( - GLImage* image, + OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) { NOTIMPLEMENTED(); @@ -479,7 +479,7 @@ } bool GLSurfaceAdapter::ScheduleOverlayPlane( - GLImage* image, + OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) { return surface_->ScheduleOverlayPlane(image, std::move(gpu_fence),
diff --git a/ui/gl/gl_surface.h b/ui/gl/gl_surface.h index 226059a..bcdfe21 100644 --- a/ui/gl/gl_surface.h +++ b/ui/gl/gl_surface.h
@@ -30,6 +30,10 @@ #include "ui/gl/gl_surface_format.h" #include "ui/gl/gpu_preference.h" +#if defined(USE_OZONE) +#include "ui/gfx/native_pixmap.h" +#endif + namespace gfx { namespace mojom { class DelegatedInkPointRenderer; @@ -51,6 +55,13 @@ class GLImage; class EGLTimestampClient; +// OverlayImage is a platform specific type for overlay plane image data. +#if defined(USE_OZONE) +using OverlayImage = scoped_refptr<gfx::NativePixmap>; +#else +using OverlayImage = GLImage*; +#endif + // Contains per frame data, and is passed along with SwapBuffer, PostSubbuffer, // CommitOverlayPlanes type methods. struct FrameData { @@ -255,7 +266,7 @@ // |overlay_plane_data| specifies overlay data such as opacity, z_order, size, // etc. virtual bool ScheduleOverlayPlane( - GLImage* image, + OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data); @@ -424,7 +435,7 @@ gfx::VSyncProvider* GetVSyncProvider() override; void SetVSyncEnabled(bool enabled) override; bool ScheduleOverlayPlane( - GLImage* image, + OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) override; bool ScheduleDCLayer(
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc index cf79f62..6e56cf7b 100644 --- a/ui/gl/gl_surface_egl.cc +++ b/ui/gl/gl_surface_egl.cc
@@ -1040,7 +1040,7 @@ } bool NativeViewGLSurfaceEGL::ScheduleOverlayPlane( - GLImage* image, + OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) { NOTIMPLEMENTED();
diff --git a/ui/gl/gl_surface_egl.h b/ui/gl/gl_surface_egl.h index 2ec34b5..2886986be 100644 --- a/ui/gl/gl_surface_egl.h +++ b/ui/gl/gl_surface_egl.h
@@ -98,7 +98,7 @@ gfx::VSyncProvider* GetVSyncProvider() override; void SetVSyncEnabled(bool enabled) override; bool ScheduleOverlayPlane( - GLImage* image, + OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) override; gfx::SurfaceOrigin GetOrigin() const override;
diff --git a/ui/gl/gl_surface_egl_surface_control.cc b/ui/gl/gl_surface_egl_surface_control.cc index e6bf35d..a491acc 100644 --- a/ui/gl/gl_surface_egl_surface_control.cc +++ b/ui/gl/gl_surface_egl_surface_control.cc
@@ -21,6 +21,7 @@ #include "ui/gl/gl_context.h" #include "ui/gl/gl_features.h" #include "ui/gl/gl_fence_android_native_fence_sync.h" +#include "ui/gl/gl_image.h" #include "ui/gl/gl_utils.h" namespace gl { @@ -318,7 +319,7 @@ } bool GLSurfaceEGLSurfaceControl::ScheduleOverlayPlane( - GLImage* image, + OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) { if (surface_lost_) {
diff --git a/ui/gl/gl_surface_egl_surface_control.h b/ui/gl/gl_surface_egl_surface_control.h index a9484d7c..43e20c8e 100644 --- a/ui/gl/gl_surface_egl_surface_control.h +++ b/ui/gl/gl_surface_egl_surface_control.h
@@ -50,7 +50,7 @@ gfx::Size GetSize() override; bool OnMakeCurrent(GLContext* context) override; bool ScheduleOverlayPlane( - GLImage* image, + OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) override; bool IsSurfaceless() const override;
diff --git a/ui/gl/gl_surface_overlay.cc b/ui/gl/gl_surface_overlay.cc index 2917cea..1d1a671 100644 --- a/ui/gl/gl_surface_overlay.cc +++ b/ui/gl/gl_surface_overlay.cc
@@ -11,10 +11,10 @@ namespace gl { GLSurfaceOverlay::GLSurfaceOverlay( - GLImage* image, + scoped_refptr<gfx::NativePixmap> pixmap, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) - : image_(image), + : pixmap_(std::move(pixmap)), gpu_fence_(std::move(gpu_fence)), overlay_plane_data_(overlay_plane_data) {} @@ -27,10 +27,9 @@ if (gpu_fence_) acquire_fences.push_back(std::move(*gpu_fence_)); - auto pixmap = image_->GetNativePixmap(); - DCHECK(pixmap); - return pixmap->ScheduleOverlayPlane(widget, overlay_plane_data_, - std::move(acquire_fences), {}); + DCHECK(pixmap_); + return pixmap_->ScheduleOverlayPlane(widget, overlay_plane_data_, + std::move(acquire_fences), {}); } } // namespace gl
diff --git a/ui/gl/gl_surface_overlay.h b/ui/gl/gl_surface_overlay.h index 73306908..46032ceb 100644 --- a/ui/gl/gl_surface_overlay.h +++ b/ui/gl/gl_surface_overlay.h
@@ -7,10 +7,10 @@ #include "base/memory/ref_counted.h" #include "ui/gfx/gpu_fence.h" +#include "ui/gfx/native_pixmap.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/overlay_plane_data.h" #include "ui/gl/gl_export.h" -#include "ui/gl/gl_image.h" namespace gfx { class GpuFence; @@ -21,7 +21,7 @@ // For saving the properties of a GLImage overlay plane and scheduling it later. class GL_EXPORT GLSurfaceOverlay { public: - GLSurfaceOverlay(GLImage* image, + GLSurfaceOverlay(scoped_refptr<gfx::NativePixmap> pixmap, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data); GLSurfaceOverlay(GLSurfaceOverlay&& other); @@ -37,7 +37,7 @@ int z_order() const { return overlay_plane_data_.z_order; } private: - scoped_refptr<GLImage> image_; + scoped_refptr<gfx::NativePixmap> pixmap_; std::unique_ptr<gfx::GpuFence> gpu_fence_; gfx::OverlayPlaneData overlay_plane_data_; };
diff --git a/ui/gl/swap_chain_presenter.cc b/ui/gl/swap_chain_presenter.cc index 31deec1..4f4ace5b 100644 --- a/ui/gl/swap_chain_presenter.cc +++ b/ui/gl/swap_chain_presenter.cc
@@ -8,6 +8,7 @@ #include <d3d11_4.h> #include "base/debug/alias.h" +#include "base/debug/crash_logging.h" #include "base/debug/dump_without_crashing.h" #include "base/feature_list.h" #include "base/memory/raw_ptr.h" @@ -702,17 +703,40 @@ visual_transform->set_rc(1, 1, scale_y); #if DCHECK_IS_ON() - // The new transform matrix should transform the swap chain correctly - gfx::Rect new_swap_chain_rect(params.quad_rect.origin(), *swap_chain_size); - gfx::Rect result_rect = visual_transform->MapRect(new_swap_chain_rect); - if (IsWithinMargin(clipped_onscreen_rect.x(), 0)) { - DCHECK_EQ(result_rect.x(), 0); - DCHECK_EQ(result_rect.width(), monitor_size.width()); - } + { + // The new transform matrix should transform the swap chain correctly + gfx::Rect new_swap_chain_rect(params.quad_rect.origin(), *swap_chain_size); + gfx::Rect result_rect = visual_transform->MapRect(new_swap_chain_rect); + gfx::Rect new_clipped_onscreen_rect = clipped_onscreen_rect; + gfx::Transform new_visual_transform = *visual_transform; + base::debug::Alias(&new_swap_chain_rect); + base::debug::Alias(&result_rect); + base::debug::Alias(&new_clipped_onscreen_rect); + base::debug::Alias(&new_visual_transform); + // https://crbug.com/1366493: "DCHECK_EQ(result_rect.x(), 0);" sometimes + // failed in the field. But here we collect possible crashes in general. + static auto* new_swap_chain_rect_key = base::debug::AllocateCrashKeyString( + "new-swap-chain-rect", base::debug::CrashKeySize::Size256); + base::debug::ScopedCrashKeyString scoped_crash_key_1( + new_swap_chain_rect_key, new_swap_chain_rect.ToString()); + static auto* visual_transform_key = base::debug::AllocateCrashKeyString( + "visual-transform", base::debug::CrashKeySize::Size256); + base::debug::ScopedCrashKeyString scoped_crash_key_2( + visual_transform_key, visual_transform->ToString()); + static auto* result_rect_key = base::debug::AllocateCrashKeyString( + "result-rect", base::debug::CrashKeySize::Size256); + base::debug::ScopedCrashKeyString scoped_crash_key_3( + result_rect_key, result_rect.ToString()); - if (IsWithinMargin(clipped_onscreen_rect.y(), 0)) { - DCHECK_EQ(result_rect.y(), 0); - DCHECK_EQ(result_rect.height(), monitor_size.height()); + if (IsWithinMargin(clipped_onscreen_rect.x(), 0)) { + DCHECK_EQ(result_rect.x(), 0); + DCHECK_EQ(result_rect.width(), monitor_size.width()); + } + + if (IsWithinMargin(clipped_onscreen_rect.y(), 0)) { + DCHECK_EQ(result_rect.y(), 0); + DCHECK_EQ(result_rect.height(), monitor_size.height()); + } } #endif }
diff --git a/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc b/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc index 5e6124fe..8e1b4430b 100644 --- a/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc +++ b/ui/ozone/demo/skia/skia_surfaceless_gl_renderer.cc
@@ -24,6 +24,7 @@ #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/rrect_f.h" #include "ui/gfx/gpu_fence.h" +#include "ui/gfx/native_pixmap.h" #include "ui/gfx/overlay_plane_data.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_context.h" @@ -79,7 +80,9 @@ BufferWrapper(); ~BufferWrapper(); - gl::GLImage* image() const { return image_.get(); } + scoped_refptr<gfx::NativePixmap> image() const { + return image_->GetNativePixmap(); + } SkSurface* sk_surface() const { return sk_surface_.get(); } bool Initialize(GrDirectContext* gr_context,
diff --git a/ui/ozone/demo/surfaceless_gl_renderer.cc b/ui/ozone/demo/surfaceless_gl_renderer.cc index 6cf6e7e..7e0e3cc 100644 --- a/ui/ozone/demo/surfaceless_gl_renderer.cc +++ b/ui/ozone/demo/surfaceless_gl_renderer.cc
@@ -76,6 +76,11 @@ } } +scoped_refptr<gfx::NativePixmap> SurfacelessGlRenderer::BufferWrapper::image() + const { + return image_->GetNativePixmap(); +} + bool SurfacelessGlRenderer::BufferWrapper::Initialize( gfx::AcceleratedWidget widget, const gfx::Size& size) {
diff --git a/ui/ozone/demo/surfaceless_gl_renderer.h b/ui/ozone/demo/surfaceless_gl_renderer.h index 590e8e8..624f6d0 100644 --- a/ui/ozone/demo/surfaceless_gl_renderer.h +++ b/ui/ozone/demo/surfaceless_gl_renderer.h
@@ -8,6 +8,7 @@ #include <memory> #include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/native_pixmap.h" #include "ui/ozone/demo/gl_renderer.h" namespace gl { @@ -45,7 +46,7 @@ BufferWrapper(); ~BufferWrapper(); - gl::GLImage* image() const { return image_.get(); } + scoped_refptr<gfx::NativePixmap> image() const; bool Initialize(gfx::AcceleratedWidget widget, const gfx::Size& size); void BindFramebuffer();
diff --git a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc index 9e37b2d6..1a1efd6 100644 --- a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc +++ b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
@@ -70,11 +70,11 @@ } bool GbmSurfaceless::ScheduleOverlayPlane( - gl::GLImage* image, + gl::OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) { - unsubmitted_frames_.back()->overlays.push_back( - gl::GLSurfaceOverlay(image, std::move(gpu_fence), overlay_plane_data)); + unsubmitted_frames_.back()->overlays.emplace_back( + std::move(image), std::move(gpu_fence), overlay_plane_data); return true; }
diff --git a/ui/ozone/platform/drm/gpu/gbm_surfaceless.h b/ui/ozone/platform/drm/gpu/gbm_surfaceless.h index 74102c0..a34cea4 100644 --- a/ui/ozone/platform/drm/gpu/gbm_surfaceless.h +++ b/ui/ozone/platform/drm/gpu/gbm_surfaceless.h
@@ -47,7 +47,7 @@ gfx::SwapResult SwapBuffers(PresentationCallback callback, gl::FrameData data) override; bool ScheduleOverlayPlane( - gl::GLImage* image, + gl::OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) override; bool Resize(const gfx::Size& size,
diff --git a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc index 00edfa6..c6355fb4 100644 --- a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc +++ b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
@@ -125,7 +125,7 @@ } bool GbmSurfacelessWayland::ScheduleOverlayPlane( - gl::GLImage* image, + gl::OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) { auto* frame = unsubmitted_frames_.back().get(); @@ -162,9 +162,7 @@ if (gpu_fence) acquire_fences.push_back(std::move(*gpu_fence)); - auto pixmap = image->GetNativePixmap(); - DCHECK(pixmap); - frame->schedule_planes_succeeded = pixmap->ScheduleOverlayPlane( + frame->schedule_planes_succeeded = image->ScheduleOverlayPlane( widget_, overlay_plane_data, std::move(acquire_fences), {}); } return frame->schedule_planes_succeeded;
diff --git a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h index ef70bc1..638cadfc 100644 --- a/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h +++ b/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
@@ -43,7 +43,7 @@ // gl::GLSurface: bool ScheduleOverlayPlane( - gl::GLImage* image, + gl::OverlayImage image, std::unique_ptr<gfx::GpuFence> gpu_fence, const gfx::OverlayPlaneData& overlay_plane_data) override; bool IsOffscreen() override;
diff --git a/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc b/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc index fef154d..e90874b 100644 --- a/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc +++ b/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc
@@ -22,7 +22,6 @@ #include "ui/gfx/native_pixmap.h" #include "ui/gfx/overlay_plane_data.h" #include "ui/gfx/overlay_priority_hint.h" -#include "ui/gl/gl_image_egl.h" #include "ui/gl/gl_utils.h" #include "ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h" #include "ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h" @@ -49,14 +48,16 @@ constexpr uint32_t kAugmentedSurfaceNotSupportedVersion = 0; -// Fake GLImage that just schedules overlay plane. It must become busy when -// scheduled and be associated with the swap id to track correct order of swaps -// and releases of the image. -class FakeGLImageNativePixmap : public gl::GLImageEGL { +// Holds a NativePixmap used for scheduling overlay planes. It must become busy +// when scheduled and be associated with the swap id to track correct order of +// swaps and releases of the image. +// TODO(rjkroege): Consider putting extra state inside a test NativePixmap +// implementation instead of a wrapper class. +class OverlayImageHolder : public base::RefCounted<OverlayImageHolder> { public: - FakeGLImageNativePixmap(scoped_refptr<gfx::NativePixmap> pixmap, - const gfx::Size& size) - : gl::GLImageEGL(size), pixmap_(pixmap) {} + OverlayImageHolder(scoped_refptr<gfx::NativePixmap> pixmap, + const gfx::Size& size) + : pixmap_(pixmap) {} // Associates swap id with this image. void AssociateWithSwapId(uint32_t swap_id) { @@ -76,18 +77,16 @@ void SetDisplayed(bool displayed) { displayed_ = displayed; } bool displayed() const { return displayed_; } - // Overridden from GLImage: - scoped_refptr<gfx::NativePixmap> GetNativePixmap() override { - return pixmap_; - } - - protected: - ~FakeGLImageNativePixmap() override {} + scoped_refptr<gfx::NativePixmap> GetNativePixmap() { return pixmap_; } private: + friend class base::RefCounted<OverlayImageHolder>; + + ~OverlayImageHolder() = default; + scoped_refptr<gfx::NativePixmap> pixmap_; - // Indicated if the gl image is busy. If yes, it was scheduled as overlay + // Indicated if the overlay image is busy. If yes, it was scheduled as overlay // plane for further submission and can't be reused until it's freed. bool busy_ = false; @@ -119,27 +118,27 @@ } // Finishes the submission by setting the swap id of completed buffer swap and - // sets the associated gl_image as displayed and non-busy, which indicates - // that 1) the image has been sent to be shown after being scheduled 2) the - // image is displayed. This sort of mimics a buffer queue, but in a simpliear - // way. + // sets the associated overlay_image as displayed and non-busy, which + // indicates that 1) the image has been sent to be shown after being scheduled + // 2) the image is displayed. This sort of mimics a buffer queue, but in a + // simpler way. void FinishSwapBuffersAsync( uint32_t local_swap_id, - std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images, + std::vector<scoped_refptr<OverlayImageHolder>> overlay_images, gfx::SwapCompletionResult result) { last_finish_swap_id_ = pending_local_swap_ids_.front(); pending_local_swap_ids_.pop(); - for (auto& gl_image : gl_images) { - EXPECT_EQ(gl_image->GetAssociateWithSwapId(), last_finish_swap_id_); - EXPECT_TRUE(gl_image->busy() && !gl_image->displayed()); - gl_image->SetBusy(false); - gl_image->SetDisplayed(true); + for (auto& overlay_image : overlay_images) { + EXPECT_EQ(overlay_image->GetAssociateWithSwapId(), last_finish_swap_id_); + EXPECT_TRUE(overlay_image->busy() && !overlay_image->displayed()); + overlay_image->SetBusy(false); + overlay_image->SetDisplayed(true); } for (auto& displayed_image : displayed_images_) displayed_image->SetDisplayed(false); - displayed_images_ = std::move(gl_images); + displayed_images_ = std::move(overlay_images); } void BufferPresented(uint64_t local_swap_id, @@ -158,7 +157,7 @@ base::queue<uint64_t> pending_local_swap_ids_; // Keeps track of a displayed image. - std::vector<scoped_refptr<FakeGLImageNativePixmap>> displayed_images_; + std::vector<scoped_refptr<OverlayImageHolder>> displayed_images_; }; } // namespace @@ -210,7 +209,7 @@ } void ScheduleOverlayPlane(gl::GLSurface* gl_surface, - gl::GLImage* image, + gl::OverlayImage image, int z_order) { gl_surface->ScheduleOverlayPlane( image, nullptr, @@ -248,12 +247,12 @@ EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(4); // Create buffers and FakeGlImageNativePixmap. - std::vector<scoped_refptr<FakeGLImageNativePixmap>> fake_gl_image; + std::vector<scoped_refptr<OverlayImageHolder>> fake_overlay_image; for (int i = 0; i < 4; ++i) { auto native_pixmap = surface_factory_->CreateNativePixmap( widget_, nullptr, window_->size_px(), gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::SCANOUT); - fake_gl_image.push_back(base::MakeRefCounted<FakeGLImageNativePixmap>( + fake_overlay_image.push_back(base::MakeRefCounted<OverlayImageHolder>( native_pixmap, window_->size_px())); } @@ -270,32 +269,34 @@ auto swap_id = cbs_helper.GetNextLocalSwapId(); // Associate the image with the next swap id so that we can easily track if // it became free to reuse. - fake_gl_image[0]->AssociateWithSwapId(swap_id); + fake_overlay_image[0]->AssociateWithSwapId(swap_id); // And set it to be busy... - fake_gl_image[0]->SetBusy(true); + fake_overlay_image[0]->SetBusy(true); // Prepare background. - ScheduleOverlayPlane(gl_surface.get(), fake_gl_image[0].get(), + ScheduleOverlayPlane(gl_surface.get(), + fake_overlay_image[0]->GetNativePixmap(), /*z_order=*/INT32_MIN); // Associate the image with the next swap id so that we can easily track if // it became free to reuse. - fake_gl_image[1]->AssociateWithSwapId(swap_id); + fake_overlay_image[1]->AssociateWithSwapId(swap_id); // And set it to be busy... - fake_gl_image[1]->SetBusy(true); + fake_overlay_image[1]->SetBusy(true); // Prepare overlay plane. - ScheduleOverlayPlane(gl_surface.get(), fake_gl_image[1].get(), + ScheduleOverlayPlane(gl_surface.get(), + fake_overlay_image[1]->GetNativePixmap(), /*z_order=*/1); - std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images; - gl_images.push_back(fake_gl_image[0]); - gl_images.push_back(fake_gl_image[1]); + std::vector<scoped_refptr<OverlayImageHolder>> overlay_images; + overlay_images.push_back(fake_overlay_image[0]); + overlay_images.push_back(fake_overlay_image[1]); // And submit each image. They will be executed in FIFO manner. gl_surface->SwapBuffersAsync( base::BindOnce(&CallbacksHelper::FinishSwapBuffersAsync, - base::Unretained(&cbs_helper), swap_id, gl_images), + base::Unretained(&cbs_helper), swap_id, overlay_images), base::BindOnce(&CallbacksHelper::BufferPresented, base::Unretained(&cbs_helper), swap_id), gl::FrameData()); @@ -339,14 +340,14 @@ cbs_helper.ResetLastFinishedSwapId(); - for (const auto& gl_image : fake_gl_image) { + for (const auto& overlay_image : fake_overlay_image) { // All the images except the first one, which was associated with swap // id=0u, must be busy and not displayed. The first one must be displayed. - if (gl_image->GetAssociateWithSwapId() == 0u) { - EXPECT_FALSE(gl_image->busy()); - EXPECT_TRUE(gl_image->displayed()); + if (overlay_image->GetAssociateWithSwapId() == 0u) { + EXPECT_FALSE(overlay_image->busy()); + EXPECT_TRUE(overlay_image->displayed()); } else { - EXPECT_FALSE(gl_image->displayed()); + EXPECT_FALSE(overlay_image->displayed()); } } @@ -362,21 +363,22 @@ auto swap_id = cbs_helper.GetNextLocalSwapId(); // Associate the image with the next swap id so that we can easily track if // it became free to reuse. - fake_gl_image[2]->AssociateWithSwapId(swap_id); + fake_overlay_image[2]->AssociateWithSwapId(swap_id); // And set it to be busy... - fake_gl_image[2]->SetBusy(true); + fake_overlay_image[2]->SetBusy(true); // Prepare overlay plane. - ScheduleOverlayPlane(gl_surface.get(), fake_gl_image[2].get(), + ScheduleOverlayPlane(gl_surface.get(), + fake_overlay_image[2]->GetNativePixmap(), /*z_order=*/1); - std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images; - gl_images.push_back(fake_gl_image[2]); + std::vector<scoped_refptr<OverlayImageHolder>> overlay_images; + overlay_images.push_back(fake_overlay_image[2]); // And submit each image. They will be executed in FIFO manner. gl_surface->SwapBuffersAsync( base::BindOnce(&CallbacksHelper::FinishSwapBuffersAsync, - base::Unretained(&cbs_helper), swap_id, gl_images), + base::Unretained(&cbs_helper), swap_id, overlay_images), base::BindOnce(&CallbacksHelper::BufferPresented, base::Unretained(&cbs_helper), swap_id), gl::FrameData()); @@ -433,21 +435,22 @@ auto swap_id = cbs_helper.GetNextLocalSwapId(); // Associate the image with the next swap id so that we can easily track if // it became free to reuse. - fake_gl_image[3]->AssociateWithSwapId(swap_id); + fake_overlay_image[3]->AssociateWithSwapId(swap_id); // And set it to be busy... - fake_gl_image[3]->SetBusy(true); + fake_overlay_image[3]->SetBusy(true); // Prepare primary plane. - ScheduleOverlayPlane(gl_surface.get(), fake_gl_image[3].get(), + ScheduleOverlayPlane(gl_surface.get(), + fake_overlay_image[3]->GetNativePixmap(), /*z_order=*/0); - std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images; - gl_images.push_back(fake_gl_image[3]); + std::vector<scoped_refptr<OverlayImageHolder>> overlay_images; + overlay_images.push_back(fake_overlay_image[3]); // And submit each image. They will be executed in FIFO manner. gl_surface->SwapBuffersAsync( base::BindOnce(&CallbacksHelper::FinishSwapBuffersAsync, - base::Unretained(&cbs_helper), swap_id, gl_images), + base::Unretained(&cbs_helper), swap_id, overlay_images), base::BindOnce(&CallbacksHelper::BufferPresented, base::Unretained(&cbs_helper), swap_id), gl::FrameData()); @@ -511,12 +514,12 @@ cbs_helper.ResetLastFinishedSwapId(); - for (const auto& gl_image : fake_gl_image) { - if (gl_image->GetAssociateWithSwapId() == 2u) { - EXPECT_TRUE(gl_image->displayed()); - EXPECT_FALSE(gl_image->busy()); + for (const auto& overlay_image : fake_overlay_image) { + if (overlay_image->GetAssociateWithSwapId() == 2u) { + EXPECT_TRUE(overlay_image->displayed()); + EXPECT_FALSE(overlay_image->busy()); } else { - EXPECT_FALSE(gl_image->displayed()); + EXPECT_FALSE(overlay_image->displayed()); } } } @@ -541,12 +544,12 @@ ->SetNoGLFlushForTests(); // Create buffers and FakeGlImageNativePixmap. - std::vector<scoped_refptr<FakeGLImageNativePixmap>> fake_gl_image; + std::vector<scoped_refptr<OverlayImageHolder>> fake_overlay_image; for (int i = 0; i < 5; ++i) { auto native_pixmap = surface_factory_->CreateNativePixmap( widget_, nullptr, window_->size_px(), gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::SCANOUT); - fake_gl_image.push_back(base::MakeRefCounted<FakeGLImageNativePixmap>( + fake_overlay_image.push_back(base::MakeRefCounted<OverlayImageHolder>( native_pixmap, window_->size_px())); } @@ -563,43 +566,46 @@ auto swap_id = cbs_helper.GetNextLocalSwapId(); // Associate the image with the next swap id so that we can easily track if // it became free to reuse. - fake_gl_image[0]->AssociateWithSwapId(swap_id); + fake_overlay_image[0]->AssociateWithSwapId(swap_id); // And set it to be busy... - fake_gl_image[0]->SetBusy(true); + fake_overlay_image[0]->SetBusy(true); // Prepare background. - ScheduleOverlayPlane(gl_surface.get(), fake_gl_image[0].get(), + ScheduleOverlayPlane(gl_surface.get(), + fake_overlay_image[0]->GetNativePixmap(), /*z_order=*/INT32_MIN); // Associate the image with the next swap id so that we can easily track if // it became free to reuse. - fake_gl_image[1]->AssociateWithSwapId(swap_id); + fake_overlay_image[1]->AssociateWithSwapId(swap_id); // And set it to be busy... - fake_gl_image[1]->SetBusy(true); + fake_overlay_image[1]->SetBusy(true); // Prepare primary plane. - ScheduleOverlayPlane(gl_surface.get(), fake_gl_image[1].get(), + ScheduleOverlayPlane(gl_surface.get(), + fake_overlay_image[1]->GetNativePixmap(), /*z_order=*/0); // Associate the image with the next swap id so that we can easily track if // it became free to reuse. - fake_gl_image[2]->AssociateWithSwapId(swap_id); + fake_overlay_image[2]->AssociateWithSwapId(swap_id); // And set it to be busy... - fake_gl_image[2]->SetBusy(true); + fake_overlay_image[2]->SetBusy(true); // Prepare underlay plane. - ScheduleOverlayPlane(gl_surface.get(), fake_gl_image[2].get(), + ScheduleOverlayPlane(gl_surface.get(), + fake_overlay_image[2]->GetNativePixmap(), /*z_order=*/-1); - std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images; - gl_images.push_back(fake_gl_image[0]); - gl_images.push_back(fake_gl_image[1]); - gl_images.push_back(fake_gl_image[2]); + std::vector<scoped_refptr<OverlayImageHolder>> overlay_images; + overlay_images.push_back(fake_overlay_image[0]); + overlay_images.push_back(fake_overlay_image[1]); + overlay_images.push_back(fake_overlay_image[2]); // And submit each image. They will be executed in FIFO manner. gl_surface->SwapBuffersAsync( base::BindOnce(&CallbacksHelper::FinishSwapBuffersAsync, - base::Unretained(&cbs_helper), swap_id, gl_images), + base::Unretained(&cbs_helper), swap_id, overlay_images), base::BindOnce(&CallbacksHelper::BufferPresented, base::Unretained(&cbs_helper), swap_id), gl::FrameData()); @@ -647,14 +653,14 @@ cbs_helper.ResetLastFinishedSwapId(); - for (const auto& gl_image : fake_gl_image) { + for (const auto& overlay_image : fake_overlay_image) { // All the images except the first one, which was associated with swap // id=0u, must be busy and not displayed. The first one must be displayed. - if (gl_image->GetAssociateWithSwapId() == 0u) { - EXPECT_FALSE(gl_image->busy()); - EXPECT_TRUE(gl_image->displayed()); + if (overlay_image->GetAssociateWithSwapId() == 0u) { + EXPECT_FALSE(overlay_image->busy()); + EXPECT_TRUE(overlay_image->displayed()); } else { - EXPECT_FALSE(gl_image->displayed()); + EXPECT_FALSE(overlay_image->displayed()); } } @@ -665,32 +671,34 @@ auto swap_id = cbs_helper.GetNextLocalSwapId(); // Associate the image with the next swap id so that we can easily track if // it became free to reuse. - fake_gl_image[3]->AssociateWithSwapId(swap_id); + fake_overlay_image[3]->AssociateWithSwapId(swap_id); // And set it to be busy... - fake_gl_image[3]->SetBusy(true); + fake_overlay_image[3]->SetBusy(true); // Prepare primary plane. - ScheduleOverlayPlane(gl_surface.get(), fake_gl_image[3].get(), + ScheduleOverlayPlane(gl_surface.get(), + fake_overlay_image[3]->GetNativePixmap(), /*z_order=*/0); // Associate the image with the next swap id so that we can easily track if // it became free to reuse. - fake_gl_image[4]->AssociateWithSwapId(swap_id); + fake_overlay_image[4]->AssociateWithSwapId(swap_id); // And set it to be busy... - fake_gl_image[4]->SetBusy(true); + fake_overlay_image[4]->SetBusy(true); // Prepare overlay plane. - ScheduleOverlayPlane(gl_surface.get(), fake_gl_image[4].get(), + ScheduleOverlayPlane(gl_surface.get(), + fake_overlay_image[4]->GetNativePixmap(), /*z_order=*/1); - std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images; - gl_images.push_back(fake_gl_image[3]); - gl_images.push_back(fake_gl_image[4]); + std::vector<scoped_refptr<OverlayImageHolder>> overlay_images; + overlay_images.push_back(fake_overlay_image[3]); + overlay_images.push_back(fake_overlay_image[4]); // And submit each image. They will be executed in FIFO manner. gl_surface->SwapBuffersAsync( base::BindOnce(&CallbacksHelper::FinishSwapBuffersAsync, - base::Unretained(&cbs_helper), swap_id, gl_images), + base::Unretained(&cbs_helper), swap_id, overlay_images), base::BindOnce(&CallbacksHelper::BufferPresented, base::Unretained(&cbs_helper), swap_id), gl::FrameData()); @@ -776,12 +784,12 @@ // SwapCompletionCallback should run. EXPECT_EQ(cbs_helper.GetLastFinishedSwapId(), 1u); - for (const auto& gl_image : fake_gl_image) { - if (gl_image->GetAssociateWithSwapId() == 1u) { - EXPECT_TRUE(gl_image->displayed()); - EXPECT_FALSE(gl_image->busy()); + for (const auto& overlay_image : fake_overlay_image) { + if (overlay_image->GetAssociateWithSwapId() == 1u) { + EXPECT_TRUE(overlay_image->displayed()); + EXPECT_FALSE(overlay_image->busy()); } else { - EXPECT_FALSE(gl_image->displayed()); + EXPECT_FALSE(overlay_image->displayed()); } } } @@ -935,11 +943,11 @@ window_->size_px().height())); // Create buffer and FakeGlImageNativePixmap. - std::vector<scoped_refptr<FakeGLImageNativePixmap>> fake_gl_image; + std::vector<scoped_refptr<OverlayImageHolder>> fake_overlay_image; auto native_pixmap = surface_factory_->CreateNativePixmap( widget_, nullptr, test_buffer_size, gfx::BufferFormat::BGRA_8888, gfx::BufferUsage::SCANOUT); - fake_gl_image.push_back(base::MakeRefCounted<FakeGLImageNativePixmap>( + fake_overlay_image.push_back(base::MakeRefCounted<OverlayImageHolder>( native_pixmap, test_buffer_size)); auto* root_surface = server_.GetObject<wl::MockSurface>( @@ -954,26 +962,26 @@ auto swap_id = cbs_helper.GetNextLocalSwapId(); // Associate the image with the next swap id so that we can easily track if // it became free to reuse. - fake_gl_image[0]->AssociateWithSwapId(swap_id); + fake_overlay_image[0]->AssociateWithSwapId(swap_id); // And set it to be busy... - fake_gl_image[0]->SetBusy(true); + fake_overlay_image[0]->SetBusy(true); // Prepare background. gl_surface->ScheduleOverlayPlane( - fake_gl_image[0].get(), nullptr, + fake_overlay_image[0]->GetNativePixmap(), nullptr, gfx::OverlayPlaneData( INT32_MIN, gfx::OverlayTransform::OVERLAY_TRANSFORM_ROTATE_270, gfx::RectF(window_->GetBoundsInPixels()), crop_uv, false, gfx::Rect(test_buffer_dmg), 1.0f, gfx::OverlayPriorityHint::kNone, gfx::RRectF(), gfx::ColorSpace::CreateSRGB(), absl::nullopt)); - std::vector<scoped_refptr<FakeGLImageNativePixmap>> gl_images; - gl_images.push_back(fake_gl_image[0]); + std::vector<scoped_refptr<OverlayImageHolder>> overlay_images; + overlay_images.push_back(fake_overlay_image[0]); // And submit each image. They will be executed in FIFO manner. gl_surface->SwapBuffersAsync( base::BindOnce(&CallbacksHelper::FinishSwapBuffersAsync, - base::Unretained(&cbs_helper), swap_id, gl_images), + base::Unretained(&cbs_helper), swap_id, overlay_images), base::BindOnce(&CallbacksHelper::BufferPresented, base::Unretained(&cbs_helper), swap_id), gl::FrameData());
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc index b5b2c94..4937df8 100644 --- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc +++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -1032,6 +1032,9 @@ const gfx::Point& location, int operation, ui::mojom::DragEventSource source) { + if (!content_window_) + return; + views::RunShellDrag(content_window_, std::move(data), location, operation, source); }
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm index 50c4b63..90abe2e1 100644 --- a/ui/views/widget/native_widget_mac.mm +++ b/ui/views/widget/native_widget_mac.mm
@@ -738,6 +738,8 @@ const gfx::Point& location, int operation, ui::mojom::DragEventSource source) { + if (!ns_window_host_) + return; ns_window_host_->drag_drop_client()->StartDragAndDrop(view, std::move(data), operation, source); }
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc index f2442cb..1209461 100644 --- a/ui/views/widget/widget.cc +++ b/ui/views/widget/widget.cc
@@ -283,6 +283,7 @@ // static void Widget::ReparentNativeView(gfx::NativeView native_view, gfx::NativeView new_parent) { + DCHECK(native_view); internal::NativeWidgetPrivate::ReparentNativeView(native_view, new_parent); Widget* child_widget = GetWidgetForNativeView(native_view); Widget* parent_widget = @@ -640,6 +641,9 @@ const gfx::Vector2d& drag_offset, MoveLoopSource source, MoveLoopEscapeBehavior escape_behavior) { + if (!native_widget_) + return MoveLoopResult::kCanceled; + return native_widget_->RunMoveLoop(drag_offset, source, escape_behavior); }
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc index 43d2c75..066e37af 100644 --- a/ui/views/widget/widget_unittest.cc +++ b/ui/views/widget/widget_unittest.cc
@@ -18,6 +18,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/accessibility/ax_node_data.h" +#include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h" #include "ui/base/hit_test.h" #include "ui/color/color_id.h" #include "ui/color/color_provider.h" @@ -1174,6 +1175,16 @@ widget()->NotifyWillRemoveView(widget()->non_client_view()); } +TEST_P(WidgetWithDestroyedNativeViewTest, parent) { + widget()->parent(); +} + +TEST_P(WidgetWithDestroyedNativeViewTest, + RegisterPaintAsActiveChangedCallback) { + auto subscription = + widget()->RegisterPaintAsActiveChangedCallback(base::DoNothing()); +} + TEST_P(WidgetWithDestroyedNativeViewTest, ReleaseCapture) { widget()->ReleaseCapture(); } @@ -1182,10 +1193,26 @@ widget()->ReorderNativeViews(); } +TEST_P(WidgetWithDestroyedNativeViewTest, ReparentNativeView) { + EXPECT_DCHECK_DEATH( + Widget::ReparentNativeView(widget()->GetNativeView(), nullptr)); +} + TEST_P(WidgetWithDestroyedNativeViewTest, Restore) { widget()->Restore(); } +TEST_P(WidgetWithDestroyedNativeViewTest, RunMoveLoop) { + widget()->RunMoveLoop(gfx::Vector2d(), views::Widget::MoveLoopSource::kMouse, + views::Widget::MoveLoopEscapeBehavior::kHide); +} + +TEST_P(WidgetWithDestroyedNativeViewTest, RunShellDrag) { + std::unique_ptr<OSExchangeData> data(std::make_unique<OSExchangeData>()); + widget()->RunShellDrag(nullptr, std::move(data), gfx::Point(), 0, + ui::mojom::DragEventSource::kMouse); +} + TEST_P(WidgetWithDestroyedNativeViewTest, SchedulePaintInRect) { widget()->SchedulePaintInRect(gfx::Rect(0, 0, 1, 2)); }