diff --git a/DEPS b/DEPS index ccbc679..4015fb75 100644 --- a/DEPS +++ b/DEPS
@@ -325,7 +325,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': 'd5b2b322948ba9779c01eb2ea76aad4542a7da45', + 'skia_revision': 'af3cd504644942bf0062461be22fec7785be55b3', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -333,7 +333,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '36111b25fe13a46896c18d8614191889fbd2a032', + 'angle_revision': '377216093693e092dd886d12d654f30605165cbf', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -341,7 +341,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': '7bac6ae44fef54e80c086f95d0a074022051bd57', + 'pdfium_revision': 'ce0337c0880cb6c3d7bf213b828aee57a3020530', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. @@ -396,7 +396,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': '2da767c6c13e331107f4c8026fd4584a7a85a214', + 'catapult_revision': 'c5ac2a64a6688f72cd857b2f6f3a01d7379ae83d', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling CrossBench # and whatever else without interference from each other. @@ -412,7 +412,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling devtools-frontend # and whatever else without interference from each other. - 'devtools_frontend_revision': '847d9112cf89902d5ee7f37b80887abdc582adf4', + 'devtools_frontend_revision': 'd1a36c004c4ab384ef857a71dc3413d50a7653ee', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libprotobuf-mutator # and whatever else without interference from each other. @@ -452,7 +452,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': '9543f74739118a853dd5e5a46297f5442c3352f8', + 'dawn_revision': '7972a10aa8f3686c9b9d7f69fc9b18d9db53d3e7', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -800,7 +800,7 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - '0a29c4d421df48939cd95bf6ef7581dc2bcace0a', + '39571ae0a7fbb42f9baba19fa85a7409ccf2cd93', 'condition': 'checkout_android and checkout_src_internal', }, @@ -989,7 +989,7 @@ 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': 'naNt8xoIXXo_9dIBIcVTLD4u9Qq-edLsdJ3hzMAy1IQC', + 'version': '7Pq-5ILyMsBaCykhDCAcLI9KkdBnuD9ugOF6Q03YVpUC', }, ], 'condition': 'checkout_android', @@ -1246,7 +1246,7 @@ Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), 'src/third_party/devtools-frontend-internal': { - 'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'e588d2222bdb1dbc05a8f293a18604e7421f1ea4', + 'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'f250c7663bc0220d167c537a233c15a72630fe30', 'condition': 'checkout_src_internal', }, @@ -1701,7 +1701,7 @@ Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + 'db956674bbdfbaab5acdd3fdb4117c2fef5527e9', 'src/third_party/openscreen/src': - Var('chromium_git') + '/openscreen' + '@' + 'a3d689dae21d8d4b1f2c9846370bbf74f6d054e0', + Var('chromium_git') + '/openscreen' + '@' + '9fa061bbb71807041927af9952aae249014160f1', 'src/third_party/openxr/src': { 'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + 'bf21ccb1007bb531b45d9978919a56ea5059c245', @@ -1718,7 +1718,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + 'd26e33e5ce7dcb680b43053be9453a6a3da19642', + Var('android_git') + '/platform/external/perfetto.git' + '@' + 'f90d61c166b857309dcbc12e5a7c328578e5bd51', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1903,7 +1903,7 @@ Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '6c8361e98f1daba65902f5e2fc1297893ac14b67', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + 'e1298afaa66e1fa88bd1e257d93f6af9db6e3e76', + Var('webrtc_git') + '/src.git' + '@' + 'd877589e16f2ad93563d91f4dc54c67d9a17a920', # 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. @@ -1915,6 +1915,13 @@ 'condition': 'checkout_linux', }, + # A conformance-suite developed by canonical for the mir wayland server. + # Required to compile exo_wlcs on chromeos checkouts. + 'src/third_party/wlcs/src': { + 'url': Var('chromium_git') + '/external/github.com/MirServer/wlcs.git' + '@' + '2930ad4b5ca602446ad211b49fb1827303ce9f4b', + 'condition': 'checkout_chromeos', + }, + 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d', 'condition': 'checkout_linux', @@ -1930,7 +1937,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/linux-amd64', - 'version': 'uu72l9rwYReKqlKTHTUE26N6gMAt6YHvdysfn6rcAvgC', + 'version': 'iQ7zKud-gha6r9hEdwqYWRdOpeAs6gFfDxnviDUt4FQC', }, ], 'dep_type': 'cipd', @@ -1940,7 +1947,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/windows-amd64', - 'version': 'DxsfuVWQrwfkUxTCKXzhO_Wh4OYOLWM-sSQpfx92DxwC', + 'version': 'we56UJIWxJJ2GkQ_ne0o3oGAr7FBJa5T5Jr1xguLn-gC', }, ], 'dep_type': 'cipd', @@ -1951,7 +1958,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/mac-amd64', - 'version': 'EPIsKRgwINCn8DCzCmRC1ZH3EivSehuq2ymx_qN6MhMC', + 'version': '9Wfje1bt82IO9pJokAt9lboy59X_Pe-s0b4EpmH7RT4C', }, ], 'dep_type': 'cipd', @@ -1962,7 +1969,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/mac-arm64', - 'version': 'NcrJgtTlI-mdqmPTl4LBprsY9nhx_5nzK08RLKJG9CAC', + 'version': 'zihT2Lk2afg0XzIZozuGcZXWv7RJujaDEi_6q7QL4DgC', }, ], 'dep_type': 'cipd', @@ -1973,7 +1980,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': Var('chrome_git') + '/chrome/src-internal.git@02aabbc7ffc30397435d88760e7831cd81f4af95', + 'url': Var('chrome_git') + '/chrome/src-internal.git@0b25bff5936a5effb5ad49ad7a0e3117fc7d3046', 'condition': 'checkout_src_internal', }, @@ -4178,7 +4185,7 @@ 'src/ios_internal': { 'url': '{chrome_git}/chrome/ios_internal.git' + '@' + - 'c4c13e9e59ee894857c7e836810922482e29704b', + '6b1f4ed31be2e8831073e1fa54e2f7b8402dfa48', 'condition': 'checkout_src_internal and checkout_ios', },
diff --git a/WATCHLISTS b/WATCHLISTS index cde3c87d..7320bb97 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -1242,8 +1242,8 @@ 'input_device_settings': { 'filepath': 'ash/system/input_device_settings/|'\ 'chrome/browser/resources/settings/chromeos/device_page/|'\ - 'chrome/browser/ui/webui/settings/ash/device_section.*'\ - 'chrome/browser/ui/webui/settings/ash/input_device_settings/|', + 'chrome/browser/ui/webui/settings/ash/device_section.*|'\ + 'chrome/browser/ui/webui/settings/ash/input_device_settings/', }, 'input_devices': { 'filepath': 'ui/events/devices/',
diff --git a/ash/accelerators/accelerator_commands.cc b/ash/accelerators/accelerator_commands.cc index eca6137..2359cb4 100644 --- a/ash/accelerators/accelerator_commands.cc +++ b/ash/accelerators/accelerator_commands.cc
@@ -489,10 +489,6 @@ return display::Screen::GetScreen()->GetNumDisplays() > 1; } -bool CanToggleCalendar() { - return features::IsCalendarViewEnabled(); -} - bool CanToggleDictation() { return Shell::Get()->accessibility_controller()->dictation().enabled(); }
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc index e95a50cd..2a4a4b8 100644 --- a/ash/accelerators/accelerator_controller_impl.cc +++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -680,7 +680,7 @@ accelerator, previous_accelerator, accelerator_history_->currently_pressed_keys()); case TOGGLE_CALENDAR: - return accelerators::CanToggleCalendar(); + return true; case TOGGLE_CAPS_LOCK: return CanHandleToggleCapsLock( accelerator, previous_accelerator,
diff --git a/ash/accelerators/ash_accelerator_configuration.cc b/ash/accelerators/ash_accelerator_configuration.cc index 642c562..053c2570 100644 --- a/ash/accelerators/ash_accelerator_configuration.cc +++ b/ash/accelerators/ash_accelerator_configuration.cc
@@ -183,7 +183,17 @@ } AcceleratorConfigResult AshAcceleratorConfiguration::RestoreAllDefaults() { - return AcceleratorConfigResult::kActionLocked; + accelerators_.clear(); + id_to_accelerators_.clear(); + accelerator_to_id_.Clear(); + + // TODO(jimmyxgong): Reset the prefs here too. + id_to_accelerators_ = default_id_to_accelerators_cache_; + accelerator_to_id_ = default_accelerators_to_id_cache_; + + UpdateAndNotifyAccelerators(); + + return AcceleratorConfigResult::kSuccess; } void AshAcceleratorConfiguration::Initialize() {
diff --git a/ash/accelerators/ash_accelerator_configuration_unittest.cc b/ash/accelerators/ash_accelerator_configuration_unittest.cc index ce0312a..ced38cd2 100644 --- a/ash/accelerators/ash_accelerator_configuration_unittest.cc +++ b/ash/accelerators/ash_accelerator_configuration_unittest.cc
@@ -494,4 +494,87 @@ ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators()); EXPECT_EQ(2, observer_.num_times_accelerator_updated_called()); } + +TEST_F(AshAcceleratorConfigurationTest, RemoveAndRestoreDefault) { + EXPECT_EQ(0, observer_.num_times_accelerator_updated_called()); + const AcceleratorData test_data[] = { + {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN, + SWITCH_TO_LAST_USED_IME}, + {/*trigger_on_press=*/true, ui::VKEY_SPACE, + ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME}, + {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN, + CYCLE_FORWARD_MRU}, + {/*trigger_on_press=*/true, ui::VKEY_TAB, + ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU}, + }; + + config_->Initialize(test_data); + + ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators()); + EXPECT_EQ(1, observer_.num_times_accelerator_updated_called()); + + // Remove `SWITCH_TO_LAST_USE_IME`. + const AcceleratorData updated_test_data[] = { + {/*trigger_on_press=*/true, ui::VKEY_SPACE, + ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME}, + {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN, + CYCLE_FORWARD_MRU}, + {/*trigger_on_press=*/true, ui::VKEY_TAB, + ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU}, + }; + AcceleratorConfigResult result = config_->RemoveAccelerator( + SWITCH_TO_LAST_USED_IME, + ui::Accelerator(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN)); + EXPECT_EQ(AcceleratorConfigResult::kSuccess, result); + + // Compare expected accelerators and that the observer was fired after + // removing an accelerator. + ExpectAllAcceleratorsEqual(updated_test_data, config_->GetAllAccelerators()); + EXPECT_EQ(2, observer_.num_times_accelerator_updated_called()); + + // Restore all defaults. + result = config_->RestoreAllDefaults(); + EXPECT_EQ(AcceleratorConfigResult::kSuccess, result); + + // Expect accelerators to revert back to the default state and observer + // is called. + ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators()); + EXPECT_EQ(3, observer_.num_times_accelerator_updated_called()); +} + +TEST_F(AshAcceleratorConfigurationTest, RestoreAllConsecutively) { + EXPECT_EQ(0, observer_.num_times_accelerator_updated_called()); + const AcceleratorData test_data[] = { + {/*trigger_on_press=*/true, ui::VKEY_SPACE, ui::EF_CONTROL_DOWN, + SWITCH_TO_LAST_USED_IME}, + {/*trigger_on_press=*/true, ui::VKEY_SPACE, + ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, SWITCH_TO_LAST_USED_IME}, + {/*trigger_on_press=*/true, ui::VKEY_TAB, ui::EF_ALT_DOWN, + CYCLE_FORWARD_MRU}, + {/*trigger_on_press=*/true, ui::VKEY_TAB, + ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU}, + }; + + config_->Initialize(test_data); + + ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators()); + EXPECT_EQ(1, observer_.num_times_accelerator_updated_called()); + + // Restore all defaults, even though no change was made. + AcceleratorConfigResult reset_result = config_->RestoreAllDefaults(); + EXPECT_EQ(AcceleratorConfigResult::kSuccess, reset_result); + + // Nothing should have changed, but the observer is called. + ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators()); + EXPECT_EQ(2, observer_.num_times_accelerator_updated_called()); + + // Restore all defaults again, even though no change was made. + reset_result = config_->RestoreAllDefaults(); + EXPECT_EQ(AcceleratorConfigResult::kSuccess, reset_result); + + // Nothing should have changed, but the observer is called. + ExpectAllAcceleratorsEqual(test_data, config_->GetAllAccelerators()); + EXPECT_EQ(3, observer_.num_times_accelerator_updated_called()); +} + } // namespace ash
diff --git a/ash/ambient/metrics/ambient_multi_screen_metrics_recorder.cc b/ash/ambient/metrics/ambient_multi_screen_metrics_recorder.cc index 790bf40..d39bd1a 100644 --- a/ash/ambient/metrics/ambient_multi_screen_metrics_recorder.cc +++ b/ash/ambient/metrics/ambient_multi_screen_metrics_recorder.cc
@@ -86,8 +86,7 @@ *largest_timestamp_offset); break; case AmbientTheme::kSlideshow: - case AmbientTheme::kVideoNewMexico: - case AmbientTheme::kVideoClouds: + case AmbientTheme::kVideo: LOG(DFATAL) << "Should not be recording animation metrics for " << ToString(theme_); break;
diff --git a/ash/app_list/app_list_item_util.cc b/ash/app_list/app_list_item_util.cc index bb40d1b..f85bd944 100644 --- a/ash/app_list/app_list_item_util.cc +++ b/ash/app_list/app_list_item_util.cc
@@ -4,6 +4,11 @@ #include "ash/app_list/app_list_item_util.h" +#include <string> + +#include "base/no_destructor.h" +#include "base/pickle.h" + namespace ash { const ui::ClipboardFormatType& GetAppItemFormatType() { @@ -13,4 +18,20 @@ return *format; } +absl::optional<std::string> GetAppIdFromDropData( + const ui::OSExchangeData& data) { + base::Pickle data_pickle; + if (!data.GetPickledData(GetAppItemFormatType(), &data_pickle)) { + return absl::nullopt; + } + + std::string app_id; + base::PickleIterator iter(data_pickle); + if (!iter.ReadString(&app_id)) { + return absl::nullopt; + } + + return app_id; +} + } // namespace ash
diff --git a/ash/app_list/app_list_item_util.h b/ash/app_list/app_list_item_util.h index 63fd6d4..7770e53e 100644 --- a/ash/app_list/app_list_item_util.h +++ b/ash/app_list/app_list_item_util.h
@@ -5,13 +5,22 @@ #ifndef ASH_APP_LIST_APP_LIST_ITEM_UTIL_H_ #define ASH_APP_LIST_APP_LIST_ITEM_UTIL_H_ +#include <string> + +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/base/clipboard/clipboard_format_type.h" #include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/os_exchange_data.h" namespace ash { const ui::ClipboardFormatType& GetAppItemFormatType(); +// Retrieve and app id carried in a OSExchangeData object during app list drag +// and drop actions. +absl::optional<std::string> GetAppIdFromDropData( + const ui::OSExchangeData& data); + } // namespace ash #endif // ASH_APP_LIST_APP_LIST_ITEM_UTIL_H_
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc index 205eceb..7b6c335 100644 --- a/ash/app_list/views/apps_grid_view.cc +++ b/ash/app_list/views/apps_grid_view.cc
@@ -44,7 +44,6 @@ #include "base/metrics/histogram_macros.h" #include "base/metrics/user_metrics.h" #include "base/metrics/user_metrics_action.h" -#include "base/pickle.h" #include "base/ranges/algorithm.h" #include "base/time/time.h" #include "ui/aura/window.h" @@ -746,14 +745,16 @@ // An EndDrag can be received during a reparent via a model change. This // is always a cancel and needs to be forwarded to the folder. if (cancel) { + DCHECK_EQ(!reparent_drag_cancellation_, is_drag_drop_refactor_enabled); if (reparent_drag_cancellation_) { std::move(reparent_drag_cancellation_).Run(); + return; } } else { UpdateDropTargetRegion(); EndDragFromReparentItemInRootLevel(nullptr, false, false, nullptr); + return; } - return; } if (!cancel && was_dragging) { @@ -1097,6 +1098,11 @@ return false; } + auto app_id = GetAppIdFromDropData(data); + if (app_id->empty()) { + return false; + } + return data.HasCustomFormat(GetAppItemFormatType()); } @@ -1127,7 +1133,7 @@ dragging_for_reparent_item_ = true; folder_delegate_->Close(); } - drag_item_ = nullptr; + CancelDragWithNoDropAnimation(); } void AppsGridView::OnDragEntered(const ui::DropTargetEvent& event) { @@ -1141,19 +1147,12 @@ return; } - std::string drag_item_id; - - base::Pickle data_pickle; - if (!event.data().GetPickledData(GetAppItemFormatType(), &data_pickle)) { + auto app_id = GetAppIdFromDropData(event.data()); + if (app_id->empty()) { return; } - base::PickleIterator iter(data_pickle); - if (!iter.ReadString(&drag_item_id)) { - return; - } - - drag_item_ = AppListModelProvider::Get()->model()->FindItem(drag_item_id); + drag_item_ = AppListModelProvider::Get()->model()->FindItem(app_id.value()); if (!drag_item_) { return; }
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc index bfa3d6a..bc746a72 100644 --- a/ash/app_list/views/apps_grid_view_unittest.cc +++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -572,6 +572,14 @@ const int selected_page = GetSelectedPage(apps_grid_view); GridIndex index(selected_page, row * apps_grid_view->cols() + column); AppListItemView* view = test_api.GetViewAtIndex(index); + + InitiateDragForView(pointer, view, apps_grid_view); + return view; + } + + void InitiateDragForView(AppsGridView::Pointer pointer, + AppListItemView* view, + AppsGridView* apps_grid_view) { DCHECK(view); gfx::Point from = view->GetBoundsInScreen().CenterPoint(); @@ -602,7 +610,6 @@ // target OnDragUpdate(). current_drag_location_ = from + gfx::Vector2d(10, 10); UpdateDragInScreen(pointer, current_drag_location_.value(), 2); - return view; } void UpdateDragInScreen(AppsGridView::Pointer pointer, @@ -620,6 +627,7 @@ generator->MoveMouseTo(drag_increment_point); } } + current_drag_location_ = to_in_screen; } // Updates the drag from the current drag location to the destination point @@ -4255,19 +4263,13 @@ EXPECT_FALSE(item_view->HasFocus()); } -TEST_P(AppsGridViewDragLegacyTest, DragAndPinItemToShelf) { +TEST_P(AppsGridViewDragTest, DragAndPinItemToShelf) { model_->PopulateApps(2); UpdateLayout(); - AppListItemView* const item_view = GetItemViewInTopLevelGrid(1); + AppListItemView* const item_view = InitiateDragForItemAtCurrentPageAt( + AppsGridView::MOUSE, 0, 1, apps_grid_view_); - auto* generator = GetEventGenerator(); - generator->MoveMouseTo(item_view->GetBoundsInScreen().CenterPoint()); - generator->PressLeftButton(); - if (!use_drag_drop_refactor()) { - item_view->FireMouseDragTimerForTest(); - } - generator->MoveMouseBy(10, 10); MaybeCheckHaptickEventsCount(1); // Verify that item drag has started. @@ -4277,19 +4279,24 @@ // Shelf should start handling the drag if it moves within its bounds. auto* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting(); - generator->MoveMouseTo(shelf_view->GetBoundsInScreen().left_center()); - ASSERT_TRUE(apps_grid_view_->FireDragToShelfTimerForTest()); + UpdateDragInScreen( + AppsGridView::MOUSE, + shelf_view->GetBoundsInScreen().left_center() + gfx::Vector2d(5, 5), + /*steps=*/1); + if (!use_drag_drop_refactor()) { + ASSERT_TRUE(apps_grid_view_->FireDragToShelfTimerForTest()); + } EXPECT_EQ("Item 1", shelf_view->drag_and_drop_shelf_id().app_id); // Releasing drag over shelf should pin the dragged app. - generator->ReleaseLeftButton(); + EndDrag(); EXPECT_TRUE(ShelfModel::Get()->IsAppPinned("Item 1")); EXPECT_EQ("Item 1", ShelfModel::Get()->items()[0].id.app_id); MaybeCheckHaptickEventsCount(1); } -TEST_P(AppsGridViewDragLegacyTest, DragAndPinNotInitiallyVisibleItemToShelf) { +TEST_P(AppsGridViewDragTest, DragAndPinNotInitiallyVisibleItemToShelf) { // Add more apps to the root apps grid. model_->PopulateApps(50); UpdateLayout(); @@ -4307,13 +4314,7 @@ ASSERT_TRUE(apps_grid_view_->GetWidget()->GetWindowBoundsInScreen().Contains( item_view->GetBoundsInScreen())); - auto* generator = GetEventGenerator(); - generator->MoveMouseTo(item_view->GetBoundsInScreen().CenterPoint()); - generator->PressLeftButton(); - if (!use_drag_drop_refactor()) { - item_view->FireMouseDragTimerForTest(); - } - generator->MoveMouseBy(10, 10); + InitiateDragForView(AppsGridView::MOUSE, item_view, apps_grid_view_); MaybeCheckHaptickEventsCount(1); // Verify app list item drag has started. @@ -4323,19 +4324,24 @@ // Shelf should start handling the drag if it moves within its bounds. auto* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting(); - generator->MoveMouseTo(shelf_view->GetBoundsInScreen().left_center()); - ASSERT_TRUE(apps_grid_view_->FireDragToShelfTimerForTest()); + UpdateDragInScreen( + AppsGridView::MOUSE, + shelf_view->GetBoundsInScreen().left_center() + gfx::Vector2d(5, 5), + /*steps=*/1); + if (!use_drag_drop_refactor()) { + ASSERT_TRUE(apps_grid_view_->FireDragToShelfTimerForTest()); + } EXPECT_EQ("Item 40", shelf_view->drag_and_drop_shelf_id().app_id); // Releasing drag over shelf should pin the dragged app. - generator->ReleaseLeftButton(); + EndDrag(); MaybeCheckHaptickEventsCount(1); EXPECT_TRUE(ShelfModel::Get()->IsAppPinned("Item 40")); EXPECT_EQ("Item 40", ShelfModel::Get()->items()[0].id.app_id); } -TEST_P(AppsGridViewDragLegacyTest, DragItemToAndFromShelf) { +TEST_P(AppsGridViewDragTest, DragItemToAndFromShelf) { model_->PopulateApps(2); UpdateLayout(); @@ -4351,9 +4357,13 @@ // Shelf should start handling the drag if it moves within its bounds. auto* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting(); - UpdateDragInScreen(AppsGridView::MOUSE, - shelf_view->GetBoundsInScreen().left_center()); - ASSERT_TRUE(apps_grid_view_->FireDragToShelfTimerForTest()); + UpdateDragInScreen( + AppsGridView::MOUSE, + shelf_view->GetBoundsInScreen().left_center() + gfx::Vector2d(5, 5), + /*steps=*/1); + if (!use_drag_drop_refactor()) { + ASSERT_TRUE(apps_grid_view_->FireDragToShelfTimerForTest()); + } EXPECT_EQ("Item 1", shelf_view->drag_and_drop_shelf_id().app_id); @@ -4368,7 +4378,7 @@ EXPECT_TRUE(ShelfModel::Get()->items().empty()); } -TEST_P(AppsGridViewDragLegacyTest, DragAndPinItemFromFolderToShelf) { +TEST_P(AppsGridViewDragTest, DragAndPinItemFromFolderToShelf) { // Creates a folder item - the folder size was chosen arbitrarily. model_->CreateAndPopulateFolderWithApps(5); // Add more apps to the root apps grid. @@ -4378,16 +4388,9 @@ // Open the folder. test_api_->PressItemAt(0); - AppListItemView* const item_view = - GetItemViewInAppsGridAt(1, folder_apps_grid_view()); + AppListItemView* const item_view = InitiateDragForItemAtCurrentPageAt( + AppsGridView::MOUSE, 0, 1, folder_apps_grid_view()); - auto* generator = GetEventGenerator(); - generator->MoveMouseTo(item_view->GetBoundsInScreen().CenterPoint()); - generator->PressLeftButton(); - if (!use_drag_drop_refactor()) { - item_view->FireMouseDragTimerForTest(); - } - generator->MoveMouseBy(10, 10); MaybeCheckHaptickEventsCount(1); // Verify app list item drag has started. @@ -4395,9 +4398,11 @@ ASSERT_TRUE(folder_apps_grid_view()->IsDragging()); ASSERT_EQ(item_view->item(), folder_apps_grid_view()->drag_item()); - generator->MoveMouseTo( + UpdateDragInScreen( + AppsGridView::MOUSE, app_list_folder_view()->GetBoundsInScreen().right_center() + - gfx::Vector2d(20, 0)); + gfx::Vector2d(20, 0), + /*steps=*/1); // Fire the reparent timer that should be started when an item is dragged out // of folder bounds. @@ -4405,20 +4410,24 @@ // Shelf should start handling the drag if it moves within its bounds. auto* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting(); - generator->MoveMouseTo(shelf_view->GetBoundsInScreen().left_center()); - ASSERT_TRUE(folder_apps_grid_view()->FireDragToShelfTimerForTest()); + UpdateDragInScreen( + AppsGridView::MOUSE, + shelf_view->GetBoundsInScreen().left_center() + gfx::Vector2d(5, 5), + /*steps=*/1); + if (!use_drag_drop_refactor()) { + ASSERT_TRUE(folder_apps_grid_view()->FireDragToShelfTimerForTest()); + } EXPECT_EQ("Item 1", shelf_view->drag_and_drop_shelf_id().app_id); // Releasing drag over shelf should pin the dragged app. - generator->ReleaseLeftButton(); + EndDrag(); MaybeCheckHaptickEventsCount(1); EXPECT_TRUE(ShelfModel::Get()->IsAppPinned("Item 1")); EXPECT_EQ("Item 1", ShelfModel::Get()->items()[0].id.app_id); } -TEST_P(AppsGridViewDragLegacyTest, - DragAndPinNotInitiallyVisibleFolderItemToShelf) { +TEST_P(AppsGridViewDragTest, DragAndPinNotInitiallyVisibleFolderItemToShelf) { model_->CreateAndPopulateFolderWithApps(kMaxItemsInFolder); UpdateLayout(); @@ -4440,13 +4449,7 @@ ASSERT_TRUE(app_list_folder_view()->GetBoundsInScreen().Contains( item_view->GetBoundsInScreen())); - auto* generator = GetEventGenerator(); - generator->MoveMouseTo(item_view->GetBoundsInScreen().CenterPoint()); - generator->PressLeftButton(); - if (!use_drag_drop_refactor()) { - item_view->FireMouseDragTimerForTest(); - } - generator->MoveMouseBy(10, 10); + InitiateDragForView(AppsGridView::MOUSE, item_view, apps_grid_view_); MaybeCheckHaptickEventsCount(1); // Verify app list item drag has started. @@ -4454,9 +4457,11 @@ ASSERT_TRUE(folder_apps_grid_view()->IsDragging()); ASSERT_EQ(item_view->item(), folder_apps_grid_view()->drag_item()); - generator->MoveMouseTo( + UpdateDragInScreen( + AppsGridView::MOUSE, app_list_folder_view()->GetBoundsInScreen().right_center() + - gfx::Vector2d(20, 0)); + gfx::Vector2d(20, 0), + /*steps=*/1); // Fire the reparent timer that should be started when an item is dragged out // of folder bounds. @@ -4464,20 +4469,25 @@ // Shelf should start handling the drag if it moves within its bounds. auto* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting(); - generator->MoveMouseTo(shelf_view->GetBoundsInScreen().left_center()); - ASSERT_TRUE(folder_apps_grid_view()->FireDragToShelfTimerForTest()); + UpdateDragInScreen( + AppsGridView::MOUSE, + shelf_view->GetBoundsInScreen().left_center() + gfx::Vector2d(5, 5), + /*steps=*/1); + if (!use_drag_drop_refactor()) { + ASSERT_TRUE(folder_apps_grid_view()->FireDragToShelfTimerForTest()); + } EXPECT_EQ("Item 30", shelf_view->drag_and_drop_shelf_id().app_id); // Releasing drag over shelf should pin the dragged app. - generator->ReleaseLeftButton(); + EndDrag(); MaybeCheckHaptickEventsCount(1); EXPECT_TRUE(ShelfModel::Get()->IsAppPinned("Item 30")); EXPECT_EQ("Item 30", ShelfModel::Get()->items()[0].id.app_id); } -TEST_P(AppsGridViewDragLegacyTest, DragAnItemFromFolderToAndFromShelf) { +TEST_P(AppsGridViewDragTest, DragAnItemFromFolderToAndFromShelf) { // Creates a folder item - the folder size was chosen arbitrarily. model_->CreateAndPopulateFolderWithApps(5); // Add more apps to the root apps grid. @@ -4487,16 +4497,8 @@ // Open the folder. test_api_->PressItemAt(0); - AppListItemView* const item_view = - GetItemViewInAppsGridAt(1, folder_apps_grid_view()); - - auto* generator = GetEventGenerator(); - generator->MoveMouseTo(item_view->GetBoundsInScreen().CenterPoint()); - generator->PressLeftButton(); - if (!use_drag_drop_refactor()) { - item_view->FireMouseDragTimerForTest(); - } - generator->MoveMouseBy(10, 10); + AppListItemView* const item_view = InitiateDragForItemAtCurrentPageAt( + AppsGridView::MOUSE, 0, 1, folder_apps_grid_view()); MaybeCheckHaptickEventsCount(1); // Verify app list item drag has started. @@ -4504,9 +4506,11 @@ ASSERT_TRUE(folder_apps_grid_view()->IsDragging()); ASSERT_EQ(item_view->item(), folder_apps_grid_view()->drag_item()); - generator->MoveMouseTo( + UpdateDragInScreen( + AppsGridView::MOUSE, app_list_folder_view()->GetBoundsInScreen().right_center() + - gfx::Vector2d(20, 0)); + gfx::Vector2d(20, 0), + /*steps=*/1); // Fire the reparent timer that should be started when an item is dragged out // of folder bounds. @@ -4514,22 +4518,29 @@ // Shelf should start handling the drag if it moves within its bounds. auto* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting(); - generator->MoveMouseTo(shelf_view->GetBoundsInScreen().left_center()); - ASSERT_TRUE(folder_apps_grid_view()->FireDragToShelfTimerForTest()); + UpdateDragInScreen( + AppsGridView::MOUSE, + shelf_view->GetBoundsInScreen().left_center() + gfx::Vector2d(5, 5), + /*steps=*/1); + if (!use_drag_drop_refactor()) { + ASSERT_TRUE(folder_apps_grid_view()->FireDragToShelfTimerForTest()); + } EXPECT_EQ("Item 1", shelf_view->drag_and_drop_shelf_id().app_id); // Move the app away from shelf, and verify the app doesn't get pinned when // the drag ends. - generator->MoveMouseTo(apps_grid_view_->GetBoundsInScreen().origin()); - generator->ReleaseLeftButton(); + UpdateDragInScreen(AppsGridView::MOUSE, + apps_grid_view_->GetBoundsInScreen().origin(), + /*steps=*/1); + EndDrag(); MaybeCheckHaptickEventsCount(1); EXPECT_FALSE(ShelfModel::Get()->IsAppPinned("Item 1")); EXPECT_TRUE(ShelfModel::Get()->items().empty()); } -TEST_P(AppsGridViewDragLegacyTest, RemoveDisplayWhileDraggingItemOntoShelf) { +TEST_P(AppsGridViewDragTest, RemoveDisplayWhileDraggingItemOntoShelf) { UpdateDisplay("1024x768,1024x768"); model_->PopulateApps(3); @@ -4539,13 +4550,7 @@ AppListItemView* const item_view = GetItemViewInTopLevelGrid(1); - auto* generator = GetEventGenerator(); - generator->MoveMouseTo(item_view->GetBoundsInScreen().CenterPoint()); - generator->PressLeftButton(); - if (!use_drag_drop_refactor()) { - item_view->FireMouseDragTimerForTest(); - } - generator->MoveMouseBy(10, 10); + InitiateDragForView(AppsGridView::MOUSE, item_view, apps_grid_view_); MaybeCheckHaptickEventsCount(1); // Verify that item drag has started. @@ -4559,8 +4564,13 @@ // Shelf should start handling the drag if it moves within its bounds. ShelfView* shelf_view = secondary_shelf->GetShelfViewForTesting(); - generator->MoveMouseTo(shelf_view->GetBoundsInScreen().left_center()); - ASSERT_TRUE(apps_grid_view_->FireDragToShelfTimerForTest()); + UpdateDragInScreen( + AppsGridView::MOUSE, + shelf_view->GetBoundsInScreen().left_center() + gfx::Vector2d(5, 5), + /*steps=*/1); + if (!use_drag_drop_refactor()) { + ASSERT_TRUE(apps_grid_view_->FireDragToShelfTimerForTest()); + } EXPECT_EQ("Item 1", shelf_view->drag_and_drop_shelf_id().app_id); @@ -4575,8 +4585,7 @@ EXPECT_TRUE(ShelfModel::Get()->items().empty()); } -TEST_P(AppsGridViewDragLegacyTest, - RemoveDisplayWhileDraggingFolderItemOntoShelf) { +TEST_P(AppsGridViewDragTest, RemoveDisplayWhileDraggingFolderItemOntoShelf) { UpdateDisplay("1024x768,1024x768"); // Creates a folder item - the folder size was chosen arbitrarily. @@ -4593,14 +4602,7 @@ AppListItemView* const item_view = GetItemViewInAppsGridAt(1, folder_apps_grid_view()); - - auto* generator = GetEventGenerator(); - generator->MoveMouseTo(item_view->GetBoundsInScreen().CenterPoint()); - generator->PressLeftButton(); - if (!use_drag_drop_refactor()) { - item_view->FireMouseDragTimerForTest(); - } - generator->MoveMouseBy(10, 10); + InitiateDragForView(AppsGridView::MOUSE, item_view, folder_apps_grid_view()); MaybeCheckHaptickEventsCount(1); // Verify app list item drag has started. @@ -4608,9 +4610,11 @@ ASSERT_TRUE(folder_apps_grid_view()->IsDragging()); ASSERT_EQ(item_view->item(), folder_apps_grid_view()->drag_item()); - generator->MoveMouseTo( + UpdateDragInScreen( + AppsGridView::MOUSE, app_list_folder_view()->GetBoundsInScreen().right_center() + - gfx::Vector2d(20, 0)); + gfx::Vector2d(20, 0), + /*steps=*/1); // Fire the reparent timer that should be started when an item is dragged out // of folder bounds. @@ -4622,8 +4626,13 @@ // Shelf should start handling the drag if it moves within its bounds. ShelfView* shelf_view = secondary_shelf->GetShelfViewForTesting(); - generator->MoveMouseTo(shelf_view->GetBoundsInScreen().left_center()); - ASSERT_TRUE(folder_apps_grid_view()->FireDragToShelfTimerForTest()); + UpdateDragInScreen( + AppsGridView::MOUSE, + shelf_view->GetBoundsInScreen().left_center() + gfx::Vector2d(5, 5), + /*steps=*/1); + if (!use_drag_drop_refactor()) { + ASSERT_TRUE(folder_apps_grid_view()->FireDragToShelfTimerForTest()); + } EXPECT_EQ("Item 1", shelf_view->drag_and_drop_shelf_id().app_id);
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc index 1865e398..5ac4eba 100644 --- a/ash/app_list/views/search_box_view.cc +++ b/ash/app_list/views/search_box_view.cc
@@ -909,6 +909,9 @@ if (!ShouldProcessAutocomplete()) return; + // Clear existing autocomplete text and reset the highlight range. + ClearAutocompleteText(); + const std::u16string& current_text = search_box()->GetText(); // Currrent text is a prefix of autocomplete text. DCHECK(base::StartsWith(autocomplete_text, current_text, @@ -916,6 +919,7 @@ // Autocomplete text should not be the same as current search box text. DCHECK(autocomplete_text != current_text); // Autocomplete text should not be the same as highlighted text. + const std::u16string& highlighted_text = autocomplete_text.substr(highlight_range_.start()); DCHECK(highlighted_text != current_text);
diff --git a/ash/components/arc/arc_features.cc b/ash/components/arc/arc_features.cc index 7ce32e6..9e541cb 100644 --- a/ash/components/arc/arc_features.cc +++ b/ash/components/arc/arc_features.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "ash/components/arc/arc_features.h" + #include "base/feature_list.h" namespace arc { @@ -41,13 +42,11 @@ "ArcIdleManager", base::FEATURE_DISABLED_BY_DEFAULT); - // For test purposes, ignore battery status changes, allowing Doze mode to // kick in even if we do not receive powerd changes related to battery. const base::FeatureParam<bool> kEnableArcIdleManagerIgnoreBatteryForPLT{ &kEnableArcIdleManager, "ignore_battery_for_test", false}; - // Controls whether files shared to ARC Nearby Share are shared through the // FuseBox filesystem, instead of the default method (through a temporary path // managed by file manager). @@ -83,17 +82,6 @@ "ArcEnablePerVmCoreScheduling", base::FEATURE_ENABLED_BY_DEFAULT); -// Controls whether to use ARC TTS caching to optimize ARC boot. -BASE_FEATURE(kEnableTTSCaching, - "ArcEnableTTSCaching", - base::FEATURE_DISABLED_BY_DEFAULT); - -// Controls whether to use pregenerated ARC TTS cache to optimize ARC boot and -// also whether or not TTS cache is used. -BASE_FEATURE(kEnableTTSCacheSetup, - "ArcEnableTTSCacheSetup", - base::FEATURE_ENABLED_BY_DEFAULT); - // Controls whether we should delegate audio focus requests from ARC to Chrome. BASE_FEATURE(kEnableUnifiedAudioFocusFeature, "ArcEnableUnifiedAudioFocus",
diff --git a/ash/components/arc/arc_features.h b/ash/components/arc/arc_features.h index f0808fa..87fb5515 100644 --- a/ash/components/arc/arc_features.h +++ b/ash/components/arc/arc_features.h
@@ -24,8 +24,6 @@ BASE_DECLARE_FEATURE(kEnableArcVmDataMigration); BASE_DECLARE_FEATURE(kEnableLazyWebViewInit); BASE_DECLARE_FEATURE(kEnablePerVmCoreScheduling); -BASE_DECLARE_FEATURE(kEnableTTSCaching); -BASE_DECLARE_FEATURE(kEnableTTSCacheSetup); BASE_DECLARE_FEATURE(kEnableUnifiedAudioFocusFeature); BASE_DECLARE_FEATURE(kEnableUnmanagedToManagedTransitionFeature); BASE_DECLARE_FEATURE(kEnableUsap);
diff --git a/ash/components/arc/session/arc_session_impl.cc b/ash/components/arc/session/arc_session_impl.cc index d340981..227eb1ee 100644 --- a/ash/components/arc/session/arc_session_impl.cc +++ b/ash/components/arc/session/arc_session_impl.cc
@@ -467,12 +467,13 @@ params.num_cores_disabled = num_cores_disabled; params.enable_notifications_refresh = ash::features::IsNotificationsRefreshEnabled(); - params.enable_tts_caching = - base::FeatureList::IsEnabled(kEnableTTSCacheSetup); + params.enable_tts_caching = true; params.enable_consumer_auto_update_toggle = base::FeatureList::IsEnabled( ash::features::kConsumerAutoUpdateToggleAllowed); params.enable_privacy_hub_for_chrome = base::FeatureList::IsEnabled(ash::features::kCrosPrivacyHub); + params.arc_switch_to_keymint = + base::FeatureList::IsEnabled(kSwitchToKeyMintOnT); params.use_virtio_blk_data = use_virtio_blk_data_; // TODO (b/196460968): Remove after CTS run is complete.
diff --git a/ash/components/arc/session/arc_session_impl_unittest.cc b/ash/components/arc/session/arc_session_impl_unittest.cc index 107e22ece..e8ee8e5 100644 --- a/ash/components/arc/session/arc_session_impl_unittest.cc +++ b/ash/components/arc/session/arc_session_impl_unittest.cc
@@ -940,17 +940,6 @@ GetClient(arc_session.get())->last_start_params().enable_tts_caching); } -// Test that validates TTS caching is enabled. -TEST_F(ArcSessionImplTest, TTSCachingEnabled) { - base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatureState(arc::kEnableTTSCacheSetup, true); - auto arc_session = CreateArcSession(); - arc_session->StartMiniInstance(); - base::RunLoop().RunUntilIdle(); - EXPECT_TRUE( - GetClient(arc_session.get())->last_start_params().enable_tts_caching); -} - // Test "<<" operator for ArcSessionImpl::State type. TEST_F(ArcSessionImplTest, StateTypeStreamOutput) { EXPECT_EQ(ConvertToString(ArcSessionImpl::State::NOT_STARTED), "NOT_STARTED");
diff --git a/ash/components/arc/session/arc_start_params.h b/ash/components/arc/session/arc_start_params.h index 16da74a..a995ea4 100644 --- a/ash/components/arc/session/arc_start_params.h +++ b/ash/components/arc/session/arc_start_params.h
@@ -110,6 +110,9 @@ // Flag to enable Privacy Hub for chrome. bool enable_privacy_hub_for_chrome = false; + + // Flag to switch to KeyMint for T+. + bool arc_switch_to_keymint = false; }; } // namespace arc
diff --git a/ash/components/arc/session/arc_upgrade_params.cc b/ash/components/arc/session/arc_upgrade_params.cc index 89ca30af..13355bd 100644 --- a/ash/components/arc/session/arc_upgrade_params.cc +++ b/ash/components/arc/session/arc_upgrade_params.cc
@@ -37,9 +37,7 @@ skip_gms_core_cache(base::CommandLine::ForCurrentProcess()->HasSwitch( ash::switches::kArcDisableGmsCoreCache)), skip_tts_cache(base::CommandLine::ForCurrentProcess()->HasSwitch( - ash::switches::kArcDisableTtsCache) || - !base::FeatureList::IsEnabled(arc::kEnableTTSCacheSetup)) { -} + ash::switches::kArcDisableTtsCache)) {} UpgradeParams::UpgradeParams(const UpgradeParams& other) = default; UpgradeParams::UpgradeParams(UpgradeParams&& other) = default;
diff --git a/ash/components/arc/session/arc_upgrade_params_unittest.cc b/ash/components/arc/session/arc_upgrade_params_unittest.cc index 97c4d5f..ade90da9 100644 --- a/ash/components/arc/session/arc_upgrade_params_unittest.cc +++ b/ash/components/arc/session/arc_upgrade_params_unittest.cc
@@ -27,12 +27,5 @@ EXPECT_FALSE(upgradeParams.skip_tts_cache); } -TEST(ArcUpgradeParamsTest, Constructor_WithTtsCacheSetupFeatureDisabled) { - base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatureState(arc::kEnableTTSCacheSetup, true); - UpgradeParams upgradeParams; - EXPECT_FALSE(upgradeParams.skip_tts_cache); -} - } // namespace } // namespace arc
diff --git a/ash/components/arc/session/arc_vm_client_adapter.cc b/ash/components/arc/session/arc_vm_client_adapter.cc index 7277360..c0d8192 100644 --- a/ash/components/arc/session/arc_vm_client_adapter.cc +++ b/ash/components/arc/session/arc_vm_client_adapter.cc
@@ -366,6 +366,10 @@ mini_instance_request->set_enable_privacy_hub_for_chrome( base::FeatureList::IsEnabled(ash::features::kCrosPrivacyHub)); + if (GetArcAndroidSdkVersionAsInt() == kArcVersionT) { + mini_instance_request->set_arc_switch_to_keymint( + base::FeatureList::IsEnabled(kSwitchToKeyMintOnT)); + } request.set_enable_rw(file_system_status.is_host_rootfs_writable() && file_system_status.is_system_image_ext_format());
diff --git a/ash/constants/BUILD.gn b/ash/constants/BUILD.gn index f7c86c3a..385a0d5 100644 --- a/ash/constants/BUILD.gn +++ b/ash/constants/BUILD.gn
@@ -12,6 +12,8 @@ sources = [ "ambient_theme.cc", "ambient_theme.h", + "ambient_video.cc", + "ambient_video.h", "app_types.h", "ash_constants.cc", "ash_constants.h",
diff --git a/ash/constants/ambient_theme.cc b/ash/constants/ambient_theme.cc index a8932e4..61c5ba2 100644 --- a/ash/constants/ambient_theme.cc +++ b/ash/constants/ambient_theme.cc
@@ -17,10 +17,8 @@ return "FeelTheBreeze"; case AmbientTheme::kFloatOnBy: return "FloatOnBy"; - case AmbientTheme::kVideoNewMexico: - return "NewMexico"; - case AmbientTheme::kVideoClouds: - return "Clouds"; + case AmbientTheme::kVideo: + return "Video"; } }
diff --git a/ash/constants/ambient_theme.h b/ash/constants/ambient_theme.h index e817632..75cf634 100644 --- a/ash/constants/ambient_theme.h +++ b/ash/constants/ambient_theme.h
@@ -25,9 +25,8 @@ kFloatOnBy = 2, // Scenic videos that get played on loop at full screen. The videos are static // and Google-owned. - kVideoNewMexico = 3, - kVideoClouds = 4, - kMaxValue = kVideoClouds, + kVideo = 3, + kMaxValue = kVideo, }; inline constexpr AmbientTheme kDefaultAmbientTheme = AmbientTheme::kSlideshow;
diff --git a/ash/constants/ambient_video.cc b/ash/constants/ambient_video.cc new file mode 100644 index 0000000..2d27b9e --- /dev/null +++ b/ash/constants/ambient_video.cc
@@ -0,0 +1,21 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/constants/ambient_video.h" + +namespace ash { + +base::StringPiece ToString(AmbientVideo video) { + // See the "AmbientModeThemes" <variants> tag in histograms.xml. These names + // are currently used for metrics purposes, so they cannot be arbitrarily + // renamed. + switch (video) { + case AmbientVideo::kVideoNewMexico: + return "NewMexico"; + case AmbientVideo::kVideoClouds: + return "Clouds"; + } +} + +} // namespace ash
diff --git a/ash/constants/ambient_video.h b/ash/constants/ambient_video.h new file mode 100644 index 0000000..66cee3a --- /dev/null +++ b/ash/constants/ambient_video.h
@@ -0,0 +1,34 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_CONSTANTS_AMBIENT_VIDEO_H_ +#define ASH_CONSTANTS_AMBIENT_VIDEO_H_ + +#include "base/component_export.h" +#include "base/strings/string_piece.h" + +namespace ash { + +// Only applies when |AmbientTheme::kVideo| is active. +// +// Each corresponds to a video in ambient mode that can be selected by the user. +// The videos get played on loop at full screen. They are static and +// Google-owned. +// +// These values are persisted in user pref storage and logs, so they should +// never be renumbered or reused. +enum class AmbientVideo { + kVideoNewMexico = 0, + kVideoClouds = 1, + kMaxValue = kVideoClouds, +}; + +// The returned StringPiece is guaranteed to be null-terminated and point to +// memory valid for the lifetime of the program. +COMPONENT_EXPORT(ASH_CONSTANTS) +base::StringPiece ToString(AmbientVideo video); + +} // namespace ash + +#endif // ASH_CONSTANTS_AMBIENT_THEME_H_
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc index 77fa0927..1b78f90b 100644 --- a/ash/constants/ash_features.cc +++ b/ash/constants/ash_features.cc
@@ -286,10 +286,6 @@ "BorealisStorageBallooning", base::FEATURE_ENABLED_BY_DEFAULT); -// Enable or disable calendar view from the system tray. Also enables the system -// tray to show date in the shelf when the screen is sufficiently large. -BASE_FEATURE(kCalendarView, "CalendarView", base::FEATURE_ENABLED_BY_DEFAULT); - // Enable or disable debug mode for CalendarModel. BASE_FEATURE(kCalendarModelDebugMode, "CalendarModelDebugMode", @@ -2376,10 +2372,6 @@ return base::FeatureList::IsEnabled(kBluetoothQualityReport); } -bool IsCalendarViewEnabled() { - return base::FeatureList::IsEnabled(kCalendarView); -} - bool IsCalendarModelDebugModeEnabled() { return base::FeatureList::IsEnabled(kCalendarModelDebugMode); }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h index 6cef393..41221e5 100644 --- a/ash/constants/ash_features.h +++ b/ash/constants/ash_features.h
@@ -83,7 +83,6 @@ COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kBorealisPermitted); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kBorealisStorageBallooning); -COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kCalendarView); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kCalendarModelDebugMode); COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kCalendarJelly); COMPONENT_EXPORT(ASH_CONSTANTS) @@ -650,7 +649,6 @@ COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBackgroundBlurEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBentoBarEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBluetoothQualityReportEnabled(); -COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCalendarViewEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCalendarModelDebugModeEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCalendarJellyEnabled(); COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCaptivePortalErrorPageEnabled();
diff --git a/ash/curtain/security_curtain_controller_impl_unittest.cc b/ash/curtain/security_curtain_controller_impl_unittest.cc index dba285d05..fb0864f0 100644 --- a/ash/curtain/security_curtain_controller_impl_unittest.cc +++ b/ash/curtain/security_curtain_controller_impl_unittest.cc
@@ -14,6 +14,7 @@ #include "ash/system/power/power_button_menu_view.h" #include "ash/system/power/power_button_test_base.h" #include "ash/test/ash_test_base.h" +#include "base/check_deref.h" #include "chromeos/ash/components/audio/cras_audio_handler.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -247,6 +248,14 @@ return *power_button_test_api().GetPowerButtonMenuView()->GetWidget(); } + const aura::Window& GetPowerMenuWidgetContainerParent() { + EXPECT_TRUE(power_button_test_api().IsMenuOpened()); + + return CHECK_DEREF(Shell::GetPrimaryRootWindow() + ->GetChildById(kShellWindowId_PowerMenuContainer) + ->parent()); + } + private: std::unique_ptr<PowerButtonControllerTestApi> power_button_test_api_; }; @@ -572,15 +581,17 @@ TEST_F(SecurityCurtainControllerImplTest, ShouldResetParentOfPowerMenuWidgetWhenDisabled) { PressPowerButton(); - views::Widget* parent_before_enabled = GetOpenPowerWidget().parent(); + const aura::Window& parent_before_enabled = + GetPowerMenuWidgetContainerParent(); ReleasePowerButton(); security_curtain_controller().Enable(init_params()); security_curtain_controller().Disable(); PressPowerButton(); - views::Widget* parent_after_disabled = GetOpenPowerWidget().parent(); + const aura::Window& parent_after_disabled = + GetPowerMenuWidgetContainerParent(); - ASSERT_EQ(parent_before_enabled, parent_after_disabled); + ASSERT_EQ(&parent_before_enabled, &parent_after_disabled); } } // namespace ash::curtain
diff --git a/ash/events/accessibility_event_rewriter_unittest.cc b/ash/events/accessibility_event_rewriter_unittest.cc index 72d089ce..82e41540 100644 --- a/ash/events/accessibility_event_rewriter_unittest.cc +++ b/ash/events/accessibility_event_rewriter_unittest.cc
@@ -188,7 +188,7 @@ return true; } - bool TopRowKeysAreFunctionKeys() const override { return false; } + bool TopRowKeysAreFunctionKeys(int device_id) const override { return false; } bool IsExtensionCommandRegistered(ui::KeyboardCode key_code, int flags) const override { @@ -583,7 +583,7 @@ return true; } - bool TopRowKeysAreFunctionKeys() const override { return false; } + bool TopRowKeysAreFunctionKeys(int device_id) const override { return false; } bool IsExtensionCommandRegistered(ui::KeyboardCode key_code, int flags) const override {
diff --git a/ash/public/cpp/ambient/ambient_prefs.cc b/ash/public/cpp/ambient/ambient_prefs.cc index 10ad8d2..f4fdc881 100644 --- a/ash/public/cpp/ambient/ambient_prefs.cc +++ b/ash/public/cpp/ambient/ambient_prefs.cc
@@ -19,6 +19,11 @@ // theme". constexpr char kAmbientTheme[] = "ash.ambient.animation_theme"; +constexpr char kAmbientUiSettings[] = "ash.ambient.ui_settings"; + +constexpr char kAmbientUiSettingsFieldTheme[] = "theme"; +constexpr char kAmbientUiSettingsFieldVideo[] = "video"; + constexpr char kAmbientBackdropClientId[] = "ash.ambient.backdrop.client.id"; constexpr char kAmbientModeEnabled[] = "settings.ambient_mode.enabled";
diff --git a/ash/public/cpp/ambient/ambient_prefs.h b/ash/public/cpp/ambient/ambient_prefs.h index 4131932..de6e3ae 100644 --- a/ash/public/cpp/ambient/ambient_prefs.h +++ b/ash/public/cpp/ambient/ambient_prefs.h
@@ -13,8 +13,22 @@ // Integer pref corresponding to the ambient mode theme that the user has // selected (see AmbientTheme enum). +// DEPRECATED: Use |kAmbientUiSettings| instead; that's the successor. ASH_PUBLIC_EXPORT extern const char kAmbientTheme[]; +// Dictionary pref capturing the ambient UI that the user has selected: +// { +// // Required. +// "theme": <integer value of |AmbientTheme| enum> +// // Which video the user picked. Only used if the "theme" is |kVideo|. +// "video": <integer value of |AmbientVideo| enum> +// } +ASH_PUBLIC_EXPORT extern const char kAmbientUiSettings[]; + +// Fields in the |kAmbientUiSettings| dictionary. +ASH_PUBLIC_EXPORT extern const char kAmbientUiSettingsFieldTheme[]; +ASH_PUBLIC_EXPORT extern const char kAmbientUiSettingsFieldVideo[]; + // A GUID for backdrop client. ASH_PUBLIC_EXPORT extern const char kAmbientBackdropClientId[];
diff --git a/ash/public/cpp/ash_view_ids.h b/ash/public/cpp/ash_view_ids.h index 0b923e8..fcb83e9 100644 --- a/ash/public/cpp/ash_view_ids.h +++ b/ash/public/cpp/ash_view_ids.h
@@ -19,6 +19,7 @@ VIEW_ID_ACCESSIBILITY_VIRTUAL_KEYBOARD_ENABLED, // Feature tile ids. + VIEW_ID_ACCESSIBILITY_FEATURE_TILE, VIEW_ID_SCREEN_CAPTURE_FEATURE_TILE, VIEW_ID_DND_FEATURE_TILE, VIEW_ID_AUTOROTATE_FEATURE_TILE,
diff --git a/ash/public/cpp/input_device_settings_controller.cc b/ash/public/cpp/input_device_settings_controller.cc index 48af99d..2bebeda 100644 --- a/ash/public/cpp/input_device_settings_controller.cc +++ b/ash/public/cpp/input_device_settings_controller.cc
@@ -10,6 +10,12 @@ InputDeviceSettingsController* g_instance = nullptr; } +template <> +InputDeviceSettingsController*& InputDeviceSettingsController:: + ScopedResetterForTest::GetGlobalInstanceHolder() { + return g_instance; +} + InputDeviceSettingsController::InputDeviceSettingsController() { DCHECK_EQ(nullptr, g_instance); g_instance = this;
diff --git a/ash/public/cpp/input_device_settings_controller.h b/ash/public/cpp/input_device_settings_controller.h index 9552b6a..442b675 100644 --- a/ash/public/cpp/input_device_settings_controller.h +++ b/ash/public/cpp/input_device_settings_controller.h
@@ -8,6 +8,7 @@ #include <vector> #include "ash/public/cpp/ash_public_export.h" +#include "ash/public/cpp/scoped_singleton_resetter_for_test.h" #include "ash/public/mojom/input_device_settings.mojom-forward.h" #include "base/observer_list_types.h" @@ -18,6 +19,8 @@ class ASH_PUBLIC_EXPORT InputDeviceSettingsController { public: using DeviceId = uint32_t; + using ScopedResetterForTest = + ScopedSingletonResetterForTest<InputDeviceSettingsController>; class Observer : public base::CheckedObserver { public:
diff --git a/ash/public/cpp/system_tray_test_api.h b/ash/public/cpp/system_tray_test_api.h index 4dd877c..7e0cabf 100644 --- a/ash/public/cpp/system_tray_test_api.h +++ b/ash/public/cpp/system_tray_test_api.h
@@ -12,10 +12,16 @@ namespace message_center { class MessagePopupView; -} +} // namespace message_center + +namespace views { +class ScrollView; +} // namespace views namespace ash { +class AccessibilityDetailedView; + // Public test API for the system tray. Methods only apply to the system tray // on the primary display. class ASH_EXPORT SystemTrayTestApi { @@ -47,12 +53,23 @@ void ShowAccessibilityDetailedView(); void ShowNetworkDetailedView(); + // Returns the current `ash::AccessibilityDetailedView`. This assumes that the + // accessibility detailed view is currently showing. + AccessibilityDetailedView* GetAccessibilityDetailedView(); + // Returns true if the view exists in the bubble and is visible. // If |open_tray| is true, it also opens system tray bubble. bool IsBubbleViewVisible(int view_id, bool open_tray); - // If the view is in a scroll contents, scrolls the bubble to shown the view. - void ScrollToShowView(int view_id); + // Returns true if the `TrayToggleButton` with ID `view_id` is toggled on, + // false otherwise. + bool IsToggleOn(int view_id); + + // Searches for a `views::View` having ID `view_id`, and then scrolls it onto + // the screen to make it visible (if it is already visible then no scrolling + // is performed). The view should be a descendant of `scroll_view` (this is + // `DCHECK`ed). + void ScrollToShowView(views::ScrollView* scroll_view, int view_id); // Clicks the view |view_id|. void ClickBubbleView(int view_id);
diff --git a/ash/shelf/scrollable_shelf_view_unittest.cc b/ash/shelf/scrollable_shelf_view_unittest.cc index 33d837f..2eb5c186 100644 --- a/ash/shelf/scrollable_shelf_view_unittest.cc +++ b/ash/shelf/scrollable_shelf_view_unittest.cc
@@ -91,10 +91,12 @@ void InkDropRippleAnimationEnded( views::InkDropState ink_drop_state) override { if (ink_drop_state != views::InkDropState::ACTIVATED && - ink_drop_state != views::InkDropState::HIDDEN) + ink_drop_state != views::InkDropState::HIDDEN) { return; - if (run_loop_.get()) + } + if (run_loop_.get()) { run_loop_->Quit(); + } } views::Button* button_ = nullptr; @@ -159,8 +161,9 @@ void AddAppShortcutsUntilRightArrowIsShown() { ASSERT_FALSE(scrollable_shelf_view_->right_arrow()->GetVisible()); - while (!scrollable_shelf_view_->right_arrow()->GetVisible()) + while (!scrollable_shelf_view_->right_arrow()->GetVisible()) { AddAppShortcut(); + } } void CheckFirstAndLastTappableIconsBounds() { @@ -256,8 +259,9 @@ // Adds enough app icons so that after display rotation the scrollable // shelf is still in overflow mode. const int num = display.bounds().height() / shelf_view_->GetButtonSize(); - for (int i = 0; i < num; i++) + for (int i = 0; i < num; i++) { AddAppShortcut(); + } // Because the display's height is greater than the display's width, // the scrollable shelf is in overflow mode before display rotation. @@ -572,8 +576,9 @@ // (https://crbug.com/1035596). TEST_P(ScrollableShelfViewRTLTest, CheckTappableIndicesOnSecondDisplay) { constexpr size_t icon_number = 5; - for (size_t i = 0; i < icon_number; i++) + for (size_t i = 0; i < icon_number; i++) { AddAppShortcut(); + } // Adds the second display. UpdateDisplay("600x800,600x800"); @@ -597,8 +602,9 @@ // after switching to tablet mode (https://crbug.com/1017979). TEST_P(ScrollableShelfViewRTLTest, CorrectUIAfterSwitchingToTablet) { // Add enough app shortcuts to ensure that at least three pages of icons show. - for (int i = 0; i < 25; i++) + for (int i = 0; i < 25; i++) { AddAppShortcut(); + } ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton, scrollable_shelf_view_->layout_strategy_for_test()); @@ -658,8 +664,9 @@ TEST_P(ScrollableShelfViewRTLTest, CorrectUIInTabletWithoutOverflow) { Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true); - for (int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { AddAppShortcut(); + } ASSERT_EQ(ScrollableShelfView::kNotShowArrowButtons, scrollable_shelf_view_->layout_strategy_for_test()); @@ -1215,8 +1222,9 @@ scrollable_shelf_view_->layout_strategy_for_test()); // Pins the icons of running apps to the shelf. - for (size_t i = 0; i < 2 * num; i++) + for (size_t i = 0; i < 2 * num; i++) { AddAppShortcut(ShelfItemType::TYPE_APP); + } { ShelfID shelf_id = AddAppShortcut(); @@ -1242,22 +1250,21 @@ ~ScrollableShelfViewWithAppScalingTest() override = default; void SetUp() override { - // When the calendar view is enabled, the status widget's bounds could vary - // under different dates. For example, "June 10" is longer than "June 9". + // With the calendar view, the status widget's bounds could vary under + // different dates. For example, "June 10" is longer than "June 9". // Therefore, the code below sets the constant date to avoid flakiness. - if (features::IsCalendarViewEnabled()) { - scoped_locale_ = - std::make_unique<base::test::ScopedRestoreICUDefaultLocale>("en_US"), - time_zone_ = std::make_unique<base::test::ScopedRestoreDefaultTimezone>( - "America/Chicago"); - constexpr char kFakeNowTimeString[] = "Sunday, 5 June 2022 14:30:00 CDT"; - ASSERT_TRUE(base::Time::FromString(kFakeNowTimeString, - &TimeOverrideHelper::current_time)); - time_override_ = std::make_unique<base::subtle::ScopedTimeClockOverrides>( - &TimeOverrideHelper::TimeNow, /*time_ticks_override=*/nullptr, - /*thread_ticks_override=*/nullptr); - } + scoped_locale_ = + std::make_unique<base::test::ScopedRestoreICUDefaultLocale>("en_US"), + time_zone_ = std::make_unique<base::test::ScopedRestoreDefaultTimezone>( + "America/Chicago"); + + constexpr char kFakeNowTimeString[] = "Sunday, 5 June 2022 14:30:00 CDT"; + ASSERT_TRUE(base::Time::FromString(kFakeNowTimeString, + &TimeOverrideHelper::current_time)); + time_override_ = std::make_unique<base::subtle::ScopedTimeClockOverrides>( + &TimeOverrideHelper::TimeNow, /*time_ticks_override=*/nullptr, + /*thread_ticks_override=*/nullptr); ScrollableShelfViewTest::SetUp(); @@ -1306,10 +1313,8 @@ // its children's sizes if there is insufficient space for shelf buttons to show // without scrolling. TEST_F(ScrollableShelfViewWithAppScalingTest, AppScalingBasics) { - if (features::IsCalendarViewEnabled()) - PopulateAppShortcut(kAppCountWithShowingDateTray); - else - PopulateAppShortcut(kAppCount); + PopulateAppShortcut(kAppCountWithShowingDateTray); + HotseatWidget* hotseat_widget = GetPrimaryShelf()->shelf_widget()->hotseat_widget(); EXPECT_EQ(HotseatDensity::kNormal, hotseat_widget->target_hotseat_density()); @@ -1352,10 +1357,8 @@ // Verifies that app scaling works as expected with hotseat state transition. TEST_F(ScrollableShelfViewWithAppScalingTest, VerifyWithHotseatStateTransition) { - if (features::IsCalendarViewEnabled()) - PopulateAppShortcut(kAppCountWithShowingDateTray); - else - PopulateAppShortcut(kAppCount); + PopulateAppShortcut(kAppCountWithShowingDateTray); + HotseatWidget* hotseat_widget = GetPrimaryShelf()->shelf_widget()->hotseat_widget(); EXPECT_EQ(HotseatDensity::kNormal, hotseat_widget->target_hotseat_density());
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc index 86f3867..4d29c8a 100644 --- a/ash/shelf/shelf_view.cc +++ b/ash/shelf/shelf_view.cc
@@ -8,11 +8,13 @@ #include <utility> #include "ash/app_list/app_list_controller_impl.h" +#include "ash/app_list/app_list_item_util.h" #include "ash/app_list/views/app_drag_icon_proxy.h" #include "ash/app_list/views/ghost_image_view.h" #include "ash/constants/ash_features.h" #include "ash/keyboard/keyboard_util.h" #include "ash/keyboard/ui/keyboard_ui_controller.h" +#include "ash/public/cpp/app_list/app_list_features.h" #include "ash/public/cpp/metrics_util.h" #include "ash/public/cpp/shelf_item.h" #include "ash/public/cpp/shelf_model.h" @@ -65,6 +67,7 @@ #include "components/services/app_service/public/cpp/app_types.h" #include "ui/accessibility/ax_node_data.h" #include "ui/aura/window.h" +#include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/menu_source_utils.h" #include "ui/base/metadata/metadata_impl_macros.h" @@ -1183,6 +1186,8 @@ return; } + DCHECK(app_list_features::IsDragAndDropRefactorEnabled() || + !drag_icon_proxy_); drag_icon_proxy_ = std::move(icon_proxy); views::View* drag_and_drop_view = @@ -1352,6 +1357,8 @@ base::Unretained(this), std::make_unique<ViewOpacityResetter>(drag_view_))); } + } else if (drag_view_) { + drag_view_->layer()->SetOpacity(1.0f); } // If the drag pointer is NONE, no drag operation is going on and the @@ -1688,7 +1695,9 @@ return; } - drag_icon_proxy_->UpdatePosition(screen_location); + if (drag_icon_proxy_) { + drag_icon_proxy_->UpdatePosition(screen_location); + } return; } @@ -1724,7 +1733,9 @@ } // Make the item partially disappear to show that it will get removed if // dropped. - drag_icon_proxy_->SetOpacity(kDraggedImageOpacity); + if (drag_icon_proxy_) { + drag_icon_proxy_->SetOpacity(kDraggedImageOpacity); + } } } } @@ -1737,7 +1748,6 @@ // Coming here we should always have a |drag_view_|. DCHECK(drag_view_); - DCHECK(drag_icon_proxy_); delegate_->CancelScrollForItemDrag(); @@ -1982,7 +1992,7 @@ gfx::Rect ShelfView::GetDragIconBoundsInScreenForTest() const { if (!drag_icon_proxy_) - return gfx::Rect(); + return drag_view_ ? drag_view_->GetBoundsInScreen() : gfx::Rect(); return drag_icon_proxy_->GetBoundsInScreen(); } @@ -2779,6 +2789,80 @@ [this](size_t idx) { return IsItemPinned(model_->items()[idx]); }); } +views::View::DropCallback ShelfView::GetDropCallback( + const ui::DropTargetEvent& event) { + return app_list_features::IsDragAndDropRefactorEnabled() + ? base::BindOnce(&ShelfView::EndDragCallback, + base::Unretained(this)) + : base::DoNothing(); +} + +void ShelfView::EndDragCallback(const ui::DropTargetEvent& event, + ui::mojom::DragOperation& output_drag_op) { + // TODO(b/271601288): Hook up drop animation with the drag image icon. + output_drag_op = ui::mojom::DragOperation::kMove; + EndDrag(false, /*icon_proxy = */ nullptr); +} + +bool ShelfView::GetDropFormats( + int* formats, + std::set<ui::ClipboardFormatType>* format_types) { + if (app_list_features::IsDragAndDropRefactorEnabled()) { + format_types->insert(GetAppItemFormatType()); + } + return true; +} + +bool ShelfView::CanDrop(const OSExchangeData& data) { + if (!app_list_features::IsDragAndDropRefactorEnabled()) { + return true; + } + + auto app_id = GetAppIdFromDropData(data); + if (app_id->empty()) { + return false; + } + + std::set<ui::ClipboardFormatType> format_types; + format_types.insert(GetAppItemFormatType()); + return data.HasAnyFormat(0, format_types); +} + +void ShelfView::OnDragExited() { + if (!app_list_features::IsDragAndDropRefactorEnabled()) { + views::View::OnDragExited(); + return; + } + EndDrag(/*cancel=*/true, nullptr); +} + +void ShelfView::OnDragEntered(const ui::DropTargetEvent& event) { + if (!app_list_features::IsDragAndDropRefactorEnabled()) { + views::View::OnDragEntered(event); + return; + } + + auto app_id = GetAppIdFromDropData(event.data()); + if (app_id->empty()) { + views::View::OnDragEntered(event); + return; + } + + gfx::Point drag_point_in_screen = event.location(); + views::View::ConvertPointToScreen(this, &drag_point_in_screen); + StartDrag(app_id.value(), drag_point_in_screen, gfx::Rect()); +} + +int ShelfView::OnDragUpdated(const ui::DropTargetEvent& event) { + if (app_list_features::IsDragAndDropRefactorEnabled()) { + gfx::Point drag_point_in_screen = event.location(); + views::View::ConvertPointToScreen(this, &drag_point_in_screen); + Drag(drag_point_in_screen, + drag_view_ ? drag_view_->GetBoundsInScreen() : gfx::Rect()); + } + return ui::DragDropTypes::DRAG_MOVE; +} + BEGIN_METADATA(ShelfView, views::AccessiblePaneView) END_METADATA
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h index 7f6bda8..7ad481b 100644 --- a/ash/shelf/shelf_view.h +++ b/ash/shelf/shelf_view.h
@@ -172,6 +172,17 @@ void GetAccessibleNodeData(ui::AXNodeData* node_data) override; View* GetTooltipHandlerForPoint(const gfx::Point& point) override; + bool CanDrop(const OSExchangeData& data) override; + void OnDragEntered(const ui::DropTargetEvent& event) override; + void OnDragExited() override; + int OnDragUpdated(const ui::DropTargetEvent& event) override; + DropCallback GetDropCallback(const ui::DropTargetEvent& event) override; + bool GetDropFormats(int* formats, + std::set<ui::ClipboardFormatType>* format_types) override; + + void EndDragCallback(const ui::DropTargetEvent& event, + ui::mojom::DragOperation& output_drag_op); + // ShelfButtonDelegate: void OnShelfButtonAboutToRequestFocusFromTabTraversal(ShelfButton* button, bool reverse) override;
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc index 71fa538f..c799b883 100644 --- a/ash/shelf/shelf_view_unittest.cc +++ b/ash/shelf/shelf_view_unittest.cc
@@ -162,8 +162,9 @@ bool RunPendingContextMenuCallback( std::unique_ptr<ui::SimpleMenuModel> model) { - if (pending_context_menu_callback_.is_null()) + if (pending_context_menu_callback_.is_null()) { return false; + } std::move(pending_context_menu_callback_).Run(std::move(model)); return true; } @@ -392,8 +393,9 @@ // Set a delegate; some tests require one to select the item. model_->ReplaceShelfItemDelegate( item.id, std::make_unique<ShelfItemSelectionTracker>()); - if (wait_for_animations) + if (wait_for_animations) { test_api_->RunMessageLoopUntilAnimationsDone(); + } return item.id; } ShelfID AddAppShortcut() { return AddItem(TYPE_PINNED_APP, true); } @@ -527,8 +529,9 @@ if (progressively) { int sgn = dist_x > 0 ? 1 : -1; dist_x = abs(dist_x); - for (; dist_x; dist_x -= std::min(10, dist_x)) + for (; dist_x; dist_x -= std::min(10, dist_x)) { DoDrag(sgn * std::min(10, abs(dist_x)), 0, button, pointer, to); + } } else { DoDrag(dist_x, dist_y, button, pointer, to); } @@ -550,12 +553,14 @@ ContinueDrag(button, pointer, button_index, destination_index, false); } else if (button_index < destination_index) { for (int cur_index = button_index + 1; cur_index <= destination_index; - cur_index++) + cur_index++) { ContinueDrag(button, pointer, cur_index - 1, cur_index, true); + } } else if (button_index > destination_index) { for (int cur_index = button_index - 1; cur_index >= destination_index; - cur_index--) + cur_index--) { ContinueDrag(button, pointer, cur_index + 1, cur_index, true); + } } return button; } @@ -1318,8 +1323,9 @@ // The tooltip shouldn't hide if the mouse is on normal buttons. for (size_t i = 0; i < test_api_->GetButtonCount(); i++) { ShelfAppButton* button = test_api_->GetButton(i); - if (!button) + if (!button) { continue; + } EXPECT_FALSE(shelf_view_->ShouldHideTooltip( button->GetMirroredBounds().CenterPoint())) << "ShelfView tries to hide on button " << i; @@ -1332,11 +1338,13 @@ int right = 0; for (size_t i = 0; i < test_api_->GetButtonCount(); ++i) { ShelfAppButton* button = test_api_->GetButton(i); - if (!button) + if (!button) { continue; + } right = button->GetBoundsInScreen().x(); - if (right > left) + if (right > left) { break; + } } gfx::Point test_point(left + (right - left) / 2, @@ -1358,8 +1366,9 @@ gfx::Rect all_area; for (size_t i = 0; i < test_api_->GetButtonCount(); i++) { ShelfAppButton* button = test_api_->GetButton(i); - if (!button) + if (!button) { continue; + } all_area.Union(button->GetMirroredBounds()); } @@ -1382,8 +1391,9 @@ // The tooltip shouldn't hide if the mouse is on normal buttons. for (size_t i = 2; i < test_api_->GetButtonCount(); i++) { ShelfAppButton* button = test_api_->GetButton(i); - if (!button) + if (!button) { continue; + } EXPECT_FALSE(shelf_view_->ShouldHideTooltip( button->GetMirroredBounds().CenterPoint())) @@ -2526,11 +2536,13 @@ gfx::Rect visible_bounds = shelf_view_->GetVisibleItemsBoundsInScreen(); gfx::Rect shelf_bounds = shelf_view_->GetBoundsInScreen(); EXPECT_TRUE(shelf_bounds.Contains(visible_bounds)); - for (size_t i = 0; i < test_api_->GetButtonCount(); ++i) + for (size_t i = 0; i < test_api_->GetButtonCount(); ++i) { if (ShelfAppButton* button = test_api_->GetButton(i)) { - if (button->GetVisible()) + if (button->GetVisible()) { EXPECT_TRUE(visible_bounds.Contains(button->GetBoundsInScreen())); + } } + } } private: @@ -3256,13 +3268,10 @@ ExpectNotFocused(shelf_view_); ExpectFocused(status_area_); - // If calendar view is enabled, move the focusing ring from the date tray to - // the unified tray. - if (features::IsCalendarViewEnabled()) { - DoTab(); - ExpectNotFocused(shelf_view_); - ExpectFocused(status_area_); - } + // Move the focusing ring from the date tray to the unified tray. + DoTab(); + ExpectNotFocused(shelf_view_); + ExpectFocused(status_area_); // And keep going forward, now we should be cycling back to the first shelf // element. @@ -3287,13 +3296,10 @@ ExpectNotFocused(shelf_view_); ExpectFocused(status_area_); - // If calendar view is enabled, move the focusing ring from the unified tray - // to the date tray. - if (features::IsCalendarViewEnabled()) { - DoShiftTab(); - ExpectNotFocused(shelf_view_); - ExpectFocused(status_area_); - } + // Move the focusing ring from the unified tray to the date tray. + DoShiftTab(); + ExpectNotFocused(shelf_view_); + ExpectFocused(status_area_); // Advance backwards to the last element of the shelf. DoShiftTab(); @@ -3559,8 +3565,9 @@ // Exercises the party animation. TEST_P(ShelfPartyTest, PartyAnimation) { - for (int i = 0; i < 16; ++i) + for (int i = 0; i < 16; ++i) { AddAppShortcut(); + } model_->ToggleShelfParty(); task_environment()->FastForwardBy(base::Seconds(2)); model_->ToggleShelfParty();
diff --git a/ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc b/ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc index 5f2889b..a621938 100644 --- a/ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc +++ b/ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc
@@ -1416,19 +1416,17 @@ item_list->emplace_back(toggle_all_desks_shortcut); } - if (ash::features::IsCalendarViewEnabled()) { - const ash::KeyboardShortcutItem toggle_calendar = { - // |categories| - {ShortcutCategory::kSystemAndDisplay}, - IDS_KSV_DESCRIPTION_TOGGLE_CALENDAR, - {}, - // |accelerator_ids| - {}, - // |shortcut_key_codes| - {{ui::VKEY_COMMAND, ui::VKEY_UNKNOWN, ui::VKEY_C}}}; + const ash::KeyboardShortcutItem toggle_calendar = { + // |categories| + {ShortcutCategory::kSystemAndDisplay}, + IDS_KSV_DESCRIPTION_TOGGLE_CALENDAR, + {}, + // |accelerator_ids| + {}, + // |shortcut_key_codes| + {{ui::VKEY_COMMAND, ui::VKEY_UNKNOWN, ui::VKEY_C}}}; - item_list->emplace_back(toggle_calendar); - } + item_list->emplace_back(toggle_calendar); for (auto& item : *item_list) { if (item.shortcut_key_codes.empty() && !item.accelerator_ids.empty()) { @@ -1444,15 +1442,17 @@ // ui::VKEY_UNKNOWN is used as a separator and will be shown as a // highlighted "+" sign between the bubble views and the rest of the // text. - if (!item.shortcut_key_codes.empty()) + if (!item.shortcut_key_codes.empty()) { item.shortcut_key_codes.push_back(ui::VKEY_UNKNOWN); + } item.shortcut_key_codes.push_back(GetKeyCodeForModifier(modifier)); } } // For non grouped accelerators, we need to populate the key as well. if (item.accelerator_ids.size() == 1) { - if (!item.shortcut_key_codes.empty()) + if (!item.shortcut_key_codes.empty()) { item.shortcut_key_codes.push_back(ui::VKEY_UNKNOWN); + } item.shortcut_key_codes.push_back(accelerator_id.keycode); } }
diff --git a/ash/system/accessibility/accessibility_feature_pod_controller.cc b/ash/system/accessibility/accessibility_feature_pod_controller.cc index e7c5484..b99983b16 100644 --- a/ash/system/accessibility/accessibility_feature_pod_controller.cc +++ b/ash/system/accessibility/accessibility_feature_pod_controller.cc
@@ -56,6 +56,7 @@ base::BindRepeating(&FeaturePodControllerBase::OnIconPressed, weak_ptr_factory_.GetWeakPtr()), /*is_togglable=*/false); + feature_tile->SetID(VIEW_ID_ACCESSIBILITY_FEATURE_TILE); feature_tile->SetVectorIcon(kUnifiedMenuAccessibilityIcon); feature_tile->SetLabel( l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY));
diff --git a/ash/system/accessibility/unified_accessibility_detailed_view_controller.h b/ash/system/accessibility/unified_accessibility_detailed_view_controller.h index dfe26dc1..1f56a5f 100644 --- a/ash/system/accessibility/unified_accessibility_detailed_view_controller.h +++ b/ash/system/accessibility/unified_accessibility_detailed_view_controller.h
@@ -38,6 +38,10 @@ // AccessibilityObserver: void OnAccessibilityStatusChanged() override; + AccessibilityDetailedView* accessibility_detailed_view_for_testing() { + return view_; + } + private: const std::unique_ptr<DetailedViewDelegate> detailed_view_delegate_;
diff --git a/ash/system/eche/eche_tray.cc b/ash/system/eche/eche_tray.cc index c81d119..b99f289 100644 --- a/ash/system/eche/eche_tray.cc +++ b/ash/system/eche/eche_tray.cc
@@ -376,13 +376,6 @@ case eche_app::mojom::StreamStatus::kStreamStatusInitializing: is_stream_started_ = false; break; - case eche_app::mojom::StreamStatus::kStreamStatusConnected: - is_stream_started_ = false; - break; - case eche_app::mojom::StreamStatus::kStreamStatusFailed: - is_stream_started_ = false; - PurgeAndClose(); - break; case eche_app::mojom::StreamStatus::kStreamStatusUnknown: PA_LOG(WARNING) << "Unexpected stream status"; is_stream_started_ = false;
diff --git a/ash/system/eche/eche_tray_unittest.cc b/ash/system/eche/eche_tray_unittest.cc index 01878a8e..41f92bf 100644 --- a/ash/system/eche/eche_tray_unittest.cc +++ b/ash/system/eche/eche_tray_unittest.cc
@@ -273,7 +273,7 @@ EXPECT_FALSE(phone_hub_tray()->eche_loading_indicator()->GetAnimating()); } -TEST_F(EcheTrayTest, EcheTrayCreatesBubbleButStreamStatusChangedToStopped) { +TEST_F(EcheTrayTest, EcheTrayCreatesBubbleButStreamStatusChanged) { // Verify the eche tray button is not active, and the eche tray bubble // is not shown initially. EXPECT_FALSE(eche_tray()->is_active()); @@ -308,41 +308,6 @@ EXPECT_FALSE(eche_tray()->GetVisible()); } -TEST_F(EcheTrayTest, EcheTrayCreatesBubbleButStreamStatusChangedToFailed) { - // Verify the eche tray button is not active, and the eche tray bubble - // is not shown initially. - EXPECT_FALSE(eche_tray()->is_active()); - EXPECT_FALSE(eche_tray()->get_bubble_wrapper_for_test()); - - // Allow us to create the bubble but it is not visible until we need this - // bubble to show up. - eche_tray()->LoadBubble(GURL("http://google.com"), CreateTestImage(), - u"app 1", u"your phone"); - - EXPECT_FALSE(eche_tray()->is_active()); - EXPECT_TRUE(eche_tray()->get_bubble_wrapper_for_test()); - EXPECT_FALSE( - eche_tray()->get_bubble_wrapper_for_test()->bubble_view()->GetVisible()); - - // When the streaming status changes, the bubble should show up. - eche_tray()->OnStreamStatusChanged( - eche_app::mojom::StreamStatus::kStreamStatusStarted); - // Wait for the tray bubble widget to open. - base::RunLoop().RunUntilIdle(); - EXPECT_TRUE(eche_tray()->is_active()); - EXPECT_TRUE(eche_tray()->get_bubble_wrapper_for_test()); - EXPECT_TRUE( - eche_tray()->get_bubble_wrapper_for_test()->bubble_view()->GetVisible()); - - // Change the streaming status, the bubble should be closed. - eche_tray()->OnStreamStatusChanged( - eche_app::mojom::StreamStatus::kStreamStatusFailed); - // Wait for the tray bubble widget to close. - base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(eche_tray()->is_active()); - EXPECT_FALSE(eche_tray()->GetVisible()); -} - TEST_F(EcheTrayTest, EcheTrayMinimizeButtonClicked) { eche_tray()->LoadBubble(GURL("http://google.com"), CreateTestImage(), u"app 1", u"your phone");
diff --git a/ash/system/input_device_settings/input_device_settings_controller_impl.cc b/ash/system/input_device_settings/input_device_settings_controller_impl.cc index 3138c91..bcb7631 100644 --- a/ash/system/input_device_settings/input_device_settings_controller_impl.cc +++ b/ash/system/input_device_settings/input_device_settings_controller_impl.cc
@@ -401,12 +401,14 @@ } } -void InputDeviceSettingsControllerImpl::DispatchKeyboardDisconnected( - DeviceId id) { +void InputDeviceSettingsControllerImpl:: + DispatchKeyboardDisconnectedAndEraseFromList(DeviceId id) { DCHECK(base::Contains(keyboards_, id)); - const auto& keyboard = *keyboards_.at(id); + auto keyboard_iter = keyboards_.find(id); + auto keyboard = std::move(keyboard_iter->second); + keyboards_.erase(keyboard_iter); for (auto& observer : observers_) { - observer.OnKeyboardDisconnected(keyboard); + observer.OnKeyboardDisconnected(*keyboard); } } @@ -427,12 +429,14 @@ } } -void InputDeviceSettingsControllerImpl::DispatchTouchpadDisconnected( - DeviceId id) { +void InputDeviceSettingsControllerImpl:: + DispatchTouchpadDisconnectedAndEraseFromList(DeviceId id) { DCHECK(base::Contains(touchpads_, id)); - const auto& touchpad = *touchpads_.at(id); + auto touchpad_iter = touchpads_.find(id); + auto touchpad = std::move(touchpad_iter->second); + touchpads_.erase(touchpad_iter); for (auto& observer : observers_) { - observer.OnTouchpadDisconnected(touchpad); + observer.OnTouchpadDisconnected(*touchpad); } } @@ -453,11 +457,14 @@ } } -void InputDeviceSettingsControllerImpl::DispatchMouseDisconnected(DeviceId id) { +void InputDeviceSettingsControllerImpl:: + DispatchMouseDisconnectedAndEraseFromList(DeviceId id) { DCHECK(base::Contains(mice_, id)); - const auto& mouse = *mice_.at(id); + auto mouse_iter = mice_.find(id); + auto mouse = std::move(mouse_iter->second); + mice_.erase(mouse_iter); for (auto& observer : observers_) { - observer.OnMouseDisconnected(mouse); + observer.OnMouseDisconnected(*mouse); } } @@ -479,12 +486,14 @@ } } -void InputDeviceSettingsControllerImpl::DispatchPointingStickDisconnected( - DeviceId id) { +void InputDeviceSettingsControllerImpl:: + DispatchPointingStickDisconnectedAndEraseFromList(DeviceId id) { DCHECK(base::Contains(pointing_sticks_, id)); - const auto& pointing_stick = *pointing_sticks_.at(id); + auto pointing_stick_iter = pointing_sticks_.find(id); + auto pointing_stick = std::move(pointing_stick_iter->second); + pointing_sticks_.erase(pointing_stick_iter); for (auto& observer : observers_) { - observer.OnPointingStickDisconnected(pointing_stick); + observer.OnPointingStickDisconnected(*pointing_stick); } } @@ -513,8 +522,7 @@ } for (const auto id : keyboard_ids_to_remove) { - DispatchKeyboardDisconnected(id); - keyboards_.erase(id); + DispatchKeyboardDisconnectedAndEraseFromList(id); } } @@ -532,8 +540,7 @@ } for (const auto id : touchpad_ids_to_remove) { - DispatchTouchpadDisconnected(id); - touchpads_.erase(id); + DispatchTouchpadDisconnectedAndEraseFromList(id); } } @@ -551,8 +558,7 @@ } for (const auto id : mouse_ids_to_remove) { - DispatchMouseDisconnected(id); - mice_.erase(id); + DispatchMouseDisconnectedAndEraseFromList(id); } } @@ -571,8 +577,7 @@ } for (const auto id : pointing_stick_ids_to_remove) { - DispatchPointingStickDisconnected(id); - pointing_sticks_.erase(id); + DispatchPointingStickDisconnectedAndEraseFromList(id); } }
diff --git a/ash/system/input_device_settings/input_device_settings_controller_impl.h b/ash/system/input_device_settings/input_device_settings_controller_impl.h index 80007008..5aadb39c 100644 --- a/ash/system/input_device_settings/input_device_settings_controller_impl.h +++ b/ash/system/input_device_settings/input_device_settings_controller_impl.h
@@ -85,19 +85,19 @@ void Init(); void DispatchKeyboardConnected(DeviceId id); - void DispatchKeyboardDisconnected(DeviceId id); + void DispatchKeyboardDisconnectedAndEraseFromList(DeviceId id); void DispatchKeyboardSettingsChanged(DeviceId id); void DispatchTouchpadConnected(DeviceId id); - void DispatchTouchpadDisconnected(DeviceId id); + void DispatchTouchpadDisconnectedAndEraseFromList(DeviceId id); void DispatchTouchpadSettingsChanged(DeviceId id); void DispatchMouseConnected(DeviceId id); - void DispatchMouseDisconnected(DeviceId id); + void DispatchMouseDisconnectedAndEraseFromList(DeviceId id); void DispatchMouseSettingsChanged(DeviceId id); void DispatchPointingStickConnected(DeviceId id); - void DispatchPointingStickDisconnected(DeviceId id); + void DispatchPointingStickDisconnectedAndEraseFromList(DeviceId id); void DispatchPointingStickSettingsChanged(DeviceId id); base::ObserverList<InputDeviceSettingsController::Observer> observers_;
diff --git a/ash/system/power/power_button_controller.h b/ash/system/power/power_button_controller.h index bf46480..b0be0cec 100644 --- a/ash/system/power/power_button_controller.h +++ b/ash/system/power/power_button_controller.h
@@ -156,7 +156,7 @@ void OnTabletModeStarted() override; void OnTabletModeEnded() override; - // Used by the SecurityCurtainController to notify when power button is + // Used by the `ash::curtain::Session` to notify when power button is // enabled/disabled. void OnSecurityCurtainEnabled(); void OnSecurityCurtainDisabled();
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc index 7e433ae7..a1379ee 100644 --- a/ash/system/status_area_widget.cc +++ b/ash/system/status_area_widget.cc
@@ -74,8 +74,9 @@ StatusAreaWidget::ScopedTrayBubbleCounter::~ScopedTrayBubbleCounter() { // ScopedTrayBubbleCounter may live longer than StatusAreaWidget. - if (!status_area_widget_) + if (!status_area_widget_) { return; + } --status_area_widget_->tray_bubble_count_; if (status_area_widget_->tray_bubble_count_ == 0) { @@ -163,10 +164,8 @@ auto unified_system_tray = std::make_unique<UnifiedSystemTray>(shelf_); unified_system_tray_ = unified_system_tray.get(); - if (features::IsCalendarViewEnabled()) { - date_tray_ = - AddTrayButton(std::make_unique<DateTray>(shelf_, unified_system_tray_)); - } + date_tray_ = + AddTrayButton(std::make_unique<DateTray>(shelf_, unified_system_tray_)); AddTrayButton(std::move(unified_system_tray)); overview_button_tray_ = @@ -208,8 +207,9 @@ // If QsRevamp flag is enabled, `notification_center_tray_` may be null in // some unittests. During the test environment tear-down, removing the // observer will lead to a crash. - if (features::IsQsRevampEnabled() && notification_center_tray_) + if (features::IsQsRevampEnabled() && notification_center_tray_) { notification_center_tray_->RemoveObserver(this); + } // If QsRevamp flag is enabled, reset `animation_controller_` before // destroying `notification_center_tray_` so that we don't run into a UaF. @@ -225,22 +225,23 @@ } void StatusAreaWidget::UpdateAfterLoginStatusChange(LoginStatus login_status) { - if (login_status_ == login_status) + if (login_status_ == login_status) { return; + } login_status_ = login_status; - for (TrayBackgroundView* tray_button : tray_buttons_) + for (TrayBackgroundView* tray_button : tray_buttons_) { tray_button->UpdateAfterLoginStatusChange(); + } } void StatusAreaWidget::SetSystemTrayVisibility(bool visible) { unified_system_tray_->SetVisiblePreferred(visible); + date_tray_->SetVisiblePreferred(visible); - if (features::IsCalendarViewEnabled()) - date_tray_->SetVisiblePreferred(visible); - - if (features::IsQsRevampEnabled()) + if (features::IsQsRevampEnabled()) { notification_center_tray_->OnSystemTrayVisibilityChanged(visible); + } if (visible) { Show(); @@ -252,8 +253,9 @@ void StatusAreaWidget::OnSessionStateChanged( session_manager::SessionState state) { - for (TrayBackgroundView* tray_button : tray_buttons_) + for (TrayBackgroundView* tray_button : tray_buttons_) { tray_button->UpdateBackground(); + } } void StatusAreaWidget::UpdateCollapseState() { @@ -310,8 +312,9 @@ case TrayBackgroundViewCatalogName::kVirtualKeyboardStatusArea: case TrayBackgroundViewCatalogName::kWmMode: case TrayBackgroundViewCatalogName::kVideoConferenceTray: - if (!tray_button->GetVisible()) + if (!tray_button->GetVisible()) { continue; + } visible_pod_count += 1; break; } @@ -327,8 +330,9 @@ } void StatusAreaWidget::CalculateTargetBounds() { - for (TrayBackgroundView* tray_button : tray_buttons_) + for (TrayBackgroundView* tray_button : tray_buttons_) { tray_button->CalculateTargetBounds(); + } status_area_widget_delegate_->CalculateTargetBounds(); gfx::Size status_size(status_area_widget_delegate_->GetTargetBounds().size()); @@ -336,18 +340,20 @@ const gfx::Point shelf_origin = shelf_->shelf_widget()->GetTargetBounds().origin(); - if (shelf_->IsHorizontalAlignment()) + if (shelf_->IsHorizontalAlignment()) { status_size.set_height(shelf_size.height()); - else + } else { status_size.set_width(shelf_size.width()); + } gfx::Point status_origin = shelf_->SelectValueForShelfAlignment( gfx::Point(0, 0), gfx::Point(shelf_size.width() - status_size.width(), shelf_size.height() - status_size.height()), gfx::Point(0, shelf_size.height() - status_size.height())); - if (shelf_->IsHorizontalAlignment() && !base::i18n::IsRTL()) + if (shelf_->IsHorizontalAlignment() && !base::i18n::IsRTL()) { status_origin.set_x(shelf_size.width() - status_size.width()); + } status_origin.Offset(shelf_origin.x(), shelf_origin.y()); target_bounds_ = gfx::Rect(status_origin, status_size); } @@ -358,24 +364,28 @@ void StatusAreaWidget::UpdateLayout(bool animate) { const LayoutInputs new_layout_inputs = GetLayoutInputs(); - if (layout_inputs_ == new_layout_inputs) + if (layout_inputs_ == new_layout_inputs) { return; + } - if (!new_layout_inputs.should_animate) + if (!new_layout_inputs.should_animate) { animate = false; + } - for (TrayBackgroundView* tray_button : tray_buttons_) + for (TrayBackgroundView* tray_button : tray_buttons_) { tray_button->UpdateLayout(); + } status_area_widget_delegate_->UpdateLayout(animate); // Having a window which is visible but does not have an opacity is an // illegal state. ui::Layer* layer = GetNativeView()->layer(); layer->SetOpacity(new_layout_inputs.opacity); - if (new_layout_inputs.opacity) + if (new_layout_inputs.opacity) { ShowInactive(); - else + } else { Hide(); + } ui::ScopedLayerAnimationSettings animation_setter(layer->GetAnimator()); animation_setter.SetTransitionDuration( @@ -389,10 +399,11 @@ } void StatusAreaWidget::UpdateTargetBoundsForGesture(int shelf_position) { - if (shelf_->IsHorizontalAlignment()) + if (shelf_->IsHorizontalAlignment()) { target_bounds_.set_y(shelf_position); - else + } else { target_bounds_.set_x(shelf_position); + } } void StatusAreaWidget::HandleLocaleChange() { @@ -416,8 +427,9 @@ } void StatusAreaWidget::CalculateButtonVisibilityForCollapsedState() { - if (!initialized_) + if (!initialized_) { return; + } DCHECK(collapse_state_ == CollapseState::COLLAPSED); @@ -437,8 +449,9 @@ // First, reset all tray button to be hidden. overflow_button_tray_->ResetStateToCollapsed(); - for (TrayBackgroundView* tray_button : tray_buttons_) + for (TrayBackgroundView* tray_button : tray_buttons_) { tray_button->set_show_when_collapsed(false); + } // Iterate backwards making tray buttons visible until |available_width| is // exceeded. @@ -447,11 +460,13 @@ int used_width = 0; for (TrayBackgroundView* tray : base::Reversed(tray_buttons_)) { // Skip non-enabled tray buttons. - if (!tray->visible_preferred()) + if (!tray->visible_preferred()) { continue; + } // Skip |stop_recording_button_tray_| since it's always visible. - if (tray == stop_recording_button_tray_) + if (tray == stop_recording_button_tray_) { continue; + } // Show overflow button once available width is exceeded. int tray_width = tray->tray_container()->GetPreferredSize().width(); @@ -461,8 +476,10 @@ // Maybe remove the last tray button to make room for the overflow tray. int overflow_button_width = overflow_button_tray_->GetPreferredSize().width(); - if (previous_tray && used_width + overflow_button_width > available_width) + if (previous_tray && + used_width + overflow_button_width > available_width) { previous_tray->set_show_when_collapsed(false); + } break; } @@ -472,13 +489,15 @@ } // Skip |stop_recording_button_tray_| so it's always visible. - if (stop_recording_button_tray_->visible_preferred()) + if (stop_recording_button_tray_->visible_preferred()) { stop_recording_button_tray_->set_show_when_collapsed(true); + } overflow_button_tray_->SetVisiblePreferred(show_overflow_button); overflow_button_tray_->UpdateAfterStatusAreaCollapseChange(); - for (TrayBackgroundView* tray_button : tray_buttons_) + for (TrayBackgroundView* tray_button : tray_buttons_) { tray_button->UpdateAfterStatusAreaCollapseChange(); + } } void StatusAreaWidget::EnsureTrayOrder() { @@ -494,12 +513,14 @@ const { // The status area is only collapsible in tablet mode. Otherwise, we just show // all trays. - if (!Shell::Get()->tablet_mode_controller()) + if (!Shell::Get()->tablet_mode_controller()) { return CollapseState::NOT_COLLAPSIBLE; + } // An update may occur during initialization of the shelf, so just skip it. - if (!initialized_) + if (!initialized_) { return CollapseState::NOT_COLLAPSIBLE; + } bool is_collapsible = Shell::Get()->tablet_mode_controller()->InTabletMode() && @@ -529,15 +550,18 @@ for (TrayBackgroundView* tray : base::Reversed(tray_buttons_)) { // If we reach the final overflow tray button, then all the tray buttons // fit and there is no need for a collapse state. - if (tray == overflow_button_tray_) + if (tray == overflow_button_tray_) { return CollapseState::NOT_COLLAPSIBLE; + } // Skip non-enabled tray buttons. - if (!tray->visible_preferred()) + if (!tray->visible_preferred()) { continue; + } int tray_width = tray->tray_container()->GetPreferredSize().width(); - if (used_width + tray_width > available_width) + if (used_width + tray_width > available_width) { break; + } used_width += tray_width; } @@ -549,15 +573,17 @@ // Use the target visibility of the layer instead of the visibility of the // view because the view is still visible when fading away, but we do not want // to anchor to this element in that case. - if (overview_button_tray_->layer()->GetTargetVisibility()) + if (overview_button_tray_->layer()->GetTargetVisibility()) { return overview_button_tray_; + } return unified_system_tray_; } gfx::Rect StatusAreaWidget::GetMediaTrayAnchorRect() const { - if (!media_tray_) + if (!media_tray_) { return gfx::Rect(); + } // Calculate anchor rect of media tray bubble. This is required because the // bubble can be visible while the tray button is hidden. (e.g. when user @@ -572,8 +598,9 @@ continue; } - if (!found_media_tray || !tray_button->GetVisible()) + if (!found_media_tray || !tray_button->GetVisible()) { continue; + } offset += shelf_->IsHorizontalAlignment() ? tray_button->width() : tray_button->height(); @@ -610,30 +637,35 @@ bool StatusAreaWidget::ShouldShowShelf() const { // If it has main bubble, return true. - if (unified_system_tray_->IsBubbleShown()) + if (unified_system_tray_->IsBubbleShown()) { return true; + } // If any tray is showing a context menu, the shelf should be visible. for (TrayBackgroundView* tray_button : tray_buttons_) { - if (tray_button->IsShowingMenu()) + if (tray_button->IsShowingMenu()) { return true; + } } // If it has a slider bubble, return false. - if (unified_system_tray_->IsSliderBubbleShown()) + if (unified_system_tray_->IsSliderBubbleShown()) { return false; + } // Some TrayBackgroundViews' cache their bubble, the shelf should only be // forced to show if the bubble is visible, and we should not show the shelf // for cached, hidden bubbles. if (tray_bubble_count_ > 0) { for (TrayBackgroundView* tray_button : tray_buttons_) { - if (!tray_button->GetBubbleView()) + if (!tray_button->GetBubbleView()) { continue; + } // Any tray bubble is showing, show shelf. - if (tray_button->GetBubbleView()->GetVisible()) + if (tray_button->GetBubbleView()->GetVisible()) { return true; + } // Tray bubble view is not null and not visible, tray bubble is cached // for hidden case. If the tray caches the view for hidden, we should @@ -654,22 +686,26 @@ } void StatusAreaWidget::SchedulePaint() { - for (TrayBackgroundView* tray_button : tray_buttons_) + for (TrayBackgroundView* tray_button : tray_buttons_) { tray_button->SchedulePaint(); + } } bool StatusAreaWidget::OnNativeWidgetActivationChanged(bool active) { - if (!Widget::OnNativeWidgetActivationChanged(active)) + if (!Widget::OnNativeWidgetActivationChanged(active)) { return false; - if (active) + } + if (active) { status_area_widget_delegate_->SetPaneFocusAndFocusDefault(); + } return true; } void StatusAreaWidget::OnViewVisibilityChanged(views::View* observed_view, views::View* starting_view) { - if (observed_view != notification_center_tray_) + if (observed_view != notification_center_tray_) { return; + } UpdateDateTrayRoundedCorners(); } @@ -706,8 +742,9 @@ void StatusAreaWidget::OnScrollEvent(ui::ScrollEvent* event) { shelf_->ProcessScrollEvent(event); - if (!event->handled()) + if (!event->handled()) { views::Widget::OnScrollEvent(event); + } } template <typename TrayButtonT> @@ -725,8 +762,9 @@ DCHECK(tray_buttons_.size() < std::numeric_limits<decltype(child_visibility_bitmask)>::digits); for (unsigned int i = 0; i < tray_buttons_.size(); ++i) { - if (tray_buttons_[i]->GetVisible()) + if (tray_buttons_[i]->GetVisible()) { child_visibility_bitmask |= 1 << i; + } } bool should_animate = true; @@ -750,8 +788,9 @@ } void StatusAreaWidget::UpdateDateTrayRoundedCorners() { - if (!features::IsQsRevampEnabled() || !date_tray_) + if (!features::IsQsRevampEnabled() || !date_tray_) { return; + } date_tray_->SetRoundedCornerBehavior( notification_center_tray_->GetVisible() @@ -763,16 +802,15 @@ const int shelf_width = shelf_->shelf_widget()->GetClientAreaBoundsInScreen().width(); - if (!force_collapsible) + if (!force_collapsible) { return shelf_width / 2 - kStatusAreaLeftPaddingForOverflow; + } int available_width = kStatusAreaForceCollapseAvailableWidth; - // If calendar view is enabled, add the date tray width to the collapse - // available width. - if (features::IsCalendarViewEnabled()) { - DCHECK(date_tray_); - available_width += date_tray_->tray_container()->GetPreferredSize().width(); - } + // Add the date tray width to the collapse available width. + DCHECK(date_tray_); + available_width += date_tray_->tray_container()->GetPreferredSize().width(); + return available_width; }
diff --git a/ash/system/time/calendar_view_unittest.cc b/ash/system/time/calendar_view_unittest.cc index b586b79..ad5beac 100644 --- a/ash/system/time/calendar_view_unittest.cc +++ b/ash/system/time/calendar_view_unittest.cc
@@ -95,8 +95,9 @@ for (const auto* child_view : month->children()) { auto* current_date_cell = static_cast<const views::LabelButton*>(child_view); - if (day != current_date_cell->GetText()) + if (day != current_date_cell->GetText()) { continue; + } date_cell = current_date_cell; break; @@ -667,14 +668,17 @@ const int scroll_up_count = 10; const int scroll_down_count = scroll_up_count - 1; // Scroll up. - for (int i = 0; i < scroll_up_count; ++i) + for (int i = 0; i < scroll_up_count; ++i) { ScrollUpOneMonth(); + } // Return to today. - for (int i = 0; i < scroll_up_count; ++i) + for (int i = 0; i < scroll_up_count; ++i) { ScrollDownOneMonth(); + } // Scroll down from today. - for (int i = 0; i < scroll_down_count; ++i) + for (int i = 0; i < scroll_down_count; ++i) { ScrollDownOneMonth(); + } DestroyCalendarViewWidget(); @@ -711,8 +715,9 @@ views::View* focused_now) override {} void OnDidChangeFocus(views::View* focused_before, views::View* focused_now) override { - if (found_) + if (found_) { return; + } steps_taken_++; found_ = static_cast<const views::LabelButton*>(focused_now)->GetText() == @@ -1389,8 +1394,9 @@ for (const auto* child_view : month->children()) { auto* current_date_cell = static_cast<const views::LabelButton*>(child_view); - if (day != current_date_cell->GetText()) + if (day != current_date_cell->GetText()) { continue; + } date_cell = current_date_cell; break; @@ -2074,8 +2080,8 @@ void SetUp() override { scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>(); - scoped_feature_list_->InitWithFeatures( - {features::kCalendarView, features::kNotificationsRefresh}, {}); + scoped_feature_list_->InitWithFeatures({features::kNotificationsRefresh}, + {}); AshTestBase::SetUp(); } @@ -2123,8 +2129,9 @@ while ((current_focusable_view = message_center_focus_manager()->GetNextFocusableView( current_focusable_view, widget, /*reverse=*/false, - /*dont_loop=*/true))) + /*dont_loop=*/true))) { count++; + } return count; } @@ -2166,8 +2173,9 @@ PressTab(); // Keep tabbing until exiting the message center. - for (int i = 0; i < number_of_focusable_views_in_message_center; i++) + for (int i = 0; i < number_of_focusable_views_in_message_center; i++) { PressTab(); + } // The "back to today" `PillButton` is the first focused view. EXPECT_STREQ(calendar_focus_manager()->GetFocusedView()->GetClassName(), @@ -2177,8 +2185,9 @@ PressShiftTab(); // Keep tabbing backwards until exiting the message center. - for (int i = 0; i < number_of_focusable_views_in_message_center; i++) + for (int i = 0; i < number_of_focusable_views_in_message_center; i++) { PressShiftTab(); + } // Today's date cell should be focused now. EXPECT_EQ(current_date_cell_view, calendar_focus_manager()->GetFocusedView()); @@ -2195,8 +2204,7 @@ void SetUp() override { scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>(); - scoped_feature_list_->InitWithFeatures( - {features::kCalendarView, features::kCalendarJelly}, {}); + scoped_feature_list_->InitWithFeatures({features::kCalendarJelly}, {}); CalendarViewTest::SetUp(); }
diff --git a/ash/system/time/time_tray_item_view_unittest.cc b/ash/system/time/time_tray_item_view_unittest.cc index af92cf1..12ff54c 100644 --- a/ash/system/time/time_tray_item_view_unittest.cc +++ b/ash/system/time/time_tray_item_view_unittest.cc
@@ -8,12 +8,10 @@ #include "ash/shelf/shelf.h" #include "ash/system/time/time_view.h" #include "ash/test/ash_test_base.h" -#include "base/test/scoped_feature_list.h" namespace ash { -class TimeTrayItemViewTest : public AshTestBase, - public testing::WithParamInterface<bool> { +class TimeTrayItemViewTest : public AshTestBase { public: TimeTrayItemViewTest() = default; ~TimeTrayItemViewTest() override = default; @@ -21,9 +19,6 @@ // AshTestBase: void SetUp() override { AshTestBase::SetUp(); - scoped_feature_list_.InitWithFeatureState(features::kCalendarView, - IsCalendarViewEnabled()); - time_tray_item_view_ = std::make_unique<TimeTrayItemView>( GetPrimaryShelf(), TimeView::Type::kTime); } @@ -33,8 +28,6 @@ AshTestBase::TearDown(); } - bool IsCalendarViewEnabled() { return GetParam(); } - // Returns true if the time view is in horizontal layout, false if it is in // vertical layout. bool IsTimeViewInHorizontalLayout() { @@ -44,15 +37,10 @@ } protected: - base::test::ScopedFeatureList scoped_feature_list_; std::unique_ptr<TimeTrayItemView> time_tray_item_view_; }; -INSTANTIATE_TEST_SUITE_P(All, - TimeTrayItemViewTest, - testing::Bool() /* IsCalendarViewEnabled() */); - -TEST_P(TimeTrayItemViewTest, ShelfAlignment) { +TEST_F(TimeTrayItemViewTest, ShelfAlignment) { // The tray should show time horizontal view when the shelf is bottom. GetPrimaryShelf()->SetAlignment(ShelfAlignment::kBottom); time_tray_item_view_->UpdateAlignmentForShelf(GetPrimaryShelf());
diff --git a/ash/system/tray/tray_detailed_view.h b/ash/system/tray/tray_detailed_view.h index 7567a776..80b8253 100644 --- a/ash/system/tray/tray_detailed_view.h +++ b/ash/system/tray/tray_detailed_view.h
@@ -50,6 +50,8 @@ // Setter for `progress_bar_` accessibility label. void OverrideProgressBarAccessibleName(const std::u16string& name); + views::ScrollView* scroll_view_for_testing() { return scroller_; } + protected: // views::View: void Layout() override;
diff --git a/ash/system/tray/tray_event_filter.cc b/ash/system/tray/tray_event_filter.cc index b7d85106..6ef02f2 100644 --- a/ash/system/tray/tray_event_filter.cc +++ b/ash/system/tray/tray_event_filter.cc
@@ -35,35 +35,41 @@ void TrayEventFilter::AddBubble(TrayBubbleBase* bubble) { bool was_empty = bubbles_.empty(); bubbles_.insert(bubble); - if (was_empty && !bubbles_.empty()) + if (was_empty && !bubbles_.empty()) { Shell::Get()->AddPreTargetHandler(this); + } } void TrayEventFilter::RemoveBubble(TrayBubbleBase* bubble) { bubbles_.erase(bubble); - if (bubbles_.empty()) + if (bubbles_.empty()) { Shell::Get()->RemovePreTargetHandler(this); + } } void TrayEventFilter::OnMouseEvent(ui::MouseEvent* event) { - if (event->type() == ui::ET_MOUSE_PRESSED) + if (event->type() == ui::ET_MOUSE_PRESSED) { ProcessPressedEvent(*event); + } } void TrayEventFilter::OnTouchEvent(ui::TouchEvent* event) { - if (event->type() == ui::ET_TOUCH_PRESSED) + if (event->type() == ui::ET_TOUCH_PRESSED) { ProcessPressedEvent(*event); + } } void TrayEventFilter::OnGestureEvent(ui::GestureEvent* event) { - if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN) + if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN) { ProcessPressedEvent(*event); + } } void TrayEventFilter::ProcessPressedEvent(const ui::LocatedEvent& event) { // Users in a capture session may be trying to capture tray bubble(s). - if (capture_mode_util::IsCaptureModeActive()) + if (capture_mode_util::IsCaptureModeActive()) { return; + } // The hit target window for the virtual keyboard isn't the same as its // views::Widget. @@ -78,8 +84,9 @@ const int container_id = container->GetId(); // Don't process events that occurred inside an embedded menu, for example // the right-click menu in a popup notification. - if (container_id == kShellWindowId_MenuContainer) + if (container_id == kShellWindowId_MenuContainer) { return; + } // Don't process events that occurred inside a popup notification // from message center. if (container_id == kShellWindowId_ShelfContainer && @@ -89,8 +96,9 @@ return; } // Don't process events that occurred inside a virtual keyboard. - if (container_id == kShellWindowId_VirtualKeyboardContainer) + if (container_id == kShellWindowId_VirtualKeyboardContainer) { return; + } } std::set<TrayBackgroundView*> trays; @@ -101,8 +109,9 @@ : event.root_location(); for (const TrayBubbleBase* bubble : bubbles_) { const views::Widget* bubble_widget = bubble->GetBubbleWidget(); - if (!bubble_widget) + if (!bubble_widget) { continue; + } gfx::Rect bounds = bubble_widget->GetWindowBoundsInScreen(); bounds.Inset(bubble->GetBubbleView()->GetBorderInsets()); @@ -133,8 +142,7 @@ // When Quick Settings bubble is opened and the date tray is clicked, the // bubble should not be closed since it will transition to show calendar. - if (features::IsCalendarViewEnabled() && - status_area->date_tray()->GetBoundsInScreen().Contains( + if (status_area->date_tray()->GetBoundsInScreen().Contains( screen_location)) { continue; } @@ -150,24 +158,27 @@ } } - if (bounds.Contains(screen_location)) + if (bounds.Contains(screen_location)) { continue; + } if (bubble->GetTray()) { // Maybe close the parent tray if the user drags on it. Otherwise, let the // tray logic handle the event and determine show/hide behavior if the // user clicks on the parent tray. bounds = bubble->GetTray()->GetBoundsInScreen(); if (bubble->GetTray()->GetVisible() && bounds.Contains(screen_location) && - event.type() != ui::ET_GESTURE_SCROLL_BEGIN) + event.type() != ui::ET_GESTURE_SCROLL_BEGIN) { continue; + } } trays.insert(bubble->GetTray()); } // Close all bubbles other than the one that the user clicked on. - for (TrayBackgroundView* tray_background_view : trays) + for (TrayBackgroundView* tray_background_view : trays) { tray_background_view->ClickedOutsideBubble(); + } } } // namespace ash
diff --git a/ash/system/unified/system_tray_test_api.cc b/ash/system/unified/system_tray_test_api.cc index 91f202e6..198dc200 100644 --- a/ash/system/unified/system_tray_test_api.cc +++ b/ash/system/unified/system_tray_test_api.cc
@@ -10,9 +10,12 @@ #include "ash/root_window_controller.h" #include "ash/shell.h" #include "ash/system/accessibility/select_to_speak/select_to_speak_tray.h" +#include "ash/system/accessibility/unified_accessibility_detailed_view_controller.h" #include "ash/system/status_area_widget.h" #include "ash/system/time/time_tray_item_view.h" #include "ash/system/time/time_view.h" +#include "ash/system/tray/tray_detailed_view.h" +#include "ash/system/tray/tray_toggle_button.h" #include "ash/system/unified/power_button.h" #include "ash/system/unified/quick_settings_footer.h" #include "ash/system/unified/unified_system_tray.h" @@ -78,6 +81,14 @@ GetTray()->bubble_->controller_->ShowNetworkDetailedView(true /* force */); } +AccessibilityDetailedView* SystemTrayTestApi::GetAccessibilityDetailedView() { + auto* unified_system_tray_controller = GetTray()->bubble_->controller_.get(); + DCHECK(unified_system_tray_controller->IsDetailedViewShown()); + return static_cast<UnifiedAccessibilityDetailedViewController*>( + unified_system_tray_controller->detailed_view_controller()) + ->accessibility_detailed_view_for_testing(); +} + bool SystemTrayTestApi::IsBubbleViewVisible(int view_id, bool open_tray) { if (open_tray) GetTray()->ShowBubble(); @@ -85,16 +96,16 @@ return view && view->GetVisible(); } -void SystemTrayTestApi::ScrollToShowView(int view_id) { - views::View* view = GetBubbleView(view_id); +bool SystemTrayTestApi::IsToggleOn(int view_id) { + auto* view = static_cast<TrayToggleButton*>(GetBubbleView(view_id)); DCHECK(view); + return view->GetIsOn(); +} - views::View* contents = view->parent(); - DCHECK(contents); - - views::ScrollView* scroll_view = - views::ScrollView::GetScrollViewForContents(contents); - DCHECK(scroll_view); +void SystemTrayTestApi::ScrollToShowView(views::ScrollView* scroll_view, + int view_id) { + views::View* view = GetBubbleView(view_id); + DCHECK(view && scroll_view->Contains(view)); gfx::Point view_center = view->GetBoundsInScreen().CenterPoint(); gfx::Rect scroll_bounds = scroll_view->GetBoundsInScreen();
diff --git a/ash/system/unified/unified_system_info_view.cc b/ash/system/unified/unified_system_info_view.cc index 291bc79c..62758dc 100644 --- a/ash/system/unified/unified_system_info_view.cc +++ b/ash/system/unified/unified_system_info_view.cc
@@ -118,9 +118,6 @@ Update(); Shell::Get()->system_tray_model()->clock()->AddObserver(this); - if (!features::IsCalendarViewEnabled()) - SetEnabled( - Shell::Get()->system_tray_model()->clock()->IsSettingsAvailable()); SetInstallFocusRingOnFocus(true); views::FocusRing::Get(this)->SetColorId(ui::kColorAshFocusRing); views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::OFF); @@ -141,7 +138,7 @@ quick_settings_metrics_util::RecordQsButtonActivated( QsButtonCatalogName::kDateViewButton); - if (features::IsCalendarViewEnabled() && controller_->IsExpanded()) { + if (controller_->IsExpanded()) { controller_->ShowCalendarView( calendar_metrics::CalendarViewShowSource::kDateView, calendar_metrics::GetEventType(event)); @@ -155,13 +152,10 @@ base::Time now = base::Time::Now(); label_->SetText(l10n_util::GetStringFUTF16( IDS_ASH_STATUS_TRAY_DATE, FormatDayOfWeek(now), FormatDate(now))); - if (features::IsCalendarViewEnabled()) { - SetAccessibleName(l10n_util::GetStringFUTF16( - IDS_ASH_CALENDAR_ENTRY_ACCESSIBLE_DESCRIPTION, - TimeFormatFriendlyDateAndTime(now))); - } else { - SetAccessibleName(TimeFormatFriendlyDateAndTime(now)); - } + + SetAccessibleName( + l10n_util::GetStringFUTF16(IDS_ASH_CALENDAR_ENTRY_ACCESSIBLE_DESCRIPTION, + TimeFormatFriendlyDateAndTime(now))); } gfx::Insets DateView::GetInsets() const { @@ -202,8 +196,9 @@ separator_view_->SetPreferredLength(kUnifiedSystemInfoHeight); const bool use_smart_charging_ui = UseSmartChargingUI(); - if (use_smart_charging_ui) + if (use_smart_charging_ui) { AddChildView(std::make_unique<BatteryIconView>(controller)); + } AddChildView(std::make_unique<BatteryLabelView>(controller, use_smart_charging_ui)); }
diff --git a/ash/system/unified/unified_system_tray.cc b/ash/system/unified/unified_system_tray.cc index a6c3f6d1..4edac3a 100644 --- a/ash/system/unified/unified_system_tray.cc +++ b/ash/system/unified/unified_system_tray.cc
@@ -175,10 +175,9 @@ void UnifiedSystemTray::UiDelegate::HideMessageCenter() {} UnifiedSystemTray::UnifiedSystemTray(Shelf* shelf) - : TrayBackgroundView( - shelf, - TrayBackgroundViewCatalogName::kUnifiedSystem, - (features::IsCalendarViewEnabled() ? kEndRounded : kAllRounded)), + : TrayBackgroundView(shelf, + TrayBackgroundViewCatalogName::kUnifiedSystem, + kEndRounded), ui_delegate_(std::make_unique<UiDelegate>(this)), model_(base::MakeRefCounted<UnifiedSystemTrayModel>(shelf)), slider_bubble_controller_(
diff --git a/ash/system/unified/unified_system_tray_controller.cc b/ash/system/unified/unified_system_tray_controller.cc index 6acc495..f2d9db7 100644 --- a/ash/system/unified/unified_system_tray_controller.cc +++ b/ash/system/unified/unified_system_tray_controller.cc
@@ -509,10 +509,6 @@ void UnifiedSystemTrayController::ShowCalendarView( calendar_metrics::CalendarViewShowSource show_source, calendar_metrics::CalendarEventSource event_source) { - if (!features::IsCalendarViewEnabled()) { - return; - } - calendar_metrics::RecordCalendarShowMetrics(show_source, event_source); ShowDetailedView(std::make_unique<UnifiedCalendarViewController>(this));
diff --git a/ash/webui/BUILD.gn b/ash/webui/BUILD.gn index 57e64bb..2e10d95 100644 --- a/ash/webui/BUILD.gn +++ b/ash/webui/BUILD.gn
@@ -36,7 +36,7 @@ "//ash/webui/scanning/mojom:unit_tests", "//ash/webui/shimless_rma/backend:unit_tests", "//ash/webui/shimless_rma/mojom:unit_tests", - "//ash/webui/shortcut_customization_ui/backend:unit_tests", + "//ash/webui/shortcut_customization_ui:unit_tests", "//ash/webui/shortcut_customization_ui/mojom:unit_tests", "//base", "//base/test:test_support",
diff --git a/ash/webui/diagnostics_ui/backend/input/input_data_provider_unittest.cc b/ash/webui/diagnostics_ui/backend/input/input_data_provider_unittest.cc index 59267f0..f018e91 100644 --- a/ash/webui/diagnostics_ui/backend/input/input_data_provider_unittest.cc +++ b/ash/webui/diagnostics_ui/backend/input/input_data_provider_unittest.cc
@@ -582,7 +582,7 @@ int* result) const override { return false; } - bool TopRowKeysAreFunctionKeys() const override { return false; } + bool TopRowKeysAreFunctionKeys(int device_id) const override { return false; } bool IsExtensionCommandRegistered(ui::KeyboardCode key_code, int flags) const override { return false;
diff --git a/ash/webui/eche_app_ui/eche_stream_status_change_handler_unittest.cc b/ash/webui/eche_app_ui/eche_stream_status_change_handler_unittest.cc index afead8755..1e93232 100644 --- a/ash/webui/eche_app_ui/eche_stream_status_change_handler_unittest.cc +++ b/ash/webui/eche_app_ui/eche_stream_status_change_handler_unittest.cc
@@ -145,10 +145,6 @@ EXPECT_EQ(mojom::StreamStatus::kStreamStatusInitializing, GetObservedStreamStatus()); - NotifyStreamStatus(mojom::StreamStatus::kStreamStatusConnected); - EXPECT_EQ(mojom::StreamStatus::kStreamStatusConnected, - GetObservedStreamStatus()); - NotifyStreamStatus(mojom::StreamStatus::kStreamStatusStarted); EXPECT_EQ(mojom::StreamStatus::kStreamStatusStarted, GetObservedStreamStatus()); @@ -156,10 +152,6 @@ NotifyStreamStatus(mojom::StreamStatus::kStreamStatusStopped); EXPECT_EQ(mojom::StreamStatus::kStreamStatusStopped, GetObservedStreamStatus()); - - NotifyStreamStatus(mojom::StreamStatus::kStreamStatusFailed); - EXPECT_EQ(mojom::StreamStatus::kStreamStatusFailed, - GetObservedStreamStatus()); } } // namespace eche_app
diff --git a/ash/webui/eche_app_ui/eche_tray_stream_status_observer_unittest.cc b/ash/webui/eche_app_ui/eche_tray_stream_status_observer_unittest.cc index 44f4e91..89c767e7 100644 --- a/ash/webui/eche_app_ui/eche_tray_stream_status_observer_unittest.cc +++ b/ash/webui/eche_app_ui/eche_tray_stream_status_observer_unittest.cc
@@ -152,25 +152,6 @@ EXPECT_FALSE(eche_tray()->is_active()); } -TEST_F(EcheTrayStreamStatusObserverTest, OnStreamStatusChangedFailed) { - LaunchBubble(GURL("http://google.com"), gfx::Image(), u"app 1", u"your phone", - base::BindOnce(&GracefulCloseFunction), - base::BindRepeating(&GracefulGoBackFunction)); - OnStreamStatusChanged(mojom::StreamStatus::kStreamStatusStarted); - - // Wait for Eche Tray to load Eche Web to complete. - base::RunLoop().RunUntilIdle(); - // Eche tray should be visible when streaming is active - EXPECT_TRUE(eche_tray()->get_bubble_wrapper_for_test()); - - OnStreamStatusChanged(mojom::StreamStatus::kStreamStatusFailed); - - // Wait for Eche Web to close. - base::RunLoop().RunUntilIdle(); - // Eche tray should not be visible when streaming is finished - EXPECT_FALSE(eche_tray()->is_active()); -} - TEST_F(EcheTrayStreamStatusObserverTest, StartGracefulCloseWhenFeatureStatusToIneligible) { ResetUnloadWebContent();
diff --git a/ash/webui/eche_app_ui/mojom/eche_app.mojom b/ash/webui/eche_app_ui/mojom/eche_app.mojom index 7d7ea1d..925eaa1 100644 --- a/ash/webui/eche_app_ui/mojom/eche_app.mojom +++ b/ash/webui/eche_app_ui/mojom/eche_app.mojom
@@ -108,8 +108,6 @@ kStreamStatusInitializing, // Eche browser is setting up video streaming kStreamStatusStarted, // Video streaming is set up and started kStreamStatusStopped, // Video streaming is stopped - kStreamStatusConnected, // Connected to phone, but not streaming video - kStreamStatusFailed, // Failed bootstrap connection }; // Interface to notify the video streaming status from Eche browser to Eche
diff --git a/ash/webui/help_app_ui/search/search_concept.cc b/ash/webui/help_app_ui/search/search_concept.cc index fba5709..878caab 100644 --- a/ash/webui/help_app_ui/search/search_concept.cc +++ b/ash/webui/help_app_ui/search/search_concept.cc
@@ -12,6 +12,7 @@ #include "base/files/file_util.h" #include "base/files/important_file_writer.h" #include "base/functional/bind.h" +#include "base/metrics/histogram_functions.h" #include "base/strings/utf_string_conversions.h" #include "base/task/bind_post_task.h" #include "base/task/thread_pool.h" @@ -23,6 +24,31 @@ using Concept = SearchConceptProto::Concept; +constexpr char kReadHistogram[] = + "Discover.SearchConcept.PersistenceReadStatus"; +constexpr char kWriteHistogram[] = + "Discover.SearchConcept.PersistenceWriteStatus"; + +// The result of reading a backing file from disk. These values persist to logs. +// Entries should not be renumbered and numeric values should never be reused. +enum class ReadStatus { + kOk = 0, + kMissing = 1, + kReadError = 2, + kParseError = 3, + kMaxValue = kParseError, +}; + +// The result of writing a backing file to disk. These values persist to logs. +// Entries should not be renumbered and numeric values should never be reused. +enum class WriteStatus { + kOk = 0, + kWriteError = 1, + kSerializationError = 2, + kReplaceError = 3, + kMaxValue = kReplaceError, +}; + // This should be incremented whenever a change to the search concept is made // that is incompatible with on-disk state. On reading, any state is wiped if // its version doesn't match. @@ -33,18 +59,22 @@ base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::BlockingType::MAY_BLOCK); if (!base::PathExists(file_path)) { + base::UmaHistogramEnumeration(kReadHistogram, ReadStatus::kMissing); return nullptr; } std::string proto_str; if (!base::ReadFileToString(file_path, &proto_str)) { + base::UmaHistogramEnumeration(kReadHistogram, ReadStatus::kReadError); return nullptr; } auto proto = std::make_unique<SearchConceptProto>(); if (!proto->ParseFromString(proto_str)) { + base::UmaHistogramEnumeration(kReadHistogram, ReadStatus::kParseError); return nullptr; } + base::UmaHistogramEnumeration(kReadHistogram, ReadStatus::kOk); // Discard the proto if the version does not match. if (!proto->has_version() || proto->version() != kVersion) { @@ -61,6 +91,8 @@ const base::FilePath& temp_file_path) { std::string proto_str; if (!proto->SerializeToString(&proto_str)) { + base::UmaHistogramEnumeration(kWriteHistogram, + WriteStatus::kSerializationError); return; } @@ -78,11 +110,18 @@ temp_file_path, proto_str, "HelpAppPersistentProto"); } + if (!write_succeed) { + base::UmaHistogramEnumeration(kWriteHistogram, WriteStatus::kWriteError); + return; + } + // Replace the proto in `file_path_` by the temporary proto if the write is // succeed. - if (write_succeed) { - base::ReplaceFile(temp_file_path, file_path, nullptr); - } + const bool replace_succeed = + base::ReplaceFile(temp_file_path, file_path, nullptr); + base::UmaHistogramEnumeration( + kWriteHistogram, + replace_succeed ? WriteStatus::kOk : WriteStatus::kReplaceError); } } // namespace
diff --git a/ash/webui/help_app_ui/search/search_handler.cc b/ash/webui/help_app_ui/search/search_handler.cc index 5a1e093..7bab197 100644 --- a/ash/webui/help_app_ui/search/search_handler.cc +++ b/ash/webui/help_app_ui/search/search_handler.cc
@@ -27,12 +27,12 @@ // persist to logs. Entries should not be renumbered and numeric values should // never be reused. enum class SearchResultStatus { - // The first Update hasn't finished yet, and the index is still empty, so the - // Search Handler is not ready to handle searches. + // There is no cache update, and the index is still empty, so the Search + // Handler is not ready to handle searches. kNotReadyAndEmptyIndex = 0, - // Not ready and the status is something other than EmptyIndex. This should be - // far less common than kNotReadyAndEmptyIndex. - kNotReadyAndOtherStatus = 1, + // Not ready and the cache status is updating. This should be far less common + // than kNotReadyAndEmptyIndex. + kNotReadyAndUpdating = 1, // Ready and the LSS response status is Success. kReadyAndSuccess = 2, // Ready and the LSS response status is EmptyIndex. This can happen for @@ -74,7 +74,8 @@ SearchTagRegistry* search_tag_registry, local_search_service::LocalSearchServiceProxy* local_search_service_proxy) : search_tag_registry_(search_tag_registry), - cache_status_(CacheStatus::kEmpty) { + cache_status_(CacheStatus::kEmpty), + construction_time_(base::TimeTicks::Now()) { local_search_service_proxy->GetIndex( local_search_service::IndexId::kHelpAppLauncher, local_search_service::Backend::kInvertedIndex, @@ -115,6 +116,9 @@ // Reject the search request if the cache is not ready yet. if (cache_status_ != CacheStatus::kReady) { + LogSearchResultStatus(cache_status_ == CacheStatus::kEmpty + ? SearchResultStatus::kNotReadyAndEmptyIndex + : SearchResultStatus::kNotReadyAndUpdating); std::move(callback).Run({}); return; } @@ -162,6 +166,12 @@ void SearchHandler::OnRegistryUpdated() { cache_status_ = CacheStatus::kReady; + if (!construction_time_.is_null()) { + base::UmaHistogramTimes("Discover.SearchHandler.SearchAvailableTime", + base::TimeTicks::Now() - construction_time_); + // Reset as we only care about the first time the search is available. + construction_time_ = base::TimeTicks(); + } for (auto& observer : observers_) observer->OnSearchResultAvailabilityChanged(); } @@ -191,19 +201,10 @@ const absl::optional<std::vector<local_search_service::Result>>& local_search_service_results) { if (response_status != local_search_service::ResponseStatus::kSuccess) { - if (response_status == local_search_service::ResponseStatus::kEmptyIndex) { - if (cache_status_ == CacheStatus::kReady) { - LogSearchResultStatus(SearchResultStatus::kReadyAndEmptyIndex); - } else { - LogSearchResultStatus(SearchResultStatus::kNotReadyAndEmptyIndex); - } - } else { - if (cache_status_ == CacheStatus::kReady) { - LogSearchResultStatus(SearchResultStatus::kReadyAndOtherStatus); - } else { - LogSearchResultStatus(SearchResultStatus::kNotReadyAndOtherStatus); - } - } + LogSearchResultStatus( + response_status == local_search_service::ResponseStatus::kEmptyIndex + ? SearchResultStatus::kReadyAndEmptyIndex + : SearchResultStatus::kReadyAndOtherStatus); std::move(callback).Run({}); return; }
diff --git a/ash/webui/help_app_ui/search/search_handler.h b/ash/webui/help_app_ui/search/search_handler.h index 8a50df2c..c4215d6 100644 --- a/ash/webui/help_app_ui/search/search_handler.h +++ b/ash/webui/help_app_ui/search/search_handler.h
@@ -12,6 +12,7 @@ #include "ash/webui/help_app_ui/search/search_concept.h" #include "ash/webui/help_app_ui/search/search_tag_registry.h" #include "base/memory/weak_ptr.h" +#include "base/time/time.h" #include "chromeos/ash/components/local_search_service/public/cpp/local_search_service_proxy.h" #include "chromeos/ash/components/local_search_service/public/mojom/index.mojom.h" #include "mojo/public/cpp/bindings/pending_receiver.h" @@ -88,6 +89,11 @@ CacheStatus cache_status_; + // The time this class is constructed. This is also used as a flag to indicate + // if the availability latency has been logged. After logging, the + // `construction_time_` will be reset to null. + base::TimeTicks construction_time_; + // Note: Expected to have multiple clients, so ReceiverSet/RemoteSet are used. mojo::ReceiverSet<mojom::SearchHandler> receivers_; mojo::RemoteSet<mojom::SearchResultsObserver> observers_;
diff --git a/ash/webui/help_app_ui/search/search_handler_unittest.cc b/ash/webui/help_app_ui/search/search_handler_unittest.cc index de738df5..25e0236 100644 --- a/ash/webui/help_app_ui/search/search_handler_unittest.cc +++ b/ash/webui/help_app_ui/search/search_handler_unittest.cc
@@ -257,12 +257,7 @@ EXPECT_GT(search_results[1]->relevance_score, 0.01); } -// TODO(b/270091661): These two unit tests below are currently broken as the -// search logic has been changed a bit. We will modify the UMA metric and -// re-enable them when the UMA metric changes are merged. - -// Re-enable the UMA metric checks when the changes are made. -TEST_F(HelpAppSearchHandlerTest, DISABLED_SearchStatusNotReadyAndEmptyIndex) { +TEST_F(HelpAppSearchHandlerTest, SearchStatusNotReadyAndEmptyIndex) { base::HistogramTester histogram_tester; std::vector<mojom::SearchResultPtr> search_results; @@ -276,22 +271,6 @@ "Discover.SearchHandler.SearchResultStatus", 0, 1); } -// Re-enable the UMA metric checks when the changes are made. -TEST_F(HelpAppSearchHandlerTest, DISABLED_SearchStatusNotReadyAndOtherStatus) { - base::HistogramTester histogram_tester; - std::vector<mojom::SearchResultPtr> search_results; - - // The empty search query makes the LSS respond with kEmptyQuery rather than - // kEmptyIndex. - mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) - .Search(u"", /*max_num_results=*/3u, &search_results); - - EXPECT_TRUE(search_results.empty()); - // 1 is kNotReadyAndOtherStatus. - histogram_tester.ExpectUniqueSample( - "Discover.SearchHandler.SearchResultStatus", 1, 1); -} - TEST_F(HelpAppSearchHandlerTest, SearchStatusReadyAndSuccess) { // Add one item to the search index. std::vector<mojom::SearchConceptPtr> search_concepts;
diff --git a/ash/webui/personalization_app/mojom/personalization_app.mojom b/ash/webui/personalization_app/mojom/personalization_app.mojom index c459e63..84aa31f 100644 --- a/ash/webui/personalization_app/mojom/personalization_app.mojom +++ b/ash/webui/personalization_app/mojom/personalization_app.mojom
@@ -543,8 +543,7 @@ kSlideshow = 0, kFeelTheBreeze = 1, kFloatOnBy = 2, - kVideoNewMexico = 3, - kVideoClouds = 4, + kVideo = 3, }; // The source of the screensaver images come from. @@ -677,11 +676,19 @@ const uint32 kIndigoColor = 0x4285F4; const uint32 kPurpleColor = 0xB76DF8; +union CurrentBacklightState { + // Set if the user has selected a specific color as backlight color. + BacklightColor color; + + // Set if the user has selected multi-zone colors as backlight color. + array<BacklightColor> zone_colors; +}; + // Receives information whenever there are keyboard backlight related changes // such as backlight colors. interface KeyboardBacklightObserver { - // Notifies the JS side about the current state of the backlight color. - OnBacklightColorChanged(BacklightColor color); + // Notifies the JS side about the current backlight state. + OnBacklightStateChanged(CurrentBacklightState currentBacklightState); // Notifies the JS side the current wallpaper-extracted color. OnWallpaperColorChanged(skia.mojom.SkColor wallpaper_color);
diff --git a/ash/webui/personalization_app/mojom/personalization_app_mojom_traits.cc b/ash/webui/personalization_app/mojom/personalization_app_mojom_traits.cc index 3c0cb75..3bd03ab4 100644 --- a/ash/webui/personalization_app/mojom/personalization_app_mojom_traits.cc +++ b/ash/webui/personalization_app/mojom/personalization_app_mojom_traits.cc
@@ -401,10 +401,8 @@ return MojomAnimationTheme::kFeelTheBreeze; case ash::AmbientTheme::kFloatOnBy: return MojomAnimationTheme::kFloatOnBy; - case ash::AmbientTheme::kVideoNewMexico: - return MojomAnimationTheme::kVideoNewMexico; - case ash::AmbientTheme::kVideoClouds: - return MojomAnimationTheme::kVideoClouds; + case ash::AmbientTheme::kVideo: + return MojomAnimationTheme::kVideo; } } @@ -421,11 +419,8 @@ case MojomAnimationTheme::kFloatOnBy: *output = ash::AmbientTheme::kFloatOnBy; return true; - case MojomAnimationTheme::kVideoNewMexico: - *output = ash::AmbientTheme::kVideoNewMexico; - return true; - case MojomAnimationTheme::kVideoClouds: - *output = ash::AmbientTheme::kVideoClouds; + case MojomAnimationTheme::kVideo: + *output = ash::AmbientTheme::kVideo; return true; } NOTREACHED();
diff --git a/ash/webui/personalization_app/personalization_app_ui.cc b/ash/webui/personalization_app/personalization_app_ui.cc index e9d7987..c7d871cd 100644 --- a/ash/webui/personalization_app/personalization_app_ui.cc +++ b/ash/webui/personalization_app/personalization_app_ui.cc
@@ -259,6 +259,7 @@ IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_ZONE_CUSTOMIZATION_DISMISS_BUTTON}, {"wallpaperColorDescription", IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WALLPAPER_COLOR_DESCRIPTION}, + {"zoneTitle", IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_ZONE_TITLE}, // Google Photos strings // TODO(b/229149314): Finalize error and retry strings. @@ -342,6 +343,7 @@ AddResources(source); AddStrings(source); AddBooleans(source); + AddIntegers(source); } PersonalizationAppUI::~PersonalizationAppUI() = default; @@ -403,11 +405,13 @@ source->AddBoolean("isPersonalizationJellyEnabled", features::IsPersonalizationJellyEnabled()); +} - source->AddBoolean( - "isMultiZoneRgbKeyboardSupported", - features::IsMultiZoneRgbKeyboardEnabled() && - Shell::Get()->rgb_keyboard_manager()->GetZoneCount() > 1); +void PersonalizationAppUI::AddIntegers(content::WebUIDataSource* source) { + source->AddInteger("keyboardBacklightZoneCount", + features::IsMultiZoneRgbKeyboardEnabled() + ? Shell::Get()->rgb_keyboard_manager()->GetZoneCount() + : 0); } void PersonalizationAppUI::HandleWebUIRequest(
diff --git a/ash/webui/personalization_app/personalization_app_ui.h b/ash/webui/personalization_app/personalization_app_ui.h index 378525d..c65e447 100644 --- a/ash/webui/personalization_app/personalization_app_ui.h +++ b/ash/webui/personalization_app/personalization_app_ui.h
@@ -72,6 +72,8 @@ private: void AddBooleans(content::WebUIDataSource* source); + void AddIntegers(content::WebUIDataSource* source); + void HandleWebUIRequest(const std::string& path, content::WebUIDataSource::GotDataCallback callback);
diff --git a/ash/webui/personalization_app/resources/BUILD.gn b/ash/webui/personalization_app/resources/BUILD.gn index dc94157..f224703e 100644 --- a/ash/webui/personalization_app/resources/BUILD.gn +++ b/ash/webui/personalization_app/resources/BUILD.gn
@@ -55,6 +55,7 @@ "js/ambient/zero_state_element.ts", "js/keyboard_backlight/keyboard_backlight_element.ts", "js/keyboard_backlight/zone_customization_element.ts", + "js/keyboard_backlight/color_icon_element.ts", "js/personalization_main_element.ts", "js/personalization_router_element.ts", "js/personalization_toast_element.ts",
diff --git a/ash/webui/personalization_app/resources/js/ambient/ambient_preview_large_element.html b/ash/webui/personalization_app/resources/js/ambient/ambient_preview_large_element.html index c4580a52..34f4781 100644 --- a/ash/webui/personalization_app/resources/js/ambient/ambient_preview_large_element.html +++ b/ash/webui/personalization_app/resources/js/ambient/ambient_preview_large_element.html
@@ -273,6 +273,11 @@ width: 100%; } + #thumbnailContainer.thumbnail-0 { + background-color: var(--personalization-app-grid-item-background-color); + border-radius: 12px; + } + #thumbnailContainer.thumbnail-1 .thumbnail-item { border-radius: 60px; }
diff --git a/ash/webui/personalization_app/resources/js/ambient/ambient_preview_large_element.ts b/ash/webui/personalization_app/resources/js/ambient/ambient_preview_large_element.ts index f22aa9c..d9aafbcdd 100644 --- a/ash/webui/personalization_app/resources/js/ambient/ambient_preview_large_element.ts +++ b/ash/webui/personalization_app/resources/js/ambient/ambient_preview_large_element.ts
@@ -46,24 +46,20 @@ collageImages_: { type: Array, computed: - 'computeCollageImages_(topicSource_, previewAlbums_, previewImages_)', + 'computeThumbnailImages_(topicSource_, previewAlbums_, previewImages_)', }, }; } private collageImages_: Url[]; - /** Returns the array of images that form the collage. */ + /** Returns the array of images that form the collage when Jelly is off. */ private computeCollageImages_(): Url[] { - const maxLength = this.isPersonalizationJellyEnabled_ ? 3 : 4; switch (this.topicSource_) { case TopicSource.kArtGallery: - if (this.isPersonalizationJellyEnabled_ && - isNonEmptyArray(this.previewImages_)) { - return this.previewImages_.slice(0, 2); - } return (this.previewAlbums_ || []).map(album => album.url).slice(0, 2); case TopicSource.kGooglePhotos: + const maxLength = 4; if (isNonEmptyArray(this.previewImages_)) { return this.previewImages_.length < maxLength ? [this.previewImages_[0]] : @@ -78,6 +74,20 @@ return []; } + /** Returns the array of thumbnail images. */ + private computeThumbnailImages_(): Url[] { + if (!this.isPersonalizationJellyEnabled_) { + return this.computeCollageImages_(); + } + if (isNonEmptyArray(this.previewImages_)) { + const maxLength = Math.min( + this.previewImages_.length, + this.topicSource_ === TopicSource.kArtGallery ? 2 : 3); + return this.previewImages_.slice(0, maxLength); + } + return []; + } + private onClickAmbientSubpageLink_() { PersonalizationRouter.instance().goToRoute(Paths.AMBIENT); }
diff --git a/ash/webui/personalization_app/resources/js/ambient/ambient_preview_small_element.html b/ash/webui/personalization_app/resources/js/ambient/ambient_preview_small_element.html index 391b71de..62af25a2 100644 --- a/ash/webui/personalization_app/resources/js/ambient/ambient_preview_small_element.html +++ b/ash/webui/personalization_app/resources/js/ambient/ambient_preview_small_element.html
@@ -130,6 +130,7 @@ <cr-button class$="[[getScreenSaverPreviewClass_(ambientUiVisibility_)]]" aria-label$="[[getScreenSaverPreviewAriaLabel_(ambientUiVisibility_)]]" + role$="[[getScreenSaverPreviewRole_(ambientUiVisibility_)]]" on-click="startScreenSaverPreview_"> <iron-icon icon="personalization:fullscreen" hidden$="[[screenSaverPreviewActive_]]"></iron-icon>
diff --git a/ash/webui/personalization_app/resources/js/ambient/ambient_preview_small_element.ts b/ash/webui/personalization_app/resources/js/ambient/ambient_preview_small_element.ts index 38d1e70..e906df0c 100644 --- a/ash/webui/personalization_app/resources/js/ambient/ambient_preview_small_element.ts +++ b/ash/webui/personalization_app/resources/js/ambient/ambient_preview_small_element.ts
@@ -99,6 +99,10 @@ this.i18n('screenSaverPreviewDownloadingAriaLabel') : this.i18n('screenSaverPreviewButtonAriaLabel'); } + + private getScreenSaverPreviewRole_(): string { + return this.screenSaverPreviewActive_ ? 'none' : 'button'; + } } customElements.define(AmbientPreviewSmall.is, AmbientPreviewSmall);
diff --git a/ash/webui/personalization_app/resources/js/keyboard_backlight/color_icon_element.html b/ash/webui/personalization_app/resources/js/keyboard_backlight/color_icon_element.html new file mode 100644 index 0000000..bd4fc483 --- /dev/null +++ b/ash/webui/personalization_app/resources/js/keyboard_backlight/color_icon_element.html
@@ -0,0 +1,42 @@ +<style include="common"> + :host { + height: 28px; + width: 28px; + } + .color-inner-container { + align-items: center; + border-radius: 50%; + display: flex; + height: 100%; + justify-content: center; + position: relative; + width: 100%; + } + .dark-icon { + fill: var(--cros-icon-color-primary-dark); + } + .light-icon { + fill: var(--cros-icon-color-primary-light); + } +</style> +<template is="dom-if" if="[[isWallpaperColorId_(colorId)]]"> + <div class="color-inner-container" + style$="[[getWallpaperColorInnerContainerStyle_(wallpaperColor_)]]"> + <svg class$="[[getWallpaperIconColorClass_(wallpaperColor_)]]" viewBox="0 0 20 20" width="16" height="16"> + <path fill-rule="evenodd" clip-rule="evenodd" d="M12.0605 5.585L13.5805 6.935C14.3705 7.715 14.2005 8.985 13.4105 9.765L4.76469 18.4108C3.98364 19.1918 2.71731 19.1918 1.93626 18.4108L0.583834 17.0584C-0.196879 16.2776 -0.197265 15.012 0.582974 14.2308L7.40048 7.405L9.23048 5.585C10.0105 4.805 11.2805 4.805 12.0605 5.585ZM8.82101 8.81494L1.95101 15.6849L3.36101 17.1049L10.231 10.2249L8.82101 8.81494Z"> + </path> + <path d="M16 6L15.06 3.94L13 3L15.06 2.06L16 0L16.94 2.06L19 3L16.94 3.94L16 6Z"> + </path> + <path d="M17.001 17L16.061 14.94L14.001 14L16.061 13.06L17.001 11L17.941 13.06L20.001 14L17.941 14.94L17.001 17Z"> + </path> + <path d="M17.001 17L16.061 14.94L14.001 14L16.061 13.06L17.001 11L17.941 13.06L20.001 14L17.941 14.94L17.001 17Z"> + </path> + <path d="M3.06 4.94L4 7L4.94 4.94L7 4L4.94 3.06L4 1L3.06 3.06L1 4L3.06 4.94Z"> + </path> + </svg> + </div> +</template> +<template is="dom-if" if="[[!isWallpaperColorId_(colorId)]]"> + <div class="color-inner-container" style$="[[getColorInnerContainerStyle_(colorId)]]"> + </div> +</template>
diff --git a/ash/webui/personalization_app/resources/js/keyboard_backlight/color_icon_element.ts b/ash/webui/personalization_app/resources/js/keyboard_backlight/color_icon_element.ts new file mode 100644 index 0000000..2d5d747 --- /dev/null +++ b/ash/webui/personalization_app/resources/js/keyboard_backlight/color_icon_element.ts
@@ -0,0 +1,110 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js'; + +import {WithPersonalizationStore} from '../personalization_store.js'; +import {convertToRgbHexStr, getPresetColors, GREEN, INDIGO, RAINBOW, RED, WALLPAPER, YELLOW} from '../utils.js'; + +import {getTemplate} from './color_icon_element.html.js'; + +/** + * @fileoverview + * The color icon indicates wallpaper or preset colors in keyboard backlight and + * zone customization section. + */ + +/** + Based on this algorithm suggested by the W3: + https://www.w3.org/TR/AERT/#color-contrast +*/ +function calculateColorBrightness(hexVal: number): number { + const r = (hexVal >> 16) & 0xff; // extract red + const g = (hexVal >> 8) & 0xff; // extract green + const b = (hexVal >> 0) & 0xff; // extract blue + return (r * 299 + g * 587 + b * 114) / 1000; +} + +export class ColorIcon extends WithPersonalizationStore { + static get is() { + return 'color-icon'; + } + + static get template() { + return getTemplate(); + } + + static get properties() { + return { + /** The color id indicates the color of the icon. */ + colorId: { + type: String, + value: null, + reflectToAttribute: true, + }, + + /** The current wallpaper extracted color. */ + wallpaperColor_: Object, + }; + } + + colorId: string|null; + private wallpaperColor_: SkColor|null; + + override connectedCallback() { + super.connectedCallback(); + this.watch( + 'wallpaperColor_', state => state.keyboardBacklight.wallpaperColor); + this.updateFromStore(); + } + + private isWallpaperColorId_(colorId: string|null): boolean { + return colorId === WALLPAPER; + } + + private getColorInnerContainerStyle_(colorId: string|null): string { + if (colorId === null) { + return ''; + } + const colors = getPresetColors(); + const outlineStyle = `outline: 2px solid var(--cros-separator-color); + outline-offset: -2px;`; + switch (colorId) { + case RAINBOW: + return `background-image: linear-gradient(90deg, + ${colors[RED].hexVal}, + ${colors[YELLOW].hexVal}, + ${colors[GREEN].hexVal}, + ${colors[INDIGO].hexVal}); + ${outlineStyle}`; + default: + return `background-color: ${colors[colorId].hexVal}; + ${outlineStyle};`; + } + } + + private getWallpaperColorInnerContainerStyle_(wallpaperColor: SkColor| + null): string { + const hexStr = !wallpaperColor ? + '#FFFFFF' : + convertToRgbHexStr(wallpaperColor.value & 0xFFFFFF); + return `background-color: ${hexStr}; + outline: 2px solid var(--cros-separator-color); + outline-offset: -2px;`; + } + + private getWallpaperIconColorClass_(wallpaperColor: SkColor): string { + if (!wallpaperColor || (wallpaperColor.value & 0xFFFFFF) === 0xFFFFFF) { + return `light-icon`; + } + const brightness = + calculateColorBrightness(wallpaperColor.value & 0xFFFFFF); + if (brightness < 125) { + return `dark-icon`; + } + return `light-icon`; + } +} + +customElements.define(ColorIcon.is, ColorIcon);
diff --git a/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_actions.ts b/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_actions.ts index 3a50bc4..887e4d7 100644 --- a/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_actions.ts +++ b/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_actions.ts
@@ -5,7 +5,7 @@ import {Action} from 'chrome://resources/ash/common/store/store.js'; import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js'; -import {BacklightColor} from '../../personalization_app.mojom-webui.js'; +import {CurrentBacklightState} from '../../personalization_app.mojom-webui.js'; /** @@ -13,17 +13,17 @@ */ export enum KeyboardBacklightActionName { - SET_BACKLIGHT_COLOR = 'set_backlight_color', + SET_CURRENT_BACKLIGHT_STATE = 'set_current_backlight_state', SET_SHOULD_SHOW_NUDGE = 'set_should_show_nudge', SET_WALLPAPER_COLOR = 'set_wallpaper_color', } -export type KeyboardBacklightActions = - SetBacklightColorAction|SetShouldShowNudgeAction|SetWallpaperColorAction; +export type KeyboardBacklightActions = SetCurrentBacklightStateAction| + SetShouldShowNudgeAction|SetWallpaperColorAction; -export type SetBacklightColorAction = Action&{ - name: KeyboardBacklightActionName.SET_BACKLIGHT_COLOR, - backlightColor: BacklightColor, +export type SetCurrentBacklightStateAction = Action&{ + name: KeyboardBacklightActionName.SET_CURRENT_BACKLIGHT_STATE, + currentBacklightState: CurrentBacklightState, }; export type SetShouldShowNudgeAction = Action&{ @@ -37,13 +37,14 @@ }; /** - * Sets the current value of the backlight color. + * Sets the current value of the backlight state. */ -export function setBacklightColorAction(backlightColor: BacklightColor): - SetBacklightColorAction { +export function setCurrentBacklightStateAction( + currentBacklightState: CurrentBacklightState): + SetCurrentBacklightStateAction { return { - name: KeyboardBacklightActionName.SET_BACKLIGHT_COLOR, - backlightColor, + name: KeyboardBacklightActionName.SET_CURRENT_BACKLIGHT_STATE, + currentBacklightState, }; }
diff --git a/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_controller.ts b/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_controller.ts index b9b45b923..650459f5 100644 --- a/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_controller.ts +++ b/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_controller.ts
@@ -5,7 +5,7 @@ import {BacklightColor, KeyboardBacklightProviderInterface} from '../../personalization_app.mojom-webui.js'; import {PersonalizationStore} from '../personalization_store.js'; -import {setBacklightColorAction, setShouldShowNudgeAction} from './keyboard_backlight_actions.js'; +import {setCurrentBacklightStateAction, setShouldShowNudgeAction} from './keyboard_backlight_actions.js'; /** * @fileoverview contains all of the functions to interact with keyboard @@ -19,8 +19,8 @@ provider: KeyboardBacklightProviderInterface, store: PersonalizationStore) { provider.setBacklightColor(backlightColor); - // Dispatch action to highlight backlight color. - store.dispatch(setBacklightColorAction(backlightColor)); + // Dispatch action to set the current backlight state. + store.dispatch(setCurrentBacklightStateAction({color: backlightColor})); } // Set the keyboard backlight color for the given zone.
diff --git a/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_element.html b/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_element.html index f225c416..414575b7 100644 --- a/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_element.html +++ b/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_element.html
@@ -70,25 +70,7 @@ width: var(--color-container-size); } - .color-inner-container { - align-items: center; - border-radius: 50%; - display: flex; - height: 28px; - justify-content: center; - position: relative; - width: 28px; - } - - .dark-icon { - fill: var(--cros-icon-color-primary-dark); - } - - .light-icon { - fill: var(--cros-icon-color-primary-light); - } - - .color-container[aria-checked='true'] .color-inner-container { + .color-container[aria-checked='true'] color-icon { height: 36px; width: 36px; } @@ -176,21 +158,7 @@ aria-checked$="[[getWallpaperColorAriaChecked_(backlightColor_)]]" title$="[[getWallpaperColorTitle_()]]" role="radio"> - <div class="color-inner-container" - style$="[[getWallpaperColorInnerContainerStyle_(wallpaperColor_)]]"> - <svg class$="[[getWallpaperIconColorClass_(wallpaperColor_)]]" viewBox="0 0 20 20" width="16" height="16"> - <path fill-rule="evenodd" clip-rule="evenodd" d="M12.0605 5.585L13.5805 6.935C14.3705 7.715 14.2005 8.985 13.4105 9.765L4.76469 18.4108C3.98364 19.1918 2.71731 19.1918 1.93626 18.4108L0.583834 17.0584C-0.196879 16.2776 -0.197265 15.012 0.582974 14.2308L7.40048 7.405L9.23048 5.585C10.0105 4.805 11.2805 4.805 12.0605 5.585ZM8.82101 8.81494L1.95101 15.6849L3.36101 17.1049L10.231 10.2249L8.82101 8.81494Z"> - </path> - <path d="M16 6L15.06 3.94L13 3L15.06 2.06L16 0L16.94 2.06L19 3L16.94 3.94L16 6Z"> - </path> - <path d="M17.001 17L16.061 14.94L14.001 14L16.061 13.06L17.001 11L17.941 13.06L20.001 14L17.941 14.94L17.001 17Z"> - </path> - <path d="M17.001 17L16.061 14.94L14.001 14L16.061 13.06L17.001 11L17.941 13.06L20.001 14L17.941 14.94L17.001 17Z"> - </path> - <path d="M3.06 4.94L4 7L4.94 4.94L7 4L4.94 3.06L4 1L3.06 3.06L1 4L3.06 4.94Z"> - </path> - </svg> - </div> + <color-icon color-id="[[wallpaperColorId_]]"></color-icon> </div> </template> <template is="dom-if" if="[[shouldShowNudge_]]"> @@ -215,9 +183,7 @@ role="radio" aria-checked$="[[getPresetColorAriaChecked_(presetColorId, presetColors_, backlightColor_)]]" title$="[[getPresetColorAriaLabel_(presetColorId)]]"> - <div class="color-inner-container" - style$="[[getColorInnerContainerStyle_(presetColorId, presetColors_)]]"> - </div> + <color-icon color-id="[[presetColorId]]"></color-icon> </div> </template> <div id$="[[rainbowColorId_]]" @@ -227,9 +193,7 @@ aria-label="$i18n{rainbowColor}" role="radio" aria-checked$="[[getRainbowColorAriaChecked_(backlightColor_)]]" title="$i18n{rainbowColor}"> - <div class="color-inner-container" - style$="[[getColorInnerContainerStyle_(rainbowColorId_, presetColors_)]]"> - </div> + <color-icon color-id="[[rainbowColorId_]]"></color-icon> </div> <template is="dom-if" if="[[isMultiZoneRgbKeyboardSupported_]]"> <div id$="[[wallpaperColorId_]]" @@ -241,28 +205,14 @@ aria-checked$="[[getWallpaperColorAriaChecked_(backlightColor_)]]" title$="[[getWallpaperColorTitle_()]]" role="radio"> - <div class="color-inner-container" - style$="[[getWallpaperColorInnerContainerStyle_(wallpaperColor_)]]"> - <svg class$="[[getWallpaperIconColorClass_(wallpaperColor_)]]" viewBox="0 0 20 20" width="16" height="16"> - <path fill-rule="evenodd" clip-rule="evenodd" d="M12.0605 5.585L13.5805 6.935C14.3705 7.715 14.2005 8.985 13.4105 9.765L4.76469 18.4108C3.98364 19.1918 2.71731 19.1918 1.93626 18.4108L0.583834 17.0584C-0.196879 16.2776 -0.197265 15.012 0.582974 14.2308L7.40048 7.405L9.23048 5.585C10.0105 4.805 11.2805 4.805 12.0605 5.585ZM8.82101 8.81494L1.95101 15.6849L3.36101 17.1049L10.231 10.2249L8.82101 8.81494Z"> - </path> - <path d="M16 6L15.06 3.94L13 3L15.06 2.06L16 0L16.94 2.06L19 3L16.94 3.94L16 6Z"> - </path> - <path d="M17.001 17L16.061 14.94L14.001 14L16.061 13.06L17.001 11L17.941 13.06L20.001 14L17.941 14.94L17.001 17Z"> - </path> - <path d="M17.001 17L16.061 14.94L14.001 14L16.061 13.06L17.001 11L17.941 13.06L20.001 14L17.941 14.94L17.001 17Z"> - </path> - <path d="M3.06 4.94L4 7L4.94 4.94L7 4L4.94 3.06L4 1L3.06 3.06L1 4L3.06 4.94Z"> - </path> - </svg> - </div> + <color-icon color-id="[[wallpaperColorId_]]"></color-icon> </div> <div id="wallpaperColorDescription"> $i18n{wallpaperColorDescription} </div> <cr-button id="zoneCustomizationButton" on-click="showZoneCustomizationDialog_" class="secondary" - aria-pressed$="[[getZoneCustomizationButtonAriaPressed_()]]"> + aria-pressed$="[[getZoneCustomizationButtonAriaPressed_(currentBacklightState_)]]"> <iron-icon icon="personalization:circle_checkmark"></iron-icon> <div class="text">$i18n{zoneCustomize}</div> </cr-button>
diff --git a/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_element.ts b/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_element.ts index 5806b0b..9251509 100644 --- a/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_element.ts +++ b/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_element.ts
@@ -6,6 +6,7 @@ import 'chrome://resources/polymer/v3_0/iron-selector/iron-selector.js'; import 'chrome://resources/polymer/v3_0/paper-ripple/paper-ripple.js'; import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js'; +import './color_icon_element.js'; import '../../css/common.css.js'; import '../../css/cros_button_style.css.js'; @@ -15,10 +16,10 @@ import {IronA11yKeysElement} from 'chrome://resources/polymer/v3_0/iron-a11y-keys/iron-a11y-keys.js'; import {IronSelectorElement} from 'chrome://resources/polymer/v3_0/iron-selector/iron-selector.js'; -import {BacklightColor, BLUE_COLOR, GREEN_COLOR, INDIGO_COLOR, PURPLE_COLOR, RED_COLOR, WHITE_COLOR, YELLOW_COLOR} from '../../personalization_app.mojom-webui.js'; +import {BacklightColor, CurrentBacklightState} from '../../personalization_app.mojom-webui.js'; import {isMultiZoneRgbKeyboardSupported} from '../load_time_booleans.js'; import {WithPersonalizationStore} from '../personalization_store.js'; -import {convertToRgbHexStr, isSelectionEvent} from '../utils.js'; +import {ColorInfo, getPresetColors, isSelectionEvent, RAINBOW, WALLPAPER} from '../utils.js'; import {getShouldShowNudge, handleNudgeShown, setBacklightColor} from './keyboard_backlight_controller.js'; import {getTemplate} from './keyboard_backlight_element.html.js'; @@ -41,22 +42,6 @@ }; } -/** - Based on this algorithm suggested by the W3: - https://www.w3.org/TR/AERT/#color-contrast -*/ -function calculateColorBrightness(hexVal: number): number { - const r = (hexVal >> 16) & 0xff; // extract red - const g = (hexVal >> 8) & 0xff; // extract green - const b = (hexVal >> 0) & 0xff; // extract blue - return (r * 299 + g * 587 + b * 114) / 1000; -} - -interface ColorInfo { - hexVal: string; - enumVal: BacklightColor; -} - export class KeyboardBacklight extends WithPersonalizationStore { static get is() { return 'keyboard-backlight'; @@ -87,19 +72,24 @@ rainbowColorId_: { type: String, - value: 'rainbow', + value: RAINBOW, }, wallpaperColorId_: { type: String, - value: 'wallpaper', + value: WALLPAPER, + }, + + backlightColor_: { + type: Object, + computed: 'computeBacklightColor_(currentBacklightState_)', }, /** The color currently highlighted by keyboard navigation. */ ironSelectedColor_: Object, - /** The selected backlight color in the system. */ - backlightColor_: Object, + /** The current backlight state in the system. */ + currentBacklightState_: Object, /** The current wallpaper extracted color. */ wallpaperColor_: Object, @@ -115,10 +105,11 @@ private isMultiZoneRgbKeyboardSupported_: boolean; private presetColors_: Record<string, ColorInfo>; private presetColorIds_: string[]; + private backlightColor_: BacklightColor|null|undefined; private rainbowColorId_: string; private wallpaperColorId_: string; private ironSelectedColor_: HTMLElement; - private backlightColor_: BacklightColor|null; + private currentBacklightState_: CurrentBacklightState|null; private wallpaperColor_: SkColor|null; private shouldShowNudge_: boolean; @@ -130,8 +121,9 @@ override connectedCallback() { super.connectedCallback(); KeyboardBacklightObserver.initKeyboardBacklightObserverIfNeeded(); - this.watch<KeyboardBacklight['backlightColor_']>( - 'backlightColor_', state => state.keyboardBacklight.backlightColor); + this.watch<KeyboardBacklight['currentBacklightState_']>( + 'currentBacklightState_', + state => state.keyboardBacklight.currentBacklightState); this.watch<KeyboardBacklight['shouldShowNudge_']>( 'shouldShowNudge_', state => state.keyboardBacklight.shouldShowNudge); this.watch<KeyboardBacklight['wallpaperColor_']>( @@ -142,36 +134,7 @@ } private computePresetColors_(): Record<string, ColorInfo> { - return { - 'whiteColor': { - hexVal: convertToRgbHexStr(WHITE_COLOR), - enumVal: BacklightColor.kWhite, - }, - 'redColor': { - hexVal: convertToRgbHexStr(RED_COLOR), - enumVal: BacklightColor.kRed, - }, - 'yellowColor': { - hexVal: convertToRgbHexStr(YELLOW_COLOR), - enumVal: BacklightColor.kYellow, - }, - 'greenColor': { - hexVal: convertToRgbHexStr(GREEN_COLOR), - enumVal: BacklightColor.kGreen, - }, - 'blueColor': { - hexVal: convertToRgbHexStr(BLUE_COLOR), - enumVal: BacklightColor.kBlue, - }, - 'indigoColor': { - hexVal: convertToRgbHexStr(INDIGO_COLOR), - enumVal: BacklightColor.kIndigo, - }, - 'purpleColor': { - hexVal: convertToRgbHexStr(PURPLE_COLOR), - enumVal: BacklightColor.kPurple, - }, - }; + return getPresetColors(); } private computePresetColorIds_(presetColors: Record<string, string>): @@ -180,6 +143,11 @@ return Object.keys(presetColors); } + private computeBacklightColor_(currentBacklightState: CurrentBacklightState): + BacklightColor|null|undefined { + return currentBacklightState ? currentBacklightState.color : null; + } + /** Handle keyboard navigation. */ private onKeysPress_( e: CustomEvent<{key: string, keyboardEvent: KeyboardEvent}>) { @@ -258,46 +226,6 @@ this.getStore()); } - private getColorInnerContainerStyle_( - colorId: string, colors: Record<string, ColorInfo>) { - const outlineStyle = `outline: 2px solid var(--cros-separator-color); - outline-offset: -2px;`; - switch (colorId) { - case this.rainbowColorId_: - return `background-image: linear-gradient(90deg, - ${colors['redColor'].hexVal}, - ${colors['yellowColor'].hexVal}, - ${colors['greenColor'].hexVal}, - ${colors['indigoColor'].hexVal}); - ${outlineStyle}`; - default: - return `background-color: ${colors[colorId].hexVal}; - ${outlineStyle};`; - } - } - - private getWallpaperColorInnerContainerStyle_(wallpaperColor: SkColor): - string { - const hexStr = !wallpaperColor ? - '#FFFFFF' : - convertToRgbHexStr(wallpaperColor.value & 0xFFFFFF); - return `background-color: ${hexStr}; - outline: 2px solid var(--cros-separator-color); - outline-offset: -2px;`; - } - - private getWallpaperIconColorClass_(wallpaperColor: SkColor): string { - if (!wallpaperColor || (wallpaperColor.value & 0xFFFFFF) === 0xFFFFFF) { - return `light-icon`; - } - const brightness = - calculateColorBrightness(wallpaperColor.value & 0xFFFFFF); - if (brightness < 125) { - return `dark-icon`; - } - return `light-icon`; - } - private getPresetColorAriaLabel_(presetColorId: string): string { return this.i18n(presetColorId); } @@ -362,9 +290,10 @@ this.$.zoneCustomizationRender.get().showModal(); } - private getZoneCustomizationButtonAriaPressed_() { - // TODO(b/265854825): Handle selected state. - return 'false'; + private getZoneCustomizationButtonAriaPressed_( + currentBacklightState: CurrentBacklightState): string { + return (!!currentBacklightState && !!currentBacklightState.zoneColors) + .toString(); } }
diff --git a/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_observer.ts b/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_observer.ts index fc2e218..239516cf 100644 --- a/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_observer.ts +++ b/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_observer.ts
@@ -4,10 +4,10 @@ import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js'; -import {BacklightColor, KeyboardBacklightObserverInterface, KeyboardBacklightObserverReceiver, KeyboardBacklightProviderInterface} from '../../personalization_app.mojom-webui.js'; +import {CurrentBacklightState, KeyboardBacklightObserverInterface, KeyboardBacklightObserverReceiver, KeyboardBacklightProviderInterface} from '../../personalization_app.mojom-webui.js'; import {PersonalizationStore} from '../personalization_store.js'; -import {setBacklightColorAction, setWallpaperColorAction} from './keyboard_backlight_actions.js'; +import {setCurrentBacklightStateAction, setWallpaperColorAction} from './keyboard_backlight_actions.js'; import {getKeyboardBacklightProvider} from './keyboard_backlight_interface_provider.js'; /** @fileoverview listens for updates on keyboard backlight settings changes. */ @@ -45,9 +45,9 @@ return receiver; } - onBacklightColorChanged(backlightColor: BacklightColor): void { + onBacklightStateChanged(currentBacklightState: CurrentBacklightState): void { const store = PersonalizationStore.getInstance(); - store.dispatch(setBacklightColorAction(backlightColor)); + store.dispatch(setCurrentBacklightStateAction(currentBacklightState)); } onWallpaperColorChanged(wallpaperColor: SkColor): void {
diff --git a/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_reducers.ts b/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_reducers.ts index 1268935..17695af 100644 --- a/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_reducers.ts +++ b/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_reducers.ts
@@ -4,7 +4,7 @@ import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js'; -import {BacklightColor} from '../../personalization_app.mojom-webui.js'; +import {CurrentBacklightState} from '../../personalization_app.mojom-webui.js'; import {Actions} from '../personalization_actions.js'; import {ReducerFunction} from '../personalization_reducers.js'; import {PersonalizationState} from '../personalization_state.js'; @@ -12,12 +12,12 @@ import {KeyboardBacklightActionName} from './keyboard_backlight_actions.js'; import {KeyboardBacklightState} from './keyboard_backlight_state.js'; -export function backlightColorReducer( - state: BacklightColor|null, action: Actions, - _: PersonalizationState): BacklightColor|null { +export function currentBacklightStateReducer( + state: CurrentBacklightState|null, action: Actions, + _: PersonalizationState): CurrentBacklightState|null { switch (action.name) { - case KeyboardBacklightActionName.SET_BACKLIGHT_COLOR: - return action.backlightColor; + case KeyboardBacklightActionName.SET_CURRENT_BACKLIGHT_STATE: + return action.currentBacklightState; default: return state; } @@ -48,7 +48,7 @@ [K in keyof KeyboardBacklightState]: ReducerFunction<KeyboardBacklightState[K]> } = { - backlightColor: backlightColorReducer, + currentBacklightState: currentBacklightStateReducer, shouldShowNudge: shouldShowNudgeReducer, wallpaperColor: wallpaperColorReducer, };
diff --git a/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_state.ts b/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_state.ts index aab0db8f..0c584136 100644 --- a/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_state.ts +++ b/ash/webui/personalization_app/resources/js/keyboard_backlight/keyboard_backlight_state.ts
@@ -4,20 +4,20 @@ import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js'; -import {BacklightColor} from '../../personalization_app.mojom-webui.js'; +import {CurrentBacklightState} from '../../personalization_app.mojom-webui.js'; /** * Stores keyboard backlight related states. */ export interface KeyboardBacklightState { - backlightColor: BacklightColor|null; + currentBacklightState: CurrentBacklightState|null; shouldShowNudge: boolean; wallpaperColor: SkColor|null; } export function emptyState(): KeyboardBacklightState { return { - backlightColor: null, + currentBacklightState: null, shouldShowNudge: false, wallpaperColor: null, };
diff --git a/ash/webui/personalization_app/resources/js/keyboard_backlight/zone_customization_element.html b/ash/webui/personalization_app/resources/js/keyboard_backlight/zone_customization_element.html index 64750d1b..45c122d 100644 --- a/ash/webui/personalization_app/resources/js/keyboard_backlight/zone_customization_element.html +++ b/ash/webui/personalization_app/resources/js/keyboard_backlight/zone_customization_element.html
@@ -1,9 +1,51 @@ <style include="common cros-button-style"> + cr-dialog::part(dialog) { + width: 600px; + } + #zoneSelector { + background-color: var(--cros-sys-system_on_base); + border-radius: 18px; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(0,1fr)); + grid-template-rows: minmax(0,1fr); + width: 100%; + } + .zone-button { + border-style: none; + height: 100%; + width: 100%; + } + .zone-title-container { + align-items: center; + display: flex; + width: 100%; + } + color-icon { + margin-inline-end: 10px; + } + #zoneTitle { + max-width: calc(100% - 40px); + word-wrap: break-word; + } </style> <cr-dialog id="dialog" on-close="closeZoneCustomizationDialog_"> - <!-- TODO(b/265853968): Add zone selector --> <div slot="body"> - [temp] zone customization + <iron-selector + id="zoneSelector" + selected="0" + selected-item="{{ironSelectedZone_}}"> + <template is="dom-repeat" items="[[zoneIdxs_]]" as="zoneIdx"> + <cr-button + class="zone-button tab-slider" + tabindex$="[[getTabIndex_(zoneIdx)]]" + on-click="onClickZoneButton_"> + <div class="zone-title-container"> + <color-icon color-id="[[getColorId_(zoneIdx, zoneColors_)]]"></color-icon> + <div id="zoneTitle">[[getZoneTitle_(zoneIdx)]]</div> + </div> + </cr-button> + </template> + </iron-selector> <!-- TODO(b/265855838): Add color selector --> <cr-button on-click="setZoneOneToRed_">[temp] Red</cr-button> </div>
diff --git a/ash/webui/personalization_app/resources/js/keyboard_backlight/zone_customization_element.ts b/ash/webui/personalization_app/resources/js/keyboard_backlight/zone_customization_element.ts index bd55a0f..31f278e2 100644 --- a/ash/webui/personalization_app/resources/js/keyboard_backlight/zone_customization_element.ts +++ b/ash/webui/personalization_app/resources/js/keyboard_backlight/zone_customization_element.ts
@@ -9,11 +9,14 @@ */ import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js'; +import './color_icon_element.js'; +import {loadTimeData} from 'chrome://resources/ash/common/load_time_data.m.js'; import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js'; -import {BacklightColor} from '../../personalization_app.mojom-webui.js'; +import {BacklightColor, CurrentBacklightState} from '../../personalization_app.mojom-webui.js'; import {WithPersonalizationStore} from '../personalization_store.js'; +import {RAINBOW, staticColorIds} from '../utils.js'; import {setBacklightZoneColor} from './keyboard_backlight_controller.js'; import {getKeyboardBacklightProvider} from './keyboard_backlight_interface_provider.js'; @@ -34,6 +37,50 @@ return getTemplate(); } + static get properties() { + return { + /** The currently selected zone index. */ + ironSelectedZone_: Object, + + /** The current backlight state in the system. */ + currentBacklightState_: Object, + + /** The current backlight zone colors. */ + zoneColors_: { + type: Array, + computed: 'computeZoneColors_(currentBacklightState_, zoneCount_)', + }, + + /** Number of zones available for customization */ + zoneCount_: { + type: Number, + value() { + return loadTimeData.getInteger('keyboardBacklightZoneCount'); + }, + }, + + /** The zone indexes (of zoneColors_) to indicate the zone number. */ + zoneIdxs_: { + type: Array, + computed: 'computeZoneIdxs_(zoneCount_)', + }, + }; + } + + private ironSelectedZone_: number; + private currentBacklightState_: CurrentBacklightState|null; + private zoneColors_: BacklightColor[]|null; + private zoneCount_: number; + private zoneIds_: number[]; + + override connectedCallback() { + super.connectedCallback(); + this.watch( + 'currentBacklightState_', + state => state.keyboardBacklight.currentBacklightState); + this.updateFromStore(); + } + showModal() { this.$.dialog.showModal(); } @@ -47,6 +94,45 @@ 0, BacklightColor.kRed, getKeyboardBacklightProvider()); } + private computeZoneIdxs_(): number[] { + return [...Array(this.zoneCount_).keys()]; + } + + private computeZoneColors_(): BacklightColor[]|null { + if (this.currentBacklightState_ && this.currentBacklightState_.zoneColors) { + return this.currentBacklightState_.zoneColors; + } else if ( + this.currentBacklightState_ && + this.currentBacklightState_.color !== undefined) { + return Array(this.zoneCount_).fill(this.currentBacklightState_.color); + } + return null; + } + + private getTabIndex_(zoneIdx: number): string { + return zoneIdx === 0 ? '0' : '-1'; + } + + private getZoneTitle_(zoneIdx: number): string { + return loadTimeData.getStringF('zoneTitle', zoneIdx + 1); + } + + // Returns the matching colorId for each zone based on its zone color. + private getColorId_(zoneIdx: number, zoneColors: BacklightColor[]): string + |null { + if (!zoneColors) { + return null; + } + const zoneColor = zoneColors[zoneIdx]; + if (zoneColor === BacklightColor.kRainbow) { + return RAINBOW; + } + // BacklightColor value matches with the index of staticColorIds. + // Ex: zoneColor value is BacklightColor.kGreen or 4, corresponding to + // staticColorIds[4] which is GREEN. + return staticColorIds[zoneColor]; + } + private closeZoneCustomizationDialog_() { this.$.dialog.close(); }
diff --git a/ash/webui/personalization_app/resources/js/load_time_booleans.ts b/ash/webui/personalization_app/resources/js/load_time_booleans.ts index 37ac306b..d0328d2a 100644 --- a/ash/webui/personalization_app/resources/js/load_time_booleans.ts +++ b/ash/webui/personalization_app/resources/js/load_time_booleans.ts
@@ -40,5 +40,5 @@ } export function isMultiZoneRgbKeyboardSupported() { - return loadTimeData.getBoolean('isMultiZoneRgbKeyboardSupported'); + return loadTimeData.getInteger('keyboardBacklightZoneCount') > 1; }
diff --git a/ash/webui/personalization_app/resources/js/personalization_app.ts b/ash/webui/personalization_app/resources/js/personalization_app.ts index dccd7fc..1bed516 100644 --- a/ash/webui/personalization_app/resources/js/personalization_app.ts +++ b/ash/webui/personalization_app/resources/js/personalization_app.ts
@@ -22,6 +22,7 @@ import './ambient/topic_source_item_element.js'; import './ambient/topic_source_list_element.js'; import './ambient/zero_state_element.js'; +import './keyboard_backlight/color_icon_element.js'; import './keyboard_backlight/keyboard_backlight_element.js'; import './keyboard_backlight/zone_customization_element.js'; import './personalization_router_element.js'; @@ -67,7 +68,8 @@ export {TopicSourceItem} from './ambient/topic_source_item_element.js'; export {TopicSourceList} from './ambient/topic_source_list_element.js'; export {AmbientZeroState} from './ambient/zero_state_element.js'; -export {KeyboardBacklightActionName, KeyboardBacklightActions, SetBacklightColorAction, setBacklightColorAction, SetShouldShowNudgeAction, setShouldShowNudgeAction, SetWallpaperColorAction, setWallpaperColorAction} from './keyboard_backlight/keyboard_backlight_actions.js'; +export {ColorIcon} from './keyboard_backlight/color_icon_element.js'; +export {KeyboardBacklightActionName, KeyboardBacklightActions, SetCurrentBacklightStateAction, setCurrentBacklightStateAction, SetShouldShowNudgeAction, setShouldShowNudgeAction, SetWallpaperColorAction, setWallpaperColorAction} from './keyboard_backlight/keyboard_backlight_actions.js'; export {KeyboardBacklight} from './keyboard_backlight/keyboard_backlight_element.js'; export {setKeyboardBacklightProviderForTesting} from './keyboard_backlight/keyboard_backlight_interface_provider.js'; export {KeyboardBacklightObserver} from './keyboard_backlight/keyboard_backlight_observer.js'; @@ -96,7 +98,7 @@ export {UserPreview} from './user/user_preview_element.js'; export {UserSubpage} from './user/user_subpage_element.js'; export {GetUserMediaProxy, getWebcamUtils, setWebcamUtilsForTesting} from './user/webcam_utils_proxy.js'; -export {getCountText, getNumberOfGridItemsPerRow, getSanitizedDefaultImageUrl} from './utils.js'; +export {getCountText, getNumberOfGridItemsPerRow, getSanitizedDefaultImageUrl, staticColorIds} from './utils.js'; export {DefaultImageSymbol, DisplayableImage, kDefaultImageSymbol, kMaximumLocalImagePreviews} from './wallpaper/constants.js'; export {GooglePhotosAlbums} from './wallpaper/google_photos_albums_element.js'; export {GooglePhotosCollection} from './wallpaper/google_photos_collection_element.js';
diff --git a/ash/webui/personalization_app/resources/js/utils.ts b/ash/webui/personalization_app/resources/js/utils.ts index a3320a0..2311b339 100644 --- a/ash/webui/personalization_app/resources/js/utils.ts +++ b/ash/webui/personalization_app/resources/js/utils.ts
@@ -10,9 +10,27 @@ import {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js'; import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js'; -import {AmbientModeAlbum, GooglePhotosAlbum} from './../personalization_app.mojom-webui.js'; +import {AmbientModeAlbum, BacklightColor, BLUE_COLOR, GooglePhotosAlbum, GREEN_COLOR, INDIGO_COLOR, PURPLE_COLOR, RED_COLOR, WHITE_COLOR, YELLOW_COLOR} from './../personalization_app.mojom-webui.js'; import {isPersonalizationJellyEnabled} from './load_time_booleans.js'; +export interface ColorInfo { + hexVal: string; + enumVal: BacklightColor; +} + +export const WALLPAPER: string = 'wallpaper'; +export const WHITE: string = 'whiteColor'; +export const RED: string = 'redColor'; +export const YELLOW: string = 'yellowColor'; +export const GREEN: string = 'greenColor'; +export const BLUE: string = 'blueColor'; +export const INDIGO: string = 'indigoColor'; +export const PURPLE: string = 'purpleColor'; +export const RAINBOW: string = 'rainbow'; + +export const staticColorIds = + [WALLPAPER, WHITE, RED, YELLOW, GREEN, BLUE, INDIGO, PURPLE]; + export type PersonalizationAppSelectionEvent = MouseEvent&{type: 'click'}|KeyboardEvent&{key: 'Enter'}; @@ -103,6 +121,43 @@ } /** + * Returns the mapping of preset colors to their hex value and enum value in + * BacklightColor. + */ +export function getPresetColors(): Record<string, ColorInfo> { + return { + [WHITE]: { + hexVal: convertToRgbHexStr(WHITE_COLOR), + enumVal: BacklightColor.kWhite, + }, + [RED]: { + hexVal: convertToRgbHexStr(RED_COLOR), + enumVal: BacklightColor.kRed, + }, + [YELLOW]: { + hexVal: convertToRgbHexStr(YELLOW_COLOR), + enumVal: BacklightColor.kYellow, + }, + [GREEN]: { + hexVal: convertToRgbHexStr(GREEN_COLOR), + enumVal: BacklightColor.kGreen, + }, + [BLUE]: { + hexVal: convertToRgbHexStr(BLUE_COLOR), + enumVal: BacklightColor.kBlue, + }, + [INDIGO]: { + hexVal: convertToRgbHexStr(INDIGO_COLOR), + enumVal: BacklightColor.kIndigo, + }, + [PURPLE]: { + hexVal: convertToRgbHexStr(PURPLE_COLOR), + enumVal: BacklightColor.kPurple, + }, + }; +} + +/** * Returns whether the given album is Recent Highlights. */ export function isRecentHighlightsAlbum(album: AmbientModeAlbum|
diff --git a/ash/webui/shortcut_customization_ui/BUILD.gn b/ash/webui/shortcut_customization_ui/BUILD.gn index 2bb5cbef..41f2647 100644 --- a/ash/webui/shortcut_customization_ui/BUILD.gn +++ b/ash/webui/shortcut_customization_ui/BUILD.gn
@@ -33,3 +33,39 @@ "//ui/webui", ] } + +source_set("unit_tests") { + testonly = true + + sources = [ + "backend/accelerator_configuration_provider_unittest.cc", + "backend/search/search_concept_registry_unittest.cc", + "backend/search/search_handler_unittest.cc", + "shortcuts_app_manager_unittest.cc", + ] + + deps = [ + ":shortcut_customization_ui", + "//ash", + "//ash:test_support", + "//ash/public/cpp", + "//ash/public/mojom", + "//ash/webui/shortcut_customization_ui/backend", + "//ash/webui/shortcut_customization_ui/backend/search:mojo_bindings", + "//ash/webui/shortcut_customization_ui/mojom", + "//base/test:test_support", + "//chromeos/ash/components:test_support", + "//chromeos/ash/components/local_search_service/public/cpp:cpp", + "//chromeos/ash/components/local_search_service/public/mojom", + "//chromeos/ash/components/local_search_service/public/mojom:mojom", + "//content/test:test_support", + "//device/udev_linux:test_support", + "//mojo/public/cpp/bindings:bindings", + "//testing/gtest", + "//ui/base:test_support", + "//ui/base/ime/ash", + "//ui/chromeos/events", + "//ui/events/devices", + "//ui/events/devices:test_support", + ] +}
diff --git a/ash/webui/shortcut_customization_ui/backend/BUILD.gn b/ash/webui/shortcut_customization_ui/backend/BUILD.gn index a64096f..ed8960a 100644 --- a/ash/webui/shortcut_customization_ui/backend/BUILD.gn +++ b/ash/webui/shortcut_customization_ui/backend/BUILD.gn
@@ -38,37 +38,3 @@ "//ui/events/devices", ] } - -source_set("unit_tests") { - testonly = true - - sources = [ - "accelerator_configuration_provider_unittest.cc", - "search/search_concept_registry_unittest.cc", - "search/search_handler_unittest.cc", - ] - - deps = [ - ":backend", - "//ash", - "//ash:test_support", - "//ash/public/cpp", - "//ash/public/mojom", - "//ash/webui/shortcut_customization_ui/backend/search:mojo_bindings", - "//ash/webui/shortcut_customization_ui/mojom", - "//base/test:test_support", - "//chromeos/ash/components:test_support", - "//chromeos/ash/components/local_search_service/public/cpp:cpp", - "//chromeos/ash/components/local_search_service/public/mojom", - "//chromeos/ash/components/local_search_service/public/mojom:mojom", - "//content/test:test_support", - "//device/udev_linux:test_support", - "//mojo/public/cpp/bindings:bindings", - "//testing/gtest", - "//ui/base:test_support", - "//ui/base/ime/ash", - "//ui/chromeos/events", - "//ui/events/devices", - "//ui/events/devices:test_support", - ] -}
diff --git a/ash/webui/shortcut_customization_ui/backend/search/fake_search_data.h b/ash/webui/shortcut_customization_ui/backend/search/fake_search_data.h index 386b79a9..83d41b8 100644 --- a/ash/webui/shortcut_customization_ui/backend/search/fake_search_data.h +++ b/ash/webui/shortcut_customization_ui/backend/search/fake_search_data.h
@@ -14,7 +14,7 @@ namespace ash::shortcut_ui::fake_search_data { -enum FakeActionIds { kAction1 = 1, kAction2 = 2 }; +enum FakeActionIds { kAction1 = 1, kAction2 = 2, kAction3 = 3 }; ash::mojom::AcceleratorInfoPtr CreateFakeStandardAcceleratorInfo();
diff --git a/ash/webui/shortcut_customization_ui/backend/search/search_concept_registry.cc b/ash/webui/shortcut_customization_ui/backend/search/search_concept_registry.cc index 53f765e..54c4f11 100644 --- a/ash/webui/shortcut_customization_ui/backend/search/search_concept_registry.cc +++ b/ash/webui/shortcut_customization_ui/backend/search/search_concept_registry.cc
@@ -26,54 +26,6 @@ namespace ash::shortcut_ui { -namespace { - -// Given text accelerator properties, return a string that will be searchable -// by the Local Search Service. -std::u16string TextAcceleratorToContentString( - const mojom::TextAcceleratorPropertiesPtr& text_accelerator_properties) { - // To get the searchable part of a Text Accelerator, combine all of the - // TextAcceleratorParts into one string. - std::u16string output; - for (const auto& part : text_accelerator_properties->parts) { - base::StrAppend(&output, {part->text}); - } - return output; -} - -// Given text accelerator properties, return an ID that will be used for the -// Local Search Service Content object, which represents a searchable piece of -// data. -std::string StandardAcceleratorToContentId( - const mojom::StandardAcceleratorPropertiesPtr& - standard_accelerator_properties) { - // ID strings only need to be unique within a given SearchConcept, - // so it's sufficient to create an ID from the accelerator modifiers and - // key_code. - return base::StrCat( - {base::NumberToString( - standard_accelerator_properties->accelerator.modifiers()), - "-", - base::NumberToString( - standard_accelerator_properties->accelerator.key_code())}); -} - -// Given standard accelerator properties, return a string that will be -// searchable by the Local Search Service. -std::u16string StandardAcceleratorToContentString( - const mojom::StandardAcceleratorPropertiesPtr& - standard_accelerator_properties) { - // TODO(cambickel) GetShortcutText outputs a shortcut with "+" as the - // delimiter, e.g. "Ctrl+Shift+Q". We want to delimit with spaces, e.g. "Ctrl - // Shift Q". This has a fair amount of edge cases so we'll handle this later. - // TODO(cambickel) GetShortcutText also doesn't properly handle certain - // "special" keys, e.g. BrightnessDown. For now, we'll use it, but we should - // eventually switch to a more robust solution. - return standard_accelerator_properties->accelerator.GetShortcutText(); -} - -} // namespace - SearchConceptRegistry::SearchConceptRegistry( local_search_service::LocalSearchServiceProxy& local_search_service_proxy) { local_search_service_proxy.GetIndex( @@ -149,9 +101,8 @@ // queries. std::vector<local_search_service::Content> local_search_service_contents; // Reserve the size for the vector since we insert once for the description - // and once for each AcceleratorInfo. - local_search_service_contents.reserve( - search_concept.accelerator_infos.size() + 1); + // and at most once more in the case of text accelerators. + local_search_service_contents.reserve(2); // First, add this SearchConcept's description as searchable Content. // Note that the Content ID is prefixed with the SearchConcept ID, since @@ -162,30 +113,25 @@ /*id=*/base::StrCat({search_concept.id, "-description"}), /*content=*/search_concept.accelerator_layout_info->description); - // Next, for each accelerator_info, register it (and its accelerators in the - // case of standard accelerators) as searchable Content. - for (const auto& accelerator_info : search_concept.accelerator_infos) { + // Only text accelerators should become searchable LSS Content. + DCHECK(search_concept.accelerator_infos.size() > 0); + // Text accelerators should only have one entry in accelerator_infos. + const mojom::AcceleratorInfoPtr& first_accelerator_info = + search_concept.accelerator_infos.at(0); + + if (first_accelerator_info->layout_properties->is_text_accelerator()) { // Content->id needs to be unique across the entire index, // so we prefix it with the SearchConcept's id. - // The part of the id besides the SearchConcept's id only - // needs to be unique within that SearchConcept's accelerators. - std::string content_id; - std::u16string content_string; + std::string content_id = + base::StrCat({search_concept.id, "-text-accelerator"}); - if (accelerator_info->layout_properties->is_text_accelerator()) { - // The content_id for a text accelerator doesn't need to be based on the - // content of the text accelerator because text accelerators have only - // one entry in SearchConcept.accelerator_infos. - content_id = base::StrCat({search_concept.id, "-text-accelerator"}); - content_string = TextAcceleratorToContentString( - accelerator_info->layout_properties->get_text_accelerator()); - } else { - content_id = base::StrCat( - {search_concept.id, "-", - StandardAcceleratorToContentId(accelerator_info->layout_properties - ->get_standard_accelerator())}); - content_string = StandardAcceleratorToContentString( - accelerator_info->layout_properties->get_standard_accelerator()); + // To get the searchable part of a Text Accelerator, combine all of the + // TextAcceleratorParts into one string. + std::u16string content_string; + for (const auto& part : + first_accelerator_info->layout_properties->get_text_accelerator() + ->parts) { + base::StrAppend(&content_string, {part->text}); } local_search_service_contents.emplace_back(
diff --git a/ash/webui/shortcut_customization_ui/backend/search/search_concept_registry.h b/ash/webui/shortcut_customization_ui/backend/search/search_concept_registry.h index 246052a..bece3977 100644 --- a/ash/webui/shortcut_customization_ui/backend/search/search_concept_registry.h +++ b/ash/webui/shortcut_customization_ui/backend/search/search_concept_registry.h
@@ -66,6 +66,7 @@ SearchConceptToDataStandardAccelerator); FRIEND_TEST_ALL_PREFIXES(SearchConceptRegistryTest, SearchConceptToDataTextAccelerator); + FRIEND_TEST_ALL_PREFIXES(ShortcutsAppManagerTest, SetSearchConcepts); void NotifyRegistryUpdated();
diff --git a/ash/webui/shortcut_customization_ui/backend/search/search_concept_registry_unittest.cc b/ash/webui/shortcut_customization_ui/backend/search/search_concept_registry_unittest.cc index 1480316..79761f6 100644 --- a/ash/webui/shortcut_customization_ui/backend/search/search_concept_registry_unittest.cc +++ b/ash/webui/shortcut_customization_ui/backend/search/search_concept_registry_unittest.cc
@@ -219,28 +219,11 @@ // The overall data ID should be source + action. EXPECT_EQ(data.id, "0-1"); - // There should be three entries: one for the description, and two more total - // (one for each of the AcceleratorInfos). - EXPECT_EQ(data.contents.size(), 3u); + // There should be only one contents entry for the description. + EXPECT_EQ(data.contents.size(), 1u); // The first entry will always be the description of the SearchConcept. EXPECT_EQ(data.contents[0].id, "0-1-description"); EXPECT_EQ(data.contents[0].content, u"Open the Foobar"); - // The second entry in this case will be the first accelerator info's - // accelerator. The "0-1" in the id is the SearchConcept's id, the "6" is the - // modifier, and the "65" is the key_code. - EXPECT_EQ(data.contents[1].id, "0-1-6-65"); - // TODO(cambickel): When we update the Content string here to be delimited by - // spaces instead of pluses, update this test. - EXPECT_EQ(data.contents[1].content, u"Ctrl+Shift+A"); - // The third entry in this case will be the second accelerator info's - // accelerator. The "0-1" in the id is the SearchConcept's id, the "8" is the - // modifier, and the "216" is the key_code. - EXPECT_EQ(data.contents[2].id, "0-1-8-216"); - // TODO(cambickel): When we update the Content string here to be delimited by - // spaces instead of pluses, update this test. Also, "BrightnessDown" and - // other top-row keys are not currently handled correctly, so when we fix - // that, update this test accordingly. - EXPECT_EQ(data.contents[2].content, u"Alt+"); } TEST_F(SearchConceptRegistryTest, SearchConceptToDataTextAccelerator) {
diff --git a/ash/webui/shortcut_customization_ui/shortcuts_app_manager.cc b/ash/webui/shortcut_customization_ui/shortcuts_app_manager.cc index 0f397508..0e40857 100644 --- a/ash/webui/shortcut_customization_ui/shortcuts_app_manager.cc +++ b/ash/webui/shortcut_customization_ui/shortcuts_app_manager.cc
@@ -7,7 +7,10 @@ #include <memory> #include "ash/constants/ash_features.h" +#include "ash/public/mojom/accelerator_info.mojom-forward.h" +#include "ash/public/mojom/accelerator_info.mojom.h" #include "ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h" +#include "ash/webui/shortcut_customization_ui/backend/search/search_concept.h" #include "ash/webui/shortcut_customization_ui/backend/search/search_concept_registry.h" #include "ash/webui/shortcut_customization_ui/backend/search/search_handler.h" #include "base/feature_list.h" @@ -16,9 +19,6 @@ namespace ash::shortcut_ui { -// TODO(cambickel): Update this constructor to fetch the list of accelerators -// and use them to call SearchConceptRegistry.AddSearchConcepts, to populate -// the LSS index. ShortcutsAppManager::ShortcutsAppManager( local_search_service::LocalSearchServiceProxy* local_search_service_proxy) { if (features::IsSearchInShortcutsAppEnabled()) { @@ -66,8 +66,25 @@ shortcut_ui::AcceleratorConfigurationProvider::AcceleratorConfigurationMap config, std::vector<mojom::AcceleratorLayoutInfoPtr> layout_infos) { - // TODO(cambickel): Use accelerators to create search concepts and add them to - // the search concept registry. + if (!features::IsSearchInShortcutsAppEnabled()) { + return; + } + + std::vector<SearchConcept> search_concepts; + + for (auto& layout_info : layout_infos) { + if (const auto& config_iterator = config.find(layout_info->source); + config_iterator != config.end()) { + if (const auto& map_iterator = + config_iterator->second.find(layout_info->action); + map_iterator != config_iterator->second.end()) { + search_concepts.emplace_back(std::move(layout_info), + std::move(map_iterator->second)); + } + } + } + + search_concept_registry_->SetSearchConcepts(std::move(search_concepts)); } } // namespace ash::shortcut_ui
diff --git a/ash/webui/shortcut_customization_ui/shortcuts_app_manager.h b/ash/webui/shortcut_customization_ui/shortcuts_app_manager.h index cc3b2d2df..9496a3d9 100644 --- a/ash/webui/shortcut_customization_ui/shortcuts_app_manager.h +++ b/ash/webui/shortcut_customization_ui/shortcuts_app_manager.h
@@ -5,12 +5,14 @@ #ifndef ASH_WEBUI_SHORTCUT_CUSTOMIZATION_UI_SHORTCUTS_APP_MANAGER_H_ #define ASH_WEBUI_SHORTCUT_CUSTOMIZATION_UI_SHORTCUTS_APP_MANAGER_H_ +#include <memory> + +#include "ash/public/mojom/accelerator_info.mojom-forward.h" #include "ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h" #include "ash/webui/shortcut_customization_ui/mojom/shortcut_customization.mojom.h" +#include "base/gtest_prod_util.h" #include "components/keyed_service/core/keyed_service.h" -#include <memory> - namespace ash { namespace local_search_service { @@ -53,6 +55,7 @@ configs) override; private: + FRIEND_TEST_ALL_PREFIXES(ShortcutsAppManagerTest, SetSearchConcepts); // KeyedService: void Shutdown() override;
diff --git a/ash/webui/shortcut_customization_ui/shortcuts_app_manager_unittest.cc b/ash/webui/shortcut_customization_ui/shortcuts_app_manager_unittest.cc new file mode 100644 index 0000000..bc9129c4 --- /dev/null +++ b/ash/webui/shortcut_customization_ui/shortcuts_app_manager_unittest.cc
@@ -0,0 +1,148 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/webui/shortcut_customization_ui/shortcuts_app_manager.h" + +#include <cstdint> +#include <memory> +#include <vector> + +#include "ash/constants/ash_features.h" +#include "ash/public/cpp/accelerator_configuration.h" +#include "ash/public/mojom/accelerator_info.mojom-shared.h" +#include "ash/public/mojom/accelerator_info.mojom.h" +#include "ash/shell.h" +#include "ash/test/ash_test_base.h" +#include "ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h" +#include "ash/webui/shortcut_customization_ui/backend/search/fake_search_data.h" +#include "ash/webui/shortcut_customization_ui/backend/search/search_concept_registry.h" +#include "ash/webui/shortcut_customization_ui/shortcuts_app_manager.h" +#include "base/containers/flat_map.h" +#include "base/run_loop.h" +#include "base/test/scoped_feature_list.h" +#include "chromeos/ash/components/local_search_service/public/cpp/local_search_service_proxy.h" +#include "chromeos/ash/components/test/ash_test_suite.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/resource/resource_bundle.h" + +namespace ash::shortcut_ui { + +class ShortcutsAppManagerTest : public AshTestBase { + protected: + ShortcutsAppManagerTest() = default; + ~ShortcutsAppManagerTest() override = default; + + // AshTestBase: + void SetUp() override { + scoped_feature_list_.InitWithFeatures({features::kSearchInShortcutsApp}, + {}); + ui::ResourceBundle::CleanupSharedInstance(); + AshTestSuite::LoadTestResources(); + AshTestBase::SetUp(); + + local_search_service_proxy_ = + std::make_unique<local_search_service::LocalSearchServiceProxy>( + /*for_testing=*/true); + + manager_ = std::make_unique<ShortcutsAppManager>( + local_search_service_proxy_.get()); + + // Let the RunLoop so that the AshAcceleratorConfiguration can load its + // default accelerators. + base::RunLoop().RunUntilIdle(); + } + + void TearDown() override { + manager_.reset(); + AshTestBase::TearDown(); + } + + void ValidateSearchConceptById( + const base::flat_map<std::string, SearchConcept>& search_concepts_map, + const std::string search_concept_id, + const mojom::AcceleratorSource expected_source, + const uint32_t expected_action) const { + EXPECT_TRUE(search_concepts_map.contains(search_concept_id)); + EXPECT_EQ(search_concepts_map.at(search_concept_id) + .accelerator_layout_info->source, + expected_source); + EXPECT_EQ(search_concepts_map.at(search_concept_id) + .accelerator_layout_info->action, + expected_action); + } + + base::test::ScopedFeatureList scoped_feature_list_; + std::unique_ptr<local_search_service::LocalSearchServiceProxy> + local_search_service_proxy_; + std::unique_ptr<ShortcutsAppManager> manager_; +}; + +TEST_F(ShortcutsAppManagerTest, SetSearchConcepts) { + // Create all the fake AcceleratorInfo maps for use in the fake config. + AcceleratorConfigurationProvider::ActionIdToAcceleratorsInfoMap ash_info_map; + ash_info_map.insert({fake_search_data::FakeActionIds::kAction1, + fake_search_data::CreateFakeAcceleratorInfoList()}); + + AcceleratorConfigurationProvider::ActionIdToAcceleratorsInfoMap + browser_info_map; + browser_info_map.insert({fake_search_data::FakeActionIds::kAction2, + fake_search_data::CreateFakeAcceleratorInfoList()}); + browser_info_map.insert({fake_search_data::FakeActionIds::kAction3, + fake_search_data::CreateFakeAcceleratorInfoList()}); + + // Create the fake config. + shortcut_ui::AcceleratorConfigurationProvider::AcceleratorConfigurationMap + fake_config; + fake_config.insert({mojom::AcceleratorSource::kAsh, std::move(ash_info_map)}); + fake_config.insert( + {mojom::AcceleratorSource::kBrowser, std::move(browser_info_map)}); + + // Create the fake AcceleratorLayoutInfos list. + std::vector<mojom::AcceleratorLayoutInfoPtr> fake_layout_infos; + fake_layout_infos.push_back(fake_search_data::CreateFakeAcceleratorLayoutInfo( + u"Open launcher", ash::mojom::AcceleratorSource::kAsh, + fake_search_data::FakeActionIds::kAction1, + ash::mojom::AcceleratorLayoutStyle::kDefault)); + fake_layout_infos.push_back(fake_search_data::CreateFakeAcceleratorLayoutInfo( + u"Open new tab", ash::mojom::AcceleratorSource::kBrowser, + fake_search_data::FakeActionIds::kAction2, + ash::mojom::AcceleratorLayoutStyle::kDefault)); + fake_layout_infos.push_back(fake_search_data::CreateFakeAcceleratorLayoutInfo( + u"Close tab", ash::mojom::AcceleratorSource::kBrowser, + fake_search_data::FakeActionIds::kAction3, + ash::mojom::AcceleratorLayoutStyle::kDefault)); + + auto& registry_search_concepts = + manager_->search_concept_registry_.get()->result_id_to_search_concept_; + + // AshAcceleratorConfiguration loads some initial accelerators (which ends up + // populating the registry map), so clear the registry map to get a fresh + // slate for the test. + registry_search_concepts.clear(); + EXPECT_EQ(registry_search_concepts.size(), 0u); + + manager_->SetSearchConcepts(std::move(fake_config), + std::move(fake_layout_infos)); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(registry_search_concepts.size(), 3u); + // Test that the expected search concepts are present and check a few + // attributes to be sure. + ValidateSearchConceptById(/*search_concepts_map=*/registry_search_concepts, + /*search_concept_id=*/"0-1", + /*expected_source=*/mojom::AcceleratorSource::kAsh, + /*expected_action=*/fake_search_data::kAction1); + ValidateSearchConceptById( + /*search_concepts_map=*/registry_search_concepts, + /*search_concept_id=*/"2-2", + /*expected_source=*/mojom::AcceleratorSource::kBrowser, + /*expected_action=*/fake_search_data::kAction2); + ValidateSearchConceptById( + /*search_concepts_map=*/registry_search_concepts, + /*search_concept_id=*/"2-3", + /*expected_source=*/mojom::AcceleratorSource::kBrowser, + /*expected_action=*/fake_search_data::kAction3); +} + +} // namespace ash::shortcut_ui \ No newline at end of file
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc index c5412f6..ee4db71 100644 --- a/ash/wm/desks/desks_unittests.cc +++ b/ash/wm/desks/desks_unittests.cc
@@ -4864,7 +4864,7 @@ int* result) const override { return false; } - bool TopRowKeysAreFunctionKeys() const override { return false; } + bool TopRowKeysAreFunctionKeys(int device_id) const override { return false; } bool IsExtensionCommandRegistered(ui::KeyboardCode key_code, int flags) const override { return false;
diff --git a/ash/wm/float/float_controller.cc b/ash/wm/float/float_controller.cc index de23f24..48e86ec 100644 --- a/ash/wm/float/float_controller.cc +++ b/ash/wm/float/float_controller.cc
@@ -42,8 +42,6 @@ #include "ui/display/screen.h" #include "ui/wm/core/coordinate_conversion.h" -using MagnetismCorner = ash::FloatController::MagnetismCorner; - namespace ash { namespace {
diff --git a/ash/wm/float/float_controller_unittest.cc b/ash/wm/float/float_controller_unittest.cc index 623db6d..6ea5ee1 100644 --- a/ash/wm/float/float_controller_unittest.cc +++ b/ash/wm/float/float_controller_unittest.cc
@@ -1686,11 +1686,11 @@ ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession()); ASSERT_TRUE(split_view_controller->InSplitViewMode()); - // Float the window so we can snap it again. Assert that we are still in - // overview, but no longer in splitview. + // Float the window so we can snap it again. Assert that we are no longer in + // overview or splitview. PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN); ASSERT_TRUE(WindowState::Get(window.get())->IsFloated()); - ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession()); + ASSERT_FALSE(Shell::Get()->overview_controller()->InOverviewSession()); ASSERT_FALSE(split_view_controller->InSplitViewMode()); // Create a second window. @@ -1704,6 +1704,14 @@ EXPECT_TRUE(split_view_controller->BothSnapped()); EXPECT_EQ(split_view_controller->primary_window(), window.get()); EXPECT_EQ(split_view_controller->secondary_window(), other_window.get()); + + // Tests that in overview mode, with at least one app window in overview, that + // we also exit splitview and overview when floating the snapped window. + ToggleOverview(); + wm::ActivateWindow(window.get()); + PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN); + EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession()); + EXPECT_FALSE(split_view_controller->InSplitViewMode()); } // When reset a floated window that's previously snapped, maximize instead of
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc index 1e65af9..2ab5d42 100644 --- a/ash/wm/splitview/split_view_controller.cc +++ b/ash/wm/splitview/split_view_controller.cc
@@ -1781,19 +1781,16 @@ if (do_divider_spawn_animation) DoSplitDividerSpawnAnimation(window); } else if (window_state->IsNormalStateType() || window_state->IsMaximized() || - window_state->IsFullscreen()) { + window_state->IsFullscreen() || window_state->IsFloated()) { // End split view, and also overview if overview is active, in these cases: // 1. A left clamshell split view window gets unsnapped by Alt+[. // 2. A right clamshell split view window gets unsnapped by Alt+]. // 3. A (clamshell or tablet) split view window gets maximized. // 4. A (clamshell or tablet) split view window becomes full screen. + // 5. A split view window becomes floated. EndSplitView(); Shell::Get()->overview_controller()->EndOverview( OverviewEndAction::kSplitView); - } else if (window_state->IsFloated()) { - OnSnappedWindowDetached(window, WindowDetachedReason::kWindowFloated); - - // TODO(crbug.com/1351562): Consider ending overview here. } else if (window_state->IsMinimized()) { OnSnappedWindowDetached(window, WindowDetachedReason::kWindowMinimized);
diff --git a/base/BUILD.gn b/base/BUILD.gn index 60da4027..bd94a2c 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -933,6 +933,7 @@ "types/pass_key.h", "types/strong_alias.h", "types/token_type.h", + "types/variant_util.h", "unguessable_token.cc", "unguessable_token.h", "value_iterators.cc", @@ -3402,6 +3403,7 @@ "types/pass_key_unittest.cc", "types/strong_alias_unittest.cc", "types/token_type_unittest.cc", + "types/variant_util_unittest.cc", "unguessable_token_unittest.cc", "value_iterators_unittest.cc", "values_unittest.cc", @@ -3428,6 +3430,7 @@ "//base/test:run_all_unittests", "//base/test:test_support", "//base/third_party/dynamic_annotations", + "//build:blink_buildflags", "//build:chromecast_buildflags", "//build:chromeos_buildflags", "//testing/gmock", @@ -4036,6 +4039,7 @@ "thread_annotations_unittest.nc", "traits_bag_unittest.nc", "types/pass_key_unittest.nc", + "types/variant_util_unittest.nc", "values_unittest.nc", ]
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_unittest.cc b/base/allocator/partition_allocator/shim/allocator_shim_unittest.cc index b68f6fe..626d098 100644 --- a/base/allocator/partition_allocator/shim/allocator_shim_unittest.cc +++ b/base/allocator/partition_allocator/shim/allocator_shim_unittest.cc
@@ -19,7 +19,6 @@ #include "base/memory/page_size.h" #include "base/synchronization/waitable_event.h" #include "base/threading/platform_thread.h" -#include "base/threading/thread_local.h" #include "build/build_config.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -113,9 +112,9 @@ // Hitting it for the first time will cause a failure, causing the // invocation of the std::new_handler. if (size == 0xFEED) { - if (!instance_->did_fail_realloc_0xfeed_once->Get()) { - instance_->did_fail_realloc_0xfeed_once->Set( - instance_->did_fail_realloc_0xfeed_once.get()); + thread_local bool did_fail_realloc_0xfeed_once = false; + if (!did_fail_realloc_0xfeed_once) { + did_fail_realloc_0xfeed_once = true; return nullptr; } return address; @@ -260,8 +259,6 @@ aligned_reallocs_intercepted_by_size.resize(MaxSizeTracked()); aligned_reallocs_intercepted_by_addr.resize(MaxSizeTracked()); aligned_frees_intercepted_by_addr.resize(MaxSizeTracked()); - did_fail_realloc_0xfeed_once = - std::make_unique<base::ThreadLocalStorage::Slot>(); num_new_handler_calls.store(0, std::memory_order_release); instance_ = this; @@ -303,7 +300,6 @@ std::vector<size_t> aligned_reallocs_intercepted_by_size; std::vector<size_t> aligned_reallocs_intercepted_by_addr; std::vector<size_t> aligned_frees_intercepted_by_addr; - std::unique_ptr<base::ThreadLocalStorage::Slot> did_fail_realloc_0xfeed_once; std::atomic<uint32_t> num_new_handler_calls; private:
diff --git a/base/process/process_mac.cc b/base/process/process_mac.cc index 066e4ce..b551ec2 100644 --- a/base/process/process_mac.cc +++ b/base/process/process_mac.cc
@@ -18,15 +18,47 @@ #include "base/feature_list.h" #include "base/mac/mach_logging.h" #include "base/memory/free_deleter.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace base { +namespace { + // Enables setting the task role of every child process to // TASK_DEFAULT_APPLICATION. BASE_FEATURE(kMacSetDefaultTaskRole, "MacSetDefaultTaskRole", FEATURE_DISABLED_BY_DEFAULT); +// Returns the `task_role_t` of the process whose process ID is `pid`. +absl::optional<task_role_t> GetTaskCategoryPolicyRole( + PortProvider* port_provider, + ProcessId pid) { + DCHECK(port_provider); + + mach_port_t task_port = port_provider->TaskForPid(pid); + if (task_port == TASK_NULL) { + return absl::nullopt; + } + + task_category_policy_data_t category_policy; + mach_msg_type_number_t task_info_count = TASK_CATEGORY_POLICY_COUNT; + boolean_t get_default = FALSE; + + kern_return_t result = + task_policy_get(task_port, TASK_CATEGORY_POLICY, + reinterpret_cast<task_policy_t>(&category_policy), + &task_info_count, &get_default); + if (result != KERN_SUCCESS) { + MACH_LOG(ERROR, result) << "task_policy_get TASK_CATEGORY_POLICY"; + return absl::nullopt; + } + DCHECK(!get_default); + return category_policy.role; +} + +} // namespace + Time Process::CreationTime() const { int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, Pid()}; size_t len = 0; @@ -48,25 +80,12 @@ DCHECK(IsValid()); DCHECK(port_provider); - mach_port_t task_port = port_provider->TaskForPid(Pid()); - if (task_port == TASK_NULL) - return false; - - task_category_policy_data_t category_policy; - mach_msg_type_number_t task_info_count = TASK_CATEGORY_POLICY_COUNT; - boolean_t get_default = FALSE; - - kern_return_t result = - task_policy_get(task_port, TASK_CATEGORY_POLICY, - reinterpret_cast<task_policy_t>(&category_policy), - &task_info_count, &get_default); - MACH_LOG_IF(ERROR, result != KERN_SUCCESS, result) - << "task_policy_get TASK_CATEGORY_POLICY"; - - if (result == KERN_SUCCESS && get_default == FALSE) { - return category_policy.role == TASK_BACKGROUND_APPLICATION; - } - return false; + // A process is backgrounded if the role is explicitly + // TASK_BACKGROUND_APPLICATION (as opposed to not being + // TASK_FOREGROUND_APPLICATION). + absl::optional<task_role_t> task_role = + GetTaskCategoryPolicyRole(port_provider, Pid()); + return task_role && *task_role == TASK_BACKGROUND_APPLICATION; } bool Process::SetProcessBackgrounded(PortProvider* port_provider, @@ -82,8 +101,16 @@ if (task_port == TASK_NULL) return false; - if (IsProcessBackgrounded(port_provider) == background) + absl::optional<task_role_t> current_role = + GetTaskCategoryPolicyRole(port_provider, Pid()); + if (!current_role) { + return false; + } + + if ((background && *current_role == TASK_BACKGROUND_APPLICATION) || + (!background && *current_role == TASK_FOREGROUND_APPLICATION)) { return true; + } task_category_policy category_policy; category_policy.role =
diff --git a/base/threading/platform_thread_unittest.cc b/base/threading/platform_thread_unittest.cc index a4e34ee..5e08929 100644 --- a/base/threading/platform_thread_unittest.cc +++ b/base/threading/platform_thread_unittest.cc
@@ -12,6 +12,7 @@ #include "base/test/scoped_feature_list.h" #include "base/threading/thread.h" #include "base/threading/threading_features.h" +#include "build/blink_buildflags.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" @@ -539,7 +540,9 @@ TEST(PlatformThreadTest, GetDefaultThreadStackSize) { size_t stack_size = PlatformThread::GetDefaultThreadStackSize(); -#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS) || BUILDFLAG(IS_FUCHSIA) || \ +#if BUILDFLAG(IS_IOS) && BUILDFLAG(USE_BLINK) + EXPECT_EQ(1024u * 1024u, stack_size); +#elif BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS) || BUILDFLAG(IS_FUCHSIA) || \ ((BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && defined(__GLIBC__) && \ !defined(THREAD_SANITIZER)) || \ (BUILDFLAG(IS_ANDROID) && !defined(ADDRESS_SANITIZER))
diff --git a/base/types/variant_util.h b/base/types/variant_util.h new file mode 100644 index 0000000..3a40f46 --- /dev/null +++ b/base/types/variant_util.h
@@ -0,0 +1,52 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_TYPES_VARIANT_UTIL_H_ +#define BASE_TYPES_VARIANT_UTIL_H_ + +#include <stddef.h> + +#include <type_traits> + +#include "base/types/always_false.h" +#include "third_party/abseil-cpp/absl/types/variant.h" + +namespace base { +namespace internal { + +template <typename Variant, typename T> +struct VariantIndexOfTypeHelper { + static_assert(AlwaysFalse<Variant>, "Variant must be an absl::variant<...>"); +}; + +template <typename... Ts, typename T> +struct VariantIndexOfTypeHelper<absl::variant<Ts...>, T> { + static constexpr size_t Index() { + static_assert(std::is_constructible_v<absl::variant<LiteralType<Ts>...>, + LiteralType<T>>, + "Variant is not constructible from T"); + return absl::variant<LiteralType<Ts>...>(LiteralType<T>()).index(); + } + + // Helper struct; even if `Tag` may not be usable as a literal type, a + // `LiteralType<Tag>` will be. + template <typename Tag> + struct LiteralType {}; +}; + +} // namespace internal + +// Returns the 0-based index of `T` in `Variant`'s list of alternative types, +// e.g. given `Variant` == `absl::variant<A, B, C>` and `T` == `B`, returns 1. +// +// Note that this helper cannot be used if the list of alternative types +// contains duplicates. +template <typename Variant, typename T> +constexpr size_t VariantIndexOfType() { + return internal::VariantIndexOfTypeHelper<Variant, T>::Index(); +} + +} // namespace base + +#endif // BASE_TYPES_VARIANT_UTIL_H_
diff --git a/base/types/variant_util_unittest.cc b/base/types/variant_util_unittest.cc new file mode 100644 index 0000000..b421203 --- /dev/null +++ b/base/types/variant_util_unittest.cc
@@ -0,0 +1,22 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/types/variant_util.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/abseil-cpp/absl/types/variant.h" + +namespace base { +namespace { + +TEST(VariantUtilTest, IndexOfType) { + using TestType = absl::variant<bool, int, double>; + + static_assert(VariantIndexOfType<TestType, bool>() == 0); + static_assert(VariantIndexOfType<TestType, int>() == 1); + static_assert(VariantIndexOfType<TestType, double>() == 2); +} + +} // namespace +} // namespace base
diff --git a/base/types/variant_util_unittest.nc b/base/types/variant_util_unittest.nc new file mode 100644 index 0000000..aec037b --- /dev/null +++ b/base/types/variant_util_unittest.nc
@@ -0,0 +1,23 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is a "No Compile Test" suite. +// http://dev.chromium.org/developers/testing/no-compile-tests + +#include "base/types/variant_util.h" +#include "third_party/abseil-cpp/absl/types/variant.h" + +namespace base { + +#if defined(NCTEST_DUPLICATE_ALTERNATIVE_TYPES) // [r"Variant is not constructible from T"] + +inline constexpr size_t kValue = VariantIndexOfType<absl::variant<int, int>, int>(); + +#elif defined(NCTEST_NOT_AN_ALTERNATIVE_TYPE) // [r"Variant is not constructible from T"] + +inline constexpr size_t kValue = VariantIndexOfType<absl::variant<int, int>, bool>(); + +#endif + +} // namespace base
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1 index 461b3b5..acf709a 100644 --- a/build/fuchsia/linux_internal.sdk.sha1 +++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@ -12.20230309.2.1 +12.20230309.3.1
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni index cd458e1..12d0f33 100644 --- a/chrome/android/chrome_java_sources.gni +++ b/chrome/android/chrome_java_sources.gni
@@ -209,6 +209,7 @@ "java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkUtils.java", "java/src/org/chromium/chrome/browser/bookmarks/ReadingListSectionHeader.java", "java/src/org/chromium/chrome/browser/bookmarks/TabBookmarker.java", + "java/src/org/chromium/chrome/browser/bookmarks/TestingDelegate.java", "java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetProvider.java", "java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetProxy.java", "java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetServiceImpl.java",
diff --git a/chrome/android/expectations/lint-baseline.xml b/chrome/android/expectations/lint-baseline.xml index 8126df8..4202fa5 100644 --- a/chrome/android/expectations/lint-baseline.xml +++ b/chrome/android/expectations/lint-baseline.xml
@@ -1977,39 +1977,6 @@ <issue id="WrongConstant" - message="Must be one of: ViewType.INVALID, ViewType.PERSONALIZED_SIGNIN_PROMO, ViewType.PERSONALIZED_SYNC_PROMO, ViewType.SYNC_PROMO, ViewType.FOLDER, ViewType.BOOKMARK, ViewType.DIVIDER, ViewType.SECTION_HEADER, ViewType.SHOPPING_POWER_BOOKMARK, ViewType.TAG_CHIP_LIST" - errorLine1=" case ViewType.SHOPPING_FILTER:" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java" - line="254" - column="27"/> - </issue> - - <issue - id="WrongConstant" - message="Must be one of: ViewType.INVALID, ViewType.PERSONALIZED_SIGNIN_PROMO, ViewType.PERSONALIZED_SYNC_PROMO, ViewType.SYNC_PROMO, ViewType.FOLDER, ViewType.BOOKMARK, ViewType.DIVIDER, ViewType.SECTION_HEADER, ViewType.SHOPPING_POWER_BOOKMARK, ViewType.TAG_CHIP_LIST" - errorLine1=" } else if (holder.getItemViewType() == ViewType.SHOPPING_FILTER) {" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java" - line="296" - column="57"/> - </issue> - - <issue - id="WrongConstant" - message="Must be one of: ViewType.INVALID, ViewType.PERSONALIZED_SIGNIN_PROMO, ViewType.PERSONALIZED_SYNC_PROMO, ViewType.SYNC_PROMO, ViewType.FOLDER, ViewType.BOOKMARK, ViewType.DIVIDER, ViewType.SECTION_HEADER, ViewType.SHOPPING_POWER_BOOKMARK, ViewType.TAG_CHIP_LIST" - errorLine1=" ViewType.SHOPPING_FILTER, /*bookmarkItem=*/null, /*sectionHeaderData=*/null);" - errorLine2=" ~~~~~~~~~~~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkListEntry.java" - line="109" - column="26"/> - </issue> - - <issue - id="WrongConstant" message="Must be one of: CustomTabsUiType.DEFAULT, CustomTabsUiType.MEDIA_VIEWER, CustomTabsUiType.INFO_PAGE, CustomTabsUiType.READER_MODE, CustomTabsUiType.MINIMAL_UI_WEBAPP, CustomTabsUiType.OFFLINE_PAGE" errorLine1=" intent.putExtra(CustomTabIntentDataProvider.EXTRA_UI_TYPE, CustomTabsUiType.READ_LATER);" errorLine2=" ~~~~~~~~~~"> @@ -3220,83 +3187,6 @@ <issue id="WrongConstant" - message="Must be one of: ScrollDirection.LEFT, ScrollDirection.RIGHT, ScrollDirection.UP, ScrollDirection.DOWN" - errorLine1=" mScrollDirection = ScrollDirection.UNKNOWN;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java" - line="516" - column="32"/> - </issue> - - <issue - id="WrongConstant" - message="Must be one of: ScrollDirection.LEFT, ScrollDirection.RIGHT, ScrollDirection.UP, ScrollDirection.DOWN" - errorLine1=" mScrollDirection = ScrollDirection.UNKNOWN;" - errorLine2=" ~~~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java" - line="516" - column="48"/> - </issue> - - <issue - id="WrongConstant" - message="Must be one of: ScrollDirection.LEFT, ScrollDirection.RIGHT, ScrollDirection.UP, ScrollDirection.DOWN" - errorLine1=" if (mScrollDirection != ScrollDirection.UNKNOWN) {" - errorLine2=" ~~~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java" - line="531" - column="53"/> - </issue> - - <issue - id="WrongConstant" - message="Must be one of: ScrollDirection.LEFT, ScrollDirection.RIGHT, ScrollDirection.UP, ScrollDirection.DOWN" - errorLine1=" if (mScrollDirection == ScrollDirection.UNKNOWN) return;" - errorLine2=" ~~~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java" - line="537" - column="53"/> - </issue> - - <issue - id="WrongConstant" - message="Must be one of: ScrollDirection.LEFT, ScrollDirection.RIGHT, ScrollDirection.UP, ScrollDirection.DOWN" - errorLine1=" int direction = ScrollDirection.UNKNOWN;" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java" - line="578" - column="29"/> - </issue> - - <issue - id="WrongConstant" - message="Must be one of: ScrollDirection.LEFT, ScrollDirection.RIGHT, ScrollDirection.UP, ScrollDirection.DOWN" - errorLine1=" int direction = ScrollDirection.UNKNOWN;" - errorLine2=" ~~~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java" - line="578" - column="45"/> - </issue> - - <issue - id="WrongConstant" - message="Must be one of: ScrollDirection.LEFT, ScrollDirection.RIGHT, ScrollDirection.UP, ScrollDirection.DOWN" - errorLine1=" direction = ScrollDirection.LEFT;" - errorLine2=" ~~~~~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java" - line="584" - column="17"/> - </issue> - - <issue - id="WrongConstant" message="Must be one of: LensSupportStatus.LENS_SEARCH_SUPPORTED, LensSupportStatus.NON_GOOGLE_SEARCH_ENGINE, LensSupportStatus.ACTIVITY_NOT_ACCESSIBLE, LensSupportStatus.OUT_OF_DATE, LensSupportStatus.SEARCH_BY_IMAGE_UNAVAILABLE, LensSupportStatus.LEGACY_OS, LensSupportStatus.INVALID_PACKAGE, LensSupportStatus.LENS_SHOP_SUPPORTED, LensSupportStatus.LENS_SHOP_AND_SEARCH_SUPPORTED, LensSupportStatus.CAMERA_NOT_AVAILABLE, LensSupportStatus.DISABLED_ON_LOW_END_DEVICE, LensSupportStatus.AGSA_VERSION_NOT_SUPPORTED, LensSupportStatus.DISABLED_ON_INCOGNITO, LensSupportStatus.DISABLED_ON_TABLET, LensSupportStatus.DISABLED_FOR_ENTERPRISE_USER" errorLine1=" histogramName, reason, LensSupportStatus.NUM_ENTRIES);" errorLine2=" ~~~~~~~~~~~"> @@ -4144,28 +4034,6 @@ <issue id="WrongConstant" - message="Must be one of: PriceTrackingState.PRICE_TRACKING_SHOWN, PriceTrackingState.PRICE_TRACKING_ENABLED, PriceTrackingState.PRICE_TRACKING_DISABLED" - errorLine1=" PriceTrackingState.COUNT);" - errorLine2=" ~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkMetrics.java" - line="29" - column="36"/> - </issue> - - <issue - id="WrongConstant" - message="Must be one of: PriceTrackingState.PRICE_TRACKING_SHOWN, PriceTrackingState.PRICE_TRACKING_ENABLED, PriceTrackingState.PRICE_TRACKING_DISABLED" - errorLine1=" PriceTrackingState.COUNT);" - errorLine2=" ~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkMetrics.java" - line="37" - column="36"/> - </issue> - - <issue - id="WrongConstant" message="Must be one of: ExitReason.REASON_ANR, ExitReason.REASON_CRASH, ExitReason.REASON_CRASH_NATIVE, ExitReason.REASON_DEPENDENCY_DIED, ExitReason.REASON_EXCESSIVE_RESOURCE_USAGE, ExitReason.REASON_EXIT_SELF, ExitReason.REASON_INITIALIZATION_FAILURE, ExitReason.REASON_LOW_MEMORY, ExitReason.REASON_OTHER, ExitReason.REASON_PERMISSION_CHANGE, ExitReason.REASON_SIGNALED, ExitReason.REASON_UNKNOWN, ExitReason.REASON_USER_REQUESTED, ExitReason.REASON_USER_STOPPED" errorLine1=" RecordHistogram.recordEnumeratedHistogram(umaName, reason, ExitReason.NUM_ENTRIES);" errorLine2=" ~~~~~~~~~~~"> @@ -6961,50 +6829,6 @@ <issue id="NotifyDataSetChanged" message="It will always be more efficient to use more specific change events if you can. Rely on `notifyDataSetChanged` as a last resort." - errorLine1=" notifyDataSetChanged();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java" - line="190" - column="9"/> - </issue> - - <issue - id="NotifyDataSetChanged" - message="It will always be more efficient to use more specific change events if you can. Rely on `notifyDataSetChanged` as a last resort." - errorLine1=" notifyDataSetChanged();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java" - line="356" - column="9"/> - </issue> - - <issue - id="NotifyDataSetChanged" - message="It will always be more efficient to use more specific change events if you can. Rely on `notifyDataSetChanged` as a last resort." - errorLine1=" notifyDataSetChanged();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java" - line="406" - column="9"/> - </issue> - - <issue - id="NotifyDataSetChanged" - message="It will always be more efficient to use more specific change events if you can. Rely on `notifyDataSetChanged` as a last resort." - errorLine1=" if (mElements != null) notifyDataSetChanged();" - errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> - <location - file="../../chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java" - line="419" - column="32"/> - </issue> - - <issue - id="NotifyDataSetChanged" - message="It will always be more efficient to use more specific change events if you can. Rely on `notifyDataSetChanged` as a last resort." errorLine1=" notifyDataSetChanged();" errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> <location
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceAlertsMessageCardTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceAlertsMessageCardTest.java index 1650ba3..5584ae0 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceAlertsMessageCardTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/PriceAlertsMessageCardTest.java
@@ -49,7 +49,6 @@ import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CriteriaHelper; -import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.MinAndroidSdkLevel; import org.chromium.base.test.util.Restriction; @@ -318,7 +317,6 @@ @Test @MediumTest @CommandLineFlags.Add({BASE_PARAMS}) - @DisabledTest(message = "https://crbug.com/1375813") public void testDisableMessageAfterShowingTenTimes() { final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java index 92484e04..5900d65 100644 --- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java +++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
@@ -125,7 +125,6 @@ @Test @MediumTest - @DisabledTest(message = "crbug.com/1416886") public void testShowAndHideIphDialog() { final ChromeTabbedActivity cta = mActivityTestRule.getActivity(); @@ -143,7 +142,7 @@ verifyIphDialogHiding(cta); // Check the IPH message card is showing and open the IPH dialog. - onView(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed())); + onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed())); onView(allOf(withId(R.id.action_button), withParent(withId(R.id.tab_grid_message_item)))) .perform(click()); verifyIphDialogShowing(cta); @@ -151,10 +150,10 @@ // Press back should dismiss the IPH dialog. pressBack(); verifyIphDialogHiding(cta); - onView(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed())); + onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed())); // Check the IPH message card is showing and open the IPH dialog. - onView(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed())); + onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed())); onView(allOf(withId(R.id.action_button), withParent(withId(R.id.tab_grid_message_item)))) .perform(click()); verifyIphDialogShowing(cta); @@ -213,13 +212,12 @@ mActivityTestRule.startMainActivityFromLauncher(); cta = mActivityTestRule.getActivity(); enterTabSwitcher(cta); - onViewWaiting(withId(R.id.tab_grid_message_item)).check(doesNotExist()); + onView(withId(R.id.tab_grid_message_item)).check(doesNotExist()); } @Test @MediumTest @Feature({"RenderTest"}) - @DisabledTest(message = "crbug.com/1412391") public void testRenderIph_Portrait() throws IOException { ChromeTabbedActivity cta = mActivityTestRule.getActivity(); @@ -228,6 +226,7 @@ TabSwitcherCoordinator::hasAppendedMessagesForTesting); onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed())); + ChromeRenderTestRule.sanitize(cta.findViewById(R.id.tab_grid_message_item)); mRenderTestRule.render( cta.findViewById(R.id.tab_grid_message_item), "iph_entrance_portrait"); } @@ -245,8 +244,9 @@ onViewWaiting(allOf(withParent(withId(TabUiTestHelper.getTabSwitcherParentId(cta))), withId(R.id.tab_list_view))) .perform(RecyclerViewActions.scrollTo(withId(R.id.tab_grid_message_item))); - onView(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed())); + onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed())); + ChromeRenderTestRule.sanitize(cta.findViewById(R.id.tab_grid_message_item)); mRenderTestRule.render( cta.findViewById(R.id.tab_grid_message_item), "iph_entrance_landscape"); } @@ -272,6 +272,7 @@ Animatable iphAnimation = (Animatable) iphImageView.getDrawable(); CriteriaHelper.pollUiThread(() -> !iphAnimation.isRunning()); + ChromeRenderTestRule.sanitize(iphDialogView); mRenderTestRule.render(iphDialogView, "iph_dialog_portrait"); } @@ -302,12 +303,12 @@ Animatable iphAnimation = (Animatable) iphImageView.getDrawable(); CriteriaHelper.pollUiThread(() -> !iphAnimation.isRunning()); + ChromeRenderTestRule.sanitize(iphDialogView); mRenderTestRule.render(iphDialogView, "iph_dialog_landscape"); } @Test @MediumTest - @DisabledTest(message = "https://crbug.com/1412947") public void testIphItemChangeWithLastTab() { ChromeTabbedActivity cta = mActivityTestRule.getActivity(); @@ -323,7 +324,7 @@ // Undo the closure of the last tab and the IPH item should reshow. CriteriaHelper.pollInstrumentationThread(TabUiTestHelper::verifyUndoBarShowingAndClickUndo); - onView(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed())); + onViewWaiting(withId(R.id.tab_grid_message_item)).check(matches(isDisplayed())); // Close the last tab in the tab switcher. closeFirstTabInTabSwitcher(cta); @@ -383,7 +384,7 @@ private void verifyIphDialogShowing(ChromeTabbedActivity cta) { // Verify IPH dialog view. - onView(withId(R.id.iph_dialog)) + onViewWaiting(withId(R.id.iph_dialog)) .inRoot(withDecorView(not(cta.getWindow().getDecorView()))) .check((v, noMatchException) -> { if (noMatchException != null) throw noMatchException;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkDelegate.java index b5b1f6d9..cf6e887 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkDelegate.java
@@ -4,6 +4,7 @@ package org.chromium.chrome.browser.bookmarks; +import org.chromium.chrome.browser.bookmarks.BookmarkUiState.BookmarkUiMode; import org.chromium.components.bookmarks.BookmarkId; import org.chromium.components.browser_ui.widget.dragreorder.DragStateDelegate; import org.chromium.components.browser_ui.widget.selectable_list.SelectableListLayout; @@ -85,10 +86,11 @@ BookmarkModel getModel(); /** - * @return Current UiState of bookmark main UI. If no mode is stored, - * {@link BookmarkUiState#STATE_LOADING} is returned. + * Returns current mode of bookmark main UI. If no mode is stored, + * {@link BookmarkUiMode.LOADING} is returned. */ - int getCurrentState(); + @BookmarkUiMode + int getCurrentUiMode(); /** * @return LargeIconBridge instance. By sharing the instance, we can also share the cache.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java index edaf009..ce9ad5c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java
@@ -10,6 +10,7 @@ import android.util.AttributeSet; import org.chromium.chrome.R; +import org.chromium.chrome.browser.bookmarks.BookmarkUiState.BookmarkUiMode; import org.chromium.chrome.browser.ui.favicon.FaviconUtils; import org.chromium.components.bookmarks.BookmarkId; import org.chromium.components.bookmarks.BookmarkItem; @@ -56,11 +57,11 @@ @Override public void onClick() { - switch (mDelegate.getCurrentState()) { - case BookmarkUiState.STATE_FOLDER: - case BookmarkUiState.STATE_SEARCHING: + switch (mDelegate.getCurrentUiMode()) { + case BookmarkUiMode.FOLDER: + case BookmarkUiMode.SEARCHING: break; - case BookmarkUiState.STATE_LOADING: + case BookmarkUiMode.LOADING: assert false : "The main content shouldn't be inflated if it's still loading"; break;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java index da4e93b..0668d0e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemsAdapter.java
@@ -5,33 +5,17 @@ package org.chromium.chrome.browser.bookmarks; import android.content.Context; -import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; +import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.ViewHolder; -import org.chromium.base.metrics.RecordUserAction; -import org.chromium.chrome.R; import org.chromium.chrome.browser.bookmarks.BookmarkListEntry.ViewType; -import org.chromium.chrome.browser.bookmarks.BookmarkRow.Location; -import org.chromium.chrome.browser.feature_engagement.TrackerFactory; -import org.chromium.chrome.browser.flags.ChromeFeatureList; -import org.chromium.chrome.browser.profiles.Profile; -import org.chromium.chrome.browser.sync.SyncService; -import org.chromium.chrome.browser.sync.SyncService.SyncStateChangedListener; -import org.chromium.chrome.browser.ui.signin.SyncPromoController.SyncPromoState; -import org.chromium.components.bookmarks.BookmarkId; -import org.chromium.components.bookmarks.BookmarkItem; -import org.chromium.components.bookmarks.BookmarkType; import org.chromium.components.browser_ui.widget.dragreorder.DragReorderableListAdapter; -import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate.SelectionObserver; -import org.chromium.components.feature_engagement.EventConstants; -import org.chromium.components.power_bookmarks.PowerBookmarkMeta; import org.chromium.ui.modelutil.PropertyModel; import java.util.ArrayList; @@ -41,9 +25,6 @@ * BaseAdapter for {@link RecyclerView}. It manages bookmarks to list there. */ public class BookmarkItemsAdapter extends DragReorderableListAdapter<BookmarkListEntry> { - private static final int MAXIMUM_NUMBER_OF_SEARCH_RESULTS = 500; - private static final String EMPTY_QUERY = null; - /** Abstraction around how to build type specific {@link View}s. */ interface ViewFactory { /** @@ -59,230 +40,53 @@ void bindView(View view, @ViewType int viewType, PropertyModel model); } - private final List<BookmarkId> mTopLevelFolders = new ArrayList<>(); - private final Profile mProfile; + /** + * Temporary interface to provide functionality that needs dependencies until this adapter is + * replaced. See https://crbug.com/1413463. + */ + interface ViewDelegate { + PropertyModel buildModel(ViewHolder holder, int position); + void recycleView(View view, @ViewType int viewType); + void setOrder(List<BookmarkListEntry> listEntries); + boolean isReorderable(BookmarkListEntry entry); + } + private final ViewFactory mViewFactory; private final ViewBinder mViewBinder; - private final SyncService mSyncService; - private final BookmarkPromoHeader mPromoHeaderManager; + private @Nullable BookmarkDelegate mBookmarkDelegate; + private @Nullable ViewDelegate mViewDelegate; - // There can only be one promo header at a time. This takes on one of the values: - // ViewType.PERSONALIZED_SIGNIN_PROMO, ViewType.SYNC_PROMO, or ViewType.INVALID. - @ViewType - private int mPromoHeaderType = ViewType.INVALID; - private BookmarkDelegate mDelegate; - private String mSearchText; - private BookmarkId mCurrentFolder; - - // Keep track of the currently highlighted bookmark - used for "show in folder" action. - private BookmarkId mHighlightedBookmark; - - private BookmarkModelObserver mBookmarkModelObserver = new BookmarkModelObserver() { - @Override - public void bookmarkNodeChanged(BookmarkItem node) { - assert mDelegate != null; - clearHighlight(); - int position = getPositionForBookmark(node.getId()); - if (position >= 0) notifyItemChanged(position); - } - - @Override - public void bookmarkNodeRemoved(BookmarkItem parent, int oldIndex, BookmarkItem node, - boolean isDoingExtensiveChanges) { - assert mDelegate != null; - clearHighlight(); - - if (mDelegate.getCurrentState() == BookmarkUiState.STATE_SEARCHING) { - // We cannot rely on removing the specific list item that corresponds to the - // removed node because the node might be a parent with children also shown - // in the list. - search(mSearchText); - return; - } - - if (node.isFolder()) { - mDelegate.notifyStateChange(mBookmarkUiObserver); - } else { - int deletedPosition = getPositionForBookmark(node.getId()); - if (deletedPosition >= 0) { - removeItem(deletedPosition); - } - } - } - - @Override - public void bookmarkModelChanged() { - assert mDelegate != null; - clearHighlight(); - mDelegate.notifyStateChange(mBookmarkUiObserver); - - if (mDelegate.getCurrentState() == BookmarkUiState.STATE_SEARCHING) { - if (!TextUtils.equals(mSearchText, EMPTY_QUERY)) { - search(mSearchText); - } else { - mDelegate.closeSearchUi(); - } - } - } - }; - - private final BookmarkUiObserver mBookmarkUiObserver = new BookmarkUiObserver() { - @Override - public void onDestroy() { - mDelegate.removeUiObserver(mBookmarkUiObserver); - mDelegate.getModel().removeObserver(mBookmarkModelObserver); - mDelegate.getSelectionDelegate().removeObserver(mSelectionObserver); - mDelegate = null; - mPromoHeaderManager.destroy(); - mSyncService.removeSyncStateChangedListener(mSyncStateChangedListener); - } - - @Override - public void onFolderStateSet(BookmarkId folder) { - assert mDelegate != null; - clearHighlight(); - - mSearchText = EMPTY_QUERY; - mCurrentFolder = folder; - enableDrag(); - - if (topLevelFoldersShowing()) { - setBookmarks(mTopLevelFolders); - } else { - setBookmarks(mDelegate.getModel().getChildIDs(folder)); - } - - if (BookmarkId.SHOPPING_FOLDER.equals(folder)) { - mDelegate.getSelectableListLayout().setEmptyViewText( - R.string.tracked_products_empty_list_title); - } else if (folder.getType() == BookmarkType.READING_LIST) { - TrackerFactory.getTrackerForProfile(mProfile).notifyEvent( - EventConstants.READ_LATER_BOOKMARK_FOLDER_OPENED); - mDelegate.getSelectableListLayout().setEmptyViewText( - R.string.reading_list_empty_list_title); - } else { - mDelegate.getSelectableListLayout().setEmptyViewText( - R.string.bookmarks_folder_empty); - } - } - - @Override - public void onSearchStateSet() { - clearHighlight(); - disableDrag(); - // Headers should not appear in Search mode - // Don't need to notify because we need to redraw everything in the next step - updateHeader(false); - removeSectionHeaders(); - notifyDataSetChanged(); - } - }; - - private final SelectionObserver<BookmarkId> mSelectionObserver = new SelectionObserver<>() { - @Override - public void onSelectionStateChange(List<BookmarkId> selectedBookmarks) { - clearHighlight(); - } - }; - - private final SyncStateChangedListener mSyncStateChangedListener = - new SyncStateChangedListener() { - @Override - public void syncStateChanged() { - // If mDelegate is null, we will set the top level folders upon its - // initialization (see onBookmarkDelegateInitialized method above). - if (mDelegate == null) { - return; - } - mTopLevelFolders.clear(); - populateTopLevelFoldersList(); - } - }; - - BookmarkItemsAdapter( - Context context, Profile profile, ViewFactory viewFactory, ViewBinder viewBinder) { + BookmarkItemsAdapter(Context context, ViewFactory viewFactory, ViewBinder viewBinder) { super(context); - mProfile = profile; mViewFactory = viewFactory; mViewBinder = viewBinder; - - mSyncService = SyncService.get(); - mSyncService.addSyncStateChangedListener(mSyncStateChangedListener); - - Runnable promoHeaderChangeAction = () -> { - // Notify the view of changes to the elements list as the promo might be showing. - updateHeader(true); - }; - mPromoHeaderManager = new BookmarkPromoHeader(mContext, mProfile, promoHeaderChangeAction); } /** - * @return The position of the given bookmark in adapter. Will return -1 if not found. + * Sets the delegate to use to handle UI actions related to this adapter. + * @param bookmarkDelegate A {@link ViewDelegate} instance to backend interactions. + * @param viewDelegate A {@link ViewDelegate} instance to handle model interactions. */ - int getPositionForBookmark(BookmarkId bookmark) { - assert bookmark != null; - int position = -1; - for (int i = 0; i < getItemCount(); i++) { - if (bookmark.equals(getIdByPosition(i))) { - position = i; - break; - } - } - return position; - } - - private void filterForPriceTrackingCategory(List<BookmarkId> bookmarks) { - for (int i = bookmarks.size() - 1; i >= 0; i--) { - PowerBookmarkMeta meta = mDelegate.getModel().getPowerBookmarkMeta(bookmarks.get(i)); - if (meta == null || !meta.hasShoppingSpecifics() - || !meta.getShoppingSpecifics().getIsPriceTracked()) { - bookmarks.remove(i); - continue; - } - } - } - - private void setBookmarks(List<BookmarkId> bookmarks) { - clearHighlight(); - mElements.clear(); - - // Restore the header, if it exists, then update it. - if (hasPromoHeader()) { - mElements.add(BookmarkListEntry.createSyncPromoHeader(mPromoHeaderType)); - } - - updateHeader(false); - if (BookmarkId.SHOPPING_FOLDER.equals(mCurrentFolder)) { - filterForPriceTrackingCategory(bookmarks); - } - - for (BookmarkId bookmarkId : bookmarks) { - BookmarkItem item = mDelegate.getModel().getBookmarkById(bookmarkId); - - mElements.add(BookmarkListEntry.createBookmarkEntry( - item, mDelegate.getModel().getPowerBookmarkMeta(bookmarkId))); - } - - if (mCurrentFolder.getType() == BookmarkType.READING_LIST - && mDelegate.getCurrentState() != BookmarkUiState.STATE_SEARCHING) { - ReadingListSectionHeader.maybeSortAndInsertSectionHeaders(mElements, mContext); - } - - if (ChromeFeatureList.isEnabled(ChromeFeatureList.SHOPPING_LIST) - && topLevelFoldersShowing()) { - mElements.add(BookmarkListEntry.createDivider()); - mElements.add(BookmarkListEntry.createShoppingFilter()); - } - + @SuppressWarnings("NotifyDataSetChanged") + void onBookmarkDelegateInitialized( + BookmarkDelegate bookmarkDelegate, ViewDelegate viewDelegate) { + mElements = new ArrayList<>(); + mBookmarkDelegate = bookmarkDelegate; + mViewDelegate = viewDelegate; + setDragStateDelegate(bookmarkDelegate.getDragStateDelegate()); notifyDataSetChanged(); } - private void removeItem(int position) { - mElements.remove(position); - notifyItemRemoved(position); + List<BookmarkListEntry> getElements() { + return mElements; + } + + ItemTouchHelper getItemTouchHelper() { + return mItemTouchHelper; } // DragReorderableListAdapter implementation. + @Override public @ViewType int getItemViewType(int position) { BookmarkListEntry entry = getItemByPosition(position); @@ -296,294 +100,30 @@ @Override public void onBindViewHolder(ViewHolder holder, int position) { - PropertyModel model = new PropertyModel(BookmarkManagerProperties.ALL_KEYS); - @ViewType - int viewType = holder.getItemViewType(); - - if (holder.getItemViewType() == ViewType.PERSONALIZED_SIGNIN_PROMO - || holder.getItemViewType() == ViewType.PERSONALIZED_SYNC_PROMO) { - model.set(BookmarkManagerProperties.BOOKMARK_PROMO_HEADER, mPromoHeaderManager); - } else if (holder.getItemViewType() == ViewType.SECTION_HEADER) { - model.set(BookmarkManagerProperties.BOOKMARK_LIST_ENTRY, getItemByPosition(position)); - } else if (BookmarkListEntry.isBookmarkEntry(holder.getItemViewType())) { - BookmarkId id = getIdByPosition(position); - model.set(BookmarkManagerProperties.BOOKMARK_ID, id); - model.set(BookmarkManagerProperties.LOCATION, getLocationFromPosition(position)); - model.set(BookmarkManagerProperties.IS_FROM_FILTER_VIEW, - BookmarkId.SHOPPING_FOLDER.equals(mCurrentFolder)); - model.set(BookmarkManagerProperties.ITEM_TOUCH_HELPER, mItemTouchHelper); - model.set(BookmarkManagerProperties.VIEW_HOLDER, holder); - model.set(BookmarkManagerProperties.IS_HIGHLIGHTED, id.equals(mHighlightedBookmark)); - model.set(BookmarkManagerProperties.CLEAR_HIGHLIGHT, this::clearHighlight); - } else if (holder.getItemViewType() == ViewType.SHOPPING_FILTER) { - model.set(BookmarkManagerProperties.OPEN_FOLDER, mDelegate::openFolder); - } - - mViewBinder.bindView(holder.itemView, viewType, model); + PropertyModel propertyModel = mViewDelegate.buildModel(holder, position); + mViewBinder.bindView(holder.itemView, holder.getItemViewType(), propertyModel); } @Override public void onViewRecycled(ViewHolder holder) { - switch (holder.getItemViewType()) { - case ViewType.PERSONALIZED_SIGNIN_PROMO: - // fall through - case ViewType.PERSONALIZED_SYNC_PROMO: - mPromoHeaderManager.detachPersonalizePromoView(); - break; - default: - // Other view holders don't have special recycling code. - } - } - - /** - * Sets the delegate to use to handle UI actions related to this adapter. - * - * @param delegate A {@link BookmarkDelegate} instance to handle all backend interaction. - */ - void onBookmarkDelegateInitialized(BookmarkDelegate delegate) { - mDelegate = delegate; - mDelegate.addUiObserver(mBookmarkUiObserver); - mDelegate.getModel().addObserver(mBookmarkModelObserver); - mDelegate.getSelectionDelegate().addObserver(mSelectionObserver); - - populateTopLevelFoldersList(); - - mElements = new ArrayList<>(); - setDragStateDelegate(delegate.getDragStateDelegate()); - notifyDataSetChanged(); - } - - /** - * Refresh the list of bookmarks within the currently visible folder. - */ - public void refresh() { - // Tell the RecyclerView to update its elements. - if (mElements != null) notifyDataSetChanged(); - } - - /** - * Synchronously searches for the given query. - * - * @param query The query text to search for. - */ - public void search(@Nullable String query) { - mSearchText = query == null ? "" : query.trim(); - - List<BookmarkId> bookmarks = - mDelegate.getModel().searchBookmarks(mSearchText, MAXIMUM_NUMBER_OF_SEARCH_RESULTS); - setBookmarks(bookmarks); - } - - /** - * See {@link BookmarkDelegate#moveUpOne(BookmarkId)}. - */ - void moveUpOne(BookmarkId bookmarkId) { - int pos = getPositionForBookmark(bookmarkId); - assert isReorderable(getItemByPosition(pos)); - mElements.remove(pos); - mElements.add(pos - 1, - BookmarkListEntry.createBookmarkEntry( - mDelegate.getModel().getBookmarkById(bookmarkId), - mDelegate.getModel().getPowerBookmarkMeta(bookmarkId))); - setOrder(mElements); - } - - /** - * See {@link BookmarkDelegate#moveDownOne(BookmarkId)}. - */ - void moveDownOne(BookmarkId bookmarkId) { - int pos = getPositionForBookmark(bookmarkId); - assert isReorderable(getItemByPosition(pos)); - mElements.remove(pos); - mElements.add(pos + 1, - BookmarkListEntry.createBookmarkEntry( - mDelegate.getModel().getBookmarkById(bookmarkId), - mDelegate.getModel().getPowerBookmarkMeta(bookmarkId))); - setOrder(mElements); - } - - /** - * Updates mPromoHeaderType. Makes sure that the 0th index of mElements is consistent with the - * promo header. This 0th index is null iff there is a promo header. - * - * @param shouldNotify True iff we should notify the RecyclerView of changes to the promoheader. - * (This should be false iff we are going to make further changes to the - * list of elements, as we do in setBookmarks, and true iff we are only - * changing the header, as we do in the promoHeaderChangeAction runnable). - */ - private void updateHeader(boolean shouldNotify) { - if (mDelegate == null) return; - - boolean wasShowingPromo = hasPromoHeader(); - - int currentUiState = mDelegate.getCurrentState(); - if (currentUiState == BookmarkUiState.STATE_LOADING) { - return; - } else if (currentUiState == BookmarkUiState.STATE_SEARCHING) { - mPromoHeaderType = ViewType.INVALID; - } else { - switch (mPromoHeaderManager.getPromoState()) { - case SyncPromoState.NO_PROMO: - mPromoHeaderType = ViewType.INVALID; - break; - case SyncPromoState.PROMO_FOR_SIGNED_OUT_STATE: - mPromoHeaderType = ViewType.PERSONALIZED_SIGNIN_PROMO; - break; - case SyncPromoState.PROMO_FOR_SIGNED_IN_STATE: - mPromoHeaderType = ViewType.PERSONALIZED_SYNC_PROMO; - break; - case SyncPromoState.PROMO_FOR_SYNC_TURNED_OFF_STATE: - mPromoHeaderType = ViewType.SYNC_PROMO; - break; - default: - assert false : "Unexpected value for promo state!"; - } - } - - boolean willShowPromo = hasPromoHeader(); - - if (!wasShowingPromo && willShowPromo) { - // A null element at the 0th index represents a promo header. - mElements.add(0, BookmarkListEntry.createSyncPromoHeader(mPromoHeaderType)); - if (shouldNotify) notifyItemInserted(0); - } else if (wasShowingPromo && willShowPromo) { - if (shouldNotify) notifyItemChanged(0); - } else if (wasShowingPromo && !willShowPromo) { - mElements.remove(0); - if (shouldNotify) notifyItemRemoved(0); - } - } - - /** Removes all section headers from the current list. */ - private void removeSectionHeaders() { - for (int i = mElements.size() - 1; i >= 0; i--) { - if (mElements.get(i).getViewType() == ViewType.SECTION_HEADER) { - mElements.remove(i); - } - } - } - - private void populateTopLevelFoldersList() { - mTopLevelFolders.addAll(BookmarkUtils.populateTopLevelFolders(mDelegate.getModel())); + mViewDelegate.recycleView(holder.itemView, holder.getItemViewType()); } @Override protected void setOrder(List<BookmarkListEntry> listEntries) { - assert !topLevelFoldersShowing() : "Cannot reorder top-level folders!"; - assert mCurrentFolder.getType() - != BookmarkType.PARTNER : "Cannot reorder partner bookmarks!"; - assert mDelegate.getCurrentState() - == BookmarkUiState.STATE_FOLDER : "Can only reorder items from folder mode!"; - - int startIndex = getBookmarkItemStartIndex(); - int endIndex = getBookmarkItemEndIndex(); - - // Get the new order for the IDs. - long[] newOrder = new long[endIndex - startIndex + 1]; - for (int i = startIndex; i <= endIndex; i++) { - BookmarkItem bookmarkItem = listEntries.get(i).getBookmarkItem(); - assert bookmarkItem != null; - newOrder[i - startIndex] = bookmarkItem.getId().getId(); - } - mDelegate.getModel().reorderBookmarks(mCurrentFolder, newOrder); - if (mDragStateDelegate.getDragActive()) { - RecordUserAction.record("MobileBookmarkManagerDragReorder"); - } - } - - private int getBookmarkItemStartIndex() { - return hasPromoHeader() ? 1 : 0; - } - - private int getBookmarkItemEndIndex() { - int endIndex = mElements.size() - 1; - BookmarkItem bookmarkItem = mElements.get(endIndex).getBookmarkItem(); - if (bookmarkItem == null || !BookmarkUtils.isMovable(bookmarkItem)) { - endIndex--; - } - return endIndex; - } - - private boolean isReorderable(BookmarkListEntry entry) { - return entry != null && entry.getBookmarkItem() != null - && entry.getBookmarkItem().isReorderable(); + mViewDelegate.setOrder(listEntries); } // DragReorderableListAdapter implementation. + @Override - @VisibleForTesting public boolean isActivelyDraggable(ViewHolder viewHolder) { return isPassivelyDraggable(viewHolder) && ((BookmarkRow) viewHolder.itemView).isItemSelected(); } @Override - @VisibleForTesting public boolean isPassivelyDraggable(ViewHolder viewHolder) { - return isReorderable(getItemByHolder(viewHolder)); - } - - public BookmarkId getIdByPosition(int position) { - BookmarkListEntry entry = getItemByPosition(position); - if (entry == null || entry.getBookmarkItem() == null) return null; - return entry.getBookmarkItem().getId(); - } - - public BookmarkPromoHeader getPromoHeaderManager() { - return mPromoHeaderManager; - } - - private boolean hasPromoHeader() { - return mPromoHeaderType != ViewType.INVALID; - } - - private @Location int getLocationFromPosition(int position) { - if (position == getBookmarkItemStartIndex() && position == getBookmarkItemEndIndex()) { - return Location.SOLO; - } else if (position == getBookmarkItemStartIndex()) { - return Location.TOP; - } else if (position == getBookmarkItemEndIndex()) { - return Location.BOTTOM; - } else { - return Location.MIDDLE; - } - } - - /** - * @return True iff the currently-open folder is the root folder - * (which is true iff the top-level folders are showing) - */ - private boolean topLevelFoldersShowing() { - return mCurrentFolder.equals(mDelegate.getModel().getRootFolderId()); - } - - /** - * Scroll the bookmarks list such that bookmarkId is shown in the view, and highlight it. - * - * @param bookmarkId The BookmarkId of the bookmark of interest - */ - void highlightBookmark(BookmarkId bookmarkId) { - assert mHighlightedBookmark == null : "There should not already be a highlighted bookmark!"; - - mRecyclerView.scrollToPosition(getPositionForBookmark(bookmarkId)); - mHighlightedBookmark = bookmarkId; - } - - /** - * Clears the highlighted bookmark, if there is one. - */ - private void clearHighlight() { - mHighlightedBookmark = null; - } - - @VisibleForTesting - BookmarkDelegate getDelegateForTesting() { - return mDelegate; - } - - @VisibleForTesting - public void simulateSignInForTesting() { - mSyncStateChangedListener.syncStateChanged(); - mBookmarkUiObserver.onFolderStateSet(mCurrentFolder); + return mViewDelegate.isReorderable(getItemByHolder(viewHolder)); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java index 44cda55..c8c5abf 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java
@@ -13,7 +13,6 @@ import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; import androidx.recyclerview.widget.RecyclerView; import org.chromium.base.ContextUtils; @@ -58,7 +57,6 @@ private final BookmarkOpener mBookmarkOpener; private final BookmarkToolbarCoordinator mBookmarkToolbarCoordinator; private final BookmarkManagerMediator mMediator; - private final BookmarkUndoController mUndoController; private final ImageFetcher mImageFetcher; private final SnackbarManager mSnackbarManager; private final BookmarkPromoHeader mPromoHeaderManager; @@ -107,10 +105,9 @@ mMainView.findViewById(R.id.selectable_list); mSelectableListLayout = selectableList; mSelectableListLayout.initializeEmptyView(R.string.bookmarks_folder_empty); - BookmarkItemsAdapter bookmarkItemsAdapter = - new BookmarkItemsAdapter(context, profile, this::createView, this::bindView); - mPromoHeaderManager = bookmarkItemsAdapter.getPromoHeaderManager(); + BookmarkItemsAdapter bookmarkItemsAdapter = + new BookmarkItemsAdapter(context, this::createView, this::bindView); mRecyclerView = mSelectableListLayout.initializeRecyclerView( (RecyclerView.Adapter<RecyclerView.ViewHolder>) bookmarkItemsAdapter); @@ -122,13 +119,16 @@ bookmarkDelegateSupplier, mBookmarkModel, mBookmarkOpener); mSelectableListLayout.configureWideDisplayStyle(); - LargeIconBridge largeIconBridge = new LargeIconBridge(profile); + LargeIconBridge largeIconBridge = new LargeIconBridge(mProfile); largeIconBridge.createCache(computeCacheMaxSize()); - mUndoController = new BookmarkUndoController(context, mBookmarkModel, snackbarManager); + BookmarkUndoController bookmarkUndoController = + new BookmarkUndoController(context, mBookmarkModel, snackbarManager); mMediator = new BookmarkManagerMediator(context, mBookmarkModel, mBookmarkOpener, mSelectableListLayout, selectionDelegate, mRecyclerView, bookmarkItemsAdapter, - largeIconBridge, isDialogUi, isIncognito, mBackPressStateSupplier); + largeIconBridge, isDialogUi, isIncognito, mBackPressStateSupplier, mProfile, + bookmarkUndoController); + mPromoHeaderManager = mMediator.getPromoHeaderManager(); bookmarkDelegateSupplier.set(/*bookmarkDelegate=*/mMediator); @@ -204,7 +204,7 @@ @Override public void onSearchTextChanged(String query) { - mMediator.onSearchTextChanged(query); + mMediator.search(query); } @Override @@ -346,17 +346,14 @@ // Testing methods. - @VisibleForTesting public BookmarkToolbar getToolbarForTesting() { return mBookmarkToolbarCoordinator.getToolbarForTesting(); // IN-TEST } - @VisibleForTesting public BookmarkUndoController getUndoControllerForTesting() { - return mUndoController; + return mMediator.getUndoControllerForTesting(); } - @VisibleForTesting public RecyclerView getRecyclerViewForTesting() { return mRecyclerView; } @@ -372,4 +369,8 @@ public BookmarkDelegate getBookmarkDelegateForTesting() { return mMediator; } + + public TestingDelegate getTestingDelegate() { + return mMediator; + } } \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java index aa9cbcc..1c8e19c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java
@@ -6,40 +6,60 @@ import android.content.Context; import android.text.TextUtils; +import android.view.View; import android.view.accessibility.AccessibilityManager; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver; +import androidx.recyclerview.widget.RecyclerView.ViewHolder; import org.chromium.base.ObserverList; import org.chromium.base.metrics.RecordUserAction; import org.chromium.base.supplier.ObservableSupplierImpl; import org.chromium.chrome.R; +import org.chromium.chrome.browser.bookmarks.BookmarkListEntry.ViewType; +import org.chromium.chrome.browser.bookmarks.BookmarkRow.Location; +import org.chromium.chrome.browser.bookmarks.BookmarkUiState.BookmarkUiMode; +import org.chromium.chrome.browser.feature_engagement.TrackerFactory; +import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmarksReader; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.sync.SyncService; +import org.chromium.chrome.browser.sync.SyncService.SyncStateChangedListener; import org.chromium.chrome.browser.ui.native_page.BasicNativePage; +import org.chromium.chrome.browser.ui.signin.SyncPromoController.SyncPromoState; import org.chromium.components.bookmarks.BookmarkId; import org.chromium.components.bookmarks.BookmarkItem; import org.chromium.components.bookmarks.BookmarkType; import org.chromium.components.browser_ui.widget.dragreorder.DragStateDelegate; import org.chromium.components.browser_ui.widget.selectable_list.SelectableListLayout; import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate; +import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate.SelectionObserver; import org.chromium.components.favicon.LargeIconBridge; +import org.chromium.components.feature_engagement.EventConstants; +import org.chromium.ui.modelutil.PropertyModel; import org.chromium.url.GURL; +import java.util.ArrayList; import java.util.List; import java.util.Stack; /** Responsible for BookmarkManager business logic. */ // TODO(crbug.com/1416611): Remove BookmarkDelegate if possible. -class BookmarkManagerMediator - implements BookmarkDelegate, PartnerBookmarksReader.FaviconUpdateObserver { +class BookmarkManagerMediator implements BookmarkDelegate, TestingDelegate, + PartnerBookmarksReader.FaviconUpdateObserver, + BookmarkItemsAdapter.ViewDelegate { + private static final int MAXIMUM_NUMBER_OF_SEARCH_RESULTS = 500; + private static final String EMPTY_QUERY = null; + private static boolean sPreventLoadingForTesting; /** * Keeps track of whether drag is enabled / active for bookmark lists. */ - class BookmarkDragStateDelegate implements DragStateDelegate { + private class BookmarkDragStateDelegate implements DragStateDelegate { private BookmarkDelegate mBookmarkDelegate; private SelectionDelegate<BookmarkId> mSelectionDelegate; private AccessibilityManager mA11yManager; @@ -62,8 +82,7 @@ // DragStateDelegate implementation @Override public boolean getDragEnabled() { - return !mA11yEnabled - && mBookmarkDelegate.getCurrentState() == BookmarkUiState.STATE_FOLDER; + return !mA11yEnabled && mBookmarkDelegate.getCurrentUiMode() == BookmarkUiMode.FOLDER; } @Override @@ -86,7 +105,7 @@ private final BookmarkModelObserver mBookmarkModelObserver = new BookmarkModelObserver() { @Override public void bookmarkNodeChildrenReordered(BookmarkItem node) { - mBookmarkItemsAdapter.refresh(); + refresh(); } @Override @@ -95,7 +114,7 @@ boolean isDoingExtensiveChanges) { // If the folder is removed in folder mode, show the parent folder or falls back to all // bookmarks mode. - if (getCurrentState() == BookmarkUiState.STATE_FOLDER + if (getCurrentUiMode() == BookmarkUiMode.FOLDER && node.getId().equals(mStateStack.peek().mFolder)) { if (mBookmarkModel.getTopLevelFolderIDs(true, true).contains(node.getId())) { openFolder(mBookmarkModel.getDefaultFolderViewLocation()); @@ -113,7 +132,7 @@ @Override public void bookmarkNodeChanged(BookmarkItem node) { - if (getCurrentState() == BookmarkUiState.STATE_FOLDER && !mStateStack.isEmpty() + if (getCurrentUiMode() == BookmarkUiMode.FOLDER && !mStateStack.isEmpty() && node.getId().equals(mStateStack.peek().mFolder)) { notifyUi(mStateStack.peek()); return; @@ -125,7 +144,7 @@ public void bookmarkModelChanged() { // If the folder no longer exists in folder mode, we need to fall back. Relying on the // default behavior by setting the folder mode again. - if (getCurrentState() == BookmarkUiState.STATE_FOLDER) { + if (getCurrentUiMode() == BookmarkUiMode.FOLDER) { setState(mStateStack.peek()); } } @@ -163,6 +182,123 @@ } }; + // TODO(https://crbug.com/1413463): Combine with mBookmarkModelObserver. + private BookmarkModelObserver mBookmarkModelObserver2 = new BookmarkModelObserver() { + @Override + public void bookmarkNodeChanged(BookmarkItem node) { + clearHighlight(); + int position = getPositionForBookmark(node.getId()); + if (position >= 0) mBookmarkItemsAdapter.notifyItemChanged(position); + } + + @Override + public void bookmarkNodeRemoved(BookmarkItem parent, int oldIndex, BookmarkItem node, + boolean isDoingExtensiveChanges) { + clearHighlight(); + + if (getCurrentUiMode() == BookmarkUiMode.SEARCHING) { + // We cannot rely on removing the specific list item that corresponds to the + // removed node because the node might be a parent with children also shown + // in the list. + search(mSearchText); + return; + } + + if (node.isFolder()) { + notifyStateChange(mBookmarkUiObserver); + } else { + int deletedPosition = getPositionForBookmark(node.getId()); + if (deletedPosition >= 0) { + removeItem(deletedPosition); + } + } + } + + @Override + public void bookmarkModelChanged() { + clearHighlight(); + notifyStateChange(mBookmarkUiObserver); + + if (getCurrentUiMode() == BookmarkUiMode.SEARCHING) { + if (!TextUtils.equals(mSearchText, EMPTY_QUERY)) { + search(mSearchText); + } else { + closeSearchUi(); + } + } + } + }; + + private final BookmarkUiObserver mBookmarkUiObserver = new BookmarkUiObserver() { + @Override + public void onDestroy() { + removeUiObserver(mBookmarkUiObserver); + mBookmarkModel.removeObserver(mBookmarkModelObserver2); + getSelectionDelegate().removeObserver(mSelectionObserver); + mPromoHeaderManager.destroy(); + mSyncService.removeSyncStateChangedListener(mSyncStateChangedListener); + } + + @Override + public void onFolderStateSet(BookmarkId folder) { + clearHighlight(); + + mSearchText = EMPTY_QUERY; + mCurrentFolder = folder; + mBookmarkItemsAdapter.enableDrag(); + + if (topLevelFoldersShowing()) { + setBookmarks(mTopLevelFolders); + } else { + setBookmarks(mBookmarkModel.getChildIDs(folder)); + } + + if (BookmarkId.SHOPPING_FOLDER.equals(folder)) { + getSelectableListLayout().setEmptyViewText( + R.string.tracked_products_empty_list_title); + } else if (folder.getType() == BookmarkType.READING_LIST) { + TrackerFactory.getTrackerForProfile(mProfile).notifyEvent( + EventConstants.READ_LATER_BOOKMARK_FOLDER_OPENED); + getSelectableListLayout().setEmptyViewText(R.string.reading_list_empty_list_title); + } else { + getSelectableListLayout().setEmptyViewText(R.string.bookmarks_folder_empty); + } + } + + @SuppressWarnings("NotifyDataSetChanged") + @Override + public void onSearchStateSet() { + clearHighlight(); + mBookmarkItemsAdapter.disableDrag(); + // Headers should not appear in Search mode + // Don't need to notify because we need to redraw everything in the next step + updateHeader(false); + removeSectionHeaders(); + mBookmarkItemsAdapter.notifyDataSetChanged(); + } + }; + + private final SelectionObserver<BookmarkId> mSelectionObserver = new SelectionObserver<>() { + @Override + public void onSelectionStateChange(List<BookmarkId> selectedBookmarks) { + clearHighlight(); + } + }; + + private final SyncStateChangedListener mSyncStateChangedListener = + new SyncStateChangedListener() { + @Override + public void syncStateChanged() { + // If mDelegate is null, we will set the top level folders upon its + // initialization (see onBookmarkDelegateInitialized method above). + // if (mDelegate == null) { + // return; + //} + mTopLevelFolders.clear(); + populateTopLevelFoldersList(); + } + }; + private final ObserverList<BookmarkUiObserver> mUiObservers = new ObserverList<>(); private final BookmarkDragStateDelegate mDragStateDelegate = new BookmarkDragStateDelegate(); private final Context mContext; @@ -178,23 +314,36 @@ // TODO(crbug.com/1416611): Remove reference to BookmarkItemsAdapter. private final BookmarkItemsAdapter mBookmarkItemsAdapter; private final LargeIconBridge mLargeIconBridge; - /** Whether we're showing in a dialog UI which is only true for phones. */ + // Whether we're showing in a dialog UI which is only true for phones. private final boolean mIsDialogUi; private final boolean mIsIncognito; private final ObservableSupplierImpl<Boolean> mBackPressStateSupplier; + private final List<BookmarkId> mTopLevelFolders = new ArrayList<>(); + private final Profile mProfile; + private final SyncService mSyncService; + private final BookmarkPromoHeader mPromoHeaderManager; + private final BookmarkUndoController mBookmarkUndoController; - /** Whether this instance has been destroyed. */ + // Whether this instance has been destroyed. private boolean mIsDestroyed; private String mInitialUrl; private boolean mFaviconsNeedRefresh; private BasicNativePage mNativePage; + // There can only be one promo header at a time. This takes on one of the values: + // ViewType.PERSONALIZED_SIGNIN_PROMO, ViewType.SYNC_PROMO, or ViewType.INVALID. + private @ViewType int mPromoHeaderType = ViewType.INVALID; + private String mSearchText; + private BookmarkId mCurrentFolder; + // Keep track of the currently highlighted bookmark - used for "show in folder" action. + private BookmarkId mHighlightedBookmark; BookmarkManagerMediator(Context context, BookmarkModel bookmarkModel, BookmarkOpener bookmarkOpener, SelectableListLayout<BookmarkId> selectableListLayout, SelectionDelegate<BookmarkId> selectionDelegate, RecyclerView recyclerView, BookmarkItemsAdapter bookmarkItemsAdapter, LargeIconBridge largeIconBridge, boolean isDialogUi, boolean isIncognito, - ObservableSupplierImpl<Boolean> backPressStateSupplier) { + ObservableSupplierImpl<Boolean> backPressStateSupplier, Profile profile, + BookmarkUndoController bookmarkUndoController) { mContext = context; mBookmarkModel = bookmarkModel; mBookmarkModel.addObserver(mBookmarkModelObserver); @@ -210,6 +359,13 @@ mIsDialogUi = isDialogUi; mIsIncognito = isIncognito; mBackPressStateSupplier = backPressStateSupplier; + mProfile = profile; + mSyncService = SyncService.get(); + mSyncService.addSyncStateChangedListener(mSyncStateChangedListener); + // Notify the view of changes to the elements list as the promo might be showing. + Runnable promoHeaderChangeAction = () -> updateHeader(true); + mPromoHeaderManager = new BookmarkPromoHeader(mContext, mProfile, promoHeaderChangeAction); + mBookmarkUndoController = bookmarkUndoController; // Previously we were waiting for BookmarkModel to be loaded, but it's not necessary. PartnerBookmarksReader.addFaviconUpdateObserver(this); @@ -222,7 +378,16 @@ void onBookmarkModelLoaded() { mDragStateDelegate.onBookmarkDelegateInitialized(this); - mBookmarkItemsAdapter.onBookmarkDelegateInitialized(this); + + // TODO(https://crbug.com/1413463): This logic is here to keep the same execution order + // from when it was in the original adapter. It doesn't coceptaully make senes to be here, + // and should happen earlier. + addUiObserver(mBookmarkUiObserver); + mBookmarkModel.addObserver(mBookmarkModelObserver2); + mSelectionDelegate.addObserver(mSelectionObserver); + populateTopLevelFoldersList(); + + mBookmarkItemsAdapter.onBookmarkDelegateInitialized(this, this); if (!TextUtils.isEmpty(mInitialUrl)) { setState(BookmarkUiState.createStateFromUrl(mInitialUrl, mBookmarkModel)); @@ -237,17 +402,21 @@ mLargeIconBridge.destroy(); PartnerBookmarksReader.removeFaviconUpdateObserver(this); + mBookmarkUndoController.destroy(); + for (BookmarkUiObserver observer : mUiObservers) { observer.onDestroy(); } assert mUiObservers.size() == 0; } - /** See BookmarkManager(Coordinator)#onBackPressed. */ + /** + * See BookmarkManager(Coordinator)#onBackPressed. + */ boolean onBackPressed() { if (mIsDestroyed) return false; - // TODO(twellington): replicate this behavior for other list UIs during unification. + // TODO(twellington): Replicate this behavior for other list UIs during unification. if (mSelectableListLayout.onBackPressed()) { return true; } @@ -262,20 +431,23 @@ return false; } - /** See BookmarkManager(Coordinator)#setBasicNativePage. */ + /** + * See BookmarkManager(Coordinator)#setBasicNativePage. + */ void setBasicNativePage(BasicNativePage nativePage) { mNativePage = nativePage; } - /** See BookmarkManager(Coordinator)#updateForUrl */ + /** + * See BookmarkManager(Coordinator)#updateForUrl + */ void updateForUrl(String url) { // Bookmark model is null if the manager has been destroyed. if (mBookmarkModel == null) return; if (mBookmarkModel.isBookmarkModelLoaded()) { BookmarkUiState searchState = null; - if (!mStateStack.isEmpty() - && mStateStack.peek().mState == BookmarkUiState.STATE_SEARCHING) { + if (!mStateStack.isEmpty() && mStateStack.peek().mUiMode == BookmarkUiMode.SEARCHING) { searchState = mStateStack.pop(); } @@ -287,91 +459,138 @@ } } - /** See BookmarkManager(Coordinator)#getCurrentUrl. */ - String getCurrentUrl() { - if (mStateStack.isEmpty()) return null; - return mStateStack.peek().mUrl; + BookmarkPromoHeader getPromoHeaderManager() { + return mPromoHeaderManager; + } + + BookmarkId getIdByPosition(int position) { + BookmarkListEntry entry = getItemByPosition(position); + if (entry == null || entry.getBookmarkItem() == null) return null; + return entry.getBookmarkItem().getId(); } /** - * Puts all UI elements to loading state. This state might be overridden synchronously by - * {@link #updateForUrl(String)}, if the bookmark model is already loaded. + * Synchronously searches for the given query. + * @param query The query text to search for. */ - private void initializeToLoadingState() { - assert mStateStack.isEmpty(); - setState(BookmarkUiState.createLoadingState()); + void search(@Nullable String query) { + mSearchText = query == null ? "" : query.trim(); + List<BookmarkId> bookmarks = + mBookmarkModel.searchBookmarks(mSearchText, MAXIMUM_NUMBER_OF_SEARCH_RESULTS); + setBookmarks(bookmarks); } - /** - * This is the ultimate internal method that updates UI and controls backstack. And it is the - * only method that pushes states to {@link #mStateStack}. - * - * <p>If the given state is not valid, all_bookmark state will be shown. Afterwards, this method - * checks the current state: if currently in loading state, it pops it out and adds the new - * state to the back stack. It also notifies the {@link #mNativePage} (if any) that the - * url has changed. - * - * <p>Also note that even if we store states to {@link #mStateStack}, on tablet the back - * navigation and back button are not controlled by the manager: the tab handles back key and - * backstack navigation. - */ - private void setState(BookmarkUiState state) { - if (!state.isValid(mBookmarkModel)) { - state = BookmarkUiState.createFolderState( - mBookmarkModel.getDefaultFolderViewLocation(), mBookmarkModel); - } + // BookmarkItemsAdapter.ViewDelegate implementation. - if (!mStateStack.isEmpty() && mStateStack.peek().equals(state)) return; - - // The loading state is not persisted in history stack and once we have a valid state it - // shall be removed. - if (!mStateStack.isEmpty() && mStateStack.peek().mState == BookmarkUiState.STATE_LOADING) { - mStateStack.pop(); + @Override + public PropertyModel buildModel(ViewHolder holder, int position) { + PropertyModel model = new PropertyModel(BookmarkManagerProperties.ALL_KEYS); + final @ViewType int viewType = holder.getItemViewType(); + if (viewType == ViewType.PERSONALIZED_SIGNIN_PROMO + || viewType == ViewType.PERSONALIZED_SYNC_PROMO) { + model.set(BookmarkManagerProperties.BOOKMARK_PROMO_HEADER, mPromoHeaderManager); + } else if (viewType == ViewType.SECTION_HEADER) { + model.set(BookmarkManagerProperties.BOOKMARK_LIST_ENTRY, getItemByPosition(position)); + } else if (BookmarkListEntry.isBookmarkEntry(viewType)) { + BookmarkId id = getIdByPosition(position); + model.set(BookmarkManagerProperties.BOOKMARK_ID, id); + model.set(BookmarkManagerProperties.LOCATION, getLocationFromPosition(position)); + model.set(BookmarkManagerProperties.IS_FROM_FILTER_VIEW, + BookmarkId.SHOPPING_FOLDER.equals(mCurrentFolder)); + model.set(BookmarkManagerProperties.ITEM_TOUCH_HELPER, + mBookmarkItemsAdapter.getItemTouchHelper()); + model.set(BookmarkManagerProperties.VIEW_HOLDER, holder); + model.set(BookmarkManagerProperties.IS_HIGHLIGHTED, id.equals(mHighlightedBookmark)); + model.set(BookmarkManagerProperties.CLEAR_HIGHLIGHT, this::clearHighlight); + } else if (viewType == ViewType.SHOPPING_FILTER) { + model.set(BookmarkManagerProperties.OPEN_FOLDER, this::openFolder); } - mStateStack.push(state); - notifyUi(state); + return model; } - private void notifyUi(BookmarkUiState state) { - if (state.mState == BookmarkUiState.STATE_FOLDER) { - // Loading and searching states may be pushed to the stack but should never be stored in - // preferences. - BookmarkUtils.setLastUsedUrl(mContext, state.mUrl); - // If a loading state is replaced by another loading state, do not notify this change. - if (mNativePage != null) { - mNativePage.onStateChange(state.mUrl, false); - } - } - - for (BookmarkUiObserver observer : mUiObservers) { - notifyStateChange(observer); + @Override + public void recycleView(View view, @ViewType int viewType) { + switch (viewType) { + case ViewType.PERSONALIZED_SIGNIN_PROMO: + // fall through + case ViewType.PERSONALIZED_SYNC_PROMO: + mPromoHeaderManager.detachPersonalizePromoView(); + break; + default: + // Other view holders don't have special recycling code. } } - // TODO(lazzzis): This method can be moved to adapter after bookmark reordering launches. - /** - * Some bookmarks may be moved to another folder or removed in another devices. However, it may - * still be stored by {@link #mSelectionDelegate}, which causes incorrect selection counting. - */ - private void syncAdapterAndSelectionDelegate() { - for (BookmarkId node : mSelectionDelegate.getSelectedItemsAsList()) { - if (mSelectionDelegate.isItemSelected(node) - && mBookmarkItemsAdapter.getPositionForBookmark(node) == -1) { - mSelectionDelegate.toggleSelectionForItem(node); - } + @Override + public void setOrder(List<BookmarkListEntry> listEntries) { + assert !topLevelFoldersShowing() : "Cannot reorder top-level folders!"; + assert mCurrentFolder.getType() + != BookmarkType.PARTNER : "Cannot reorder partner bookmarks!"; + assert getCurrentUiMode() + == BookmarkUiMode.FOLDER : "Can only reorder items from folder mode!"; + + int startIndex = getBookmarkItemStartIndex(); + int endIndex = getBookmarkItemEndIndex(); + + // Get the new order for the IDs. + long[] newOrder = new long[endIndex - startIndex + 1]; + for (int i = startIndex; i <= endIndex; i++) { + BookmarkItem bookmarkItem = listEntries.get(i).getBookmarkItem(); + assert bookmarkItem != null; + newOrder[i - startIndex] = bookmarkItem.getId().getId(); + } + mBookmarkModel.reorderBookmarks(mCurrentFolder, newOrder); + if (mDragStateDelegate.getDragActive()) { + RecordUserAction.record("MobileBookmarkManagerDragReorder"); } } - // BookmarkDelegate implementation + @Override + public boolean isReorderable(BookmarkListEntry entry) { + return entry != null && entry.getBookmarkItem() != null + && entry.getBookmarkItem().isReorderable(); + } + + // TestingDelegate implementation. + + @Override + public BookmarkId getIdByPositionForTesting(int position) { + return getIdByPosition(position); + } + + @Override + public void searchForTesting(@Nullable String query) { + search(query); + } + + @Override + public void simulateSignInForTesting() { + mSyncStateChangedListener.syncStateChanged(); + mBookmarkUiObserver.onFolderStateSet(mCurrentFolder); + } + + // BookmarkDelegate implementation. @Override public void moveDownOne(BookmarkId bookmarkId) { - mBookmarkItemsAdapter.moveDownOne(bookmarkId); + int pos = getPositionForBookmark(bookmarkId); + assert isReorderable(getItemByPosition(pos)); + getElements().remove(pos); + getElements().add(pos + 1, + BookmarkListEntry.createBookmarkEntry(mBookmarkModel.getBookmarkById(bookmarkId), + mBookmarkModel.getPowerBookmarkMeta(bookmarkId))); + setOrder(getElements()); } @Override public void moveUpOne(BookmarkId bookmarkId) { - mBookmarkItemsAdapter.moveUpOne(bookmarkId); + int pos = getPositionForBookmark(bookmarkId); + assert isReorderable(getItemByPosition(pos)); + getElements().remove(pos); + getElements().add(pos - 1, + BookmarkListEntry.createBookmarkEntry(mBookmarkModel.getBookmarkById(bookmarkId), + mBookmarkModel.getPowerBookmarkMeta(bookmarkId))); + setOrder(getElements()); } @Override @@ -401,15 +620,15 @@ @Override public void notifyStateChange(BookmarkUiObserver observer) { - int state = getCurrentState(); - observer.onStateChanged(state); + int state = getCurrentUiMode(); + observer.onUiModeChanged(state); switch (state) { - case BookmarkUiState.STATE_FOLDER: + case BookmarkUiMode.FOLDER: observer.onFolderStateSet(mStateStack.peek().mFolder); break; - case BookmarkUiState.STATE_LOADING: + case BookmarkUiMode.LOADING: break; - case BookmarkUiState.STATE_SEARCHING: + case BookmarkUiMode.SEARCHING: observer.onSearchStateSet(); break; default: @@ -463,9 +682,9 @@ } @Override - public int getCurrentState() { - if (mStateStack.isEmpty()) return BookmarkUiState.STATE_LOADING; - return mStateStack.peek().mState; + public @BookmarkUiMode int getCurrentUiMode() { + if (mStateStack.isEmpty()) return BookmarkUiMode.LOADING; + return mStateStack.peek().mUiMode; } @Override @@ -480,16 +699,15 @@ @Override public void highlightBookmark(BookmarkId bookmarkId) { - mBookmarkItemsAdapter.highlightBookmark(bookmarkId); + assert mHighlightedBookmark == null : "There should not already be a highlighted bookmark!"; + + mRecyclerView.scrollToPosition(getPositionForBookmark(bookmarkId)); + mHighlightedBookmark = bookmarkId; } // SearchDelegate implementation. // Actual interface implemented in BookmarkManager(Coordinator). - void onSearchTextChanged(String query) { - mBookmarkItemsAdapter.search(query); - } - void onEndSearch() { mSelectableListLayout.onEndSearch(); @@ -515,13 +733,81 @@ public void onCompletedFaviconLoading() { assert mBookmarkModel.isBookmarkModelLoaded(); if (mFaviconsNeedRefresh) { - mBookmarkItemsAdapter.refresh(); + refresh(); mFaviconsNeedRefresh = false; } } // Private methods. + /** + * Puts all UI elements to loading state. This state might be overridden synchronously by + * {@link #updateForUrl(String)}, if the bookmark model is already loaded. + */ + private void initializeToLoadingState() { + assert mStateStack.isEmpty(); + setState(BookmarkUiState.createLoadingState()); + } + + /** + * This is the ultimate internal method that updates UI and controls backstack. And it is the + * only method that pushes states to {@link #mStateStack}. + * + * <p>If the given state is not valid, all_bookmark state will be shown. Afterwards, this method + * checks the current state: if currently in loading state, it pops it out and adds the new + * state to the back stack. It also notifies the {@link #mNativePage} (if any) that the + * url has changed. + * + * <p>Also note that even if we store states to {@link #mStateStack}, on tablet the back + * navigation and back button are not controlled by the manager: the tab handles back key and + * backstack navigation. + */ + private void setState(BookmarkUiState state) { + if (!state.isValid(mBookmarkModel)) { + state = BookmarkUiState.createFolderState( + mBookmarkModel.getDefaultFolderViewLocation(), mBookmarkModel); + } + + if (!mStateStack.isEmpty() && mStateStack.peek().equals(state)) return; + + // The loading state is not persisted in history stack and once we have a valid state it + // shall be removed. + if (!mStateStack.isEmpty() && mStateStack.peek().mUiMode == BookmarkUiMode.LOADING) { + mStateStack.pop(); + } + mStateStack.push(state); + notifyUi(state); + } + + private void notifyUi(BookmarkUiState state) { + if (state.mUiMode == BookmarkUiMode.FOLDER) { + // Loading and searching states may be pushed to the stack but should never be stored in + // preferences. + BookmarkUtils.setLastUsedUrl(mContext, state.mUrl); + // If a loading state is replaced by another loading state, do not notify this change. + if (mNativePage != null) { + mNativePage.onStateChange(state.mUrl, false); + } + } + + for (BookmarkUiObserver observer : mUiObservers) { + notifyStateChange(observer); + } + } + + // TODO(lazzzis): This method can be moved to adapter after bookmark reordering launches. + /** + * Some bookmarks may be moved to another folder or removed in another devices. However, it may + * still be stored by {@link #mSelectionDelegate}, which causes incorrect selection counting. + */ + private void syncAdapterAndSelectionDelegate() { + for (BookmarkId node : mSelectionDelegate.getSelectedItemsAsList()) { + if (mSelectionDelegate.isItemSelected(node) && getPositionForBookmark(node) == -1) { + mSelectionDelegate.toggleSelectionForItem(node); + } + } + } + private void onBackPressStateChanged() { if (mIsDestroyed) { mBackPressStateSupplier.set(false); @@ -532,8 +818,196 @@ || mStateStack.size() > 1); } - // Testing methods. + /** @return The position of the given bookmark in adapter. Will return -1 if not found. */ + private int getPositionForBookmark(BookmarkId bookmark) { + assert bookmark != null; + int position = -1; + for (int i = 0; i < getItemCount(); i++) { + if (bookmark.equals(getIdByPosition(i))) { + position = i; + break; + } + } + return position; + } + private void filterForPriceTrackingCategory(List<BookmarkId> bookmarks) { + for (int i = bookmarks.size() - 1; i >= 0; i--) { + org.chromium.components.power_bookmarks.PowerBookmarkMeta meta = + mBookmarkModel.getPowerBookmarkMeta(bookmarks.get(i)); + if (meta == null || !meta.hasShoppingSpecifics() + || !meta.getShoppingSpecifics().getIsPriceTracked()) { + bookmarks.remove(i); + continue; + } + } + } + + @SuppressWarnings("NotifyDataSetChanged") + private void setBookmarks(List<BookmarkId> bookmarks) { + clearHighlight(); + getElements().clear(); + + // Restore the header, if it exists, then update it. + if (hasPromoHeader()) { + getElements().add(BookmarkListEntry.createSyncPromoHeader(mPromoHeaderType)); + } + + updateHeader(false); + if (BookmarkId.SHOPPING_FOLDER.equals(mCurrentFolder)) { + filterForPriceTrackingCategory(bookmarks); + } + + for (BookmarkId bookmarkId : bookmarks) { + BookmarkItem item = mBookmarkModel.getBookmarkById(bookmarkId); + + getElements().add(BookmarkListEntry.createBookmarkEntry( + item, mBookmarkModel.getPowerBookmarkMeta(bookmarkId))); + } + + if (mCurrentFolder.getType() == BookmarkType.READING_LIST + && getCurrentUiMode() != BookmarkUiMode.SEARCHING) { + ReadingListSectionHeader.maybeSortAndInsertSectionHeaders(getElements(), mContext); + } + + if (ChromeFeatureList.isEnabled(ChromeFeatureList.SHOPPING_LIST) + && topLevelFoldersShowing()) { + getElements().add(BookmarkListEntry.createDivider()); + getElements().add(BookmarkListEntry.createShoppingFilter()); + } + + mBookmarkItemsAdapter.notifyDataSetChanged(); + } + + private void removeItem(int position) { + getElements().remove(position); + mBookmarkItemsAdapter.notifyItemRemoved(position); + } + + /** Refresh the list of bookmarks within the currently visible folder. */ + @SuppressWarnings("NotifyDataSetChanged") + private void refresh() { + // Tell the RecyclerView to update its elements. + if (getElements() != null) mBookmarkItemsAdapter.notifyDataSetChanged(); + } + + /** + * Updates mPromoHeaderType. Makes sure that the 0th index of getElements() is consistent with + * the promo header. This 0th index is null iff there is a promo header. + * + * @param shouldNotify True iff we should notify the RecyclerView of changes to the promoheader. + * (This should be false iff we are going to make further changes to the + * list of elements, as we do in setBookmarks, and true iff we are only + * changing the header, as we do in the promoHeaderChangeAction runnable). + */ + private void updateHeader(boolean shouldNotify) { + boolean wasShowingPromo = hasPromoHeader(); + + int currentUiState = getCurrentUiMode(); + if (currentUiState == BookmarkUiMode.LOADING) { + return; + } else if (currentUiState == BookmarkUiMode.SEARCHING) { + mPromoHeaderType = ViewType.INVALID; + } else { + switch (mPromoHeaderManager.getPromoState()) { + case SyncPromoState.NO_PROMO: + mPromoHeaderType = ViewType.INVALID; + break; + case SyncPromoState.PROMO_FOR_SIGNED_OUT_STATE: + mPromoHeaderType = ViewType.PERSONALIZED_SIGNIN_PROMO; + break; + case SyncPromoState.PROMO_FOR_SIGNED_IN_STATE: + mPromoHeaderType = ViewType.PERSONALIZED_SYNC_PROMO; + break; + case SyncPromoState.PROMO_FOR_SYNC_TURNED_OFF_STATE: + mPromoHeaderType = ViewType.SYNC_PROMO; + break; + default: + assert false : "Unexpected value for promo state!"; + } + } + + boolean willShowPromo = hasPromoHeader(); + if (!wasShowingPromo && willShowPromo) { + // A null element at the 0th index represents a promo header. + getElements().add(0, BookmarkListEntry.createSyncPromoHeader(mPromoHeaderType)); + if (shouldNotify) mBookmarkItemsAdapter.notifyItemInserted(0); + } else if (wasShowingPromo && willShowPromo) { + if (shouldNotify) mBookmarkItemsAdapter.notifyItemChanged(0); + } else if (wasShowingPromo && !willShowPromo) { + getElements().remove(0); + if (shouldNotify) mBookmarkItemsAdapter.notifyItemRemoved(0); + } + } + + /** Removes all section headers from the current list. */ + private void removeSectionHeaders() { + for (int i = getElements().size() - 1; i >= 0; i--) { + if (getElements().get(i).getViewType() == ViewType.SECTION_HEADER) { + getElements().remove(i); + } + } + } + + private void populateTopLevelFoldersList() { + mTopLevelFolders.addAll(BookmarkUtils.populateTopLevelFolders(mBookmarkModel)); + } + + private int getBookmarkItemStartIndex() { + return hasPromoHeader() ? 1 : 0; + } + + private int getBookmarkItemEndIndex() { + int endIndex = getElements().size() - 1; + BookmarkItem bookmarkItem = getElements().get(endIndex).getBookmarkItem(); + if (bookmarkItem == null || !BookmarkUtils.isMovable(bookmarkItem)) { + endIndex--; + } + return endIndex; + } + + private boolean hasPromoHeader() { + return mPromoHeaderType != ViewType.INVALID; + } + + private @Location int getLocationFromPosition(int position) { + if (position == getBookmarkItemStartIndex() && position == getBookmarkItemEndIndex()) { + return Location.SOLO; + } else if (position == getBookmarkItemStartIndex()) { + return Location.TOP; + } else if (position == getBookmarkItemEndIndex()) { + return Location.BOTTOM; + } else { + return Location.MIDDLE; + } + } + + /** + * Return true iff the currently-open folder is the root folder + * (which is true iff the top-level folders are showing) + */ + private boolean topLevelFoldersShowing() { + return mCurrentFolder.equals(mBookmarkModel.getRootFolderId()); + } + + /** Clears the highlighted bookmark, if there is one. */ + private void clearHighlight() { + mHighlightedBookmark = null; + } + + private BookmarkListEntry getItemByPosition(int position) { + return getElements().get(position); + } + + private List<BookmarkListEntry> getElements() { + return mBookmarkItemsAdapter.getElements(); + } + + private int getItemCount() { + return getElements().size(); + } + + // Testing methods. /** Whether to prevent the bookmark model from fully loading for testing. */ static void preventLoadingForTesting(boolean preventLoading) { sPreventLoadingForTesting = preventLoading; @@ -542,4 +1016,8 @@ void clearStateStackForTesting() { mStateStack.clear(); } + + BookmarkUndoController getUndoControllerForTesting() { + return mBookmarkUndoController; + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java index 84d7ecb..f58ffc35 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkRow.java
@@ -21,6 +21,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.app.bookmarks.BookmarkAddEditFolderActivity; import org.chromium.chrome.browser.app.bookmarks.BookmarkFolderSelectActivity; +import org.chromium.chrome.browser.bookmarks.BookmarkUiState.BookmarkUiMode; import org.chromium.components.bookmarks.BookmarkId; import org.chromium.components.bookmarks.BookmarkItem; import org.chromium.components.bookmarks.BookmarkType; @@ -190,9 +191,9 @@ listItems.add(buildMenuListItem(R.string.bookmark_item_delete, 0, 0)); } - if (mDelegate.getCurrentState() == BookmarkUiState.STATE_SEARCHING) { + if (mDelegate.getCurrentUiMode() == BookmarkUiMode.SEARCHING) { listItems.add(buildMenuListItem(R.string.bookmark_show_in_folder, 0, 0)); - } else if (mDelegate.getCurrentState() == BookmarkUiState.STATE_FOLDER + } else if (mDelegate.getCurrentUiMode() == BookmarkUiMode.FOLDER && mLocation != Location.SOLO && canReorder) { // Only add move up / move down buttons if there is more than 1 item if (mLocation != Location.TOP) { @@ -316,7 +317,6 @@ @Override public void onSearchStateSet() {} - @VisibleForTesting public boolean isItemSelected() { return mDelegate.getSelectionDelegate().isItemSelected(mBookmarkId); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbar.java index d23b07a..04e5047 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbar.java
@@ -18,6 +18,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.app.bookmarks.BookmarkAddEditFolderActivity; import org.chromium.chrome.browser.app.bookmarks.BookmarkFolderSelectActivity; +import org.chromium.chrome.browser.bookmarks.BookmarkUiState.BookmarkUiMode; import org.chromium.chrome.browser.incognito.IncognitoUtils; import org.chromium.components.bookmarks.BookmarkId; import org.chromium.components.bookmarks.BookmarkItem; @@ -42,7 +43,7 @@ // TODO(crbug.com/1413463): Remove BookmarkId reference. private BookmarkId mCurrentFolderId; private BookmarkItem mCurrentFolder; - private int mBookmarkUiState; + private @BookmarkUiMode int mBookmarkUiMode; private Runnable mOpenSearchUiRunnable; private Callback<BookmarkId> mOpenFolderCallback; @@ -82,21 +83,21 @@ getMenu().setGroupEnabled(R.id.selection_mode_menu_group, true); } - void setBookmarkUiState(int state) { - mBookmarkUiState = state; - if (mBookmarkUiState == BookmarkUiState.STATE_LOADING) { + void setBookmarkUiMode(@BookmarkUiMode int mode) { + mBookmarkUiMode = mode; + if (mBookmarkUiMode == BookmarkUiMode.LOADING) { showLoadingUi(); } else { showNormalView(); } - if (state == BookmarkUiState.STATE_SEARCHING) { + if (mBookmarkUiMode == BookmarkUiMode.SEARCHING) { showSearchView(/*showKeyboard=*/true); } else { hideSearchView(/*notify=*/false); } - if (mBookmarkUiState == BookmarkUiState.STATE_FOLDER && mCurrentFolder != null) { + if (mBookmarkUiMode == BookmarkUiMode.FOLDER && mCurrentFolder != null) { // It's possible that the folder was renamed, so refresh the folder UI just in case. setCurrentFolder(mCurrentFolder.getId()); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarCoordinator.java index 1dc8c4f..d76f89a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarCoordinator.java
@@ -6,6 +6,7 @@ import org.chromium.base.supplier.OneshotSupplier; import org.chromium.chrome.R; +import org.chromium.chrome.browser.bookmarks.BookmarkUiState.BookmarkUiMode; import org.chromium.components.bookmarks.BookmarkId; import org.chromium.components.browser_ui.widget.selectable_list.SelectableListLayout; import org.chromium.components.browser_ui.widget.selectable_list.SelectableListToolbar.SearchDelegate; @@ -34,7 +35,7 @@ mModel.set(BookmarkToolbarProperties.BOOKMARK_MODEL, bookmarkModel); mModel.set(BookmarkToolbarProperties.BOOKMARK_OPENER, bookmarkOpener); mModel.set(BookmarkToolbarProperties.SELECTION_DELEGATE, selectionDelegate); - mModel.set(BookmarkToolbarProperties.BOOKMARK_UI_STATE, BookmarkUiState.STATE_LOADING); + mModel.set(BookmarkToolbarProperties.BOOKMARK_UI_STATE, BookmarkUiMode.LOADING); mModel.set(BookmarkToolbarProperties.IS_DIALOG_UI, isDialogUi); mModel.set(BookmarkToolbarProperties.DRAG_ENABLED, false); mMediator =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediator.java index 2fca096..15ec9003 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediator.java
@@ -49,8 +49,8 @@ } @Override - public void onStateChanged(int state) { - mModel.set(BookmarkToolbarProperties.BOOKMARK_UI_STATE, state); + public void onUiModeChanged(int mode) { + mModel.set(BookmarkToolbarProperties.BOOKMARK_UI_STATE, mode); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinder.java index 7a08e70..cc3128dd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinder.java
@@ -22,7 +22,7 @@ bookmarkToolbar.setSelectionDelegate( model.get(BookmarkToolbarProperties.SELECTION_DELEGATE)); } else if (key == BookmarkToolbarProperties.BOOKMARK_UI_STATE) { - bookmarkToolbar.setBookmarkUiState( + bookmarkToolbar.setBookmarkUiMode( model.get(BookmarkToolbarProperties.BOOKMARK_UI_STATE)); } else if (key == BookmarkToolbarProperties.SOFT_KEYBOARD_VISIBLE) { bookmarkToolbar.setSoftKeyboardVisible(Boolean.TRUE.equals(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUiObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUiObserver.java index 27661a49..cf99836 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUiObserver.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUiObserver.java
@@ -4,6 +4,7 @@ package org.chromium.chrome.browser.bookmarks; +import org.chromium.chrome.browser.bookmarks.BookmarkUiState.BookmarkUiMode; import org.chromium.components.bookmarks.BookmarkId; /** @@ -18,12 +19,12 @@ /** @see BookmarkDelegate#openFolder(BookmarkId) */ default void onFolderStateSet(BookmarkId folder) {} - /** Called when the UI state is set to {@link BookmarkUiState#STATE_SEARCHING}. */ + /** Called when the UI state is set to {@link BookmarkUiMode.SEARCHING}. */ default void onSearchStateSet() {} /** Called when a bookmark menu item is opened. */ default void onBookmarkItemMenuOpened() {} - /** Called when the bookmark UI state changes. */ - default void onStateChanged(int state) {} + /** Called when the bookmark UI mode changes. */ + default void onUiModeChanged(@BookmarkUiMode int mode) {} }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUiState.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUiState.java index 7a06a14..be783f8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUiState.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUiState.java
@@ -7,62 +7,64 @@ import android.net.Uri; import android.text.TextUtils; +import androidx.annotation.IntDef; + import org.chromium.components.bookmarks.BookmarkId; import org.chromium.components.embedder_support.util.UrlConstants; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * A class representing the UI state of the {@link BookmarkManager}. All * states can be uniquely identified by a URL. */ public class BookmarkUiState { - // TODO(crbug.com/1419494): Use an intdef here instead. - public static final int STATE_LOADING = 1; - public static final int STATE_FOLDER = 2; - public static final int STATE_SEARCHING = 3; - private static final int STATE_INVALID = 0; + @IntDef({BookmarkUiMode.INVALID, BookmarkUiMode.LOADING, BookmarkUiMode.FOLDER, + BookmarkUiMode.SEARCHING}) + @Retention(RetentionPolicy.SOURCE) + public @interface BookmarkUiMode { + int INVALID = 0; + int LOADING = 1; + int FOLDER = 2; + int SEARCHING = 3; + } + private static final String SHOPPING_FILTER_URL = UrlConstants.BOOKMARKS_FOLDER_URL + "/shopping"; - /** - * One of the STATE_* constants. - */ - int mState; - String mUrl; - BookmarkId mFolder; + final @BookmarkUiMode int mUiMode; + final String mUrl; + final BookmarkId mFolder; static BookmarkUiState createLoadingState() { - BookmarkUiState state = new BookmarkUiState(); - state.mState = STATE_LOADING; - state.mUrl = ""; - return state; + return new BookmarkUiState(BookmarkUiMode.LOADING, "", null); } static BookmarkUiState createSearchState() { - BookmarkUiState state = new BookmarkUiState(); - state.mState = STATE_SEARCHING; - state.mUrl = ""; - return state; + return new BookmarkUiState(BookmarkUiMode.SEARCHING, "", null); } static BookmarkUiState createShoppingFilterState() { - BookmarkUiState state = new BookmarkUiState(); - state.mState = STATE_FOLDER; - state.mUrl = SHOPPING_FILTER_URL; - state.mFolder = BookmarkId.SHOPPING_FOLDER; - return state; + return new BookmarkUiState( + BookmarkUiMode.FOLDER, SHOPPING_FILTER_URL, BookmarkId.SHOPPING_FOLDER); } static BookmarkUiState createFolderState(BookmarkId folder, BookmarkModel bookmarkModel) { - if (BookmarkId.SHOPPING_FOLDER.equals(folder)) return createShoppingFilterState(); - return createStateFromUrl(createFolderUrl(folder), bookmarkModel); + if (BookmarkId.SHOPPING_FOLDER.equals(folder)) { + return createShoppingFilterState(); + } else { + return createStateFromUrl(createFolderUrl(folder), bookmarkModel); + } } - /** - * @see #createStateFromUrl(Uri, BookmarkModel) - */ + /** @see #createStateFromUrl(Uri, BookmarkModel). */ static BookmarkUiState createStateFromUrl(String url, BookmarkModel bookmarkModel) { - if (SHOPPING_FILTER_URL.equals(url)) return createShoppingFilterState(); - return createStateFromUrl(Uri.parse(url), bookmarkModel); + if (SHOPPING_FILTER_URL.equals(url)) { + return createShoppingFilterState(); + } else { + return createStateFromUrl(Uri.parse(url), bookmarkModel); + } } /** @@ -70,25 +72,24 @@ * return all_bookmarks. */ static BookmarkUiState createStateFromUrl(Uri uri, BookmarkModel bookmarkModel) { - BookmarkUiState state = new BookmarkUiState(); - state.mState = STATE_INVALID; - state.mUrl = uri.toString(); + String url = uri.toString(); - if (state.mUrl.equals(UrlConstants.BOOKMARKS_URL)) { + BookmarkUiState tempState = null; + if (url.equals(UrlConstants.BOOKMARKS_URL)) { return createFolderState(bookmarkModel.getDefaultFolderViewLocation(), bookmarkModel); - } else if (state.mUrl.startsWith(UrlConstants.BOOKMARKS_FOLDER_URL)) { + } else if (url.startsWith(UrlConstants.BOOKMARKS_FOLDER_URL)) { String path = uri.getLastPathSegment(); if (!path.isEmpty()) { - state.mFolder = BookmarkId.getBookmarkIdFromString(path); - state.mState = STATE_FOLDER; + tempState = new BookmarkUiState( + BookmarkUiMode.FOLDER, url, BookmarkId.getBookmarkIdFromString(path)); } } - if (!state.isValid(bookmarkModel)) { - state = createFolderState(bookmarkModel.getDefaultFolderViewLocation(), bookmarkModel); + if (tempState != null && tempState.isValid(bookmarkModel)) { + return tempState; + } else { + return createFolderState(bookmarkModel.getDefaultFolderViewLocation(), bookmarkModel); } - - return state; } public static Uri createFolderUrl(BookmarkId folderId) { @@ -99,28 +100,32 @@ return builder.build(); } - private BookmarkUiState() {} + private BookmarkUiState(@BookmarkUiMode int uiMode, String url, BookmarkId folder) { + mUiMode = uiMode; + mUrl = url; + mFolder = folder; + } @Override public int hashCode() { - return 31 * mUrl.hashCode() + mState; + return 31 * mUrl.hashCode() + mUiMode; } @Override public boolean equals(Object obj) { if (!(obj instanceof BookmarkUiState)) return false; BookmarkUiState other = (BookmarkUiState) obj; - return mState == other.mState && TextUtils.equals(mUrl, other.mUrl); + return mUiMode == other.mUiMode && TextUtils.equals(mUrl, other.mUrl); } /** * @return Whether this state is valid. */ boolean isValid(BookmarkModel bookmarkModel) { - if (mUrl == null || mState == STATE_INVALID) return false; + if (mUrl == null || mUiMode == BookmarkUiMode.INVALID) return false; if (mUrl.equals(SHOPPING_FILTER_URL)) return true; - if (mState == STATE_FOLDER) { + if (mUiMode == BookmarkUiMode.FOLDER) { return mFolder != null && bookmarkModel.doesBookmarkExist(mFolder); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUndoController.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUndoController.java index 31fce5e..88a0759 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUndoController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUndoController.java
@@ -124,7 +124,7 @@ // Adding a new bookmark should not affect undo. } - // BookmarkDeleteObserver implementation + // BookmarkDeleteObserver implementation. @Override public void onDeleteBookmarks(String[] titles, boolean isUndoable) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkMetrics.java index 2e82107..c66c7d0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkMetrics.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/PowerBookmarkMetrics.java
@@ -14,7 +14,7 @@ // numeric values should never be reused. Keep up-to-date with the PriceTrackingState enum in // tools/metrics/histograms/enums.xml. @IntDef({PriceTrackingState.PRICE_TRACKING_SHOWN, PriceTrackingState.PRICE_TRACKING_ENABLED, - PriceTrackingState.PRICE_TRACKING_DISABLED}) + PriceTrackingState.PRICE_TRACKING_DISABLED, PriceTrackingState.COUNT}) public @interface PriceTrackingState { int PRICE_TRACKING_SHOWN = 0; int PRICE_TRACKING_ENABLED = 1;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/TestingDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/TestingDelegate.java new file mode 100644 index 0000000..0726a3f --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/TestingDelegate.java
@@ -0,0 +1,18 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.bookmarks; + +import androidx.annotation.Nullable; + +import org.chromium.components.bookmarks.BookmarkId; + +/** Exposes business logic methods to the various bookmark integration. */ +public interface TestingDelegate { + BookmarkId getIdByPositionForTesting(int position); + + void searchForTesting(@Nullable String query); + + void simulateSignInForTesting(); +} \ No newline at end of file
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java index bf78788b..b083016 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/BookmarkTest.java
@@ -73,8 +73,10 @@ import org.chromium.chrome.browser.bookmarks.BookmarkRow; import org.chromium.chrome.browser.bookmarks.BookmarkToolbar; import org.chromium.chrome.browser.bookmarks.BookmarkUiState; +import org.chromium.chrome.browser.bookmarks.BookmarkUiState.BookmarkUiMode; import org.chromium.chrome.browser.bookmarks.BookmarkUtils; import org.chromium.chrome.browser.bookmarks.PowerBookmarkShoppingItemRow; +import org.chromium.chrome.browser.bookmarks.TestingDelegate; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.night_mode.ChromeNightModeTestUtils; @@ -424,7 +426,7 @@ BookmarkTestUtil.waitForBookmarkModelLoaded(); BookmarkDelegate delegate = getBookmarkDelegate(); - Assert.assertEquals(BookmarkUiState.STATE_FOLDER, delegate.getCurrentState()); + Assert.assertEquals(BookmarkUiMode.FOLDER, delegate.getCurrentUiMode()); Assert.assertEquals("chrome-native://bookmarks/folder/3", BookmarkUtils.getLastUsedUrl(mActivityTestRule.getActivity())); } @@ -499,13 +501,13 @@ // Open the new folder where these bookmarks were created. openFolder(folder); - Assert.assertEquals(BookmarkUiState.STATE_FOLDER, delegate.getCurrentState()); + Assert.assertEquals(BookmarkUiMode.FOLDER, delegate.getCurrentUiMode()); Assert.assertEquals( "Wrong number of items before starting search.", 3, adapter.getItemCount()); TestThreadUtils.runOnUiThreadBlocking(delegate::openSearchUi); - Assert.assertEquals(BookmarkUiState.STATE_SEARCHING, delegate.getCurrentState()); + Assert.assertEquals(BookmarkUiMode.SEARCHING, delegate.getCurrentUiMode()); Assert.assertEquals( "Wrong number of items after showing search UI. The promo should be hidden.", 2, adapter.getItemCount()); @@ -530,7 +532,7 @@ () -> mBookmarkManagerCoordinator.getToolbarForTesting().hideSearchView()); Assert.assertEquals("Wrong number of items after closing search UI.", 3, mItemsContainer.getAdapter().getItemCount()); - Assert.assertEquals(BookmarkUiState.STATE_FOLDER, delegate.getCurrentState()); + Assert.assertEquals(BookmarkUiMode.FOLDER, delegate.getCurrentUiMode()); } @Test @@ -555,7 +557,7 @@ TestThreadUtils.runOnUiThreadBlocking(delegate::openSearchUi); - Assert.assertEquals(BookmarkUiState.STATE_SEARCHING, delegate.getCurrentState()); + Assert.assertEquals(BookmarkUiMode.SEARCHING, delegate.getCurrentUiMode()); Assert.assertEquals( "Wrong number of items after showing search UI. The promo should be hidden.", 2, adapter.getItemCount()); @@ -566,7 +568,7 @@ // Exit search UI. TestThreadUtils.runOnUiThreadBlocking( mBookmarkActivity.getOnBackPressedDispatcher()::onBackPressed); - Assert.assertNotEquals(BookmarkUiState.STATE_SEARCHING, delegate.getCurrentState()); + Assert.assertNotEquals(BookmarkUiMode.SEARCHING, delegate.getCurrentUiMode()); // Enter search UI again. TestThreadUtils.runOnUiThreadBlocking(delegate::openSearchUi); @@ -591,21 +593,21 @@ // Clear selection but still in search UI. CriteriaHelper.pollUiThread( () -> !itemView.isChecked(), "Expected item \"test\" to become not selected"); - Assert.assertEquals(BookmarkUiState.STATE_SEARCHING, delegate.getCurrentState()); + Assert.assertEquals(BookmarkUiMode.SEARCHING, delegate.getCurrentUiMode()); Assert.assertEquals(Boolean.TRUE, mBookmarkManagerCoordinator.getHandleBackPressChangedSupplier().get()); // Exit search UI. TestThreadUtils.runOnUiThreadBlocking( mBookmarkActivity.getOnBackPressedDispatcher()::onBackPressed); - Assert.assertEquals(BookmarkUiState.STATE_FOLDER, delegate.getCurrentState()); + Assert.assertEquals(BookmarkUiMode.FOLDER, delegate.getCurrentUiMode()); // Exit folder. Assert.assertEquals(Boolean.TRUE, mBookmarkManagerCoordinator.getHandleBackPressChangedSupplier().get()); TestThreadUtils.runOnUiThreadBlocking( mBookmarkActivity.getOnBackPressedDispatcher()::onBackPressed); - Assert.assertEquals(BookmarkUiState.STATE_FOLDER, delegate.getCurrentState()); + Assert.assertEquals(BookmarkUiMode.FOLDER, delegate.getCurrentUiMode()); // Exit bookmark activity. Assert.assertEquals(Boolean.FALSE, @@ -630,8 +632,8 @@ // Open the new folder where these bookmarks were created. openFolder(testFolder); - Assert.assertEquals("Wrong state, should be in folder", BookmarkUiState.STATE_FOLDER, - getBookmarkDelegate().getCurrentState()); + Assert.assertEquals("Wrong state, should be in folder", BookmarkUiMode.FOLDER, + getBookmarkDelegate().getCurrentUiMode()); Assert.assertEquals( "Wrong number of items before starting search.", 3, adapter.getItemCount()); @@ -639,8 +641,8 @@ // are currently testFolder's children (3). TestThreadUtils.runOnUiThreadBlocking(getBookmarkDelegate()::openSearchUi); RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer); - Assert.assertEquals("Wrong state, should be searching", BookmarkUiState.STATE_SEARCHING, - getBookmarkDelegate().getCurrentState()); + Assert.assertEquals("Wrong state, should be searching", BookmarkUiMode.SEARCHING, + getBookmarkDelegate().getCurrentUiMode()); Assert.assertEquals( "Wrong number of items before starting search.", 3, adapter.getItemCount()); @@ -669,8 +671,8 @@ // The user should still be searching, and the bookmark should be gone. We're refreshing // the search query again here, but in this case it's now "Google". - Assert.assertEquals("Wrong state, should be searching", BookmarkUiState.STATE_SEARCHING, - getBookmarkDelegate().getCurrentState()); + Assert.assertEquals("Wrong state, should be searching", BookmarkUiMode.SEARCHING, + getBookmarkDelegate().getCurrentUiMode()); Assert.assertEquals("Wrong number of items after searching.", 0, mItemsContainer.getAdapter().getItemCount()); @@ -680,8 +682,8 @@ // The user should still be searching, and the bookmark should reappear. Refreshing the // search yet again, now with the "Google" search matching returning 1 result. - Assert.assertEquals("Wrong state, should be searching", BookmarkUiState.STATE_SEARCHING, - getBookmarkDelegate().getCurrentState()); + Assert.assertEquals("Wrong state, should be searching", BookmarkUiMode.SEARCHING, + getBookmarkDelegate().getCurrentUiMode()); Assert.assertEquals("Wrong number of items after searching.", 1, mItemsContainer.getAdapter().getItemCount()); } @@ -695,12 +697,10 @@ addBookmark(TEST_PAGE_TITLE_FOO, mTestPageFoo, testFolder); openBookmarkManager(); - RecyclerView.Adapter adapter = getAdapter(); - // Start searching, enter a query. TestThreadUtils.runOnUiThreadBlocking(getBookmarkDelegate()::openSearchUi); - Assert.assertEquals("Wrong state, should be searching", BookmarkUiState.STATE_SEARCHING, - getBookmarkDelegate().getCurrentState()); + Assert.assertEquals("Wrong state, should be searching", BookmarkUiMode.SEARCHING, + getBookmarkDelegate().getCurrentUiMode()); searchBookmarks("test"); Assert.assertEquals("Wrong number of items after searching.", 2, mItemsContainer.getAdapter().getItemCount()); @@ -709,8 +709,8 @@ removeBookmark(testFolder); // The user should still be searching, and the bookmark should be gone. - Assert.assertEquals("Wrong state, should be searching", BookmarkUiState.STATE_SEARCHING, - getBookmarkDelegate().getCurrentState()); + Assert.assertEquals("Wrong state, should be searching", BookmarkUiMode.SEARCHING, + getBookmarkDelegate().getCurrentUiMode()); Assert.assertEquals("Wrong number of items after searching.", 0, mItemsContainer.getAdapter().getItemCount()); @@ -719,8 +719,8 @@ () -> mBookmarkManagerCoordinator.getUndoControllerForTesting().onAction(null)); // The user should still be searching, and the bookmark should reappear. - Assert.assertEquals("Wrong state, should be searching", BookmarkUiState.STATE_SEARCHING, - getBookmarkDelegate().getCurrentState()); + Assert.assertEquals("Wrong state, should be searching", BookmarkUiMode.SEARCHING, + getBookmarkDelegate().getCurrentUiMode()); Assert.assertEquals("Wrong number of items after searching.", 2, mItemsContainer.getAdapter().getItemCount()); } @@ -1342,7 +1342,7 @@ getAdapter().getItemCount()); // Verify that bookmark 1 is editable (so more button can be triggered) but not movable. - BookmarkId partnerBookmarkId1 = getReorderAdapter().getIdByPosition(0); + BookmarkId partnerBookmarkId1 = getIdByPosition(0); TestThreadUtils.runOnUiThreadBlocking(() -> { BookmarkItem partnerBookmarkItem1 = mBookmarkModel.getBookmarkById(partnerBookmarkId1); partnerBookmarkItem1.forceEditableForTesting(); @@ -1362,7 +1362,7 @@ onView(withText("Move down")).check(doesNotExist()); // Verify that bookmark 2 is not movable. - BookmarkId partnerBookmarkId2 = getReorderAdapter().getIdByPosition(1); + BookmarkId partnerBookmarkId2 = getIdByPosition(1); TestThreadUtils.runOnUiThreadBlocking(() -> { BookmarkItem partnerBookmarkItem2 = mBookmarkModel.getBookmarkById(partnerBookmarkId2); partnerBookmarkItem2.forceEditableForTesting(); @@ -1397,7 +1397,7 @@ mBookmarkModel.getOtherFolderId(), 0, TEST_TITLE_A, mTestUrlA)); }); - TestThreadUtils.runOnUiThreadBlocking(adapter::simulateSignInForTesting); + TestThreadUtils.runOnUiThreadBlocking(getTestingDelegate()::simulateSignInForTesting); Assert.assertEquals( "Expected promo, \"Reading List\", \"Mobile Bookmarks\" and \"Other Bookmarks\" folder to appear!", 4, adapter.getItemCount()); @@ -1911,6 +1911,10 @@ return (BookmarkItemsAdapter) getAdapter(); } + private TestingDelegate getTestingDelegate() { + return mBookmarkManagerCoordinator.getTestingDelegate(); + } + private void enterSearch() throws Exception { View searchButton = mBookmarkManagerCoordinator.getToolbarForTesting().findViewById( R.id.search_menu_id); @@ -2000,11 +2004,11 @@ } private BookmarkId getIdByPosition(int pos) { - return getReorderAdapter().getIdByPosition(pos); + return getTestingDelegate().getIdByPositionForTesting(pos); } private void searchBookmarks(final String query) { - TestThreadUtils.runOnUiThreadBlocking(() -> getReorderAdapter().search(query)); + TestThreadUtils.runOnUiThreadBlocking(() -> getTestingDelegate().searchForTesting(query)); } private void openFolder(BookmarkId folder) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java index 7c9e358..e9dde621d 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/bookmarks/ReadingListTest.java
@@ -51,15 +51,15 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeTabbedActivity; import org.chromium.chrome.browser.bookmarks.BookmarkDelegate; -import org.chromium.chrome.browser.bookmarks.BookmarkItemsAdapter; import org.chromium.chrome.browser.bookmarks.BookmarkManagerCoordinator; import org.chromium.chrome.browser.bookmarks.BookmarkModel; import org.chromium.chrome.browser.bookmarks.BookmarkPage; import org.chromium.chrome.browser.bookmarks.BookmarkPromoHeader; import org.chromium.chrome.browser.bookmarks.BookmarkRow; import org.chromium.chrome.browser.bookmarks.BookmarkToolbar; -import org.chromium.chrome.browser.bookmarks.BookmarkUiState; +import org.chromium.chrome.browser.bookmarks.BookmarkUiState.BookmarkUiMode; import org.chromium.chrome.browser.bookmarks.BookmarkUtils; +import org.chromium.chrome.browser.bookmarks.TestingDelegate; import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.sync.SyncService; import org.chromium.chrome.browser.tab.Tab; @@ -190,16 +190,12 @@ return bookmarkId; } - private BookmarkItemsAdapter getReorderAdapter() { - return (BookmarkItemsAdapter) getAdapter(); - } - - private RecyclerView.Adapter getAdapter() { - return mItemsContainer.getAdapter(); + private TestingDelegate getTestingDelegate() { + return mBookmarkManagerCoordinator.getTestingDelegate(); } private BookmarkId getIdByPosition(int pos) { - return getReorderAdapter().getIdByPosition(pos); + return getTestingDelegate().getIdByPositionForTesting(pos); } private BookmarkDelegate getBookmarkDelegate() { @@ -216,7 +212,7 @@ BookmarkToolbar toolbar = mBookmarkManagerCoordinator.getToolbarForTesting(); // We should default to the root bookmark. - Assert.assertEquals(BookmarkUiState.STATE_FOLDER, delegate.getCurrentState()); + Assert.assertEquals(BookmarkUiMode.FOLDER, delegate.getCurrentUiMode()); Assert.assertEquals("chrome-native://bookmarks/folder/0", BookmarkUtils.getLastUsedUrl(mActivityTestRule.getActivity())); Assert.assertEquals("Bookmarks", toolbar.getTitle()); @@ -235,7 +231,7 @@ ApplicationTestUtils.waitForActivityState(mBookmarkActivity, Stage.DESTROYED); // Reopen and make sure we're back in "Mobile bookmarks". - Assert.assertEquals(BookmarkUiState.STATE_FOLDER, delegate.getCurrentState()); + Assert.assertEquals(BookmarkUiMode.FOLDER, delegate.getCurrentUiMode()); Assert.assertEquals("chrome-native://bookmarks/folder/3", BookmarkUtils.getLastUsedUrl(mActivityTestRule.getActivity())); } @@ -307,8 +303,8 @@ // Enter search UI, but don't enter any search key word. TestThreadUtils.runOnUiThreadBlocking(getBookmarkDelegate()::openSearchUi); - Assert.assertEquals("Wrong state, should be searching", BookmarkUiState.STATE_SEARCHING, - getBookmarkDelegate().getCurrentState()); + Assert.assertEquals("Wrong state, should be searching", BookmarkUiMode.SEARCHING, + getBookmarkDelegate().getCurrentUiMode()); RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer); // Delete the reading list page in search state. @@ -350,7 +346,7 @@ View readingListRow = mItemsContainer.findViewHolderForAdapterPosition(1).itemView; Assert.assertEquals("The 2nd view should be reading list.", BookmarkType.READING_LIST, - getReorderAdapter().getIdByPosition(1).getType()); + getIdByPosition(1).getType()); TestThreadUtils.runOnUiThreadBlocking(() -> TouchCommon.singleClickView(readingListRow)); ChromeTabbedActivity activity = BookmarkTestUtil.waitForTabbedActivity(); @@ -418,7 +414,7 @@ Assert.assertEquals("No overflow menu for reading list folder.", View.GONE, readingListRow.findViewById(R.id.more).getVisibility()); Assert.assertEquals("The 1st view should be reading list.", BookmarkType.READING_LIST, - getReorderAdapter().getIdByPosition(0).getType()); + getIdByPosition(0).getType()); onView(withText("Reading list")).check(matches(isDisplayed())); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRowTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRowTest.java index 113899d6..5afec90 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRowTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRowTest.java
@@ -26,6 +26,7 @@ import org.chromium.chrome.R; import org.chromium.chrome.browser.bookmarks.BookmarkRow.Location; +import org.chromium.chrome.browser.bookmarks.BookmarkUiState.BookmarkUiMode; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.components.bookmarks.BookmarkId; import org.chromium.components.bookmarks.BookmarkItem; @@ -108,7 +109,7 @@ @Test @SmallTest public void testSetBookmarkId() { - doReturn(BookmarkUiState.STATE_FOLDER).when(mDelegate).getCurrentState(); + doReturn(BookmarkUiMode.FOLDER).when(mDelegate).getCurrentUiMode(); TestThreadUtils.runOnUiThreadBlocking( () -> { mBookmarkItemRow.setBookmarkId(mBookmarkId, Location.TOP, false); }); @@ -126,7 +127,7 @@ @Test(expected = AssertionError.class) @SmallTest public void testSetBookmarkId_LoadingWhileClicked() { - doReturn(BookmarkUiState.STATE_LOADING).when(mDelegate).getCurrentState(); + doReturn(BookmarkUiMode.LOADING).when(mDelegate).getCurrentUiMode(); TestThreadUtils.runOnUiThreadBlocking( () -> { mBookmarkItemRow.setBookmarkId(mBookmarkId, Location.TOP, false); });
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarTest.java index 2824a63..f7948329 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarTest.java
@@ -39,6 +39,7 @@ import org.chromium.chrome.browser.app.bookmarks.BookmarkAddEditFolderActivity; import org.chromium.chrome.browser.app.bookmarks.BookmarkEditActivity; import org.chromium.chrome.browser.app.bookmarks.BookmarkFolderSelectActivity; +import org.chromium.chrome.browser.bookmarks.BookmarkUiState.BookmarkUiMode; import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.incognito.IncognitoUtils; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; @@ -160,7 +161,7 @@ mBookmarkToolbar.setBookmarkModel(mBookmarkModel); mBookmarkToolbar.setBookmarkOpener(mBookmarkOpener); mBookmarkToolbar.setSelectionDelegate(mSelectionDelegate); - mBookmarkToolbar.setBookmarkUiState(BookmarkUiState.STATE_FOLDER); + mBookmarkToolbar.setBookmarkUiMode(BookmarkUiMode.FOLDER); mBookmarkToolbar.setIsDialogUi(true); mBookmarkToolbar.setOpenSearchUiRunnable(mOpenSearchUiRunnable); mBookmarkToolbar.setOpenFolderCallback(mOpenFolderCallback); @@ -221,7 +222,7 @@ @UiThreadTest public void onNavigationBack_searching() { initializeNormal(); - mBookmarkToolbar.setBookmarkUiState(BookmarkUiState.STATE_SEARCHING); + mBookmarkToolbar.setBookmarkUiMode(BookmarkUiMode.SEARCHING); mBookmarkToolbar.onNavigationBack(); Assert.assertFalse(mBookmarkToolbar.isSearching()); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/BrowsingDataTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/BrowsingDataTest.java index 2de08c3..6b9fd6058e 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/BrowsingDataTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/BrowsingDataTest.java
@@ -88,6 +88,11 @@ sActivityTestRule.getWebContents(), type); } + private String runJavascriptSync(String type) throws Exception { + return JavaScriptUtils.executeJavaScriptAndWaitForResult( + sActivityTestRule.getWebContents(), type); + } + /** * Test cookies deletion. */ @@ -96,14 +101,14 @@ public void testCookiesDeleted() throws Exception { Assert.assertEquals(0, getCookieCount()); sActivityTestRule.loadUrl(mUrl); - Assert.assertEquals("false", runJavascriptAsync("hasCookie()")); + Assert.assertEquals("false", runJavascriptSync("hasCookie()")); - runJavascriptAsync("setCookie()"); - Assert.assertEquals("true", runJavascriptAsync("hasCookie()")); + runJavascriptSync("setCookie()"); + Assert.assertEquals("true", runJavascriptSync("hasCookie()")); Assert.assertEquals(1, getCookieCount()); clearBrowsingData(BrowsingDataType.COOKIES, TimePeriod.LAST_HOUR); - Assert.assertEquals("false", runJavascriptAsync("hasCookie()")); + Assert.assertEquals("false", runJavascriptSync("hasCookie()")); Assert.assertEquals(0, getCookieCount()); } @@ -121,15 +126,15 @@ for (String type : siteData) { Assert.assertEquals(type, 0, getCookieCount()); - Assert.assertEquals(type, "false", runJavascriptAsync("has" + type + "()")); + Assert.assertEquals(type, "false", runJavascriptAsync("has" + type + "Async()")); - runJavascriptAsync("set" + type + "()"); + runJavascriptAsync("set" + type + "Async()"); Assert.assertEquals(type, 1, getCookieCount()); - Assert.assertEquals(type, "true", runJavascriptAsync("has" + type + "()")); + Assert.assertEquals(type, "true", runJavascriptAsync("has" + type + "Async()")); clearBrowsingData(BrowsingDataType.COOKIES, TimePeriod.LAST_HOUR); Assert.assertEquals(type, 0, getCookieCount()); - Assert.assertEquals(type, "false", runJavascriptAsync("has" + type + "()")); + Assert.assertEquals(type, "false", runJavascriptAsync("has" + type + "Async()")); // Some types create data by checking for them, so we need to do a cleanup at the end. clearBrowsingData(BrowsingDataType.COOKIES, TimePeriod.LAST_HOUR); @@ -165,12 +170,12 @@ public void testHistoryDeleted() throws Exception { Assert.assertEquals(0, getCookieCount()); sActivityTestRule.loadUrlInNewTab(mUrl); - Assert.assertEquals("false", runJavascriptAsync("hasHistory()")); + Assert.assertEquals("false", runJavascriptSync("hasHistory()")); - runJavascriptAsync("setHistory()"); - Assert.assertEquals("true", runJavascriptAsync("hasHistory()")); + runJavascriptSync("setHistory()"); + Assert.assertEquals("true", runJavascriptSync("hasHistory()")); clearBrowsingData(BrowsingDataType.HISTORY, TimePeriod.LAST_HOUR); - Assert.assertEquals("false", runJavascriptAsync("hasHistory()")); + Assert.assertEquals("false", runJavascriptSync("hasHistory()")); } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoStorageLeakageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoStorageLeakageTest.java index b74761a..a23bb3b 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoStorageLeakageTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoStorageLeakageTest.java
@@ -105,12 +105,12 @@ // Sets the session storage in tab1 assertEquals("true", - JavaScriptUtils.runJavascriptWithAsyncResult( + JavaScriptUtils.executeJavaScriptAndWaitForResult( tab1.getWebContents(), "setSessionStorage()")); // Checks the sessions storage is set in tab1 assertEquals("true", - JavaScriptUtils.runJavascriptWithAsyncResult( + JavaScriptUtils.executeJavaScriptAndWaitForResult( tab1.getWebContents(), "hasSessionStorage()")); Tab tab2 = activity2.launchUrl( @@ -121,7 +121,7 @@ // Checks the session storage in tab2. Session storage set in tab1 should not be accessible. // The session storage is per tab basis. assertEquals("false", - JavaScriptUtils.runJavascriptWithAsyncResult( + JavaScriptUtils.executeJavaScriptAndWaitForResult( tab2.getWebContents(), "hasSessionStorage()")); } @@ -160,11 +160,11 @@ // Set the storage in tab1 assertEquals("true", JavaScriptUtils.runJavascriptWithAsyncResult( - tab1.getWebContents(), "set" + type + "()")); + tab1.getWebContents(), "set" + type + "Async()")); // Checks the storage is set in tab1 assertEquals("true", JavaScriptUtils.runJavascriptWithAsyncResult( - tab1.getWebContents(), "has" + type + "()")); + tab1.getWebContents(), "has" + type + "Async()")); TestThreadUtils.runOnUiThreadBlocking( () -> tab2.loadIfNeeded(LoadIfNeededCaller.OTHER)); @@ -173,7 +173,7 @@ // Access the storage from tab2 assertEquals(expected, JavaScriptUtils.runJavascriptWithAsyncResult( - tab2.getWebContents(), "has" + type + "()")); + tab2.getWebContents(), "has" + type + "Async()")); } } }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java index a0e72c7..5c127156 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
@@ -242,13 +242,13 @@ private void expectHasCookies(boolean hasData) throws TimeoutException { for (String type : sCookieDataTypes) { - assertEquals(hasData ? "true" : "false", runJavascriptAsync("has" + type + "()")); + assertEquals(hasData ? "true" : "false", runJavascriptAsync("has" + type + "Async()")); } } private void createCookies() throws TimeoutException { for (String type : sCookieDataTypes) { - runJavascriptAsync("set" + type + "()"); + runJavascriptAsync("set" + type + "Async()"); } }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinatorTest.java index 5aafb890..e5ac123 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinatorTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinatorTest.java
@@ -8,7 +8,6 @@ import android.view.View; import android.widget.FrameLayout; -import androidx.test.core.app.ActivityScenario; import androidx.test.ext.junit.rules.ActivityScenarioRule; import org.junit.Assert; @@ -80,7 +79,6 @@ @Mock BookmarkModel mBookmarkModel; - private ActivityScenario<TestActivity> mActivityScenario; private Activity mActivity; private BookmarkManagerCoordinator mCoordinator;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java index 7d707ac0..197d538 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java
@@ -10,15 +10,17 @@ import static org.chromium.ui.test.util.MockitoHelper.doRunnable; -import android.content.Context; -import android.view.accessibility.AccessibilityManager; +import android.app.Activity; import androidx.recyclerview.widget.RecyclerView; +import androidx.test.core.app.ActivityScenario; +import androidx.test.ext.junit.rules.ActivityScenarioRule; 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.Mock; import org.mockito.junit.MockitoJUnit; @@ -28,11 +30,23 @@ import org.chromium.base.supplier.ObservableSupplierImpl; import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.Batch; +import org.chromium.chrome.browser.bookmarks.BookmarkUiState.BookmarkUiMode; +import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.signin.services.IdentityServicesProvider; +import org.chromium.chrome.browser.signin.services.SigninManager; +import org.chromium.chrome.browser.sync.SyncService; +import org.chromium.chrome.test.util.browser.Features; import org.chromium.components.bookmarks.BookmarkId; +import org.chromium.components.bookmarks.BookmarkItem; import org.chromium.components.bookmarks.BookmarkType; import org.chromium.components.browser_ui.widget.selectable_list.SelectableListLayout; import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate; import org.chromium.components.favicon.LargeIconBridge; +import org.chromium.components.signin.AccountManagerFacade; +import org.chromium.components.signin.AccountManagerFacadeProvider; +import org.chromium.components.signin.identitymanager.IdentityManager; +import org.chromium.ui.base.TestActivity; import java.util.Arrays; @@ -40,13 +54,17 @@ @Batch(Batch.UNIT_TESTS) @RunWith(BaseRobolectricTestRunner.class) @Config(manifest = Config.NONE) +@Features.EnableFeatures({ChromeFeatureList.BOOKMARKS_REFRESH, ChromeFeatureList.SHOPPING_LIST}) public class BookmarkManagerMediatorTest { @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule + public ActivityScenarioRule<TestActivity> mActivityScenarioRule = + new ActivityScenarioRule<>(TestActivity.class); + @Rule + public TestRule mFeaturesProcessorRule = new Features.JUnitProcessor(); @Mock - Context mContext; - @Mock BookmarkModel mBookmarkModel; @Mock BookmarkOpener mBookmarkOpener; @@ -57,13 +75,23 @@ @Mock RecyclerView mRecyclerView; @Mock - BookmarkItemsAdapter mBookmarkItemsAdapter; - @Mock LargeIconBridge mLargeIconBridge; @Mock - AccessibilityManager mAccessibilityManager; - @Mock BookmarkUiObserver mBookmarkUiObserver; + @Mock + Profile mProfile; + @Mock + SyncService mSyncService; + @Mock + IdentityServicesProvider mIdentityServicesProvider; + @Mock + SigninManager mSigninManager; + @Mock + IdentityManager mIdentityManager; + @Mock + AccountManagerFacade mAccountManagerFacade; + @Mock + BookmarkUndoController mBookmarkUndoController; final ObservableSupplierImpl<Boolean> mBackPressStateSupplier = new ObservableSupplierImpl<>(); final ObservableSupplierImpl<Boolean> mSelectableListLayoutHandleBackPressChangedSupplier = @@ -71,35 +99,48 @@ final BookmarkId mFolderId = new BookmarkId(/*id=*/1, BookmarkType.NORMAL); final BookmarkId mFolder2Id = new BookmarkId(/*id=*/2, BookmarkType.NORMAL); - BookmarkManagerMediator mMediator; + private ActivityScenario<TestActivity> mActivityScenario; + private Activity mActivity; + private BookmarkManagerMediator mMediator; @Before public void setUp() { - // Setup Context. - doReturn(mAccessibilityManager) - .when(mContext) - .getSystemService(Context.ACCESSIBILITY_SERVICE); + mActivityScenarioRule.getScenario().onActivity((activity) -> { + mActivity = activity; - // Setup BookmarkModel. - doReturn(true).when(mBookmarkModel).doesBookmarkExist(any()); - doReturn(Arrays.asList(mFolder2Id)).when(mBookmarkModel).getChildIDs(mFolderId); + // Setup BookmarkModel. + doReturn(true).when(mBookmarkModel).doesBookmarkExist(any()); + doReturn(Arrays.asList(mFolder2Id)).when(mBookmarkModel).getChildIDs(mFolderId); + BookmarkItem bookmarkItem = + new BookmarkItem(mFolderId, "Folder", null, true, null, true, false, 0, false); + doReturn(bookmarkItem).when(mBookmarkModel).getBookmarkById(any()); - // Setup SelectableListLayout. - doReturn(mContext).when(mSelectableListLayout).getContext(); - doReturn(mSelectableListLayoutHandleBackPressChangedSupplier) - .when(mSelectableListLayout) - .getHandleBackPressChangedSupplier(); + // Setup SelectableListLayout. + doReturn(mActivity).when(mSelectableListLayout).getContext(); + doReturn(mSelectableListLayoutHandleBackPressChangedSupplier) + .when(mSelectableListLayout) + .getHandleBackPressChangedSupplier(); - // Setup BookmarkUIObserver. - doRunnable(() -> mMediator.removeUiObserver(mBookmarkUiObserver)) - .when(mBookmarkUiObserver) - .onDestroy(); + // Setup BookmarkUIObserver. + doRunnable(() -> mMediator.removeUiObserver(mBookmarkUiObserver)) + .when(mBookmarkUiObserver) + .onDestroy(); - mMediator = new BookmarkManagerMediator(mContext, mBookmarkModel, mBookmarkOpener, - mSelectableListLayout, mSelectionDelegate, mRecyclerView, mBookmarkItemsAdapter, - mLargeIconBridge, /*isDialogUi=*/true, /*isIncognito=*/false, - mBackPressStateSupplier); - mMediator.addUiObserver(mBookmarkUiObserver); + // Setup sync/identify mocks. + SyncService.overrideForTests(mSyncService); + IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider); + doReturn(mSigninManager).when(mIdentityServicesProvider).getSigninManager(any()); + doReturn(mIdentityManager).when(mSigninManager).getIdentityManager(); + AccountManagerFacadeProvider.setInstanceForTests(mAccountManagerFacade); + + BookmarkItemsAdapter bookmarkItemsAdapter = + new BookmarkItemsAdapter(mActivity, (a, b) -> null, (a, b, c) -> {}); + mMediator = new BookmarkManagerMediator(mActivity, mBookmarkModel, mBookmarkOpener, + mSelectableListLayout, mSelectionDelegate, mRecyclerView, bookmarkItemsAdapter, + mLargeIconBridge, /*isDialogUi=*/true, /*isIncognito=*/false, + mBackPressStateSupplier, mProfile, mBookmarkUndoController); + mMediator.addUiObserver(mBookmarkUiObserver); + }); } void finishLoading() { @@ -109,7 +150,7 @@ @Test public void initAndLoadBookmarkModel() { finishLoading(); - Assert.assertEquals(BookmarkUiState.STATE_LOADING, mMediator.getCurrentState()); + Assert.assertEquals(BookmarkUiMode.LOADING, mMediator.getCurrentUiMode()); } @Test @@ -118,15 +159,16 @@ mMediator.updateForUrl("chrome-native://bookmarks/folder/" + mFolderId.getId()); finishLoading(); - Assert.assertEquals(BookmarkUiState.STATE_FOLDER, mMediator.getCurrentState()); + Assert.assertEquals(BookmarkUiMode.FOLDER, mMediator.getCurrentUiMode()); } @Test - public void destroyUnregistersObservers() { + public void testDestroy() { finishLoading(); mMediator.onDestroy(); verify(mBookmarkUiObserver).onDestroy(); + verify(mBookmarkUndoController).destroy(); } @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediatorTest.java index 5a9403f..f6dad0bf 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediatorTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediatorTest.java
@@ -20,6 +20,7 @@ import org.chromium.base.supplier.OneshotSupplierImpl; import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.util.Batch; +import org.chromium.chrome.browser.bookmarks.BookmarkUiState.BookmarkUiMode; import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate; import org.chromium.ui.modelutil.PropertyModel; @@ -57,8 +58,7 @@ .with(BookmarkToolbarProperties.BOOKMARK_MODEL, mBookmarkModel) .with(BookmarkToolbarProperties.BOOKMARK_OPENER, mBookmarkOpener) .with(BookmarkToolbarProperties.SELECTION_DELEGATE, mSelectionDelegate) - .with(BookmarkToolbarProperties.BOOKMARK_UI_STATE, - BookmarkUiState.STATE_LOADING) + .with(BookmarkToolbarProperties.BOOKMARK_UI_STATE, BookmarkUiMode.LOADING) .with(BookmarkToolbarProperties.IS_DIALOG_UI, false) .with(BookmarkToolbarProperties.DRAG_ENABLED, false) .with(BookmarkToolbarProperties.OPEN_SEARCH_UI_RUNNABLE, @@ -78,16 +78,16 @@ @Test public void onStateChangedUpdatesModel() { - mMediator.onStateChanged(BookmarkUiState.STATE_LOADING); - Assert.assertEquals(BookmarkUiState.STATE_LOADING, + mMediator.onUiModeChanged(BookmarkUiMode.LOADING); + Assert.assertEquals(BookmarkUiMode.LOADING, mModel.get(BookmarkToolbarProperties.BOOKMARK_UI_STATE).intValue()); - mMediator.onStateChanged(BookmarkUiState.STATE_SEARCHING); - Assert.assertEquals(BookmarkUiState.STATE_SEARCHING, + mMediator.onUiModeChanged(BookmarkUiMode.SEARCHING); + Assert.assertEquals(BookmarkUiMode.SEARCHING, mModel.get(BookmarkToolbarProperties.BOOKMARK_UI_STATE).intValue()); - mMediator.onStateChanged(BookmarkUiState.STATE_FOLDER); - Assert.assertEquals(BookmarkUiState.STATE_FOLDER, + mMediator.onUiModeChanged(BookmarkUiMode.FOLDER); + Assert.assertEquals(BookmarkUiMode.FOLDER, mModel.get(BookmarkToolbarProperties.BOOKMARK_UI_STATE).intValue()); }
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h index 1099d1b1..885be4b 100644 --- a/chrome/app/chrome_command_ids.h +++ b/chrome/app/chrome_command_ids.h
@@ -231,7 +231,10 @@ #define IDC_LACROS_DATA_MIGRATION 40265 #endif -#define IDC_PERFORMANCE 40266 +#define IDC_PERFORMANCE 40266 +#define IDC_EXTENSIONS_SUBMENU 40267 +#define IDC_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS 40268 +#define IDC_EXTENSIONS_SUBMENU_VISIT_CHROME_WEB_STORE 40269 // Spell-check // Insert any additional suggestions before _LAST; these have to be consecutive.
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc index 1169f5c..da238542 100644 --- a/chrome/app/chrome_main_delegate.cc +++ b/chrome/app/chrome_main_delegate.cc
@@ -108,7 +108,6 @@ #include "chrome/child/v8_crashpad_support_win.h" #include "chrome/chrome_elf/chrome_elf_main.h" #include "chrome/common/child_process_logging.h" -#include "chrome/common/win/delay_load_failure_hook.h" #include "sandbox/win/src/sandbox.h" #include "sandbox/win/src/sandbox_factory.h" #include "ui/base/resource/resource_bundle_win.h" @@ -722,11 +721,6 @@ // Initialize the cleaner of left-behind tmp files now that the main thread // has its SequencedTaskRunner; see https://crbug.com/1075917. base::ImportantFileWriterCleaner::GetInstance().Initialize(); - - // For now, do not enable delay load failure hooks for browser process except - // in tests, where failures really shouldn't happen. - if (invoked_in_browser->is_running_test) - chrome::DisableDelayLoadFailureHooksForCurrentModule(); #endif #if !BUILDFLAG(IS_FUCHSIA)
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index cb4c892f..bc37292 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -1484,6 +1484,15 @@ <message name="IDS_SHOW_EXTENSIONS" desc="The show extensions menu in the app menu"> &Extensions </message> + <message name="IDS_EXTENSIONS_SUBMENU" desc="The extensions menu in the app menu (title case)."> + &Extensions + </message> + <message name="IDS_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS_ITEM" desc="The text for the link in the extensions menu, in the app menu, to visit chrome://extensions to view all extensions installed (title case)."> + Manage Extensions + </message> + <message name="IDS_EXTENSIONS_SUBMENU_CHROME_WEBSTORE_ITEM" desc="The text for the link in the extensions menu, in the app menu, to visit the Chrome Web Store to get more extensions (title case)."> + Visit Chrome Web Store + </message> <message name="IDS_SHOW_PERFORMANCE" desc="The text label of the Performance menu item"> Performance </message> @@ -1525,6 +1534,15 @@ <message name="IDS_SHOW_EXTENSIONS" desc="In Title Case: The show extensions menu in the app menu"> &Extensions </message> + <message name="IDS_EXTENSIONS_SUBMENU" desc="In Title Case: The extensions menu in the app menu."> + &Extensions + </message> + <message name="IDS_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS_ITEM" desc="In Title Case: The text for the link in the extensions menu, in the app menu, to visit chrome://extensions to view all extensions installed."> + Manage Extensions + </message> + <message name="IDS_EXTENSIONS_SUBMENU_CHROME_WEBSTORE_ITEM" desc="In Title Case: The text for the link in the extensions menu, in the app menu, to visit the Chrome Web Store to get more extensions."> + Visit Chrome Web Store + </message> <message name="IDS_SHOW_PERFORMANCE" desc="In Title Case: The text label of the Performance menu item"> Performance </message> @@ -7578,6 +7596,9 @@ <message name="IDS_READING_MODE_BLUE_COLOR_LABEL" desc="Combobox option for setting the color theme for the reading mode feature."> Blue </message> + <message name="IDS_READ_ANYTHING_LOADING" desc="Message to show while Read Anything is figuring out what text to display."> + Getting ready + </message> <!-- User note strings --> <message name="IDS_USER_NOTE_TITLE" desc="Title of the User note feature, which gives users access to private user notes.">
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_SUBMENU.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_SUBMENU.png.sha1 new file mode 100644 index 0000000..20355225 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_SUBMENU.png.sha1
@@ -0,0 +1 @@ +1d89c386592ad93c2b63e8fe942ee2adaf909539 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_SUBMENU_CHROME_WEBSTORE_ITEM.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_SUBMENU_CHROME_WEBSTORE_ITEM.png.sha1 new file mode 100644 index 0000000..9b6d4602 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_SUBMENU_CHROME_WEBSTORE_ITEM.png.sha1
@@ -0,0 +1 @@ +65862db3687d70fd338e38ce370f5de8a5dfcb9b \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS_ITEM.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS_ITEM.png.sha1 new file mode 100644 index 0000000..c0fb2011 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS_ITEM.png.sha1
@@ -0,0 +1 @@ +000a4144865e17b3401913e888436b2efd2de3c2 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_READ_ANYTHING_LOADING.png.sha1 b/chrome/app/generated_resources_grd/IDS_READ_ANYTHING_LOADING.png.sha1 new file mode 100644 index 0000000..0b267dfc --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_READ_ANYTHING_LOADING.png.sha1
@@ -0,0 +1 @@ +b6f6cbc03238587a5de03210a77191512e1fc63e \ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 9fccc44..0ac02bf 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -359,8 +359,6 @@ "content_settings/page_specific_content_settings_delegate.h", "content_settings/sound_content_setting_observer.cc", "content_settings/sound_content_setting_observer.h", - "crash_upload_list/crash_upload_list.cc", - "crash_upload_list/crash_upload_list.h", "custom_handlers/chrome_protocol_handler_registry_delegate.cc", "custom_handlers/chrome_protocol_handler_registry_delegate.h", "custom_handlers/protocol_handler_registry_factory.cc", @@ -1996,6 +1994,7 @@ "//chrome/browser/breadcrumbs", "//chrome/browser/browsing_data:constants", "//chrome/browser/chrome_for_testing:buildflags", + "//chrome/browser/crash_upload_list", "//chrome/browser/devtools", "//chrome/browser/enterprise/identifiers", "//chrome/browser/enterprise/platform_auth:features", @@ -2936,8 +2935,6 @@ "content_creation/notes/internal/note_service_factory.h", "content_settings/request_desktop_site_web_contents_observer_android.cc", "content_settings/request_desktop_site_web_contents_observer_android.h", - "crash_upload_list/crash_upload_list_android.cc", - "crash_upload_list/crash_upload_list_android.h", "creator/android/creator_api_bridge.cc", "device_reauth/android/device_authenticator_android.cc", "device_reauth/android/device_authenticator_android.h", @@ -5526,8 +5523,6 @@ "certificate_provider/sign_requests.h", "certificate_provider/thread_safe_certificate_map.cc", "certificate_provider/thread_safe_certificate_map.h", - "crash_upload_list/crash_upload_list_chromeos.cc", - "crash_upload_list/crash_upload_list_chromeos.h", "download/notification/download_item_notification.cc", "download/notification/download_item_notification.h", "download/notification/download_notification_manager.cc", @@ -6264,8 +6259,6 @@ if (is_fuchsia) { sources += [ - "crash_upload_list/crash_upload_list_fuchsia.cc", - "crash_upload_list/crash_upload_list_fuchsia.h", "download/download_status_updater_fuchsia.cc", "first_run/first_run_internal_fuchsia.cc", "first_run/upgrade_util_fuchsia.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 533b3bf..79034a7 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -3622,9 +3622,6 @@ {"calendar-jelly", flag_descriptions::kCalendarJellyName, flag_descriptions::kCalendarJellyDescription, kOsCrOS, FEATURE_VALUE_TYPE(ash::features::kCalendarJelly)}, - {"calendar-view", flag_descriptions::kCalendarViewName, - flag_descriptions::kCalendarViewDescription, kOsCrOS, - FEATURE_VALUE_TYPE(ash::features::kCalendarView)}, {"calendar-view-debug-mode", flag_descriptions::kCalendarModelDebugModeName, flag_descriptions::kCalendarModelDebugModeDescription, kOsCrOS, FEATURE_VALUE_TYPE(ash::features::kCalendarModelDebugMode)}, @@ -5179,6 +5176,12 @@ #endif // BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_ANDROID) + + {"omnibox-adapt-narrow-tablet-windows", + flag_descriptions::kOmniboxAdaptNarrowTabletWindowsName, + flag_descriptions::kOmniboxAdaptNarrowTabletWindowsDescription, kOsAndroid, + FEATURE_VALUE_TYPE(chrome::android::kOmniboxAdaptNarrowTabletWindows)}, + {"omnibox-assistant-voice-search", flag_descriptions::kOmniboxAssistantVoiceSearchName, flag_descriptions::kOmniboxAssistantVoiceSearchDescription, kOsAndroid,
diff --git a/chrome/browser/apps/app_service/metrics/app_service_metrics.cc b/chrome/browser/apps/app_service/metrics/app_service_metrics.cc index 02a0577..2f3ddc8 100644 --- a/chrome/browser/apps/app_service/metrics/app_service_metrics.cc +++ b/chrome/browser/apps/app_service/metrics/app_service_metrics.cc
@@ -248,6 +248,16 @@ } else if (app_id == arc::kGoogleTVAppId) { RecordDefaultAppLaunch(DefaultAppName::kGoogleTv, launch_source); #endif // BUILDFLAG(IS_CHROMEOS_ASH) + } else if (app_id == web_app::kGoogleCalendarAppId) { + RecordDefaultAppLaunch(DefaultAppName::kGoogleCalendar, launch_source); + } else if (app_id == web_app::kGoogleChatAppId) { + RecordDefaultAppLaunch(DefaultAppName::kGoogleChat, launch_source); + } else if (app_id == web_app::kGoogleMeetAppId) { + RecordDefaultAppLaunch(DefaultAppName::kGoogleMeet, launch_source); + } else if (app_id == web_app::kGoogleMapsAppId) { + RecordDefaultAppLaunch(DefaultAppName::kGoogleMaps, launch_source); + } else if (app_id == web_app::kMessagesAppId) { + RecordDefaultAppLaunch(DefaultAppName::kGoogleMessages, launch_source); } // Above are default apps; below are built-in apps.
diff --git a/chrome/browser/apps/app_service/metrics/app_service_metrics.h b/chrome/browser/apps/app_service/metrics/app_service_metrics.h index 3f6c0f0..3ef8d05 100644 --- a/chrome/browser/apps/app_service/metrics/app_service_metrics.h +++ b/chrome/browser/apps/app_service/metrics/app_service_metrics.h
@@ -68,9 +68,14 @@ kCalculator = 50, kFirmwareUpdateApp = 51, kGoogleTv = 52, + kGoogleCalendar = 53, + kGoogleChat = 54, + kGoogleMeet = 55, + kGoogleMaps = 56, + kGoogleMessages = 57, // Add any new values above this one, and update kMaxValue to the highest // enumerator value. - kMaxValue = kGoogleTv, + kMaxValue = kGoogleMessages, }; // The built-in app's histogram name. This is used for logging so do not change
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn index 985291e..4134863 100644 --- a/chrome/browser/ash/BUILD.gn +++ b/chrome/browser/ash/BUILD.gn
@@ -3761,6 +3761,7 @@ "//chrome/browser/autofill", "//chrome/browser/browsing_data:constants", "//chrome/browser/chromeos/drivefs", + "//chrome/browser/crash_upload_list", "//chrome/browser/devtools", "//chrome/browser/favicon", "//chrome/browser/google",
diff --git a/chrome/browser/ash/app_restore/arc_app_queue_restore_handler.h b/chrome/browser/ash/app_restore/arc_app_queue_restore_handler.h index 29df797..4c3e20e1 100644 --- a/chrome/browser/ash/app_restore/arc_app_queue_restore_handler.h +++ b/chrome/browser/ash/app_restore/arc_app_queue_restore_handler.h
@@ -59,23 +59,6 @@ kMaxValue = kNotFinish, }; -// This is usded for logging, so do not remove or reorder existing entries. -enum class NoGhostWindowReason { - kNoHandler = 0, - kNoHandlerFromCrash = 1, - kNoRootBounds = 2, - kNoRootBoundsFromCrash = 3, - kNoScreenBounds = 4, - kNoScreenBoundsFromCrash = 5, - kFlagDisabled = 6, - kNotARCVM = 7, - kNoExoHelper = 8, - - // Add any new values above this one, and update kMaxValue to the highest - // enumerator value. - kMaxValue = kNoExoHelper, -}; - // This is used for logging, so do not remove or reorder existing entries. enum class ArcRestoreState { kSuccess = 0,
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc index 9e19abb..08289466 100644 --- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc +++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
@@ -38,6 +38,7 @@ #include "ui/views/controls/focus_ring.h" #include "ui/views/controls/highlight_path_generator.h" #include "ui/views/widget/widget.h" +#include "util.h" namespace arc::input_overlay { @@ -534,6 +535,9 @@ SetEventTarget(overlay_widget, /*on_overlay=*/false); break; case DisplayMode::kEdit: + // When using Tab to traverse views and enter into the edit mode, it needs + // to reset the focus before removing the menu. + ResetFocusTo(overlay_widget->GetContentsView()); RemoveInputMenuView(); RemoveMenuEntryView(); RemoveEducationalView();
diff --git a/chrome/browser/ash/events/event_rewriter_delegate_impl.cc b/chrome/browser/ash/events/event_rewriter_delegate_impl.cc index cc2aa6e..dd5a550 100644 --- a/chrome/browser/ash/events/event_rewriter_delegate_impl.cc +++ b/chrome/browser/ash/events/event_rewriter_delegate_impl.cc
@@ -4,8 +4,11 @@ #include "chrome/browser/ash/events/event_rewriter_delegate_impl.h" +#include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" +#include "ash/public/cpp/input_device_settings_controller.h" #include "ash/public/cpp/window_properties.h" +#include "ash/public/mojom/input_device_settings.mojom.h" #include "chrome/browser/ash/login/ui/login_display_host.h" #include "chrome/browser/ash/notifications/deprecation_notification_controller.h" #include "chrome/browser/extensions/extension_commands_global_registry.h" @@ -23,14 +26,17 @@ : EventRewriterDelegateImpl( activation_client, std::make_unique<DeprecationNotificationController>( - message_center::MessageCenter::Get())) {} + message_center::MessageCenter::Get()), + InputDeviceSettingsController::Get()) {} EventRewriterDelegateImpl::EventRewriterDelegateImpl( wm::ActivationClient* activation_client, - std::unique_ptr<DeprecationNotificationController> deprecation_controller) + std::unique_ptr<DeprecationNotificationController> deprecation_controller, + InputDeviceSettingsController* input_device_settings_controller) : pref_service_for_testing_(nullptr), activation_client_(activation_client), - deprecation_controller_(std::move(deprecation_controller)) {} + deprecation_controller_(std::move(deprecation_controller)), + input_device_settings_controller_(input_device_settings_controller) {} EventRewriterDelegateImpl::~EventRewriterDelegateImpl() {} @@ -73,11 +79,20 @@ return true; } -bool EventRewriterDelegateImpl::TopRowKeysAreFunctionKeys() const { - const PrefService* pref_service = GetPrefService(); - if (!pref_service) - return false; - return pref_service->GetBoolean(prefs::kSendFunctionKeys); +bool EventRewriterDelegateImpl::TopRowKeysAreFunctionKeys(int device_id) const { + // When the flag is disabled, `device_id` is unused. + if (!ash::features::IsInputDeviceSettingsSplitEnabled()) { + const PrefService* pref_service = GetPrefService(); + if (!pref_service) { + return false; + } + return pref_service->GetBoolean(prefs::kSendFunctionKeys); + } + + const mojom::KeyboardSettings* settings = + input_device_settings_controller_->GetKeyboardSettings(device_id); + // TODO(dpad): Add metric for when settings are not able to be found. + return settings && settings->top_row_are_fkeys; } bool EventRewriterDelegateImpl::IsExtensionCommandRegistered(
diff --git a/chrome/browser/ash/events/event_rewriter_delegate_impl.h b/chrome/browser/ash/events/event_rewriter_delegate_impl.h index 23d04195..9db97ad0 100644 --- a/chrome/browser/ash/events/event_rewriter_delegate_impl.h +++ b/chrome/browser/ash/events/event_rewriter_delegate_impl.h
@@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_ASH_EVENTS_EVENT_REWRITER_DELEGATE_IMPL_H_ #define CHROME_BROWSER_ASH_EVENTS_EVENT_REWRITER_DELEGATE_IMPL_H_ +#include "ash/public/cpp/input_device_settings_controller.h" #include "ui/chromeos/events/event_rewriter_chromeos.h" #include "ui/wm/public/activation_client.h" @@ -17,9 +18,10 @@ class EventRewriterDelegateImpl : public ui::EventRewriterChromeOS::Delegate { public: explicit EventRewriterDelegateImpl(wm::ActivationClient* activation_client); - EventRewriterDelegateImpl(wm::ActivationClient* activation_client, - std::unique_ptr<DeprecationNotificationController> - deprecation_controller); + EventRewriterDelegateImpl( + wm::ActivationClient* activation_client, + std::unique_ptr<DeprecationNotificationController> deprecation_controller, + InputDeviceSettingsController* input_device_settings_controller); EventRewriterDelegateImpl(const EventRewriterDelegateImpl&) = delete; EventRewriterDelegateImpl& operator=(const EventRewriterDelegateImpl&) = @@ -36,7 +38,7 @@ bool RewriteMetaTopRowKeyComboEvents() const override; bool GetKeyboardRemappedPrefValue(const std::string& pref_name, int* result) const override; - bool TopRowKeysAreFunctionKeys() const override; + bool TopRowKeysAreFunctionKeys(int device_id) const override; bool IsExtensionCommandRegistered(ui::KeyboardCode key_code, int flags) const override; bool IsSearchKeyAcceleratorReserved() const override; @@ -60,6 +62,8 @@ // Tracks whether meta + top row key rewrites should be suppressed or not. bool suppress_meta_top_row_key_rewrites_ = false; + + raw_ptr<InputDeviceSettingsController> input_device_settings_controller_; }; } // namespace ash
diff --git a/chrome/browser/ash/events/event_rewriter_unittest.cc b/chrome/browser/ash/events/event_rewriter_unittest.cc index 9b12e0c..cd35f437 100644 --- a/chrome/browser/ash/events/event_rewriter_unittest.cc +++ b/chrome/browser/ash/events/event_rewriter_unittest.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 <memory> #include <vector> #include "ash/accelerators/accelerator_controller_impl.h" @@ -9,6 +10,9 @@ #include "ash/accessibility/sticky_keys/sticky_keys_overlay.h" #include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" +#include "ash/public/cpp/input_device_settings_controller.h" +#include "ash/public/cpp/test/mock_input_device_settings_controller.h" +#include "ash/public/mojom/input_device_settings.mojom.h" #include "ash/shell.h" #include "base/command_line.h" #include "base/containers/contains.h" @@ -52,6 +56,7 @@ #include "ui/events/test/events_test_utils.h" #include "ui/events/test/test_event_processor.h" #include "ui/events/test/test_event_rewriter_continuation.h" +#include "ui/events/types/event_type.h" #include "ui/message_center/fake_message_center.h" #include "ui/wm/core/window_util.h" @@ -204,7 +209,7 @@ std::make_unique<DeprecationNotificationController>(&message_center_); deprecation_controller_ = deprecation_controller.get(); delegate_ = std::make_unique<EventRewriterDelegateImpl>( - nullptr, std::move(deprecation_controller)); + nullptr, std::move(deprecation_controller), nullptr); delegate_->set_pref_service_for_testing(prefs()); device_data_manager_test_api_.SetKeyboardDevices({}); rewriter_ = std::make_unique<ui::EventRewriterChromeOS>( @@ -5337,7 +5342,7 @@ return true; } - bool TopRowKeysAreFunctionKeys() const override { return false; } + bool TopRowKeysAreFunctionKeys(int device_id) const override { return false; } bool IsExtensionCommandRegistered(ui::KeyboardCode key_code, int flags) const override { @@ -5729,4 +5734,53 @@ modifier_key_usage_mapping_, 0); } +class EventRewriterSettingsSplitTest : public EventRewriterTest { + public: + void SetUp() override { + EventRewriterTest::SetUp(); + scoped_feature_list_.InitAndEnableFeature( + ash::features::kInputDeviceSettingsSplit); + controller_resetter_ = std::make_unique< + InputDeviceSettingsController::ScopedResetterForTest>(); + mock_controller_ = std::make_unique<MockInputDeviceSettingsController>(); + auto deprecation_controller = + std::make_unique<DeprecationNotificationController>(&message_center_); + deprecation_controller_ = deprecation_controller.get(); + delegate_ = std::make_unique<EventRewriterDelegateImpl>( + nullptr, std::move(deprecation_controller), mock_controller_.get()); + rewriter_ = std::make_unique<ui::EventRewriterChromeOS>( + delegate_.get(), nullptr, false, &fake_ime_keyboard_); + } + + void TearDown() override { + mock_controller_.reset(); + controller_resetter_.reset(); + EventRewriterTest::TearDown(); + } + + protected: + std::unique_ptr<InputDeviceSettingsController::ScopedResetterForTest> + controller_resetter_; + std::unique_ptr<MockInputDeviceSettingsController> mock_controller_; +}; + +TEST_F(EventRewriterSettingsSplitTest, TopRowAreFKeys) { + mojom::KeyboardSettings settings; + EXPECT_CALL(*mock_controller_, GetKeyboardSettings(kKeyboardDeviceId)) + .WillRepeatedly(testing::Return(&settings)); + + settings.top_row_are_fkeys = false; + TestExternalGenericKeyboard( + {{ui::ET_KEY_PRESSED, + {ui::VKEY_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1}, + {ui::VKEY_BROWSER_BACK, ui::DomCode::BROWSER_BACK, ui::EF_NONE, + ui::DomKey::BROWSER_BACK}}}); + + settings.top_row_are_fkeys = true; + TestExternalGenericKeyboard( + {{ui::ET_KEY_PRESSED, + {ui::VKEY_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1}, + {ui::VKEY_F1, ui::DomCode::F1, ui::EF_NONE, ui::DomKey::F1}}}); +} + } // namespace ash
diff --git a/chrome/browser/ash/system/tray_accessibility_browsertest.cc b/chrome/browser/ash/system/tray_accessibility_browsertest.cc index 8858807..97b2375 100644 --- a/chrome/browser/ash/system/tray_accessibility_browsertest.cc +++ b/chrome/browser/ash/system/tray_accessibility_browsertest.cc
@@ -7,6 +7,8 @@ #include "ash/constants/ash_switches.h" #include "ash/public/cpp/ash_view_ids.h" #include "ash/public/cpp/system_tray_test_api.h" +#include "ash/system/accessibility/accessibility_detailed_view.h" +#include "ash/system/tray/tray_detailed_view.h" #include "base/functional/callback.h" #include "base/run_loop.h" #include "base/test/scoped_feature_list.h" @@ -194,7 +196,10 @@ bool IsMenuButtonVisible() { bool visible = tray_test_api_->IsBubbleViewVisible( - ash::VIEW_ID_ACCESSIBILITY_TRAY_ITEM, true /* open_tray */); + base::FeatureList::IsEnabled(ash::features::kQsRevamp) + ? ash::VIEW_ID_ACCESSIBILITY_FEATURE_TILE + : ash::VIEW_ID_ACCESSIBILITY_TRAY_ITEM, + true /* open_tray */); tray_test_api_->CloseBubble(); return visible; } @@ -206,12 +211,18 @@ void ClickVirtualKeyboardOnDetailMenu() { // Scroll the detailed view to show the virtual keyboard option. tray_test_api_->ScrollToShowView( + tray_test_api_->GetAccessibilityDetailedView() + ->scroll_view_for_testing(), ash::VIEW_ID_ACCESSIBILITY_VIRTUAL_KEYBOARD); tray_test_api_->ClickBubbleView( ash::VIEW_ID_ACCESSIBILITY_VIRTUAL_KEYBOARD); } bool IsVirtualKeyboardEnabledOnDetailMenu() const { + if (features::IsQsRevampEnabled()) { + return tray_test_api_->IsToggleOn( + ash::VIEW_ID_ACCESSIBILITY_VIRTUAL_KEYBOARD_ENABLED); + } return tray_test_api_->IsBubbleViewVisible( ash::VIEW_ID_ACCESSIBILITY_VIRTUAL_KEYBOARD_ENABLED, false /* open_tray */); @@ -571,10 +582,6 @@ } IN_PROC_BROWSER_TEST_P(TrayAccessibilityTest, KeepMenuVisibilityOnLockScreen) { - // TODO: (b/270609503) test the revapmped view. - if (base::FeatureList::IsEnabled(ash::features::kQsRevamp)) { - return; - } // Enables high contrast mode. EnableHighContrast(true); EXPECT_TRUE(IsMenuButtonVisible()); @@ -598,10 +605,6 @@ // Do not use a feature which requires an enable/disable confirmation dialog // here, as the dialogs change focus and close the detail menu. IN_PROC_BROWSER_TEST_P(TrayAccessibilityTest, DetailMenuRemainsOpen) { - // TODO: (b/270609503) test the revapmped view. - if (base::FeatureList::IsEnabled(ash::features::kQsRevamp)) { - return; - } CreateDetailedMenu(); ClickVirtualKeyboardOnDetailMenu(); @@ -640,10 +643,6 @@ IN_PROC_BROWSER_TEST_P(TrayAccessibilityLoginTest, ShowMenuWithShowOnLoginScreen) { - // TODO: (b/270609503) test the revapmped view. - if (base::FeatureList::IsEnabled(ash::features::kQsRevamp)) { - return; - } EXPECT_FALSE(user_manager::UserManager::Get()->IsUserLoggedIn()); // Confirms that the menu is visible.
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl.cc index 698fcdb..9bc0486f 100644 --- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl.cc +++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl.cc
@@ -24,6 +24,8 @@ namespace ash::personalization_app { +using DisplayType = KeyboardBacklightColorController::DisplayType; + PersonalizationAppKeyboardBacklightProviderImpl:: PersonalizationAppKeyboardBacklightProviderImpl(content::WebUI* web_ui) : profile_(Profile::FromWebUI(web_ui)) {} @@ -44,8 +46,9 @@ keyboard_backlight_observer_remote_.reset(); keyboard_backlight_observer_remote_.Bind(std::move(observer)); - // Call it once to get the status of color preset. - NotifyBacklightColorChanged(); + // Call it once to get the current status of backlight state (backlight color + // is either static color or multi-zone colors). + NotifyBacklightStateChanged(); // Bind wallpaper observer now that rgb keyboard section is ready and visible // to users. @@ -70,7 +73,10 @@ ->keyboard_backlight_color_nudge_controller() ->SetUserPerformedAction(); - NotifyBacklightColorChanged(); + // Get the current status of backlight state as backlight color has changed. + // Notifies backlight changed to a static color or rainbow color to highlight + // the selected state of color icon button. + NotifyBacklightStateChanged(); } void PersonalizationAppKeyboardBacklightProviderImpl::SetBacklightZoneColor( @@ -92,11 +98,10 @@ ->keyboard_backlight_color_nudge_controller() ->SetUserPerformedAction(); - // Notifies backlight changed to |kMultizone| to highlight the selected state - // of customization button. - NotifyBacklightColorChanged(); - - // TODO(b/265855838): Notify backlight zone colors have changed. + // Get the current status of backlight state as backlight color has changed to + // zone colors. Notifies backlight changed to |kMultizone| to highlight the + // selected state of customization button. + NotifyBacklightStateChanged(); } void PersonalizationAppKeyboardBacklightProviderImpl::ShouldShowNudge( @@ -135,11 +140,40 @@ } void PersonalizationAppKeyboardBacklightProviderImpl:: + NotifyBacklightStateChanged() { + const auto displayType = + GetKeyboardBacklightColorController()->GetDisplayType( + GetAccountId(profile_)); + switch (displayType) { + case DisplayType::kStatic: { + NotifyBacklightColorChanged(); + return; + } + case DisplayType::kMultiZone: { + NotifyBacklightZoneColorsChanged(); + return; + } + } +} + +void PersonalizationAppKeyboardBacklightProviderImpl:: NotifyBacklightColorChanged() { DCHECK(keyboard_backlight_observer_remote_.is_bound()); - keyboard_backlight_observer_remote_->OnBacklightColorChanged( - GetKeyboardBacklightColorController()->GetBacklightColor( - GetAccountId(profile_))); + + keyboard_backlight_observer_remote_->OnBacklightStateChanged( + ash::personalization_app::mojom::CurrentBacklightState::NewColor( + GetKeyboardBacklightColorController()->GetBacklightColor( + GetAccountId(profile_)))); +} + +void PersonalizationAppKeyboardBacklightProviderImpl:: + NotifyBacklightZoneColorsChanged() { + DCHECK(keyboard_backlight_observer_remote_.is_bound()); + + keyboard_backlight_observer_remote_->OnBacklightStateChanged( + ash::personalization_app::mojom::CurrentBacklightState::NewZoneColors( + GetKeyboardBacklightColorController()->GetBacklightZoneColors( + GetAccountId(profile_)))); } } // namespace ash::personalization_app
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl.h b/chrome/browser/ash/web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl.h index 7534762..f0fbf9f1 100644 --- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl.h +++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl.h
@@ -64,9 +64,15 @@ private: KeyboardBacklightColorController* GetKeyboardBacklightColorController(); + // Notify webUI the current status of backlight state. + void NotifyBacklightStateChanged(); + // Notify webUI the current state of backlight color. void NotifyBacklightColorChanged(); + // Notify webUI the current state of backlight zone colors. + void NotifyBacklightZoneColorsChanged(); + // Pointer to profile of user that opened personalization SWA. Not owned. raw_ptr<Profile> const profile_ = nullptr;
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl_unittest.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl_unittest.cc index d73140f..4bdb9b0 100644 --- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl_unittest.cc +++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl_unittest.cc
@@ -5,6 +5,7 @@ #include "chrome/browser/ash/web_applications/personalization_app/personalization_app_keyboard_backlight_provider_impl.h" #include <memory> +#include <vector> #include "ash/constants/ash_features.h" #include "ash/system/keyboard_brightness/keyboard_backlight_color_controller.h" @@ -32,8 +33,9 @@ class TestKeyboardBacklightObserver : public ash::personalization_app::mojom::KeyboardBacklightObserver { public: - void OnBacklightColorChanged(mojom::BacklightColor backlight_color) override { - backlight_color_ = backlight_color; + void OnBacklightStateChanged( + mojom::CurrentBacklightStatePtr current_backlight_state) override { + current_backlight_state_ = std::move(current_backlight_state); } void OnWallpaperColorChanged(SkColor wallpaper_color) override { @@ -50,9 +52,9 @@ return keyboard_backlight_observer_receiver_.BindNewPipeAndPassRemote(); } - mojom::BacklightColor backlight_color() { + mojom::CurrentBacklightState* current_backlight_state() { keyboard_backlight_observer_receiver_.FlushForTesting(); - return backlight_color_; + return current_backlight_state_.get(); } SkColor wallpaper_color() { @@ -63,8 +65,8 @@ private: mojo::Receiver<ash::personalization_app::mojom::KeyboardBacklightObserver> keyboard_backlight_observer_receiver_{this}; - - mojom::BacklightColor backlight_color_ = mojom::BacklightColor::kWallpaper; + mojom::CurrentBacklightStatePtr current_backlight_state_ = + mojom::CurrentBacklightState::NewColor(mojom::BacklightColor::kRed); SkColor wallpaper_color_ = SK_ColorTRANSPARENT; }; @@ -76,7 +78,9 @@ PersonalizationAppKeyboardBacklightProviderImplTest() : scoped_user_manager_(std::make_unique<ash::FakeChromeUserManager>()), profile_manager_(TestingBrowserProcess::GetGlobal()) { - scoped_feature_list_.InitWithFeatures({ash::features::kRgbKeyboard}, {}); + scoped_feature_list_.InitWithFeatures( + {ash::features::kRgbKeyboard, ash::features::kMultiZoneRgbKeyboard}, + {}); } PersonalizationAppKeyboardBacklightProviderImplTest( const PersonalizationAppKeyboardBacklightProviderImplTest&) = delete; @@ -140,9 +144,9 @@ test_keyboard_backlight_observer_.pending_remote()); } - mojom::BacklightColor ObservedBacklightColor() { + mojom::CurrentBacklightState* ObservedBacklightColor() { keyboard_backlight_provider_remote_.FlushForTesting(); - return test_keyboard_backlight_observer_.backlight_color(); + return test_keyboard_backlight_observer_.current_backlight_state(); } SkColor ObservedWallpaperColor() { @@ -175,7 +179,9 @@ mojom::BacklightColor::kBlue); // Verify JS side is notified. - EXPECT_EQ(mojom::BacklightColor::kBlue, ObservedBacklightColor()); + EXPECT_TRUE(ObservedBacklightColor()->is_color()); + EXPECT_EQ(mojom::BacklightColor::kBlue, + ObservedBacklightColor()->get_color()); histogram_tester().ExpectBucketCount( kPersonalizationKeyboardBacklightColorHistogramName, mojom::BacklightColor::kBlue, 1);
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc index d494a6c..0298eba1 100644 --- a/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc +++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc
@@ -187,10 +187,7 @@ const std::string& script, content::WebContents* web_contents) { EXPECT_TRUE(web_contents); - bool data; - EXPECT_TRUE( - content::ExecuteScriptAndExtractBool(web_contents, script, &data)); - return data; + return content::EvalJs(web_contents, script).ExtractBool(); } void BrowsingDataRemoverBrowserTestBase::VerifyDownloadCount(size_t expected,
diff --git a/chrome/browser/chrome_web_platform_security_metrics_browsertest.cc b/chrome/browser/chrome_web_platform_security_metrics_browsertest.cc index 20f3b875..223c4be 100644 --- a/chrome/browser/chrome_web_platform_security_metrics_browsertest.cc +++ b/chrome/browser/chrome_web_platform_security_metrics_browsertest.cc
@@ -2136,6 +2136,132 @@ CheckCounter(WebFeature::kExecutedJavaScriptURLFromFrame, 0); } +IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest, + DanglingMarkupInIframeName) { + GURL url = https_server().GetURL("a.test", "/empty.html"); + EXPECT_TRUE(content::NavigateToURL(web_contents(), url)); + EXPECT_TRUE(content::ExecJs(web_contents(), R"( + new Promise(resolve => { + let iframe = document.createElement("iframe"); + iframe.src = '/empty.html'; + iframe.name = "<\n>"; + iframe.onload = resolve; + document.body.appendChild(iframe); + }); + )")); + CheckCounter(WebFeature::kDanglingMarkupInWindowName, 1); + CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithNewLineOrGT, + 0); + CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithGT, 0); + CheckCounter(WebFeature::kDanglingMarkupInTarget, 0); + CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithGT, 0); + CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithNewLineOrGT, 0); +} + +IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest, + DanglingMarkupInNameWithGreaterThan) { + GURL url = https_server().GetURL("a.test", "/empty.html"); + EXPECT_TRUE(content::NavigateToURL(web_contents(), url)); + EXPECT_TRUE(content::ExecJs(web_contents(), R"( + new Promise(resolve => { + let iframe = document.createElement("iframe"); + iframe.src = '/empty.html'; + iframe.name = "<\n"; + iframe.onload = resolve; + document.body.appendChild(iframe); + }); + )")); + CheckCounter(WebFeature::kDanglingMarkupInWindowName, 1); + CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithNewLineOrGT, + 0); + CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithGT, 1); + CheckCounter(WebFeature::kDanglingMarkupInTarget, 0); + CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithGT, 0); + CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithNewLineOrGT, 0); +} + +IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest, + DanglingMarkupInNameWithNewLineOrGreaterThan) { + GURL url = https_server().GetURL("a.test", "/empty.html"); + EXPECT_TRUE(content::NavigateToURL(web_contents(), url)); + EXPECT_TRUE(content::ExecJs(web_contents(), R"( + new Promise(resolve => { + let iframe = document.createElement("iframe"); + iframe.src = '/empty.html'; + iframe.name = "<\ntest"; + iframe.onload = resolve; + document.body.appendChild(iframe); + }); + )")); + + CheckCounter(WebFeature::kDanglingMarkupInWindowName, 1); + CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithNewLineOrGT, + 1); + CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithGT, 1); + CheckCounter(WebFeature::kDanglingMarkupInTarget, 0); + CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithGT, 0); + CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithNewLineOrGT, 0); +} + +IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest, + DanglingMarkupInTarget) { + GURL url = https_server().GetURL("a.test", "/empty.html"); + EXPECT_TRUE(content::NavigateToURL(web_contents(), url)); + EXPECT_TRUE(content::ExecJs(web_contents(), R"( + let link = document.createElement("a"); + link.href = '/empty.html'; + link.target = "<\n>"; + document.body.appendChild(link); + link.click(); + )")); + + CheckCounter(WebFeature::kDanglingMarkupInWindowName, 0); + CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithNewLineOrGT, + 0); + CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithGT, 0); + CheckCounter(WebFeature::kDanglingMarkupInTarget, 1); + CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithGT, 0); + CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithNewLineOrGT, 0); +} + +IN_PROC_BROWSER_TEST_F(ChromeWebPlatformSecurityMetricsBrowserTest, + DanglingMarkupInTargetWithNewLineOrGreaterThan) { + GURL url = https_server().GetURL("a.test", "/empty.html"); + EXPECT_TRUE(content::NavigateToURL(web_contents(), url)); + EXPECT_TRUE(content::ExecJs(web_contents(), R"( + let link = document.createElement("a"); + link.href = '/empty.html'; + link.target = "<\n"; + document.body.appendChild(link); + link.click(); + )")); + + CheckCounter(WebFeature::kDanglingMarkupInWindowName, 0); + CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithNewLineOrGT, + 0); + CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithGT, 0); + CheckCounter(WebFeature::kDanglingMarkupInTarget, 1); + CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithGT, 1); + CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithNewLineOrGT, 0); + + EXPECT_TRUE(content::ExecJs(web_contents(), R"( + let base = document.createElement("base"); + base.target = "<\ntest"; + document.body.appendChild(base); + let link = document.createElement("a"); + link.href = '/empty.html'; + document.body.appendChild(link); + link.click(); + )")); + CheckCounter(WebFeature::kDanglingMarkupInWindowName, 0); + CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithNewLineOrGT, + 0); + CheckCounter(WebFeature::kDanglingMarkupInWindowNameNotEndsWithGT, 0); + CheckCounter(WebFeature::kDanglingMarkupInTarget, 2); + CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithGT, 2); + CheckCounter(WebFeature::kDanglingMarkupInTargetNotEndsWithNewLineOrGT, 1); +} + // TODO(arthursonzogni): Add basic test(s) for the WebFeatures: // [ ] CrossOriginOpenerPolicySameOrigin // [ ] CrossOriginOpenerPolicySameOriginAllowPopups
diff --git a/chrome/browser/content_settings/content_settings_browsertest.cc b/chrome/browser/content_settings/content_settings_browsertest.cc index 050838a..1763b88 100644 --- a/chrome/browser/content_settings/content_settings_browsertest.cc +++ b/chrome/browser/content_settings/content_settings_browsertest.cc
@@ -1645,8 +1645,7 @@ "LocalStorage", "SessionStorage", "CacheStorage", "FileSystem", "FileSystemAccess", "IndexedDb", "SharedWorker", "ServiceWorker"}; for (auto storage_type : storage_types_to_test) { - EXPECT_TRUE(content::EvalJs(fenced_frame, "set" + storage_type + "();", - content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) + EXPECT_TRUE(content::EvalJs(fenced_frame, "set" + storage_type + "();") .ExtractBool()); }
diff --git a/chrome/browser/content_settings/page_specific_content_settings_delegate.cc b/chrome/browser/content_settings/page_specific_content_settings_delegate.cc index 3111c4d..fc14ee8 100644 --- a/chrome/browser/content_settings/page_specific_content_settings_delegate.cc +++ b/chrome/browser/content_settings/page_specific_content_settings_delegate.cc
@@ -22,6 +22,7 @@ #include "chrome/common/renderer_configuration.mojom.h" #include "components/content_settings/browser/page_specific_content_settings.h" #include "components/content_settings/core/common/content_settings_utils.h" +#include "components/permissions/permission_recovery_success_rate_tracker.h" #if BUILDFLAG(ENABLE_EXTENSIONS) #include "components/guest_view/browser/guest_view_base.h" #endif @@ -80,6 +81,33 @@ void PageSpecificContentSettingsDelegate::UpdateLocationBar() { content_settings::UpdateLocationBarUiForWebContents(web_contents()); + + PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame( + web_contents()->GetPrimaryMainFrame()); + + if (pscs == nullptr) { + // There are cases, e.g. MPArch, where there is no active instance of + // PageSpecificContentSettings for a frame. + return; + } + + PageSpecificContentSettings::MicrophoneCameraState state = + pscs->GetMicrophoneCameraState(); + + if ((state & PageSpecificContentSettings::CAMERA_ACCESSED) || + (state & PageSpecificContentSettings::MICROPHONE_ACCESSED)) { + auto* permission_tracker = + permissions::PermissionRecoverySuccessRateTracker::FromWebContents( + web_contents()); + + if (state & PageSpecificContentSettings::MICROPHONE_ACCESSED) { + permission_tracker->TrackUsage(ContentSettingsType::MEDIASTREAM_MIC); + } + + if (state & PageSpecificContentSettings::CAMERA_ACCESSED) { + permission_tracker->TrackUsage(ContentSettingsType::MEDIASTREAM_CAMERA); + } + } } PrefService* PageSpecificContentSettingsDelegate::GetPrefs() {
diff --git a/chrome/browser/content_settings/page_specific_content_settings_unittest.cc b/chrome/browser/content_settings/page_specific_content_settings_unittest.cc index cd9b7e5..15dd255 100644 --- a/chrome/browser/content_settings/page_specific_content_settings_unittest.cc +++ b/chrome/browser/content_settings/page_specific_content_settings_unittest.cc
@@ -9,6 +9,7 @@ #include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/content_settings/page_specific_content_settings_delegate.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" +#include "components/permissions/permission_recovery_success_rate_tracker.h" #include "content/public/browser/web_contents.h" #include "testing/gmock/include/gmock/gmock-matchers.h" @@ -26,6 +27,9 @@ web_contents(), std::make_unique<chrome::PageSpecificContentSettingsDelegate>( web_contents())); + + permissions::PermissionRecoverySuccessRateTracker::CreateForWebContents( + web_contents()); } };
diff --git a/chrome/browser/crash_upload_list/BUILD.gn b/chrome/browser/crash_upload_list/BUILD.gn new file mode 100644 index 0000000..da014a87 --- /dev/null +++ b/chrome/browser/crash_upload_list/BUILD.gn
@@ -0,0 +1,70 @@ +# Copyright 2023 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/buildflag_header.gni") +import("//extensions/buildflags/buildflags.gni") +import("//testing/test.gni") + +static_library("crash_upload_list") { + sources = [ + "crash_upload_list.cc", + "crash_upload_list.h", + ] + + if (is_android) { + sources += [ + "crash_upload_list_android.cc", + "crash_upload_list_android.h", + ] + } + + if (is_chromeos) { + sources += [ + "crash_upload_list_chromeos.cc", + "crash_upload_list_chromeos.h", + ] + } + + if (is_fuchsia) { + sources += [ + "crash_upload_list_fuchsia.cc", + "crash_upload_list_fuchsia.h", + ] + } + + deps = [ + "//chrome/common:constants", + "//components/crash/core/browser:browser", + "//components/upload_list", + "//ui/base:base", + ] + + if (is_android) { + deps += [ "//chrome/android:chrome_jni_headers" ] + } + + if (!is_fuchsia) { + deps += [ "//components/crash/core/app:app" ] + } + + public_deps = [ + "//base", + "//third_party/abseil-cpp:absl", + ] +} + +source_set("unit_tests") { + testonly = true + + if (is_chromeos) { + sources = [ "crash_upload_list_chromeos_unittest.cc" ] + } + + deps = [ + ":crash_upload_list", + "//base/test:test_support", + "//testing/gmock", + "//testing/gtest", + ] +}
diff --git a/chrome/browser/error_reporting/BUILD.gn b/chrome/browser/error_reporting/BUILD.gn index 7eff1312..8b041701 100644 --- a/chrome/browser/error_reporting/BUILD.gn +++ b/chrome/browser/error_reporting/BUILD.gn
@@ -72,6 +72,7 @@ ":error_reporting", ":test_support", "//base", + "//chrome/browser/crash_upload_list", "//chrome/test:test_support", "//components/crash/content/browser/error_reporting", "//components/crash/content/browser/error_reporting:mock_crash_endpoint",
diff --git a/chrome/browser/extensions/api/history/history_apitest.cc b/chrome/browser/extensions/api/history/history_apitest.cc index b26ec518..a204862 100644 --- a/chrome/browser/extensions/api/history/history_apitest.cc +++ b/chrome/browser/extensions/api/history/history_apitest.cc
@@ -13,23 +13,11 @@ #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_navigation_observer.h" +#include "extensions/browser/background_script_executor.h" #include "extensions/browser/process_manager.h" #include "extensions/test/extension_test_message_listener.h" #include "net/dns/mock_host_resolver.h" -namespace { - -std::string RunScriptAndReturnResult(const extensions::ExtensionHost* host, - const std::string& script) { - std::string result; - EXPECT_TRUE(content::ExecuteScriptAndExtractString(host->host_contents(), - script, &result)) - << script; - return result; -} - -} // namespace - namespace extensions { class HistoryApiTest : public ExtensionApiTest { @@ -40,50 +28,49 @@ host_resolver()->AddRule("www.a.com", "127.0.0.1"); host_resolver()->AddRule("www.b.com", "127.0.0.1"); } + + static std::string ExecuteScript(const ExtensionId& extension_id, + content::BrowserContext* context, + const std::string& script) { + base::Value result = BackgroundScriptExecutor::ExecuteScript( + context, extension_id, script, + BackgroundScriptExecutor::ResultCapture::kSendScriptResult); + EXPECT_TRUE(result.is_string()); + return result.is_string() ? result.GetString() : std::string(); + } }; IN_PROC_BROWSER_TEST_F(HistoryApiTest, MiscSearch) { ASSERT_TRUE(StartEmbeddedTestServer()); - ASSERT_TRUE(RunExtensionTest("history/regular", - {.extension_url = "misc_search.html"})) - << message_; + ASSERT_TRUE(RunExtensionTest("history/regular/misc_search")) << message_; } IN_PROC_BROWSER_TEST_F(HistoryApiTest, TimedSearch) { ASSERT_TRUE(StartEmbeddedTestServer()); - ASSERT_TRUE(RunExtensionTest("history/regular", - {.extension_url = "timed_search.html"})) - << message_; + ASSERT_TRUE(RunExtensionTest("history/regular/timed_search")) << message_; } IN_PROC_BROWSER_TEST_F(HistoryApiTest, Delete) { ASSERT_TRUE(StartEmbeddedTestServer()); - ASSERT_TRUE( - RunExtensionTest("history/regular", {.extension_url = "delete.html"})) - << message_; + ASSERT_TRUE(RunExtensionTest("history/regular/delete")) << message_; } IN_PROC_BROWSER_TEST_F(HistoryApiTest, DeleteProhibited) { browser()->profile()->GetPrefs()-> SetBoolean(prefs::kAllowDeletingBrowserHistory, false); ASSERT_TRUE(StartEmbeddedTestServer()); - ASSERT_TRUE(RunExtensionTest("history/regular", - {.extension_url = "delete_prohibited.html"})) + ASSERT_TRUE(RunExtensionTest("history/regular/delete_prohibited")) << message_; } IN_PROC_BROWSER_TEST_F(HistoryApiTest, GetVisits) { ASSERT_TRUE(StartEmbeddedTestServer()); - ASSERT_TRUE( - RunExtensionTest("history/regular", {.extension_url = "get_visits.html"})) - << message_; + ASSERT_TRUE(RunExtensionTest("history/regular/get_visits")) << message_; } IN_PROC_BROWSER_TEST_F(HistoryApiTest, SearchAfterAdd) { ASSERT_TRUE(StartEmbeddedTestServer()); - ASSERT_TRUE(RunExtensionTest("history/regular", - {.extension_url = "search_after_add.html"})) - << message_; + ASSERT_TRUE(RunExtensionTest("history/regular/search_after_add")) << message_; } // Test when History API is used from incognito mode, it has access to the @@ -92,6 +79,7 @@ ASSERT_TRUE(StartEmbeddedTestServer()); // Setup. Browser* incognito_browser = CreateIncognitoBrowser(browser()->profile()); + Profile* incognito_profile = incognito_browser->profile(); ExtensionTestMessageListener regular_listener("regular ready"); ExtensionTestMessageListener incognito_listener("incognito ready"); const Extension* extension = @@ -101,32 +89,23 @@ ASSERT_TRUE(regular_listener.WaitUntilSatisfied()); ASSERT_TRUE(incognito_listener.WaitUntilSatisfied()); - ExtensionHost* on_the_record_background_page = - ProcessManager::Get(browser()->profile()) - ->GetBackgroundHostForExtension(extension->id()); - ASSERT_TRUE(on_the_record_background_page); - - ExtensionHost* incognito_background_page = - ProcessManager::Get(incognito_browser->profile()) - ->GetBackgroundHostForExtension(extension->id()); - ASSERT_TRUE(incognito_background_page); - EXPECT_NE(incognito_background_page, on_the_record_background_page); + const ExtensionId& extension_id = extension->id(); // Check if history is empty in regular mode. - EXPECT_EQ("0", RunScriptAndReturnResult(on_the_record_background_page, - "countItemsInHistory()")); + EXPECT_EQ("0", + ExecuteScript(extension_id, profile(), "countItemsInHistory()")); // Insert an item in incognito mode. - EXPECT_EQ(std::string("success"), - RunScriptAndReturnResult(incognito_background_page, "addItem()")); + EXPECT_EQ("success", + ExecuteScript(extension_id, incognito_profile, "addItem()")); // Check history in incognito mode. - EXPECT_EQ("1", RunScriptAndReturnResult(on_the_record_background_page, - "countItemsInHistory()")); + EXPECT_EQ("1", ExecuteScript(extension_id, incognito_profile, + "countItemsInHistory()")); // Check history in regular mode. - EXPECT_EQ("1", RunScriptAndReturnResult(on_the_record_background_page, - "countItemsInHistory()")); + EXPECT_EQ("1", + ExecuteScript(extension_id, profile(), "countItemsInHistory()")); // Perform navigation in incognito mode. const GURL b_com = @@ -137,13 +116,13 @@ EXPECT_TRUE(incognito_observer.last_navigation_succeeded()); // Check history in regular mode is not modified by incognito navigation. - EXPECT_EQ("1", RunScriptAndReturnResult(on_the_record_background_page, - "countItemsInHistory()")); + EXPECT_EQ("1", + ExecuteScript(extension_id, profile(), "countItemsInHistory()")); // Check that history in incognito mode is not modified by navigation as // incognito navigations are not recorded in history. - EXPECT_EQ("1", RunScriptAndReturnResult(incognito_background_page, - "countItemsInHistory()")); + EXPECT_EQ("1", ExecuteScript(extension_id, incognito_profile, + "countItemsInHistory()")); // Perform navigation in regular mode. content::TestNavigationObserver regular_observer( @@ -152,12 +131,12 @@ EXPECT_TRUE(regular_observer.last_navigation_succeeded()); // Check history in regular mode is modified by navigation. - EXPECT_EQ("2", RunScriptAndReturnResult(on_the_record_background_page, - "countItemsInHistory()")); + EXPECT_EQ("2", + ExecuteScript(extension_id, profile(), "countItemsInHistory()")); // Check history in incognito mode is modified by navigation. - EXPECT_EQ("2", RunScriptAndReturnResult(incognito_background_page, - "countItemsInHistory()")); + EXPECT_EQ("2", ExecuteScript(extension_id, incognito_profile, + "countItemsInHistory()")); } } // namespace extensions
diff --git a/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc b/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc index 77e14b49..b565437 100644 --- a/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc +++ b/chrome/browser/extensions/api/permissions/permissions_api_helpers_unittest.cc
@@ -36,8 +36,6 @@ TEST(ExtensionPermissionsAPIHelpers, Pack) { APIPermissionSet apis; apis.insert(APIPermissionID::kTab); - apis.insert(APIPermissionID::kFileBrowserHandler); - // Note: kFileBrowserHandler implies kFileBrowserHandlerInternal. URLPatternSet explicit_hosts( {URLPattern(Extension::kValidHostPermissionSchemes, "http://a.com/*"), @@ -52,9 +50,7 @@ std::move(explicit_hosts), std::move(scriptable_hosts)))); ASSERT_TRUE(pack_result); ASSERT_TRUE(pack_result->permissions); - EXPECT_THAT(*pack_result->permissions, - testing::UnorderedElementsAre("tabs", "fileBrowserHandler", - "fileBrowserHandlerInternal")); + EXPECT_THAT(*pack_result->permissions, testing::UnorderedElementsAre("tabs")); ASSERT_TRUE(pack_result->origins); EXPECT_THAT(*pack_result->origins, testing::UnorderedElementsAre(
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 0025e3f..7141e8a3 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -874,11 +874,6 @@ "expiry_milestone": 118 }, { - "name": "calendar-view", - "owners": ["jiamingc", "cros-status-area-eng@google.com"], - "expiry_milestone" : 106 - }, - { "name": "calendar-view-debug-mode", "owners": [ "rtinkoff" ], "expiry_milestone": 113 @@ -4399,11 +4394,6 @@ "expiry_milestone": 95 }, { - "name": "interest-feed-v2-clicks-and-views-cond-upload", - "owners": [ "//chrome/android/feed/OWNERS", "feed@chromium.org", "edchin@chromium.org" ], - "expiry_milestone": 95 - }, - { "name": "ios-autofill-branding", "owners": [ "ginnyhuang@google.com", "bling-flags@google.com" ], "expiry_milestone": 115 @@ -5141,6 +5131,11 @@ "expiry_milestone": 120 }, { + "name": "omnibox-adapt-narrow-tablet-windows", + "owners": [ "pnoland", "chrome-omnibox-team@google.com" ], + "expiry_milestone": 120 + }, + { "name": "omnibox-adaptive-suggestions-count", "owners": [ "stkhapugin", "chrome-omnibox-team@google.com" ], "expiry_milestone": 120
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index ea95fa7..df8b613 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -2034,6 +2034,12 @@ "flag to adjust the limit of offered suggestions. The number of shown " "suggestions will be no less than the platform default limit."; +const char kOmniboxAdaptNarrowTabletWindowsName[] = + "Omnibox adapts to narrow tablet windows"; +const char kOmniboxAdaptNarrowTabletWindowsDescription[] = + "When enabled the omnibox uses a phone-like appearance for windows that " + "are less than 600dp wide, regardless of the overall display width."; + const char kOmniboxAssistantVoiceSearchName[] = "Omnibox Assistant Voice Search"; const char kOmniboxAssistantVoiceSearchDescription[] = @@ -4972,12 +4978,6 @@ const char kCalendarJellyDescription[] = "Enables Jelly changes for the sys tray Calendar views."; -const char kCalendarViewName[] = - "Productivity experiment: Monthly Calendar View"; -const char kCalendarViewDescription[] = - "Show Monthly Calendar View with Google Calendar events to increase " - "productivity by helping users view their schedules more quickly."; - const char kCaptureModeDemoToolsName[] = "Enable demo tools feature in screen capture"; const char kCaptureModeDemoToolsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 8bbcc8e3..895b160 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -1146,6 +1146,9 @@ extern const char kOmniboxAdaptiveSuggestionsCountName[]; extern const char kOmniboxAdaptiveSuggestionsCountDescription[]; +extern const char kOmniboxAdaptNarrowTabletWindowsName[]; +extern const char kOmniboxAdaptNarrowTabletWindowsDescription[]; + extern const char kOmniboxAssistantVoiceSearchName[]; extern const char kOmniboxAssistantVoiceSearchDescription[]; @@ -2859,9 +2862,6 @@ extern const char kCalendarJellyName[]; extern const char kCalendarJellyDescription[]; -extern const char kCalendarViewName[]; -extern const char kCalendarViewDescription[]; - extern const char kCalendarModelDebugModeName[]; extern const char kCalendarModelDebugModeDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc index b23a4da..0784bd4a 100644 --- a/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -245,6 +245,7 @@ &kBackGestureRefactorActivityAndroid, &kBackGestureRefactorAndroid, &kOmahaMinSdkVersionAndroid, + &kOmniboxAdaptNarrowTabletWindows, &kOmniboxConsumesImeInsets, &kOmniboxModernizeVisualUpdate, &kOpaqueOriginForIncomingIntents, @@ -784,6 +785,10 @@ "OmahaMinSdkVersionAndroid", base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kOmniboxAdaptNarrowTabletWindows, + "OmniboxAdaptNarrowTabletWindows", + base::FEATURE_DISABLED_BY_DEFAULT); + BASE_FEATURE(kOmniboxConsumesImeInsets, "OmniboxConsumesImeInsets", base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h index f777837..02f0266 100644 --- a/chrome/browser/flags/android/chrome_feature_list.h +++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -106,6 +106,7 @@ BASE_DECLARE_FEATURE(kNotificationPermissionVariant); BASE_DECLARE_FEATURE(kNotificationPermissionBottomSheet); BASE_DECLARE_FEATURE(kOmahaMinSdkVersionAndroid); +BASE_DECLARE_FEATURE(kOmniboxAdaptNarrowTabletWindows); BASE_DECLARE_FEATURE(kOmniboxConsumesImeInsets); BASE_DECLARE_FEATURE(kOmniboxModernizeVisualUpdate); BASE_DECLARE_FEATURE(kOptimizeGeolocationHeaderGeneration);
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 b3d4715..053e8b2 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
@@ -377,6 +377,8 @@ public static final String OFFLINE_PAGES_LIVE_PAGE_SHARING = "OfflinePagesLivePageSharing"; public static final String OFFLINE_PAGES_PREFETCHING = "OfflinePagesPrefetching"; public static final String OMAHA_MIN_SDK_VERSION_ANDROID = "OmahaMinSdkVersionAndroid"; + public static final String OMNIBOX_ADAPT_NARROW_TABLET_WINDOWS = + "OmniboxAdaptNarrowTabletWindows"; public static final String OMNIBOX_ASSISTANT_VOICE_SEARCH = "OmniboxAssistantVoiceSearch"; public static final String OMNIBOX_CONSUMERS_IME_INSETS = "OmniboxConsumesImeInsets"; public static final String OMNIBOX_MATCH_TOOLBAR_AND_STATUS_BAR_COLOR =
diff --git a/chrome/browser/media/router/mojo/media_router_debugger_impl_unittest.cc b/chrome/browser/media/router/mojo/media_router_debugger_impl_unittest.cc index 8d43983..b9d8628 100644 --- a/chrome/browser/media/router/mojo/media_router_debugger_impl_unittest.cc +++ b/chrome/browser/media/router/mojo/media_router_debugger_impl_unittest.cc
@@ -7,7 +7,6 @@ #include "base/test/gmock_callback_support.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/mock_callback.h" -#include "base/test/scoped_feature_list.h" #include "chrome/browser/media/router/mojo/media_router_mojo_impl.h" #include "chrome/browser/media/router/test/provider_test_helpers.h" #include "chrome/test/base/testing_profile.h" @@ -114,8 +113,6 @@ TEST_F(MediaRouterDebuggerImplTest, NonMirroringRoutes) { debugger()->EnableRtcpReports(); - base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatures({media::kEnableRtcpReporting}, {}); const std::vector<MediaRoute> routes{CreateMediaRoute()}; EXPECT_CALL(observer_, OnMirroringStatsUpdated(_)).Times(0); @@ -124,8 +121,6 @@ TEST_F(MediaRouterDebuggerImplTest, FetchMirroringStats) { debugger()->EnableRtcpReports(); - base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatures({media::kEnableRtcpReporting}, {}); const std::vector<MediaRoute> routes{CreateTabMirroringMediaRoute()}; EXPECT_CALL(observer_, OnMirroringStatsUpdated(_)).Times(1);
diff --git a/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc b/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc index 5974afb..f6e6f45 100644 --- a/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc +++ b/chrome/browser/navigation_predictor/anchor_element_preloader_browsertest.cc
@@ -1,6 +1,8 @@ // 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 "base/metrics/field_trial_params.h" #include "base/run_loop.h" #include "base/task/single_thread_task_runner.h" #include "base/test/metrics/histogram_tester.h" @@ -41,13 +43,16 @@ static constexpr char kOrigin1[] = "https://www.origin1.com/"; static constexpr char kOrigin2[] = "https://www.origin2.com/"; - virtual void SetFeatures() { - feature_list_.InitAndEnableFeature( - blink::features::kAnchorElementInteraction); + virtual base::FieldTrialParams GetAnchorElementInteractionFieldTrialParams() { + return {}; } void SetUp() override { - SetFeatures(); + feature_list_.InitWithFeaturesAndParameters( + {{blink::features::kAnchorElementInteraction, + GetAnchorElementInteractionFieldTrialParams()}, + {blink::features::kSpeculationRulesPointerDownHeuristics, {}}}, + {blink::features::kSpeculationRulesPointerHoverHeuristics}); https_server_ = std::make_unique<net::EmbeddedTestServer>( net::EmbeddedTestServer::TYPE_HTTPS); https_server_->ServeFilesFromSourceDirectory("chrome/test/data/preload"); @@ -370,10 +375,9 @@ class AnchorElementPreloaderHoldbackBrowserTest : public AnchorElementPreloaderBrowserTest { public: - void SetFeatures() override { - feature_list_.InitAndEnableFeatureWithParameters( - blink::features::kAnchorElementInteraction, - {{"preconnect_holdback", "true"}}); + base::FieldTrialParams GetAnchorElementInteractionFieldTrialParams() + override { + return {{"preconnect_holdback", "true"}}; } }; @@ -422,10 +426,9 @@ class AnchorElementPreloaderLimitedBrowserTest : public AnchorElementPreloaderBrowserTest { public: - void SetFeatures() override { - feature_list_.InitAndEnableFeatureWithParameters( - blink::features::kAnchorElementInteraction, - {{"max_preloading_attempts", "1"}}); + base::FieldTrialParams GetAnchorElementInteractionFieldTrialParams() + override { + return {{"max_preloading_attempts", "1"}}; } };
diff --git a/chrome/browser/net/storage_test_utils.cc b/chrome/browser/net/storage_test_utils.cc index 756b97f..8532453 100644 --- a/chrome/browser/net/storage_test_utils.cc +++ b/chrome/browser/net/storage_test_utils.cc
@@ -55,9 +55,7 @@ base::flat_map<std::string, bool> expected; for (const auto& data_type : GetStorageTypesForFrame(include_cookies)) { actual[data_type] = - content::EvalJs(frame, "set" + data_type + "()", - content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) - .ExtractBool(); + content::EvalJs(frame, "set" + data_type + "()").ExtractBool(); if (frame->GetLastCommittedOrigin() != frame->GetMainFrame()->GetLastCommittedOrigin() && data_type == "WebSql") { @@ -75,9 +73,7 @@ base::flat_map<std::string, bool> expected; for (const auto& data_type : kStorageTypesForWorker) { actual[data_type] = - content::EvalJs(frame, "set" + data_type + "()", - content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) - .ExtractBool(); + content::EvalJs(frame, "set" + data_type + "()").ExtractBool(); expected[data_type] = true; } EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected)); @@ -90,9 +86,7 @@ base::flat_map<std::string, bool> expected_elts; for (const auto& data_type : GetStorageTypesForFrame(include_cookies)) { actual[data_type] = - content::EvalJs(frame, "has" + data_type + "();", - content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) - .ExtractBool(); + content::EvalJs(frame, "has" + data_type + "();").ExtractBool(); if (frame->GetLastCommittedOrigin() != frame->GetMainFrame()->GetLastCommittedOrigin() && data_type == "WebSql") { @@ -110,9 +104,7 @@ base::flat_map<std::string, bool> expected_elts; for (const auto& data_type : kStorageTypesForWorker) { actual[data_type] = - content::EvalJs(frame, "has" + data_type + "();", - content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) - .ExtractBool(); + content::EvalJs(frame, "has" + data_type + "();").ExtractBool(); expected_elts[data_type] = expected; } EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected_elts)); @@ -123,9 +115,7 @@ base::flat_map<std::string, bool> expected; for (const auto& data_type : kCrossTabCommunicationTypes) { actual[data_type] = - content::EvalJs(frame, "set" + data_type + "()", - content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) - .ExtractBool(); + content::EvalJs(frame, "set" + data_type + "()").ExtractBool(); expected[data_type] = true; } EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected)); @@ -137,9 +127,7 @@ base::flat_map<std::string, bool> expected_elts; for (const auto& data_type : kCrossTabCommunicationTypes) { actual[data_type] = - content::EvalJs(frame, "has" + data_type + "();", - content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) - .ExtractBool(); + content::EvalJs(frame, "has" + data_type + "();").ExtractBool(); expected_elts[data_type] = expected; } EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected_elts));
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/liblouis.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/liblouis.js index c4552f55..3e458e0d 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/liblouis.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/liblouis.js
@@ -50,6 +50,17 @@ this.loadOrReload_(opt_loadCallback); } + /** + * Convenience method to wait for the constructor to resolve its callback. + * @param {string} wasmPath Path to .wasm file for the module. + * @param {string=} opt_tablesDir Path to tables directory. + * @return {!Promise<LibLouis>} + */ + static async create(wasmPath, opt_tablesDir) { + return new Promise( + resolve => new LibLouis(wasmPath, opt_tablesDir, resolve)); + } + isLoaded() { return this.isLoaded_; }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/liblouis_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/liblouis_test.js index 4b2128297..0b2e37e 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/liblouis_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/liblouis_test.js
@@ -21,13 +21,6 @@ 'BrailleKeyEvent', '/chromevox/common/braille/braille_key_types.js'); } - createLiblouis() { - return new LibLouis( - chrome.extension.getURL( - 'chromevox/background/braille/liblouis_wrapper.js'), - '', () => {}); - } - withTranslator(liblouis, tableNames, callback) { liblouis.getTranslator(tableNames, this.newCallback(callback)); } @@ -45,13 +38,14 @@ function LIBLOUIS_TEST_F(testName, testFunc, opt_preamble) { // This needs to stay a function - don't convert to arrow function. const wrappedTestFunc = function() { - const liblouis = new LibLouis( - chrome.extension.getURL( - 'chromevox/background/braille/liblouis_wrapper.js'), - // This needs to stay bound - don't convert to arrow function. - '', testFunc.bind(this)); + LibLouis + .create( + chrome.extension.getURL( + 'chromevox/background/braille/liblouis_wrapper.js'), + '') + .then(liblouis => testFunc(liblouis)); }; - TEST_F('ChromeVoxLibLouisTest', testName, wrappedTestFunc, opt_preamble); + AX_TEST_F('ChromeVoxLibLouisTest', testName, wrappedTestFunc, opt_preamble); } function LIBLOUIS_TEST_F_WITH_PREAMBLE(preamble, testName, testFunc) {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/command_store.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/command_store.js index 57a2b56f..99b88202 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/command_store.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/command_store.js
@@ -29,7 +29,7 @@ * @return {string|undefined} The message id, if any. */ static messageForCommand(command) { - return (CommandStore.COMMAND_DATA[command] || {}).msgId; + return CommandStore.COMMAND_DATA[command]?.msgId; } /** @@ -38,7 +38,7 @@ * @return {string|undefined} The category, if any. */ static categoryForCommand(command) { - return (CommandStore.COMMAND_DATA[command] || {}).category; + return CommandStore.COMMAND_DATA[command]?.category; } /** @@ -292,13 +292,10 @@ /** * @typedef {{ - * announce: boolean, * category: (undefined|!CommandCategory), * msgId: (undefined|string), * denySignedOut: (undefined|boolean) * }} - * announce: Whether to call finishNavCommand and announce the current - * position after the command is done. * category: The command's category. * msgId: The message resource describing the command. * denySignedOut: Explicitly denies this command when on chrome://oobe/* or @@ -312,76 +309,62 @@ */ CommandStore.COMMAND_DATA = { [Command.TOGGLE_STICKY_MODE]: { - announce: false, msgId: 'toggle_sticky_mode', category: CommandCategory.MODIFIER_KEYS, }, [Command.PASS_THROUGH_MODE]: { - announce: false, msgId: 'pass_through_key_description', category: CommandCategory.MODIFIER_KEYS, }, [Command.STOP_SPEECH]: { - announce: false, msgId: 'stop_speech_key', category: CommandCategory.CONTROLLING_SPEECH, }, - [Command.OPEN_CHROMEVOX_MENUS]: {announce: false, msgId: 'menus_title'}, + [Command.OPEN_CHROMEVOX_MENUS]: {msgId: 'menus_title'}, [Command.RESET_TEXT_TO_SPEECH_SETTINGS]: { - announce: false, msgId: 'reset_tts_settings', category: CommandCategory.CONTROLLING_SPEECH, }, [Command.DECREASE_TTS_RATE]: { - announce: false, msgId: 'decrease_tts_rate', category: CommandCategory.CONTROLLING_SPEECH, }, [Command.INCREASE_TTS_RATE]: { - announce: false, msgId: 'increase_tts_rate', category: CommandCategory.CONTROLLING_SPEECH, }, [Command.DECREASE_TTS_PITCH]: { - announce: false, msgId: 'decrease_tts_pitch', category: CommandCategory.CONTROLLING_SPEECH, }, [Command.INCREASE_TTS_PITCH]: { - announce: false, msgId: 'increase_tts_pitch', category: CommandCategory.CONTROLLING_SPEECH, }, [Command.DECREASE_TTS_VOLUME]: { - announce: false, msgId: 'decrease_tts_volume', category: CommandCategory.CONTROLLING_SPEECH, }, [Command.INCREASE_TTS_VOLUME]: { - announce: false, msgId: 'increase_tts_volume', category: CommandCategory.CONTROLLING_SPEECH, }, [Command.CYCLE_PUNCTUATION_ECHO]: { - announce: false, msgId: 'cycle_punctuation_echo', category: CommandCategory.CONTROLLING_SPEECH, }, [Command.CYCLE_TYPING_ECHO]: { - announce: false, msgId: 'cycle_typing_echo', category: CommandCategory.CONTROLLING_SPEECH, }, [Command.TOGGLE_DICTATION]: { - announce: false, msgId: 'toggle_dictation', category: CommandCategory.ACTIONS, }, [Command.TOGGLE_EARCONS]: { - announce: true, msgId: 'toggle_earcons', category: CommandCategory.CONTROLLING_SPEECH, }, @@ -400,204 +383,166 @@ category: CommandCategory.NAVIGATION, }, [Command.FORWARD]: { - announce: true, msgId: 'forward', category: CommandCategory.NAVIGATION, }, [Command.BACKWARD]: { - announce: true, msgId: 'backward', category: CommandCategory.NAVIGATION, }, [Command.RIGHT]: { - announce: true, msgId: 'right', category: CommandCategory.NAVIGATION, }, [Command.LEFT]: { - announce: true, msgId: 'left', category: CommandCategory.NAVIGATION, }, [Command.PREVIOUS_GRANULARITY]: { - announce: true, msgId: 'previous_granularity', category: CommandCategory.NAVIGATION, }, [Command.NEXT_GRANULARITY]: { - announce: true, msgId: 'next_granularity', category: CommandCategory.NAVIGATION, }, [Command.PREVIOUS_AT_GRANULARITY]: { - announce: true, msgId: 'previous_at_granularity', category: CommandCategory.NAVIGATION, }, [Command.NEXT_AT_GRANULARITY]: { - announce: true, msgId: 'next_at_granularity', category: CommandCategory.NAVIGATION, }, [Command.PREVIOUS_CHARACTER]: { - announce: true, msgId: 'previous_character', category: CommandCategory.NAVIGATION, }, [Command.NEXT_CHARACTER]: { - announce: true, msgId: 'next_character', category: CommandCategory.NAVIGATION, }, [Command.PREVIOUS_WORD]: { - announce: true, msgId: 'previous_word', category: CommandCategory.NAVIGATION, }, [Command.NEXT_WORD]: { - announce: true, msgId: 'next_word', category: CommandCategory.NAVIGATION, }, [Command.PREVIOUS_LINE]: { - announce: true, msgId: 'previous_line', category: CommandCategory.NAVIGATION, }, [Command.NEXT_LINE]: { - announce: true, msgId: 'next_line', category: CommandCategory.NAVIGATION, }, [Command.PREVIOUS_SENTENCE]: { - announce: true, msgId: 'previous_sentence', category: CommandCategory.NAVIGATION, }, [Command.NEXT_SENTENCE]: { - announce: true, msgId: 'next_sentence', category: CommandCategory.NAVIGATION, }, [Command.PREVIOUS_OBJECT]: { - announce: true, msgId: 'previous_object', category: CommandCategory.NAVIGATION, }, [Command.NEXT_OBJECT]: { - announce: true, msgId: 'next_object', category: CommandCategory.NAVIGATION, }, [Command.PREVIOUS_GROUP]: { - announce: true, msgId: 'previous_group', category: CommandCategory.NAVIGATION, }, [Command.NEXT_GROUP]: { - announce: true, msgId: 'next_group', category: CommandCategory.NAVIGATION, }, [Command.PREVIOUS_SIMILAR_ITEM]: { - announce: true, msgId: 'previous_similar_item', category: CommandCategory.NAVIGATION, }, [Command.NEXT_SIMILAR_ITEM]: { - announce: true, msgId: 'next_similar_item', category: CommandCategory.NAVIGATION, }, [Command.PREVIOUS_INVALID_ITEM]: { - announce: true, msgId: 'previous_invalid_item', category: CommandCategory.NAVIGATION, }, [Command.NEXT_INVALID_ITEM]: { - announce: true, msgId: 'next_invalid_item', category: CommandCategory.NAVIGATION, }, [Command.JUMP_TO_TOP]: { - announce: true, msgId: 'jump_to_top', category: CommandCategory.NAVIGATION, }, [Command.JUMP_TO_BOTTOM]: { - announce: true, msgId: 'jump_to_bottom', category: CommandCategory.NAVIGATION, }, // Intentionally uncategorized. - [Command.MOVE_TO_START_OF_LINE]: {announce: true}, - [Command.MOVE_TO_END_OF_LINE]: {announce: true}, + [Command.MOVE_TO_START_OF_LINE]: {}, + [Command.MOVE_TO_END_OF_LINE]: {}, [Command.JUMP_TO_DETAILS]: { - announce: false, msgId: 'jump_to_details', category: CommandCategory.NAVIGATION, }, [Command.READ_FROM_HERE]: { - announce: false, msgId: 'read_from_here', category: CommandCategory.NAVIGATION, }, [Command.FORCE_CLICK_ON_CURRENT_ITEM]: { - announce: true, msgId: 'force_click_on_current_item', category: CommandCategory.ACTIONS, }, [Command.FORCE_LONG_CLICK_ON_CURRENT_ITEM]: { - announce: true, msgId: 'force_long_click_on_current_item', }, - [Command.FORCE_DOUBLE_CLICK_ON_CURRENT_ITEM]: {announce: true}, + [Command.FORCE_DOUBLE_CLICK_ON_CURRENT_ITEM]: {}, [Command.READ_LINK_URL]: { - announce: false, msgId: 'read_link_url', category: CommandCategory.INFORMATION, }, [Command.READ_CURRENT_TITLE]: { - announce: false, msgId: 'read_current_title', category: CommandCategory.INFORMATION, }, [Command.READ_CURRENT_URL]: { - announce: false, msgId: 'read_current_url', category: CommandCategory.INFORMATION, }, [Command.FULLY_DESCRIBE]: { - announce: false, msgId: 'fully_describe', category: CommandCategory.INFORMATION, }, [Command.SPEAK_TIME_AND_DATE]: { - announce: false, msgId: 'speak_time_and_date', category: CommandCategory.INFORMATION, }, [Command.TOGGLE_SELECTION]: { - announce: true, msgId: 'toggle_selection', category: CommandCategory.ACTIONS, }, [Command.TOGGLE_SEARCH_WIDGET]: { - announce: false, msgId: 'toggle_search_widget', category: CommandCategory.INFORMATION, }, [Command.TOGGLE_SCREEN]: { - announce: false, msgId: 'toggle_screen', category: CommandCategory.MODIFIER_KEYS, }, @@ -606,84 +551,69 @@ {msgId: 'toggle_braille_table', category: CommandCategory.HELP_COMMANDS}, [Command.TOGGLE_KEYBOARD_HELP]: { - announce: false, msgId: 'show_panel_menu', category: CommandCategory.HELP_COMMANDS, }, [Command.SHOW_PANEL_MENU_MOST_RECENT]: { - announce: false, msgId: 'show_panel_menu', category: CommandCategory.HELP_COMMANDS, }, [Command.HELP]: { - announce: false, msgId: 'help', category: CommandCategory.HELP_COMMANDS, }, [Command.CONTEXT_MENU]: { - announce: false, msgId: 'show_context_menu', category: CommandCategory.INFORMATION, }, [Command.SHOW_OPTIONS_PAGE]: { - announce: false, msgId: 'show_options_page', denySignedOut: true, category: CommandCategory.HELP_COMMANDS, }, [Command.SHOW_LOG_PAGE]: { - announce: false, msgId: 'show_log_page', denySignedOut: true, category: CommandCategory.HELP_COMMANDS, }, [Command.SHOW_LEARN_MODE_PAGE]: { - announce: false, msgId: 'show_kb_explorer_page', denySignedOut: true, category: CommandCategory.HELP_COMMANDS, }, [Command.SHOW_TTS_SETTINGS]: { - announce: false, msgId: 'show_tts_settings', category: CommandCategory.HELP_COMMANDS, denySignedOut: true, }, [Command.TOGGLE_BRAILLE_CAPTIONS]: { - announce: false, msgId: 'braille_captions', category: CommandCategory.HELP_COMMANDS, }, [Command.REPORT_ISSUE]: { - announce: false, denySignedOut: true, msgId: 'panel_menu_item_report_issue', category: CommandCategory.HELP_COMMANDS, }, [Command.SHOW_FORMS_LIST]: { - announce: false, msgId: 'show_forms_list', category: CommandCategory.OVERVIEW, }, [Command.SHOW_HEADINGS_LIST]: { - announce: false, msgId: 'show_headings_list', category: CommandCategory.OVERVIEW, }, [Command.SHOW_LANDMARKS_LIST]: { - announce: false, msgId: 'show_landmarks_list', category: CommandCategory.OVERVIEW, }, [Command.SHOW_LINKS_LIST]: { - announce: false, msgId: 'show_links_list', category: CommandCategory.OVERVIEW, }, [Command.SHOW_TABLES_LIST]: { - announce: false, msgId: 'show_tables_list', category: CommandCategory.OVERVIEW, }, @@ -881,100 +811,82 @@ // Table Actions. [Command.ANNOUNCE_HEADERS]: { - announce: false, msgId: 'announce_headers', category: CommandCategory.TABLES, }, [Command.SPEAK_TABLE_LOCATION]: { - announce: false, msgId: 'speak_table_location', category: CommandCategory.TABLES, }, [Command.GO_TO_FIRST_CELL]: { - announce: true, msgId: 'skip_to_beginning', category: CommandCategory.TABLES, }, [Command.GO_TO_LAST_CELL]: - {announce: true, msgId: 'skip_to_end', category: CommandCategory.TABLES}, + {msgId: 'skip_to_end', category: CommandCategory.TABLES}, [Command.GO_TO_ROW_FIRST_CELL]: { - announce: true, msgId: 'skip_to_row_beginning', category: CommandCategory.TABLES, }, [Command.GO_TO_ROW_LAST_CELL]: { - announce: true, msgId: 'skip_to_row_end', category: CommandCategory.TABLES, }, [Command.GO_TO_COL_FIRST_CELL]: { - announce: true, msgId: 'skip_to_col_beginning', category: CommandCategory.TABLES, }, [Command.GO_TO_COL_LAST_CELL]: { - announce: true, msgId: 'skip_to_col_end', category: CommandCategory.TABLES, }, [Command.PREVIOUS_ROW]: { - announce: true, msgId: 'skip_to_prev_row', category: CommandCategory.TABLES, }, [Command.PREVIOUS_COL]: { - announce: true, msgId: 'skip_to_prev_col', category: CommandCategory.TABLES, }, [Command.NEXT_ROW]: { - announce: true, msgId: 'skip_to_next_row', category: CommandCategory.TABLES, }, [Command.NEXT_COL]: { - announce: true, msgId: 'skip_to_next_col', category: CommandCategory.TABLES, }, // Generic Actions. [Command.ENTER_SHIFTER]: { - announce: true, msgId: 'enter_content', category: CommandCategory.NAVIGATION, }, [Command.EXIT_SHIFTER]: { - announce: true, msgId: 'exit_content', category: CommandCategory.NAVIGATION, }, - [Command.EXIT_SHIFTER_CONTENT]: {announce: true}, + [Command.EXIT_SHIFTER_CONTENT]: {}, [Command.OPEN_LONG_DESC]: { - announce: false, msgId: 'open_long_desc', category: CommandCategory.INFORMATION, }, [Command.PAUSE_ALL_MEDIA]: { - announce: false, msgId: 'pause_all_media', category: CommandCategory.INFORMATION, }, [Command.ANNOUNCE_BATTERY_DESCRIPTION]: { - announce: true, msgId: 'announce_battery_description', category: CommandCategory.INFORMATION, }, [Command.ANNOUNCE_RICH_TEXT_DESCRIPTION]: { - announce: true, msgId: 'announce_rich_text_description', category: CommandCategory.INFORMATION, }, [Command.READ_PHONETIC_PRONUNCIATION]: { - announce: true, msgId: 'read_phonetic_pronunciation', category: CommandCategory.INFORMATION, }, @@ -985,65 +897,55 @@ // Math specific commands. [Command.TOGGLE_SEMANTICS]: { - announce: false, msgId: 'toggle_semantics', category: CommandCategory.INFORMATION, }, // Braille specific commands. [Command.ROUTING]: { - announce: false, msgId: 'braille_routing', category: CommandCategory.BRAILLE, }, [Command.PAN_LEFT]: { - announce: true, msgId: 'braille_pan_left', category: CommandCategory.BRAILLE, }, [Command.PAN_RIGHT]: { - announce: true, msgId: 'braille_pan_right', category: CommandCategory.BRAILLE, }, [Command.LINE_UP]: { - announce: true, msgId: 'braille_line_up', category: CommandCategory.BRAILLE, }, [Command.LINE_DOWN]: { - announce: true, msgId: 'braille_line_down', category: CommandCategory.BRAILLE, }, [Command.TOP]: { - announce: true, msgId: 'braille_top', category: CommandCategory.BRAILLE, }, [Command.BOTTOM]: { - announce: true, msgId: 'braille_bottom', category: CommandCategory.BRAILLE, }, [Command.VIEW_GRAPHIC_AS_BRAILLE]: { - announce: true, msgId: 'view_graphic_as_braille', category: CommandCategory.BRAILLE, }, // Developer commands. [Command.ENABLE_CONSOLE_TTS]: { - announce: false, msgId: 'enable_tts_log', category: CommandCategory.DEVELOPER, }, - [Command.START_HISTORY_RECORDING]: {announce: false}, - [Command.STOP_HISTORY_RECORDING]: {announce: false}, - [Command.AUTORUNNER]: {announce: false}, + [Command.START_HISTORY_RECORDING]: {}, + [Command.STOP_HISTORY_RECORDING]: {}, + [Command.AUTORUNNER]: {}, - [Command.DEBUG]: {announce: false}, + [Command.DEBUG]: {}, - [Command.NOP]: {announce: false}, + [Command.NOP]: {}, };
diff --git a/chrome/browser/resources/chromeos/accessibility/common/testing/common.js b/chrome/browser/resources/chromeos/accessibility/common/testing/common.js index 61441aed..7badfa8 100644 --- a/chrome/browser/resources/chromeos/accessibility/common/testing/common.js +++ b/chrome/browser/resources/chromeos/accessibility/common/testing/common.js
@@ -12,11 +12,12 @@ * @param {string} testFixture Fixture name. * @param {string} testName Test name. * @param {function} testFunction The test impl. + * @param {string=} preamble C++ code to execute before the test. */ -function AX_TEST_F(testFixture, testName, testFunction) { +function AX_TEST_F(testFixture, testName, testFunction, preamble) { TEST_F(testFixture, testName, function() { this.newCallback(testFunction)(); - }); + }, preamble); } // var is used to export this function alias outside of the current context
diff --git a/chrome/browser/resources/new_tab_page/modules/history_clusters/module.html b/chrome/browser/resources/new_tab_page/modules/history_clusters/module.html index aa23bda..2934cf1e 100644 --- a/chrome/browser/resources/new_tab_page/modules/history_clusters/module.html +++ b/chrome/browser/resources/new_tab_page/modules/history_clusters/module.html
@@ -55,12 +55,11 @@ </style> <div id="content"> <ntp-module-header - disable-text="[[i18n('modulesDisableButtonText', title_)]]" - show-dismiss-button - dismiss-text="[[i18n('modulesDismissButtonText', title_)]]" - on-disable-button-click="onDisableButtonClick_" - show-info-button on-info-button-click="onInfoButtonClick_" - icon-src="chrome://resources/images/icon_journeys.svg"> + disable-text="[[i18n('modulesDisableButtonText', title_)]]" + dismiss-text="[[i18n('modulesDismissButtonText', title_)]]" + show-info-button show-dismiss-button + on-dismiss-button-click="onDismissButtonClick_" + icon-src="chrome://resources/images/icon_journeys.svg"> [[i18n('modulesJourneysResumeJourney', title_)]] <button id="showAllButton" type="button" on-click="onShowAllClick_" slot="title-actions">
diff --git a/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts b/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts index 2c2128947..f7550ed0d 100644 --- a/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts +++ b/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts
@@ -9,7 +9,7 @@ import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {Cluster, URLVisit} from '../../history_cluster_types.mojom-webui.js'; -import {I18nMixin} from '../../i18n_setup.js'; +import {I18nMixin, loadTimeData} from '../../i18n_setup.js'; import {ModuleDescriptor} from '../module_descriptor.js'; import {HistoryClustersProxyImpl} from './history_clusters_proxy.js'; @@ -74,6 +74,19 @@ return type === this.layoutType; } + private onDismissButtonClick_() { + HistoryClustersProxyImpl.getInstance().handler.dismissCluster( + [this.searchResultPage, ...this.cluster.visits]); + this.dispatchEvent(new CustomEvent('dismiss-module', { + bubbles: true, + composed: true, + detail: { + message: + loadTimeData.getStringF('dismissModuleToastMessage', this.title_), + }, + })); + } + private onShowAllClick_() { HistoryClustersProxyImpl.getInstance().handler.showJourneysSidePanel( this.cluster.label || '');
diff --git a/chrome/browser/resources/new_tab_page/modules/modules.html b/chrome/browser/resources/new_tab_page/modules/modules.html index a6b95b05..cbe4b08 100644 --- a/chrome/browser/resources/new_tab_page/modules/modules.html +++ b/chrome/browser/resources/new_tab_page/modules/modules.html
@@ -136,11 +136,13 @@ </div> <cr-toast id="removeModuleToast" duration="10000"> <div id="removeModuleToastMessage">[[removedModuleData_.message]]</div> - <cr-button id="undoRemoveModuleButton" - aria-label="$i18n{undoDescription}" - on-click="onUndoRemoveModuleButtonClick_"> - $i18n{undo} - </cr-button> + <template is="dom-if" if="[[removedModuleData_.undo]]"> + <cr-button id="undoRemoveModuleButton" + aria-label="$i18n{undoDescription}" + on-click="onUndoRemoveModuleButtonClick_"> + $i18n{undo} + </cr-button> + </template> </cr-toast> <cr-toast id="removeModuleFreToast" duration="10000"> <div id="removeModuleFreToastMessage">
diff --git a/chrome/browser/resources/new_tab_page/modules/modules.ts b/chrome/browser/resources/new_tab_page/modules/modules.ts index 49ac3a6d..4d2b758 100644 --- a/chrome/browser/resources/new_tab_page/modules/modules.ts +++ b/chrome/browser/resources/new_tab_page/modules/modules.ts
@@ -21,7 +21,7 @@ import {getTemplate} from './modules.html.js'; export type DismissModuleEvent = - CustomEvent<{message: string, restoreCallback: () => void}>; + CustomEvent<{message: string, restoreCallback?: () => void}>; export type DisableModuleEvent = DismissModuleEvent; declare global { @@ -151,7 +151,7 @@ private modulesRedesignedLayoutEnabled_: boolean; private modulesShownToUser: boolean; private modulesVisibilityDetermined_: boolean; - private removedModuleData_: {message: string, undo: () => void}|null; + private removedModuleData_: {message: string, undo?: () => void}|null; private setDisabledModulesListenerId_: number|null = null; private setModulesFreVisibilityListenerId_: number|null = null; @@ -363,11 +363,14 @@ const restoreCallback = e.detail.restoreCallback; this.removedModuleData_ = { message: e.detail.message, - undo: () => { - this.splice('dismissedModules_', this.dismissedModules_.indexOf(id), 1); - restoreCallback(); - NewTabPageProxy.getInstance().handler.onRestoreModule(id); - }, + undo: restoreCallback ? + () => { + this.splice( + 'dismissedModules_', this.dismissedModules_.indexOf(id), 1); + restoreCallback(); + NewTabPageProxy.getInstance().handler.onRestoreModule(id); + } : + undefined, }; if (!this.dismissedModules_.includes(id)) { this.push('dismissedModules_', id); @@ -419,7 +422,7 @@ } // Restore the module. - this.removedModuleData_.undo(); + this.removedModuleData_.undo!(); // Notify the user. this.$.removeModuleToast.hide();
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/esim_install_error_dialog.html b/chrome/browser/resources/settings/chromeos/internet_page/esim_install_error_dialog.html index e94fd47..cd081f4f 100644 --- a/chrome/browser/resources/settings/chromeos/internet_page/esim_install_error_dialog.html +++ b/chrome/browser/resources/settings/chromeos/internet_page/esim_install_error_dialog.html
@@ -1,4 +1,4 @@ -<style include="iron-flex iron-positioning"> +<style include="settings-shared iron-flex iron-positioning"> :host { --cr-dialog-width: 372px; }
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/esim_install_error_dialog.ts b/chrome/browser/resources/settings/chromeos/internet_page/esim_install_error_dialog.ts index 676ddb5..9215022b 100644 --- a/chrome/browser/resources/settings/chromeos/internet_page/esim_install_error_dialog.ts +++ b/chrome/browser/resources/settings/chromeos/internet_page/esim_install_error_dialog.ts
@@ -10,6 +10,7 @@ import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js'; import 'chrome://resources/cr_elements/cr_input/cr_input.js'; import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js'; +import '../../settings_shared.css.js'; import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js'; import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/esim_remove_profile_dialog.html b/chrome/browser/resources/settings/chromeos/internet_page/esim_remove_profile_dialog.html index 4f9e79b..ea4d973 100644 --- a/chrome/browser/resources/settings/chromeos/internet_page/esim_remove_profile_dialog.html +++ b/chrome/browser/resources/settings/chromeos/internet_page/esim_remove_profile_dialog.html
@@ -1,4 +1,4 @@ -<style> +<style include="settings-shared"> :host { --cr-dialog-width: 416px; }
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/esim_remove_profile_dialog.ts b/chrome/browser/resources/settings/chromeos/internet_page/esim_remove_profile_dialog.ts index 43258b1..dec3ec1 100644 --- a/chrome/browser/resources/settings/chromeos/internet_page/esim_remove_profile_dialog.ts +++ b/chrome/browser/resources/settings/chromeos/internet_page/esim_remove_profile_dialog.ts
@@ -10,6 +10,7 @@ import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js'; import 'chrome://resources/cr_elements/cr_button/cr_button.js'; import 'chrome://resources/cr_elements/cr_input/cr_input.js'; +import '../../settings_shared.css.js'; import {getESimProfile} from 'chrome://resources/ash/common/cellular_setup/esim_manager_utils.js'; import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.html b/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.html index c27c5f62..132b42a 100644 --- a/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.html +++ b/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.html
@@ -1,4 +1,4 @@ -<style include="iron-positioning"> +<style include="settings-shared iron-positioning"> :host { --cr-dialog-width: 416px; --cr-dialog-title-slot-padding-bottom: 10px;
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.ts b/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.ts index 8158f38..faad5bb8 100644 --- a/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.ts +++ b/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.ts
@@ -10,6 +10,7 @@ import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js'; import 'chrome://resources/cr_elements/cr_input/cr_input.js'; import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js'; +import '../../settings_shared.css.js'; import {getESimProfile} from 'chrome://resources/ash/common/cellular_setup/esim_manager_utils.js'; import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
diff --git a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_contact_visibility_dialog.html b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_contact_visibility_dialog.html index 714a754..cd5aa34b 100644 --- a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_contact_visibility_dialog.html +++ b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_contact_visibility_dialog.html
@@ -1,4 +1,4 @@ -<style> +<style include="settings-shared"> .title { color: var(--cr-primary-text-color); font-family: 'Google Sans';
diff --git a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_contact_visibility_dialog.ts b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_contact_visibility_dialog.ts index e4fe3e1..da311644 100644 --- a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_contact_visibility_dialog.ts +++ b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_contact_visibility_dialog.ts
@@ -13,6 +13,7 @@ import '/shared/nearby_contact_visibility.js'; import '/shared/nearby_onboarding_page.js'; import '/shared/nearby_visibility_page.js'; +import '../../settings_shared.css.js'; import {NearbyContactVisibilityElement} from '/shared/nearby_contact_visibility.js'; import {NearbySettings} from '/shared/nearby_share_settings_mixin.js';
diff --git a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_data_usage_dialog.html b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_data_usage_dialog.html index 05f0f8a..00d4748 100644 --- a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_data_usage_dialog.html +++ b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_data_usage_dialog.html
@@ -1,4 +1,4 @@ -<style> +<style include="settings-shared"> :host { --cr-dialog-width: 340px; }
diff --git a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_data_usage_dialog.ts b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_data_usage_dialog.ts index 60c8377..8480697c 100644 --- a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_data_usage_dialog.ts +++ b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_data_usage_dialog.ts
@@ -12,6 +12,7 @@ import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js'; import 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.js'; import 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.js'; +import '../../settings_shared.css.js'; import {getNearbyShareSettings} from '/shared/nearby_share_settings.js'; import {NearbySettings} from '/shared/nearby_share_settings_mixin.js';
diff --git a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_device_name_dialog.html b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_device_name_dialog.html index 7dce630..3871f66 100644 --- a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_device_name_dialog.html +++ b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_device_name_dialog.html
@@ -1,4 +1,4 @@ -<style> +<style include="settings-shared"> :host { --cr-dialog-width: 340px; }
diff --git a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_device_name_dialog.ts b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_device_name_dialog.ts index b08a014..81226a0 100644 --- a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_device_name_dialog.ts +++ b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_device_name_dialog.ts
@@ -11,6 +11,7 @@ import 'chrome://resources/cr_elements/cr_button/cr_button.js'; import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js'; import 'chrome://resources/cr_elements/cr_input/cr_input.js'; +import '../../settings_shared.css.js'; import {getNearbyShareSettings} from '/shared/nearby_share_settings.js'; import {NearbySettings} from '/shared/nearby_share_settings_mixin.js';
diff --git a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_dialog.html b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_dialog.html index 593bebe..04d9ff4a 100644 --- a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_dialog.html +++ b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_dialog.html
@@ -1,4 +1,4 @@ -<style> +<style include="settings-shared"> cr-dialog::part(dialog) { height: 420px; width: 512px;
diff --git a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_dialog.ts b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_dialog.ts index d3bb1a0..b4d3ecf 100644 --- a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_dialog.ts +++ b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_dialog.ts
@@ -23,6 +23,7 @@ import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js'; import 'chrome://resources/cr_elements/cr_view_manager/cr_view_manager.js'; import '../../prefs/prefs.js'; +import '../../settings_shared.css.js'; import '/shared/nearby_onboarding_one_page.js'; import '/shared/nearby_onboarding_page.js'; import '/shared/nearby_visibility_page.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/change_dictation_locale_dialog.ts b/chrome/browser/resources/settings/chromeos/os_a11y_page/change_dictation_locale_dialog.ts index 28a0db19..8454c67e 100644 --- a/chrome/browser/resources/settings/chromeos/os_a11y_page/change_dictation_locale_dialog.ts +++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/change_dictation_locale_dialog.ts
@@ -10,11 +10,11 @@ import 'chrome://resources/cr_elements/cr_button/cr_button.js'; import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js'; import 'chrome://resources/cr_elements/cr_search_field/cr_search_field.js'; -import 'chrome://resources/cr_elements/cr_shared_style.css.js'; import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js'; import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js'; import 'chrome://resources/polymer/v3_0/paper-ripple/paper-ripple.js'; import '../../settings_shared.css.js'; +import '../os_languages_page/shared_style.css.js'; import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js'; import {CrSearchFieldElement} from 'chrome://resources/cr_elements/cr_search_field/cr_search_field.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_overlapping_apps_dialog.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_overlapping_apps_dialog.html index 35f00862..093094b 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_overlapping_apps_dialog.html +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_overlapping_apps_dialog.html
@@ -1,3 +1,4 @@ +<style include="settings-shared"></style> <cr-dialog show-on-attach id="dialog" close-text="close"> <div slot="title">$i18n{appManagementIntentOverlapDialogTitle}</div> <div slot="body">[[getBodyText_(apps_)]]</div>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_overlapping_apps_dialog.ts b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_overlapping_apps_dialog.ts index 3a5bf06..59277e0e 100644 --- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_overlapping_apps_dialog.ts +++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_overlapping_apps_dialog.ts
@@ -3,6 +3,7 @@ // found in the LICENSE file. import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js'; +import '../../../settings_shared.css.js'; import {App} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js'; import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_pairing_dialog.html b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_pairing_dialog.html index d2a8af5..1840f0f 100644 --- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_pairing_dialog.html +++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_pairing_dialog.html
@@ -1,4 +1,4 @@ -<style> +<style include="settings-shared"> #dialog { --cr-dialog-top-container-min-height: 0; } @@ -8,4 +8,4 @@ <bluetooth-pairing-ui on-finished="closeDialog_"> </bluetooth-pairing-ui> </div> -</cr-dialog> \ No newline at end of file +</cr-dialog>
diff --git a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_pairing_dialog.ts b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_pairing_dialog.ts index 8ac8bf6..df3deb6 100644 --- a/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_pairing_dialog.ts +++ b/chrome/browser/resources/settings/chromeos/os_bluetooth_page/os_bluetooth_pairing_dialog.ts
@@ -8,6 +8,7 @@ */ import 'chrome://resources/ash/common/bluetooth/bluetooth_pairing_ui.js'; import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js'; +import '../../settings_shared.css.js'; import {BluetoothUiSurface, recordBluetoothUiSurfaceMetrics} from 'chrome://resources/ash/common/bluetooth/bluetooth_metrics_utils.js'; import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
diff --git a/chrome/browser/resources/side_panel/read_anything/app.html b/chrome/browser/resources/side_panel/read_anything/app.html index 5dc4eff..9852629a 100644 --- a/chrome/browser/resources/side_panel/read_anything/app.html +++ b/chrome/browser/resources/side_panel/read_anything/app.html
@@ -25,9 +25,9 @@ <div id="container" hidden="[[!hasContent_]]"></div> <div id="empty-state-container" hidden="[[hasContent_]]"> <sp-empty-state - image-path="./images/empty_state.svg" - dark-image-path="./images/empty_state.svg" - heading="$i18n{emptyStateHeader}" - body="$i18n{emptyStateSubheader}"> + image-path="[[emptyStateImagePath_]]" + dark-image-path="[[emptyStateDarkImagePath_]]" + heading="[[emptyStateHeading_]]" + body="[[emptyStateSubheading_]]"> </sp-empty-state> </div>
diff --git a/chrome/browser/resources/side_panel/read_anything/app.ts b/chrome/browser/resources/side_panel/read_anything/app.ts index 8afe9fd..a4b7452 100644 --- a/chrome/browser/resources/side_panel/read_anything/app.ts +++ b/chrome/browser/resources/side_panel/read_anything/app.ts
@@ -9,6 +9,7 @@ import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js'; import {assert} from 'chrome://resources/js/assert_ts.js'; import {rgbToSkColor, skColorToRgba} from 'chrome://resources/js/color_utils.js'; +import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js'; import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; @@ -75,6 +76,12 @@ assert(readAnythingApp); readAnythingApp.updateTheme(); }; + + chrome.readAnything.showLoading = () => { + const readAnythingApp = document.querySelector('read-anything-app'); + assert(readAnythingApp); + readAnythingApp.showLoading(); + }; } export class ReadAnythingElement extends ReadAnythingElementBase { @@ -107,6 +114,10 @@ private domNodeToAxNodeIdMap_: TwoWayMap = new TwoWayMap(); private hasContent_: boolean; + private emptyStateImagePath_: string; + private emptyStateDarkImagePath_: string; + private emptyStateHeading_: string; + private emptyStateSubheading_: string; override connectedCallback() { super.connectedCallback(); @@ -114,6 +125,8 @@ chrome.readAnything.onConnected(); } + this.showLoading(); + document.onselectionchange = () => { const shadowRoot = this.shadowRoot; assert(shadowRoot); @@ -194,6 +207,16 @@ return parentElement; } + showLoading() { + this.emptyStateImagePath_ = 'chrome://resources/images/throbber_small.svg'; + this.emptyStateDarkImagePath_ = + 'chrome://resources/images/throbber_small_dark.svg'; + this.emptyStateHeading_ = + loadTimeData.getString('readAnythingLoadingMessage'); + this.emptyStateSubheading_ = ''; + this.hasContent_ = false; + } + updateContent() { const shadowRoot = this.shadowRoot; assert(shadowRoot); @@ -220,6 +243,11 @@ // If there is no content to show, the empty state container will be shown. const node = this.buildSubtree_(rootId); if (!node.textContent) { + this.emptyStateImagePath_ = './images/empty_state.svg'; + this.emptyStateDarkImagePath_ = './images/empty_state.svg'; + this.emptyStateHeading_ = loadTimeData.getString('emptyStateHeader'); + this.emptyStateSubheading_ = + loadTimeData.getString('emptyStateSubheader'); this.hasContent_ = false; return; }
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts b/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts index 32a2af96f0..38cd9a22 100644 --- a/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts +++ b/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
@@ -105,6 +105,9 @@ // Implemented in read_anything/app.ts and called by native c++. //////////////////////////////////////////////////////////////// + // Display a loading screen to tell the user we are distilling the page. + function showLoading(): void; + // Ping that an AXTree has been distilled for the active tab's render frame // and is available to consume. function updateContent(): void;
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything.html b/chrome/browser/resources/side_panel/read_anything/read_anything.html index e392507..02d87e4 100644 --- a/chrome/browser/resources/side_panel/read_anything/read_anything.html +++ b/chrome/browser/resources/side_panel/read_anything/read_anything.html
@@ -25,6 +25,5 @@ <body> <read-anything-app></read-anything-app> <script type="module" src="app.js"></script> - </script> </body> </html>
diff --git a/chrome/browser/safe_browsing/chrome_client_side_detection_host_delegate_unittest.cc b/chrome/browser/safe_browsing/chrome_client_side_detection_host_delegate_unittest.cc index 4fc6381..f877ddc 100644 --- a/chrome/browser/safe_browsing/chrome_client_side_detection_host_delegate_unittest.cc +++ b/chrome/browser/safe_browsing/chrome_client_side_detection_host_delegate_unittest.cc
@@ -63,13 +63,12 @@ TEST_F(ChromeClientSideDetectionHostDelegateTest, GetReferrerChain) { base::Time now = base::Time::Now(); - base::Time one_hour_ago = - base::Time::FromDoubleT(now.ToDoubleT() - 60.0 * 60.0); + base::Time one_second_ago = base::Time::FromDoubleT(now.ToDoubleT() - 1.0); std::unique_ptr<NavigationEvent> first_navigation = std::make_unique<NavigationEvent>(); first_navigation->original_request_url = GURL("http://a.com/"); - first_navigation->last_updated = one_hour_ago; + first_navigation->last_updated = one_second_ago; first_navigation->navigation_initiation = ReferrerChainEntry::BROWSER_INITIATED; navigation_event_list()->RecordNavigationEvent(std::move(first_navigation)); @@ -101,13 +100,12 @@ TEST_F(ChromeClientSideDetectionHostDelegateTest, NoNavigationObserverManager) { base::Time now = base::Time::Now(); - base::Time one_hour_ago = - base::Time::FromDoubleT(now.ToDoubleT() - 60.0 * 60.0); + base::Time one_second_ago = base::Time::FromDoubleT(now.ToDoubleT() - 1.0); std::unique_ptr<NavigationEvent> first_navigation = std::make_unique<NavigationEvent>(); first_navigation->original_request_url = GURL("http://a.com/"); - first_navigation->last_updated = one_hour_ago; + first_navigation->last_updated = one_second_ago; first_navigation->navigation_initiation = ReferrerChainEntry::BROWSER_INITIATED; navigation_event_list()->RecordNavigationEvent(std::move(first_navigation));
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 81f982f5..d585d97 100644 --- a/chrome/browser/safe_browsing/download_protection/download_request_maker.cc +++ b/chrome/browser/safe_browsing/download_protection/download_request_maker.cc
@@ -18,6 +18,7 @@ #include "chrome/browser/safe_browsing/chrome_user_population_helper.h" #include "chrome/browser/safe_browsing/download_protection/download_protection_service.h" #include "chrome/browser/safe_browsing/download_protection/download_protection_util.h" +#include "components/safe_browsing/core/common/features.h" #include "components/safe_browsing/core/common/proto/csd.pb.h" #include "components/safe_browsing/core/common/utils.h" #include "content/public/browser/browser_context.h" @@ -165,6 +166,10 @@ ->IsUnderAdvancedProtection(); *request_->mutable_population() = GetUserPopulationForProfile(profile); + if (base::FeatureList::IsEnabled(kNestedArchives)) { + request_->mutable_population()->add_finch_active_groups( + "SafeBrowsingArchiveImprovements.Enabled"); + } request_->set_request_ap_verdicts(is_under_advanced_protection); request_->set_locale(g_browser_process->GetApplicationLocale()); request_->set_file_basename(target_file_path_.BaseName().AsUTF8Unsafe());
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc index cde5de20..33fb396 100644 --- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc +++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
@@ -549,8 +549,8 @@ auto* nav_event = observer_manager_->navigation_event_list()->GetNavigationEvent( *nav_event_index); - observer_manager_->AddToReferrerChain(referrer_chain, nav_event, GURL(), - ReferrerChainEntry::EVENT_URL); + observer_manager_->MaybeAddToReferrerChain( + referrer_chain, nav_event, GURL(), ReferrerChainEntry::EVENT_URL); } }
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java index 721e336..5b2fd2f 100644 --- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java +++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java
@@ -148,7 +148,7 @@ private boolean hideBottomSheetContentOnTap(FirstPartyOption firstPartyOption) { if (USER_ACTION_SCREENSHOT_SELECTED.equals(firstPartyOption.featureNameForMetrics) - || USER_ACTION_WEB_STYLE_NOTES_SELECTED.equals( + || USER_ACTION_LONG_SCREENSHOT_SELECTED.equals( firstPartyOption.featureNameForMetrics)) { return false; }
diff --git a/chrome/browser/shared_highlighting/DIR_METADATA b/chrome/browser/shared_highlighting/DIR_METADATA deleted file mode 100644 index 3ccc9071..0000000 --- a/chrome/browser/shared_highlighting/DIR_METADATA +++ /dev/null
@@ -1,3 +0,0 @@ -monorail { - component: "UI>Browser>Creation" -} \ No newline at end of file
diff --git a/chrome/browser/shared_highlighting/OWNERS b/chrome/browser/shared_highlighting/OWNERS deleted file mode 100644 index 6eda829..0000000 --- a/chrome/browser/shared_highlighting/OWNERS +++ /dev/null
@@ -1,2 +0,0 @@ -gayane@chromium.org -sebsg@chromium.org
diff --git a/chrome/browser/shared_highlighting/shared_highlighting_browsertest.cc b/chrome/browser/shared_highlighting/shared_highlighting_browsertest.cc deleted file mode 100644 index bd46439..0000000 --- a/chrome/browser/shared_highlighting/shared_highlighting_browsertest.cc +++ /dev/null
@@ -1,391 +0,0 @@ -// 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. - -#include "base/memory/raw_ptr.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "build/build_config.h" -#include "build/chromeos_buildflags.h" -#include "chrome/app/chrome_command_ids.h" -#include "chrome/browser/renderer_context_menu/link_to_text_menu_observer.h" -#include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/test/base/in_process_browser_test.h" -#include "chrome/test/base/ui_test_utils.h" -#include "components/feature_engagement/public/feature_constants.h" -#include "content/public/browser/web_contents.h" -#include "content/public/test/browser_test.h" -#include "content/public/test/browser_test_utils.h" -#include "content/public/test/fenced_frame_test_util.h" -#include "net/http/http_status_code.h" -#include "net/test/embedded_test_server/embedded_test_server.h" -#include "net/test/embedded_test_server/http_request.h" -#include "net/test/embedded_test_server/http_response.h" -#include "third_party/blink/public/mojom/link_to_text/link_to_text.mojom-test-utils.h" -#include "third_party/blink/public/mojom/link_to_text/link_to_text.mojom.h" -#include "ui/base/clipboard/clipboard.h" - -namespace shared_highlighting { -namespace { - -using net::test_server::BasicHttpResponse; -using net::test_server::HttpRequest; -using net::test_server::HttpResponse; - -} // namespace - -// Wait until text fragment matches are received. -class GetMatchesWaiter { - public: - GetMatchesWaiter() = default; - ~GetMatchesWaiter() = default; - - void Wait() { run_loop_.Run(); } - - void OnMatchesRecieved(const std::vector<std::string>& matches) { - matches_ = matches; - run_loop_.Quit(); - } - - std::vector<std::string> GetMatches() { return matches_; } - - private: - std::vector<std::string> matches_; - base::RunLoop run_loop_; -}; - -class SharedHighlightingBrowserTest : public InProcessBrowserTest { - public: - SharedHighlightingBrowserTest(const SharedHighlightingBrowserTest&) = delete; - SharedHighlightingBrowserTest& operator=( - const SharedHighlightingBrowserTest&) = delete; - - protected: - SharedHighlightingBrowserTest() = default; - ~SharedHighlightingBrowserTest() override = default; - - // InProcessBrowserTest - void SetUpCommandLine(base::CommandLine* command_line) override; - - // BrowserTestBase - void SetUpOnMainThread() override; - - bool SelectTextInCurrentTab(); - int GetSelectionMidX(); - int GetSelectionMidY(); - std::u16string GetClipboardText(); - void OpenInNewTab(GURL url); - std::string GetFirstHighlightedText(); - - content::WebContents* GetCurrentTab(); - - private: - std::unique_ptr<HttpResponse> HandleRequest(const HttpRequest& request); - - std::string html_content_ = R"HTML( - <!DOCTYPE html> - <style> - body { - height: 2200px; - } - #first { - position: absolute; - top: 1000px; - } - #second { - position: absolute; - top: 2000px; - } - </style> - <p id="selected">This is a test page</p> - <p id="second">With some more text</p> - )HTML"; -}; - -void SharedHighlightingBrowserTest::SetUpCommandLine( - base::CommandLine* command_line) {} - -void SharedHighlightingBrowserTest::SetUpOnMainThread() { - InProcessBrowserTest::SetUpOnMainThread(); - - embedded_test_server()->RegisterRequestHandler(base::BindRepeating( - &SharedHighlightingBrowserTest::HandleRequest, base::Unretained(this))); - ASSERT_TRUE(embedded_test_server()->Start()); -} - -bool SharedHighlightingBrowserTest::SelectTextInCurrentTab() { - return content::ExecuteScript( - GetCurrentTab(), - "var node = document.getElementById('selected');" - "if (document.body.createTextRange) {" - " const range = document.body.createTextRange();" - " range.moveToElementText(node);" - " range.select();" - "} else if (window.getSelection) {" - " const selection = window.getSelection();" - " const range = document.createRange();" - " range.selectNodeContents(node);" - " selection.removeAllRanges();" - " selection.addRange(range);" - "}"); -} - -int SharedHighlightingBrowserTest::GetSelectionMidX() { - int x; - EXPECT_TRUE(content::ExecuteScriptAndExtractInt( - GetCurrentTab(), - "var bounds = document.getElementById('selected')" - ".getBoundingClientRect();" - "domAutomationController.send(" - " Math.floor(bounds.left + bounds.width / 2));", - &x)); - return x; -} - -int SharedHighlightingBrowserTest::GetSelectionMidY() { - int y; - EXPECT_TRUE(content::ExecuteScriptAndExtractInt( - GetCurrentTab(), - "var bounds = document.getElementById('selected')" - ".getBoundingClientRect();" - "domAutomationController.send(" - " Math.floor(bounds.top + bounds.height / 2));", - &y)); - return y; -} - -std::u16string SharedHighlightingBrowserTest::GetClipboardText() { - ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); - std::u16string result; - clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr, - &result); - return result; -} - -void SharedHighlightingBrowserTest::OpenInNewTab(GURL url) { - ui_test_utils::NavigateToURLWithDisposition( - browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB, - ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); -} - -std::string SharedHighlightingBrowserTest::GetFirstHighlightedText() { - GetMatchesWaiter get_matches_waiter; - mojo::Remote<blink::mojom::TextFragmentReceiver> remote; - browser() - ->tab_strip_model() - ->GetActiveWebContents() - ->GetPrimaryMainFrame() - ->GetRemoteInterfaces() - ->GetInterface(remote.BindNewPipeAndPassReceiver()); - remote->ExtractTextFragmentsMatches( - base::BindOnce(&GetMatchesWaiter::OnMatchesRecieved, - base::Unretained(&get_matches_waiter))); - get_matches_waiter.Wait(); - - return get_matches_waiter.GetMatches()[0]; -} - -content::WebContents* SharedHighlightingBrowserTest::GetCurrentTab() { - return browser()->tab_strip_model()->GetActiveWebContents(); -} - -std::unique_ptr<HttpResponse> SharedHighlightingBrowserTest::HandleRequest( - const HttpRequest& request) { - auto response = std::make_unique<BasicHttpResponse>(); - response->set_code(net::HTTP_OK); - response->set_content(html_content_); - response->set_content_type("text/html; charset=utf-8"); - return std::move(response); -} - -// Wait for a link to text generation completion. If successful "Copy link to -// text" menu options will be enabled. -class GenerationCompleteObserver { - public: - GenerationCompleteObserver() { - LinkToTextMenuObserver::RegisterGenerationCompleteCallbackForTesting( - base::BindOnce(&GenerationCompleteObserver::OnGenerationComplete, - base::Unretained(this))); - } - ~GenerationCompleteObserver() = default; - - void Wait() { run_loop_.Run(); } - - std::string GetSelector() const { return selector_; } - - private: - void OnGenerationComplete(const std::string& selector) { - selector_ = selector; - run_loop_.Quit(); - } - - std::string selector_; - base::RunLoop run_loop_; -}; - -// Wait for context menu to be shown and use the menu handler to execute menu -// option commands. -class ContextMenuObserver { - public: - ContextMenuObserver() { - RenderViewContextMenu::RegisterMenuShownCallbackForTesting(base::BindOnce( - &ContextMenuObserver::MenuShown, base::Unretained(this))); - } - - ~ContextMenuObserver() = default; - - void WaitForMenuShown() { run_loop_.Run(); } - - void ExecuteCommand(int command_to_execute) { - context_menu_->ExecuteCommand(command_to_execute, 0); - } - - private: - void MenuShown(RenderViewContextMenu* context_menu) { - context_menu_ = context_menu; - run_loop_.Quit(); - } - - raw_ptr<RenderViewContextMenu> context_menu_; - base::RunLoop run_loop_; -}; - -#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || \ - BUILDFLAG(IS_LINUX) -// Disabled because it fails for mac specific context menu: -// TODO(crbug.com/1275253): Flakily crashes under Windows and Mac & Linux. -// TODO(crbug.com/1276463): Flakily crashes under lacros. -#define MAYBE_LinkGenerationTest DISABLED_LinkGenerationTest -#else -#define MAYBE_LinkGenerationTest LinkGenerationTest -#endif -IN_PROC_BROWSER_TEST_F(SharedHighlightingBrowserTest, - MAYBE_LinkGenerationTest) { - // Load the URL. - auto url = embedded_test_server()->GetURL("/test.html"); - ASSERT_NO_FATAL_FAILURE( - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url))); - - // Select the text, with element id 'selected'. - ASSERT_TRUE(SelectTextInCurrentTab()); - - // Find the coordinates to click at to show the context menu - int x = GetSelectionMidX(); - int y = GetSelectionMidY(); - - // Right-click on selection and wait for context menu to show up. - GenerationCompleteObserver generation_waiter; - ContextMenuObserver menu_waiter; - content::SimulateMouseClickAt(GetCurrentTab(), /*modifiers=*/0, - blink::WebMouseEvent::Button::kRight, - gfx::Point(x, y)); - menu_waiter.WaitForMenuShown(); - - // Wait until link to text generation is complete and "Copy link to text" menu - // option is enabled to execute that option. - generation_waiter.Wait(); - ASSERT_NE(std::string(), generation_waiter.GetSelector()); - menu_waiter.ExecuteCommand(IDC_CONTENT_CONTEXT_COPYLINKTOTEXT); - - // Get the generated link to text from clipboard. - std::u16string link_to_text_str = GetClipboardText(); - EXPECT_TRUE( - base::StartsWith(link_to_text_str, base::UTF8ToUTF16(url.spec()))); - - // Navigate to link to text in a new tab. - OpenInNewTab(GURL(link_to_text_str)); - - // Extract and check that highlighted text matches the selected text. - EXPECT_EQ("This is a test page", GetFirstHighlightedText()); -} - -class MockTextFragmentReceiver - : public blink::mojom::TextFragmentReceiverInterceptorForTesting { - public: - MockTextFragmentReceiver() = default; - ~MockTextFragmentReceiver() override = default; - - MockTextFragmentReceiver(const MockTextFragmentReceiver&) = delete; - MockTextFragmentReceiver& operator=(const MockTextFragmentReceiver&) = delete; - - TextFragmentReceiver* GetForwardingInterface() override { return this; } - - void Bind(mojo::ScopedMessagePipeHandle handle) { - bound_ = true; - receiver_.Bind(mojo::PendingReceiver<blink::mojom::TextFragmentReceiver>( - std::move(handle))); - } - - bool bound() { return bound_; } - - MOCK_METHOD1(GetExistingSelectors, void(GetExistingSelectorsCallback)); - - private: - mojo::Receiver<blink::mojom::TextFragmentReceiver> receiver_{this}; - bool bound_ = false; -}; - -class SharedHighlightingFencedFrameBrowserTest - : public SharedHighlightingBrowserTest { - public: - SharedHighlightingFencedFrameBrowserTest() { - feature_list_.InitAndEnableFeatures( - {feature_engagement::kIPHDesktopSharedHighlightingFeature}); - } - ~SharedHighlightingFencedFrameBrowserTest() override = default; - - void SetUpOnMainThread() override { - InProcessBrowserTest::SetUpOnMainThread(); - ASSERT_TRUE(embedded_test_server()->Start()); - } - - content::WebContents* web_contents() { - return browser()->tab_strip_model()->GetActiveWebContents(); - } - - content::test::FencedFrameTestHelper& fenced_frame_test_helper() { - return fenced_frame_helper_; - } - - private: - content::test::FencedFrameTestHelper fenced_frame_helper_; - feature_engagement::test::ScopedIphFeatureList feature_list_; -}; - -IN_PROC_BROWSER_TEST_F( - SharedHighlightingFencedFrameBrowserTest, - EnsureTextFragmentReceiverMojoMethodIsNotCalledForFencedFrame) { - GURL initial_url(embedded_test_server()->GetURL("/empty.html")); - EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - - GURL fenced_frame_url = - embedded_test_server()->GetURL("/fenced_frames/title1.html"); - content::RenderFrameHost* fenced_frame_host = - fenced_frame_test_helper().CreateFencedFrame( - web_contents()->GetPrimaryMainFrame(), fenced_frame_url); - ASSERT_TRUE(fenced_frame_host); - - // Intercept TextFragmentReceiver Mojo connection. - MockTextFragmentReceiver text_fragment_receiver; - service_manager::InterfaceProvider::TestApi interface_overrider( - web_contents()->GetPrimaryMainFrame()->GetRemoteInterfaces()); - interface_overrider.SetBinderForName( - blink::mojom::TextFragmentReceiver::Name_, - base::BindRepeating(&MockTextFragmentReceiver::Bind, - base::Unretained(&text_fragment_receiver))); - - // GetExistingSelectors method should not be called by the navigation on a - // fenced frame. - EXPECT_CALL(text_fragment_receiver, GetExistingSelectors(testing::_)) - .Times(0); - - GURL text_fragment_url = - embedded_test_server()->GetURL("/fenced_frames/title2.html#:~:text="); - fenced_frame_test_helper().NavigateFrameInFencedFrameTree(fenced_frame_host, - text_fragment_url); - EXPECT_FALSE(text_fragment_receiver.bound()); -} - -} // namespace shared_highlighting
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 1703002..a2e65c9 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -1345,6 +1345,8 @@ "tabs/saved_tab_groups/saved_tab_group_service_factory.h", "tabs/saved_tab_groups/saved_tab_group_utils.cc", "tabs/saved_tab_groups/saved_tab_group_utils.h", + "tabs/saved_tab_groups/saved_tab_group_web_contents_listener.cc", + "tabs/saved_tab_groups/saved_tab_group_web_contents_listener.h", "tabs/tab.h", "tabs/tab_change_type.h", "tabs/tab_group.cc", @@ -3520,7 +3522,10 @@ } else { allow_circular_includes_from += [ "//chrome/browser/ui/webui/bluetooth_internals" ] - deps += [ "//chrome/browser/ui/webui/bluetooth_internals" ] + deps += [ + "//chrome/browser/crash_upload_list", + "//chrome/browser/ui/webui/bluetooth_internals", + ] sources += [ "webui/crashes_ui.cc", "webui/crashes_ui.h",
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxFeatures.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxFeatures.java index f33a129..7ad5b67 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxFeatures.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxFeatures.java
@@ -32,6 +32,9 @@ private static final MutableFlagWithSafeDefault sOmniboxConsumesImeInsets = new MutableFlagWithSafeDefault(ChromeFeatureList.OMNIBOX_CONSUMERS_IME_INSETS, false); + private static final MutableFlagWithSafeDefault sShouldAdaptToNarrowTabletWindows = + new MutableFlagWithSafeDefault( + ChromeFeatureList.OMNIBOX_ADAPT_NARROW_TABLET_WINDOWS, false); /** * @param context The activity context. @@ -43,6 +46,14 @@ } /** + * Returns whether the omnibox dropdown should be switched to a phone-like appearance when the + * window width is <600dp. + */ + public static boolean shouldAdaptToNarrowTabletWindows() { + return sShouldAdaptToNarrowTabletWindows.isEnabled(); + } + + /** * @return Whether to show an active color for Omnibox which has a different background color * than toolbar. */
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImpl.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImpl.java index c196dd9a..38c51e9b 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImpl.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImpl.java
@@ -92,7 +92,7 @@ @Override public boolean isTablet() { - if (OmniboxFeatures.shouldShowModernizeVisualUpdate(mContext)) { + if (OmniboxFeatures.shouldAdaptToNarrowTabletWindows()) { return mWindowWidthDp >= DeviceFormFactor.MINIMUM_TABLET_WIDTH_DP; } else { return DeviceFormFactor.isWindowOnTablet(mWindowAndroid); @@ -155,7 +155,7 @@ mWindowWidthDp = windowWidth; mWindowHeightDp = windowHeight; - if (OmniboxFeatures.shouldShowModernizeVisualUpdate(mContext) + if (OmniboxFeatures.shouldAdaptToNarrowTabletWindows() || OmniboxFeatures.omniboxConsumesImeInsets()) { recalculateOmniboxAlignment(); }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImplTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImplTest.java index 871846b..c68b8da 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImplTest.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxSuggestionsDropdownEmbedderImplTest.java
@@ -153,7 +153,8 @@ @Test @Config(qualifiers = "w600dp-h820dp") - @EnableFeatures({ChromeFeatureList.OMNIBOX_MODERNIZE_VISUAL_UPDATE}) + @EnableFeatures({ChromeFeatureList.OMNIBOX_MODERNIZE_VISUAL_UPDATE, + ChromeFeatureList.OMNIBOX_ADAPT_NARROW_TABLET_WINDOWS}) @CommandLineFlags. Add({"enable-features=" + ChromeFeatureList.OMNIBOX_MODERNIZE_VISUAL_UPDATE + "<Study", "force-fieldtrials=Study/Group", @@ -179,7 +180,8 @@ } @Test - @EnableFeatures({ChromeFeatureList.OMNIBOX_MODERNIZE_VISUAL_UPDATE}) + @EnableFeatures({ChromeFeatureList.OMNIBOX_MODERNIZE_VISUAL_UPDATE, + ChromeFeatureList.OMNIBOX_ADAPT_NARROW_TABLET_WINDOWS}) @CommandLineFlags. Add({"enable-features=" + ChromeFeatureList.OMNIBOX_MODERNIZE_VISUAL_UPDATE + "<Study", "force-fieldtrials=Study/Group", @@ -205,6 +207,27 @@ } @Test + @EnableFeatures({ChromeFeatureList.OMNIBOX_ADAPT_NARROW_TABLET_WINDOWS}) + @Config(qualifiers = "w600dp-h820dp") + public void testRecalculateOmniboxAlignment_tabletToPhoneSwitch_revampDisabled() { + doReturn(mAnchorView).when(mHorizontalAlignmentView).getParent(); + doReturn(40).when(mHorizontalAlignmentView).getLeft(); + mImpl.recalculateOmniboxAlignment(); + OmniboxAlignment alignment = mImpl.getCurrentAlignment(); + assertEquals(new OmniboxAlignment(0, ANCHOR_HEIGHT + ANCHOR_TOP, ANCHOR_WIDTH, 0, 40, + ANCHOR_WIDTH - ALIGNMENT_WIDTH - 40), + alignment); + + Configuration newConfig = new Configuration(); + newConfig.smallestScreenWidthDp = DeviceFormFactor.MINIMUM_TABLET_WIDTH_DP - 1; + mImpl.onConfigurationChanged(newConfig); + assertFalse(mImpl.isTablet()); + OmniboxAlignment newAlignment = mImpl.getCurrentAlignment(); + assertEquals(new OmniboxAlignment(0, ANCHOR_HEIGHT + ANCHOR_TOP, ANCHOR_WIDTH, 0, 0, 0), + newAlignment); + } + + @Test @Config(qualifiers = "w600dp-h820dp") @EnableFeatures({ChromeFeatureList.OMNIBOX_MODERNIZE_VISUAL_UPDATE}) @CommandLineFlags.
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc index e906ce45..645f50d3 100644 --- a/chrome/browser/ui/browser_command_controller.cc +++ b/chrome/browser/ui/browser_command_controller.cc
@@ -795,6 +795,14 @@ case IDC_MANAGE_EXTENSIONS: ShowExtensions(browser_->GetBrowserForOpeningWebUi()); break; + case IDC_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS: + CHECK(base::FeatureList::IsEnabled(features::kExtensionsMenuInAppMenu)); + ShowExtensions(browser_->GetBrowserForOpeningWebUi()); + break; + case IDC_EXTENSIONS_SUBMENU_VISIT_CHROME_WEB_STORE: + CHECK(base::FeatureList::IsEnabled(features::kExtensionsMenuInAppMenu)); + ShowWebStore(browser_); + break; case IDC_PERFORMANCE: ShowSettingsSubPage(browser_->GetBrowserForOpeningWebUi(), chrome::kPerformanceSubPage); @@ -1254,6 +1262,7 @@ UpdateCommandsForContentRestrictionState(); UpdateCommandsForBookmarkEditing(); UpdateCommandsForIncognitoAvailability(); + UpdateCommandsForExtensionsMenu(); UpdateCommandsForTabKeyboardFocus(GetKeyboardFocusedTabIndex(browser_)); UpdateCommandsForWebContentsFocus(); } @@ -1314,6 +1323,22 @@ } } +void BrowserCommandController::UpdateCommandsForExtensionsMenu() { + // TODO(crbug.com/401026): Talk with isandrk@chromium.org about whether this + // is necessary for the experiment or not. + if (is_locked_fullscreen_) { + return; + } + + if (base::FeatureList::IsEnabled(features::kExtensionsMenuInAppMenu)) { + command_updater_.UpdateCommandEnabled( + IDC_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS, + /*state=*/true); + command_updater_.UpdateCommandEnabled( + IDC_EXTENSIONS_SUBMENU_VISIT_CHROME_WEB_STORE, /*state=*/true); + } +} + void BrowserCommandController::UpdateCommandsForTabState() { if (is_locked_fullscreen_) return;
diff --git a/chrome/browser/ui/browser_command_controller.h b/chrome/browser/ui/browser_command_controller.h index 448cabe..6cf9112a 100644 --- a/chrome/browser/ui/browser_command_controller.h +++ b/chrome/browser/ui/browser_command_controller.h
@@ -156,6 +156,9 @@ // app windows. void UpdateCommandsForHostedAppAvailability(); + // Update commands that are used in the Extensions menu in the app menu. + void UpdateCommandsForExtensionsMenu(); + #if BUILDFLAG(IS_CHROMEOS) // Update commands whose state depends on whether the window is in locked // fullscreen mode or not.
diff --git a/chrome/browser/ui/cocoa/l10n_util.mm b/chrome/browser/ui/cocoa/l10n_util.mm index 665aea7..09c6ab6 100644 --- a/chrome/browser/ui/cocoa/l10n_util.mm +++ b/chrome/browser/ui/cocoa/l10n_util.mm
@@ -8,7 +8,6 @@ #include "base/mac/mac_util.h" #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" -#import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h" namespace cocoa_l10n_util {
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc index 4173d07..672c3518 100644 --- a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc +++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
@@ -41,6 +41,7 @@ #include "components/infobars/content/content_infobar_manager.h" #include "components/infobars/core/infobar_delegate.h" #include "components/permissions/permission_decision_auto_blocker.h" +#include "components/permissions/permission_recovery_success_rate_tracker.h" #include "components/permissions/permission_result.h" #include "components/prefs/pref_service.h" #include "components/strings/grit/components_strings.h" @@ -79,6 +80,9 @@ std::make_unique<chrome::PageSpecificContentSettingsDelegate>( web_contents())); infobars::ContentInfoBarManager::CreateForWebContents(web_contents()); + + permissions::PermissionRecoverySuccessRateTracker::CreateForWebContents( + web_contents()); } TestingProfile::TestingFactories GetTestingFactories() const override {
diff --git a/chrome/browser/ui/content_settings/content_setting_media_image_model_unittest.mm b/chrome/browser/ui/content_settings/content_setting_media_image_model_unittest.mm index f723849..b0cdf8bd 100644 --- a/chrome/browser/ui/content_settings/content_setting_media_image_model_unittest.mm +++ b/chrome/browser/ui/content_settings/content_setting_media_image_model_unittest.mm
@@ -21,6 +21,7 @@ #include "components/content_settings/browser/page_specific_content_settings.h" #include "components/infobars/content/content_infobar_manager.h" #include "components/no_state_prefetch/browser/no_state_prefetch_manager.h" +#include "components/permissions/permission_recovery_success_rate_tracker.h" #include "components/prefs/pref_service.h" #include "components/vector_icons/vector_icons.h" #include "content/public/test/web_contents_tester.h" @@ -66,6 +67,9 @@ std::make_unique<chrome::PageSpecificContentSettingsDelegate>( web_contents())); infobars::ContentInfoBarManager::CreateForWebContents(web_contents()); + + permissions::PermissionRecoverySuccessRateTracker::CreateForWebContents( + web_contents()); } std::string GetDefaultAudioDevice() {
diff --git a/chrome/browser/ui/page_info/page_info_unittest.cc b/chrome/browser/ui/page_info/page_info_unittest.cc index d328d60a..e1094a5 100644 --- a/chrome/browser/ui/page_info/page_info_unittest.cc +++ b/chrome/browser/ui/page_info/page_info_unittest.cc
@@ -39,6 +39,7 @@ #include "components/page_info/core/features.h" #include "components/page_info/page_info_ui.h" #include "components/permissions/features.h" +#include "components/permissions/permission_recovery_success_rate_tracker.h" #include "components/safe_browsing/buildflags.h" #include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h" #include "components/strings/grit/components_strings.h" @@ -178,6 +179,9 @@ std::make_unique<chrome::PageSpecificContentSettingsDelegate>( web_contents())); + permissions::PermissionRecoverySuccessRateTracker::CreateForWebContents( + web_contents()); + // Setup mock ui. ResetMockUI(); }
diff --git a/chrome/browser/ui/quick_answers/ui/rich_answers_view.cc b/chrome/browser/ui/quick_answers/ui/rich_answers_view.cc index de2c3953..ebe83b3 100644 --- a/chrome/browser/ui/quick_answers/ui/rich_answers_view.cc +++ b/chrome/browser/ui/quick_answers/ui/rich_answers_view.cc
@@ -37,6 +37,9 @@ constexpr int kSettingsButtonSizeDip = 14; constexpr int kSettingsButtonBorderDip = 3; +// Border corner radius. +constexpr int kBorderCornerRadius = 12; + } // namespace // RichAnswersView ----------------------------------------------------------- @@ -80,8 +83,12 @@ void RichAnswersView::OnThemeChanged() { views::View::OnThemeChanged(); - SetBackground(views::CreateSolidBackground( + SetBorder(views::CreateRoundedRectBorder( + /*thickness=*/2, kBorderCornerRadius, GetColorProvider()->GetColor(ui::kColorPrimaryBackground))); + SetBackground(views::CreateRoundedRectBackground( + GetColorProvider()->GetColor(ui::kColorPrimaryBackground), + kBorderCornerRadius, /*for_border_thickness=*/2)); if (settings_button_) { settings_button_->SetImage( views::Button::ButtonState::STATE_NORMAL, @@ -125,6 +132,7 @@ params.shadow_type = views::Widget::InitParams::ShadowType::kDrop; params.type = views::Widget::InitParams::TYPE_POPUP; params.z_order = ui::ZOrderLevel::kFloatingUIElement; + params.corner_radius = kBorderCornerRadius; views::Widget* widget = new views::Widget(); widget->Init(std::move(params));
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc index 49f2349..dab3659 100644 --- a/chrome/browser/ui/tab_helpers.cc +++ b/chrome/browser/ui/tab_helpers.cc
@@ -129,6 +129,7 @@ #include "components/performance_manager/embedder/performance_manager_registry.h" #include "components/performance_manager/public/features.h" #include "components/permissions/features.h" +#include "components/permissions/permission_recovery_success_rate_tracker.h" #include "components/permissions/permission_request_manager.h" #include "components/permissions/unused_site_permissions_service.h" #include "components/safe_browsing/content/browser/safe_browsing_navigation_observer.h" @@ -375,6 +376,8 @@ pm_registry->SetPageType(web_contents, performance_manager::PageType::kTab); } permissions::PermissionRequestManager::CreateForWebContents(web_contents); + permissions::PermissionRecoverySuccessRateTracker::CreateForWebContents( + web_contents); // The PopupBlockerTabHelper has an implicit dependency on // ChromeSubresourceFilterClient being available in its constructor. blocked_content::PopupBlockerTabHelper::CreateForWebContents(web_contents);
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc index f9cddf7..eeaf5bb838 100644 --- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc +++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.cc
@@ -7,7 +7,6 @@ #include "base/containers/flat_set.h" #include "base/memory/raw_ptr.h" #include "base/ranges/algorithm.h" -#include "chrome/browser/favicon/favicon_utils.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_list.h" @@ -21,37 +20,6 @@ #include "content/public/browser/web_contents.h" #include "ui/base/page_transition_types.h" -SavedTabGroupWebContentsListener::SavedTabGroupWebContentsListener( - content::WebContents* web_contents, - base::Token token, - SavedTabGroupModel* model) - : token_(token), web_contents_(web_contents), model_(model) { - Observe(web_contents_); -} - -SavedTabGroupWebContentsListener::~SavedTabGroupWebContentsListener() = default; - -void SavedTabGroupWebContentsListener::DidFinishNavigation( - content::NavigationHandle* navigation_handle) { - ui::PageTransition page_transition = navigation_handle->GetPageTransition(); - if (!ui::IsValidPageTransitionType(page_transition) || - ui::PageTransitionIsRedirect(page_transition) || - !ui::PageTransitionIsMainFrame(page_transition)) { - return; - } - - SavedTabGroup* group = model_->GetGroupContainingTab(token_); - if (!group) { - return; - } - - SavedTabGroupTab* tab = group->GetTab(token_); - tab->SetTitle(web_contents_->GetTitle()); - tab->SetURL(web_contents_->GetURL()); - tab->SetFavicon(favicon::TabFaviconFromWebContents(web_contents_)); - model_->UpdateTabInGroup(group->saved_guid(), *tab); -} - // TODO(crbug/1376259): Update SavedTabGroupModel state with any groups that // should be in the SavedTabGroupModel. SavedTabGroupBrowserListener::SavedTabGroupBrowserListener(
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h index 13843fd..837cdca 100644 --- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h +++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_model_listener.h
@@ -8,36 +8,16 @@ #include "base/containers/flat_set.h" #include "base/memory/raw_ptr.h" #include "chrome/browser/ui/browser_list_observer.h" +#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_web_contents_listener.h" #include "chrome/browser/ui/tabs/tab_strip_model_observer.h" #include "components/tab_groups/tab_group_id.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/web_contents.h" -#include "content/public/browser/web_contents_observer.h" class SavedTabGroupModel; class TabStripModel; class Profile; -class SavedTabGroupWebContentsListener : public content::WebContentsObserver { - public: - SavedTabGroupWebContentsListener(content::WebContents* web_contents, - base::Token token, - SavedTabGroupModel* model); - ~SavedTabGroupWebContentsListener() override; - - // content::WebContentsObserver - void DidFinishNavigation( - content::NavigationHandle* navigation_handle) override; - - base::Token token() { return token_; } - content::WebContents* web_contents() { return web_contents_; } - - private: - base::Token token_; - raw_ptr<content::WebContents> web_contents_; - raw_ptr<SavedTabGroupModel> model_; -}; - // Manages the listening state for each individual tabstrip. class SavedTabGroupBrowserListener : public TabStripModelObserver { public:
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_web_contents_listener.cc b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_web_contents_listener.cc new file mode 100644 index 0000000..eb2f4ef --- /dev/null +++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_web_contents_listener.cc
@@ -0,0 +1,44 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_web_contents_listener.h" + +#include "chrome/browser/favicon/favicon_utils.h" +#include "components/saved_tab_groups/saved_tab_group.h" +#include "components/saved_tab_groups/saved_tab_group_model.h" +#include "components/saved_tab_groups/saved_tab_group_tab.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/browser/web_contents.h" +#include "ui/base/page_transition_types.h" + +SavedTabGroupWebContentsListener::SavedTabGroupWebContentsListener( + content::WebContents* web_contents, + base::Token token, + SavedTabGroupModel* model) + : token_(token), web_contents_(web_contents), model_(model) { + Observe(web_contents_); +} + +SavedTabGroupWebContentsListener::~SavedTabGroupWebContentsListener() = default; + +void SavedTabGroupWebContentsListener::DidFinishNavigation( + content::NavigationHandle* navigation_handle) { + ui::PageTransition page_transition = navigation_handle->GetPageTransition(); + if (!ui::IsValidPageTransitionType(page_transition) || + ui::PageTransitionIsRedirect(page_transition) || + !ui::PageTransitionIsMainFrame(page_transition)) { + return; + } + + SavedTabGroup* group = model_->GetGroupContainingTab(token_); + if (!group) { + return; + } + + SavedTabGroupTab* tab = group->GetTab(token_); + tab->SetTitle(web_contents_->GetTitle()); + tab->SetURL(web_contents_->GetURL()); + tab->SetFavicon(favicon::TabFaviconFromWebContents(web_contents_)); + model_->UpdateTabInGroup(group->saved_guid(), *tab); +}
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_web_contents_listener.h b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_web_contents_listener.h new file mode 100644 index 0000000..40c0782 --- /dev/null +++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_web_contents_listener.h
@@ -0,0 +1,38 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_TABS_SAVED_TAB_GROUPS_SAVED_TAB_GROUP_WEB_CONTENTS_LISTENER_H_ +#define CHROME_BROWSER_UI_TABS_SAVED_TAB_GROUPS_SAVED_TAB_GROUP_WEB_CONTENTS_LISTENER_H_ + +#include "base/token.h" +#include "content/public/browser/web_contents_observer.h" + +class SavedTabGroupModel; + +namespace content { +class NavigationHandle; +class WebContents; +} // namespace content + +class SavedTabGroupWebContentsListener : public content::WebContentsObserver { + public: + SavedTabGroupWebContentsListener(content::WebContents* web_contents, + base::Token token, + SavedTabGroupModel* model); + ~SavedTabGroupWebContentsListener() override; + + // content::WebContentsObserver + void DidFinishNavigation( + content::NavigationHandle* navigation_handle) override; + + base::Token token() { return token_; } + content::WebContents* web_contents() { return web_contents_; } + + private: + base::Token token_; + raw_ptr<content::WebContents> web_contents_; + raw_ptr<SavedTabGroupModel> model_; +}; + +#endif // CHROME_BROWSER_UI_TABS_SAVED_TAB_GROUPS_SAVED_TAB_GROUP_WEB_CONTENTS_LISTENER_H_
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc index 655ce235..cf694aa6 100644 --- a/chrome/browser/ui/toolbar/app_menu_model.cc +++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -110,9 +110,14 @@ DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kDownloadsMenuItem); DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kHistoryMenuItem); +DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kExtensionsMenuItem); DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kMoreToolsMenuItem); DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(AppMenuModel, kIncognitoMenuItem); DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ToolsMenuModel, kPerformanceMenuItem); +DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ExtensionsMenuModel, + kManageExtensionsMenuItem); +DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ExtensionsMenuModel, + kVisitChromeWebStoreMenuItem); namespace { @@ -246,7 +251,9 @@ AddSeparator(ui::NORMAL_SEPARATOR); AddItemWithStringId(IDC_CLEAR_BROWSING_DATA, IDS_CLEAR_BROWSING_DATA); - AddItemWithStringId(IDC_MANAGE_EXTENSIONS, IDS_SHOW_EXTENSIONS); + if (!base::FeatureList::IsEnabled(features::kExtensionsMenuInAppMenu)) { + AddItemWithStringId(IDC_MANAGE_EXTENSIONS, IDS_SHOW_EXTENSIONS); + } if (base::FeatureList::IsEnabled( performance_manager::features::kHighEfficiencyModeAvailable) || base::FeatureList::IsEnabled( @@ -270,6 +277,36 @@ } //////////////////////////////////////////////////////////////////////////////// +// ExtensionsMenuModel + +ExtensionsMenuModel::ExtensionsMenuModel( + ui::SimpleMenuModel::Delegate* delegate, + Browser* browser) + : SimpleMenuModel(delegate) { + Build(browser); +} + +ExtensionsMenuModel::~ExtensionsMenuModel() = default; + +// Extensions (sub)menu is constructed as follows: +// - An overflow with two items: +// - An item to manage extensions at chrome://extensions +// - An item to visit the Chrome Web Store +void ExtensionsMenuModel::Build(Browser* browser) { + AddItemWithStringId(IDC_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS, + IDS_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS_ITEM); + SetElementIdentifierAt( + GetIndexOfCommandId(IDC_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS).value(), + kManageExtensionsMenuItem); + AddItemWithStringId(IDC_EXTENSIONS_SUBMENU_VISIT_CHROME_WEB_STORE, + IDS_EXTENSIONS_SUBMENU_CHROME_WEBSTORE_ITEM); + SetElementIdentifierAt( + GetIndexOfCommandId(IDC_EXTENSIONS_SUBMENU_VISIT_CHROME_WEB_STORE) + .value(), + kVisitChromeWebStoreMenuItem); +} + +//////////////////////////////////////////////////////////////////////////////// // AppMenuModel // static @@ -478,7 +515,24 @@ } LogMenuAction(MENU_ACTION_PIN_TO_START_SCREEN); break; - + // Extensions menu. + case IDC_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS: + CHECK(base::FeatureList::IsEnabled(features::kExtensionsMenuInAppMenu)); + // Logging the original histograms for experiment comparison purposes. + if (!uma_action_recorded_) { + UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.ManageExtensions", + delta); + } + LogMenuAction(MENU_ACTION_MANAGE_EXTENSIONS); + break; + case IDC_EXTENSIONS_SUBMENU_VISIT_CHROME_WEB_STORE: + CHECK(base::FeatureList::IsEnabled(features::kExtensionsMenuInAppMenu)); + if (!uma_action_recorded_) { + UMA_HISTOGRAM_MEDIUM_TIMES( + "WrenchMenu.TimeToAction.VisitChromeWebStore", delta); + } + LogMenuAction(MENU_ACTION_VISIT_CHROME_WEB_STORE); + break; // Recent tabs menu. case IDC_RESTORE_TAB: if (!uma_action_recorded_) @@ -899,6 +953,15 @@ bookmark_sub_menu_model_.get()); } + if (base::FeatureList::IsEnabled(features::kExtensionsMenuInAppMenu)) { + // Extensions sub menu. + sub_menus_.push_back(std::make_unique<ExtensionsMenuModel>(this, browser_)); + AddSubMenuWithStringId(IDC_EXTENSIONS_SUBMENU, IDS_EXTENSIONS_SUBMENU, + sub_menus_.back().get()); + SetElementIdentifierAt(GetIndexOfCommandId(IDC_EXTENSIONS_SUBMENU).value(), + kExtensionsMenuItem); + } + AddSeparator(ui::LOWER_SEPARATOR); CreateZoomMenu(); AddSeparator(ui::UPPER_SEPARATOR);
diff --git a/chrome/browser/ui/toolbar/app_menu_model.h b/chrome/browser/ui/toolbar/app_menu_model.h index fa3ceed4..5d39d301 100644 --- a/chrome/browser/ui/toolbar/app_menu_model.h +++ b/chrome/browser/ui/toolbar/app_menu_model.h
@@ -84,6 +84,9 @@ MENU_ACTION_CHROME_WHATS_NEW = 54, MENU_ACTION_LACROS_DATA_MIGRATION = 55, MENU_ACTION_MENU_OPENED = 56, + // Only used by ExtensionsMenuModel sub menu. + MENU_ACTION_VISIT_CHROME_WEB_STORE = 57, + MENU_ACTION_MANAGE_EXTENSIONS_VIA_EXT_MENU = 58, LIMIT_MENU_ACTION }; @@ -121,6 +124,23 @@ void Build(Browser* browser); }; +class ExtensionsMenuModel : public ui::SimpleMenuModel { + public: + DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kManageExtensionsMenuItem); + DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kVisitChromeWebStoreMenuItem); + + ExtensionsMenuModel(ui::SimpleMenuModel::Delegate* delegate, + Browser* browser); + + ExtensionsMenuModel(const ExtensionsMenuModel&) = delete; + ExtensionsMenuModel& operator=(const ExtensionsMenuModel&) = delete; + + ~ExtensionsMenuModel() override; + + private: + void Build(Browser* browser); +}; + // A menu model that builds the contents of the app menu. class AppMenuModel : public ui::SimpleMenuModel, public ui::SimpleMenuModel::Delegate, @@ -130,6 +150,7 @@ public: DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kDownloadsMenuItem); DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kHistoryMenuItem); + DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kExtensionsMenuItem); DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kMoreToolsMenuItem); DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kIncognitoMenuItem);
diff --git a/chrome/browser/ui/toolbar/app_menu_model_interactive_uitest.cc b/chrome/browser/ui/toolbar/app_menu_model_interactive_uitest.cc index 616a2987..95ae0a9 100644 --- a/chrome/browser/ui/toolbar/app_menu_model_interactive_uitest.cc +++ b/chrome/browser/ui/toolbar/app_menu_model_interactive_uitest.cc
@@ -5,6 +5,7 @@ #include "base/logging.h" #include "base/test/bind.h" #include "base/test/gtest_util.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/accelerator_utils.h" @@ -13,6 +14,7 @@ #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/toolbar/app_menu_model.h" +#include "chrome/browser/ui/ui_features.h" #include "chrome/common/webui_url_constants.h" #include "chrome/test/base/interactive_test_utils.h" #include "chrome/test/base/ui_test_utils.h" @@ -21,6 +23,7 @@ #include "chrome/test/interaction/webcontents_interaction_test_util.h" #include "components/performance_manager/public/features.h" #include "content/public/test/browser_test.h" +#include "extensions/common/extension_urls.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/interaction/element_identifier.h" #include "ui/base/interaction/element_tracker.h" @@ -70,6 +73,8 @@ return new_browser->profile()->IsIncognitoProfile(); })); } + + base::test::ScopedFeatureList scoped_feature_list_; }; IN_PROC_BROWSER_TEST_F(AppMenuModelInteractiveTest, PerformanceNavigation) { @@ -97,3 +102,105 @@ SendAccelerator(kAppMenuButtonElementId, incognito_accelerator), CheckInconitoWindowOpened()); } + +class ExtensionsMenuModelInteractiveTest : public AppMenuModelInteractiveTest { + public: + explicit ExtensionsMenuModelInteractiveTest(bool enable_feature = true) { + scoped_feature_list_.InitWithFeatureState( + features::kExtensionsMenuInAppMenu, enable_feature); + } + ~ExtensionsMenuModelInteractiveTest() override = default; + ExtensionsMenuModelInteractiveTest( + const ExtensionsMenuModelInteractiveTest&) = delete; + void operator=(const ExtensionsMenuModelInteractiveTest&) = delete; + + void SetUp() override { + set_open_about_blank_on_browser_launch(true); + ASSERT_TRUE(embedded_test_server()->InitializeAndListen()); + InteractiveBrowserTest::SetUp(); + } + + protected: + base::HistogramTester histograms; +}; + +class ExtensionsMenuModelPresenceTest + : public ExtensionsMenuModelInteractiveTest, + public testing::WithParamInterface<bool> { + public: + ExtensionsMenuModelPresenceTest() + : ExtensionsMenuModelInteractiveTest(/*enable_feature=*/GetParam()) {} + ~ExtensionsMenuModelPresenceTest() override = default; + ExtensionsMenuModelPresenceTest(const ExtensionsMenuModelPresenceTest&) = + delete; + void operator=(const ExtensionsMenuModelPresenceTest&) = delete; +}; + +INSTANTIATE_TEST_SUITE_P( + All, + ExtensionsMenuModelPresenceTest, + /* features::kNewExtensionsTopLevelMenu status */ testing::Bool()); + +// Test to confirm that the structure of the Extensions menu is present but that +// no histograms are logged since it isn't interacted with. +IN_PROC_BROWSER_TEST_P(ExtensionsMenuModelPresenceTest, MenuPresence) { + if (GetParam()) { // Menu enabled + RunTestSequence( + InstrumentTab(kPrimaryTabPageElementId), + PressButton(kAppMenuButtonElementId), + EnsurePresent(AppMenuModel::kExtensionsMenuItem), + SelectMenuItem(AppMenuModel::kExtensionsMenuItem), + EnsurePresent(ExtensionsMenuModel::kManageExtensionsMenuItem), + EnsurePresent(ExtensionsMenuModel::kVisitChromeWebStoreMenuItem)); + } else { + RunTestSequence(InstrumentTab(kPrimaryTabPageElementId), + PressButton(kAppMenuButtonElementId), + EnsureNotPresent(AppMenuModel::kExtensionsMenuItem)); + } + + histograms.ExpectTotalCount("WrenchMenu.TimeToAction.VisitChromeWebStore", 0); + histograms.ExpectTotalCount("WrenchMenu.TimeToAction.ManageExtensions", 0); + histograms.ExpectBucketCount("WrenchMenu.MenuAction", + MENU_ACTION_MANAGE_EXTENSIONS, 0); + histograms.ExpectBucketCount("WrenchMenu.MenuAction", + MENU_ACTION_VISIT_CHROME_WEB_STORE, 0); +} + +// Test to confirm that the manage extensions menu item navigates when selected +// and emite histograms that it did so. +IN_PROC_BROWSER_TEST_F(ExtensionsMenuModelInteractiveTest, ManageExtensions) { + RunTestSequence( + InstrumentTab(kPrimaryTabPageElementId), + PressButton(kAppMenuButtonElementId), + SelectMenuItem(AppMenuModel::kExtensionsMenuItem), + SelectMenuItem(ExtensionsMenuModel::kManageExtensionsMenuItem), + WaitForWebContentsNavigation(kPrimaryTabPageElementId, + GURL(chrome::kChromeUIExtensionsURL))); + + histograms.ExpectTotalCount("WrenchMenu.TimeToAction.ManageExtensions", 1); + histograms.ExpectTotalCount("WrenchMenu.TimeToAction.VisitChromeWebStore", 0); + histograms.ExpectBucketCount("WrenchMenu.MenuAction", + MENU_ACTION_MANAGE_EXTENSIONS, 1); + histograms.ExpectBucketCount("WrenchMenu.MenuAction", + MENU_ACTION_VISIT_CHROME_WEB_STORE, 0); +} + +// Test to confirm that the visit Chome Web Store menu item navigates when +// selected and emits histograms that it did so. +IN_PROC_BROWSER_TEST_F(ExtensionsMenuModelInteractiveTest, + VisitChromeWebStore) { + RunTestSequence( + InstrumentTab(kPrimaryTabPageElementId), + PressButton(kAppMenuButtonElementId), + SelectMenuItem(AppMenuModel::kExtensionsMenuItem), + SelectMenuItem(ExtensionsMenuModel::kVisitChromeWebStoreMenuItem), + WaitForWebContentsNavigation(kPrimaryTabPageElementId, + extension_urls::GetWebstoreLaunchURL())); + + histograms.ExpectTotalCount("WrenchMenu.TimeToAction.VisitChromeWebStore", 1); + histograms.ExpectTotalCount("WrenchMenu.TimeToAction.ManageExtensions", 0); + histograms.ExpectBucketCount("WrenchMenu.MenuAction", + MENU_ACTION_VISIT_CHROME_WEB_STORE, 1); + histograms.ExpectBucketCount("WrenchMenu.MenuAction", + MENU_ACTION_MANAGE_EXTENSIONS, 0); +}
diff --git a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc index a7f0d08f..e7b711d 100644 --- a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc +++ b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
@@ -18,6 +18,7 @@ #include "chrome/browser/ui/global_error/global_error_service_factory.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/toolbar/app_menu_icon_controller.h" +#include "chrome/browser/ui/ui_features.h" #include "chrome/browser/upgrade_detector/upgrade_detector.h" #include "chrome/test/base/browser_with_test_window_test.h" #include "chrome/test/base/menu_model_test.h" @@ -115,8 +116,30 @@ ui::Accelerator* accelerator) const override { return false; } + + protected: + base::test::ScopedFeatureList feature_list_; }; +class ExtensionsMenuModelTest : public AppMenuModelTest, + public testing::WithParamInterface<bool> { + public: + ExtensionsMenuModelTest() { + feature_list_.InitWithFeatureState(features::kExtensionsMenuInAppMenu, + GetParam()); + } + + ExtensionsMenuModelTest(const ExtensionsMenuModelTest&) = delete; + ExtensionsMenuModelTest& operator=(const ExtensionsMenuModelTest&) = delete; + + ~ExtensionsMenuModelTest() override = default; +}; + +INSTANTIATE_TEST_SUITE_P( + All, + ExtensionsMenuModelTest, + /* features::kNewExtensionsTopLevelMenu enabled */ testing::Bool()); + // Copies parts of MenuModelTest::Delegate and combines them with the // AppMenuModel since AppMenuModel is now a SimpleMenuModel::Delegate and // not derived from SimpleMenuModel. @@ -258,6 +281,27 @@ EXPECT_EQ(1, error1->execute_count()); } +// Tests that extensions sub menu (when enabled) generates the correct elements +// or does not generate its elements when disabled. +TEST_P(ExtensionsMenuModelTest, ExtensionsMenu) { + AppMenuModel model(this, browser()); + model.Init(); + + if (GetParam()) { // Menu enabled + ASSERT_TRUE(model.GetIndexOfCommandId(IDC_EXTENSIONS_SUBMENU)); + ui::MenuModel* extensions_submenu = model.GetSubmenuModelAt( + model.GetIndexOfCommandId(IDC_EXTENSIONS_SUBMENU).value()); + ASSERT_NE(extensions_submenu, nullptr); + ASSERT_EQ(2ul, extensions_submenu->GetItemCount()); + EXPECT_EQ(IDC_EXTENSIONS_SUBMENU_MANAGE_EXTENSIONS, + extensions_submenu->GetCommandIdAt(0)); + EXPECT_EQ(IDC_EXTENSIONS_SUBMENU_VISIT_CHROME_WEB_STORE, + extensions_submenu->GetCommandIdAt(1)); + } else { + EXPECT_FALSE(model.GetIndexOfCommandId(IDC_EXTENSIONS_SUBMENU)); + } +} + TEST_F(AppMenuModelTest, EnabledPerformanceItem) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature(
diff --git a/chrome/browser/ui/ui_features.cc b/chrome/browser/ui/ui_features.cc index 0c0c115..9909e77 100644 --- a/chrome/browser/ui/ui_features.cc +++ b/chrome/browser/ui/ui_features.cc
@@ -48,6 +48,12 @@ #endif ); +// Create new Extensions app menu option (removing "More Tools -> Extensions") +// with submenu to manage extensions and visit chrome web store. +BASE_FEATURE(kExtensionsMenuInAppMenu, + "kExtensionsMenuInAppMenu", + base::FEATURE_DISABLED_BY_DEFAULT); + #if !defined(ANDROID) // Enables "Access Code Cast" UI. BASE_FEATURE(kAccessCodeCastUI,
diff --git a/chrome/browser/ui/ui_features.h b/chrome/browser/ui/ui_features.h index 12bb173a..79b6bab 100644 --- a/chrome/browser/ui/ui_features.h +++ b/chrome/browser/ui/ui_features.h
@@ -39,6 +39,8 @@ BASE_DECLARE_FEATURE(kChromeWhatsNewUI); +BASE_DECLARE_FEATURE(kExtensionsMenuInAppMenu); + #if !defined(ANDROID) BASE_DECLARE_FEATURE(kAccessCodeCastUI); #endif
diff --git a/chrome/browser/ui/views/collected_cookies_views_browsertest.cc b/chrome/browser/ui/views/collected_cookies_views_browsertest.cc index 0381a549..f435f5c 100644 --- a/chrome/browser/ui/views/collected_cookies_views_browsertest.cc +++ b/chrome/browser/ui/views/collected_cookies_views_browsertest.cc
@@ -26,6 +26,7 @@ #include "components/page_info/core/features.h" #include "content/public/common/content_paths.h" #include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "ui/base/interaction/element_identifier.h" #include "ui/events/base_event_utils.h" @@ -244,10 +245,7 @@ bool RunScriptAndGetBool(const std::string& script, content::WebContents* web_contents) { EXPECT_TRUE(web_contents); - bool data; - EXPECT_TRUE( - content::ExecuteScriptAndExtractBool(web_contents, script, &data)); - return data; + return content::EvalJs(web_contents, script).ExtractBool(); } base::test::ScopedFeatureList feature_list_;
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.h b/chrome/browser/ui/views/extensions/extensions_toolbar_container.h index a131512..8546a66a 100644 --- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.h +++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.h
@@ -196,16 +196,16 @@ std::string extension_id; }; - // Check if the ExtensionsMenuView or ExtensionsTabbedMenuView is showing. - // TODO(crbug.com/1279986): This method will be removed once - // ExtensionsTabbedMenu is fully rolled out and we will call directly into the - // ExtensionsTabbedMenuCoordinator. + // Check if the extensions menu is showing. + // TODO(crbug.com/1279986): This method will be removed once extensions menu + // under kExtensionsMenuAccessControl feature is fully rolled out and we can + // call directly into the menu coordinator. bool IsExtensionsMenuShowing() const; - // // Hides the currently-showing ExtensionsMenuView or - // ExtensionsTabbedMenuView, if it exists. TODO(crbug.com/1279986): This - // method will be removed once ExtensionsTabbedMenu is fully rolled out and we - // will call directly into the ExtensionsTabbedMenuCoordinator. + // Hides the currently-showing extensions menu, if it exists. + // TODO(crbug.com/1279986): This method will be removed once extensions menu + // under kExtensionsMenuAccessControl feature is fully rolled out and we can + // call directly into the menu coordinator. void HideExtensionsMenu(); // Determines whether an action must be visible (i.e. cannot be hidden for any
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h b/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h index 3937072..7ded1105b 100644 --- a/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h +++ b/chrome/browser/ui/views/extensions/extensions_toolbar_unittest.h
@@ -20,8 +20,7 @@ // Base class for unit tests that use the toolbar area. This is used for unit // tests that are generally related to the ExtensionsToolbarContainer in the -// ToolbarView area (such as ExtensionsToolbarControls and -// ExtensionsTabbedMenuView). +// ToolbarView area (e.g ExtensionsToolbarControls). // When possible, prefer creating a unit test with browser view instead of a // interactive ui or browser test since they are faster and less flaky. class ExtensionsToolbarUnitTest : public TestWithBrowserView {
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc index c04428e..a6a042b 100644 --- a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc +++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
@@ -39,6 +39,7 @@ #include "components/content_settings/core/common/pref_names.h" #include "components/history/core/browser/history_service.h" #include "components/page_info/core/features.h" +#include "components/permissions/permission_recovery_success_rate_tracker.h" #include "components/permissions/permission_uma_util.h" #include "components/permissions/permission_util.h" #include "components/privacy_sandbox/privacy_sandbox_features.h" @@ -430,6 +431,9 @@ web_contents)); api_ = std::make_unique<test::PageInfoBubbleViewTestApi>( parent_window_->GetNativeWindow(), web_contents); + + permissions::PermissionRecoverySuccessRateTracker::CreateForWebContents( + web_contents); } void TearDown() override {
diff --git a/chrome/browser/ui/views/toolbar/app_menu.cc b/chrome/browser/ui/views/toolbar/app_menu.cc index 8d7e86c..970f9ad 100644 --- a/chrome/browser/ui/views/toolbar/app_menu.cc +++ b/chrome/browser/ui/views/toolbar/app_menu.cc
@@ -35,6 +35,7 @@ #include "chrome/browser/ui/global_error/global_error_service_factory.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/toolbar/app_menu_model.h" +#include "chrome/browser/ui/ui_features.h" #include "chrome/browser/ui/user_education/scoped_new_badge_tracker.h" #include "chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h" #include "chrome/browser/ui/views/frame/app_menu_button.h" @@ -983,6 +984,11 @@ if (command_id == IDC_MORE_TOOLS_MENU) return true; + if (base::FeatureList::IsEnabled(features::kExtensionsMenuInAppMenu) && + command_id == IDC_EXTENSIONS_SUBMENU) { + return true; + } + if (command_id == IDC_SHARING_HUB_MENU) return true;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc index 3e4c8f7..1e2a289 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_view.cc +++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -160,6 +160,9 @@ return kViewCommandMap; } +constexpr int kToolbarDividerWidth = 2; +constexpr int kToolbarDividerHeight = 16; +constexpr int kToolbarDividerCornerRadius = 1; } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -241,12 +244,17 @@ base::BindRepeating(callback, browser_, IDC_HOME), prefs); std::unique_ptr<ExtensionsToolbarContainer> extensions_container; + std::unique_ptr<views::View> toolbar_divider; // Do not create the extensions or browser actions container if it is a guest // profile (only regular and incognito profiles host extensions). if (!browser_->profile()->IsGuestSession()) { extensions_container = std::make_unique<ExtensionsToolbarContainer>(browser_); + + if (features::IsChromeRefresh2023()) { + toolbar_divider = std::make_unique<views::View>(); + } } std::unique_ptr<media_router::CastToolbarButton> cast; if (media_router::MediaRouterEnabled(browser_->profile())) @@ -288,6 +296,12 @@ if (extensions_container) extensions_container_ = AddChildView(std::move(extensions_container)); + if (toolbar_divider) { + toolbar_divider_ = AddChildView(std::move(toolbar_divider)); + toolbar_divider_->SetPreferredSize( + gfx::Size(kToolbarDividerWidth, kToolbarDividerHeight)); + } + if (base::FeatureList::IsEnabled(features::kChromeLabs)) { chrome_labs_model_ = std::make_unique<ChromeLabsBubbleViewModel>(); UpdateChromeLabsNewBadgePrefs(browser_->profile(), @@ -728,6 +742,12 @@ extensions_flex_rule); } + if (toolbar_divider_) { + SkColor color = GetColorProvider()->GetColor(ui::kColorSysOutline); + toolbar_divider_->SetBackground( + views::CreateRoundedRectBackground(color, kToolbarDividerCornerRadius)); + } + LayoutCommon(); }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.h b/chrome/browser/ui/views/toolbar/toolbar_view.h index 129df5e..62db2b41 100644 --- a/chrome/browser/ui/views/toolbar/toolbar_view.h +++ b/chrome/browser/ui/views/toolbar/toolbar_view.h
@@ -266,6 +266,7 @@ raw_ptr<CustomTabBarView> custom_tab_bar_ = nullptr; raw_ptr<LocationBarView> location_bar_ = nullptr; raw_ptr<ExtensionsToolbarContainer> extensions_container_ = nullptr; + raw_ptr<views::View> toolbar_divider_ = nullptr; raw_ptr<ChromeLabsButton> chrome_labs_button_ = nullptr; raw_ptr<BatterySaverButton> battery_saver_button_ = nullptr; raw_ptr<media_router::CastToolbarButton> cast_ = nullptr;
diff --git a/chrome/browser/ui/views/user_education/tutorial_interactive_uitest.cc b/chrome/browser/ui/views/user_education/tutorial_interactive_uitest.cc index f834009..60edf50b 100644 --- a/chrome/browser/ui/views/user_education/tutorial_interactive_uitest.cc +++ b/chrome/browser/ui/views/user_education/tutorial_interactive_uitest.cc
@@ -97,10 +97,10 @@ UNCALLED_MOCK_CALLBACK(TutorialService::CompletedCallback, completed); UNCALLED_MOCK_CALLBACK(TutorialService::AbortedCallback, aborted); - const bool started = GetTutorialService()->StartTutorial( - kTestTutorialId, browser()->window()->GetElementContext(), - completed.Get(), aborted.Get()); - EXPECT_TRUE(started); + GetTutorialService()->StartTutorial(kTestTutorialId, + browser()->window()->GetElementContext(), + completed.Get(), aborted.Get()); + EXPECT_TRUE(GetTutorialService()->IsRunningTutorial()); ui::ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent( GetElement(kTabStripElementId), kCustomEventType1);
diff --git a/chrome/browser/ui/views/user_education/views_tutorial_unittest.cc b/chrome/browser/ui/views/user_education/views_tutorial_unittest.cc index 462b564..ccce1d5a 100644 --- a/chrome/browser/ui/views/user_education/views_tutorial_unittest.cc +++ b/chrome/browser/ui/views/user_education/views_tutorial_unittest.cc
@@ -149,10 +149,11 @@ kIndicatorElementId, "", kArrow); tutorial_registry_.AddTutorial(kTutorialId, std::move(desc)); - ASSERT_TRUE(tutorial_service_.StartTutorial( + tutorial_service_.StartTutorial( kTutorialId, views::ElementTrackerViews::GetContextForWidget(widget_.get()), - completed.Get(), aborted.Get())); + completed.Get(), aborted.Get()); + ASSERT_TRUE(tutorial_service_.IsRunningTutorial()); hide_button_on_press_ = true; views::test::InteractionTestUtilSimulatorViews::PressButton( @@ -198,10 +199,11 @@ kIndicatorElementId, "", kArrow); tutorial_registry_.AddTutorial(kTutorialId, std::move(desc)); - ASSERT_TRUE(tutorial_service_.StartTutorial( + tutorial_service_.StartTutorial( kTutorialId, views::ElementTrackerViews::GetContextForWidget(widget_.get()), - completed.Get(), aborted.Get())); + completed.Get(), aborted.Get()); + ASSERT_TRUE(tutorial_service_.IsRunningTutorial()); views::test::InteractionTestUtilSimulatorViews::PressButton( button_.get(), ui::test::InteractionTestUtil::InputType::kKeyboard);
diff --git a/chrome/browser/ui/webui/browser_command/browser_command_handler.cc b/chrome/browser/ui/webui/browser_command/browser_command_handler.cc index e82a915..17c328ff 100644 --- a/chrome/browser/ui/webui/browser_command/browser_command_handler.cc +++ b/chrome/browser/ui/webui/browser_command/browser_command_handler.cc
@@ -219,8 +219,9 @@ BrowserHasTabGroups() ? kTabGroupWithExistingGroupTutorialId : kTabGroupTutorialId; - bool started_tutorial = tutorial_service->StartTutorial(tutorial_id, context); - tutorial_service->LogStartedFromWhatsNewPage(tutorial_id, started_tutorial); + tutorial_service->StartTutorial(tutorial_id, context); + tutorial_service->LogStartedFromWhatsNewPage( + tutorial_id, tutorial_service->IsRunningTutorial()); } void BrowserCommandHandler::OpenFeedbackForm() {
diff --git a/chrome/browser/ui/webui/browser_command/browser_command_handler_unittest.cc b/chrome/browser/ui/webui/browser_command/browser_command_handler_unittest.cc index ad0ac965..9a24b97 100644 --- a/chrome/browser/ui/webui/browser_command/browser_command_handler_unittest.cc +++ b/chrome/browser/ui/webui/browser_command/browser_command_handler_unittest.cc
@@ -130,13 +130,18 @@ return std::u16string(); } - bool StartTutorial( + void StartTutorial( user_education::TutorialIdentifier id, ui::ElementContext context, base::OnceClosure completed_callback = base::DoNothing(), base::OnceClosure aborted_callback = base::DoNothing()) override { - return true; + running_ = true; } + + bool IsRunningTutorial() const override { return running_; } + + private: + bool running_ = false; }; class MockTutorialService : public TestTutorialService { @@ -148,12 +153,13 @@ ~MockTutorialService() override = default; MOCK_METHOD4(StartTutorial, - bool(user_education::TutorialIdentifier, + void(user_education::TutorialIdentifier, ui::ElementContext, base::OnceClosure, base::OnceClosure)); MOCK_METHOD2(LogStartedFromWhatsNewPage, void(user_education::TutorialIdentifier, bool)); + MOCK_CONST_METHOD0(IsRunningTutorial, bool()); }; class MockCommandHandler : public TestCommandHandler { @@ -465,7 +471,8 @@ ClickInfoPtr info = ClickInfo::New(); EXPECT_CALL(service, StartTutorial(kTabGroupTutorialId, kTestContext1, testing::_, testing::_)) - .WillOnce(testing::Return(true)); + .Times(1); + EXPECT_CALL(service, IsRunningTutorial).WillOnce(testing::Return(true)); EXPECT_CALL(service, LogStartedFromWhatsNewPage(kTabGroupTutorialId, true)); EXPECT_TRUE( ExecuteCommand(Command::kStartTabGroupTutorial, std::move(info))); @@ -478,7 +485,8 @@ ClickInfoPtr info = ClickInfo::New(); EXPECT_CALL(service, StartTutorial(kTabGroupWithExistingGroupTutorialId, kTestContext1, testing::_, testing::_)) - .WillOnce(testing::Return(true)); + .Times(1); + EXPECT_CALL(service, IsRunningTutorial).WillOnce(testing::Return(true)); EXPECT_CALL(service, LogStartedFromWhatsNewPage( kTabGroupWithExistingGroupTutorialId, true)); EXPECT_TRUE(
diff --git a/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc b/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc index e3af8d3..ce78bb5 100644 --- a/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc +++ b/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc
@@ -153,8 +153,9 @@ const ui::ElementContext context = chrome::FindBrowserWithProfile(profile_)->window()->GetElementContext(); std::string result; - if (!tutorial_service_->StartTutorial(tutorial_id, context)) { - result = "Cannot start tutorial."; + tutorial_service_->StartTutorial(tutorial_id, context); + if (!tutorial_service_->IsRunningTutorial()) { + result = "Failed to start tutorial " + tutorial_id; } std::move(callback).Run(result); }
diff --git a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.cc b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.cc index ee9eb71e..f9f0cf1 100644 --- a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.cc +++ b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.cc
@@ -41,6 +41,24 @@ device_id, std::move(settings)); } +void InputDeviceSettingsProvider::SetPointingStickSettings( + uint32_t device_id, + ::ash::mojom::PointingStickSettingsPtr settings) { + DCHECK(features::IsInputDeviceSettingsSplitEnabled()); + DCHECK(InputDeviceSettingsController::Get()); + InputDeviceSettingsController::Get()->SetPointingStickSettings( + device_id, std::move(settings)); +} + +void InputDeviceSettingsProvider::SetMouseSettings( + uint32_t device_id, + ::ash::mojom::MouseSettingsPtr settings) { + DCHECK(features::IsInputDeviceSettingsSplitEnabled()); + DCHECK(InputDeviceSettingsController::Get()); + InputDeviceSettingsController::Get()->SetMouseSettings(device_id, + std::move(settings)); +} + void InputDeviceSettingsProvider::GetConnectedKeyboards( GetConnectedKeyboardsCallback callback) { DCHECK(features::IsInputDeviceSettingsSplitEnabled());
diff --git a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.h b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.h index f435ec94..8531f9b 100644 --- a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.h +++ b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.h
@@ -41,6 +41,11 @@ mojo::PendingRemote<mojom::MouseSettingsObserver> observer) override; void SetKeyboardSettings(uint32_t device_id, ::ash::mojom::KeyboardSettingsPtr settings) override; + void SetPointingStickSettings( + uint32_t device_id, + ::ash::mojom::PointingStickSettingsPtr settings) override; + void SetMouseSettings(uint32_t device_id, + ::ash::mojom::MouseSettingsPtr settings) override; // InputDeviceSettingsController::Observer: void OnKeyboardConnected(const ::ash::mojom::Keyboard& keyboard) override;
diff --git a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.mojom b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.mojom index 52c7b427..ff429f92 100644 --- a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.mojom +++ b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider.mojom
@@ -61,4 +61,9 @@ ObserveMouseSettings(pending_remote<MouseSettingsObserver> observer); // Sets the keyboard settings based on its id. SetKeyboardSettings(uint32 device_id, ash.mojom.KeyboardSettings settings); + // Sets the point stick settings based on its id. + SetPointingStickSettings( + uint32 device_id, ash.mojom.PointingStickSettings settings); + // Sets the mouse settings based on its id. + SetMouseSettings(uint32 device_id, ash.mojom.MouseSettings settings); };
diff --git a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc index 1cbc38ff..55da01d 100644 --- a/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc +++ b/chrome/browser/ui/webui/settings/ash/input_device_settings/input_device_settings_provider_unittest.cc
@@ -214,10 +214,14 @@ DeviceId id, ::ash::mojom::TouchpadSettingsPtr settings) override {} void SetMouseSettings(DeviceId id, - ::ash::mojom::MouseSettingsPtr settings) override {} + ::ash::mojom::MouseSettingsPtr settings) override { + ++num_times_set_mouse_settings_called_; + } void SetPointingStickSettings( DeviceId id, - ::ash::mojom::PointingStickSettingsPtr settings) override {} + ::ash::mojom::PointingStickSettingsPtr settings) override { + ++num_times_set_pointing_stick_settings_called_; + } void AddKeyboard(::ash::mojom::KeyboardPtr keyboard) { keyboards_.push_back(std::move(keyboard)); @@ -285,6 +289,12 @@ int num_times_set_keyboard_settings_called() { return num_times_set_keyboard_settings_called_; } + int num_times_set_pointing_stick_settings_called() { + return num_times_set_pointing_stick_settings_called_; + } + int num_times_set_mouse_settings_called() { + return num_times_set_mouse_settings_called_; + } private: std::vector<::ash::mojom::KeyboardPtr> keyboards_; @@ -293,6 +303,8 @@ std::vector<::ash::mojom::PointingStickPtr> pointing_sticks_; raw_ptr<InputDeviceSettingsController::Observer> observer_ = nullptr; int num_times_set_keyboard_settings_called_ = 0; + int num_times_set_pointing_stick_settings_called_ = 0; + int num_times_set_mouse_settings_called_ = 0; }; } // namespace @@ -352,6 +364,36 @@ EXPECT_EQ(2, controller_->num_times_set_keyboard_settings_called()); } +TEST_F(InputDeviceSettingsProviderTest, TestSetPointingStickSettings) { + controller_->AddPointingStick(kPointingStick1.Clone()); + provider_->SetPointingStickSettings(kPointingStick1.id, + kPointingStick1.settings->Clone()); + + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, controller_->num_times_set_pointing_stick_settings_called()); + + controller_->AddPointingStick(kPointingStick2.Clone()); + provider_->SetPointingStickSettings(kPointingStick2.id, + kPointingStick1.settings->Clone()); + + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(2, controller_->num_times_set_pointing_stick_settings_called()); +} + +TEST_F(InputDeviceSettingsProviderTest, TestSetMouseSettings) { + controller_->AddMouse(kMouse1.Clone()); + provider_->SetMouseSettings(kMouse1.id, kMouse1.settings->Clone()); + + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, controller_->num_times_set_mouse_settings_called()); + + controller_->AddMouse(kMouse2.Clone()); + provider_->SetMouseSettings(kMouse2.id, kMouse1.settings->Clone()); + + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(2, controller_->num_times_set_mouse_settings_called()); +} + TEST_F(InputDeviceSettingsProviderTest, TestKeyboardSettingsObeserver) { std::vector<::ash::mojom::KeyboardPtr> expected_keyboards; expected_keyboards.push_back(kKeyboard1.Clone());
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_ui.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_ui.cc index b8e6995e..6ffc440 100644 --- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_ui.cc +++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_ui.cc
@@ -33,6 +33,7 @@ {"readAnythingTabTitle", IDS_READING_MODE_TITLE}, {"emptyStateHeader", IDS_READING_MODE_EMPTY_STATE_HEADER}, {"emptyStateSubheader", IDS_READING_MODE_EMPTY_STATE_SUBHEADER}, + {"readAnythingLoadingMessage", IDS_READ_ANYTHING_LOADING}, }; for (const auto& str : kLocalizedStrings) webui::AddLocalizedString(source, str.name, str.id);
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 74321fa1..2fe8b92a 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1678391859-7e800eff6c1857fe3d22260194bc4394b5b36fc1.profdata +chrome-mac-arm-main-1678406398-665c7ee9471649e8f179036c5c41bb0c29fb3c46.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index 71e5324..faaea23b 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1678384789-1eea5759f041a9f55459e1641218ffd1db75330b.profdata +chrome-win32-main-1678395580-5ce0b858a568fd95838528e0ff77940b3a4d1ef6.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 8a23851..6fe1bcf43 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1678373835-19315deb83b25609d86dc667bf2ac3470c3afb02.profdata +chrome-win64-main-1678384789-e53b532a015daff2f9f2708d571a3e6ba2f04dbb.profdata
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn index b55dbfe4..a30b315 100644 --- a/chrome/common/BUILD.gn +++ b/chrome/common/BUILD.gn
@@ -710,10 +710,7 @@ # linkage and will correctly emplace over the correct symbols in # delayimp.lib at link time. source_set("delay_load_support") { - sources = [ - "win/delay_load_failure_hook.cc", - "win/delay_load_failure_hook.h", - ] + sources = [ "win/delay_load_failure_hook.cc" ] deps = [ "//base" ] }
diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc index 4b62591..fff26f3 100644 --- a/chrome/common/extensions/permissions/chrome_api_permissions.cc +++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc
@@ -151,8 +151,6 @@ APIPermissionInfo::kFlagCannotBeOptional}, {APIPermissionID::kEnterpriseReportingPrivate, "enterprise.reportingPrivate", APIPermissionInfo::kFlagCannotBeOptional}, - {APIPermissionID::kFileBrowserHandlerInternal, "fileBrowserHandlerInternal", - APIPermissionInfo::kFlagCannotBeOptional}, {APIPermissionID::kFileManagerPrivate, "fileManagerPrivate", APIPermissionInfo::kFlagCannotBeOptional}, {APIPermissionID::kIdentityPrivate, "identityPrivate",
diff --git a/chrome/common/extensions/permissions/chrome_api_permissions_unittest.cc b/chrome/common/extensions/permissions/chrome_api_permissions_unittest.cc index 851d40a..f0f6d610 100644 --- a/chrome/common/extensions/permissions/chrome_api_permissions_unittest.cc +++ b/chrome/common/extensions/permissions/chrome_api_permissions_unittest.cc
@@ -26,13 +26,8 @@ ASSERT_EQ(1u, all_api_permissions.count(mojom::APIPermissionID::kStorage)); std::string kKnownBad[] = { - "bookmarkManagerPrivate", - "fileBrowserHandlerInternal", - "homepage", - "searchProvider", - "startupPages", - "tabCaptureForTab", - "newTabPageOverride", + "homepage", "searchProvider", "startupPages", + "tabCaptureForTab", "newTabPageOverride", }; const FeatureProvider* permission_features =
diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc index 94fa3a6..266e61d 100644 --- a/chrome/common/extensions/permissions/permission_set_unittest.cc +++ b/chrome/common/extensions/permissions/permission_set_unittest.cc
@@ -835,7 +835,6 @@ skip.insert(APIPermissionID::kEchoPrivate); skip.insert(APIPermissionID::kEnterprisePlatformKeysPrivate); skip.insert(APIPermissionID::kFeedbackPrivate); - skip.insert(APIPermissionID::kFileBrowserHandlerInternal); skip.insert(APIPermissionID::kFileManagerPrivate); skip.insert(APIPermissionID::kFirstRunPrivate); skip.insert(APIPermissionID::kSharedStoragePrivate); @@ -1757,16 +1756,6 @@ EXPECT_FALSE(perm_set->IsEmpty()); } -TEST(PermissionsTest, ImpliedPermissions) { - APIPermissionSet apis; - apis.insert(APIPermissionID::kFileBrowserHandler); - EXPECT_EQ(1U, apis.size()); - - PermissionSet perm_set(std::move(apis), ManifestPermissionSet(), - URLPatternSet(), URLPatternSet()); - EXPECT_EQ(2U, perm_set.apis().size()); -} - TEST(PermissionsTest, SyncFileSystemPermission) { scoped_refptr<Extension> extension = LoadManifest( "permissions", "sync_file_system.json");
diff --git a/chrome/common/win/delay_load_failure_hook.cc b/chrome/common/win/delay_load_failure_hook.cc index b8ec34d7..91e28a9 100644 --- a/chrome/common/win/delay_load_failure_hook.cc +++ b/chrome/common/win/delay_load_failure_hook.cc
@@ -15,15 +15,10 @@ namespace { -bool g_hooks_enabled = true; - // Delay load failure hook that generates a crash report. By default a failure // to delay load will trigger an exception handled by the delay load runtime and // this won't generate a crash report. FARPROC WINAPI DelayLoadFailureHook(unsigned reason, DelayLoadInfo* dll_info) { - if (!g_hooks_enabled) - return 0; - char dll_name[MAX_PATH]; base::strlcpy(dll_name, dll_info->szDll, std::size(dll_name)); // It's not an error if "bthprops.cpl" fails to be loaded, there's a custom @@ -42,10 +37,6 @@ } // namespace -void DisableDelayLoadFailureHooksForCurrentModule() { - g_hooks_enabled = false; -} - } // namespace chrome // Set the delay load failure hook to the function above.
diff --git a/chrome/common/win/delay_load_failure_hook.h b/chrome/common/win/delay_load_failure_hook.h deleted file mode 100644 index f12bf11..0000000 --- a/chrome/common/win/delay_load_failure_hook.h +++ /dev/null
@@ -1,16 +0,0 @@ -// 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_COMMON_WIN_DELAY_LOAD_FAILURE_HOOK_H_ -#define CHROME_COMMON_WIN_DELAY_LOAD_FAILURE_HOOK_H_ - -namespace chrome { - -// This should be called early in process startup (before any delay load -// failures) to disable the delay load hooks for the current module. -void DisableDelayLoadFailureHooksForCurrentModule(); - -} // namespace chrome - -#endif // CHROME_COMMON_WIN_DELAY_LOAD_FAILURE_HOOK_H_
diff --git a/chrome/common/win/delay_load_failure_hook_unittest.cc b/chrome/common/win/delay_load_failure_hook_unittest.cc index 8165ad8a..8801398f 100644 --- a/chrome/common/win/delay_load_failure_hook_unittest.cc +++ b/chrome/common/win/delay_load_failure_hook_unittest.cc
@@ -6,10 +6,26 @@ #include <delayimp.h> +#include "base/test/gtest_util.h" #include "testing/gtest/include/gtest/gtest.h" TEST(ChromeDelayLoadHookTest, HooksAreSetAtLinkTime) { // This test verifies that delay load hooks are correctly in place for the // current module. - EXPECT_NE(__pfnDliFailureHook2, nullptr); + ASSERT_NE(__pfnDliFailureHook2, nullptr); + + // Subtle: In tests, unit_tests.exe is linked with + // chrome/common/win/delay_load_failure_hook.cc not + // chrome/app/delay_load_failure_hook_win.cc. So, __pfnDliFailureHook2 will + // always call DelayLoadFailureHook and not DelayLoadFailureHookEXE despite + // existing in unit_tests.exe. + // + // In production chrome.exe, __pfnDliFailureHook2 is instead backed by + // DelayLoadFailureHookEXE in chrome/app/delay_load_failure_hook_win.cc, while + // in chrome.dll, __pfnDliFailureHook2 is backed by DelayLoadFailureHook from + // chrome/common/win/delay_load_failure_hook.cc. + // + // This test verifies DelayLoadFailureHook crashes. + DelayLoadInfo dli = {.szDll = "test.dll"}; + EXPECT_CHECK_DEATH({ __pfnDliFailureHook2(dliFailLoadLib, &dli); }); }
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything_app_controller.cc index 0326a58a..19a30ace 100644 --- a/chrome/renderer/accessibility/read_anything_app_controller.cc +++ b/chrome/renderer/accessibility/read_anything_app_controller.cc
@@ -415,6 +415,10 @@ ui::AXTreeSerializer<const ui::AXNode*> serializer(tree_source.get()); ui::AXTreeUpdate snapshot; CHECK(serializer.SerializeChanges(tree->root(), &snapshot)); + // TODO(b/1266555): Use v8::Function rather than javascript. If possible, + // replace this function call with firing an event. + std::string script = "chrome.readAnything.showLoading();"; + render_frame_->ExecuteJavaScript(base::ASCIIToUTF16(script)); model_.SetDistillationInProgress(true); distiller_->Distill(*tree, snapshot, model_.active_ukm_source_id()); }
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl.cc b/chrome/services/speech/speech_recognition_recognizer_impl.cc index b705029..734ea2e 100644 --- a/chrome/services/speech/speech_recognition_recognizer_impl.cc +++ b/chrome/services/speech/speech_recognition_recognizer_impl.cc
@@ -13,6 +13,7 @@ #include "base/files/file_util.h" #include "base/functional/bind.h" #include "base/metrics/histogram_functions.h" +#include "base/strings/strcat.h" #include "base/strings/string_util.h" #include "base/task/bind_post_task.h" #include "base/task/sequenced_task_runner.h" @@ -121,6 +122,10 @@ } // namespace SpeechRecognitionRecognizerImpl::~SpeechRecognitionRecognizerImpl() { + base::UmaHistogramBoolean( + base::StrCat({"Accessibility.LiveCaption.", primary_language_name_, + ".SessionContainsRecognizedSpeech"}), + session_contains_speech_); RecordDuration(); soda_client_.reset(); } @@ -145,6 +150,10 @@ void SpeechRecognitionRecognizerImpl::OnRecognitionEvent( media::SpeechRecognitionResult event) { + if (!event.transcription.empty()) { + session_contains_speech_ = true; + } + if (!client_remote_.is_bound()) return;
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl.h b/chrome/services/speech/speech_recognition_recognizer_impl.h index af9fc99..d2d33ec6 100644 --- a/chrome/services/speech/speech_recognition_recognizer_impl.h +++ b/chrome/services/speech/speech_recognition_recognizer_impl.h
@@ -160,6 +160,10 @@ // Whether the client is still requesting speech recognition. bool is_client_requesting_speech_recognition_ = true; + // Whether the speech recognition session contains any recognized speech. Used + // for logging purposes only. + bool session_contains_speech_ = false; + scoped_refptr<base::SequencedTaskRunner> task_runner_; base::WeakPtrFactory<SpeechRecognitionRecognizerImpl> weak_factory_{this};
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 07e2ef6..0a0f2dc 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -2134,7 +2134,6 @@ "../browser/sessions/session_restore_observer_browsertest.cc", "../browser/sessions/session_service_log_browsertest.cc", "../browser/sessions/tab_restore_browsertest.cc", - "../browser/shared_highlighting/shared_highlighting_browsertest.cc", "../browser/signin/e2e_tests/account_capabilities_observer.cc", "../browser/signin/e2e_tests/account_capabilities_observer.h", "../browser/signin/e2e_tests/accounts_removed_waiter.cc", @@ -5822,12 +5821,6 @@ [ "../browser/policy/browser_dm_token_storage_linux_unittest.cc" ] } - if (is_chromeos) { - sources += [ - "../browser/crash_upload_list/crash_upload_list_chromeos_unittest.cc", - ] - } - if (is_chromeos_ash) { sources += [ "../browser/metrics/chrome_metrics_service_client_ash_unittest.cc", @@ -5954,6 +5947,7 @@ "//chrome/browser/breadcrumbs", "//chrome/browser/breadcrumbs:unit_tests", "//chrome/browser/browsing_data:constants", + "//chrome/browser/crash_upload_list:unit_tests", "//chrome/browser/devtools", "//chrome/browser/dips:unit_tests", "//chrome/browser/enterprise/identifiers",
diff --git a/chrome/test/data/extensions/api_test/history/incognito/incognito.js b/chrome/test/data/extensions/api_test/history/incognito/incognito.js index 2752330a..7253b6ee 100644 --- a/chrome/test/data/extensions/api_test/history/incognito/incognito.js +++ b/chrome/test/data/extensions/api_test/history/incognito/incognito.js
@@ -7,14 +7,14 @@ function addItem() { chrome.history.addUrl({url: 'http://www.a.com/'}, function() { - window.domAutomationController.send('success'); + chrome.test.sendScriptResult('success'); }); } function countItemsInHistory() { var query = {'text': ''}; chrome.history.search(query, function(results) { - window.domAutomationController.send(results.length.toString()); + chrome.test.sendScriptResult(results.length.toString()); }); }
diff --git a/chrome/test/data/extensions/api_test/history/incognito/manifest.json b/chrome/test/data/extensions/api_test/history/incognito/manifest.json index 44b23b7..e0065826a 100644 --- a/chrome/test/data/extensions/api_test/history/incognito/manifest.json +++ b/chrome/test/data/extensions/api_test/history/incognito/manifest.json
@@ -6,6 +6,7 @@ "permissions": ["history"], "incognito": "split", "background": { - "scripts": ["incognito.js"] + "scripts": ["incognito.js"], + "persistent": true } }
diff --git a/chrome/test/data/extensions/api_test/history/regular/a.html b/chrome/test/data/extensions/api_test/history/regular/a.html deleted file mode 100644 index a8791cba..0000000 --- a/chrome/test/data/extensions/api_test/history/regular/a.html +++ /dev/null
@@ -1,13 +0,0 @@ -<!-- -Copyright 2011 The Chromium Authors -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. ---> -<html> -<head> - <title>www.a.com</title> -</head> -<body> - 1234567890 -</body> -</html>
diff --git a/chrome/test/data/extensions/api_test/history/regular/b.html b/chrome/test/data/extensions/api_test/history/regular/b.html deleted file mode 100644 index 5afddb55..0000000 --- a/chrome/test/data/extensions/api_test/history/regular/b.html +++ /dev/null
@@ -1,13 +0,0 @@ -<!-- -Copyright 2011 The Chromium Authors -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. ---> -<html> -<head> - <title>www.b.com</title> -</head> -<body> - ABCDEFGHIJKLMNOPQRSTUVWXYZ -</body> -</html>
diff --git a/chrome/test/data/extensions/api_test/history/regular/common.js b/chrome/test/data/extensions/api_test/history/regular/common.js index 0768f11..6efaa93 100644 --- a/chrome/test/data/extensions/api_test/history/regular/common.js +++ b/chrome/test/data/extensions/api_test/history/regular/common.js
@@ -10,12 +10,6 @@ var GOOGLE_URL = 'http://www.google.com/'; var PICASA_URL = 'http://www.picasa.com/'; -// PORT will be changed to the port of the test server. -var A_RELATIVE_URL = - 'http://www.a.com:PORT/extensions/api_test/history/a.html'; -var B_RELATIVE_URL = - 'http://www.b.com:PORT/extensions/api_test/history/b.html'; - /** * Object used for listening to the chrome.history.onVisited events. The * global object 'itemVisitedCallback' stores the last item received. @@ -108,15 +102,7 @@ * @param {Array<function>} testFns The tests to run. */ function runHistoryTestFns(testFns) { - chrome.test.getConfig(function(config) { - var fixPort = function(url) { - return url.replace(/PORT/, config.testServer.port); - }; - A_RELATIVE_URL = fixPort(A_RELATIVE_URL); - B_RELATIVE_URL = fixPort(B_RELATIVE_URL); - - chrome.test.runTests(testFns); - }); + chrome.test.runTests(testFns); } /**
diff --git a/chrome/test/data/extensions/api_test/history/regular/delete.html b/chrome/test/data/extensions/api_test/history/regular/delete.html deleted file mode 100644 index b0983ab..0000000 --- a/chrome/test/data/extensions/api_test/history/regular/delete.html +++ /dev/null
@@ -1,7 +0,0 @@ -<!-- -Copyright 2011 The Chromium Authors -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. ---> -<script src="common.js"></script> -<script src="delete.js"></script>
diff --git a/chrome/test/data/extensions/api_test/history/regular/delete.js b/chrome/test/data/extensions/api_test/history/regular/delete/delete.js similarity index 96% rename from chrome/test/data/extensions/api_test/history/regular/delete.js rename to chrome/test/data/extensions/api_test/history/regular/delete/delete.js index 2270a173..c2f89b4 100644 --- a/chrome/test/data/extensions/api_test/history/regular/delete.js +++ b/chrome/test/data/extensions/api_test/history/regular/delete/delete.js
@@ -5,8 +5,11 @@ // History api test for Chrome. // browser_tests.exe --gtest_filter=HistoryApiTest.Delete -// runHistoryTestFns is defined in ./common.js . -runHistoryTestFns([ +const scriptUrl = '_test_resources/api_test/history/regular/common.js'; +let loadScript = chrome.test.loadScript(scriptUrl); + +loadScript.then(async function() { +chrome.test.runTests([ // All the tests require a blank state to start from. If this fails, // expect all other history tests (HistoryExtensionApiTest.*) to fail. function clearHistory() { @@ -172,4 +175,4 @@ }); }); } -]); +])});
diff --git a/chrome/test/data/extensions/api_test/history/regular/delete/manifest.json b/chrome/test/data/extensions/api_test/history/regular/delete/manifest.json new file mode 100644 index 0000000..5a7217f --- /dev/null +++ b/chrome/test/data/extensions/api_test/history/regular/delete/manifest.json
@@ -0,0 +1,11 @@ +{ + "name": "chrome.history", + "version": "0.1", + "manifest_version": 2, + "description": "end-to-end browser test for chrome.history API", + "permissions": ["history"], + "background": { + "scripts": ["delete.js"], + "persistent": true + } +}
diff --git a/chrome/test/data/extensions/api_test/history/regular/delete_prohibited.html b/chrome/test/data/extensions/api_test/history/regular/delete_prohibited.html deleted file mode 100644 index cf92004a..0000000 --- a/chrome/test/data/extensions/api_test/history/regular/delete_prohibited.html +++ /dev/null
@@ -1,7 +0,0 @@ -<!-- -Copyright 2013 The Chromium Authors -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. ---> -<script src="common.js"></script> -<script src="delete_prohibited.js"></script>
diff --git a/chrome/test/data/extensions/api_test/history/regular/delete_prohibited.js b/chrome/test/data/extensions/api_test/history/regular/delete_prohibited/delete_prohibited.js similarity index 93% rename from chrome/test/data/extensions/api_test/history/regular/delete_prohibited.js rename to chrome/test/data/extensions/api_test/history/regular/delete_prohibited/delete_prohibited.js index e1b0c82..9a268f0 100644 --- a/chrome/test/data/extensions/api_test/history/regular/delete_prohibited.js +++ b/chrome/test/data/extensions/api_test/history/regular/delete_prohibited/delete_prohibited.js
@@ -61,8 +61,11 @@ })); } -// runHistoryTestFns is defined in ./common.js . -runHistoryTestFns([ +const scriptUrl = '_test_resources/api_test/history/regular/common.js'; +let loadScript = chrome.test.loadScript(scriptUrl); + +loadScript.then(async function() { +chrome.test.runTests([ function deleteUrl() { verifyNoDeletion(function(callback) { chrome.history.deleteUrl({ 'url': GOOGLE_URL }, callback); @@ -80,4 +83,4 @@ function deleteAll() { verifyNoDeletion(chrome.history.deleteAll); } -]); +])});
diff --git a/chrome/test/data/extensions/api_test/history/regular/delete_prohibited/manifest.json b/chrome/test/data/extensions/api_test/history/regular/delete_prohibited/manifest.json new file mode 100644 index 0000000..64b163b --- /dev/null +++ b/chrome/test/data/extensions/api_test/history/regular/delete_prohibited/manifest.json
@@ -0,0 +1,11 @@ +{ + "name": "chrome.history", + "version": "0.1", + "manifest_version": 2, + "description": "end-to-end browser test for chrome.history API", + "permissions": ["history"], + "background": { + "scripts": ["delete_prohibited.js"], + "persistent": true + } +}
diff --git a/chrome/test/data/extensions/api_test/history/regular/get_visits.html b/chrome/test/data/extensions/api_test/history/regular/get_visits.html deleted file mode 100644 index 78950377..0000000 --- a/chrome/test/data/extensions/api_test/history/regular/get_visits.html +++ /dev/null
@@ -1,7 +0,0 @@ -<!-- -Copyright 2011 The Chromium Authors -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. ---> -<script src="common.js"></script> -<script src="get_visits.js"></script>
diff --git a/chrome/test/data/extensions/api_test/history/regular/get_visits.js b/chrome/test/data/extensions/api_test/history/regular/get_visits/get_visits.js similarity index 84% rename from chrome/test/data/extensions/api_test/history/regular/get_visits.js rename to chrome/test/data/extensions/api_test/history/regular/get_visits/get_visits.js index a33c344..fcbda3df 100644 --- a/chrome/test/data/extensions/api_test/history/regular/get_visits.js +++ b/chrome/test/data/extensions/api_test/history/regular/get_visits/get_visits.js
@@ -5,8 +5,11 @@ // History api test for Chrome. // browser_tests.exe --gtest_filter=HistoryExtensionApiTest.GetVisits -// runHistoryTestFns is defined in ./common.js . -runHistoryTestFns([ +const scriptUrl = '_test_resources/api_test/history/regular/common.js'; +let loadScript = chrome.test.loadScript(scriptUrl); + +loadScript.then(async function() { +chrome.test.runTests([ function getVisits() { // getVisits callback. function getVisitsTestVerification() { @@ -34,4 +37,4 @@ populateHistory([GOOGLE_URL], function() { }); }); } -]); +])});
diff --git a/chrome/test/data/extensions/api_test/history/regular/get_visits/manifest.json b/chrome/test/data/extensions/api_test/history/regular/get_visits/manifest.json new file mode 100644 index 0000000..98229a35 --- /dev/null +++ b/chrome/test/data/extensions/api_test/history/regular/get_visits/manifest.json
@@ -0,0 +1,11 @@ +{ + "name": "chrome.history", + "version": "0.1", + "manifest_version": 2, + "description": "end-to-end browser test for chrome.history API", + "permissions": ["history"], + "background": { + "scripts": ["get_visits.js"], + "persistent": true + } +}
diff --git a/chrome/test/data/extensions/api_test/history/regular/manifest.json b/chrome/test/data/extensions/api_test/history/regular/manifest.json deleted file mode 100644 index 78829e79..0000000 --- a/chrome/test/data/extensions/api_test/history/regular/manifest.json +++ /dev/null
@@ -1,7 +0,0 @@ -{ - "name": "chrome.history", - "version": "0.1", - "manifest_version": 2, - "description": "end-to-end browser test for chrome.history API", - "permissions": ["history", "tabs"] -}
diff --git a/chrome/test/data/extensions/api_test/history/regular/misc_search.html b/chrome/test/data/extensions/api_test/history/regular/misc_search.html deleted file mode 100644 index 631169b..0000000 --- a/chrome/test/data/extensions/api_test/history/regular/misc_search.html +++ /dev/null
@@ -1,7 +0,0 @@ -<!-- -Copyright 2011 The Chromium Authors -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. ---> -<script src="common.js"></script> -<script src="misc_search.js"></script>
diff --git a/chrome/test/data/extensions/api_test/history/regular/misc_search/manifest.json b/chrome/test/data/extensions/api_test/history/regular/misc_search/manifest.json new file mode 100644 index 0000000..bb05630 --- /dev/null +++ b/chrome/test/data/extensions/api_test/history/regular/misc_search/manifest.json
@@ -0,0 +1,11 @@ +{ + "name": "chrome.history", + "version": "0.1", + "manifest_version": 2, + "description": "end-to-end browser test for chrome.history API", + "permissions": ["history"], + "background": { + "scripts": ["misc_search.js"], + "persistent": true + } +}
diff --git a/chrome/test/data/extensions/api_test/history/regular/misc_search.js b/chrome/test/data/extensions/api_test/history/regular/misc_search/misc_search.js similarity index 89% rename from chrome/test/data/extensions/api_test/history/regular/misc_search.js rename to chrome/test/data/extensions/api_test/history/regular/misc_search/misc_search.js index b0714bc..00d78cb 100644 --- a/chrome/test/data/extensions/api_test/history/regular/misc_search.js +++ b/chrome/test/data/extensions/api_test/history/regular/misc_search/misc_search.js
@@ -5,8 +5,12 @@ // History api test for Chrome. // browser_tests.exe --gtest_filter=HistoryExtensionApiTest.MiscSearch -// runHistoryTestFns is defined in ./common.js . -runHistoryTestFns([ + +const scriptUrl = '_test_resources/api_test/history/regular/common.js'; +let loadScript = chrome.test.loadScript(scriptUrl); + +loadScript.then(async function() { +chrome.test.runTests([ function basicSearch() { // basicSearch callback. function basicSearchTestVerification() { @@ -56,4 +60,4 @@ populateHistory(urls, function() { }); }); }, -]); +])});
diff --git a/chrome/test/data/extensions/api_test/history/regular/search_after_add.html b/chrome/test/data/extensions/api_test/history/regular/search_after_add.html deleted file mode 100644 index 3dc215d2..0000000 --- a/chrome/test/data/extensions/api_test/history/regular/search_after_add.html +++ /dev/null
@@ -1,7 +0,0 @@ -<!-- -Copyright 2011 The Chromium Authors -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. ---> -<script src="common.js"></script> -<script src="search_after_add.js"></script>
diff --git a/chrome/test/data/extensions/api_test/history/regular/search_after_add/manifest.json b/chrome/test/data/extensions/api_test/history/regular/search_after_add/manifest.json new file mode 100644 index 0000000..b23e61c --- /dev/null +++ b/chrome/test/data/extensions/api_test/history/regular/search_after_add/manifest.json
@@ -0,0 +1,11 @@ +{ + "name": "chrome.history", + "version": "0.1", + "manifest_version": 2, + "description": "end-to-end browser test for chrome.history API", + "permissions": ["history"], + "background": { + "scripts": ["search_after_add.js"], + "persistent": true + } +}
diff --git a/chrome/test/data/extensions/api_test/history/regular/search_after_add.js b/chrome/test/data/extensions/api_test/history/regular/search_after_add/search_after_add.js similarity index 77% rename from chrome/test/data/extensions/api_test/history/regular/search_after_add.js rename to chrome/test/data/extensions/api_test/history/regular/search_after_add/search_after_add.js index 7c9b0ac..1d696cd 100644 --- a/chrome/test/data/extensions/api_test/history/regular/search_after_add.js +++ b/chrome/test/data/extensions/api_test/history/regular/search_after_add/search_after_add.js
@@ -5,8 +5,11 @@ // History api test for Chrome. // browser_tests.exe --gtest_filter=HistoryExtensionApiTest.SearchAfterAdd -// runHistoryTestFns is defined in ./common.js . -runHistoryTestFns([ +const scriptUrl = '_test_resources/api_test/history/regular/common.js'; +let loadScript = chrome.test.loadScript(scriptUrl); + +loadScript.then(async function() { +chrome.test.runTests([ function searchAfterAdd() { chrome.history.deleteAll(function() { var VALID_URL = 'http://www.google.com/'; @@ -19,4 +22,4 @@ }); }); } -]); +])});
diff --git a/chrome/test/data/extensions/api_test/history/regular/timed_search.html b/chrome/test/data/extensions/api_test/history/regular/timed_search.html deleted file mode 100644 index 1b8eec3..0000000 --- a/chrome/test/data/extensions/api_test/history/regular/timed_search.html +++ /dev/null
@@ -1,7 +0,0 @@ -<!-- -Copyright 2011 The Chromium Authors -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. ---> -<script src="common.js"></script> -<script src="timed_search.js"></script>
diff --git a/chrome/test/data/extensions/api_test/history/regular/timed_search/manifest.json b/chrome/test/data/extensions/api_test/history/regular/timed_search/manifest.json new file mode 100644 index 0000000..892da398 --- /dev/null +++ b/chrome/test/data/extensions/api_test/history/regular/timed_search/manifest.json
@@ -0,0 +1,11 @@ +{ + "name": "chrome.history", + "version": "0.1", + "manifest_version": 2, + "description": "end-to-end browser test for chrome.history API", + "permissions": ["history"], + "background": { + "scripts": ["timed_search.js"], + "persistent": true + } +}
diff --git a/chrome/test/data/extensions/api_test/history/regular/timed_search.js b/chrome/test/data/extensions/api_test/history/regular/timed_search/timed_search.js similarity index 93% rename from chrome/test/data/extensions/api_test/history/regular/timed_search.js rename to chrome/test/data/extensions/api_test/history/regular/timed_search/timed_search.js index aab07e7..dffb1f36 100644 --- a/chrome/test/data/extensions/api_test/history/regular/timed_search.js +++ b/chrome/test/data/extensions/api_test/history/regular/timed_search/timed_search.js
@@ -5,8 +5,11 @@ // History api test for Chrome. // browser_tests.exe --gtest_filter=HistoryExtensionApiTest.TimedSearch -// runHistoryTestFns is defined in ./common.js . -runHistoryTestFns([ +const scriptUrl = '_test_resources/api_test/history/regular/common.js'; +let loadScript = chrome.test.loadScript(scriptUrl); + +loadScript.then(async function() { +chrome.test.runTests([ // Give time epochs x,y,z and history events A,B which occur in the sequence // x A y B z, test that searching in [x,y] finds only A. function timeScopedSearchStartRange() { @@ -78,4 +81,4 @@ }); }); } -]); +])});
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn index 41e19df2..58e95d1 100644 --- a/chrome/test/data/webui/BUILD.gn +++ b/chrome/test/data/webui/BUILD.gn
@@ -236,6 +236,7 @@ "chromeos/firmware_update/firmware_update_browsertest.js", "chromeos/office_fallback/office_fallback_browsertest.js", "chromeos/os_feedback_ui/os_feedback_browsertest.js", + "chromeos/personalization_app/personalization_app_browsertest.js", "chromeos/scanning/scanning_app_browsertest.js", "chromeos/shimless_rma/shimless_rma_browsertest.js", "nearby_share/nearby_browsertest.js", @@ -368,6 +369,7 @@ "color_provider_css_colors_test_chromeos.ts", "color_provider_css_colors_test.ts", "fake_chrome_event.ts", + "invalidations/invalidations_test.ts", "metrics_test_support.ts", "mock_controller.ts", "mocked_metrics_reporter.ts", @@ -385,7 +387,6 @@ # TODO(dpapad): Migrate the files below to TypeScript and remove allowJs # from tsconfig_base.json. - "invalidations/invalidations_test.js", "mocha_adapter.js", "mojo_webui_test_support.js", "usb_internals_test.js",
diff --git a/chrome/test/data/webui/chai_assert.ts b/chrome/test/data/webui/chai_assert.ts index 7fbac26..4113306 100644 --- a/chrome/test/data/webui/chai_assert.ts +++ b/chrome/test/data/webui/chai_assert.ts
@@ -135,3 +135,7 @@ export function assertArrayEquals(expected: any[], actual: any[]) { assertDeepEquals(expected, actual); } + +export function assertStringContains(expected: string, contains: string) { + chai.expect(expected).to.have.string(contains); +} \ No newline at end of file
diff --git a/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn b/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn index 40db8d8..acfd9d9a 100644 --- a/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn +++ b/chrome/test/data/webui/chromeos/personalization_app/BUILD.gn
@@ -59,6 +59,7 @@ "ambient_observer_test.ts", "wallpaper_preview_element_test.ts", "wallpaper_selected_element_test.ts", + "zone_customization_element_test.ts", ] deps = [ "..:build_ts",
diff --git a/chrome/test/data/webui/chromeos/personalization_app/keyboard_backlight_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/keyboard_backlight_element_test.ts index ae8e4c0d..281b378c 100644 --- a/chrome/test/data/webui/chromeos/personalization_app/keyboard_backlight_element_test.ts +++ b/chrome/test/data/webui/chromeos/personalization_app/keyboard_backlight_element_test.ts
@@ -5,7 +5,7 @@ import 'chrome://personalization/strings.m.js'; import 'chrome://webui-test/mojo_webui_test_support.js'; -import {KeyboardBacklight, KeyboardBacklightActionName, KeyboardBacklightObserver, SetBacklightColorAction, SetShouldShowNudgeAction, SetWallpaperColorAction} from 'chrome://personalization/js/personalization_app.js'; +import {KeyboardBacklight, KeyboardBacklightActionName, KeyboardBacklightObserver, SetCurrentBacklightStateAction, SetShouldShowNudgeAction, SetWallpaperColorAction} from 'chrome://personalization/js/personalization_app.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js'; import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js'; @@ -60,47 +60,52 @@ const colorContainers = selectorContainer.querySelectorAll('.color-container'); assertEquals(9, colorContainers!.length); - personalizationStore.setReducersEnabled(true); personalizationStore.expectAction( - KeyboardBacklightActionName.SET_BACKLIGHT_COLOR); + KeyboardBacklightActionName.SET_CURRENT_BACKLIGHT_STATE); (colorContainers[1] as HTMLElement).click(); await keyboardBacklightProvider.whenCalled('setBacklightColor'); - const action = await personalizationStore.waitForAction( - KeyboardBacklightActionName.SET_BACKLIGHT_COLOR) as - SetBacklightColorAction; - assertTrue(!!action.backlightColor); - assertTrue(!!personalizationStore.data.keyboardBacklight.backlightColor); + const action = + await personalizationStore.waitForAction( + KeyboardBacklightActionName.SET_CURRENT_BACKLIGHT_STATE) as + SetCurrentBacklightStateAction; + assertTrue(!!action.currentBacklightState); + assertTrue( + !!personalizationStore.data.keyboardBacklight.currentBacklightState); }); test('sets backlight color in store on first load', async () => { personalizationStore.expectAction( - KeyboardBacklightActionName.SET_BACKLIGHT_COLOR); + KeyboardBacklightActionName.SET_CURRENT_BACKLIGHT_STATE); keyboardBacklightElement = initElement(KeyboardBacklight); await keyboardBacklightProvider.whenCalled('setKeyboardBacklightObserver'); - keyboardBacklightProvider.fireOnBacklightColorChanged( - keyboardBacklightProvider.backlightColor); - const action = await personalizationStore.waitForAction( - KeyboardBacklightActionName.SET_BACKLIGHT_COLOR) as - SetBacklightColorAction; - assertEquals( - keyboardBacklightProvider.backlightColor, action.backlightColor); + keyboardBacklightProvider.fireOnBacklightStateChanged( + keyboardBacklightProvider.currentBacklightState); + const action = + await personalizationStore.waitForAction( + KeyboardBacklightActionName.SET_CURRENT_BACKLIGHT_STATE) as + SetCurrentBacklightStateAction; + assertDeepEquals( + keyboardBacklightProvider.currentBacklightState, + action.currentBacklightState); }); test('sets backlight color data in store on changed', async () => { await keyboardBacklightProvider.whenCalled('setKeyboardBacklightObserver'); personalizationStore.expectAction( - KeyboardBacklightActionName.SET_BACKLIGHT_COLOR); + KeyboardBacklightActionName.SET_CURRENT_BACKLIGHT_STATE); keyboardBacklightProvider.keyboardBacklightObserverRemote! - .onBacklightColorChanged(keyboardBacklightProvider.backlightColor); + .onBacklightStateChanged( + keyboardBacklightProvider.currentBacklightState); - const {backlightColor} = + const {currentBacklightState} = await personalizationStore.waitForAction( - KeyboardBacklightActionName.SET_BACKLIGHT_COLOR) as - SetBacklightColorAction; - assertEquals(keyboardBacklightProvider.backlightColor, backlightColor); + KeyboardBacklightActionName.SET_CURRENT_BACKLIGHT_STATE) as + SetCurrentBacklightStateAction; + assertDeepEquals( + keyboardBacklightProvider.currentBacklightState, currentBacklightState); }); test('sets wallpaper color in store on first load', async () => { @@ -166,7 +171,8 @@ test( 'shows customization button if multi-zone rgb keyboard is supported', async () => { - loadTimeData.overrideValues({isMultiZoneRgbKeyboardSupported: true}); + loadTimeData.overrideValues( + {keyboardBacklightZoneCount: keyboardBacklightProvider.zoneCount}); keyboardBacklightElement = initElement(KeyboardBacklight); const customizationButton = keyboardBacklightElement.shadowRoot!.getElementById( @@ -175,7 +181,8 @@ }); test('clicking on customization button opens a dialog', async () => { - loadTimeData.overrideValues({isMultiZoneRgbKeyboardSupported: true}); + loadTimeData.overrideValues( + {keyboardBacklightZoneCount: keyboardBacklightProvider.zoneCount}); keyboardBacklightElement = initElement(KeyboardBacklight); const customizationButton = keyboardBacklightElement.shadowRoot!.getElementById( @@ -195,7 +202,8 @@ }); test('shows wallpaper color at the end with multi-zone enabled', async () => { - loadTimeData.overrideValues({isMultiZoneRgbKeyboardSupported: true}); + loadTimeData.overrideValues( + {keyboardBacklightZoneCount: keyboardBacklightProvider.zoneCount}); keyboardBacklightElement = initElement(KeyboardBacklight); @@ -215,7 +223,7 @@ test( 'shows wallpaper color button at the beginning with multi-zone disabled', async () => { - loadTimeData.overrideValues({isMultiZoneRgbKeyboardSupported: false}); + loadTimeData.overrideValues({keyboardBacklightZoneCount: 0}); keyboardBacklightElement = initElement(KeyboardBacklight);
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_browsertest.js b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_browsertest.js index 1aec5e859..d23808ed 100644 --- a/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_browsertest.js +++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_app_component_browsertest.js
@@ -79,6 +79,7 @@ 'GooglePhotosSharedAlbumDialogTest', 'google_photos_shared_album_dialog_element_test.js' ], + ['ZoneCustomizationTest', 'zone_customization_element_test.js'], ].forEach(test => registerTest(...test)); function registerTest(testName, module, caseName) {
diff --git a/chrome/test/data/webui/chromeos/personalization_app/test_keyboard_backlight_interface_provider.ts b/chrome/test/data/webui/chromeos/personalization_app/test_keyboard_backlight_interface_provider.ts index c8cd229..aa0d1f6 100644 --- a/chrome/test/data/webui/chromeos/personalization_app/test_keyboard_backlight_interface_provider.ts +++ b/chrome/test/data/webui/chromeos/personalization_app/test_keyboard_backlight_interface_provider.ts
@@ -2,13 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {BacklightColor, KeyboardBacklightObserverInterface, KeyboardBacklightObserverRemote, KeyboardBacklightProviderInterface} from 'chrome://personalization/js/personalization_app.js'; +import {BacklightColor, CurrentBacklightState, KeyboardBacklightObserverInterface, KeyboardBacklightObserverRemote, KeyboardBacklightProviderInterface} from 'chrome://personalization/js/personalization_app.js'; import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js'; import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js'; export class TestKeyboardBacklightProvider extends TestBrowserProxy implements KeyboardBacklightProviderInterface { - public backlightColor: BacklightColor = BacklightColor.kBlue; + public zoneCount: number = 5; + public zoneColors: BacklightColor[] = [ + BacklightColor.kBlue, + BacklightColor.kRed, + BacklightColor.kWallpaper, + BacklightColor.kYellow, + ]; + public currentBacklightState: + CurrentBacklightState = {color: BacklightColor.kBlue}; constructor() { super([ @@ -23,6 +31,14 @@ keyboardBacklightObserverRemote: KeyboardBacklightObserverInterface|null = null; + setZoneCount(zoneCount: number) { + this.zoneCount = zoneCount; + } + + setCurrentBacklightState(backlightState: CurrentBacklightState) { + this.currentBacklightState = backlightState; + } + setBacklightColor(backlightColor: BacklightColor) { this.methodCalled('setBacklightColor', backlightColor); } @@ -45,9 +61,9 @@ this.keyboardBacklightObserverRemote = remote; } - fireOnBacklightColorChanged(backlightColor: BacklightColor) { - this.keyboardBacklightObserverRemote!.onBacklightColorChanged( - backlightColor); + fireOnBacklightStateChanged(currentBacklightState: CurrentBacklightState) { + this.keyboardBacklightObserverRemote!.onBacklightStateChanged( + currentBacklightState); } fireOnWallpaperColorChanged(wallpaperColor: SkColor) {
diff --git a/chrome/test/data/webui/chromeos/personalization_app/zone_customization_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/zone_customization_element_test.ts new file mode 100644 index 0000000..be0b30e0 --- /dev/null +++ b/chrome/test/data/webui/chromeos/personalization_app/zone_customization_element_test.ts
@@ -0,0 +1,116 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'chrome://personalization/strings.m.js'; +import 'chrome://webui-test/mojo_webui_test_support.js'; + +import {CurrentBacklightState, KeyboardBacklightActionName, KeyboardBacklightObserver, SetCurrentBacklightStateAction, staticColorIds, ZoneCustomizationElement} from 'chrome://personalization/js/personalization_app.js'; +import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; +import {assertDeepEquals, assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js'; +import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js'; + +import {baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js'; +import {TestKeyboardBacklightProvider} from './test_keyboard_backlight_interface_provider.js'; +import {TestPersonalizationStore} from './test_personalization_store.js'; + +suite('ZoneCustomizationElementTest', function() { + let zoneCustomizationElement: ZoneCustomizationElement|null; + let keyboardBacklightProvider: TestKeyboardBacklightProvider; + let personalizationStore: TestPersonalizationStore; + + setup(() => { + const mocks = baseSetup(); + keyboardBacklightProvider = mocks.keyboardBacklightProvider; + personalizationStore = mocks.personalizationStore; + KeyboardBacklightObserver.initKeyboardBacklightObserverIfNeeded(); + }); + + teardown(async () => { + await teardownElement(zoneCustomizationElement); + zoneCustomizationElement = null; + KeyboardBacklightObserver.shutdown(); + }); + + async function initZoneCustomizationElement() { + loadTimeData.overrideValues( + {keyboardBacklightZoneCount: keyboardBacklightProvider.zoneCount}); + personalizationStore.data.keyboardBacklight.currentBacklightState = + keyboardBacklightProvider.currentBacklightState; + personalizationStore.notifyObservers(); + zoneCustomizationElement = initElement(ZoneCustomizationElement); + await waitAfterNextRender(zoneCustomizationElement); + } + + test( + 'displays content with current backlight state as a static color', + async () => { + await initZoneCustomizationElement(); + const zoneSelector = + zoneCustomizationElement!.shadowRoot!.getElementById( + 'zoneSelector'); + assertTrue(!!zoneSelector, 'zone selector should display'); + const zoneButtons = + zoneCustomizationElement!.shadowRoot!.querySelectorAll( + '.zone-button'); + assertEquals( + 5, zoneButtons!.length, + '5 zones should display in customization dialog'); + const dialogCloseButton = + zoneCustomizationElement!.shadowRoot!.getElementById( + 'dialogCloseButton'); + assertTrue(!!dialogCloseButton, 'close dialog button should display'); + }); + + test( + 'updates zone content with current backlight state as zone colors', + async () => { + keyboardBacklightProvider.setZoneCount(4); + keyboardBacklightProvider.setCurrentBacklightState( + {zoneColors: keyboardBacklightProvider.zoneColors}); + await initZoneCustomizationElement(); + const zoneSelector = + zoneCustomizationElement!.shadowRoot!.getElementById( + 'zoneSelector'); + assertTrue(!!zoneSelector, 'zone selector should display'); + const zoneButtons = + zoneCustomizationElement!.shadowRoot!.querySelectorAll( + '.zone-button'); + assertEquals( + 4, zoneButtons!.length, + '4 zones should display in customization dialog'); + const colorIcons = + zoneCustomizationElement!.shadowRoot!.querySelectorAll( + 'color-icon'); + assertEquals( + 4, colorIcons!.length, + '4 color icons should display in customization dialog'); + // Color of the color-icon displayed in each zone should match with the + // corresponding one in zone colors. + for (let i = 0; i < 4; i++) { + const zoneColor = keyboardBacklightProvider.zoneColors[i]; + const expectedColorId = staticColorIds[zoneColor!]; + const colorId = + (colorIcons[i] as HTMLElement).getAttribute('color-id'); + assertEquals( + expectedColorId, colorId, + `colorId for zone ${i + 1} should be ${expectedColorId}`); + } + }); + + test('sets zone colors data in store on first load', async () => { + const currentBacklightState: CurrentBacklightState = { + zoneColors: keyboardBacklightProvider.zoneColors, + }; + personalizationStore.expectAction( + KeyboardBacklightActionName.SET_CURRENT_BACKLIGHT_STATE); + await keyboardBacklightProvider.whenCalled('setKeyboardBacklightObserver'); + keyboardBacklightProvider.fireOnBacklightStateChanged( + currentBacklightState); + const action = + await personalizationStore.waitForAction( + KeyboardBacklightActionName.SET_CURRENT_BACKLIGHT_STATE) as + SetCurrentBacklightStateAction; + assertDeepEquals(currentBacklightState, action.currentBacklightState); + }); +});
diff --git a/chrome/test/data/webui/extensions/url_util_test.ts b/chrome/test/data/webui/extensions/url_util_test.ts index eab6d2db..9591a45 100644 --- a/chrome/test/data/webui/extensions/url_util_test.ts +++ b/chrome/test/data/webui/extensions/url_util_test.ts
@@ -9,7 +9,7 @@ suite('UrlUtilTest', function() { function getExpectedImageSet(url: string): string { - return '-webkit-image-set(' + + return 'image-set(' + 'url("chrome://favicon2/?size=20&scaleFactor=1x&pageUrl=' + encodeURIComponent(url) + '&allowGoogleServerFallback=0") 1x, ' + 'url("chrome://favicon2/?size=20&scaleFactor=2x&pageUrl=' +
diff --git a/chrome/test/data/webui/invalidations/invalidations_test.js b/chrome/test/data/webui/invalidations/invalidations_test.ts similarity index 68% rename from chrome/test/data/webui/invalidations/invalidations_test.js rename to chrome/test/data/webui/invalidations/invalidations_test.ts index 0a72a72..a38a0b1 100644 --- a/chrome/test/data/webui/invalidations/invalidations_test.js +++ b/chrome/test/data/webui/invalidations/invalidations_test.ts
@@ -3,23 +3,27 @@ // found in the LICENSE file. import {webUIListenerCallback} from 'chrome://resources/js/cr.js'; -import {$} from 'chrome://resources/js/util_ts.js'; -import {assertEquals, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js'; +import {getRequiredElement} from 'chrome://resources/js/util_ts.js'; -window.invalidations_test = {}; -invalidations_test = window.invalidations_test; -invalidations_test.TestNames = { - RegisterNewInvalidation: 'register new invalidation', - ChangeInvalidationsState: 'change invalidations state', - RegisterNewIds: 'register new ids', - UpdateRegisteredHandlers: 'update registered handlers', - UpdateInternalDisplay: 'update internal display', +import {assertEquals, assertNotEquals, assertTrue} from '../chai_assert.js'; + +const invalidations_test = { + TestNames: { + RegisterNewInvalidation: 'register new invalidation', + ChangeInvalidationsState: 'change invalidations state', + RegisterNewIds: 'register new ids', + UpdateRegisteredHandlers: 'update registered handlers', + UpdateInternalDisplay: 'update internal display', + }, }; +Object.assign(window, {invalidations_test}); + suite('invalidations_test', function() { // Test that registering an invalidations appears properly on the textarea. test(invalidations_test.TestNames.RegisterNewInvalidation, function() { - const invalidationsLog = $('invalidations-log'); + const invalidationsLog = + getRequiredElement<HTMLTextAreaElement>('invalidations-log'); const invalidation = [ {isUnknownVersion: 'true', objectId: {name: 'EXTENSIONS', source: 1004}}, ]; @@ -35,19 +39,21 @@ // Test that changing the Invalidations Service state appears both in the // span and in the textarea. test(invalidations_test.TestNames.ChangeInvalidationsState, function() { - const invalidationsState = $('invalidations-state'); - const invalidationsLog = $('invalidations-log'); + const invalidationsState = + getRequiredElement<HTMLElement>('invalidations-state'); + const invalidationsLog = + getRequiredElement<HTMLTextAreaElement>('invalidations-log'); const newState = 'INVALIDATIONS_ENABLED'; const newNewState = 'TRANSIENT_INVALIDATION_ERROR'; webUIListenerCallback('state-updated', newState); const isContainedState = - invalidationsState.textContent.indexOf('INVALIDATIONS_ENABLED') !== -1; + invalidationsState.textContent!.indexOf('INVALIDATIONS_ENABLED') !== -1; assertTrue(isContainedState, 'could not change the invalidations text'); invalidationsLog.value = ''; webUIListenerCallback('state-updated', newNewState); - const isContainedState2 = invalidationsState.textContent.indexOf( + const isContainedState2 = invalidationsState.textContent!.indexOf( 'TRANSIENT_INVALIDATION_ERROR') !== -1; assertTrue(isContainedState2, 'could not change the invalidations text'); const isContainedLog = invalidationsLog.value.indexOf( @@ -58,10 +64,17 @@ // Test that objects ids appear on the table. test(invalidations_test.TestNames.RegisterNewIds, function() { - let newDataType = [ + interface DataType { + name: string; + source: number; + totalCount?: number; + } + + let newDataType: DataType[] = [ {name: 'EXTENSIONS', source: 1004, totalCount: 0}, {name: 'FAVICON_IMAGE', source: 1004, totalCount: 0}, ]; + const registrarName = 'Fake'; const pattern1 = [registrarName, '1004', 'EXTENSIONS', '0', '0', '', '', '']; @@ -74,23 +87,24 @@ webUIListenerCallback('ids-updated', registrarName, newDataType); // Test that the two patterns are contained in the table. - const oidTable = $('objectsid-table-container'); + const oidTable = + getRequiredElement<HTMLTableElement>('objectsid-table-container'); let foundPattern1 = false; let foundPattern2 = false; - for (let row = 0; row < oidTable.rows.length; row++) { + for (const row of oidTable.rows) { let pattern1Test = true; let pattern2Test = true; - for (let cell = 0; cell < oidTable.rows[row].cells.length; cell++) { - pattern1Test = pattern1Test && - (pattern1[cell] === oidTable.rows[row].cells[cell].textContent); - pattern2Test = pattern2Test && - (pattern2[cell] === oidTable.rows[row].cells[cell].textContent); + for (let cell = 0; cell < row.cells.length; cell++) { + pattern1Test = + pattern1Test && (pattern1[cell] === row.cells[cell]!.textContent); + pattern2Test = + pattern2Test && (pattern2[cell] === row.cells[cell]!.textContent); } if (pattern1Test) { - assertEquals('greyed', oidTable.rows[row].className); + assertEquals('greyed', row.className); } if (pattern2Test) { - assertEquals('content', oidTable.rows[row].className); + assertEquals('content', row.className); } foundPattern1 = foundPattern1 || pattern1Test; @@ -106,7 +120,8 @@ // Test that registering new handlers appear on the website. test(invalidations_test.TestNames.UpdateRegisteredHandlers, function() { function text() { - return $('registered-handlers').textContent; + return getRequiredElement<HTMLElement>('registered-handlers').textContent! + ; } webUIListenerCallback('handlers-updated', ['FakeApi', 'FakeClient']); assertNotEquals(text().indexOf('FakeApi'), -1); @@ -121,6 +136,8 @@ test(invalidations_test.TestNames.UpdateInternalDisplay, function() { const newDetailedStatus = {MessagesSent: 1}; webUIListenerCallback('detailed-status-updated', newDetailedStatus); - assertEquals($('internal-display').value, '{\n \"MessagesSent\": 1\n}'); + assertEquals( + getRequiredElement<HTMLTextAreaElement>('internal-display').value, + '{\n \"MessagesSent\": 1\n}'); }); });
diff --git a/chrome/test/data/webui/js/icon_test.ts b/chrome/test/data/webui/js/icon_test.ts index 1ca9b76d..cea5ab3 100644 --- a/chrome/test/data/webui/js/icon_test.ts +++ b/chrome/test/data/webui/js/icon_test.ts
@@ -11,12 +11,12 @@ const url = 'http://foo.com'; function getExpectedImageSet(size: number): string { - const expectedDesktop = '-webkit-image-set(' + + const expectedDesktop = 'image-set(' + `url("chrome://favicon2/?size=${size}&scaleFactor=1x&pageUrl=` + encodeURIComponent(url) + '&allowGoogleServerFallback=0") 1x, ' + `url("chrome://favicon2/?size=${size}&scaleFactor=2x&pageUrl=` + encodeURIComponent(url) + '&allowGoogleServerFallback=0") 2x)'; - const expectedOther = '-webkit-image-set(' + + const expectedOther = 'image-set(' + `url("chrome://favicon2/?size=${size}&scaleFactor=1x&pageUrl=` + encodeURIComponent(url) + '&allowGoogleServerFallback=0") ' + window.devicePixelRatio + 'x)'; @@ -44,12 +44,12 @@ test('GetFavicon', function() { const url = 'http://foo.com/foo.ico'; - const expectedDesktop = '-webkit-image-set(' + + const expectedDesktop = 'image-set(' + 'url("chrome://favicon2/?size=16&scaleFactor=1x&iconUrl=' + encodeURIComponent('http://foo.com/foo.ico') + '") 1x, ' + 'url("chrome://favicon2/?size=16&scaleFactor=2x&iconUrl=' + encodeURIComponent('http://foo.com/foo.ico') + '") 2x)'; - const expectedOther = '-webkit-image-set(' + + const expectedOther = 'image-set(' + 'url("chrome://favicon2/?size=16&scaleFactor=1x&iconUrl=' + encodeURIComponent('http://foo.com/foo.ico') + '") ' + window.devicePixelRatio + 'x)';
diff --git a/chrome/test/data/webui/new_tab_page/modules/drive/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/drive/module_test.ts index be78cc8..30f7e524 100644 --- a/chrome/test/data/webui/new_tab_page/modules/drive/module_test.ts +++ b/chrome/test/data/webui/new_tab_page/modules/drive/module_test.ts
@@ -113,10 +113,11 @@ // Assert. const event: DismissModuleEvent = await whenFired; assertEquals('Files hidden', event.detail.message); + assertTrue(!!event.detail.restoreCallback); assertEquals(1, handler.getCallCount('dismissModule')); // Act. - event.detail.restoreCallback(); + event.detail.restoreCallback!(); // Assert. assertEquals(1, handler.getCallCount('restoreModule'));
diff --git a/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts index 2f05e21..b13d5de6 100644 --- a/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts +++ b/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts
@@ -6,12 +6,13 @@ import {Cluster, SearchQuery, URLVisit} from 'chrome://new-tab-page/history_cluster_types.mojom-webui.js'; import {PageHandlerRemote} from 'chrome://new-tab-page/history_clusters.mojom-webui.js'; -import {HistoryClusterLayoutType, historyClustersDescriptor, HistoryClustersModuleElement, HistoryClustersProxyImpl, LAYOUT_1_MIN_IMAGE_VISITS, LAYOUT_1_MIN_VISITS, LAYOUT_2_MIN_IMAGE_VISITS, LAYOUT_2_MIN_VISITS, LAYOUT_3_MIN_IMAGE_VISITS, LAYOUT_3_MIN_VISITS, MIN_RELATED_SEARCHES} from 'chrome://new-tab-page/lazy_load.js'; +import {DismissModuleEvent, HistoryClusterLayoutType, historyClustersDescriptor, HistoryClustersModuleElement, HistoryClustersProxyImpl, LAYOUT_1_MIN_IMAGE_VISITS, LAYOUT_1_MIN_VISITS, LAYOUT_2_MIN_IMAGE_VISITS, LAYOUT_2_MIN_VISITS, LAYOUT_3_MIN_IMAGE_VISITS, LAYOUT_3_MIN_VISITS, MIN_RELATED_SEARCHES} from 'chrome://new-tab-page/lazy_load.js'; import {$$} from 'chrome://new-tab-page/new_tab_page.js'; import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js'; import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js'; import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js'; import {TestMock} from 'chrome://webui-test/test_mock.js'; +import {eventToPromise} from 'chrome://webui-test/test_util.js'; import {installMock} from '../../test_support.js'; @@ -362,4 +363,32 @@ assertTrue(!!headerElement); assertModuleHeaderTitle(headerElement, 'Resume your journey for SRP'); }); + + test('Backend is notified when module is dismissed', async () => { + const sampleClusterLabel = '"Sample Journey"'; + const sampleCluster = createSampleCluster({label: sampleClusterLabel}); + handler.setResultFor( + 'getCluster', Promise.resolve({cluster: sampleCluster})); + + const moduleElement = await historyClustersDescriptor.initialize(0) as + HistoryClustersModuleElement; + + document.body.append(moduleElement); + assertTrue(!!moduleElement); + await handler.whenCalled('getCluster'); + await waitAfterNextRender(moduleElement); + + const waitForDismissEvent = eventToPromise('dismiss-module', moduleElement); + const dismissButton = + moduleElement.shadowRoot!.querySelector('ntp-module-header')! + .shadowRoot!.querySelector<HTMLElement>('#dismissButton')!; + dismissButton.click(); + const dismissEvent: DismissModuleEvent = await waitForDismissEvent; + assertEquals(`${sampleCluster.label!} hidden`, dismissEvent.detail.message); + const visits = await handler.whenCalled('dismissCluster'); + assertEquals(3, visits.length); + visits.forEach((visit: URLVisit, index: number) => { + assertEquals(index, Number(visit.visitId)); + }); + }); });
diff --git a/chrome/test/data/webui/new_tab_page/modules/modules_test.ts b/chrome/test/data/webui/new_tab_page/modules/modules_test.ts index c0a9664..c42aa04 100644 --- a/chrome/test/data/webui/new_tab_page/modules/modules_test.ts +++ b/chrome/test/data/webui/new_tab_page/modules/modules_test.ts
@@ -10,6 +10,7 @@ import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; import {fakeMetricsPrivate, MetricsTracker} from 'chrome://webui-test/metrics_test_support.js'; +import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js'; import {TestMock} from 'chrome://webui-test/test_mock.js'; import {assertNotStyle, assertStyle, capture, createElement, initNullModule, installMock, render} from '../test_support.js'; @@ -416,7 +417,11 @@ assertFalse(restoreCalled); // Act. - modulesElement.$.undoRemoveModuleButton.click(); + await waitAfterNextRender(modulesElement); + const undoRemoveModuleButton = + modulesElement.shadowRoot!.querySelector('#undoRemoveModuleButton') as + HTMLElement; + undoRemoveModuleButton.click(); // Assert. assertDeepEquals(['foo', false], handler.getArgs('setModuleDisabled')[1]); @@ -458,6 +463,53 @@ }); }); + test('modules can be dismissed with no restore action', async () => { + const fooDescriptor = new ModuleDescriptor('foo', initNullModule); + moduleRegistry.setResultFor('getDescriptors', [fooDescriptor]); + + // Act. + const modulesElement = await createModulesElement([ + { + descriptor: fooDescriptor, + element: createElement(), + }, + ]); + callbackRouterRemote.setDisabledModules(false, []); + await callbackRouterRemote.$.flushForTesting(); + + const moduleWrappers = + modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper'); + const moduleWrapperContainers = + modulesElement.shadowRoot!.querySelectorAll('.module-container'); + assertEquals(1, moduleWrappers.length); + assertEquals(1, moduleWrapperContainers.length); + assertNotStyle(moduleWrappers[0]!, 'display', 'none'); + assertNotStyle(moduleWrapperContainers[0]!, 'display', 'none'); + assertFalse(modulesElement.$.removeModuleToast.open); + + // Act. + moduleWrappers[0]!.dispatchEvent(new CustomEvent('dismiss-module', { + bubbles: true, + composed: true, + detail: { + message: 'Foo', + }, + })); + await waitAfterNextRender(modulesElement); + + // Assert. + assertNotStyle(moduleWrappers[0]!, 'display', 'none'); + assertStyle(moduleWrapperContainers[0]!, 'display', 'none'); + assertTrue(modulesElement.$.removeModuleToast.open); + assertEquals( + 'Foo', modulesElement.$.removeModuleToastMessage.textContent!.trim()); + assertEquals(1, handler.getCallCount('onDismissModule')); + assertEquals('foo', handler.getArgs('onDismissModule')[0]); + assertEquals( + null, + modulesElement.shadowRoot!.querySelector('#undoRemoveModuleButton')); + }); + test('modules can be dismissed and restored', async () => { // Arrange. let restoreCalled = false; @@ -508,7 +560,11 @@ assertFalse(restoreCalled); // Act. - modulesElement.$.undoRemoveModuleButton.click(); + await waitAfterNextRender(modulesElement); + const undoRemoveModuleButton = + modulesElement.shadowRoot!.querySelector('#undoRemoveModuleButton') as + HTMLElement; + undoRemoveModuleButton.click(); // Assert. assertNotStyle(moduleWrappers[0]!, 'display', 'none'); @@ -574,7 +630,11 @@ assertFalse(restoreCalled); // Act. - modulesElement.$.undoRemoveModuleButton.click(); + await waitAfterNextRender(modulesElement); + const undoRemoveModuleButton = + modulesElement.shadowRoot!.querySelector('#undoRemoveModuleButton') as + HTMLElement; + undoRemoveModuleButton.click(); // Assert. assertDeepEquals(['foo', false], handler.getArgs('setModuleDisabled')[1]); @@ -994,8 +1054,12 @@ assertEquals(1, moduleWrappers.indexOf(tallModule)); assertEquals(2, moduleWrappers.indexOf(shortModule1)); - // Act. - modulesElement.$.undoRemoveModuleButton.click(); + // // Act. + await waitAfterNextRender(modulesElement); + const undoRemoveModuleButton = + modulesElement.shadowRoot!.querySelector('#undoRemoveModuleButton') as + HTMLElement; + undoRemoveModuleButton.click(); // Assert. assertDeepEquals(['bar', false], handler.getArgs('setModuleDisabled')[1]);
diff --git a/chrome/test/data/webui/new_tab_page/modules/photos/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/photos/module_test.ts index 8e83847..621db6d5 100644 --- a/chrome/test/data/webui/new_tab_page/modules/photos/module_test.ts +++ b/chrome/test/data/webui/new_tab_page/modules/photos/module_test.ts
@@ -526,10 +526,11 @@ assertEquals( loadTimeData.getString('modulesPhotosMemoriesHiddenToday'), event.detail.message); + assertTrue(!!event.detail.restoreCallback); assertEquals(1, handler.getCallCount('dismissModule')); // Act. - event.detail.restoreCallback(); + event.detail.restoreCallback!(); // Assert. assertEquals(1, handler.getCallCount('restoreModule'));
diff --git a/chrome/test/data/webui/new_tab_page/modules/recipes/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/recipes/module_test.ts index 60226bc..3cec54d6 100644 --- a/chrome/test/data/webui/new_tab_page/modules/recipes/module_test.ts +++ b/chrome/test/data/webui/new_tab_page/modules/recipes/module_test.ts
@@ -207,18 +207,19 @@ moduleElement.shadowRoot!.querySelector('ntp-module-header')! .shadowRoot!.querySelector<HTMLElement>('#dismissButton')!; dismissButton.click(); - const dismissEvent: DismissModuleEvent = await waitForDismissEvent; - const toastMessage = dismissEvent.detail.message; - const restoreCallback = dismissEvent.detail.restoreCallback; // Assert. + const dismissEvent: DismissModuleEvent = await waitForDismissEvent; + const toastMessage = dismissEvent.detail.message; const moduleHeaderTitle = moduleElement.shadowRoot!.querySelector( 'ntp-module-header')!.textContent!.trim(); assertEquals(moduleHeaderTitle + ' hidden', toastMessage); + assertTrue(!!dismissEvent.detail.restoreCallback); assertEquals('Hello world', await handler.whenCalled('dismissTask')); // Act. + const restoreCallback = dismissEvent.detail.restoreCallback!; restoreCallback(); // Assert.
diff --git a/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts b/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts index 7a3ed05..1248971a 100644 --- a/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts +++ b/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts
@@ -157,7 +157,14 @@ iconElement: RealboxIconElement, destinationUrl: string) { assertStyle( iconElement.$.icon, 'background-image', - getFaviconForPageURL(destinationUrl, false, '', 32, true)); + + // Resolution units are converted from `x` (shorthand for `dppx`) to + // `dppx` (the canonical unit for the resolution type) because + // assertStyle is using computed values instead of specified ones, and + // the computed values have to return the canonical unit for the type. + getFaviconForPageURL(destinationUrl, false, '', 32, true) + .replace(' 1x', ' 1dppx') + .replace(' 2x', ' 2dppx')); assertStyle(iconElement.$.icon, '-webkit-mask-image', 'none'); }
diff --git a/chrome/test/data/webui/settings/site_favicon_test.ts b/chrome/test/data/webui/settings/site_favicon_test.ts index 18fdf9c7..b6d2cf8 100644 --- a/chrome/test/data/webui/settings/site_favicon_test.ts +++ b/chrome/test/data/webui/settings/site_favicon_test.ts
@@ -22,7 +22,7 @@ } function formExpected(url: string): string { - return '-webkit-image-set(' + + return 'image-set(' + 'url("chrome://favicon2/?size=16&scaleFactor=1x&pageUrl=' + encodeURIComponent(url) + '&allowGoogleServerFallback=0") 1x, ' + 'url("chrome://favicon2/?size=16&scaleFactor=2x&pageUrl=' +
diff --git a/chrome/test/data/webui/side_panel/read_anything/read_anything_app_test.ts b/chrome/test/data/webui/side_panel/read_anything/read_anything_app_test.ts index 0c4926f8..95cf1ee0 100644 --- a/chrome/test/data/webui/side_panel/read_anything/read_anything_app_test.ts +++ b/chrome/test/data/webui/side_panel/read_anything/read_anything_app_test.ts
@@ -6,7 +6,7 @@ import 'chrome://webui-test/mojo_webui_test_support.js'; import {ReadAnythingElement} from 'chrome://read-anything-side-panel.top-chrome/app.js'; -import {assertEquals} from 'chrome://webui-test/chai_assert.js'; +import {assertEquals, assertFalse, assertStringContains} from 'chrome://webui-test/chai_assert.js'; import {eventToPromise} from 'chrome://webui-test/test_util.js'; suite('ReadAnythingAppTest', () => { @@ -17,27 +17,6 @@ // rest of the Read Anything feature, which we are not testing here. chrome.readAnything.onConnected = () => {}; - // This is called by readAnythingApp onselectionchange. It is usually - // implemented by ReadAnythingAppController which forwards these arguments to - // the browser process in the form of an AXEventNotificationDetail. Instead, - // we capture the arguments here and verify their values. Since - // onselectionchange is called asynchronously, the test must wait for this - // function to be called; therefore we fire a custom event - // on-selection-change-for-text here for the test to await. - chrome.readAnything.onSelectionChange = - (anchorNodeId: number, anchorOffset: number, focusNodeId: number, - focusOffset: number) => { - readAnythingApp.shadowRoot!.dispatchEvent( - new CustomEvent('on-selection-change-for-test', { - detail: { - anchorNodeId: anchorNodeId, - anchorOffset: anchorOffset, - focusNodeId: focusNodeId, - focusOffset: focusOffset, - }, - })); - }; - setup(() => { document.body.innerHTML = window.trustedTypes!.emptyHTML; readAnythingApp = document.createElement('read-anything-app'); @@ -45,6 +24,29 @@ chrome.readAnything.setThemeForTesting('default', 18.0, 0, 0, 1, 0); }); + const setOnSelectionChangeForTest = () => { + // This is called by readAnythingApp onselectionchange. It is usually + // implemented by ReadAnythingAppController which forwards these arguments + // to the browser process in the form of an AXEventNotificationDetail. + // Instead, we capture the arguments here and verify their values. Since + // onselectionchange is called asynchronously, the test must wait for this + // function to be called; therefore we fire a custom event + // on-selection-change-for-text here for the test to await. + chrome.readAnything.onSelectionChange = + (anchorNodeId: number, anchorOffset: number, focusNodeId: number, + focusOffset: number) => { + readAnythingApp.shadowRoot!.dispatchEvent( + new CustomEvent('on-selection-change-for-test', { + detail: { + anchorNodeId: anchorNodeId, + anchorOffset: anchorOffset, + focusNodeId: focusNodeId, + focusOffset: focusOffset, + }, + })); + }; + }; + const assertFontName = (fontFamily: string) => { const container = readAnythingApp.shadowRoot!.getElementById('container'); assertEquals(fontFamily, getComputedStyle(container!).fontFamily); @@ -119,6 +121,78 @@ assertEquals('1.6px', getComputedStyle(container!).letterSpacing); }); + // The loading screen shows when we connect. + test('connectedCallback showLoadingScreen', () => { + const emptyState = + readAnythingApp.shadowRoot!.querySelector('sp-empty-state')!; + + assertEquals( + readAnythingApp.shadowRoot!.getElementById( + 'empty-state-container')!.hidden, + false); + assertEquals(emptyState.heading, 'Getting ready'); + assertEquals(emptyState.body, ''); + assertStringContains(emptyState.imagePath, 'throbber'); + assertStringContains(emptyState.darkImagePath, 'throbber'); + }); + + test( + 'onselectionchange nothingSelectedOnLoadingScreenSelection', async () => { + const emptyState = + readAnythingApp.shadowRoot!.getElementById('empty-state-container'); + let selectionChanged = false; + chrome.readAnything.onSelectionChange = + (_anchorNodeId: number, _anchorOffset: number, _focusNodeId: number, + _focusOffset: number) => { + selectionChanged = true; + }; + + const range = new Range(); + range.setStartBefore(emptyState!); + range.setEndAfter(emptyState!); + const selection = readAnythingApp.shadowRoot!.getSelection(); + selection!.removeAllRanges(); + selection!.addRange(range); + + setTimeout(() => { + assertFalse(selectionChanged); + }, 1000); + }); + + test('updateContent hidesLoadingScreen', () => { + // root htmlTag='#document' id=1 + // ++paragraph htmlTag='p' id=2 + // ++++staticText name='This is a paragraph' id=3 + const axTree = { + rootId: 1, + nodes: [ + { + id: 1, + role: 'rootWebArea', + htmlTag: '#document', + childIds: [2], + }, + { + id: 2, + role: 'paragraph', + htmlTag: 'p', + childIds: [3], + }, + { + id: 3, + role: 'staticText', + name: 'This is a paragraph', + }, + ], + }; + chrome.readAnything.setContentForTesting(axTree, [2]); + + assertEquals( + readAnythingApp.shadowRoot!.getElementById( + 'empty-state-container')!.hidden, + true); + }); + test('updateContent paragraph', () => { // root htmlTag='#document' id=1 // ++paragraph htmlTag='p' id=2 @@ -631,6 +705,7 @@ is_backward: false, }, }; + setOnSelectionChangeForTest(); chrome.readAnything.setContentForTesting(axTree, []); // The expected string contains the complete text of each node in the // selection. @@ -708,6 +783,7 @@ is_backward: true, }, }; + setOnSelectionChangeForTest(); chrome.readAnything.setContentForTesting(axTree, []); // The expected string contains the complete text of each node in the // selection. @@ -751,6 +827,7 @@ }, ], }; + setOnSelectionChangeForTest(); chrome.readAnything.setContentForTesting(axTree, [1]); const expected = '<div>HelloWorldFriend</div>'; assertContainerInnerHTML(expected);
diff --git a/chrome/updater/mac/keystone/agent_main.cc b/chrome/updater/mac/keystone/agent_main.cc index 537cc5b..e246d74 100644 --- a/chrome/updater/mac/keystone/agent_main.cc +++ b/chrome/updater/mac/keystone/agent_main.cc
@@ -2,17 +2,186 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <iostream> +#include <map> +#include <string> +#include <utility> + +#include "base/at_exit.h" #include "base/command_line.h" #include "base/files/file_path.h" +#include "base/functional/bind.h" +#include "base/functional/callback.h" +#include "base/functional/callback_helpers.h" +#include "base/logging.h" +#include "base/memory/scoped_refptr.h" +#include "base/message_loop/message_pump_type.h" +#include "base/path_service.h" #include "base/process/launch.h" +#include "base/ranges/algorithm.h" +#include "base/strings/string_util.h" +#include "base/task/single_thread_task_executor.h" +#include "base/task/thread_pool/thread_pool_instance.h" +#include "chrome/updater/app/app.h" #include "chrome/updater/constants.h" +#include "chrome/updater/ipc/ipc_support.h" +#include "chrome/updater/service_proxy_factory.h" +#include "chrome/updater/update_service.h" #include "chrome/updater/updater_scope.h" #include "chrome/updater/util/util.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace updater { -void Main() { +namespace { + +constexpr char kCommandProductID[] = "productIDToUpdate"; +constexpr char kCommandPrintResults[] = "printResults"; +constexpr char kCommandUserInitiated[] = "userInitiated"; + +// base::CommandLine can't be used because it is case-insensitive, and it does +// not support long switches name prefixed with single '-'. This argument parser +// converts an argv set into a map of switch name to switch value; for example +// `program_name -productIDToUpdate com.google.chrome -printResults YES` +// is converted to: +// `{"productIDToUpdate": "com.google.chrome", "printResults": "YES"}`. +std::map<std::string, std::string> ParseCommandLine(int argc, + const char* argv[]) { + std::map<std::string, std::string> result; + std::string key; + for (int i = 1; i < argc; ++i) { + std::string arg(argv[i]); + if (base::StartsWith(arg, "-")) { + key = arg.substr(1); + result[key] = ""; + } else { + if (!key.empty()) { + result[key] = arg; + } + key = ""; + } + } + return result; +} + +UpdaterScope Scope() { + base::FilePath executable_path; + if (base::PathService::Get(base::FILE_EXE, &executable_path) && + base::StartsWith(executable_path.value(), + GetKeystoneFolderPath(UpdaterScope::kSystem)->value())) { + return UpdaterScope::kSystem; + } else { + return UpdaterScope::kUser; + } +} + +class KSAgentApp : public App { + public: + explicit KSAgentApp(const std::map<std::string, std::string>& switches) + : switches_(switches) {} + + private: + ~KSAgentApp() override = default; + void FirstTaskRun() override; + + void ChooseServiceForApp( + const std::string& app_id, + base::OnceCallback<void(UpdaterScope scope)> callback) const; + + bool HasSwitch(const std::string& arg) const; + std::string SwitchValue(const std::string& arg) const; + + void UpdateApp(const std::string& app_id); + void DoUpdate(const std::string& app_id, UpdaterScope scope); + void RecordUpdateResult(const UpdateService::UpdateState& update_state); + void PrintUpdateResultAndShutDown(UpdateService::Result result); + + void Wake(); + + const std::map<std::string, std::string> switches_; + scoped_refptr<UpdateService> system_service_proxy_ = + CreateUpdateServiceProxy(UpdaterScope::kSystem); + scoped_refptr<UpdateService> user_service_proxy_ = + CreateUpdateServiceProxy(UpdaterScope::kUser); + int successful_install_count_ = 0; + ScopedIPCSupportWrapper ipc_support_; +}; + +void KSAgentApp::ChooseServiceForApp( + const std::string& app_id, + base::OnceCallback<void(UpdaterScope)> callback) const { + // Choose system scope if the app is a system app, otherwise choose + // user scope. + system_service_proxy_->GetAppStates(base::BindOnce( + [](const std::string& app_id, + base::OnceCallback<void(UpdaterScope)> callback, + const std::vector<updater::UpdateService::AppState>& states) { + std::move(callback).Run( + base::ranges::find_if( + states, + [&app_id](const updater::UpdateService::AppState& state) { + return base::EqualsCaseInsensitiveASCII(state.app_id, app_id); + }) == std::end(states) + ? UpdaterScope::kUser + : UpdaterScope::kSystem); + }, + app_id, std::move(callback))); +} + +bool KSAgentApp::HasSwitch(const std::string& arg) const { + return base::Contains(switches_, arg); +} + +std::string KSAgentApp::SwitchValue(const std::string& arg) const { + return HasSwitch(arg) ? switches_.at(arg) : std::string(); +} + +void KSAgentApp::RecordUpdateResult( + const UpdateService::UpdateState& update_state) { + if (update_state.state == UpdateService::UpdateState::State::kUpdated) { + // We don't need an accurate number of successful installations. A positive + // integer is enough to indicate that some updates are installed. + VLOG(0) << "An app update is installed successfully."; + successful_install_count_ += 1; + } +} + +void KSAgentApp::PrintUpdateResultAndShutDown(UpdateService::Result result) { + // The output string format need to be strictly followed because this is the + // contract between the registration framework and the agent. The registration + // framework parses the agent outputs and broadcasts the parsed results via + // macOS notification center. Apps (like Chrome) can monitor the notification + // center and update the UI accordingly. + std::cout << "updateCheckSuccessful_=" + << (result == UpdateService::Result::kSuccess ? "YES" : "NO") + << std::endl; + std::cout << "successfulInstallCount_=" << successful_install_count_ + << std::endl; + + Shutdown(result == UpdateService::Result::kSuccess ? 0 : 1); +} + +void KSAgentApp::UpdateApp(const std::string& app_id) { + ChooseServiceForApp(app_id, + base::BindOnce(&KSAgentApp::DoUpdate, this, app_id)); +} + +void KSAgentApp::DoUpdate(const std::string& app_id, UpdaterScope scope) { + VLOG(0) << "Updating " << app_id << " at " + << (scope == UpdaterScope::kSystem ? "system" : "user") << " scope."; + scoped_refptr<UpdateService> service_proxy = scope == UpdaterScope::kSystem + ? system_service_proxy_ + : user_service_proxy_; + service_proxy->Update( + app_id, "", UpdateService::Priority::kForeground, + UpdateService::PolicySameVersionUpdate::kNotAllowed, + /*do_update_check_only=*/false, + base::BindRepeating(&KSAgentApp::RecordUpdateResult, this), + base::BindOnce(&KSAgentApp::PrintUpdateResultAndShutDown, this)); +} + +void KSAgentApp::Wake() { + VLOG(0) << "Launching wake processes."; for (UpdaterScope scope : {UpdaterScope::kSystem, UpdaterScope::kUser}) { absl::optional<base::FilePath> path = GetUpdaterExecutablePath(scope); if (!path) { @@ -27,14 +196,40 @@ command.AppendSwitchNative(kLoggingModuleSwitch, kLoggingModuleSwitchValue); base::LaunchProcess(command, {}); } + Shutdown(0); } +void KSAgentApp::FirstTaskRun() { + // The agent is a shim to trick the keystone registration framework. + if (!SwitchValue(kCommandProductID).empty() && + SwitchValue(kCommandPrintResults) == "YES" && + SwitchValue(kCommandUserInitiated) == "YES") { + // If the agent is run with explicit arguments to print the update result, + // make a call directly into the service to get update details. + UpdateApp(SwitchValue(kCommandProductID)); + } else { + // Otherwise, just launch the --wake task. Not all callers correctly + // provide a scope, so it will wake both scopes (if present). + Wake(); + } +} + +int KSAgentAppMain(int argc, const char* argv[]) { + base::AtExitManager exit_manager; + base::CommandLine::Init(argc, argv); + updater::InitLogging(Scope()); + InitializeThreadPool("keystone"); + const base::ScopedClosureRunner shutdown_thread_pool( + base::BindOnce([]() { base::ThreadPoolInstance::Get()->Shutdown(); })); + base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI); + + return base::MakeRefCounted<KSAgentApp>(ParseCommandLine(argc, argv))->Run(); +} + +} // namespace + } // namespace updater -// The agent is a shim to trick the keystone registration framework. When run, -// it should launch the --wake task. Not all callers correctly provide a scope, -// so it will wake both scopes (if present). -int main() { - updater::Main(); - return 0; +int main(int argc, const char* argv[]) { + return updater::KSAgentAppMain(argc, argv); }
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd index 4fdc8b1..363cc9f4 100644 --- a/chromeos/chromeos_strings.grd +++ b/chromeos/chromeos_strings.grd
@@ -2609,6 +2609,9 @@ <message name="IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_WALLPAPER_COLOR_DESCRIPTION" desc="The description for the button that allows users to select a keyboard backlight wallpaper-extracted color."> Match wallpaper </message> + <message name="IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_ZONE_TITLE" desc="Title of the zone colors in the keyboard backlight customization dialog of personalization hub."> + Zone <ph name="ZONE_NUMBER">$1<ex>1</ex></ph> + </message> <!-- Personalization App Search Results --> <message name="IDS_PERSONALIZATION_APP_SEARCH_RESULT_TITLE" desc="Text for search result item which, when clicked, navigates the user to personalization app.">
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_ZONE_TITLE.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_ZONE_TITLE.png.sha1 new file mode 100644 index 0000000..df01c17d --- /dev/null +++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_KEYBOARD_BACKLIGHT_ZONE_TITLE.png.sha1
@@ -0,0 +1 @@ +cf0c4358e688f5ca594cd2883837591741e36982 \ No newline at end of file
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn index c02d908..a76dc61 100644 --- a/components/autofill/core/browser/BUILD.gn +++ b/components/autofill/core/browser/BUILD.gn
@@ -314,6 +314,7 @@ "payments/card_unmask_challenge_option.h", "payments/card_unmask_delegate.cc", "payments/card_unmask_delegate.h", + "payments/client_behavior_constants.h", "payments/credit_card_access_manager.cc", "payments/credit_card_access_manager.h", "payments/credit_card_cvc_authenticator.cc",
diff --git a/components/autofill/core/browser/payments/client_behavior_constants.h b/components/autofill/core/browser/payments/client_behavior_constants.h new file mode 100644 index 0000000..8eb40a2a --- /dev/null +++ b/components/autofill/core/browser/payments/client_behavior_constants.h
@@ -0,0 +1,27 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_CLIENT_BEHAVIOR_CONSTANTS_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_CLIENT_BEHAVIOR_CONSTANTS_H_ + +namespace autofill { +enum class ClientBehaviorConstants { + // ClientBehaviorConstant encompasses all the active client behaviors for the + // browser during the outgoing calls to the Payments server. + // Active client behaviors are the enums outlined below which tell a specific + // feature/experiment that is triggered/active on the browser. + // These enum flags are persisted to the Payments server logs. Entries should + // not be renumbered and numeric values should never be reused. + // Note that the number of the behavior, not the name, is sent in the JSON + // request to Payments. + + // For more information on this signal, see AutofillEnableNewSaveCardBubbleUi + // flag. This enum is to be always included in the client_behavior_signals + // from M113 onwards as this retrieves the correct TOS footer for + // FasterAndProtected bubble. + kUsingFasterAndProtectedUi = 1, +}; +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_CLIENT_BEHAVIOR_CONSTANTS_H_
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager.cc b/components/autofill/core/browser/payments/credit_card_save_manager.cc index a107b75b..73f5b229 100644 --- a/components/autofill/core/browser/payments/credit_card_save_manager.cc +++ b/components/autofill/core/browser/payments/credit_card_save_manager.cc
@@ -25,6 +25,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" +#include "client_behavior_constants.h" #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_experiments.h" #include "components/autofill/core/browser/autofill_type.h" @@ -33,6 +34,7 @@ #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/logging/log_manager.h" #include "components/autofill/core/browser/metrics/autofill_metrics.h" +#include "components/autofill/core/browser/payments/client_behavior_constants.h" #include "components/autofill/core/browser/payments/payments_client.h" #include "components/autofill/core/browser/payments/payments_util.h" #include "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h" @@ -277,9 +279,17 @@ show_save_prompt_ = !GetCreditCardSaveStrikeDatabase()->ShouldBlockFeature( base::UTF16ToUTF8(upload_request_.card.LastFourDigits())); +#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) + if (base::FeatureList::IsEnabled( + features::kAutofillEnableNewSaveCardBubbleUi)) { + upload_request_.client_behavior_signals.push_back( + ClientBehaviorConstants::kUsingFasterAndProtectedUi); + } +#endif // !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) + payments_client_->GetUploadDetails( country_only_profiles, upload_request_.detected_values, - upload_request_.active_experiments, app_locale_, + upload_request_.client_behavior_signals, app_locale_, base::BindOnce(&CreditCardSaveManager::OnDidGetUploadDetails, weak_ptr_factory_.GetWeakPtr()), payments::kUploadCardBillableServiceNumber,
diff --git a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc index 8eaa8d2..ba144ff 100644 --- a/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc +++ b/components/autofill/core/browser/payments/credit_card_save_manager_unittest.cc
@@ -30,6 +30,7 @@ #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/metrics/autofill_metrics.h" #include "components/autofill/core/browser/metrics/payments/credit_card_save_metrics.h" +#include "components/autofill/core/browser/payments/client_behavior_constants.h" #include "components/autofill/core/browser/payments/payments_customer_data.h" #include "components/autofill/core/browser/payments/payments_util.h" #include "components/autofill/core/browser/payments/test_credit_card_save_manager.h" @@ -568,7 +569,7 @@ FormSubmitted(credit_card_form); EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded()); - EXPECT_TRUE(payments_client_->active_experiments_in_request().empty()); + EXPECT_TRUE(payments_client_->client_behavior_signals_in_request().empty()); // Verify that even though the full address profile was saved, only the // country was included in the upload details request to payments. @@ -1689,6 +1690,70 @@ } #endif +#if !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) +TEST_F(CreditCardSaveManagerTest, + AttemptToOfferCardUploadSave_AutofillEnableNewSaveCardBubbleUiEnabled) { + // Setting the flag to enable the new bubble for Save Card UI. + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillEnableNewSaveCardBubbleUi); + + // Set up our credit card form data. + FormData credit_card_form; + CreateTestCreditCardFormData(&credit_card_form, CreditCardFormOptions()); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + + // Edit the data, and submit. + credit_card_form.fields[0].value = u"Jane Doe"; + credit_card_form.fields[1].value = u"4111111111111111"; + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); + credit_card_form.fields[4].value = u"123"; + FormSubmitted(credit_card_form); + + EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); + EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded()); + + // Confirm that client_behavior_signals vector does contain the + // FasterAndProtected signal. + std::vector<ClientBehaviorConstants> client_behavior_signals_in_request = + payments_client_->client_behavior_signals_in_request(); + EXPECT_THAT( + client_behavior_signals_in_request, + testing::Contains(ClientBehaviorConstants::kUsingFasterAndProtectedUi)); +} + +TEST_F(CreditCardSaveManagerTest, + AttemptToOfferCardUploadSave_AutofillEnableNewSaveCardBubbleUiDisabled) { + // Setting the flag to disable the new bubble for Save Card UI. + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndDisableFeature( + features::kAutofillEnableNewSaveCardBubbleUi); + + FormData credit_card_form; + CreateTestCreditCardFormData(&credit_card_form, CreditCardFormOptions()); + FormsSeen(std::vector<FormData>(1, credit_card_form)); + + credit_card_form.fields[0].value = u"Jane Doe"; + credit_card_form.fields[1].value = u"4111111111111111"; + credit_card_form.fields[2].value = ASCIIToUTF16(test::NextMonth()); + credit_card_form.fields[3].value = ASCIIToUTF16(test::NextYear()); + credit_card_form.fields[4].value = u"123"; + FormSubmitted(credit_card_form); + + EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); + EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded()); + + // Confirm that client_behavior_signals vector does not contain the + // FasterAndProtected signal. + std::vector<ClientBehaviorConstants> client_behavior_signals_in_request = + payments_client_->client_behavior_signals_in_request(); + EXPECT_THAT(client_behavior_signals_in_request, + testing::Not(testing::Contains( + ClientBehaviorConstants::kUsingFasterAndProtectedUi))); +} +#endif // !BUILDFLAG(IS_IOS) && !BUILDFLAG(IS_ANDROID) + // TODO(crbug.com/1113034): Create an equivalent test for iOS, or skip // permanently if the test doesn't apply to iOS flow. #if !BUILDFLAG(IS_IOS) @@ -4217,7 +4282,7 @@ FormSubmitted(credit_card_form); EXPECT_FALSE(autofill_client_.ConfirmSaveCardLocallyWasCalled()); EXPECT_TRUE(credit_card_save_manager_->CreditCardWasUploaded()); - EXPECT_TRUE(payments_client_->active_experiments_in_request().empty()); + EXPECT_TRUE(payments_client_->client_behavior_signals_in_request().empty()); } TEST_F(CreditCardSaveManagerTest,
diff --git a/components/autofill/core/browser/payments/local_card_migration_manager.cc b/components/autofill/core/browser/payments/local_card_migration_manager.cc index adafc6e..6e5a237 100644 --- a/components/autofill/core/browser/payments/local_card_migration_manager.cc +++ b/components/autofill/core/browser/payments/local_card_migration_manager.cc
@@ -20,6 +20,7 @@ #include "components/autofill/core/browser/form_data_importer.h" #include "components/autofill/core/browser/metrics/autofill_metrics.h" #include "components/autofill/core/browser/metrics/payments/local_card_migration_metrics.h" +#include "components/autofill/core/browser/payments/client_behavior_constants.h" #include "components/autofill/core/browser/payments/payments_client.h" #include "components/autofill/core/browser/payments/payments_util.h" #include "components/autofill/core/browser/personal_data_manager.h" @@ -139,7 +140,8 @@ payments_client_->GetUploadDetails( std::vector<AutofillProfile>(), GetDetectedValues(), - /*active_experiments=*/std::vector<const char*>(), app_locale_, + /*client_behavior_signals=*/std::vector<ClientBehaviorConstants>(), + app_locale_, base::BindOnce(&LocalCardMigrationManager::OnDidGetUploadDetails, weak_ptr_factory_.GetWeakPtr(), is_from_settings_page), payments::kMigrateCardsBillableServiceNumber,
diff --git a/components/autofill/core/browser/payments/payments_client.cc b/components/autofill/core/browser/payments/payments_client.cc index 5e6cada..7ebdc1a 100644 --- a/components/autofill/core/browser/payments/payments_client.cc +++ b/components/autofill/core/browser/payments/payments_client.cc
@@ -22,6 +22,7 @@ #include "components/autofill/core/browser/data_model/autofill_data_model.h" #include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/payments/account_info_getter.h" +#include "components/autofill/core/browser/payments/client_behavior_constants.h" #include "components/autofill/core/browser/payments/local_card_migration_manager.h" #include "components/autofill/core/browser/payments/payments_requests/get_details_for_enrollment_request.h" #include "components/autofill/core/browser/payments/payments_requests/get_unmask_details_request.h" @@ -278,7 +279,7 @@ void PaymentsClient::GetUploadDetails( const std::vector<AutofillProfile>& addresses, const int detected_values, - const std::vector<const char*>& active_experiments, + const std::vector<ClientBehaviorConstants>& client_behavior_signals, const std::string& app_locale, base::OnceCallback<void(AutofillClient::PaymentsRpcResult, const std::u16string&, @@ -288,7 +289,7 @@ const int64_t billing_customer_number, UploadCardSource upload_card_source) { IssueRequest(std::make_unique<GetUploadDetailsRequest>( - addresses, detected_values, active_experiments, + addresses, detected_values, client_behavior_signals, account_info_getter_->IsSyncFeatureEnabled(), app_locale, std::move(callback), billable_service_number, billing_customer_number, upload_card_source),
diff --git a/components/autofill/core/browser/payments/payments_client.h b/components/autofill/core/browser/payments/payments_client.h index 09ce205..833a8b2 100644 --- a/components/autofill/core/browser/payments/payments_client.h +++ b/components/autofill/core/browser/payments/payments_client.h
@@ -21,6 +21,7 @@ #include "components/autofill/core/browser/payments/autofill_error_dialog_context.h" #include "components/autofill/core/browser/payments/card_unmask_challenge_option.h" #include "components/autofill/core/browser/payments/card_unmask_delegate.h" +#include "components/autofill/core/browser/payments/client_behavior_constants.h" #include "components/autofill/core/browser/payments/virtual_card_enrollment_flow.h" #include "google_apis/gaia/google_service_auth_error.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -341,7 +342,7 @@ std::u16string context_token; std::string risk_data; std::string app_locale; - std::vector<const char*> active_experiments; + std::vector<ClientBehaviorConstants> client_behavior_signals; }; // An enum set in the GetUploadDetailsRequest indicating the source of the @@ -452,14 +453,13 @@ // decisions. |callback| is the callback function when get response from // server. |billable_service_number| is used to set the billable service // number in the GetUploadDetails request. If the conditions are met, the - // legal message will be returned via |callback|. |active_experiments| is used - // by Payments server to track requests that were triggered by enabled - // features. |upload_card_source| is used by Payments server metrics to track - // the source of the request. + // legal message will be returned via |callback|. |client_behavior_signals| is + // used by Payments server to track Chrome behaviors. |upload_card_source| is + // used by Payments server metrics to track the source of the request. virtual void GetUploadDetails( const std::vector<AutofillProfile>& addresses, const int detected_values, - const std::vector<const char*>& active_experiments, + const std::vector<ClientBehaviorConstants>& client_behavior_signals, const std::string& app_locale, base::OnceCallback<void(AutofillClient::PaymentsRpcResult, const std::u16string&,
diff --git a/components/autofill/core/browser/payments/payments_client_unittest.cc b/components/autofill/core/browser/payments/payments_client_unittest.cc index 50d81c6..8f56768 100644 --- a/components/autofill/core/browser/payments/payments_client_unittest.cc +++ b/components/autofill/core/browser/payments/payments_client_unittest.cc
@@ -21,6 +21,7 @@ #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/payments/autofill_error_dialog_context.h" #include "components/autofill/core/browser/payments/card_unmask_challenge_option.h" +#include "components/autofill/core/browser/payments/client_behavior_constants.h" #include "components/autofill/core/browser/payments/credit_card_save_manager.h" #include "components/autofill/core/browser/payments/local_card_migration_manager.h" #include "components/autofill/core/browser/payments/payments_client.h" @@ -38,8 +39,11 @@ #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" #include "services/network/test/test_url_loader_factory.h" #include "services/network/test/test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +using ::testing::HasSubstr; + namespace autofill::payments { namespace { @@ -138,9 +142,16 @@ return *this; } + GetUploadDetailsOptions& with_client_behavior_signals( + std::vector<ClientBehaviorConstants> v) { + client_behavior_signals = std::move(v); + return *this; + } + PaymentsClient::UploadCardSource upload_card_source = PaymentsClient::UploadCardSource::UNKNOWN_UPLOAD_CARD_SOURCE; int64_t billing_customer_number = 111222333444L; + std::vector<ClientBehaviorConstants> client_behavior_signals; }; struct UploadCardOptions { @@ -159,9 +170,16 @@ return *this; } + UploadCardOptions& with_client_behavior_signals( + std::vector<ClientBehaviorConstants> v) { + client_behavior_signals = std::move(v); + return *this; + } + bool include_cvc = false; bool include_nickname = false; int64_t billing_customer_number = 111222333444L; + std::vector<ClientBehaviorConstants> client_behavior_signals; }; } // namespace @@ -351,8 +369,8 @@ void StartGettingUploadDetails( GetUploadDetailsOptions get_upload_details_options) { client_->GetUploadDetails( - BuildTestProfiles(), kAllDetectableValues, std::vector<const char*>(), - "language-LOCALE", + BuildTestProfiles(), kAllDetectableValues, + get_upload_details_options.client_behavior_signals, "language-LOCALE", base::BindOnce(&PaymentsClientTest::OnDidGetUploadDetails, weak_ptr_factory_.GetWeakPtr()), /*billable_service_number=*/12345, @@ -374,6 +392,9 @@ upstream_nickname_ = u"grocery"; request_details.card.SetNickname(upstream_nickname_); } + request_details.client_behavior_signals = + upload_card_options.client_behavior_signals; + request_details.context_token = u"context token"; request_details.risk_data = "some risk data"; request_details.app_locale = "language-LOCALE"; @@ -1176,6 +1197,27 @@ } TEST_F(PaymentsClientTest, + GetDetailsIncludesIncludesClientBehaviorSignalsInChromeUserContext) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + features::kAutofillEnableNewSaveCardBubbleUi); + + StartGettingUploadDetails( + GetUploadDetailsOptions().with_client_behavior_signals( + std::vector<ClientBehaviorConstants>{ + ClientBehaviorConstants::kUsingFasterAndProtectedUi})); + + // Verify ChromeUserContext was set. + EXPECT_THAT(GetUploadData(), HasSubstr("chrome_user_context")); + // Verify Client_behavior_signals was set. + EXPECT_THAT(GetUploadData(), HasSubstr("client_behavior_signals")); + // Verify fake_client_behavior_signal was set. + // ClientBehaviorConstants::kUsingFasterAndProtectedUi has the numeric value + // set to 1. + EXPECT_THAT(GetUploadData(), HasSubstr("\"client_behavior_signals\":[1]")); +} + +TEST_F(PaymentsClientTest, GetDetailsIncludesChromeUserContextIfWalletStorageFlagEnabled) { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature( @@ -1550,6 +1592,27 @@ std::string::npos); } +TEST_F(PaymentsClientTest, UploadRequestIncludesClientBehaviorSignals) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + features::kAutofillEnableNewSaveCardBubbleUi); + + StartUploading(UploadCardOptions().with_client_behavior_signals( + std::vector<ClientBehaviorConstants>{ + ClientBehaviorConstants::kUsingFasterAndProtectedUi})); + IssueOAuthToken(); + + // Verify ChromeUserContext was set. + EXPECT_THAT(GetUploadData(), HasSubstr("chrome_user_context")); + // Verify Client_behavior_signals was set. + EXPECT_THAT(GetUploadData(), HasSubstr("client_behavior_signals")); + // Verify fake_client_behavior_signal was set. + // ClientBehaviorConstants::kUsingFasterAndProtectedUi has the numeric value + // set to 1. + EXPECT_THAT(GetUploadData(), + HasSubstr("%22client_behavior_signals%22:%5B1%5D")); +} + TEST_F(PaymentsClientTest, UploadRequestIncludesEncryptedPanUsingAlternateType) { base::test::ScopedFeatureList feature_list;
diff --git a/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.cc b/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.cc index ca3af53..934cd62 100644 --- a/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.cc +++ b/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.cc
@@ -11,6 +11,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" +#include "components/autofill/core/browser/payments/client_behavior_constants.h" namespace autofill::payments { @@ -22,7 +23,7 @@ GetUploadDetailsRequest::GetUploadDetailsRequest( const std::vector<AutofillProfile>& addresses, const int detected_values, - const std::vector<const char*>& active_experiments, + const std::vector<ClientBehaviorConstants>& client_behavior_signals, const bool full_sync_enabled, const std::string& app_locale, base::OnceCallback<void(AutofillClient::PaymentsRpcResult, @@ -34,7 +35,7 @@ PaymentsClient::UploadCardSource upload_card_source) : addresses_(addresses), detected_values_(detected_values), - active_experiments_(active_experiments), + client_behavior_signals_(client_behavior_signals), full_sync_enabled_(full_sync_enabled), app_locale_(app_locale), callback_(std::move(callback)), @@ -62,10 +63,9 @@ BuildCustomerContextDictionary(billing_customer_number_)); } request_dict.Set("context", std::move(context)); - - base::Value::Dict chrome_user_context; - chrome_user_context.Set("full_sync_enabled", full_sync_enabled_); - request_dict.Set("chrome_user_context", std::move(chrome_user_context)); + request_dict.Set( + "chrome_user_context", + BuildChromeUserContext(client_behavior_signals_, full_sync_enabled_)); base::Value::List addresses; for (const AutofillProfile& profile : addresses_) { @@ -85,8 +85,6 @@ // Payments will decide if the provided data is enough to offer upload save. request_dict.Set("detected_values", detected_values_); - SetActiveExperiments(active_experiments_, request_dict); - switch (upload_card_source_) { case PaymentsClient::UploadCardSource::UNKNOWN_UPLOAD_CARD_SOURCE: request_dict.Set("upload_card_source", "UNKNOWN_UPLOAD_CARD_SOURCE");
diff --git a/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.h b/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.h index 8f0cdde5..1023845 100644 --- a/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.h +++ b/components/autofill/core/browser/payments/payments_requests/get_upload_details_request.h
@@ -10,6 +10,7 @@ #include "base/functional/callback.h" #include "base/values.h" #include "components/autofill/core/browser/autofill_client.h" +#include "components/autofill/core/browser/payments/client_behavior_constants.h" #include "components/autofill/core/browser/payments/payments_client.h" #include "components/autofill/core/browser/payments/payments_requests/payments_request.h" @@ -20,7 +21,7 @@ GetUploadDetailsRequest( const std::vector<AutofillProfile>& addresses, const int detected_values, - const std::vector<const char*>& active_experiments, + const std::vector<ClientBehaviorConstants>& client_behavior_signals, const bool full_sync_enabled, const std::string& app_locale, base::OnceCallback<void(AutofillClient::PaymentsRpcResult, @@ -52,7 +53,7 @@ const std::vector<AutofillProfile> addresses_; const int detected_values_; - const std::vector<const char*> active_experiments_; + const std::vector<ClientBehaviorConstants> client_behavior_signals_; const bool full_sync_enabled_; std::string app_locale_; base::OnceCallback<void(AutofillClient::PaymentsRpcResult,
diff --git a/components/autofill/core/browser/payments/payments_requests/payments_request.cc b/components/autofill/core/browser/payments/payments_requests/payments_request.cc index 3924679..04df0b5 100644 --- a/components/autofill/core/browser/payments/payments_requests/payments_request.cc +++ b/components/autofill/core/browser/payments/payments_requests/payments_request.cc
@@ -7,8 +7,10 @@ #include <utility> #include "base/strings/string_number_conversions.h" +#include "base/types/cxx23_to_underlying.h" #include "base/values.h" #include "build/build_config.h" +#include "components/autofill/core/browser/payments/client_behavior_constants.h" #include "components/autofill/core/browser/payments/payments_client.h" namespace autofill::payments { @@ -53,18 +55,20 @@ return customer_context; } -void PaymentsRequest::SetActiveExperiments( - const std::vector<const char*>& active_experiments, - base::Value::Dict& request_dict) { - if (active_experiments.empty()) - return; - - base::Value::List active_chrome_experiments; - for (const char* experiment : active_experiments) - active_chrome_experiments.Append(experiment); - - request_dict.Set("active_chrome_experiments", - std::move(active_chrome_experiments)); +base::Value::Dict PaymentsRequest::BuildChromeUserContext( + const std::vector<ClientBehaviorConstants>& client_behavior_signals, + bool full_sync_enabled) { + base::Value::Dict chrome_user_context; + chrome_user_context.Set("full_sync_enabled", full_sync_enabled); + if (!client_behavior_signals.empty()) { + base::Value::List active_client_signals; + for (ClientBehaviorConstants signal : client_behavior_signals) { + active_client_signals.Append(base::to_underlying(signal)); + } + chrome_user_context.Set("client_behavior_signals", + std::move(active_client_signals)); + } + return chrome_user_context; } base::Value::Dict PaymentsRequest::BuildAddressDictionary(
diff --git a/components/autofill/core/browser/payments/payments_requests/payments_request.h b/components/autofill/core/browser/payments/payments_requests/payments_request.h index 457d75a..8d0f48fb 100644 --- a/components/autofill/core/browser/payments/payments_requests/payments_request.h +++ b/components/autofill/core/browser/payments/payments_requests/payments_request.h
@@ -11,6 +11,7 @@ #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/credit_card.h" +#include "components/autofill/core/browser/payments/client_behavior_constants.h" #include "components/autofill/core/common/autofill_payments_features.h" namespace autofill::payments { @@ -53,11 +54,11 @@ base::Value::Dict BuildCustomerContextDictionary( int64_t external_customer_id); - // Shared helper function that populates the list of active experiments that - // affect either the data sent in payments RPCs or whether the RPCs are sent - // or not. - void SetActiveExperiments(const std::vector<const char*>& active_experiments, - base::Value::Dict& request_dict); + // Shared helper function that builds the Chrome user context which is then + // set in the payment requests. + base::Value::Dict BuildChromeUserContext( + const std::vector<ClientBehaviorConstants>& client_behavior_signals, + bool full_sync_enabled); // Shared helper functoin that returns a dictionary with the structure // expected by Payments RPCs, containing each of the fields in |profile|,
diff --git a/components/autofill/core/browser/payments/payments_requests/upload_card_request.cc b/components/autofill/core/browser/payments/payments_requests/upload_card_request.cc index 25987cc..1042348 100644 --- a/components/autofill/core/browser/payments/payments_requests/upload_card_request.cc +++ b/components/autofill/core/browser/payments/payments_requests/upload_card_request.cc
@@ -76,10 +76,10 @@ } request_dict.Set("context", std::move(context)); - base::Value::Dict chrome_user_context; - chrome_user_context.Set("full_sync_enabled", full_sync_enabled_); - request_dict.Set("chrome_user_context", std::move(chrome_user_context)); - + request_dict.Set( + "chrome_user_context", + BuildChromeUserContext(request_details_.client_behavior_signals, + full_sync_enabled_)); SetStringIfNotEmpty(request_details_.card, CREDIT_CARD_NAME_FULL, app_locale, "cardholder_name", request_dict); @@ -105,8 +105,6 @@ request_dict.Set("nickname", request_details_.card.nickname()); } - SetActiveExperiments(request_details_.active_experiments, request_dict); - const std::u16string pan = request_details_.card.GetInfo( AutofillType(CREDIT_CARD_NUMBER), app_locale); std::string json_request;
diff --git a/components/autofill/core/browser/payments/test_payments_client.cc b/components/autofill/core/browser/payments/test_payments_client.cc index 42d57219..3d597b69 100644 --- a/components/autofill/core/browser/payments/test_payments_client.cc +++ b/components/autofill/core/browser/payments/test_payments_client.cc
@@ -56,7 +56,7 @@ void TestPaymentsClient::GetUploadDetails( const std::vector<AutofillProfile>& addresses, const int detected_values, - const std::vector<const char*>& active_experiments, + const std::vector<ClientBehaviorConstants>& client_behavior_signals, const std::string& app_locale, base::OnceCallback<void(AutofillClient::PaymentsRpcResult, const std::u16string&, @@ -67,7 +67,7 @@ PaymentsClient::UploadCardSource upload_card_source) { upload_details_addresses_ = addresses; detected_values_ = detected_values; - active_experiments_ = active_experiments; + client_behavior_signals_ = client_behavior_signals; billable_service_number_ = billable_service_number; billing_customer_number_ = billing_customer_number; upload_card_source_ = upload_card_source; @@ -85,7 +85,7 @@ const PaymentsClient::UploadCardResponseDetails&)> callback) { upload_card_addresses_ = request_details.profiles; - active_experiments_ = request_details.active_experiments; + client_behavior_signals_ = request_details.client_behavior_signals; std::move(callback).Run(AutofillClient::PaymentsRpcResult::kSuccess, upload_card_response_details_); }
diff --git a/components/autofill/core/browser/payments/test_payments_client.h b/components/autofill/core/browser/payments/test_payments_client.h index 363bd7b..bfd749a9 100644 --- a/components/autofill/core/browser/payments/test_payments_client.h +++ b/components/autofill/core/browser/payments/test_payments_client.h
@@ -47,7 +47,7 @@ void GetUploadDetails( const std::vector<AutofillProfile>& addresses, const int detected_values, - const std::vector<const char*>& active_experiments, + const std::vector<ClientBehaviorConstants>& client_behavior_signals, const std::string& app_locale, base::OnceCallback<void(AutofillClient::PaymentsRpcResult, const std::u16string&, @@ -139,8 +139,9 @@ const std::vector<AutofillProfile>& addresses_in_upload_card() const { return upload_card_addresses_; } - const std::vector<const char*>& active_experiments_in_request() const { - return active_experiments_; + const std::vector<ClientBehaviorConstants>& + client_behavior_signals_in_request() const { + return client_behavior_signals_; } int billable_service_number_in_request() const { return billable_service_number_; @@ -177,7 +178,7 @@ std::vector<AutofillProfile> upload_card_addresses_; int detected_values_; std::string pan_first_six_; - std::vector<const char*> active_experiments_; + std::vector<ClientBehaviorConstants> client_behavior_signals_; int billable_service_number_; int64_t billing_customer_number_; PaymentsClient::UploadCardSource upload_card_source_;
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc index 0a34ec4..739fb5ac 100644 --- a/components/autofill/core/browser/personal_data_manager.cc +++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -2138,51 +2138,22 @@ return AddIBAN(imported_iban); } -void PersonalDataManager::LogStoredProfileMetrics() const { - if (!has_logged_stored_profile_metrics_) { - autofill_metrics::LogStoredProfileMetrics(GetProfiles()); - // Only log this info once per Chrome user profile load. - has_logged_stored_profile_metrics_ = true; +void PersonalDataManager::LogStoredDataMetrics() const { + if (has_logged_stored_data_metrics_) { + return; } -} + // Only log this info once per Chrome user profile load. + has_logged_stored_data_metrics_ = true; -void PersonalDataManager::LogStoredCreditCardMetrics() const { - if (!has_logged_stored_credit_card_metrics_) { - AutofillMetrics::LogStoredCreditCardMetrics( - local_credit_cards_, server_credit_cards_, - GetServerCardWithArtImageCount(), kDisusedDataModelTimeDelta); - - // Only log this info once per Chrome user profile load. - has_logged_stored_credit_card_metrics_ = true; - } -} - -void PersonalDataManager::LogStoredIbanMetrics() const { - if (!has_logged_stored_iban_metrics_) { - autofill_metrics::LogStoredIbanMetrics(local_ibans_, - kDisusedDataModelTimeDelta); - - // Only log this info once per Chrome user profile load. - has_logged_stored_iban_metrics_ = true; - } -} - -void PersonalDataManager::LogStoredOfferMetrics() const { - if (!has_logged_stored_offer_metrics_) { - autofill_metrics::LogStoredOfferMetrics(autofill_offer_data_); - // Only log this info once per Chrome user profile load. - has_logged_stored_offer_metrics_ = true; - } -} - -void PersonalDataManager::LogStoredVirtualCardUsageMetrics() const { - if (!has_logged_stored_virtual_card_usage_metrics_) { - autofill_metrics::LogStoredVirtualCardUsageCount( - autofill_virtual_card_usage_data_.size()); - - // Only log this info once per chrome user profile load. - has_logged_stored_virtual_card_usage_metrics_ = true; - } + autofill_metrics::LogStoredProfileMetrics(GetProfiles()); + AutofillMetrics::LogStoredCreditCardMetrics( + local_credit_cards_, server_credit_cards_, + GetServerCardWithArtImageCount(), kDisusedDataModelTimeDelta); + autofill_metrics::LogStoredIbanMetrics(local_ibans_, + kDisusedDataModelTimeDelta); + autofill_metrics::LogStoredOfferMetrics(autofill_offer_data_); + autofill_metrics::LogStoredVirtualCardUsageCount( + autofill_virtual_card_usage_data_.size()); } std::string PersonalDataManager::MostCommonCountryCodeFromProfiles() const {
diff --git a/components/autofill/core/browser/personal_data_manager.h b/components/autofill/core/browser/personal_data_manager.h index 21050ca8..380ed37 100644 --- a/components/autofill/core/browser/personal_data_manager.h +++ b/components/autofill/core/browser/personal_data_manager.h
@@ -710,24 +710,8 @@ void CancelPendingServerQuery(WebDataServiceBase::Handle* handle); // The first time this is called, logs a UMA metrics about the user's autofill - // addresses. On subsequent calls, does nothing. - void LogStoredProfileMetrics() const; - - // The first time this is called, logs an UMA metric about the user's autofill - // credit cards. On subsequent calls, does nothing. - void LogStoredCreditCardMetrics() const; - - // The first time this is called, logs an UMA metric about the user's autofill - // IBANs. On subsequent calls, does nothing. - void LogStoredIbanMetrics() const; - - // The first time this is called, logs UMA metrics about the users's autofill - // offer data. On subsequent calls, does nothing. - void LogStoredOfferMetrics() const; - - // The first time this is called, logs UMA metrics about the users's autofill - // virtual card usage data. On subsequent calls, does nothing. - void LogStoredVirtualCardUsageMetrics() const; + // addresses, credit card, offer and IBAN. On subsequent calls, does nothing. + void LogStoredDataMetrics() const; // Whether the server cards are enabled and should be suggested to the user. virtual bool ShouldSuggestServerCards() const; @@ -978,21 +962,9 @@ // Default value is false. bool is_off_the_record_ = false; - // Whether we have already logged the stored profile metrics this session. - mutable bool has_logged_stored_profile_metrics_ = false; - - // Whether we have already logged the stored credit card metrics this session. - mutable bool has_logged_stored_credit_card_metrics_ = false; - - // Whether we have already logged the stored IBAN metrics this session. - mutable bool has_logged_stored_iban_metrics_ = false; - - // Whether we have already logged the stored offer metrics this session. - mutable bool has_logged_stored_offer_metrics_ = false; - - // Whether we have already logged the stored virtual card usage metrics this - // session. - mutable bool has_logged_stored_virtual_card_usage_metrics_ = false; + // Whether we have already logged the stored profile, credit card, IBAN, offer + // and virtual card usage metrics this session. + mutable bool has_logged_stored_data_metrics_ = false; // An observer to listen for changes to prefs::kAutofillCreditCardEnabled. std::unique_ptr<BooleanPrefMember> credit_card_enabled_pref_;
diff --git a/components/autofill/core/browser/personal_data_manager_cleaner.cc b/components/autofill/core/browser/personal_data_manager_cleaner.cc index 3ad8f99..74616f3 100644 --- a/components/autofill/core/browser/personal_data_manager_cleaner.cc +++ b/components/autofill/core/browser/personal_data_manager_cleaner.cc
@@ -63,12 +63,8 @@ if (!personal_data_manager_->IsSyncEnabledFor(syncer::AUTOFILL_WALLET_DATA)) ApplyCardFixesAndCleanups(); - // Log address, credit card, offer, and usage data startup metrics. - personal_data_manager_->LogStoredProfileMetrics(); - personal_data_manager_->LogStoredCreditCardMetrics(); - personal_data_manager_->LogStoredIbanMetrics(); - personal_data_manager_->LogStoredOfferMetrics(); - personal_data_manager_->LogStoredVirtualCardUsageMetrics(); + // Log address, credit card, offer, IBAN, and usage data startup metrics. + personal_data_manager_->LogStoredDataMetrics(); personal_data_manager_->NotifyPersonalDataObserver(); }
diff --git a/components/breadcrumbs/core/breadcrumb_persistent_storage_manager.cc b/components/breadcrumbs/core/breadcrumb_persistent_storage_manager.cc index 071ad992..5e591c20 100644 --- a/components/breadcrumbs/core/breadcrumb_persistent_storage_manager.cc +++ b/components/breadcrumbs/core/breadcrumb_persistent_storage_manager.cc
@@ -115,7 +115,8 @@ BreadcrumbPersistentStorageManager::BreadcrumbPersistentStorageManager( const base::FilePath& directory, - base::RepeatingCallback<bool()> is_metrics_enabled_callback) + base::RepeatingCallback<bool()> is_metrics_enabled_callback, + base::OnceClosure initialization_done_callback) : // Ensure first event will not be delayed by initializing with a time in // the past. last_written_time_(base::TimeTicks::Now() - kMinDelayBetweenWrites), @@ -128,13 +129,15 @@ task_runner_->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce(&DoGetStoredEvents, breadcrumbs_file_path_), base::BindOnce(&BreadcrumbPersistentStorageManager::Initialize, - weak_ptr_factory_.GetWeakPtr())); + weak_ptr_factory_.GetWeakPtr(), + std::move(initialization_done_callback))); } BreadcrumbPersistentStorageManager::~BreadcrumbPersistentStorageManager() = default; void BreadcrumbPersistentStorageManager::Initialize( + base::OnceClosure initialization_done_callback, const std::string& previous_session_events) { breadcrumbs::BreadcrumbManager::GetInstance().SetPreviousSessionEvents( base::SplitString(previous_session_events, kEventSeparator, @@ -143,7 +146,7 @@ // Write any startup events that have accumulated while waiting for the file // position to be set. - WriteEvents(); + WriteEvents(std::move(initialization_done_callback)); } void BreadcrumbPersistentStorageManager::Write(const std::string& events, @@ -184,7 +187,8 @@ return should_create_files; } -void BreadcrumbPersistentStorageManager::WriteEvents() { +void BreadcrumbPersistentStorageManager::WriteEvents( + base::OnceClosure done_callback) { // No events can be written to the file until the size of existing breadcrumbs // is known. if (!file_position_) { @@ -201,7 +205,8 @@ write_timer_.Start( FROM_HERE, kMinDelayBetweenWrites - time_delta_since_last_write, base::BindOnce(&BreadcrumbPersistentStorageManager::WriteEvents, - weak_ptr_factory_.GetWeakPtr())); + weak_ptr_factory_.GetWeakPtr(), + std::move(done_callback))); return; } @@ -211,13 +216,14 @@ // Use >= here instead of > to allow space for \0 to terminate file. >= kPersistedFilesizeInBytes) { Write(GetEvents(), /*append=*/false); - return; - } - - // Otherwise, simply append the pending breadcrumbs. - if (!pending_breadcrumbs_.empty()) { + } else if (!pending_breadcrumbs_.empty()) { + // Otherwise, simply append the pending breadcrumbs. Write(pending_breadcrumbs_, /*append=*/true); } + + // Add `done_callback` to the task runner's task queue, so it runs after any + // posted `DoWriteEventsToFile()` task has been run. + task_runner_->PostTask(FROM_HERE, std::move(done_callback)); } } // namespace breadcrumbs
diff --git a/components/breadcrumbs/core/breadcrumb_persistent_storage_manager.h b/components/breadcrumbs/core/breadcrumb_persistent_storage_manager.h index 77c892d..71ab605c 100644 --- a/components/breadcrumbs/core/breadcrumb_persistent_storage_manager.h +++ b/components/breadcrumbs/core/breadcrumb_persistent_storage_manager.h
@@ -10,6 +10,7 @@ #include "base/files/file_path.h" #include "base/functional/callback.h" +#include "base/functional/callback_forward.h" #include "base/task/sequenced_task_runner.h" #include "base/time/time.h" #include "base/timer/timer.h" @@ -39,7 +40,8 @@ // prepended to the event log. explicit BreadcrumbPersistentStorageManager( const base::FilePath& directory, - base::RepeatingCallback<bool()> is_metrics_enabled_callback); + base::RepeatingCallback<bool()> is_metrics_enabled_callback, + base::OnceClosure initialization_done_callback = base::DoNothing()); ~BreadcrumbPersistentStorageManager() override; BreadcrumbPersistentStorageManager( const BreadcrumbPersistentStorageManager&) = delete; @@ -50,7 +52,8 @@ // Sets `file_position_` based on the given `previous_session_events`, and // passes them to the BreadcrumbManager. If any events have already been // logged it then writes them to the file. - void Initialize(const std::string& previous_session_events); + void Initialize(base::OnceClosure initialization_done_callback, + const std::string& previous_session_events); // Returns whether metrics consent has been provided and the persistent // storage manager can therefore create its breadcrumbs files. Deletes any @@ -60,7 +63,7 @@ // Writes |pending_breadcrumbs_| to |breadcrumbs_file_| if it fits, otherwise // rewrites the file. NOTE: Writing may be delayed if the file has recently // been written into. - void WriteEvents(); + void WriteEvents(base::OnceClosure done_callback = base::DoNothing()); // Writes the given `events` to `breadcrumbs_file_`. If `append` is false, // overwrites the file.
diff --git a/components/breadcrumbs/core/breadcrumb_persistent_storage_manager_unittest.cc b/components/breadcrumbs/core/breadcrumb_persistent_storage_manager_unittest.cc index a39b9f6..bf1af09 100644 --- a/components/breadcrumbs/core/breadcrumb_persistent_storage_manager_unittest.cc +++ b/components/breadcrumbs/core/breadcrumb_persistent_storage_manager_unittest.cc
@@ -97,12 +97,18 @@ base::SPLIT_WANT_NONEMPTY); } + // Creates the persistent storage manager. Waits for it to read any existing + // events from the breadcrumbs file and rewrite it if it's oversized. void CreateBreadcrumbPersistentStorageManager() { + base::RunLoop run_loop; persistent_storage_ = std::make_unique<BreadcrumbPersistentStorageManager>( scoped_temp_dir_.GetPath(), - /*is_metrics_enabled_callback=*/base::BindRepeating( + /*is_metrics_enabled_callback=*/ + base::BindRepeating( &BreadcrumbPersistentStorageManagerTest::is_metrics_enabled, - base::Unretained(this))); + base::Unretained(this)), + /*initialization_done_callback=*/run_loop.QuitClosure()); + run_loop.Run(); } bool is_metrics_enabled() { return is_metrics_enabled_; } @@ -185,9 +191,8 @@ // Ensures that events are read correctly if the persisted file becomes // corrupted by losing the EOF token or if kPersistedFilesizeInBytes is reduced. -// TODO(crbug.com/1404642): This test is flaky. TEST_F(BreadcrumbPersistentStorageManagerTest, - DISABLED_GetStoredEventsAfterFilesizeReduction) { + GetStoredEventsAfterFilesizeReduction) { const base::FilePath breadcrumbs_file_path = GetBreadcrumbPersistentStorageFilePath(scoped_temp_dir_.GetPath()); base::File file(breadcrumbs_file_path, @@ -207,7 +212,10 @@ /*offset=*/0, base::as_bytes(base::make_span(past_breadcrumbs)))); ASSERT_TRUE(file.Flush()); file.Close(); + ASSERT_EQ(written_events, GetPersistedEvents().size()); + // Create the persistent storage manager. It should resize the breadcrumbs + // file to the appropriate length. CreateBreadcrumbPersistentStorageManager(); const auto events = GetPersistedEvents();
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/gesture/SwipeGestureListener.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/gesture/SwipeGestureListener.java index 842e12a..beb55b3 100644 --- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/gesture/SwipeGestureListener.java +++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/gesture/SwipeGestureListener.java
@@ -46,7 +46,8 @@ * used to detect simple gestures defined in {@link GestureDetector}. */ public class SwipeGestureListener extends SimpleOnGestureListener { - @IntDef({ScrollDirection.LEFT, ScrollDirection.RIGHT, ScrollDirection.UP, ScrollDirection.DOWN}) + @IntDef({ScrollDirection.UNKNOWN, ScrollDirection.LEFT, ScrollDirection.RIGHT, + ScrollDirection.UP, ScrollDirection.DOWN}) @Retention(RetentionPolicy.SOURCE) public @interface ScrollDirection { int UNKNOWN = 0;
diff --git a/components/media_router/browser/media_router_debugger_unittest.cc b/components/media_router/browser/media_router_debugger_unittest.cc index 995dfbe..71ce07557 100644 --- a/components/media_router/browser/media_router_debugger_unittest.cc +++ b/components/media_router/browser/media_router_debugger_unittest.cc
@@ -31,11 +31,6 @@ // Reports should still be disabled since we the feature flag has not been // set. debugger_->EnableRtcpReports(); - EXPECT_FALSE(debugger_->IsRtcpReportsEnabled()); - - // All conditions should be met and the function should return true now. - base::test::ScopedFeatureList feature_list; - feature_list.InitWithFeatures({media::kEnableRtcpReporting}, {}); EXPECT_TRUE(debugger_->IsRtcpReportsEnabled()); }
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc index 292d114..fbcb09ee 100644 --- a/components/omnibox/browser/autocomplete_controller.cc +++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -6,6 +6,7 @@ #include <inttypes.h> +#include <algorithm> #include <cstddef> #include <memory> #include <numeric> @@ -332,6 +333,7 @@ search_provider_(nullptr), zero_suggest_provider_(nullptr), on_device_head_provider_(nullptr), + history_fuzzy_provider_(nullptr), notify_changed_debouncer_( OmniboxFieldTrial:: kAutocompleteStabilityUpdateResultDebounceFromLastRun.Get(), @@ -475,12 +477,34 @@ // distinguished in the ms-scale buckets, is large enough to move the // arithmetic mean. base::TimeTicks start_time = base::TimeTicks::Now(); + + // Keep a max-heap of negative relevances to quickly estimate a relevance + // cutoff that can be used to improve counterfactual triggering. + // Prevent memory churn by starting with full size heap, ready for + // first change to be pushed without reallocation. + std::vector<int> relevances(result_.GetDynamicMaxMatches() + 1, 0); + relevances.pop_back(); + for (const auto& provider : providers_) { - if (!ShouldRunProvider(provider.get())) + if (!ShouldRunProvider(provider.get())) { continue; + } base::TimeTicks provider_start_time = base::TimeTicks::Now(); + if (history_fuzzy_provider_) { + history_fuzzy_provider_->SetCounterfactualRelevanceHint( + -relevances.front()); + } provider->Start(input_, minimal_changes); + + for (const AutocompleteMatch& match : provider->matches()) { + relevances.push_back(-match.relevance); + std::push_heap(relevances.begin(), relevances.end()); + std::pop_heap(relevances.begin(), relevances.end()); + relevances.pop_back(); + DCHECK(std::is_heap(relevances.begin(), relevances.end())); + } + // `UmaHistogramTimes()` uses 1ms - 10s buckets, whereas this uses 1ms - 5s // buckets. // TODO(crbug.com/1340291|manukh): This isn't handled by `metrics_` yet. It @@ -886,7 +910,8 @@ providers_.push_back(voice_suggest_provider_.get()); } if (provider_types & AutocompleteProvider::TYPE_HISTORY_FUZZY) { - providers_.push_back(new HistoryFuzzyProvider(provider_client_.get())); + history_fuzzy_provider_ = new HistoryFuzzyProvider(provider_client_.get()); + providers_.push_back(history_fuzzy_provider_.get()); } if (provider_types & AutocompleteProvider::TYPE_OPEN_TAB) { open_tab_provider_ = new OpenTabProvider(provider_client_.get());
diff --git a/components/omnibox/browser/autocomplete_controller.h b/components/omnibox/browser/autocomplete_controller.h index a349166..07cd1871 100644 --- a/components/omnibox/browser/autocomplete_controller.h +++ b/components/omnibox/browser/autocomplete_controller.h
@@ -34,6 +34,7 @@ class ClipboardProvider; class DocumentProvider; +class HistoryFuzzyProvider; class HistoryURLProvider; class HistoryQuickProvider; class KeywordProvider; @@ -374,6 +375,8 @@ raw_ptr<VoiceSuggestProvider> voice_suggest_provider_; + raw_ptr<HistoryFuzzyProvider> history_fuzzy_provider_; + raw_ptr<OpenTabProvider> open_tab_provider_; // A vector of scoring signals annotators for URL suggestions.
diff --git a/components/omnibox/browser/autocomplete_grouper_sections.cc b/components/omnibox/browser/autocomplete_grouper_sections.cc index 7b5531e..71ef821 100644 --- a/components/omnibox/browser/autocomplete_grouper_sections.cc +++ b/components/omnibox/browser/autocomplete_grouper_sections.cc
@@ -95,16 +95,11 @@ // Sort matches in the order of their potential containing groups. E.g., if // `groups_ = {group 1, group 2}, this sorts all matches that can be added to // group 1 before those that can only be added to group 2. - base::ranges::stable_sort( - matches, - [&](const auto& group_index1, const auto& group_index2) { - return group_index1 - group_index2; - }, - [&](const auto& match) { - // Don't have to handle `FindGroup()` returning `groups_.end()` since - // those matches won't be added to the section anyways. - return std::distance(groups_.begin(), FindGroup(match)); - }); + base::ranges::stable_sort(matches, std::less<int>{}, [&](const auto& match) { + // Don't have to handle `FindGroup()` returning `groups_.end()` since + // those matches won't be added to the section anyways. + return std::distance(groups_.begin(), FindGroup(match)); + }); } AndroidZpsSection::AndroidZpsSection(omnibox::GroupConfigMap& group_configs) @@ -112,7 +107,7 @@ {{1, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX}, {1, omnibox::GROUP_MOBILE_CLIPBOARD}, {1, omnibox::GROUP_MOBILE_MOST_VISITED}, - {15, omnibox::GROUP_PREVIOUS_SEARCH_RELATED}, + {15, omnibox::GROUP_VISITED_DOC_RELATED}, {15, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST}}, group_configs) {}
diff --git a/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc b/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc index c4540d3..72fb140 100644 --- a/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc +++ b/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc
@@ -52,6 +52,39 @@ test({CreateMatch(1, omnibox::GROUP_SEARCH)}, {}); } +TEST(AutocompleteGrouperGroupsTest, SortingUsesProperComparator) { + class TestZpsSection : public ZpsSection { + public: + // Up to 2 items of the following types. + explicit TestZpsSection(omnibox::GroupConfigMap& group_configs) + : ZpsSection(2, + {{1, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX}, + {1, omnibox::GROUP_MOBILE_CLIPBOARD}, + {1, omnibox::GROUP_MOBILE_MOST_VISITED}, + {1, omnibox::GROUP_VISITED_DOC_RELATED}, + {1, omnibox::GROUP_RELATED_QUERIES}}, + group_configs) {} + }; + + auto test = [](ACMatches matches, std::vector<int> expected_relevances) { + PSections sections; + omnibox::GroupConfigMap group_configs; + sections.push_back(std::make_unique<TestZpsSection>(group_configs)); + auto out_matches = Section::GroupMatches(std::move(sections), matches); + VerifyMatches(out_matches, expected_relevances); + }; + + { + SCOPED_TRACE("Confirm appropriate comparator is used to sort suggestions"); + test({CreateMatch(1, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX), + CreateMatch(2, omnibox::GROUP_MOBILE_CLIPBOARD), + CreateMatch(3, omnibox::GROUP_MOBILE_MOST_VISITED), + CreateMatch(4, omnibox::GROUP_VISITED_DOC_RELATED), + CreateMatch(5, omnibox::GROUP_RELATED_QUERIES)}, + {1, 2}); + } +} + // Tests the groups, limits, and rules for the ZPS section. TEST(AutocompleteGrouperSectionsTest, ZpsSection) { auto test = [](ACMatches matches, std::vector<int> expected_relevances) { @@ -443,3 +476,200 @@ {100, 99, 98, 97, 96, 95, 94, 93, 92, 91}); } } + +// Tests the groups, limits, and rules for the Android ZPS section. +TEST(AutocompleteGrouperSectionsTest, AndroidZpsSection) { + auto test = [](ACMatches matches, std::vector<int> expected_relevances) { + PSections sections; + omnibox::GroupConfigMap group_configs; + sections.push_back(std::make_unique<AndroidZpsSection>(group_configs)); + auto out_matches = Section::GroupMatches(std::move(sections), matches); + VerifyMatches(out_matches, expected_relevances); + }; + + { + SCOPED_TRACE("Given no matches, should return no matches."); + test({}, {}); + } + { + SCOPED_TRACE("Android/ZPS with extra searches."); + // Verify that the Clipboard suggestion is retained on top. + test( + { + CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, true), + CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(97, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(96, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(95, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(93, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(92, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(91, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(88, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(87, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + }, + {100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86}); + } + { + SCOPED_TRACE("Android/ZPS with Clipboard entries."); + // Verify that the Clipboard suggestion is retained on top. + test( + { + CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, true), + CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(97, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(96, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(95, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(93, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(92, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(91, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(88, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(87, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(3, omnibox::GROUP_MOBILE_CLIPBOARD), + // Bogus, repetitive, only one allowed. + CreateMatch(2, omnibox::GROUP_MOBILE_CLIPBOARD), + CreateMatch(1, omnibox::GROUP_MOBILE_CLIPBOARD), + }, + {3, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87}); + } + { + SCOPED_TRACE("Android/ZPS with Search Ready Omnibox."); + // Verify that the Clipboard suggestion is retained on top. + test( + { + CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, true), + CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(97, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(96, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(95, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(93, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(92, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(91, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(88, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(87, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX), + // Bogus, repetitive, only one allowed. + CreateMatch(1, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX), + CreateMatch(0, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX), + }, + {2, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87}); + } + { + SCOPED_TRACE("Android/ZPS on Web with recent searches only."); + // Verify that the Clipboard suggestion is retained on top. + test( + { + CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, true), + CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(97, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(96, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(95, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(93, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(92, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(91, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(88, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(87, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX), + }, + {2, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87}); + } + { + SCOPED_TRACE("Android/ZPS with MV Tiles."); + // Verify that the Clipboard suggestion is retained on top. + test( + { + CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, true), + CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(97, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(96, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(95, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(93, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(92, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(91, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(88, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(87, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(4, omnibox::GROUP_MOBILE_MOST_VISITED), + // Bogus, repetitive, currently only one allowed. + // This will be permitted when group rendering shifts to horizontal. + CreateMatch(3, omnibox::GROUP_MOBILE_MOST_VISITED), + CreateMatch(2, omnibox::GROUP_MOBILE_MOST_VISITED), + }, + {4, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87}); + } + { + SCOPED_TRACE("Android/ZPS with multiple auxiliary suggestions."); + // Verify that the Clipboard suggestion is retained on top. + test( + { + CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, true), + CreateMatch(99, omnibox::GROUP_VISITED_DOC_RELATED), + CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(97, omnibox::GROUP_VISITED_DOC_RELATED), + CreateMatch(96, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(95, omnibox::GROUP_VISITED_DOC_RELATED), + CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(93, omnibox::GROUP_VISITED_DOC_RELATED), + CreateMatch(92, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(91, omnibox::GROUP_VISITED_DOC_RELATED), + CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(89, omnibox::GROUP_VISITED_DOC_RELATED), + CreateMatch(88, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(87, omnibox::GROUP_VISITED_DOC_RELATED), + CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + CreateMatch(85, omnibox::GROUP_VISITED_DOC_RELATED), + CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST), + // SRO should always be shown first, despite low relevance. + // Only one item permitted. + CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX), + CreateMatch(1, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX), + // Clipboard should always be shown after SRO, if both are present. + // Only one item permitted. + CreateMatch(20, omnibox::GROUP_MOBILE_CLIPBOARD), + CreateMatch(19, omnibox::GROUP_MOBILE_CLIPBOARD), + // MV Tiles should always be on the third position if both SRO and + // Clipboard are present. + // Currently only one item is permitted. + CreateMatch(40, omnibox::GROUP_MOBILE_MOST_VISITED), + CreateMatch(39, omnibox::GROUP_MOBILE_MOST_VISITED), + }, + // Observe that PERSONALIZED_ZERO_SUGGEST and VISITED_DOC suggestions are + // grouped together. + // VISITED_DOC_RELATED are prioritized over the PERSONALIZED_ZERO_SUGGEST + // because these are more context relevant. + {2, 20, 40, 99, 97, 95, 93, 91, 89, 87, 85, 100, 98, 96, 94}); + } +}
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc index a54a3af..e668673c 100644 --- a/components/omnibox/browser/autocomplete_result.cc +++ b/components/omnibox/browser/autocomplete_result.cc
@@ -53,19 +53,9 @@ namespace { -constexpr bool is_android = -#if BUILDFLAG(IS_ANDROID) - true; -#else - false; -#endif - -constexpr bool is_ios = -#if BUILDFLAG(IS_IOS) - true; -#else - false; -#endif +constexpr bool is_android = !!BUILDFLAG(IS_ANDROID); +constexpr bool is_ios = !!BUILDFLAG(IS_IOS); +constexpr bool is_desktop = !(is_android || is_ios); // Rotates |it| to be in the front of |matches|. // |it| must be a valid iterator of |matches| or equal to |matches->end()|. @@ -348,9 +338,13 @@ // If `kGroupingFramework` is enabled and the current input & platform are // supported, delegate to the framework. + // + // - Include both Desktop ZPS and prefixed suggestions. + // - Include Android ZPS only (no prefixed suggestions), + // - IOS is currently not included. const bool is_zero_suggest = input.IsZeroSuggest(); if (base::FeatureList::IsEnabled(omnibox::kGroupingFramework) && - !is_android && !is_ios) { + (is_desktop || (is_android && is_zero_suggest))) { // Grouping requires all matches have a group ID. To keep providers 'dumb', // they only assign IDs when their ID isn't obvious from the match type. // Most matches will instead set IDs here to keep providers 'dumb' and the @@ -370,14 +364,20 @@ PSections sections; if (is_zero_suggest) { +#if BUILDFLAG(IS_ANDROID) + sections.push_back( + std::make_unique<AndroidZpsSection>(suggestion_groups_map_)); +#else // !BUILDFLAG(IS_ANDROID) sections.push_back( std::make_unique<DesktopZpsSection>(suggestion_groups_map_)); + if (page_classification == OmniboxEventProto::NTP_REALBOX && base::FeatureList::IsEnabled(omnibox::kKeepSecondaryZeroSuggest)) { // Allow secondary zero-prefix suggestions in the NTP realbox, if any. sections.push_back(std::make_unique<DesktopSecondaryZpsSection>( suggestion_groups_map_)); } +#endif // !BUILDFLAG(IS_ANDROID) } else { sections.push_back( std::make_unique<DesktopNonZpsSection>(suggestion_groups_map_));
diff --git a/components/omnibox/browser/history_fuzzy_provider.cc b/components/omnibox/browser/history_fuzzy_provider.cc index fd18321..0992b99 100644 --- a/components/omnibox/browser/history_fuzzy_provider.cc +++ b/components/omnibox/browser/history_fuzzy_provider.cc
@@ -478,6 +478,7 @@ penalty_high_ = OmniboxFieldTrial::kFuzzyUrlSuggestionsPenaltyHigh.Get(); penalty_taper_length_ = OmniboxFieldTrial::kFuzzyUrlSuggestionsPenaltyTaperLength.Get(); + counterfactual_ = OmniboxFieldTrial::kFuzzyUrlSuggestionsCounterfactual.Get(); if (ShouldBypassForLowEndDevice()) { // Note, this early return will prevent loading from database, which saves @@ -500,6 +501,10 @@ } } +void HistoryFuzzyProvider::SetCounterfactualRelevanceHint(int relevance_hint) { + counterfactual_relevance_hint_ = relevance_hint; +} + void HistoryFuzzyProvider::Start(const AutocompleteInput& input, bool minimal_changes) { TRACE_EVENT0("omnibox", "HistoryFuzzyProvider::Start"); @@ -530,16 +535,25 @@ } if (!matches_.empty()) { - // This will likely produce some false positives. - client()->GetOmniboxTriggeredFeatureService()->FeatureTriggered( - OmniboxTriggeredFeatureService::Feature::kFuzzyUrlSuggestions); + // This will likely produce some false positives, but the likelihood + // is reduced by only triggering when one of the matches exceeds + // the relevance hint, an estimated cutoff value at which we expect fuzzy + // matches could persist after sorting and culling the full match set. + const bool met_threshold = std::any_of( + matches_.begin(), matches_.end(), [=](const AutocompleteMatch& match) { + return match.relevance > counterfactual_relevance_hint_; + }); + if (met_threshold) { + client()->GetOmniboxTriggeredFeatureService()->FeatureTriggered( + OmniboxTriggeredFeatureService::Feature::kFuzzyUrlSuggestions); + } // When in the counterfactual group, we do all the work of finding fuzzy // matches, but do not provide the benefit. To reduce risk of unintended // consequences downstream (for example showing fewer suggestions than // normal), the matches are cleared here instead of at end of result // processing pipeline so they won't interact or dedupe with other matches. - if (OmniboxFieldTrial::kFuzzyUrlSuggestionsCounterfactual.Get()) { + if (counterfactual_) { matches_.clear(); } }
diff --git a/components/omnibox/browser/history_fuzzy_provider.h b/components/omnibox/browser/history_fuzzy_provider.h index d4f1909d..b10793b 100644 --- a/components/omnibox/browser/history_fuzzy_provider.h +++ b/components/omnibox/browser/history_fuzzy_provider.h
@@ -194,6 +194,10 @@ HistoryFuzzyProvider(const HistoryFuzzyProvider&) = delete; HistoryFuzzyProvider& operator=(const HistoryFuzzyProvider&) = delete; + // Set a relevance hint to improve counterfactual in late-running providers + // where most match relevances are already determined. + void SetCounterfactualRelevanceHint(int relevance_hint); + // HistoryProvider: // AutocompleteProvider. `minimal_changes` is ignored since there is no async // completion performed. @@ -260,6 +264,13 @@ int penalty_high_; size_t penalty_taper_length_; + // Cache counterfactual feature param. + bool counterfactual_; + + // A relevance value below which counterfactual matches are less likely + // to be kept, if they were to be included in the full output match set. + int counterfactual_relevance_hint_{0}; + // Weak pointer factory for callback binding safety. base::WeakPtrFactory<HistoryFuzzyProvider> weak_ptr_factory_{this}; };
diff --git a/components/omnibox/browser/zero_suggest_verbatim_match_provider.cc b/components/omnibox/browser/zero_suggest_verbatim_match_provider.cc index 8fe674a7..bebddc7 100644 --- a/components/omnibox/browser/zero_suggest_verbatim_match_provider.cc +++ b/components/omnibox/browser/zero_suggest_verbatim_match_provider.cc
@@ -92,5 +92,5 @@ return; match.provider = this; - matches_.push_back(match); + matches_.push_back(std::move(match)); }
diff --git a/components/page_info/page_info.cc b/components/page_info/page_info.cc index 517e82a4..fd169957 100644 --- a/components/page_info/page_info.cc +++ b/components/page_info/page_info.cc
@@ -48,6 +48,7 @@ #endif #include "build/chromeos_buildflags.h" #include "components/page_info/core/features.h" +#include "components/permissions/permission_recovery_success_rate_tracker.h" #include "components/permissions/permission_uma_util.h" #include "components/permissions/permission_util.h" #include "components/privacy_sandbox/privacy_sandbox_features.h" @@ -728,14 +729,21 @@ type, setting_old, setting, is_subscribed_to_permission_change_event); } - // Show the infobar only if permission's status is not handled by an - // origin. + // Show the infobar only if permission's status is not handled by an origin. // When the sound setting is changed, no reload is necessary. if (!is_subscribed_to_permission_change_event && type != ContentSettingsType::SOUND) { show_info_bar_ = true; } + if (permissions::PermissionUtil::IsPermission(type)) { + auto* permission_tracker = + permissions::PermissionRecoverySuccessRateTracker::FromWebContents( + web_contents_.get()); + + permission_tracker->PermissionStatusChanged(type, setting, show_info_bar_); + } + // Refresh the UI to reflect the new setting. PresentSitePermissions(); }
diff --git a/components/permissions/BUILD.gn b/components/permissions/BUILD.gn index 26e2d78..7876cd43 100644 --- a/components/permissions/BUILD.gn +++ b/components/permissions/BUILD.gn
@@ -78,6 +78,8 @@ "permission_manager.cc", "permission_manager.h", "permission_prompt.h", + "permission_recovery_success_rate_tracker.cc", + "permission_recovery_success_rate_tracker.h", "permission_request.cc", "permission_request.h", "permission_request_id.cc",
diff --git a/components/permissions/permission_recovery_success_rate_tracker.cc b/components/permissions/permission_recovery_success_rate_tracker.cc new file mode 100644 index 0000000..e0dae2a --- /dev/null +++ b/components/permissions/permission_recovery_success_rate_tracker.cc
@@ -0,0 +1,78 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/permissions/permission_recovery_success_rate_tracker.h" + +#include "components/content_settings/core/common/content_settings_types.h" +#include "components/permissions/permission_uma_util.h" +#include "content/public/browser/web_contents.h" + +namespace permissions { + +PermissionRecoverySuccessRateTracker::PermissionRecoverySuccessRateTracker( + content::WebContents* web_contents) + : content::WebContentsObserver(web_contents), + content::WebContentsUserData<PermissionRecoverySuccessRateTracker>( + *web_contents) { + DCHECK(web_contents); +} + +PermissionRecoverySuccessRateTracker::~PermissionRecoverySuccessRateTracker() { + DCHECK(reallowed_permissions_.empty()); +} + +void PermissionRecoverySuccessRateTracker::PermissionStatusChanged( + ContentSettingsType permission, + ContentSetting setting, + bool show_infobar) { + // If permission is not allowed, it is not actionable for origins. + if (setting != ContentSetting::CONTENT_SETTING_ALLOW) { + return; + } + + reallowed_permissions_[permission] = show_infobar; +} + +void PermissionRecoverySuccessRateTracker::ClearTrackingMap() { + for (const auto& [permission, show_infobar] : reallowed_permissions_) { + Track(permission, /*is_used=*/false, show_infobar); + } + + reallowed_permissions_.clear(); +} + +void PermissionRecoverySuccessRateTracker::TrackUsage( + ContentSettingsType permission) { + if (reallowed_permissions_.find(permission) != reallowed_permissions_.end()) { + Track(permission, /*is_used=*/true, reallowed_permissions_[permission]); + reallowed_permissions_.erase(permission); + } +} + +void PermissionRecoverySuccessRateTracker::Track(ContentSettingsType permission, + bool is_used, + bool show_infobar) { + PermissionUmaUtil::RecordPermissionRecoverySuccessRate( + permission, is_used, show_infobar, page_reload_); +} + +void PermissionRecoverySuccessRateTracker::WebContentsDestroyed() { + ClearTrackingMap(); +} + +void PermissionRecoverySuccessRateTracker::PrimaryPageChanged( + content::Page& page) { + if (origin_ != page.GetMainDocument().GetLastCommittedOrigin()) { + origin_ = page.GetMainDocument().GetLastCommittedOrigin(); + // Clear tracking map only for cross-origin navigation. + ClearTrackingMap(); + page_reload_ = false; + } else { + page_reload_ = true; + } +} + +WEB_CONTENTS_USER_DATA_KEY_IMPL(PermissionRecoverySuccessRateTracker); + +} // namespace permissions
diff --git a/components/permissions/permission_recovery_success_rate_tracker.h b/components/permissions/permission_recovery_success_rate_tracker.h new file mode 100644 index 0000000..d54a4c9 --- /dev/null +++ b/components/permissions/permission_recovery_success_rate_tracker.h
@@ -0,0 +1,73 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_PERMISSIONS_PERMISSION_RECOVERY_SUCCESS_RATE_TRACKER_H_ +#define COMPONENTS_PERMISSIONS_PERMISSION_RECOVERY_SUCCESS_RATE_TRACKER_H_ + +#include "components/content_settings/core/common/content_settings.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" + +enum class ContentSettingsType; + +namespace permissions { + +// This class track permissions that were reallowed via PageInfo. In addition, +// it records if the "Reload this page" infobar was shown or if a page was +// reloaded. The reload may be initiated via the infobar or any other UI +// elements. The recording happens when a main frame is going to be destroyed. +// That may happen while a tab is being closed or while cross-origin navigation. +// +// The recording happens instantly if permission is used by an origin as the +// goal is to verify permission usage after it was reallowed. +class PermissionRecoverySuccessRateTracker + : public content::WebContentsObserver, + public content::WebContentsUserData< + PermissionRecoverySuccessRateTracker> { + public: + explicit PermissionRecoverySuccessRateTracker( + content::WebContents* web_contents); + + PermissionRecoverySuccessRateTracker( + const PermissionRecoverySuccessRateTracker&) = delete; + PermissionRecoverySuccessRateTracker& operator=( + const PermissionRecoverySuccessRateTracker&) = delete; + + ~PermissionRecoverySuccessRateTracker() override; + + // Adds `permission` into the `reallowed_permissions_` map. `permission` will + // be recorded before a main frame is destroyed or after `permission` is used + // by an origin. + void PermissionStatusChanged(ContentSettingsType permission, + ContentSetting setting, + bool show_infobar); + + // `permission` has been used by an origin, record usage and remove from + // `reallowed_permissions_`. + void TrackUsage(ContentSettingsType permission); + + private: + friend class content::WebContentsUserData< + PermissionRecoverySuccessRateTracker>; + + void ClearTrackingMap(); + + void Track(ContentSettingsType permission, bool is_used, bool show_infobar); + + // content::WebContentsObserver: + void WebContentsDestroyed() override; + void PrimaryPageChanged(content::Page& page) override; + + absl::optional<url::Origin> origin_; + + bool page_reload_ = false; + + std::map<ContentSettingsType, bool> reallowed_permissions_; + + WEB_CONTENTS_USER_DATA_KEY_DECL(); +}; + +} // namespace permissions + +#endif // COMPONENTS_PERMISSIONS_PERMISSION_RECOVERY_SUCCESS_RATE_TRACKER_H_
diff --git a/components/permissions/permission_uma_util.cc b/components/permissions/permission_uma_util.cc index 196da56..fe7021ca 100644 --- a/components/permissions/permission_uma_util.cc +++ b/components/permissions/permission_uma_util.cc
@@ -498,6 +498,44 @@ PermissionHeaderPolicyForUMA::NUM); } +PermissionChangeInfo GetChangeInfo(bool is_used, + bool show_infobar, + bool page_reload) { + if (show_infobar) { + if (page_reload) { + if (is_used) { + return PermissionChangeInfo::kInfobarShownPageReloadPermissionUsed; + } else { + return PermissionChangeInfo::kInfobarShownPageReloadPermissionNotUsed; + } + + } else { + if (is_used) { + return PermissionChangeInfo::kInfobarShownNoPageReloadPermissionUsed; + } else { + return PermissionChangeInfo::kInfobarShownNoPageReloadPermissionNotUsed; + } + } + } else { + if (page_reload) { + if (is_used) { + return PermissionChangeInfo::kInfobarNotShownPageReloadPermissionUsed; + } else { + return PermissionChangeInfo:: + kInfobarNotShownPageReloadPermissionNotUsed; + } + + } else { + if (is_used) { + return PermissionChangeInfo::kInfobarNotShownNoPageReloadPermissionUsed; + } else { + return PermissionChangeInfo:: + kInfobarNotShownNoPageReloadPermissionNotUsed; + } + } + } +} + } // anonymous namespace // PermissionUmaUtil ---------------------------------------------------------- @@ -630,6 +668,22 @@ embargo_status, PermissionEmbargoStatus::NUM); } +void PermissionUmaUtil::RecordPermissionRecoverySuccessRate( + ContentSettingsType permission, + bool is_used, + bool show_infobar, + bool page_reload) { + PermissionChangeInfo change_info = + GetChangeInfo(is_used, show_infobar, page_reload); + + std::string permission_string = GetPermissionRequestString( + GetUmaValueForRequestType(ContentSettingsTypeToRequestType(permission))); + + base::UmaHistogramEnumeration("Permissions.PageInfo.Changed." + + permission_string + ".Reallowed.Outcome", + change_info); +} + void PermissionUmaUtil::RecordPermissionPromptAttempt( const std::vector<PermissionRequest*>& requests, bool IsLocationBarEditingOrEmpty) {
diff --git a/components/permissions/permission_uma_util.h b/components/permissions/permission_uma_util.h index c227842..0ed52cb 100644 --- a/components/permissions/permission_uma_util.h +++ b/components/permissions/permission_uma_util.h
@@ -373,6 +373,35 @@ NUM }; +// This enum backs up the +// 'Permissions.PageInfo.Changed.{PermissionType}.Reallowed.Outcome' histograms +// enum. It is used for collecting permission usage rates after permission +// status was reallowed via PageInfo. It is applicable only if permission is +// allowed as all other states are no-op for an origin. +// +// These values are persisted to logs. Entries should not be renumbered and +// numeric values should never be reused. +enum class PermissionChangeInfo { + kInfobarShownPageReloadPermissionUsed = 0, + + kInfobarShownPageReloadPermissionNotUsed = 1, + + kInfobarShownNoPageReloadPermissionUsed = 2, + + kInfobarShownNoPageReloadPermissionNotUsed = 3, + + kInfobarNotShownPageReloadPermissionUsed = 4, + + kInfobarNotShownPageReloadPermissionNotUsed = 5, + + kInfobarNotShownNoPageReloadPermissionUsed = 6, + + kInfobarNotShownNoPageReloadPermissionNotUsed = 7, + + // Always keep at the end. + kMaxValue = kInfobarNotShownNoPageReloadPermissionNotUsed +}; + // Provides a convenient way of logging UMA for permission related operations. class PermissionUmaUtil { public: @@ -423,6 +452,12 @@ static void RecordEmbargoStatus(PermissionEmbargoStatus embargo_status); + static void RecordPermissionRecoverySuccessRate( + ContentSettingsType permission, + bool is_used, + bool show_infobar, + bool page_reload); + // Recorded when a permission prompt creation is in progress. static void RecordPermissionPromptAttempt( const std::vector<PermissionRequest*>& requests,
diff --git a/components/permissions/permission_uma_util_unittest.cc b/components/permissions/permission_uma_util_unittest.cc index 2bf67b4..bfd4063 100644 --- a/components/permissions/permission_uma_util_unittest.cc +++ b/components/permissions/permission_uma_util_unittest.cc
@@ -347,6 +347,81 @@ 0); } +TEST_F(PermissionUmaUtilTest, PageInfoPermissionReallowedTest) { + base::HistogramTester histograms; + + PermissionUmaUtil::RecordPermissionRecoverySuccessRate( + ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/true, + /*show_infobar=*/true, /*page_reload=*/true); + histograms.ExpectBucketCount( + "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", + permissions::PermissionChangeInfo::kInfobarShownPageReloadPermissionUsed, + 1); + + PermissionUmaUtil::RecordPermissionRecoverySuccessRate( + ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/false, + /*show_infobar=*/true, /*page_reload=*/true); + histograms.ExpectBucketCount( + "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", + permissions::PermissionChangeInfo:: + kInfobarShownPageReloadPermissionNotUsed, + 1); + + PermissionUmaUtil::RecordPermissionRecoverySuccessRate( + ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/true, + /*show_infobar=*/true, /*page_reload=*/false); + histograms.ExpectBucketCount( + "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", + permissions::PermissionChangeInfo:: + kInfobarShownNoPageReloadPermissionUsed, + 1); + + PermissionUmaUtil::RecordPermissionRecoverySuccessRate( + ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/false, + /*show_infobar=*/true, /*page_reload=*/false); + histograms.ExpectBucketCount( + "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", + permissions::PermissionChangeInfo:: + kInfobarShownNoPageReloadPermissionNotUsed, + 1); + + PermissionUmaUtil::RecordPermissionRecoverySuccessRate( + ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/true, + /*show_infobar=*/false, /*page_reload=*/true); + histograms.ExpectBucketCount( + "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", + permissions::PermissionChangeInfo:: + kInfobarNotShownPageReloadPermissionUsed, + 1); + + PermissionUmaUtil::RecordPermissionRecoverySuccessRate( + ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/false, + /*show_infobar=*/false, /*page_reload=*/true); + histograms.ExpectBucketCount( + "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", + permissions::PermissionChangeInfo:: + kInfobarNotShownPageReloadPermissionNotUsed, + 1); + + PermissionUmaUtil::RecordPermissionRecoverySuccessRate( + ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/true, + /*show_infobar=*/false, /*page_reload=*/false); + histograms.ExpectBucketCount( + "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", + permissions::PermissionChangeInfo:: + kInfobarNotShownNoPageReloadPermissionUsed, + 1); + + PermissionUmaUtil::RecordPermissionRecoverySuccessRate( + ContentSettingsType::MEDIASTREAM_CAMERA, /*is_used=*/false, + /*show_infobar=*/false, /*page_reload=*/false); + histograms.ExpectBucketCount( + "Permissions.PageInfo.Changed.VideoCapture.Reallowed.Outcome", + permissions::PermissionChangeInfo:: + kInfobarNotShownNoPageReloadPermissionNotUsed, + 1); +} + TEST_F(PermissionsDelegationUmaUtilTest, SameOriginFrame) { base::HistogramTester histograms; auto* main_frame = GetMainFrameAndNavigate(kTopLevelUrl);
diff --git a/components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.cc b/components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.cc index b0d252f..ad234e0 100644 --- a/components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.cc +++ b/components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.cc
@@ -37,17 +37,31 @@ namespace { +// The expiration period of a user gesture. Any user gesture that happened 1.0 +// second ago is considered as expired and not relevant to upcoming navigation +// events. +static constexpr base::TimeDelta kUserGestureTTL = base::Seconds(1); +// The expiration period of navigation events and resolved IP addresses. Any +// navigation related records that happened 2 minutes ago are considered as +// expired. So we clean up these navigation footprints every 2 minutes. +static constexpr base::TimeDelta kNavigationFootprintTTL = base::Minutes(2); +// The maximum number of latest NavigationEvent we keep. It is used to limit +// memory usage of navigation tracking. This number is picked based on UMA +// metric "SafeBrowsing.NavigationObserver.NavigationEventCleanUpCount". +// Lowering it could make room for abuse. +static const int kNavigationRecordMaxSize = 100; +// The maximum number of ReferrerChainEntry. It is used to limit the size of +// reports (e.g. ClientDownloadRequest) we send to SB server. +static const int kReferrerChainMaxLength = 10; + constexpr size_t kMaxNumberOfNavigationsToAppend = 5; // Given when an event happened and its TTL, determine if it is already expired. // Note, if for some reason this event's timestamp is in the future, this // event's timestamp is invalid, hence we treat it as expired. -bool IsEventExpired(const base::Time& event_time, double ttl_in_second) { - double current_time_in_second = base::Time::Now().ToDoubleT(); - double event_time_in_second = event_time.ToDoubleT(); - if (current_time_in_second <= event_time_in_second) - return true; - return current_time_in_second - event_time_in_second > ttl_in_second; +bool IsEventExpired(const base::Time& event_time, base::TimeDelta ttl) { + base::Time now = base::Time::Now(); + return event_time > now || now - event_time > ttl; } // Helper function to determine if the URL type should be LANDING_REFERRER or @@ -88,24 +102,23 @@ } } -} // namespace +base::TimeDelta GetNavigationFootprintTTL() { + if (base::FeatureList::IsEnabled(kReferrerChainParameters)) { + return base::Seconds(kReferrerChainEventMaximumAgeSeconds.Get()); + } -// The expiration period of a user gesture. Any user gesture that happened 1.0 -// second ago is considered as expired and not relevant to upcoming navigation -// events. -static const double kUserGestureTTLInSecond = 1.0; -// The expiration period of navigation events and resolved IP addresses. Any -// navigation related records that happened 2 minutes ago are considered as -// expired. So we clean up these navigation footprints every 2 minutes. -static const double kNavigationFootprintTTLInSecond = 120.0; -// The maximum number of latest NavigationEvent we keep. It is used to limit -// memory usage of navigation tracking. This number is picked based on UMA -// metric "SafeBrowsing.NavigationObserver.NavigationEventCleanUpCount". -// Lowering it could make room for abuse. -static const int kNavigationRecordMaxSize = 100; -// The maximum number of ReferrerChainEntry. It is used to limit the size of -// reports (e.g. ClientDownloadRequest) we send to SB server. -static const int kReferrerChainMaxLength = 10; + return kNavigationFootprintTTL; +} + +int GetNavigationRecordMaxSize() { + if (base::FeatureList::IsEnabled(kReferrerChainParameters)) { + return kReferrerChainEventMaximumCount.Get(); + } + + return kNavigationRecordMaxSize; +} + +} // namespace // -------------------------ReferrerChainData----------------------- @@ -303,11 +316,11 @@ std::size_t NavigationEventList::CleanUpNavigationEvents() { // Remove any stale NavigationEnvent, if it is older than - // kNavigationFootprintTTLInSecond. + // `GetNavigationFootprintTTL()`. std::size_t removal_count = 0; while (!navigation_events_.empty() && IsEventExpired(navigation_events_[0]->last_updated, - kNavigationFootprintTTLInSecond)) { + GetNavigationFootprintTTL())) { navigation_events_.pop_front(); removal_count++; } @@ -315,8 +328,7 @@ // Clean up expired pending navigation events. auto it = pending_navigation_events_.begin(); while (it != pending_navigation_events_.end()) { - if (IsEventExpired(it->second->last_updated, - kNavigationFootprintTTLInSecond)) { + if (IsEventExpired(it->second->last_updated, GetNavigationFootprintTTL())) { it = pending_navigation_events_.erase(it); } else { ++it; @@ -330,7 +342,7 @@ // static bool SafeBrowsingNavigationObserverManager::IsUserGestureExpired( const base::Time& timestamp) { - return IsEventExpired(timestamp, kUserGestureTTLInSecond); + return IsEventExpired(timestamp, kUserGestureTTL); } // static @@ -387,11 +399,10 @@ SafeBrowsingNavigationObserverManager::SafeBrowsingNavigationObserverManager( PrefService* pref_service) - : navigation_event_list_(kNavigationRecordMaxSize), + : navigation_event_list_(GetNavigationRecordMaxSize()), pref_service_(pref_service) { // Schedule clean up in 2 minutes. - ScheduleNextCleanUpAfterInterval( - base::Seconds(kNavigationFootprintTTLInSecond)); + ScheduleNextCleanUpAfterInterval(GetNavigationFootprintTTL()); } void SafeBrowsingNavigationObserverManager::RecordNavigationEvent( @@ -479,8 +490,7 @@ CleanUpNavigationEvents(); CleanUpUserGestures(); CleanUpIpAddresses(); - ScheduleNextCleanUpAfterInterval( - base::Seconds(kNavigationFootprintTTLInSecond)); + ScheduleNextCleanUpAfterInterval(GetNavigationFootprintTTL()); } SafeBrowsingNavigationObserverManager::AttributionResult @@ -504,8 +514,8 @@ auto* nav_event = navigation_event_list_.GetNavigationEvent(*nav_event_index); AttributionResult result = SUCCESS; - AddToReferrerChain(out_referrer_chain, nav_event, GURL(), - ReferrerChainEntry::EVENT_URL); + MaybeAddToReferrerChain(out_referrer_chain, nav_event, GURL(), + ReferrerChainEntry::EVENT_URL); int user_gesture_count = 0; GetRemainingReferrerChain(*nav_event_index, user_gesture_count, user_gesture_count_limit, out_referrer_chain, @@ -538,8 +548,8 @@ } AttributionResult result = SUCCESS; - AddToReferrerChain(out_referrer_chain, pending_nav_event, GURL(), - ReferrerChainEntry::EVENT_URL); + MaybeAddToReferrerChain(out_referrer_chain, pending_nav_event, GURL(), + ReferrerChainEntry::EVENT_URL); int user_gesture_count = 0; GetRemainingReferrerChainForNavEvent( pending_nav_event, navigation_event_list_.NavigationEventsSize(), @@ -613,12 +623,13 @@ // page of this event. if (has_user_gesture) { user_gesture_count = 1; - AddToReferrerChain( + MaybeAddToReferrerChain( out_referrer_chain, nav_event, initiating_main_frame_url, GetURLTypeAndAdjustAttributionResult(user_gesture_count, &result)); } else { - AddToReferrerChain(out_referrer_chain, nav_event, initiating_main_frame_url, - ReferrerChainEntry::CLIENT_REDIRECT); + MaybeAddToReferrerChain(out_referrer_chain, nav_event, + initiating_main_frame_url, + ReferrerChainEntry::CLIENT_REDIRECT); } GetRemainingReferrerChain(*nav_event_index, user_gesture_count, @@ -742,8 +753,8 @@ while (it != navigation_event_list_.navigation_events().rend()) { // Skip navigations that happened after |last_navigation_time_msec|. if (it->get()->last_updated.ToJavaTime() < last_navigation_time_msec) { - AddToReferrerChain(&navigation_chain, it->get(), GURL(), - ReferrerChainEntry::RECENT_NAVIGATION); + MaybeAddToReferrerChain(&navigation_chain, it->get(), GURL(), + ReferrerChainEntry::RECENT_NAVIGATION); allowed_entries--; if (it->get()->IsUserInitiated()) { user_gesture_cnt++; @@ -781,18 +792,18 @@ void SafeBrowsingNavigationObserverManager::CleanUpUserGestures() { for (auto it = user_gesture_map_.begin(); it != user_gesture_map_.end();) { - if (IsEventExpired(it->second, kNavigationFootprintTTLInSecond)) + if (IsEventExpired(it->second, GetNavigationFootprintTTL())) { it = user_gesture_map_.erase(it); - else + } else { ++it; + } } } void SafeBrowsingNavigationObserverManager::CleanUpIpAddresses() { for (auto it = host_to_ip_map_.begin(); it != host_to_ip_map_.end();) { base::EraseIf(it->second, [](const ResolvedIPAddress& resolved_ip) { - return IsEventExpired(resolved_ip.timestamp, - kNavigationFootprintTTLInSecond); + return IsEventExpired(resolved_ip.timestamp, GetNavigationFootprintTTL()); }); if (it->second.empty()) it = host_to_ip_map_.erase(it); @@ -814,11 +825,19 @@ &SafeBrowsingNavigationObserverManager::CleanUpStaleNavigationFootprints); } -void SafeBrowsingNavigationObserverManager::AddToReferrerChain( +void SafeBrowsingNavigationObserverManager::MaybeAddToReferrerChain( ReferrerChain* referrer_chain, NavigationEvent* nav_event, const GURL& destination_main_frame_url, ReferrerChainEntry::URLType type) { + // For privacy reasons, we don't actually add the referrer chain events if + // they are too old, no matter what `kReferrerChainEventMaximumAgeSeconds` is + // set to. By bailing out early here, we still trace the referrer chain and + // see if the older events improve the quality of the referrer chain. + if (IsEventExpired(nav_event->last_updated, kNavigationFootprintTTL)) { + return; + } + std::unique_ptr<ReferrerChainEntry> referrer_chain_entry = std::make_unique<ReferrerChainEntry>(); referrer_chain_entry->set_navigation_initiation( @@ -912,9 +931,9 @@ last_nav_event_traced_index = *nav_event_index; last_nav_event_traced = navigation_event_list_.GetNavigationEvent( last_nav_event_traced_index); - AddToReferrerChain(out_referrer_chain, last_nav_event_traced, - last_main_frame_url_traced, - ReferrerChainEntry::CLIENT_REDIRECT); + MaybeAddToReferrerChain(out_referrer_chain, last_nav_event_traced, + last_main_frame_url_traced, + ReferrerChainEntry::CLIENT_REDIRECT); // Stop searching if the size of out_referrer_chain already reached its // limit. if (!omit_non_user_gestures_is_enabled && @@ -938,10 +957,10 @@ last_nav_event_traced_index = *nav_event_index; last_nav_event_traced = navigation_event_list_.GetNavigationEvent(last_nav_event_traced_index); - AddToReferrerChain(out_referrer_chain, last_nav_event_traced, - last_main_frame_url_traced, - GetURLTypeAndAdjustAttributionResult( - current_user_gesture_count, out_result)); + MaybeAddToReferrerChain(out_referrer_chain, last_nav_event_traced, + last_main_frame_url_traced, + GetURLTypeAndAdjustAttributionResult( + current_user_gesture_count, out_result)); // Stop searching if the size of out_referrer_chain already reached its // limit. if (!omit_non_user_gestures_is_enabled &&
diff --git a/components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.h b/components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.h index 674afbce..d967dd0 100644 --- a/components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.h +++ b/components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.h
@@ -64,7 +64,7 @@ }; // Struct that manages insertion, cleanup, and lookup of NavigationEvent -// objects. Its maximum size is kNavigationRecordMaxSize. +// objects. Its maximum size is `GetNavigationRecordMaxSize()`. struct NavigationEventList { public: explicit NavigationEventList(std::size_t size_limit); @@ -175,7 +175,7 @@ public KeyedService { public: // Helper function to check if user gesture is older than - // kUserGestureTTLInSecond. + // kUserGestureTTL. static bool IsUserGestureExpired(const base::Time& timestamp); // Helper function to strip ref fragment from a URL. Many pages end up with a @@ -223,7 +223,7 @@ void OnWebContentDestroyed(content::WebContents* web_contents); // Removes all the observed NavigationEvents, user gestures, and resolved IP - // addresses that are older than kNavigationFootprintTTLInSecond. + // addresses that are older than `GetNavigationFootprintTTL()`. void CleanUpStaleNavigationFootprints(); // Based on the |event_url| and |event_tab_id|, traces back the observed @@ -328,25 +328,27 @@ HostToIpMap* host_to_ip_map() { return &host_to_ip_map_; } // Remove stale entries from navigation_event_list_ if they are older than - // kNavigationFootprintTTLInSecond (2 minutes). + // `GetNavigationFootprintTTL()`. void CleanUpNavigationEvents(); // Remove stale entries from user_gesture_map_ if they are older than - // kNavigationFootprintTTLInSecond (2 minutes). + // `GetNavigationFootprintTTL()`. void CleanUpUserGestures(); // Remove stale entries from host_to_ip_map_ if they are older than - // kNavigationFootprintTTLInSecond (2 minutes). + // `GetNavigationFootprintTTL()`. void CleanUpIpAddresses(); bool IsCleanUpScheduled() const; void ScheduleNextCleanUpAfterInterval(base::TimeDelta interval); - void AddToReferrerChain(ReferrerChain* referrer_chain, - NavigationEvent* nav_event, - const GURL& destination_main_frame_url, - ReferrerChainEntry::URLType type); + // Adds the event to the referrer chain, unless it is older than + // `GetNavigationFootprintTTL()`. + void MaybeAddToReferrerChain(ReferrerChain* referrer_chain, + NavigationEvent* nav_event, + const GURL& destination_main_frame_url, + ReferrerChainEntry::URLType type); // Helper function to get the remaining referrer chain when we've already // traced back |current_user_gesture_count| number of user gestures. @@ -381,7 +383,7 @@ // frames, this list of NavigationEvents are ordered by navigation finish // time. Entries in navigation_event_list_ will be removed if they are older // than 2 minutes since their corresponding navigations finish or there are - // more than kNavigationRecordMaxSize entries. + // more than `GetNavigationRecordMaxSize()` entries. NavigationEventList navigation_event_list_; // user_gesture_map_ keeps track of the timestamp of last user gesture in
diff --git a/components/safe_browsing/content/browser/safe_browsing_navigation_observer_unittest.cc b/components/safe_browsing/content/browser/safe_browsing_navigation_observer_unittest.cc index f5d1070..ca4138a 100644 --- a/components/safe_browsing/content/browser/safe_browsing_navigation_observer_unittest.cc +++ b/components/safe_browsing/content/browser/safe_browsing_navigation_observer_unittest.cc
@@ -115,19 +115,16 @@ void CreateNonUserGestureReferrerChain() { user_gesture_map()->clear(); base::Time now = base::Time::Now(); - base::Time half_hour_ago = - base::Time::FromDoubleT(now.ToDoubleT() - 30.0 * 60.0); - base::Time one_hour_ago = - base::Time::FromDoubleT(now.ToDoubleT() - 60.0 * 60.0); - base::Time two_hours_ago = - base::Time::FromDoubleT(now.ToDoubleT() - 2 * 60.0 * 60.0); + base::Time half_second_ago = base::Time::FromDoubleT(now.ToDoubleT() - 0.5); + base::Time one_second_ago = base::Time::FromDoubleT(now.ToDoubleT() - 1.0); + base::Time two_seconds_ago = base::Time::FromDoubleT(now.ToDoubleT() - 2.0); // Add 13 navigations and one starting page. The first is BROWSER_INITIATED // to A. Then from A to B, then 10 redirects to C, then back to A. std::unique_ptr<NavigationEvent> first_navigation = std::make_unique<NavigationEvent>(); first_navigation->original_request_url = GURL("http://A.com"); - first_navigation->last_updated = two_hours_ago; + first_navigation->last_updated = two_seconds_ago; first_navigation->navigation_initiation = ReferrerChainEntry::BROWSER_INITIATED; navigation_event_list()->RecordNavigationEvent(std::move(first_navigation)); @@ -136,7 +133,7 @@ std::make_unique<NavigationEvent>(); second_navigation->source_url = GURL("http://A.com"); second_navigation->original_request_url = GURL("http://B.com"); - second_navigation->last_updated = one_hour_ago; + second_navigation->last_updated = one_second_ago; second_navigation->navigation_initiation = ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE; navigation_event_list()->RecordNavigationEvent( @@ -149,7 +146,7 @@ std::make_unique<NavigationEvent>(); navigation->source_url = prev_url; navigation->original_request_url = current_url; - navigation->last_updated = one_hour_ago; + navigation->last_updated = one_second_ago; navigation->navigation_initiation = ReferrerChainEntry::RENDERER_INITIATED_WITHOUT_USER_GESTURE; navigation_event_list()->RecordNavigationEvent(std::move(navigation)); @@ -161,7 +158,7 @@ std::make_unique<NavigationEvent>(); last_navigation->source_url = prev_url; last_navigation->original_request_url = GURL("http://A.com"); - last_navigation->last_updated = half_hour_ago; + last_navigation->last_updated = half_second_ago; last_navigation->navigation_initiation = ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE; navigation_event_list()->RecordNavigationEvent(std::move(last_navigation)); @@ -243,12 +240,9 @@ TEST_F(SBNavigationObserverTest, TestInfiniteLoop) { user_gesture_map()->clear(); base::Time now = base::Time::Now(); - base::Time half_hour_ago = - base::Time::FromDoubleT(now.ToDoubleT() - 30.0 * 60.0); - base::Time one_hour_ago = - base::Time::FromDoubleT(now.ToDoubleT() - 60.0 * 60.0); - base::Time two_hours_ago = - base::Time::FromDoubleT(now.ToDoubleT() - 2 * 60.0 * 60.0); + base::Time half_second_ago = base::Time::FromDoubleT(now.ToDoubleT() - 0.5); + base::Time one_second_ago = base::Time::FromDoubleT(now.ToDoubleT() - 1.0); + base::Time two_seconds_ago = base::Time::FromDoubleT(now.ToDoubleT() - 2.0); // Add 5 navigations and one starting page. The first is BROWSER_INITIATED // to A. Then from A to B, then 2 redirects back and forth between B and C, @@ -256,7 +250,7 @@ std::unique_ptr<NavigationEvent> first_navigation = std::make_unique<NavigationEvent>(); first_navigation->original_request_url = GURL("http://A.com"); - first_navigation->last_updated = two_hours_ago; + first_navigation->last_updated = two_seconds_ago; first_navigation->navigation_initiation = ReferrerChainEntry::BROWSER_INITIATED; navigation_event_list()->RecordNavigationEvent(std::move(first_navigation)); @@ -265,7 +259,7 @@ std::make_unique<NavigationEvent>(); second_navigation->source_url = GURL("http://A.com"); second_navigation->original_request_url = GURL("http://B.com"); - second_navigation->last_updated = one_hour_ago; + second_navigation->last_updated = one_second_ago; second_navigation->navigation_initiation = ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE; navigation_event_list()->RecordNavigationEvent(std::move(second_navigation)); @@ -276,7 +270,7 @@ std::make_unique<NavigationEvent>(); navigation->source_url = prev_url; navigation->original_request_url = current_url; - navigation->last_updated = one_hour_ago; + navigation->last_updated = one_second_ago; navigation->navigation_initiation = ReferrerChainEntry::RENDERER_INITIATED_WITHOUT_USER_GESTURE; navigation_event_list()->RecordNavigationEvent(std::move(navigation)); @@ -289,7 +283,7 @@ std::make_unique<NavigationEvent>(); last_navigation->source_url = prev_url; last_navigation->original_request_url = GURL("http://A.com"); - last_navigation->last_updated = half_hour_ago; + last_navigation->last_updated = half_second_ago; last_navigation->navigation_initiation = ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE; navigation_event_list()->RecordNavigationEvent(std::move(last_navigation)); @@ -610,17 +604,15 @@ TEST_F(SBNavigationObserverTest, TimestampIsDecreasing) { base::Time now = base::Time::Now(); - base::Time one_hour_ago = - base::Time::FromDoubleT(now.ToDoubleT() - 60.0 * 60.0); - base::Time two_hours_ago = - base::Time::FromDoubleT(now.ToDoubleT() - 2 * 60.0 * 60.0); + base::Time one_second_ago = base::Time::FromDoubleT(now.ToDoubleT() - 1.0); + base::Time two_seconds_ago = base::Time::FromDoubleT(now.ToDoubleT() - 2.0); // Add three navigations. The first is BROWSER_INITIATED to A. Then from A to // B, and then from B back to A. std::unique_ptr<NavigationEvent> first_navigation = std::make_unique<NavigationEvent>(); first_navigation->original_request_url = GURL("http://A.com"); - first_navigation->last_updated = two_hours_ago; + first_navigation->last_updated = two_seconds_ago; first_navigation->navigation_initiation = ReferrerChainEntry::BROWSER_INITIATED; navigation_event_list()->RecordNavigationEvent(std::move(first_navigation)); @@ -629,7 +621,7 @@ std::make_unique<NavigationEvent>(); second_navigation->source_url = GURL("http://A.com"); second_navigation->original_request_url = GURL("http://B.com"); - second_navigation->last_updated = one_hour_ago; + second_navigation->last_updated = one_second_ago; second_navigation->navigation_initiation = ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE; navigation_event_list()->RecordNavigationEvent(std::move(second_navigation)); @@ -698,14 +690,14 @@ TEST_F(SBNavigationObserverTest, RemoveNonUserGestureEntriesWithExcessiveUserGestureEvents) { GURL url = GURL("http://A.com"); - base::Time half_hour_ago = - base::Time::FromDoubleT(base::Time::Now().ToDoubleT() - 30.0 * 60.0); + base::Time half_second_ago = + base::Time::FromDoubleT(base::Time::Now().ToDoubleT() - 0.5); // Append 10 navigation events with user gesture. for (int i = 0; i < 10; i++) { std::unique_ptr<NavigationEvent> navigation_event = std::make_unique<NavigationEvent>(); navigation_event->source_url = url; - navigation_event->last_updated = half_hour_ago; + navigation_event->last_updated = half_second_ago; navigation_event->navigation_initiation = ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE; navigation_event_list()->RecordNavigationEvent(std::move(navigation_event)); @@ -756,8 +748,7 @@ TEST_F(SBNavigationObserverTest, ChainWorksThroughNewTab) { base::Time now = base::Time::Now(); - base::Time one_hour_ago = - base::Time::FromDoubleT(now.ToDoubleT() - 60.0 * 60.0); + base::Time one_second_ago = base::Time::FromDoubleT(now.ToDoubleT() - 1.0); SessionID source_tab = SessionID::NewUnique(); SessionID target_tab = SessionID::NewUnique(); @@ -768,7 +759,7 @@ std::make_unique<NavigationEvent>(); first_navigation->source_url = GURL("http://a.com/"); first_navigation->original_request_url = GURL("http://b.com/"); - first_navigation->last_updated = one_hour_ago; + first_navigation->last_updated = one_second_ago; first_navigation->navigation_initiation = ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE; first_navigation->source_tab_id = source_tab; @@ -799,13 +790,12 @@ TEST_F(SBNavigationObserverTest, ChainContinuesThroughBrowserInitiated) { base::Time now = base::Time::Now(); - base::Time one_hour_ago = - base::Time::FromDoubleT(now.ToDoubleT() - 60.0 * 60.0); + base::Time one_second_ago = base::Time::FromDoubleT(now.ToDoubleT() - 1.0); std::unique_ptr<NavigationEvent> first_navigation = std::make_unique<NavigationEvent>(); first_navigation->original_request_url = GURL("http://a.com/"); - first_navigation->last_updated = one_hour_ago; + first_navigation->last_updated = one_second_ago; first_navigation->navigation_initiation = ReferrerChainEntry::BROWSER_INITIATED; navigation_event_list()->RecordNavigationEvent(std::move(first_navigation)); @@ -830,8 +820,7 @@ TEST_F(SBNavigationObserverTest, CanceledRetargetingNavigationHasCorrectEventUrl) { base::Time now = base::Time::Now(); - base::Time one_hour_ago = - base::Time::FromDoubleT(now.ToDoubleT() - 60.0 * 60.0); + base::Time one_second_ago = base::Time::FromDoubleT(now.ToDoubleT() - 1.0); SessionID source_tab = SessionID::NewUnique(); SessionID target_tab = SessionID::NewUnique(); @@ -843,7 +832,7 @@ std::make_unique<NavigationEvent>(); first_navigation->source_url = GURL("http://example.com/a"); first_navigation->original_request_url = GURL("http://example.com/b"); - first_navigation->last_updated = one_hour_ago; + first_navigation->last_updated = one_second_ago; first_navigation->navigation_initiation = ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE; first_navigation->source_tab_id = source_tab; @@ -902,8 +891,7 @@ TEST_F(SBNavigationObserverTest, SanitizesDataUrls) { base::Time now = base::Time::Now(); - base::Time one_hour_ago = - base::Time::FromDoubleT(now.ToDoubleT() - 60.0 * 60.0); + base::Time one_second_ago = base::Time::FromDoubleT(now.ToDoubleT() - 1.0); SessionID tab_id = SessionID::NewUnique(); @@ -913,7 +901,7 @@ std::make_unique<NavigationEvent>(); first_navigation->source_url = GURL("http://a.com/"); first_navigation->original_request_url = GURL("data://private_data"); - first_navigation->last_updated = one_hour_ago; + first_navigation->last_updated = one_second_ago; first_navigation->navigation_initiation = ReferrerChainEntry::RENDERER_INITIATED_WITH_USER_GESTURE; first_navigation->source_tab_id = tab_id;
diff --git a/components/safe_browsing/core/common/features.cc b/components/safe_browsing/core/common/features.cc index 95e6aaab..ee0a19a8 100644 --- a/components/safe_browsing/core/common/features.cc +++ b/components/safe_browsing/core/common/features.cc
@@ -191,6 +191,17 @@ "SafeBrowsingRealTimeUrlLookupForEnterpriseAllowlistBypass", base::FEATURE_ENABLED_BY_DEFAULT); +BASE_FEATURE(kReferrerChainParameters, + "SafeBrowsingReferrerChainParameters", + base::FEATURE_DISABLED_BY_DEFAULT); + +constexpr base::FeatureParam<int> kReferrerChainEventMaximumAgeSeconds{ + &kReferrerChainParameters, "MaximumEventAgeSeconds", /*default_value=*/120}; + +constexpr base::FeatureParam<int> kReferrerChainEventMaximumCount{ + &kReferrerChainParameters, "MaximumEventCount", + /*default_value=*/100}; + BASE_FEATURE(kSafeBrowsingCsbrrNewDownloadTrigger, "SafeBrowsingCsbrrNewDownloadTrigger", base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/components/safe_browsing/core/common/features.h b/components/safe_browsing/core/common/features.h index 21a624b..a02c71d 100644 --- a/components/safe_browsing/core/common/features.h +++ b/components/safe_browsing/core/common/features.h
@@ -191,6 +191,18 @@ // Bypass RealTime URL Lookup allowlist for enterprise users. BASE_DECLARE_FEATURE(kRealTimeUrlLookupForEnterpriseAllowlistBypass); +// Enables modifying key parameters on the navigation event collection used to +// populate referrer chains. +BASE_DECLARE_FEATURE(kReferrerChainParameters); + +// The maximum age entry we keep in memory. Older entries are cleaned up. This +// is independent of the maximum age entry we send to Safe Browsing, which is +// fixed for privacy reasons. +extern const base::FeatureParam<int> kReferrerChainEventMaximumAgeSeconds; + +// The maximum number of navigation events we keep in memory. +extern const base::FeatureParam<int> kReferrerChainEventMaximumCount; + // Controls whether download Client Safe Browsing Reports are sent under the // new triggers BASE_DECLARE_FEATURE(kSafeBrowsingCsbrrNewDownloadTrigger);
diff --git a/components/segmentation_platform/embedder/default_model/device_switcher_model.cc b/components/segmentation_platform/embedder/default_model/device_switcher_model.cc index 1fae71d..c0b5c9f0 100644 --- a/components/segmentation_platform/embedder/default_model/device_switcher_model.cc +++ b/components/segmentation_platform/embedder/default_model/device_switcher_model.cc
@@ -85,7 +85,6 @@ .name = "SyncDeviceInfo"}); (*sync_input->mutable_additional_args())["wait_for_device_info_in_seconds"] = "60"; - (*sync_input->mutable_additional_args())["active_days_limit"] = "14"; writer.AddOutputConfigForMultiClassClassifier( kOutputLabels.begin(), kOutputLabels.size(), kOutputLabels.size(), 0.1);
diff --git a/components/segmentation_platform/internal/execution/processing/sync_device_info_observer.cc b/components/segmentation_platform/internal/execution/processing/sync_device_info_observer.cc index a7a158b..e5bbb7b 100644 --- a/components/segmentation_platform/internal/execution/processing/sync_device_info_observer.cc +++ b/components/segmentation_platform/internal/execution/processing/sync_device_info_observer.cc
@@ -156,7 +156,8 @@ device_info_status_ == DeviceInfoStatus::TIMEOUT_POSTED_BUT_NOT_HIT)) { pending_actions_.push_back(base::BindOnce( &SyncDeviceInfoObserver::ReadyToFinishProcessing, - weak_ptr_factory_.GetWeakPtr(), input, std::move(callback))); + weak_ptr_factory_.GetWeakPtr(), input, + feature_processor_state.input_context(), std::move(callback))); if (device_info_status_ == DeviceInfoStatus::TIMEOUT_NOT_POSTED) { device_info_status_ = DeviceInfoStatus::TIMEOUT_POSTED_BUT_NOT_HIT; @@ -168,13 +169,14 @@ } } else { ReadyToFinishProcessing( - input, std::move(callback), + input, feature_processor_state.input_context(), std::move(callback), device_info_status_ == DeviceInfoStatus::INFO_AVAILABLE); } } void SyncDeviceInfoObserver::ReadyToFinishProcessing( const proto::CustomInput& input, + scoped_refptr<InputContext> input_context, ProcessedCallback callback, bool success) { if (!success) { @@ -185,10 +187,14 @@ } int active_threshold = kDefaultActiveDaysThreshold; - auto it2 = input.additional_args().find("active_days_limit"); - if (it2 != input.additional_args().end()) { - if (!base::StringToInt(it2->second, &active_threshold)) { - active_threshold = kDefaultActiveDaysThreshold; + if (input_context) { + auto input_context_iter = + input_context->metadata_args.find("active_days_limit"); + if (input_context_iter != input_context->metadata_args.end()) { + const auto& processed_value = input_context_iter->second; + if (processed_value.type == ProcessedValue::INT) { + active_threshold = processed_value.int_val; + } } } std::map<
diff --git a/components/segmentation_platform/internal/execution/processing/sync_device_info_observer.h b/components/segmentation_platform/internal/execution/processing/sync_device_info_observer.h index 32b4e36..8866247 100644 --- a/components/segmentation_platform/internal/execution/processing/sync_device_info_observer.h +++ b/components/segmentation_platform/internal/execution/processing/sync_device_info_observer.h
@@ -9,6 +9,7 @@ #include "base/feature_list.h" #include "base/memory/weak_ptr.h" #include "base/time/time.h" +#include "components/segmentation_platform/public/input_context.h" #include "components/segmentation_platform/public/input_delegate.h" #include "components/sync_device_info/device_info.h" #include "components/sync_device_info/device_info_tracker.h" @@ -49,6 +50,7 @@ // Called when ready to finish processing. void ReadyToFinishProcessing(const proto::CustomInput& input, + scoped_refptr<InputContext> input_context, ProcessedCallback callback, bool success);
diff --git a/components/segmentation_platform/internal/execution/processing/sync_device_info_observer_unittest.cc b/components/segmentation_platform/internal/execution/processing/sync_device_info_observer_unittest.cc index 8be17d0..d760670 100644 --- a/components/segmentation_platform/internal/execution/processing/sync_device_info_observer_unittest.cc +++ b/components/segmentation_platform/internal/execution/processing/sync_device_info_observer_unittest.cc
@@ -53,6 +53,12 @@ /*interested_data_types=*/syncer::ModelTypeSet()); } +scoped_refptr<InputContext> CreateInputContext() { + auto input_context = base::MakeRefCounted<InputContext>(); + input_context->metadata_args.emplace("active_days_limit", 14); + return input_context; +} + class SyncDeviceInfoObserverTest : public testing::Test { public: SyncDeviceInfoObserverTest() = default; @@ -165,7 +171,7 @@ .name = "SyncDeviceInfo"}); (*sync_input->mutable_additional_args())["wait_for_device_info_in_seconds"] = "2"; - (*sync_input->mutable_additional_args())["active_days_limit"] = "14"; + state.set_input_context_for_testing(CreateInputContext()); std::unique_ptr<DeviceInfo> local_device_info = CreateDeviceInfo("local_device", kLocalDeviceType, kLocalDeviceOS); @@ -200,7 +206,7 @@ .name = "SyncDeviceInfo"}); (*sync_input->mutable_additional_args())["wait_for_device_info_in_seconds"] = "2"; - (*sync_input->mutable_additional_args())["active_days_limit"] = "14"; + state.set_input_context_for_testing(CreateInputContext()); std::vector<float> expected_result = {0, 0, 0, 0, 0, 1, 0, 0, 0, 0}; base::RunLoop loop; @@ -227,7 +233,7 @@ .name = "SyncDeviceInfo"}); (*sync_input->mutable_additional_args())["wait_for_device_info_in_seconds"] = "0"; - (*sync_input->mutable_additional_args())["active_days_limit"] = "14"; + state.set_input_context_for_testing(CreateInputContext()); // Failure flag is set. std::vector<float> expected_result = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -255,7 +261,7 @@ .name = "SyncDeviceInfo"}); (*sync_input->mutable_additional_args())["wait_for_device_info_in_seconds"] = "true"; - (*sync_input->mutable_additional_args())["active_days_limit"] = "14"; + state.set_input_context_for_testing(CreateInputContext()); // Failure flag is set. std::vector<float> expected_result = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -284,7 +290,7 @@ .name = "SyncDeviceInfo"}); (*sync_input->mutable_additional_args())["wait_for_device_info_in_seconds"] = "2"; - (*sync_input->mutable_additional_args())["active_days_limit"] = "14"; + state.set_input_context_for_testing(CreateInputContext()); // Failure flag is set. std::vector<float> expected_result = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
diff --git a/components/segmentation_platform/public/proto/model_metadata.proto b/components/segmentation_platform/public/proto/model_metadata.proto index c700e280..1cfb519 100644 --- a/components/segmentation_platform/public/proto/model_metadata.proto +++ b/components/segmentation_platform/public/proto/model_metadata.proto
@@ -128,11 +128,12 @@ // Output type: float // Output length: 10 // Additional arg: - // `active_days_limit`: Number of days after which the device is - // considered not active after last sync. // `wait_for_device_info_in_seconds`: Number of seconds to wait for sync // device info before timeout. If 0, then does not wait for sync and times // out immediately if device info is not available. + // InputContext arg: + // `active_days_limit`: Number of days after which the device is + // considered not active after last sync. Must be INT. FILL_SYNC_DEVICE_INFO = 5; }
diff --git a/components/user_education/common/feature_promo_controller.cc b/components/user_education/common/feature_promo_controller.cc index 4cd85975..6e8c92e 100644 --- a/components/user_education/common/feature_promo_controller.cc +++ b/components/user_education/common/feature_promo_controller.cc
@@ -508,15 +508,17 @@ DCHECK_EQ(current_iph_feature_, iph_feature); tutorial_promo_handle_ = CloseBubbleAndContinuePromo(*iph_feature); DCHECK(tutorial_promo_handle_.is_valid()); - if (tutorial_service_->StartTutorial( - tutorial_id, GetAnchorContext(), - base::BindOnce(&FeaturePromoControllerCommon::OnTutorialComplete, - weak_ptr_factory_.GetWeakPtr(), - base::Unretained(iph_feature)), - base::BindOnce(&FeaturePromoControllerCommon::OnTutorialAborted, - weak_ptr_factory_.GetWeakPtr(), - base::Unretained(iph_feature)))) + tutorial_service_->StartTutorial( + tutorial_id, GetAnchorContext(), + base::BindOnce(&FeaturePromoControllerCommon::OnTutorialComplete, + weak_ptr_factory_.GetWeakPtr(), + base::Unretained(iph_feature)), + base::BindOnce(&FeaturePromoControllerCommon::OnTutorialAborted, + weak_ptr_factory_.GetWeakPtr(), + base::Unretained(iph_feature))); + if (tutorial_service_->IsRunningTutorial()) { tutorial_service_->LogIPHLinkClicked(tutorial_id, true); + } } }
diff --git a/components/user_education/common/tutorial_service.cc b/components/user_education/common/tutorial_service.cc index 7e0eef6..e217abb 100644 --- a/components/user_education/common/tutorial_service.cc +++ b/components/user_education/common/tutorial_service.cc
@@ -9,6 +9,9 @@ #include "base/auto_reset.h" #include "base/callback_list.h" +#include "base/functional/bind.h" +#include "base/logging.h" +#include "base/time/time.h" #include "components/user_education/common/help_bubble.h" #include "components/user_education/common/help_bubble_factory_registry.h" #include "components/user_education/common/tutorial.h" @@ -17,6 +20,17 @@ namespace user_education { +namespace { +// How long a tutorial has to go without a bubble before we assume it's broken +// and abort it. +constexpr base::TimeDelta kBrokenTutorialTimeout = base::Seconds(15); +// How long a tutorial has to go before the first bubble is shown before we +// assume it's been broken or abandoned and abort it. This is longer than the +// above because we want to allow the user time to navigate to the surface that +// triggers the tutorial. +constexpr base::TimeDelta kTutorialNotStartedTimeout = base::Seconds(60); +} // namespace + TutorialService::TutorialCreationParams::TutorialCreationParams( TutorialDescription* description, ui::ElementContext context) @@ -35,21 +49,28 @@ TutorialService::~TutorialService() = default; -bool TutorialService::StartTutorial(TutorialIdentifier id, +void TutorialService::StartTutorial(TutorialIdentifier id, ui::ElementContext context, CompletedCallback completed_callback, AbortedCallback aborted_callback) { - // Overriding an existing running tutorial is not supported. In this case - // return false to the caller. - if (running_tutorial_) - return false; + // End the current tutorial, if any. + if (running_tutorial_) { + if (is_final_bubble_) { + // The current tutorial is showing the final congratulatory bubble, so it + // is effectively complete. + CompleteTutorial(); + } else { + running_tutorial_->Abort(); + } + } + is_final_bubble_ = false; // Get the description from the tutorial registry. TutorialDescription* description = tutorial_registry_->GetTutorialDescription(id); CHECK(description); - // Construct the tutorial from the dsecription. + // Construct the tutorial from the description. running_tutorial_ = Tutorial::Builder::BuildFromDescription(*description, this, context); @@ -61,11 +82,16 @@ running_tutorial_creation_params_ = std::make_unique<TutorialCreationParams>(description, context); + // Before starting the tutorial, set a timeout just in case the user never + // actually gets to a place where they can launch the first bubble. + broken_tutorial_timer_.Start( + FROM_HERE, kTutorialNotStartedTimeout, + base::BindOnce(&TutorialService::OnBrokenTutorial, + base::Unretained(this))); + // Start the tutorial and mark the params used to created it for restarting. running_tutorial_->Start(); toggle_focus_count_ = 0; - - return true; } void TutorialService::LogIPHLinkClicked(TutorialIdentifier id, @@ -126,17 +152,20 @@ // If the tutorial had been restarted and then aborted, The tutorial should be // considered completed. - if (running_tutorial_was_restarted_) - return CompleteTutorial(); + if (running_tutorial_was_restarted_) { + CompleteTutorial(); + return; + } - if (abort_step.has_value()) - running_tutorial_creation_params_->description_->histograms - ->RecordAbortStep(abort_step.value()); - - // Log the failure of completion for the tutorial. - if (running_tutorial_creation_params_->description_->histograms) + if (running_tutorial_creation_params_->description_->histograms) { + if (abort_step.has_value()) { + running_tutorial_creation_params_->description_->histograms + ->RecordAbortStep(abort_step.value()); + } running_tutorial_creation_params_->description_->histograms->RecordComplete( false); + } + UMA_HISTOGRAM_BOOLEAN("Tutorial.Completion", false); // Reset the tutorial and call the external abort callback. @@ -153,6 +182,21 @@ } } +void TutorialService::OnNonFinalBubbleClosed(HelpBubble* bubble) { + LOG(WARNING) << "On non final bubble closed."; + if (bubble != currently_displayed_bubble_.get()) { + return; + } + + bubble_closed_subscription_ = base::CallbackListSubscription(); + currently_displayed_bubble_.reset(); + + broken_tutorial_timer_.Start( + FROM_HERE, kBrokenTutorialTimeout, + base::BindOnce(&TutorialService::OnBrokenTutorial, + base::Unretained(this))); +} + void TutorialService::CompleteTutorial() { DCHECK(running_tutorial_); @@ -177,24 +221,27 @@ bool is_last_step) { DCHECK(running_tutorial_); currently_displayed_bubble_ = std::move(bubble); + broken_tutorial_timer_.Stop(); if (is_last_step) { - final_bubble_closed_subscription_ = + is_final_bubble_ = true; + bubble_closed_subscription_ = currently_displayed_bubble_->AddOnCloseCallback(base::BindOnce( [](TutorialService* service, user_education::HelpBubble*) { service->CompleteTutorial(); }, base::Unretained(this))); } else { - // If this was not the final bubble, we shouldn't be subscribed to a - // different "final bubble". - DCHECK(!final_bubble_closed_subscription_); + is_final_bubble_ = false; + bubble_closed_subscription_ = + currently_displayed_bubble_->AddOnCloseCallback(base::BindOnce( + &TutorialService::OnNonFinalBubbleClosed, base::Unretained(this))); } } void TutorialService::HideCurrentBubbleIfShowing() { if (!currently_displayed_bubble_) return; - final_bubble_closed_subscription_ = base::CallbackListSubscription(); + bubble_closed_subscription_ = base::CallbackListSubscription(); currently_displayed_bubble_.reset(); } @@ -204,6 +251,7 @@ void TutorialService::ResetRunningTutorial() { DCHECK(running_tutorial_); + broken_tutorial_timer_.Stop(); running_tutorial_.reset(); running_tutorial_creation_params_.reset(); running_tutorial_was_restarted_ = false; @@ -215,4 +263,10 @@ ++toggle_focus_count_; } +void TutorialService::OnBrokenTutorial() { + if (running_tutorial_ && !currently_displayed_bubble_) { + running_tutorial_->Abort(); + } +} + } // namespace user_education
diff --git a/components/user_education/common/tutorial_service.h b/components/user_education/common/tutorial_service.h index fad65a0..b9151c6 100644 --- a/components/user_education/common/tutorial_service.h +++ b/components/user_education/common/tutorial_service.h
@@ -12,6 +12,7 @@ #include "base/functional/callback_forward.h" #include "base/functional/callback_helpers.h" #include "base/memory/raw_ptr.h" +#include "base/timer/timer.h" #include "components/user_education/common/tutorial.h" #include "components/user_education/common/tutorial_identifier.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -39,7 +40,7 @@ using AbortedCallback = base::OnceClosure; // Returns true if there is a currently running tutorial. - bool IsRunningTutorial() const; + virtual bool IsRunningTutorial() const; // Sets the current help bubble stored by the service. void SetCurrentBubble(std::unique_ptr<HelpBubble> bubble, bool is_last_step); @@ -48,7 +49,8 @@ void HideCurrentBubbleIfShowing(); // Starts the tutorial by looking for the id in the Tutorial Registry. - virtual bool StartTutorial( + // Any existing tutorial is canceled. + virtual void StartTutorial( TutorialIdentifier id, ui::ElementContext context, CompletedCallback completed_callback = base::DoNothing(), @@ -93,6 +95,10 @@ ui::ElementContext context_; }; + // Called when a non-final bubble is closed. Used to trigger the broken + // tutorial timeout. + void OnNonFinalBubbleClosed(HelpBubble* bubble); + // Calls the completion code for the running tutorial. // TODO (dpenning): allow for registering a callback that performs any // IPH/other code on completion of tutorial @@ -104,6 +110,10 @@ // Tracks when the user toggles focus to a help bubble via the keyboard. void OnFocusToggledForAccessibility(HelpBubble* bubble); + // Called when there has been no bubble visible for enough time that the + // current tutorial should probably be aborted. + void OnBrokenTutorial(); + // Creation params for the last started tutorial. Used to restart the // tutorial after it has been completed. std::unique_ptr<TutorialCreationParams> running_tutorial_creation_params_; @@ -128,7 +138,14 @@ std::unique_ptr<HelpBubble> currently_displayed_bubble_; // Listens for when the final bubble in a Tutorial is closed. - base::CallbackListSubscription final_bubble_closed_subscription_; + base::CallbackListSubscription bubble_closed_subscription_; + + // Whether the currently-displayed bubble is the final one. + bool is_final_bubble_ = false; + + // Used to check for broken tutorials - when no bubble is visible for an + // unexpected period of time. + base::OneShotTimer broken_tutorial_timer_; // Pointers to the registries used for constructing and showing tutorials and // help bubbles.
diff --git a/components/user_education/common/tutorial_unittest.cc b/components/user_education/common/tutorial_unittest.cc index c0df87b6..e1b7b56 100644 --- a/components/user_education/common/tutorial_unittest.cc +++ b/components/user_education/common/tutorial_unittest.cc
@@ -9,6 +9,7 @@ #include "base/test/bind.h" #include "base/test/gtest_util.h" #include "base/test/mock_callback.h" +#include "base/test/task_environment.h" #include "components/strings/grit/components_strings.h" #include "components/user_education/common/help_bubble_factory_registry.h" #include "components/user_education/common/help_bubble_params.h" @@ -89,7 +90,15 @@ } // namespace -class TutorialTest : public testing::Test {}; +class TutorialTest : public testing::Test { + public: + TutorialTest() = default; + ~TutorialTest() override = default; + + protected: + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::TimeSource::MOCK_TIME}; +}; TEST_F(TutorialTest, TutorialBuilder) { const auto bubble_factory_registry = @@ -306,6 +315,66 @@ ClickCloseButton(service.currently_displayed_bubble_for_testing())); } +TEST_F(TutorialTest, StartTutorialAbortsExistingTutorial) { + UNCALLED_MOCK_CALLBACK(TutorialService::CompletedCallback, completed); + UNCALLED_MOCK_CALLBACK(TutorialService::CompletedCallback, aborted); + + const auto bubble_factory_registry = + CreateTestTutorialBubbleFactoryRegistry(); + TutorialRegistry registry; + TestTutorialService service(®istry, bubble_factory_registry.get()); + + // build elements and keep them for triggering show/hide + ui::test::TestElement element_1(kTestIdentifier1, kTestContext1); + element_1.Show(); + + // Build the tutorial Description. This has two steps, the second of which + // will not + TutorialDescription description; + description.steps.emplace_back(IDS_OK, IDS_OK, + ui::InteractionSequence::StepType::kShown, + kTestIdentifier1, "", HelpBubbleArrow::kNone); + description.steps.emplace_back(IDS_OK, IDS_OK, + ui::InteractionSequence::StepType::kShown, + kTestIdentifier2, "", HelpBubbleArrow::kNone); + registry.AddTutorial(kTestTutorial1, std::move(description)); + + service.StartTutorial(kTestTutorial1, element_1.context(), completed.Get(), + aborted.Get()); + EXPECT_CALL_IN_SCOPE( + aborted, Run, service.StartTutorial(kTestTutorial1, element_1.context())); + EXPECT_TRUE(service.IsRunningTutorial()); +} + +TEST_F(TutorialTest, StartTutorialCompletesExistingTutorial) { + UNCALLED_MOCK_CALLBACK(TutorialService::CompletedCallback, completed); + UNCALLED_MOCK_CALLBACK(TutorialService::CompletedCallback, aborted); + + const auto bubble_factory_registry = + CreateTestTutorialBubbleFactoryRegistry(); + TutorialRegistry registry; + TestTutorialService service(®istry, bubble_factory_registry.get()); + + // build elements and keep them for triggering show/hide + ui::test::TestElement element_1(kTestIdentifier1, kTestContext1); + element_1.Show(); + + // Build the tutorial Description. This has two steps, the second of which + // will not + TutorialDescription description; + description.steps.emplace_back(IDS_OK, IDS_OK, + ui::InteractionSequence::StepType::kShown, + kTestIdentifier1, "", HelpBubbleArrow::kNone); + registry.AddTutorial(kTestTutorial1, std::move(description)); + + service.StartTutorial(kTestTutorial1, element_1.context(), completed.Get(), + aborted.Get()); + EXPECT_CALL_IN_SCOPE( + completed, Run, + service.StartTutorial(kTestTutorial1, element_1.context())); + EXPECT_TRUE(service.IsRunningTutorial()); +} + TEST_F(TutorialTest, TutorialWithCustomEvent) { UNCALLED_MOCK_CALLBACK(TutorialService::CompletedCallback, completed); @@ -535,4 +604,94 @@ service.currently_displayed_bubble_for_testing()->Close()); } +TEST_F(TutorialTest, TimeoutBeforeFirstBubble) { + UNCALLED_MOCK_CALLBACK(TutorialService::CompletedCallback, completed); + UNCALLED_MOCK_CALLBACK(TutorialService::AbortedCallback, aborted); + + const auto bubble_factory_registry = + CreateTestTutorialBubbleFactoryRegistry(); + TutorialRegistry registry; + TestTutorialService service(®istry, bubble_factory_registry.get()); + + ui::test::TestElement el(kTestIdentifier1, kTestContext1); + + TutorialDescription description; + description.steps.emplace_back(IDS_OK, IDS_OK, + ui::InteractionSequence::StepType::kShown, + kTestIdentifier1, "", HelpBubbleArrow::kNone); + registry.AddTutorial(kTestTutorial1, std::move(description)); + + service.StartTutorial(kTestTutorial1, el.context(), completed.Get(), + aborted.Get()); + EXPECT_FALSE(service.currently_displayed_bubble_for_testing()); + EXPECT_CALL_IN_SCOPE(aborted, Run, + task_environment_.FastForwardUntilNoTasksRemain()); +} + +TEST_F(TutorialTest, TimeoutBetweenBubbles) { + UNCALLED_MOCK_CALLBACK(TutorialService::CompletedCallback, completed); + UNCALLED_MOCK_CALLBACK(TutorialService::AbortedCallback, aborted); + + const auto bubble_factory_registry = + CreateTestTutorialBubbleFactoryRegistry(); + TutorialRegistry registry; + TestTutorialService service(®istry, bubble_factory_registry.get()); + + ui::test::TestElement el1(kTestIdentifier1, kTestContext1); + ui::test::TestElement el2(kTestIdentifier1, kTestContext1); + el1.Show(); + + TutorialDescription description; + description.steps.emplace_back( + IDS_OK, IDS_OK, ui::InteractionSequence::StepType::kShown, + kTestIdentifier1, "", HelpBubbleArrow::kNone, + ui::CustomElementEventType(), /* must_remain_visible */ false); + description.steps.emplace_back(IDS_OK, IDS_OK, + ui::InteractionSequence::StepType::kShown, + kTestIdentifier2, "", HelpBubbleArrow::kNone); + registry.AddTutorial(kTestTutorial1, std::move(description)); + + service.StartTutorial(kTestTutorial1, el1.context(), completed.Get(), + aborted.Get()); + + // This closes the bubble but does not advance the tutorial. + el1.Hide(); + EXPECT_FALSE(service.currently_displayed_bubble_for_testing()); + EXPECT_CALL_IN_SCOPE(aborted, Run, + task_environment_.FastForwardUntilNoTasksRemain()); +} + +TEST_F(TutorialTest, NoTimeoutIfBubbleShowing) { + UNCALLED_MOCK_CALLBACK(TutorialService::CompletedCallback, completed); + UNCALLED_MOCK_CALLBACK(TutorialService::AbortedCallback, aborted); + + const auto bubble_factory_registry = + CreateTestTutorialBubbleFactoryRegistry(); + TutorialRegistry registry; + TestTutorialService service(®istry, bubble_factory_registry.get()); + + ui::test::TestElement el1(kTestIdentifier1, kTestContext1); + ui::test::TestElement el2(kTestIdentifier1, kTestContext1); + el1.Show(); + + TutorialDescription description; + description.steps.emplace_back(IDS_OK, IDS_OK, + ui::InteractionSequence::StepType::kShown, + kTestIdentifier1, "", HelpBubbleArrow::kNone); + description.steps.emplace_back(IDS_OK, IDS_OK, + ui::InteractionSequence::StepType::kShown, + kTestIdentifier2, "", HelpBubbleArrow::kNone); + registry.AddTutorial(kTestTutorial1, std::move(description)); + + service.StartTutorial(kTestTutorial1, el1.context(), completed.Get(), + aborted.Get()); + + // Since there is a bubble, there is no timeout. + EXPECT_TRUE(service.currently_displayed_bubble_for_testing()); + task_environment_.FastForwardUntilNoTasksRemain(); + + // When we exit and destroy the service, the callback will be called. + EXPECT_CALL(aborted, Run).Times(1); +} + } // namespace user_education
diff --git a/components/user_education/test/test_help_bubble.cc b/components/user_education/test/test_help_bubble.cc index 2065e1fc..11349ca 100644 --- a/components/user_education/test/test_help_bubble.cc +++ b/components/user_education/test/test_help_bubble.cc
@@ -4,7 +4,9 @@ #include "components/user_education/test/test_help_bubble.h" +#include "base/callback_list.h" #include "base/memory/weak_ptr.h" +#include "ui/base/interaction/element_identifier.h" #include "ui/base/interaction/element_test_util.h" #include "ui/base/interaction/element_tracker.h" #include "ui/base/interaction/framework_specific_implementation.h" @@ -17,9 +19,15 @@ // static constexpr int TestHelpBubble::kNoButtonWithTextIndex; -TestHelpBubble::TestHelpBubble(ui::ElementContext context, +TestHelpBubble::TestHelpBubble(ui::TrackedElement* element, HelpBubbleParams params) - : context_(context), params_(std::move(params)) {} + : element_(element), params_(std::move(params)) { + element_hidden_subscription_ = + ui::ElementTracker::GetElementTracker()->AddElementHiddenCallback( + element->identifier(), element->context(), + base::BindRepeating(&TestHelpBubble::OnElementHidden, + base::Unretained(this))); +} TestHelpBubble::~TestHelpBubble() { // Needs to be called here while we still have access to derived class @@ -69,18 +77,29 @@ } void TestHelpBubble::CloseBubbleImpl() { - context_ = ui::ElementContext(); + element_ = nullptr; + element_hidden_subscription_ = base::CallbackListSubscription(); } ui::ElementContext TestHelpBubble::GetContext() const { - return context_; + return element_ ? element_->context() : ui::ElementContext(); +} + +void TestHelpBubble::OnElementHidden(ui::TrackedElement* element) { + if (element == element_) { + if (is_open()) { + Close(); + } else { + element_ = nullptr; + element_hidden_subscription_ = base::CallbackListSubscription(); + } + } } std::unique_ptr<HelpBubble> TestHelpBubbleFactory::CreateBubble( ui::TrackedElement* element, HelpBubbleParams params) { - return std::make_unique<TestHelpBubble>(element->context(), - std::move(params)); + return std::make_unique<TestHelpBubble>(element, std::move(params)); } bool TestHelpBubbleFactory::CanBuildBubbleForTrackedElement(
diff --git a/components/user_education/test/test_help_bubble.h b/components/user_education/test/test_help_bubble.h index b74e94e2..b73f7f2 100644 --- a/components/user_education/test/test_help_bubble.h +++ b/components/user_education/test/test_help_bubble.h
@@ -8,10 +8,12 @@ #include <memory> #include "base/auto_reset.h" +#include "base/callback_list.h" #include "components/user_education/common/help_bubble.h" #include "components/user_education/common/help_bubble_factory.h" #include "components/user_education/common/help_bubble_params.h" #include "ui/base/interaction/element_identifier.h" +#include "ui/base/interaction/element_tracker.h" namespace ui { class TrackedElement; @@ -23,7 +25,7 @@ public: static constexpr int kNoButtonWithTextIndex = -1; - TestHelpBubble(ui::ElementContext context, HelpBubbleParams params); + TestHelpBubble(ui::TrackedElement* element, HelpBubbleParams params); ~TestHelpBubble() override; DECLARE_FRAMEWORK_SPECIFIC_METADATA() @@ -54,7 +56,10 @@ ui::ElementContext GetContext() const override; private: - ui::ElementContext context_; + void OnElementHidden(ui::TrackedElement* element); + + base::raw_ptr<ui::TrackedElement> element_; + base::CallbackListSubscription element_hidden_subscription_; HelpBubbleParams params_; int focus_count_ = 0;
diff --git a/components/user_education/webui/help_bubble_handler_unittest.cc b/components/user_education/webui/help_bubble_handler_unittest.cc index c13f569..7b52c3f 100644 --- a/components/user_education/webui/help_bubble_handler_unittest.cc +++ b/components/user_education/webui/help_bubble_handler_unittest.cc
@@ -431,8 +431,7 @@ params.buttons.emplace_back(std::move(button_params)); std::unique_ptr<HelpBubble> help_bubble = - std::make_unique<test::TestHelpBubble>(element->context(), - std::move(params)); + std::make_unique<test::TestHelpBubble>(element, std::move(params)); // Call the floating help bubble created method and ensure that the correct // message is sent over to the client.
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index f1cb16f..87055e4 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -2511,6 +2511,8 @@ "devtools/protocol/native_input_event_builder_mac.mm", "font_access/font_enumeration_data_source_mac.h", "font_access/font_enumeration_data_source_mac.mm", + "gpu/browser_child_process_backgrounded_bridge.h", + "gpu/browser_child_process_backgrounded_bridge.mm", "gpu/ca_transaction_gpu_coordinator.cc", "gpu/ca_transaction_gpu_coordinator.h", "media/desktop_media_window_registry_mac.mm",
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h index 316d4a53..253874d 100644 --- a/content/browser/accessibility/browser_accessibility_manager.h +++ b/content/browser/accessibility/browser_accessibility_manager.h
@@ -257,10 +257,6 @@ // Retrieve the bounds of the parent View in screen coordinates. gfx::Rect GetViewBoundsInScreenCoordinates() const; - // Fire an event telling native assistive technology to move focus to the - // given find in page result. - void ActivateFindInPageResult(int request_id, int match_index); - // Called when the renderer process has notified us of tree changes. Returns // false in fatal-error conditions, in which case the caller should destroy // the manager.
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc index 744bce4..a45ceb9 100644 --- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc +++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
@@ -14,6 +14,7 @@ #include "base/containers/flat_tree.h" #include "base/functional/bind.h" #include "base/functional/function_ref.h" +#include "base/functional/overloaded.h" #include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_functions.h" #include "base/time/time.h" @@ -210,61 +211,22 @@ AttributionInputEvent input_event; }; -struct AttributionDataHostManagerImpl::NavigationRedirectSourceRegistrations { - // Source origin to use for all registrations on a redirect chain. Will not - // change over the course of the redirect chain. +struct AttributionDataHostManagerImpl::SourceRegistrations { + // Source origin to use for all registrations on a navigation redirect or + // beacon chain. Will not change over the course of the chain. SuitableOrigin source_origin; // Number of source data we are waiting to be decoded/received. size_t pending_source_data = 0; - // True if navigation has completed, regardless of success or failure. If - // true, no further calls will be made to - // `NotifyNavigationRedirectRegistration()`. - bool navigation_complete = false; - - // The time the first registration header was received for the redirect chain. - // Will not change over the course of the redirect chain. - base::TimeTicks register_time; - - // Input event associated with the navigation. - AttributionInputEvent input_event; - - // Will not change over the course of the redirect chain. - AttributionNavigationType nav_type; - - // Whether the navigation is initiated within a fenced frame. Will not - // change over the course of the redirect chain. - bool is_within_fenced_frame; - - GlobalRenderFrameHostId render_frame_id; -}; - -struct AttributionDataHostManagerImpl::BeaconSourceRegistrations { - // Source origin to use for all registrations on a beacon. Will not - // change over the course of the beacon. - SuitableOrigin source_origin; - - // Number of source data we are waiting to be decoded/received. - size_t pending_source_data = 0; - - // Navigation source data that has been received as part of this beacon. - // Navigation sources cannot be processed until `navigation_complete` is set - // to true. - std::vector<StorableSource> sources; - // True if navigation has completed. `absl::nullopt` if it's an event beacon. absl::optional<bool> navigation_complete; - // True if the beacon has completed. If true, no further calls will be made to - // `NotifyFencedFrameReportingBeaconData()`. - bool beacon_complete = false; - - // The time the beacon was sent. Will be null when the beacon was started but - // not actually sent. + // The time the first registration header was received. Will be null when the + // beacon was started but no data was received yet. base::TimeTicks register_time; - // Whether the beacon was initiated within a fenced frame. + // Whether the registration was initiated within a fenced frame. bool is_within_fenced_frame; // Input event associated with the navigation. @@ -272,6 +234,66 @@ AttributionInputEvent input_event; GlobalRenderFrameHostId render_frame_id; + + struct NavigationRedirect { + blink::AttributionSrcToken attribution_src_token; + + // Will not change over the course of the redirect chain. + AttributionNavigationType nav_type; + }; + + struct Beacon { + BeaconId id; + + // Navigation source data that has been received as part of this beacon. + // Navigation sources cannot be processed until `navigation_complete` is set + // to true. + std::vector<StorableSource> sources; + + // True if the beacon has completed. If true, no further calls will be made + // to `NotifyFencedFrameReportingBeaconData()`. + bool beacon_complete = false; + }; + + absl::variant<NavigationRedirect, Beacon> data; + + bool operator<(const SourceRegistrations& other) const { + return Id() < other.Id(); + } + + Beacon& GetBeacon() { + DCHECK(absl::holds_alternative<Beacon>(data)); + return absl::get<Beacon>(data); + } + + NavigationRedirect& GetNavigationRedirect() { + DCHECK(absl::holds_alternative<NavigationRedirect>(data)); + return absl::get<NavigationRedirect>(data); + } + + friend bool operator<(const SourceRegistrations& a, + const SourceRegistrationsId& b) { + return a.Id() < b; + } + + friend bool operator<(const SourceRegistrationsId& a, + const SourceRegistrations& b) { + return a < b.Id(); + } + + private: + SourceRegistrationsId Id() const { + return absl::visit( + base::Overloaded{ + [](const NavigationRedirect& redirect) { + return SourceRegistrationsId(redirect.attribution_src_token); + }, + [](const Beacon& beacon) { + return SourceRegistrationsId(beacon.id); + }, + }, + data); + } }; AttributionDataHostManagerImpl::AttributionDataHostManagerImpl( @@ -354,16 +376,20 @@ return; } - auto [it, inserted] = redirect_registrations_.try_emplace( - attribution_src_token, - NavigationRedirectSourceRegistrations{ - .source_origin = source_origin, - .register_time = base::TimeTicks::Now(), - .input_event = input_event, - .nav_type = nav_type, - .is_within_fenced_frame = is_within_fenced_frame, - .render_frame_id = render_frame_id}); - DCHECK(!it->second.navigation_complete); + auto [it, inserted] = registrations_.insert(SourceRegistrations{ + .source_origin = source_origin, + .navigation_complete = false, + .register_time = base::TimeTicks::Now(), + .is_within_fenced_frame = is_within_fenced_frame, + .input_event = std::move(input_event), + .render_frame_id = render_frame_id, + .data = + SourceRegistrations::NavigationRedirect{ + .attribution_src_token = attribution_src_token, + .nav_type = nav_type, + }, + }); + DCHECK(it->navigation_complete == false); // Treat ongoing redirect registrations within a chain as a data host for the // purpose of trigger queuing. @@ -371,13 +397,13 @@ data_hosts_in_source_mode_++; } - it->second.pending_source_data++; + it->pending_source_data++; // Send the data to the decoder, but track that we are now waiting on a new // registration. data_decoder::DataDecoder::ParseJsonIsolated( header_value, - base::BindOnce(&AttributionDataHostManagerImpl::OnRedirectSourceParsed, + base::BindOnce(&AttributionDataHostManagerImpl::OnSourceParsed, weak_factory_.GetWeakPtr(), attribution_src_token, std::move(reporting_origin), header_value)); } @@ -403,22 +429,15 @@ RecordNavigationDataHostStatus(NavigationDataHostStatus::kNotFound); } - auto redirect_it = redirect_registrations_.find(attribution_src_token); - if (redirect_it == redirect_registrations_.end()) { + auto redirect_it = registrations_.find(attribution_src_token); + if (redirect_it == registrations_.end()) { return; } - NavigationRedirectSourceRegistrations& registrations = redirect_it->second; + DCHECK(redirect_it->navigation_complete == false); + redirect_it->navigation_complete = true; - DCHECK(!registrations.navigation_complete); - registrations.navigation_complete = true; - - if (registrations.pending_source_data == 0u) { - // We have finished processing all sources on this redirect chain, cleanup - // the map. - OnSourceEligibleDataHostFinished(registrations.register_time); - redirect_registrations_.erase(redirect_it); - } + MaybeOnRegistrationsFinished(attribution_src_token); } void AttributionDataHostManagerImpl::NotifyNavigationFailure( @@ -436,26 +455,20 @@ // We are not guaranteed to be processing redirect registrations for a given // navigation. - auto redirect_it = redirect_registrations_.find(*attribution_src_token); - if (redirect_it != redirect_registrations_.end()) { - NavigationRedirectSourceRegistrations& registrations = - redirect_it->second; + if (auto redirect_it = registrations_.find(*attribution_src_token); + redirect_it != registrations_.end()) { + DCHECK(redirect_it->navigation_complete == false); + redirect_it->navigation_complete = true; - DCHECK(!registrations.navigation_complete); - registrations.navigation_complete = true; - - if (registrations.pending_source_data == 0u) { - OnSourceEligibleDataHostFinished(redirect_it->second.register_time); - redirect_registrations_.erase(redirect_it); - } + MaybeOnRegistrationsFinished(*attribution_src_token); } } - auto beacon_it = - beacon_registrations_.find(BeaconId(NavigationBeaconId(navigation_id))); - if (beacon_it != beacon_registrations_.end()) { - OnSourceEligibleDataHostFinished(beacon_it->second.register_time); - beacon_registrations_.erase(beacon_it); + if (auto beacon_it = + registrations_.find(BeaconId(NavigationBeaconId(navigation_id))); + beacon_it != registrations_.end()) { + OnSourceEligibleDataHostFinished(beacon_it->register_time); + registrations_.erase(beacon_it); } } @@ -463,22 +476,22 @@ int64_t navigation_id) { NavigationBeaconId beacon_id(navigation_id); - auto it = beacon_registrations_.find(BeaconId(beacon_id)); - if (it == beacon_registrations_.end()) { + auto it = registrations_.find(BeaconId(beacon_id)); + if (it == registrations_.end()) { return; } - BeaconSourceRegistrations& registrations = it->second; - DCHECK(registrations.navigation_complete == false); - registrations.navigation_complete = true; + DCHECK(it->navigation_complete == false); + it->navigation_complete = true; - for (StorableSource& source : registrations.sources) { - attribution_manager_->HandleSource(std::move(source), - registrations.render_frame_id); + std::vector<StorableSource>& sources = it->GetBeacon().sources; + + for (StorableSource& source : sources) { + attribution_manager_->HandleSource(std::move(source), it->render_frame_id); } - registrations.sources.clear(); + sources.clear(); - MaybeOnBeaconRegistrationsFinished(beacon_id); + MaybeOnRegistrationsFinished(beacon_id); } const AttributionDataHostManagerImpl::ReceiverContext* @@ -733,80 +746,40 @@ delayed_triggers_.clear(); } -void AttributionDataHostManagerImpl::OnRedirectSourceParsed( - const blink::AttributionSrcToken& attribution_src_token, - const SuitableOrigin& reporting_origin, - const std::string& header_value, - data_decoder::DataDecoder::ValueOrError result) { - auto it = redirect_registrations_.find(attribution_src_token); - - // The registration may no longer be tracked in the event the navigation - // failed. - if (it == redirect_registrations_.end()) { - return; - } - - DCHECK_GT(it->second.pending_source_data, 0u); - NavigationRedirectSourceRegistrations& registrations = it->second; - registrations.pending_source_data--; - - absl::optional<StorableSource> source = - ParseStorableSource(std::move(result), header_value, reporting_origin, - registrations.source_origin, SourceType::kNavigation, - registrations.is_within_fenced_frame); - - if (source.has_value()) { - base::UmaHistogramEnumeration( - "Conversions.SourceRegistration.NavigationType.Foreground", - registrations.nav_type); - attribution_manager_->HandleSource(std::move(*source), - registrations.render_frame_id); - } - - if (registrations.pending_source_data == 0u && - registrations.navigation_complete) { - // We have finished processing all sources on this redirect chain, cleanup - // the map. - OnSourceEligibleDataHostFinished(registrations.register_time); - redirect_registrations_.erase(it); - } -} - void AttributionDataHostManagerImpl::NotifyFencedFrameReportingBeaconStarted( BeaconId beacon_id, SuitableOrigin source_origin, bool is_within_fenced_frame, AttributionInputEvent input_event, GlobalRenderFrameHostId render_frame_id) { - bool is_navigation = absl::holds_alternative<NavigationBeaconId>(beacon_id); + const bool is_navigation = + absl::holds_alternative<NavigationBeaconId>(beacon_id); - auto [it, inserted] = beacon_registrations_.try_emplace( - beacon_id, BeaconSourceRegistrations{ - .source_origin = std::move(source_origin), - .is_within_fenced_frame = is_within_fenced_frame, - .input_event = input_event, - .render_frame_id = render_frame_id}); - - if (!inserted) { - return; - } - - if (is_navigation) { - it->second.navigation_complete.emplace(false); - } + registrations_.insert(SourceRegistrations{ + .source_origin = std::move(source_origin), + .navigation_complete = + is_navigation ? absl::make_optional(false) : absl::nullopt, + .is_within_fenced_frame = is_within_fenced_frame, + .input_event = std::move(input_event), + .render_frame_id = render_frame_id, + .data = + SourceRegistrations::Beacon{ + .id = beacon_id, + }, + }); } void AttributionDataHostManagerImpl::NotifyFencedFrameReportingBeaconSent( BeaconId beacon_id) { - auto it = beacon_registrations_.find(beacon_id); + auto it = registrations_.find(beacon_id); // The registration may no longer be tracked in the event the navigation // failed. - if (it == beacon_registrations_.end()) { + if (it == registrations_.end()) { return; } - it->second.register_time = base::TimeTicks::Now(); + it->register_time = base::TimeTicks::Now(); // Treat ongoing beacon registrations as a data host for the purpose of // trigger queuing. @@ -818,90 +791,70 @@ url::Origin reporting_origin, const net::HttpResponseHeaders* headers, bool is_final_response) { - auto it = beacon_registrations_.find(beacon_id); + auto it = registrations_.find(beacon_id); // The registration may no longer be tracked in the event the navigation // failed. - if (it == beacon_registrations_.end()) { + if (it == registrations_.end()) { return; } - DCHECK(!it->second.beacon_complete); - it->second.beacon_complete = is_final_response; + SourceRegistrations::Beacon& data = it->GetBeacon(); + DCHECK(!data.beacon_complete); + data.beacon_complete = is_final_response; absl::optional<SuitableOrigin> suitable_reporting_origin = SuitableOrigin::Create(std::move(reporting_origin)); if (!suitable_reporting_origin) { - MaybeOnBeaconRegistrationsFinished(beacon_id); + MaybeOnRegistrationsFinished(beacon_id); return; } if (!headers) { - MaybeOnBeaconRegistrationsFinished(beacon_id); + MaybeOnRegistrationsFinished(beacon_id); return; } std::string source_header; if (!headers->GetNormalizedHeader(kAttributionReportingRegisterSourceHeader, &source_header)) { - MaybeOnBeaconRegistrationsFinished(beacon_id); + MaybeOnRegistrationsFinished(beacon_id); return; } - it->second.pending_source_data++; + it->pending_source_data++; data_decoder::DataDecoder::ParseJsonIsolated( source_header, - base::BindOnce(&AttributionDataHostManagerImpl::OnBeaconSourceParsed, + base::BindOnce(&AttributionDataHostManagerImpl::OnSourceParsed, weak_factory_.GetWeakPtr(), beacon_id, std::move(*suitable_reporting_origin), source_header)); } -void AttributionDataHostManagerImpl::OnBeaconSourceParsed( - BeaconId beacon_id, +void AttributionDataHostManagerImpl::OnSourceParsed( + SourceRegistrationsId id, const SuitableOrigin& reporting_origin, const std::string& header_value, data_decoder::DataDecoder::ValueOrError result) { - auto it = beacon_registrations_.find(beacon_id); + auto it = registrations_.find(id); // The registration may no longer be tracked in the event the navigation // failed. - if (it == beacon_registrations_.end()) { + if (it == registrations_.end()) { return; } - BeaconSourceRegistrations& registrations = it->second; + SourceRegistrations& registrations = *it; DCHECK_GT(registrations.pending_source_data, 0u); registrations.pending_source_data--; - absl::optional<StorableSource> source = - ParseStorableSource(std::move(result), header_value, reporting_origin, - registrations.source_origin, - absl::holds_alternative<NavigationBeaconId>(beacon_id) - ? SourceType::kNavigation - : SourceType::kEvent, - registrations.is_within_fenced_frame); - - if (source.has_value()) { - if (registrations.navigation_complete.value_or(true)) { - attribution_manager_->HandleSource(std::move(*source), - registrations.render_frame_id); - } else { - registrations.sources.push_back(std::move(*source)); - } + auto source_type = SourceType::kNavigation; + if (const auto* beacon = + absl::get_if<SourceRegistrations::Beacon>(®istrations.data); + beacon && absl::holds_alternative<EventBeaconId>(beacon->id)) { + source_type = SourceType::kEvent; } - MaybeOnBeaconRegistrationsFinished(beacon_id); -} - -absl::optional<StorableSource> -AttributionDataHostManagerImpl::ParseStorableSource( - data_decoder::DataDecoder::ValueOrError result, - const std::string& header_value, - const attribution_reporting::SuitableOrigin& reporting_origin, - const attribution_reporting::SuitableOrigin& source_origin, - SourceType source_type, - bool is_within_fenced_frame) { base::expected<StorableSource, SourceRegistrationError> source = base::unexpected(SourceRegistrationError::kInvalidJson); if (result.has_value()) { @@ -910,8 +863,9 @@ std::move(*result).TakeDict()); if (registration.has_value()) { source.emplace(reporting_origin, std::move(*registration), - /*source_time=*/base::Time::Now(), source_origin, - source_type, is_within_fenced_frame); + /*source_time=*/base::Time::Now(), + registrations.source_origin, source_type, + registrations.is_within_fenced_frame); } else { source = base::unexpected(registration.error()); } @@ -920,31 +874,54 @@ } } - if (!source.has_value()) { + if (source.has_value()) { + absl::visit( + base::Overloaded{ + [&](const SourceRegistrations::NavigationRedirect& redirect) { + base::UmaHistogramEnumeration( + "Conversions.SourceRegistration.NavigationType.Foreground", + redirect.nav_type); + attribution_manager_->HandleSource(std::move(*source), + registrations.render_frame_id); + }, + [&](SourceRegistrations::Beacon& beacon) { + if (registrations.navigation_complete.value_or(true)) { + attribution_manager_->HandleSource( + std::move(*source), registrations.render_frame_id); + } else { + beacon.sources.push_back(std::move(*source)); + } + }, + }, + registrations.data); + } else { attribution_manager_->NotifyFailedSourceRegistration( - header_value, source_origin, reporting_origin, source_type, - source.error()); + header_value, registrations.source_origin, reporting_origin, + source_type, source.error()); attribution_reporting::RecordSourceRegistrationError(source.error()); - return absl::nullopt; } - return std::move(*source); + MaybeOnRegistrationsFinished(id); } -void AttributionDataHostManagerImpl::MaybeOnBeaconRegistrationsFinished( - BeaconId beacon_id) { - auto it = beacon_registrations_.find(beacon_id); - if (it == beacon_registrations_.end()) { +void AttributionDataHostManagerImpl::MaybeOnRegistrationsFinished( + SourceRegistrationsId id) { + auto it = registrations_.find(id); + if (it == registrations_.end()) { return; } - BeaconSourceRegistrations& registrations = it->second; - if (registrations.pending_source_data == 0u && - registrations.navigation_complete.value_or(true) && - registrations.beacon_complete) { - OnSourceEligibleDataHostFinished(registrations.register_time); - beacon_registrations_.erase(it); + if (it->pending_source_data > 0u || !it->navigation_complete.value_or(true)) { + return; } + + if (const auto* beacon = absl::get_if<SourceRegistrations::Beacon>(&it->data); + beacon && !beacon->beacon_complete) { + return; + } + + OnSourceEligibleDataHostFinished(it->register_time); + registrations_.erase(it); } } // namespace content
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.h b/content/browser/attribution_reporting/attribution_data_host_manager_impl.h index 45ca2c0..226b153 100644 --- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.h +++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.h
@@ -12,6 +12,7 @@ #include "base/containers/circular_deque.h" #include "base/containers/flat_map.h" +#include "base/containers/flat_set.h" #include "base/functional/function_ref.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" @@ -25,13 +26,10 @@ #include "mojo/public/cpp/bindings/receiver_set.h" #include "services/data_decoder/public/cpp/data_decoder.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "third_party/abseil-cpp/absl/types/variant.h" #include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom.h" -#if BUILDFLAG(IS_ANDROID) -#include "third_party/abseil-cpp/absl/types/variant.h" -#endif - namespace attribution_reporting { class SuitableOrigin; @@ -48,7 +46,6 @@ class AttributionManager; class AttributionTrigger; -class StorableSource; struct GlobalRenderFrameHostId; @@ -121,12 +118,11 @@ struct NavigationDataHost; // Represents a set of attribution sources which registered in a top-level - // navigation redirect chain, and associated info to process them. - struct NavigationRedirectSourceRegistrations; + // navigation redirect or a beacon chain, and associated info to process them. + struct SourceRegistrations; - // Represents a set of attribution sources which registered in a beacon, and - // associated info to process them. - struct BeaconSourceRegistrations; + using SourceRegistrationsId = + absl::variant<blink::AttributionSrcToken, BeaconId>; #if BUILDFLAG(IS_ANDROID) struct OsTrigger; @@ -152,27 +148,13 @@ void OnReceiverDisconnected(); void OnSourceEligibleDataHostFinished(base::TimeTicks register_time); - void OnRedirectSourceParsed( - const blink::AttributionSrcToken& attribution_src_token, + void OnSourceParsed( + SourceRegistrationsId, const attribution_reporting::SuitableOrigin& reporting_origin, const std::string& header_value, data_decoder::DataDecoder::ValueOrError result); - void OnBeaconSourceParsed( - BeaconId beacon_id, - const attribution_reporting::SuitableOrigin& reporting_origin, - const std::string& header_value, - data_decoder::DataDecoder::ValueOrError result); - - absl::optional<StorableSource> ParseStorableSource( - data_decoder::DataDecoder::ValueOrError result, - const std::string& header_value, - const attribution_reporting::SuitableOrigin& reporting_origin, - const attribution_reporting::SuitableOrigin& source_origin, - attribution_reporting::mojom::SourceType, - bool is_within_fenced_frame); - - void MaybeOnBeaconRegistrationsFinished(BeaconId beacon_id); + void MaybeOnRegistrationsFinished(SourceRegistrationsId); void HandleTrigger(TriggerPayload, GlobalRenderFrameHostId); void MaybeBufferTrigger( @@ -193,14 +175,9 @@ base::flat_map<blink::AttributionSrcToken, NavigationDataHost> navigation_data_host_map_; - // Stores registrations received for redirects within a navigation with a - // given token. - base::flat_map<blink::AttributionSrcToken, - NavigationRedirectSourceRegistrations> - redirect_registrations_; - - // Stores registrations received for a beacon. - base::flat_map<BeaconId, BeaconSourceRegistrations> beacon_registrations_; + // Stores registrations received for redirects within a navigation or a + // beacon. + base::flat_set<SourceRegistrations> registrations_; // The number of connected receivers that may register a source. Used to // determine whether to buffer triggers. Event receivers are counted here
diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc index 50f72fe..04a9950 100644 --- a/content/browser/browser_child_process_host_impl.cc +++ b/content/browser/browser_child_process_host_impl.cc
@@ -369,6 +369,14 @@ PROCESS_TYPE_MAX); } +#if !BUILDFLAG(IS_ANDROID) +void BrowserChildProcessHostImpl::SetProcessBackgrounded(bool is_background) { + DCHECK(child_process_); + DCHECK(!child_process_->IsStarting()); + child_process_->SetProcessBackgrounded(is_background); +} +#endif // !BUILDFLAG(IS_ANDROID) + #if BUILDFLAG(IS_ANDROID) void BrowserChildProcessHostImpl::EnableWarmUpConnection() { can_use_warm_up_connection_ = true;
diff --git a/content/browser/browser_child_process_host_impl.h b/content/browser/browser_child_process_host_impl.h index 3ed6fa6..1dd30c9 100644 --- a/content/browser/browser_child_process_host_impl.h +++ b/content/browser/browser_child_process_host_impl.h
@@ -128,6 +128,10 @@ static void HistogramBadMessageTerminated(ProcessType process_type); +#if !BUILDFLAG(IS_ANDROID) + void SetProcessBackgrounded(bool is_background); +#endif // !BUILDFLAG(IS_ANDROID) + #if BUILDFLAG(IS_ANDROID) void EnableWarmUpConnection(); void DumpProcessStack();
diff --git a/content/browser/devtools/devtools_http_handler_unittest.cc b/content/browser/devtools/devtools_http_handler_unittest.cc index 27b8703..a48ec25 100644 --- a/content/browser/devtools/devtools_http_handler_unittest.cc +++ b/content/browser/devtools/devtools_http_handler_unittest.cc
@@ -188,13 +188,7 @@ content::BrowserTaskEnvironment task_environment_; }; -// TODO(https://crbug.com/1420865): Failing on iOS port. -#if BUILDFLAG(IS_IOS) -#define MAYBE_TestStartStop DISABLED_TestStartStop -#else -#define MAYBE_TestStartStop TestStartStop -#endif -TEST_F(DevToolsHttpHandlerTest, MAYBE_TestStartStop) { +TEST_F(DevToolsHttpHandlerTest, TestStartStop) { base::RunLoop run_loop, run_loop_2; auto factory = std::make_unique<DummyServerSocketFactory>( run_loop.QuitClosure(), run_loop_2.QuitClosure()); @@ -208,13 +202,7 @@ run_loop_2.Run(); } -// TODO(https://crbug.com/1420865): Failing on iOS port. -#if BUILDFLAG(IS_IOS) -#define MAYBE_TestServerSocketFailed DISABLED_TestServerSocketFailed -#else -#define MAYBE_TestServerSocketFailed TestServerSocketFailed -#endif -TEST_F(DevToolsHttpHandlerTest, MAYBE_TestServerSocketFailed) { +TEST_F(DevToolsHttpHandlerTest, TestServerSocketFailed) { base::RunLoop run_loop, run_loop_2; auto factory = std::make_unique<FailingServerSocketFactory>( run_loop.QuitClosure(), run_loop_2.QuitClosure()); @@ -231,13 +219,7 @@ run_loop_2.Run(); } -// TODO(https://crbug.com/1420865): Failing on iOS port. -#if BUILDFLAG(IS_IOS) -#define MAYBE_TestDevToolsActivePort DISABLED_TestDevToolsActivePort -#else -#define MAYBE_TestDevToolsActivePort TestDevToolsActivePort -#endif -TEST_F(DevToolsHttpHandlerTest, MAYBE_TestDevToolsActivePort) { +TEST_F(DevToolsHttpHandlerTest, TestDevToolsActivePort) { base::RunLoop run_loop, run_loop_2; base::ScopedTempDir temp_dir; EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -267,14 +249,7 @@ EXPECT_EQ(static_cast<int>(kDummyPort), port); } -// TODO(https://crbug.com/1420865): Failing on iOS port. -#if BUILDFLAG(IS_IOS) -#define MAYBE_MutatingActionsiRequireSafeVerb \ - DISABLED_MutatingActionsiRequireSafeVerb -#else -#define MAYBE_MutatingActionsiRequireSafeVerb MutatingActionsiRequireSafeVerb -#endif -TEST_F(DevToolsHttpHandlerTest, MAYBE_MutatingActionsiRequireSafeVerb) { +TEST_F(DevToolsHttpHandlerTest, MutatingActionsiRequireSafeVerb) { base::RunLoop run_loop, run_loop_2; auto* factory = new TCPServerSocketFactory(run_loop.QuitClosure(), run_loop_2.QuitClosure()); @@ -320,13 +295,7 @@ run_loop_2.Run(); } -// TODO(https://crbug.com/1420865): Failing on iOS port. -#if BUILDFLAG(IS_IOS) -#define MAYBE_TestJsonNew DISABLED_TestJsonNew -#else -#define MAYBE_TestJsonNew TestJsonNew -#endif -TEST_F(DevToolsHttpHandlerTest, MAYBE_TestJsonNew) { +TEST_F(DevToolsHttpHandlerTest, TestJsonNew) { base::RunLoop run_loop, run_loop_2; auto* factory = new TCPServerSocketFactory(run_loop.QuitClosure(), run_loop_2.QuitClosure()); @@ -443,16 +412,8 @@ base::RunLoop run_loop_2_; }; -// TODO(https://crbug.com/1420865): Failing on iOS port. -#if BUILDFLAG(IS_IOS) -#define MAYBE_TestRejectsWebSocketConnectionsWithOrigin \ - DISABLED_TestRejectsWebSocketConnectionsWithOrigin -#else -#define MAYBE_TestRejectsWebSocketConnectionsWithOrigin \ - TestRejectsWebSocketConnectionsWithOrigin -#endif TEST_F(DevToolsWebSocketHandlerTest, - MAYBE_TestRejectsWebSocketConnectionsWithOrigin) { + TestRejectsWebSocketConnectionsWithOrigin) { int port = StartServer(); std::string debugging_url = GetWebSocketDebuggingURL(port); @@ -477,16 +438,7 @@ StopServer(); } -// TODO(https://crbug.com/1420865): Failing on iOS port. -#if BUILDFLAG(IS_IOS) -#define MAYBE_TestAllowsCLIOverrideAllowsOriginsStar \ - DISABLED_TestAllowsCLIOverrideAllowsOriginsStar -#else -#define MAYBE_TestAllowsCLIOverrideAllowsOriginsStar \ - TestAllowsCLIOverrideAllowsOriginsStar -#endif -TEST_F(DevToolsWebSocketHandlerTest, - MAYBE_TestAllowsCLIOverrideAllowsOriginsStar) { +TEST_F(DevToolsWebSocketHandlerTest, TestAllowsCLIOverrideAllowsOriginsStar) { base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kRemoteAllowOrigins, "*"); int port = StartServer(); @@ -506,15 +458,7 @@ StopServer(); } -// TODO(https://crbug.com/1420865): Failing on iOS port. -#if BUILDFLAG(IS_IOS) -#define MAYBE_TestAllowsCLIOverrideAllowsOrigins \ - DISABLED_TestAllowsCLIOverrideAllowsOrigins -#else -#define MAYBE_TestAllowsCLIOverrideAllowsOrigins \ - TestAllowsCLIOverrideAllowsOrigins -#endif -TEST_F(DevToolsWebSocketHandlerTest, MAYBE_TestAllowsCLIOverrideAllowsOrigins) { +TEST_F(DevToolsWebSocketHandlerTest, TestAllowsCLIOverrideAllowsOrigins) { base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( switches::kRemoteAllowOrigins, "http://localhost"); int port = StartServer();
diff --git a/content/browser/devtools/protocol/fedcm_handler.cc b/content/browser/devtools/protocol/fedcm_handler.cc index a2da8b8..aa35d3f 100644 --- a/content/browser/devtools/protocol/fedcm_handler.cc +++ b/content/browser/devtools/protocol/fedcm_handler.cc
@@ -44,9 +44,32 @@ void FedCmHandler::OnDialogShown() { DCHECK(frontend_); - if (enabled_) { - frontend_->DialogShown(); + if (!enabled_) { + return; } + + auto* auth_request = GetFederatedAuthRequest(); + const auto* idp_data = + auth_request ? &auth_request->GetSortedIdpData() : nullptr; + DCHECK(idp_data); + DCHECK(!idp_data->empty()); + + auto accounts = std::make_unique<Array<FedCm::Account>>(); + for (const auto& data : *idp_data) { + for (const IdentityRequestAccount& account : data.accounts) { + std::unique_ptr<FedCm::Account> entry = + FedCm::Account::Create() + .SetAccountId(account.id) + .SetEmail(account.email) + .SetName(account.name) + .SetGivenName(account.given_name) + .SetPictureUrl(account.picture.spec()) + .SetIdpConfigUrl(data.idp_metadata.config_url.spec()) + .Build(); + accounts->push_back(std::move(entry)); + } + } + frontend_->DialogShown(std::move(accounts)); } FederatedAuthRequestPageData* FedCmHandler::GetPageData() { @@ -57,4 +80,12 @@ return PageUserData<FederatedAuthRequestPageData>::GetOrCreateForPage(page); } +FederatedAuthRequestImpl* FedCmHandler::GetFederatedAuthRequest() { + FederatedAuthRequestPageData* page_data = GetPageData(); + if (!page_data) { + return nullptr; + } + return page_data->PendingWebIdentityRequest(); +} + } // namespace content::protocol
diff --git a/content/browser/devtools/protocol/fedcm_handler.h b/content/browser/devtools/protocol/fedcm_handler.h index 1c24e0a..da03173 100644 --- a/content/browser/devtools/protocol/fedcm_handler.h +++ b/content/browser/devtools/protocol/fedcm_handler.h
@@ -13,6 +13,7 @@ #include "content/common/content_export.h" namespace content { +class FederatedAuthRequestImpl; class FederatedAuthRequestPageData; } // namespace content @@ -45,6 +46,7 @@ DispatchResponse Disable() override; FederatedAuthRequestPageData* GetPageData(); + FederatedAuthRequestImpl* GetFederatedAuthRequest(); RenderFrameHostImpl* frame_host_ = nullptr; std::unique_ptr<FedCm::Frontend> frontend_;
diff --git a/content/browser/gpu/browser_child_process_backgrounded_bridge.h b/content/browser/gpu/browser_child_process_backgrounded_bridge.h new file mode 100644 index 0000000..3bbf76be --- /dev/null +++ b/content/browser/gpu/browser_child_process_backgrounded_bridge.h
@@ -0,0 +1,55 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_GPU_BROWSER_CHILD_PROCESS_BACKGROUNDED_BRIDGE_H_ +#define CONTENT_BROWSER_GPU_BROWSER_CHILD_PROCESS_BACKGROUNDED_BRIDGE_H_ + +#include <objc/objc.h> + +#include "base/memory/raw_ptr.h" +#include "base/process/port_provider_mac.h" +#include "base/scoped_observation.h" +#include "content/common/content_export.h" +#include "third_party/abseil-cpp/absl/types/optional.h" + +namespace content { + +class BrowserChildProcessHostImpl; + +// This class ensures that the backgrounded state of `process` mirrors the +// backgrounded state of the browser process. +class CONTENT_EXPORT BrowserChildProcessBackgroundedBridge + : public base::PortProvider::Observer { + public: + explicit BrowserChildProcessBackgroundedBridge( + BrowserChildProcessHostImpl* process); + ~BrowserChildProcessBackgroundedBridge() override; + + void SimulateBrowserProcessForegroundedForTesting(); + void SimulateBrowserProcessBackgroundedForTesting(); + + static void SetOSNotificationsEnabledForTesting(bool enabled); + + private: + void Initialize(); + + void OnReceivedTaskPort(base::ProcessHandle process) override; + + void OnBrowserProcessForegrounded(); + void OnBrowserProcessBackgrounded(); + + raw_ptr<BrowserChildProcessHostImpl> process_; + + base::ScopedObservation<base::PortProvider, base::PortProvider::Observer> + scoped_port_provider_observer_{this}; + + // Registration IDs for NSApplicationDidBecomeActiveNotification and + // NSApplicationDidResignActiveNotification. + id did_become_active_observer_ = nil; + id did_resign_active_observer_ = nil; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_GPU_BROWSER_CHILD_PROCESS_BACKGROUNDED_BRIDGE_H_
diff --git a/content/browser/gpu/browser_child_process_backgrounded_bridge.mm b/content/browser/gpu/browser_child_process_backgrounded_bridge.mm new file mode 100644 index 0000000..f4f3a52 --- /dev/null +++ b/content/browser/gpu/browser_child_process_backgrounded_bridge.mm
@@ -0,0 +1,117 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/gpu/browser_child_process_backgrounded_bridge.h" + +#import <AppKit/AppKit.h> +#import <Foundation/Foundation.h> + +#include "base/process/process.h" +#include "content/browser/browser_child_process_host_impl.h" +#include "content/public/browser/child_process_data.h" + +namespace content { + +namespace { + +bool g_notifications_enabled = true; + +} // namespace + +BrowserChildProcessBackgroundedBridge::BrowserChildProcessBackgroundedBridge( + BrowserChildProcessHostImpl* process) + : process_(process) { + base::PortProvider* port_provider = + BrowserChildProcessHost::GetPortProvider(); + if (port_provider->TaskForPid(process_->GetData().GetProcess().Pid()) != + MACH_PORT_NULL) { + Initialize(); + } else { + // The process has launched but the task port is not available yet. Defer + // initialization until it is. + scoped_port_provider_observer_.Observe(port_provider); + } +} + +BrowserChildProcessBackgroundedBridge:: + ~BrowserChildProcessBackgroundedBridge() { + if (did_become_active_observer_) { + [NSNotificationCenter.defaultCenter + removeObserver:did_become_active_observer_]; + } + if (did_resign_active_observer_) { + [NSNotificationCenter.defaultCenter + removeObserver:did_resign_active_observer_]; + } +} + +void BrowserChildProcessBackgroundedBridge:: + SimulateBrowserProcessForegroundedForTesting() { + OnBrowserProcessForegrounded(); +} + +void BrowserChildProcessBackgroundedBridge:: + SimulateBrowserProcessBackgroundedForTesting() { + OnBrowserProcessBackgrounded(); +} + +// static +void BrowserChildProcessBackgroundedBridge::SetOSNotificationsEnabledForTesting( + bool enabled) { + g_notifications_enabled = enabled; +} + +void BrowserChildProcessBackgroundedBridge::Initialize() { + // Do the initial ajustment based on the initial value of the + // TASK_CATEGORY_POLICY role of the browser process. + base::SelfPortProvider self_port_provider; + process_->SetProcessBackgrounded( + base::Process::Current().IsProcessBackgrounded(&self_port_provider)); + + if (!g_notifications_enabled) { + return; + } + + // Now subscribe to both NSApplicationDidBecomeActiveNotification and + // NSApplicationDidResignActiveNotification, which are sent when the browser + // process becomes foreground and background, respectively. The blocks + // implicity captures `this`. It is safe to do so since the subscriptions are + // removed in the destructor + did_become_active_observer_ = [NSNotificationCenter.defaultCenter + addObserverForName:NSApplicationDidBecomeActiveNotification + object:nil + queue:nil + usingBlock:^(NSNotification* notification) { + OnBrowserProcessForegrounded(); + }]; + did_resign_active_observer_ = [NSNotificationCenter.defaultCenter + addObserverForName:NSApplicationDidResignActiveNotification + object:nil + queue:nil + usingBlock:^(NSNotification* notification) { + OnBrowserProcessBackgrounded(); + }]; +} + +void BrowserChildProcessBackgroundedBridge::OnReceivedTaskPort( + base::ProcessHandle process_handle) { + if (process_->GetData().GetProcess().Handle() != process_handle) { + return; + } + + // Just received the task port for the target process. It is now possible to + // change its TASK_CATEGORY_POLICY. + scoped_port_provider_observer_.Reset(); + Initialize(); +} + +void BrowserChildProcessBackgroundedBridge::OnBrowserProcessForegrounded() { + process_->SetProcessBackgrounded(false); +} + +void BrowserChildProcessBackgroundedBridge::OnBrowserProcessBackgrounded() { + process_->SetProcessBackgrounded(true); +} + +} // namespace content
diff --git a/content/browser/gpu/browser_child_process_backgrounded_bridge_browsertest.mm b/content/browser/gpu/browser_child_process_backgrounded_bridge_browsertest.mm new file mode 100644 index 0000000..554148d --- /dev/null +++ b/content/browser/gpu/browser_child_process_backgrounded_bridge_browsertest.mm
@@ -0,0 +1,156 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/gpu/browser_child_process_backgrounded_bridge.h" + +#include "base/process/process.h" +#include "base/test/scoped_feature_list.h" +#include "content/browser/gpu/gpu_process_host.h" +#include "content/public/browser/browser_child_process_host.h" +#include "content/public/browser/child_process_launcher_utils.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/content_browser_test.h" +#include "gpu/config/gpu_finch_features.h" + +namespace content { + +bool IsProcessBackgrounded(base::ProcessId pid) { + base::Process process = base::Process::Open(pid); + if (process.is_current()) { + base::SelfPortProvider self_port_provider; + return process.IsProcessBackgrounded(&self_port_provider); + } + + return process.IsProcessBackgrounded( + content::BrowserChildProcessHost::GetPortProvider()); +} + +void SetProcessBackgrounded(base::ProcessId pid, bool backgrounded) { + base::Process process = base::Process::Open(pid); + if (process.is_current()) { + base::SelfPortProvider self_port_provider; + process.SetProcessBackgrounded(&self_port_provider, backgrounded); + return; + } + + process.SetProcessBackgrounded( + content::BrowserChildProcessHost::GetPortProvider(), backgrounded); +} + +class BrowserChildProcessBackgroundedBridgeTest + : public content::ContentBrowserTest, + public base::PortProvider::Observer, + public testing::WithParamInterface<bool> { + public: + void SetUp() override { + scoped_feature_list_.InitAndEnableFeature( + features::kAdjustGpuProcessPriority); + content::BrowserChildProcessBackgroundedBridge:: + SetOSNotificationsEnabledForTesting(false); + content::ContentBrowserTest::SetUp(); + } + + void TearDown() override { + content::ContentBrowserTest::TearDown(); + content::BrowserChildProcessBackgroundedBridge:: + SetOSNotificationsEnabledForTesting(true); + } + + // Waits until the port for the GPU process is available. + void WaitForPort() { + auto* port_provider = content::BrowserChildProcessHost::GetPortProvider(); + DCHECK(port_provider->TaskForPid( + content::GpuProcessHost::Get()->process_id()) == MACH_PORT_NULL); + port_provider->AddObserver(this); + base::RunLoop run_loop; + + quit_closure_ = run_loop.QuitClosure(); + run_loop.Run(); + } + + void EnsureBackgroundedStateChange() { + // Do a round-trip to the process launcher task to ensure any queued task is + // run. + base::RunLoop run_loop; + GetProcessLauncherTaskRunner()->PostTaskAndReply( + FROM_HERE, base::DoNothing(), run_loop.QuitClosure()); + run_loop.Run(); + } + + private: + void OnReceivedTaskPort(base::ProcessHandle process_handle) override { + if (process_handle != content::GpuProcessHost::Get()->process_id()) { + return; + } + + content::BrowserChildProcessHost::GetPortProvider()->RemoveObserver(this); + std::move(quit_closure_).Run(); + } + + base::test::ScopedFeatureList scoped_feature_list_; + + base::OnceClosure quit_closure_; +}; + +IN_PROC_BROWSER_TEST_F(BrowserChildProcessBackgroundedBridgeTest, + InitiallyForegrounded) { + // Set the browser process as foregrounded. + SetProcessBackgrounded(base::Process::Current().Pid(), false); + + // Wait until we receive the port for the GPU process. + WaitForPort(); + + // Ensure that the initial backgrounded state changed. + EnsureBackgroundedStateChange(); + + auto* gpu_process_host = content::GpuProcessHost::Get(); + EXPECT_TRUE(gpu_process_host); + EXPECT_FALSE(IsProcessBackgrounded(gpu_process_host->process_id())); +} + +IN_PROC_BROWSER_TEST_F(BrowserChildProcessBackgroundedBridgeTest, + InitiallyBackgrounded) { + // Set the browser process as backgrounded. + SetProcessBackgrounded(base::Process::Current().Pid(), true); + + // Wait until we receive the port for the GPU process. + WaitForPort(); + + // Ensure that the initial backgrounded state changed. + EnsureBackgroundedStateChange(); + + auto* gpu_process_host = content::GpuProcessHost::Get(); + EXPECT_TRUE(gpu_process_host); + EXPECT_TRUE(IsProcessBackgrounded(gpu_process_host->process_id())); +} + +IN_PROC_BROWSER_TEST_F(BrowserChildProcessBackgroundedBridgeTest, + OnBackgroundedStateChanged) { + // Wait until we receive the port for the GPU process. + WaitForPort(); + + auto* gpu_process_host = content::GpuProcessHost::Get(); + EXPECT_TRUE(gpu_process_host); + + auto* bridge = + gpu_process_host->browser_child_process_backgrounded_bridge_for_testing(); + ASSERT_TRUE(bridge); + + bridge->SimulateBrowserProcessForegroundedForTesting(); + EnsureBackgroundedStateChange(); + + EXPECT_FALSE(IsProcessBackgrounded(gpu_process_host->process_id())); + + bridge->SimulateBrowserProcessBackgroundedForTesting(); + EnsureBackgroundedStateChange(); + + EXPECT_TRUE(IsProcessBackgrounded(gpu_process_host->process_id())); + + bridge->SimulateBrowserProcessForegroundedForTesting(); + EnsureBackgroundedStateChange(); + + EXPECT_FALSE(IsProcessBackgrounded(gpu_process_host->process_id())); +} + +} // namespace content \ No newline at end of file
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc index 04f216e4..c674795c 100644 --- a/content/browser/gpu/gpu_process_host.cc +++ b/content/browser/gpu/gpu_process_host.cc
@@ -112,6 +112,7 @@ #endif #if BUILDFLAG(IS_MAC) +#include "content/browser/gpu/browser_child_process_backgrounded_bridge.h" #include "content/browser/gpu/ca_transaction_gpu_coordinator.h" #endif @@ -928,6 +929,14 @@ process_id_ = process_->GetProcess().Pid(); DCHECK_NE(base::kNullProcessId, process_id_); gpu_host_->SetProcessId(process_id_); + +#if BUILDFLAG(IS_MAC) + if (base::FeatureList::IsEnabled(features::kAdjustGpuProcessPriority)) { + browser_child_process_backgrounded_bridge_ = + std::make_unique<BrowserChildProcessBackgroundedBridge>( + process_.get()); + } +#endif } }
diff --git a/content/browser/gpu/gpu_process_host.h b/content/browser/gpu/gpu_process_host.h index 5e17fdf0..d395951 100644 --- a/content/browser/gpu/gpu_process_host.h +++ b/content/browser/gpu/gpu_process_host.h
@@ -51,6 +51,7 @@ class BrowserChildProcessHostImpl; #if BUILDFLAG(IS_MAC) +class BrowserChildProcessBackgroundedBridge; class CATransactionGPUCoordinator; #endif @@ -128,6 +129,13 @@ viz::GpuHostImpl* gpu_host() { return gpu_host_.get(); } +#if BUILDFLAG(IS_MAC) + BrowserChildProcessBackgroundedBridge* + browser_child_process_backgrounded_bridge_for_testing() { + return browser_child_process_backgrounded_bridge_.get(); + } +#endif + private: enum class GpuTerminationOrigin { kUnknownOrigin = 0, @@ -254,6 +262,11 @@ #if BUILDFLAG(IS_MAC) scoped_refptr<CATransactionGPUCoordinator> ca_transaction_gpu_coordinator_; + + // Ensures the backgrounded state of the GPU process mirrors the backgrounded + // state of the browser process. + std::unique_ptr<BrowserChildProcessBackgroundedBridge> + browser_child_process_backgrounded_bridge_; #endif // Track the URLs of the pages which have live offscreen contexts,
diff --git a/content/browser/renderer_host/input/touch_input_browsertest.cc b/content/browser/renderer_host/input/touch_input_browsertest.cc index d1232354..44ad4f5 100644 --- a/content/browser/renderer_host/input/touch_input_browsertest.cc +++ b/content/browser/renderer_host/input/touch_input_browsertest.cc
@@ -143,11 +143,19 @@ filter->WaitForAck()); // The same is true for release because there is no touch-end handler. + // + // TODO(https://crbug.com/1417126): a suspicious flake in AR/XR tests forced + // us to disable `kDroppedTouchSequenceIncludesTouchEnd` then override the + // expectation here! filter = AddFilter(WebInputEvent::Type::kTouchEnd); touch.ReleasePoint(0); SendTouchEvent(&touch); - EXPECT_EQ(blink::mojom::InputEventResultState::kNoConsumerExists, - filter->WaitForAck()); + blink::mojom::InputEventResultState expected_touchend_result = + base::FeatureList::IsEnabled( + blink::features::kDroppedTouchSequenceIncludesTouchEnd) + ? blink::mojom::InputEventResultState::kNoConsumerExists + : blink::mojom::InputEventResultState::kNotConsumed; + EXPECT_EQ(expected_touchend_result, filter->WaitForAck()); } IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, TouchStartNoConsume) {
diff --git a/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc b/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc index 5348a1fe..54ee4dd 100644 --- a/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc +++ b/content/browser/renderer_host/input/touch_selection_controller_client_aura.cc
@@ -22,7 +22,6 @@ #include "ui/base/clipboard/clipboard.h" #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" #include "ui/base/pointer/touch_editing_controller.h" -#include "ui/base/ui_base_features.h" #include "ui/events/event_observer.h" #include "ui/gfx/geometry/point_conversions.h" #include "ui/gfx/geometry/size_conversions.h"
diff --git a/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc b/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc index 178c4ff..2944a24 100644 --- a/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc +++ b/content/browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc
@@ -12,9 +12,7 @@ #include "base/memory/raw_ptr.h" #include "base/run_loop.h" #include "base/task/single_thread_task_runner.h" -#include "base/test/scoped_feature_list.h" #include "base/test/test_timeouts.h" -#include "build/chromeos_buildflags.h" #include "content/browser/renderer_host/render_widget_host_view_aura.h" #include "content/browser/renderer_host/render_widget_host_view_child_frame.h" #include "content/browser/renderer_host/render_widget_host_view_event_handler.h" @@ -35,7 +33,6 @@ #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" #include "ui/base/pointer/touch_editing_controller.h" -#include "ui/base/ui_base_features.h" #include "ui/display/display_switches.h" #include "ui/events/event_sink.h" #include "ui/events/event_utils.h" @@ -176,12 +173,7 @@ class TouchSelectionControllerClientAuraTest : public ContentBrowserTest { public: - TouchSelectionControllerClientAuraTest() { -#if BUILDFLAG(IS_CHROMEOS) - scoped_feature_list_.InitAndEnableFeature( - features::kTouchTextEditingRedesign); -#endif // BUILDFLAG(IS_CHROMEOS) - } + TouchSelectionControllerClientAuraTest() = default; TouchSelectionControllerClientAuraTest( const TouchSelectionControllerClientAuraTest&) = delete; @@ -261,8 +253,6 @@ raw_ptr<TestTouchSelectionControllerClientAura> selection_controller_client_ = nullptr; - - base::test::ScopedFeatureList scoped_feature_list_; }; class TouchSelectionControllerClientAuraCAPFeatureTest @@ -948,7 +938,6 @@ rwhva->selection_controller()->GetVisibleRectBetweenBounds()); } -#if BUILDFLAG(IS_CHROMEOS) // Tests that the select all command in the quick menu works correctly and that // the touch handles and quick menu are shown after the command is executed. IN_PROC_BROWSER_TEST_P(TouchSelectionControllerClientAuraCAPFeatureTest, @@ -1042,7 +1031,6 @@ selection_controller_client()->GetActiveMenuClient()->GetSelectedText(), u"Text"); } -#endif class TouchSelectionControllerClientAuraScaleFactorTest : public TouchSelectionControllerClientAuraTest {
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc index d701c92..ebf9539 100644 --- a/content/browser/renderer_host/render_widget_host_unittest.cc +++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -90,7 +90,7 @@ #include "content/browser/renderer_host/test_render_widget_host_view_ios_factory.h" #endif -#if defined(USE_AURA) || BUILDFLAG(IS_MAC) +#if defined(USE_AURA) || BUILDFLAG(IS_APPLE) #include "content/browser/compositor/test/test_image_transport_factory.h" #endif @@ -566,7 +566,7 @@ site_instance_group_ = base::WrapRefCounted(new SiteInstanceGroup( SiteInstanceImpl::NextBrowsingInstanceId(), process_.get())); sink_ = &process_->sink(); -#if defined(USE_AURA) || BUILDFLAG(IS_MAC) +#if defined(USE_AURA) || BUILDFLAG(IS_APPLE) ImageTransportFactory::SetFactory( std::make_unique<TestImageTransportFactory>()); #endif @@ -637,14 +637,13 @@ site_instance_group_.reset(); process_.reset(); browser_context_.reset(); -#if defined(USE_AURA) || BUILDFLAG(IS_MAC) +#if defined(USE_AURA) || BUILDFLAG(IS_APPLE) ImageTransportFactory::Terminate(); +#endif +#if defined(USE_AURA) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID) display::Screen::SetScreenInstance(nullptr); screen_.reset(); #endif -#if BUILDFLAG(IS_ANDROID) - display::Screen::SetScreenInstance(nullptr); -#endif // Process all pending tasks to avoid leaks. base::RunLoop().RunUntilIdle();
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index 4cd6bcb..abf1c83d 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -2627,12 +2627,8 @@ ui::GestureConfiguration::GetInstance()->long_press_time_in_ms()); tsc_config.tap_slop = ui::GestureConfiguration::GetInstance() ->max_touch_move_in_pixels_for_click(); -#if BUILDFLAG(IS_CHROMEOS) tsc_config.enable_longpress_drag_selection = features::IsTouchTextEditingRedesignEnabled(); -#else - tsc_config.enable_longpress_drag_selection = false; -#endif selection_controller_ = std::make_unique<ui::TouchSelectionController>( selection_controller_client_.get(), tsc_config); }
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc index d2ec41d..5b85abe 100644 --- a/content/browser/webid/federated_auth_request_impl.cc +++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -946,13 +946,14 @@ } } + DCHECK(idp_data_for_display_.empty()); + // TODO(crbug.com/1383384): Handle auto_reauthn for multi IDP. bool idp_enabled_auto_reauthn = true; - std::vector<IdentityProviderData> idp_data_for_display; for (const auto& idp : idp_order_) { auto idp_info_it = idp_infos_.find(idp); if (idp_info_it != idp_infos_.end() && idp_info_it->second->data) { - idp_data_for_display.push_back(*idp_info_it->second->data); + idp_data_for_display_.push_back(*idp_info_it->second->data); idp_enabled_auto_reauthn &= idp_info_it->second->auto_reauthn; } } @@ -1004,7 +1005,7 @@ IdentityRequestAccount account{*auto_reauthn_account}; IdentityProviderData idp{*auto_reauthn_idp}; idp.accounts = {account}; - idp_data_for_display = {idp}; + idp_data_for_display_ = {idp}; } // TODO(crbug.com/1408520): opt-out affordance is not included in the origin @@ -1030,7 +1031,7 @@ // IDPs are failing in the multi IDP case. request_dialog_controller_->ShowAccountsDialog( WebContents::FromRenderFrameHost(&render_frame_host()), - top_frame_url_for_display, iframe_url_for_display, idp_data_for_display, + top_frame_url_for_display, iframe_url_for_display, idp_data_for_display_, auto_reauthn ? SignInMode::kAuto : SignInMode::kExplicit, show_auto_reauthn_checkbox, base::BindOnce(&FederatedAuthRequestImpl::OnAccountSelected, @@ -1580,6 +1581,7 @@ select_account_time_ = base::TimeTicks(); token_response_time_ = base::TimeTicks(); idp_infos_.clear(); + idp_data_for_display_.clear(); fetch_data_ = FetchData(); idp_order_.clear(); metrics_endpoints_.clear();
diff --git a/content/browser/webid/federated_auth_request_impl.h b/content/browser/webid/federated_auth_request_impl.h index 7b664f6a..07e8336 100644 --- a/content/browser/webid/federated_auth_request_impl.h +++ b/content/browser/webid/federated_auth_request_impl.h
@@ -87,11 +87,6 @@ // Rejects the pending request if it has not been resolved naturally yet. void OnRejectRequest(); - // For use by the devtools protocol for browser automation. - IdentityRequestDialogController* GetDialogController() { - return request_dialog_controller_.get(); - } - struct IdentityProviderGetInfo { IdentityProviderGetInfo(blink::mojom::IdentityProviderConfigPtr, bool auto_reauthn, @@ -123,6 +118,15 @@ absl::optional<IdentityProviderData> data; }; + // For use by the devtools protocol for browser automation. + IdentityRequestDialogController* GetDialogController() { + return request_dialog_controller_.get(); + } + + const std::vector<IdentityProviderData>& GetSortedIdpData() const { + return idp_data_for_display_; + } + private: friend class FederatedAuthRequestImplTest; @@ -293,6 +297,7 @@ // Populated by MaybeShowAccountsDialog(). base::flat_map<GURL, std::unique_ptr<IdentityProviderInfo>> idp_infos_; + std::vector<IdentityProviderData> idp_data_for_display_; raw_ptr<FederatedIdentityApiPermissionContextDelegate> api_permission_delegate_ = nullptr;
diff --git a/content/public/renderer/render_thread.cc b/content/public/renderer/render_thread.cc index b34be7e..e43b7ed1 100644 --- a/content/public/renderer/render_thread.cc +++ b/content/public/renderer/render_thread.cc
@@ -6,7 +6,7 @@ #include "base/no_destructor.h" #include "base/threading/thread_checker_impl.h" -#include "base/threading/thread_local.h" +#include "third_party/abseil-cpp/absl/base/attributes.h" namespace content { @@ -14,10 +14,7 @@ // Keep the global RenderThread in a TLS slot so it is impossible to access // incorrectly from the wrong thread. -base::ThreadLocalPointer<RenderThread>& GetRenderThreadLocalPointer() { - static base::NoDestructor<base::ThreadLocalPointer<RenderThread>> tls; - return *tls; -} +ABSL_CONST_INIT thread_local RenderThread* render_thread = nullptr; static const base::ThreadCheckerImpl& GetThreadChecker() { static base::NoDestructor<base::ThreadCheckerImpl> checker; @@ -27,7 +24,7 @@ } // namespace RenderThread* RenderThread::Get() { - return GetRenderThreadLocalPointer().Get(); + return render_thread; } bool RenderThread::IsMainThread() { @@ -35,12 +32,8 @@ return GetThreadChecker().CalledOnValidThread(); } -RenderThread::RenderThread() { - GetRenderThreadLocalPointer().Set(this); -} +RenderThread::RenderThread() : resetter_(&render_thread, this) {} -RenderThread::~RenderThread() { - GetRenderThreadLocalPointer().Set(nullptr); -} +RenderThread::~RenderThread() = default; } // namespace content
diff --git a/content/public/renderer/render_thread.h b/content/public/renderer/render_thread.h index 254a8884..c4373348 100644 --- a/content/public/renderer/render_thread.h +++ b/content/public/renderer/render_thread.h
@@ -9,6 +9,7 @@ #include <stdint.h> #include <memory> +#include "base/auto_reset.h" #include "base/functional/callback_forward.h" #include "base/memory/scoped_refptr.h" #include "base/task/single_thread_task_runner.h" @@ -117,6 +118,9 @@ // https://github.com/WICG/attribution-reporting-api/blob/main/app_to_web.md. virtual attribution_reporting::mojom::OsSupport GetOsSupportForAttributionReporting() = 0; + + private: + const base::AutoReset<RenderThread*> resetter_; }; } // namespace content
diff --git a/content/public/test/network_service_test_helper.cc b/content/public/test/network_service_test_helper.cc index 215dc4fb6..9e0c1c8 100644 --- a/content/public/test/network_service_test_helper.cc +++ b/content/public/test/network_service_test_helper.cc
@@ -19,6 +19,7 @@ #include "base/task/current_thread.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" +#include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h"
diff --git a/content/public/utility/utility_thread.cc b/content/public/utility/utility_thread.cc index cc26ab9..881ff6f 100644 --- a/content/public/utility/utility_thread.cc +++ b/content/public/utility/utility_thread.cc
@@ -4,27 +4,25 @@ #include "content/public/utility/utility_thread.h" -#include "base/lazy_instance.h" -#include "base/threading/thread_local.h" +#include "third_party/abseil-cpp/absl/base/attributes.h" namespace content { +namespace { + // Keep the global UtilityThread in a TLS slot so it is impossible to access // incorrectly from the wrong thread. -static base::LazyInstance<base::ThreadLocalPointer<UtilityThread>>::Leaky - lazy_tls = LAZY_INSTANCE_INITIALIZER; +ABSL_CONST_INIT thread_local UtilityThread* utility_thread = nullptr; + +} // namespace UtilityThread* UtilityThread::Get() { - return lazy_tls.Pointer()->Get(); + return utility_thread; } -UtilityThread::UtilityThread() { - lazy_tls.Pointer()->Set(this); -} +UtilityThread::UtilityThread() : resetter_(&utility_thread, this) {} -UtilityThread::~UtilityThread() { - lazy_tls.Pointer()->Set(nullptr); -} +UtilityThread::~UtilityThread() = default; } // namespace content
diff --git a/content/public/utility/utility_thread.h b/content/public/utility/utility_thread.h index 491cd08..9d0934b 100644 --- a/content/public/utility/utility_thread.h +++ b/content/public/utility/utility_thread.h
@@ -5,6 +5,7 @@ #ifndef CONTENT_PUBLIC_UTILITY_UTILITY_THREAD_H_ #define CONTENT_PUBLIC_UTILITY_UTILITY_THREAD_H_ +#include "base/auto_reset.h" #include "build/build_config.h" #include "content/common/content_export.h" #include "content/public/child/child_thread.h" @@ -30,6 +31,9 @@ // Initializes blink with web sandbox support. virtual void EnsureBlinkInitializedWithSandboxSupport() = 0; #endif + + private: + const base::AutoReset<UtilityThread*> resetter_; }; } // namespace content
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index e1357e0..540caba0 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -1967,6 +1967,7 @@ "../browser/accessibility/hit_testing_mac_browsertest.mm", "../browser/form_controls_browsertest_mac.h", "../browser/form_controls_browsertest_mac.mm", + "../browser/gpu/browser_child_process_backgrounded_bridge_browsertest.mm", "../browser/keyboard_lock_browsertest_mac.mm", "../browser/pointer_lock_browsertest_mac.mm", "../browser/renderer_host/render_frame_host_impl_mac_browsertest.mm",
diff --git a/content/test/data/browsing_data/media_license.js b/content/test/data/browsing_data/media_license.js index 535bec3a..80d3cd7 100644 --- a/content/test/data/browsing_data/media_license.js +++ b/content/test/data/browsing_data/media_license.js
@@ -37,32 +37,37 @@ '{"keys":[{"kty":"oct","k":"tQ0bJVWb6b0KPL6KtZIy_A","kid":"LwVHf8JLtPrv2GUXFW2v_A"}],"type":"persistent-license"}'); savedSessionId = session.sessionId; - session.update(license).then(success_, failure_); + return session.update(license).then(() => true, () => false); } -function setMediaLicense() { +async function setMediaLicense() { var te = new TextEncoder(); var initData = te.encode('{"kids":["LwVHf8JLtPrv2GUXFW2v_A"]}'); - createPersistentSession() - .then(function(session) { - // generateRequest() will trigger a 'message' event, which we need to - // wait for in order to call update() which provides the license. - session.addEventListener('message', handleMessageEvent, false); - return session.generateRequest('keyids', initData); - }) - // Success is reported from handleMessageEvent(). - .catch(failure_); + try { + const session = await createPersistentSession(); + // generateRequest() will trigger a 'message' event, which we need to + // wait for in order to call update() which provides the license. + const handled = new Promise((resolve, reject) => { + session.addEventListener('message', (e) => { + handleMessageEvent(e).then(resolve, reject) + }, false); + }); + await session.generateRequest('keyids', initData); + await handled; + return true; + } catch { + return false; + } } -function hasMediaLicense() { - createPersistentSession() - .then(function(session) { - return session.load(savedSessionId); - }) - .then(function(result) { - // |result| is a boolean, indicating if the session was loaded or not. - domAutomationController.send(result); - }) - .catch(failure_); +async function hasMediaLicense() { + try { + const session = await createPersistentSession(); + const result = await session.load(savedSessionId); + // |result| is a boolean, indicating if the session was loaded or not. + return result; + }catch { + return false; + } } \ No newline at end of file
diff --git a/content/test/data/browsing_data/site_data.html b/content/test/data/browsing_data/site_data.html index a1a15b1..33c77051 100644 --- a/content/test/data/browsing_data/site_data.html +++ b/content/test/data/browsing_data/site_data.html
@@ -2,14 +2,6 @@ <script src="media_license.js"></script> <script> - function success_() { - domAutomationController.send(true); - } - - function failure_() { - domAutomationController.send(false); - } - function isHttps_() { return location.protocol === 'https:'; } @@ -17,108 +9,158 @@ function setCookie() { const samesite_none_secure = '; SameSite=None; Secure'; document.cookie = 'foo=bar; Max-Age=1000' + (isHttps_() ? samesite_none_secure : ''); - success_(); + return true; + } + function setCookieAsync() { + // Provided for Java. + domAutomationController.send(setCookie()); } function hasCookie() { - domAutomationController.send(document.cookie.indexOf('foo=bar') != -1); + return document.cookie.indexOf('foo=bar') != -1; + } + function hasCookieAsync() { + // Provided for Java. + domAutomationController.send(hasCookie()); } function setSessionCookie() { const samesite_none_secure = '; SameSite=None; Secure'; document.cookie = 'bar=session' + (isHttps_() ? samesite_none_secure : ''); - success_(); + return true; } function hasSessionCookie() { - domAutomationController.send(document.cookie == 'bar=session'); + return document.cookie == 'bar=session'; } function setCookieStore() { - cookieStore.set({ + return cookieStore.set({ name: 'cookie-name', value: 'cookie-value', sameSite: 'none' - }).then(success_).catch(failure_); + }) + .then(() => true) + .catch(() => false); } function hasCookieStore() { - cookieStore.get('cookie-name').then(function (cookie) { - domAutomationController.send(cookie.value == 'cookie-value'); - }).catch(failure_); + return cookieStore.get('cookie-name') + .then((cookie) => cookie.value == 'cookie-value') + .catch(() => false); } function setLocalStorage() { localStorage.setItem('foo', 'bar'); - success_(); + return true; + } + function setLocalStorageAsync() { + // Provided for Java. + domAutomationController.send(setLocalStorage()); } function hasLocalStorage() { try { - domAutomationController.send(localStorage.getItem('foo') == 'bar'); + return localStorage.getItem('foo') == 'bar'; } catch (e) { - failure_(); + return false; } } + function hasLocalStorageAsync() { + // Provided for Java. + domAutomationController.send(hasLocalStorage()); + } function setSessionStorage() { sessionStorage.setItem('foo', 'bar'); - success_(); + return true; } function hasSessionStorage() { try { - domAutomationController.send(sessionStorage.getItem('foo') == 'bar'); + return sessionStorage.getItem('foo') == 'bar'; } catch (e) { - failure_(); + return false; } } function setServiceWorker() { - navigator.serviceWorker.register('empty_worker.js').then(function() { - navigator.serviceWorker.ready.then(success_); - }).catch(failure_); + return navigator.serviceWorker.register('empty_worker.js') + .then(() => navigator.serviceWorker.ready) + .then(() => true) + .catch(() => false); + } + async function setServiceWorkerAsync() { + // Provided for Java. + domAutomationController.send(await setServiceWorker()); } function hasServiceWorker() { - navigator.serviceWorker.getRegistrations().then(function (registrations) { - domAutomationController.send(registrations.length > 0); - }).catch(failure_); + return navigator.serviceWorker.getRegistrations() + .then((registrations) => registrations.length > 0) + .catch(() => false); + } + async function hasServiceWorkerAsync() { + // Provided for Java. + domAutomationController.send(await hasServiceWorker()); } function setCacheStorage() { - caches.open("cache").then(function (cache) { - cache.put("/foo", new Response("bar")).then(success_); - }).catch(failure_); + return caches.open("cache") + .then((cache) => cache.put("/foo", new Response("bar"))) + .then(() => true) + .catch(() => false); + } + async function setCacheStorageAsync() { + // Provided for Java. + domAutomationController.send(await setCacheStorage()); } function hasCacheStorage() { - caches.open("cache").then(function (cache) { - cache.keys().then(function (keys) { - domAutomationController.send(keys.length > 0); - }); - }).catch(failure_); + return caches.open("cache") + .then((cache) => cache.keys()) + .then((keys) => keys.length > 0) + .catch(() => false); + } + async function hasCacheStorageAsync() { + // Provided for Java. + domAutomationController.send(await hasCacheStorage()); } - function openFile_(name, options, callback, error) { - window.webkitRequestFileSystem(TEMPORARY, 1024, function (fs) { - fs.root.getFile(name, options, callback, error); - }, error); + async function openFile_(name, options, callback, error) { + const fs = await new Promise((resolve, reject) => { + window.webkitRequestFileSystem(TEMPORARY, 1024, resolve, reject); + }); + return new Promise((resolve, reject) => { + fs.root.getFile(name, options, resolve, reject); + }); } function setFileSystem() { - openFile_('foo.txt', { create: true, exclusive: true }, success_, failure_); + return openFile_('foo.txt', { create: true, exclusive: true }) + .then(() => true) + .catch(() => false); + } + async function setFileSystemAsync() { + // Provided for Java. + domAutomationController.send(await setFileSystem()); } function hasFileSystem() { - openFile_('foo.txt', { create: false }, success_, failure_); + return openFile_('foo.txt', { create: false }) + .then(() => true) + .catch(() => false); + } + async function hasFileSystemAsync() { + // Provided for Java. + domAutomationController.send(await hasFileSystem()); } async function setFileSystemAccess() { try { let dir = await navigator.storage.getDirectory(); await dir.getFileHandle('foo.txt', {create: true}); - success_(); + return true; } catch (e) { - failure_(); + return false; } } @@ -126,100 +168,126 @@ try { let dir = await navigator.storage.getDirectory(); await dir.getFileHandle('foo.txt', { create: false }); - success_(); + return true; } catch (e) { - failure_(); + return false; } } - function setWebSql() { + async function setWebSql() { try { var db = openDatabase('testdb', '1.0', 'a test db', 1024); - db.transaction(function (tx) { - tx.executeSql('CREATE TABLE IF NOT EXISTS foo (text)'); - tx.executeSql('INSERT INTO foo (text) VALUES ("bar")'); - }, failure_, success_); + await new Promise((resolve, reject) => { + db.transaction(function (tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS foo (text)'); + tx.executeSql('INSERT INTO foo (text) VALUES ("bar")'); + }, reject, resolve); + }); + return true; } catch (e) { - return failure_(); + return false; } } + async function setWebSqlAsync() { + // Provided for Java. + domAutomationController.send(await setWebSql()); + } - function hasWebSql() { + async function hasWebSql() { try { var db = openDatabase('testdb', '1.0', 'a test db', 1024); var num_results; - db.transaction(function (tx) { - tx.executeSql('CREATE TABLE IF NOT EXISTS foo (text)'); - tx.executeSql('SELECT * FROM foo', [], function (tx, results) { - num_results = results.rows.length; - }); - }, null, function() { domAutomationController.send(num_results > 0); }); + await new Promise((resolve, reject) => { + db.transaction(function (tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS foo (text)'); + tx.executeSql('SELECT * FROM foo', [], function (tx, results) { + num_results = results.rows.length; + }); + }, reject, resolve); + }); + return num_results > 0; } catch (e) { - return failure_(); + return false; } } + async function hasWebSqlAsync() { + // Provided for Java. + domAutomationController.send(await hasWebSql()); + } function setIndexedDb() { var open = indexedDB.open('db', 2); open.onupgradeneeded = function() { open.result.createObjectStore('store'); } - open.onsuccess = function() { - open.result.close(); - success_(); - } - open.onerror = failure_; + return new Promise((resolve) => { + open.onsuccess = function () { + open.result.close(); + resolve(true); + } + open.onerror = () => resolve(false); + }); + } + async function setIndexedDbAsync() { + domAutomationController.send(await setIndexedDb()); } function hasIndexedDb() { var open = indexedDB.open('db'); - open.onsuccess = function() { - var hasStore = open.result.objectStoreNames.contains('store'); - open.result.close(); - domAutomationController.send(hasStore); - } - open.onerror = failure_; + return new Promise((resolve) => { + open.onsuccess = function() { + var hasStore = open.result.objectStoreNames.contains('store'); + open.result.close(); + resolve(hasStore); + } + open.onerror = () => resolve(false); + }); + } + async function hasIndexedDbAsync() { + domAutomationController.send(await hasIndexedDb()); } function worker_(command) { let worker = new Worker("site_data_worker.js"); - worker.onmessage = e => { - domAutomationController.send(e.data); - }; - worker.postMessage(command) + return new Promise((resolve) => { + worker.onmessage = e => { + resolve(e.data); + }; + worker.postMessage(command); + }); } function hasWorkerFileSystemAccess() { - worker_("hasFileSystemAccess"); + return worker_("hasFileSystemAccess"); } function setWorkerFileSystemAccess() { - worker_("setFileSystemAccess"); + return worker_("setFileSystemAccess"); } function hasWorkerCacheStorage() { - worker_("hasCacheStorage"); + return worker_("hasCacheStorage"); } function setWorkerCacheStorage() { - worker_("setCacheStorage"); + return worker_("setCacheStorage"); } function hasWorkerIndexedDb() { - worker_("hasIndexedDb"); + return worker_("hasIndexedDb"); } function setWorkerIndexedDb() { - worker_("setIndexedDb"); + return worker_("setIndexedDb"); } function setHistory() { history.pushState({}, "foo"); - success_(); + return true; } function hasHistory() { - domAutomationController.send(history.length > 1); + return history.length > 1; } let sharedWorker; // Global variable to keep worker alive. @@ -233,39 +301,44 @@ } function setSharedWorker() { - connectSharedWorker().then(() => { + return connectSharedWorker() + .then(() => { sharedWorker.port.postMessage({ "value": "foo" }); - success_(); - }).catch(failure_); + return true; + }) + .catch(() => false); } async function hasSharedWorker() { - connectSharedWorker().then(() => { - sharedWorker.port.onmessage = e => { - domAutomationController.send(e.data.value === "foo"); - sharedWorker.port.onmessage = null; // Only send a single response via domAutomationController. - }; - sharedWorker.port.postMessage({}); - }).catch(failure_); + return connectSharedWorker() + .then(() => { + return new Promise((resolve) => { + sharedWorker.port.onmessage = e => { + resolve(e.data.value === "foo"); + }; + sharedWorker.port.postMessage({}); + }); + }) + .catch(() => false); } let lock; function setWebLock() { - navigator.locks.request("foo", l => { - lock = new Promise((res, rej) => { }); - // Now lock will be held while |lock| exists. - success_(); - return lock; - }).catch(failure_); + return new Promise((resolve) => { + navigator.locks.request("foo", () => { + lock = new Promise(() => { }); + // Now lock will be held while |lock| exists. + resolve(true); + return lock; + }) + }) + .catch(() => false); } function hasWebLock() { - navigator.locks.query().then(locks => { - if (locks.held.length) - domAutomationController.send(locks.held[0].name === "foo"); - else - failure_(); - }).catch(failure_); + return navigator.locks.query() + .then(locks => !!locks.held.length && locks.held[0].name === "foo") + .catch(() => false); } </script>
diff --git a/extensions/common/mojom/api_permission_id.mojom b/extensions/common/mojom/api_permission_id.mojom index 5dd0932..80da98e 100644 --- a/extensions/common/mojom/api_permission_id.mojom +++ b/extensions/common/mojom/api_permission_id.mojom
@@ -90,7 +90,7 @@ kDeprecated_ExternallyConnectableAllUrls = 63, kFeedbackPrivate = 64, kFileBrowserHandler = 65, - kFileBrowserHandlerInternal = 66, + kDeleted_FileBrowserHandlerInternal = 66, kFileManagerPrivate = 67, kFileSystem = 68, kFileSystemDirectory = 69,
diff --git a/extensions/common/permissions/permission_set.cc b/extensions/common/permissions/permission_set.cc index d063981..e20c0f9 100644 --- a/extensions/common/permissions/permission_set.cc +++ b/extensions/common/permissions/permission_set.cc
@@ -27,7 +27,6 @@ explicit_hosts_(std::move(explicit_hosts)), scriptable_hosts_(std::move(scriptable_hosts)) { CleanExplicitHostPaths(); - InitImplicitPermissions(); InitEffectiveHosts(); } @@ -215,9 +214,7 @@ void PermissionSet::SetAPIPermissions(APIPermissionSet new_apis) { apis_ = std::move(new_apis); // Since we're rewriting the API permissions, we need to re-initialize the - // value of whether to warn about all hosts and re-add any implicit API - // permissions. - InitImplicitPermissions(); + // value of whether to warn about all hosts. api_permissions_should_warn_all_hosts_ = UNINITIALIZED; } @@ -279,12 +276,6 @@ explicit_hosts_.AddPattern(std::move(pattern)); } -void PermissionSet::InitImplicitPermissions() { - // The fileBrowserHandler permission implies the internal version as well. - if (apis_.find(APIPermissionID::kFileBrowserHandler) != apis_.end()) - apis_.insert(APIPermissionID::kFileBrowserHandlerInternal); -} - void PermissionSet::InitEffectiveHosts() { effective_hosts_ = URLPatternSet::CreateUnion(explicit_hosts(), scriptable_hosts());
diff --git a/extensions/common/permissions/permission_set.h b/extensions/common/permissions/permission_set.h index a0fa0c8..47ec754 100644 --- a/extensions/common/permissions/permission_set.h +++ b/extensions/common/permissions/permission_set.h
@@ -138,9 +138,6 @@ // "/*", and we implicitly make this change. void CleanExplicitHostPaths(); - // Adds permissions implied independently of other context. - void InitImplicitPermissions(); - // Initializes the effective host permission based on the data in this set. void InitEffectiveHosts();
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc index 96c5e96e..e74db90f 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -2830,6 +2830,10 @@ GLenum binaryformat, const void* binary, GLsizei length) { +#if 1 // No binary shader support. + InsertError(GL_INVALID_ENUM, "Invalid enum."); + return error::kNoError; +#else std::vector<GLuint> service_shaders(n, 0); for (GLsizei i = 0; i < n; i++) { service_shaders[i] = GetShaderServiceID(shaders[i], resources_); @@ -2837,6 +2841,7 @@ api()->glShaderBinaryFn(n, service_shaders.data(), binaryformat, binary, length); return error::kNoError; +#endif } error::Error GLES2DecoderPassthroughImpl::DoShaderSource(GLuint shader,
diff --git a/gpu/config/gpu_finch_features.cc b/gpu/config/gpu_finch_features.cc index 330aeb2..a19185d 100644 --- a/gpu/config/gpu_finch_features.cc +++ b/gpu/config/gpu_finch_features.cc
@@ -188,6 +188,12 @@ #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_IOS) // Enable use of Metal for OOP rasterization. BASE_FEATURE(kMetal, "Metal", base::FEATURE_DISABLED_BY_DEFAULT); + +// If enabled, the TASK_CATEGORY_POLICY value of the GPU process will be +// adjusted to match the one from the browser process every time it changes. +BASE_FEATURE(kAdjustGpuProcessPriority, + "AdjustGpuProcessPriority", + base::FEATURE_DISABLED_BY_DEFAULT); #endif // Causes us to use the SharedImageManager, removing support for the old
diff --git a/gpu/config/gpu_finch_features.h b/gpu/config/gpu_finch_features.h index a20b868..fac08e7 100644 --- a/gpu/config/gpu_finch_features.h +++ b/gpu/config/gpu_finch_features.h
@@ -48,6 +48,8 @@ #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_IOS) GPU_EXPORT BASE_DECLARE_FEATURE(kMetal); + +GPU_EXPORT BASE_DECLARE_FEATURE(kAdjustGpuProcessPriority); #endif GPU_EXPORT BASE_DECLARE_FEATURE(kSharedImageManager);
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm index ade41c2e..44038da 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm
@@ -96,6 +96,7 @@ NSLayoutConstraint* fakeLocationBarHeightConstraint; @property(nonatomic, strong) NSLayoutConstraint* fakeToolbarTopConstraint; @property(nonatomic, strong) NSLayoutConstraint* hintLabelLeadingConstraint; +@property(nonatomic, strong) NSLayoutConstraint* hintLabelTrailingConstraint; // In the new layout, the hint label should always be at least inside the fake // omnibox. When the fake omnibox is shrunk, the position from the leading side // of the search field should yield. This constraint is not defined for the old @@ -256,6 +257,9 @@ // is taking the full width, there are few points that are not accessible and // allow to select the content below it. self.searchHintLabel.isAccessibilityElement = NO; + [self.searchHintLabel + setContentCompressionResistancePriority:UILayoutPriorityDefaultLow + forAxis:UILayoutConstraintAxisHorizontal]; // Voice search. self.voiceSearchButton = @@ -324,13 +328,15 @@ constraintLessThanOrEqualToAnchor:self.fakeLocationBar.trailingAnchor constant:-kEndButtonFakeboxTrailingSpace]; + // The voice search button is always on the leading side, even if the Lens + // button is visible. + self.hintLabelTrailingConstraint = [self.searchHintLabel.trailingAnchor + constraintLessThanOrEqualToAnchor:self.voiceSearchButton.leadingAnchor]; + self.hintLabelTrailingConstraint.priority = UILayoutPriorityDefaultHigh; [NSLayoutConstraint activateConstraints:@[ [self.voiceSearchButton.centerYAnchor constraintEqualToAnchor:self.fakeLocationBar.centerYAnchor], - // The voice search button is always on the left, even if the Lens button is - // visible. - [self.searchHintLabel.trailingAnchor - constraintLessThanOrEqualToAnchor:self.voiceSearchButton.leadingAnchor], + self.hintLabelTrailingConstraint, self.endButtonTrailingMarginConstraint, self.endButtonTrailingConstraint, ]]; @@ -500,6 +506,14 @@ (kEndButtonFakeboxTrailingSpace - kEndButtonOmniboxTrailingSpace) * percent; + // Offset the hint label constraints with half of the change in width + // from the original scale, since constraints are calculated before + // transformations are applied. This prevents the label from overlapping + // with other UI elements. + CGFloat additionalScaleOffset = + (content_suggestions::kHintTextScale * (1 - percent)) * + self.searchHintLabel.bounds.size.width * 0.5; + self.hintLabelTrailingConstraint.constant = -additionalScaleOffset; if (base::FeatureList::IsEnabled(kNewNTPOmniboxLayout)) { // A similar positioning scheme is applied to the leading-edge-aligned // hint label as the trailing-edge-aligned buttons. @@ -508,20 +522,8 @@ kHintLabelFakeboxLeadingSpace - (kHintLabelFakeboxLeadingSpace - kHintLabelOmniboxLeadingSpace) * percent; - - // Offset the hint label constraint with half of the change in width - // from the original scale, since constraints are calculated before - // transformations are applied. This is only necessary with the new NTP - // omnibox layout as the new layout does not have a center-aligned hint - // label. - CGFloat scaleValue = - 1 + (content_suggestions::kHintTextScale * (1 - percent)); - self.searchHintLabel.transform = - CGAffineTransformMakeScale(scaleValue, scaleValue); self.hintLabelLeadingConstraint.constant = - desiredLeadingSpace + (1 - percent) * - content_suggestions::kHintTextScale * - self.searchHintLabel.bounds.size.width * 0.5; + desiredLeadingSpace + additionalScaleOffset; } else { self.hintLabelLeadingConstraint.constant = subviewsDiff + ntp_header::kCenteredHintLabelSidePadding;
diff --git a/ios/chrome/browser/ui/tabs/DEPS b/ios/chrome/browser/ui/tabs/DEPS deleted file mode 100644 index c55a5355..0000000 --- a/ios/chrome/browser/ui/tabs/DEPS +++ /dev/null
@@ -1,5 +0,0 @@ -specific_include_rules = { - "tab_view\.mm": [ - "+third_party/google_toolbox_for_mac/src/iPhone/GTMFadeTruncatingLabel.h", - ], -}
diff --git a/media/audio/audio_manager_unittest.cc b/media/audio/audio_manager_unittest.cc index d34c6d8..739e7785 100644 --- a/media/audio/audio_manager_unittest.cc +++ b/media/audio/audio_manager_unittest.cc
@@ -234,6 +234,7 @@ // Flush the message loop to run any shutdown tasks posted by AudioManager. if (audio_manager_) { audio_manager_->Shutdown(); + device_info_accessor_.reset(); audio_manager_.reset(); }
diff --git a/media/audio/audio_output_device.h b/media/audio/audio_output_device.h index abf8432..d396830 100644 --- a/media/audio/audio_output_device.h +++ b/media/audio/audio_output_device.h
@@ -129,6 +129,8 @@ bool play_automatically) override; void OnIPCClosed() override; + AudioOutputIPC* GetIpcForTesting() { return ipc_.get(); } + protected: // Magic required by ref_counted.h to avoid any code deleting the object // accidentally while there are references to it.
diff --git a/media/audio/audio_output_device_unittest.cc b/media/audio/audio_output_device_unittest.cc index 89d675e..d094d6e8 100644 --- a/media/audio/audio_output_device_unittest.cc +++ b/media/audio/audio_output_device_unittest.cc
@@ -102,21 +102,27 @@ MOCK_METHOD1(OnDeviceInfoReceived, void(OutputDeviceInfo)); protected: + MockAudioOutputIPC* audio_output_ipc() { + return static_cast<MockAudioOutputIPC*>(audio_device_->GetIpcForTesting()); + } + base::test::TaskEnvironment task_env_{ base::test::TaskEnvironment::TimeSource::MOCK_TIME}; AudioParameters default_audio_parameters_; StrictMock<MockRenderCallback> callback_; - raw_ptr<MockAudioOutputIPC> audio_output_ipc_; // owned by audio_device_ - scoped_refptr<AudioOutputDevice> audio_device_; OutputDeviceStatus device_status_; private: int CalculateMemorySize(); + // These may need to outlive `audio_device_`. UnsafeSharedMemoryRegion shared_memory_region_; WritableSharedMemoryMapping shared_memory_mapping_; CancelableSyncSocket browser_socket_; CancelableSyncSocket renderer_socket_; + + protected: + scoped_refptr<AudioOutputDevice> audio_device_; }; AudioOutputDeviceTest::AudioOutputDeviceTest() @@ -126,9 +132,7 @@ SetDevice(kDefaultDeviceId); } -AudioOutputDeviceTest::~AudioOutputDeviceTest() { - audio_device_ = nullptr; -} +AudioOutputDeviceTest::~AudioOutputDeviceTest() = default; void AudioOutputDeviceTest::CreateDevice(const std::string& device_id, base::TimeDelta timeout) { @@ -136,16 +140,15 @@ if (audio_device_) StopAudioDevice(); - audio_output_ipc_ = new NiceMock<MockAudioOutputIPC>(); audio_device_ = new AudioOutputDevice( - base::WrapUnique(audio_output_ipc_.get()), + std::make_unique<NiceMock<MockAudioOutputIPC>>(), task_env_.GetMainThreadTaskRunner(), AudioSinkParameters(base::UnguessableToken(), device_id), timeout); } void AudioOutputDeviceTest::SetDevice(const std::string& device_id) { CreateDevice(device_id); - EXPECT_CALL(*audio_output_ipc_, + EXPECT_CALL(*audio_output_ipc(), RequestDeviceAuthorization(audio_device_.get(), base::UnguessableToken(), device_id)); audio_device_->RequestDeviceAuthorization(); @@ -165,7 +168,7 @@ void AudioOutputDeviceTest::ReceiveAuthorization(OutputDeviceStatus status) { device_status_ = status; if (device_status_ != OUTPUT_DEVICE_STATUS_OK) - EXPECT_CALL(*audio_output_ipc_, CloseStream()); + EXPECT_CALL(*audio_output_ipc(), CloseStream()); audio_device_->OnDeviceAuthorized(device_status_, default_audio_parameters_, kDefaultDeviceId); @@ -174,7 +177,7 @@ void AudioOutputDeviceTest::StartAudioDevice() { if (device_status_ == OUTPUT_DEVICE_STATUS_OK) - EXPECT_CALL(*audio_output_ipc_, CreateStream(audio_device_.get(), _)); + EXPECT_CALL(*audio_output_ipc(), CreateStream(audio_device_.get(), _)); else EXPECT_CALL(callback_, OnRenderError()); @@ -210,7 +213,7 @@ void AudioOutputDeviceTest::StopAudioDevice() { if (device_status_ == OUTPUT_DEVICE_STATUS_OK) - EXPECT_CALL(*audio_output_ipc_, CloseStream()); + EXPECT_CALL(*audio_output_ipc(), CloseStream()); audio_device_->Stop(); task_env_.FastForwardBy(base::TimeDelta()); @@ -218,7 +221,7 @@ void AudioOutputDeviceTest::FlushAudioDevice() { if (device_status_ == OUTPUT_DEVICE_STATUS_OK) - EXPECT_CALL(*audio_output_ipc_, FlushStream()); + EXPECT_CALL(*audio_output_ipc(), FlushStream()); audio_device_->Flush(); task_env_.FastForwardBy(base::TimeDelta()); @@ -255,7 +258,7 @@ // Expect us to shutdown IPC but not to render anything despite the stream // getting created. - EXPECT_CALL(*audio_output_ipc_, CloseStream()); + EXPECT_CALL(*audio_output_ipc(), CloseStream()); CallOnStreamCreated(); } @@ -265,7 +268,7 @@ StartAudioDevice(); StopAudioDevice(); - EXPECT_CALL(*audio_output_ipc_, + EXPECT_CALL(*audio_output_ipc(), RequestDeviceAuthorization(audio_device_.get(), base::UnguessableToken(), _)); StartAudioDevice(); @@ -294,14 +297,13 @@ TEST_F(AudioOutputDeviceTest, AuthorizationFailsBeforeInitialize_NoError) { // Clear audio device set by fixture. StopAudioDevice(); - audio_output_ipc_ = new NiceMock<MockAudioOutputIPC>(); audio_device_ = new AudioOutputDevice( - base::WrapUnique(audio_output_ipc_.get()), + std::make_unique<NiceMock<MockAudioOutputIPC>>(), task_env_.GetMainThreadTaskRunner(), AudioSinkParameters(base::UnguessableToken(), kDefaultDeviceId), kAuthTimeout); EXPECT_CALL( - *audio_output_ipc_, + *audio_output_ipc(), RequestDeviceAuthorization(audio_device_.get(), base::UnguessableToken(), kDefaultDeviceId)); @@ -321,10 +323,10 @@ TEST_F(AudioOutputDeviceTest, AuthorizationTimedOut) { CreateDevice(kNonDefaultDeviceId); EXPECT_CALL( - *audio_output_ipc_, + *audio_output_ipc(), RequestDeviceAuthorization(audio_device_.get(), base::UnguessableToken(), kNonDefaultDeviceId)); - EXPECT_CALL(*audio_output_ipc_, CloseStream()); + EXPECT_CALL(*audio_output_ipc(), CloseStream()); // Request authorization; no reply from the browser. audio_device_->RequestDeviceAuthorization(); @@ -339,7 +341,7 @@ TEST_F(AudioOutputDeviceTest, GetOutputDeviceInfoAsync_Error) { CreateDevice(kUnauthorizedDeviceId, base::TimeDelta()); EXPECT_CALL( - *audio_output_ipc_, + *audio_output_ipc(), RequestDeviceAuthorization(audio_device_.get(), base::UnguessableToken(), kUnauthorizedDeviceId)); audio_device_->RequestDeviceAuthorization(); @@ -366,7 +368,7 @@ TEST_F(AudioOutputDeviceTest, GetOutputDeviceInfoAsync_Okay) { CreateDevice(kDefaultDeviceId, base::TimeDelta()); EXPECT_CALL( - *audio_output_ipc_, + *audio_output_ipc(), RequestDeviceAuthorization(audio_device_.get(), base::UnguessableToken(), kDefaultDeviceId)); audio_device_->RequestDeviceAuthorization();
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc index d250ce6..9a935487e 100644 --- a/media/base/media_switches.cc +++ b/media/base/media_switches.cc
@@ -539,7 +539,7 @@ // information on the quality of the session using RTCP logs. BASE_FEATURE(kEnableRtcpReporting, "EnableRtcpReporting", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); // Approach original pre-REC MSE object URL autorevoking behavior, though await // actual attempt to use the object URL for attachment to perform revocation.
diff --git a/remoting/host/DEPS b/remoting/host/DEPS index 5d4d463b..f64775b 100644 --- a/remoting/host/DEPS +++ b/remoting/host/DEPS
@@ -22,7 +22,6 @@ "+services/device/public", "+services/device/wake_lock/power_save_blocker", "+services/network", - "+third_party/google_toolbox_for_mac", "+third_party/grpc", "+third_party/jsoncpp", "+third_party/skia",
diff --git a/remoting/host/input_monitor/BUILD.gn b/remoting/host/input_monitor/BUILD.gn index afc5d12..f930949 100644 --- a/remoting/host/input_monitor/BUILD.gn +++ b/remoting/host/input_monitor/BUILD.gn
@@ -48,7 +48,6 @@ "local_keyboard_input_monitor_mac.mm", "local_mouse_input_monitor_mac.mm", ] - deps += [ "//third_party/google_toolbox_for_mac" ] } if (remoting_use_x11) {
diff --git a/remoting/host/input_monitor/local_hotkey_input_monitor_mac.mm b/remoting/host/input_monitor/local_hotkey_input_monitor_mac.mm index 48317f9..453bd38 100644 --- a/remoting/host/input_monitor/local_hotkey_input_monitor_mac.mm +++ b/remoting/host/input_monitor/local_hotkey_input_monitor_mac.mm
@@ -4,10 +4,8 @@ #include "remoting/host/input_monitor/local_hotkey_input_monitor.h" -#include "base/memory/raw_ptr.h" -#import "base/task/single_thread_task_runner.h" - #import <AppKit/AppKit.h> +#import <Carbon/Carbon.h> #include <cstdint> #include <utility> @@ -18,18 +16,16 @@ #include "base/location.h" #include "base/logging.h" #include "base/mac/scoped_cftyperef.h" +#include "base/mac/scoped_nsobject.h" #include "base/memory/ptr_util.h" +#include "base/memory/raw_ptr.h" #include "base/memory/ref_counted.h" #include "base/sequence_checker.h" #include "base/synchronization/lock.h" +#import "base/task/single_thread_task_runner.h" #include "base/task/single_thread_task_runner.h" -#import "third_party/google_toolbox_for_mac/src/AppKit/GTMCarbonEvent.h" #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" -// Esc Key Code is 53. -// http://boredzo.org/blog/wp-content/uploads/2007/05/IMTx-virtual-keycodes.pdf -static const NSUInteger kEscKeyCode = 53; - namespace remoting { namespace { @@ -65,60 +61,53 @@ } // namespace } // namespace remoting -@interface LocalHotkeyInputMonitorManager : NSObject { - @private - GTMCarbonHotKey* _hotKey; - raw_ptr<remoting::LocalHotkeyInputMonitorMac::EventHandler> _monitor; -} +@interface LocalHotkeyInputMonitorManager : NSObject - (instancetype)initWithMonitor: (remoting::LocalHotkeyInputMonitorMac::EventHandler*)monitor; -// Called when the hotKey is hit. -- (void)hotKeyHit:(GTMCarbonHotKey*)hotKey; - -// Must be called when the LocalHotkeyInputMonitorManager is no longer needed. -// Similar to NSTimer in that more than a simple release is required. -- (void)invalidate; - @end -@implementation LocalHotkeyInputMonitorManager +@implementation LocalHotkeyInputMonitorManager { + id _eventMonitor; + + raw_ptr<remoting::LocalHotkeyInputMonitorMac::EventHandler> _monitor; +} - (instancetype)initWithMonitor: (remoting::LocalHotkeyInputMonitorMac::EventHandler*)monitor { if ((self = [super init])) { _monitor = monitor; - GTMCarbonEventDispatcherHandler* handler = - [GTMCarbonEventDispatcherHandler sharedEventDispatcherHandler]; - _hotKey = [handler - registerHotKey:kEscKeyCode - modifiers:(NSEventModifierFlagOption | NSEventModifierFlagControl) - target:self - action:@selector(hotKeyHit:) - userInfo:nil - whenPressed:YES]; - if (!_hotKey) { - LOG(ERROR) << "registerHotKey failed."; - [self release]; - return nil; - } + auto eventHandler = ^NSEvent*(NSEvent* event) { + const NSEventModifierFlags requiredModifiers = + NSEventModifierFlagOption | NSEventModifierFlagControl; + if ((event.keyCode == kVK_Escape) && + (event.modifierFlags & requiredModifiers)) { + // Trigger the callback. + _monitor->OnDisconnectShortcut(); + + // Stop the event propagation. + return nil; + } + + // Otherwise, let the event continue propagating. + return event; + }; + + _eventMonitor = + [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown + handler:eventHandler]; } return self; } -- (void)hotKeyHit:(GTMCarbonHotKey*)hotKey { - _monitor->OnDisconnectShortcut(); -} - -- (void)invalidate { - if (_hotKey) { - GTMCarbonEventDispatcherHandler* handler = - [GTMCarbonEventDispatcherHandler sharedEventDispatcherHandler]; - [handler unregisterHotKey:_hotKey]; - _hotKey = nullptr; +- (void)dealloc { + if (_eventMonitor) { + [NSEvent removeMonitor:_eventMonitor]; } + + [super dealloc]; } @end @@ -156,7 +145,7 @@ // Task runner on which |window_| is created. scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; - LocalHotkeyInputMonitorManager* manager_; + base::scoped_nsobject<LocalHotkeyInputMonitorManager> manager_; // Invoked in the |caller_task_runner_| thread to report session disconnect // requests. @@ -184,7 +173,6 @@ base::OnceClosure disconnect_callback) : caller_task_runner_(caller_task_runner), ui_task_runner_(ui_task_runner), - manager_(nil), disconnect_callback_(std::move(disconnect_callback)) { DCHECK(disconnect_callback_); } @@ -204,21 +192,19 @@ } LocalHotkeyInputMonitorMac::Core::~Core() { - DCHECK_EQ(manager_, nil); + DCHECK_EQ(manager_.get(), nil); } void LocalHotkeyInputMonitorMac::Core::StartOnUiThread() { DCHECK(ui_task_runner_->BelongsToCurrentThread()); - manager_ = [[LocalHotkeyInputMonitorManager alloc] initWithMonitor:this]; + manager_.reset([[LocalHotkeyInputMonitorManager alloc] initWithMonitor:this]); } void LocalHotkeyInputMonitorMac::Core::StopOnUiThread() { DCHECK(ui_task_runner_->BelongsToCurrentThread()); - [manager_ invalidate]; - [manager_ release]; - manager_ = nil; + manager_.reset(); } void LocalHotkeyInputMonitorMac::Core::OnDisconnectShortcut() {
diff --git a/services/device/generic_sensor/sensor_provider_impl.h b/services/device/generic_sensor/sensor_provider_impl.h index da593bc..a9820ba 100644 --- a/services/device/generic_sensor/sensor_provider_impl.h +++ b/services/device/generic_sensor/sensor_provider_impl.h
@@ -6,7 +6,6 @@ #define SERVICES_DEVICE_GENERIC_SENSOR_SENSOR_PROVIDER_IMPL_H_ #include "base/memory/read_only_shared_memory_region.h" -#include "base/task/single_thread_task_runner.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver_set.h" #include "mojo/public/cpp/bindings/unique_receiver_set.h"
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json index 9d0c2ce2..27aa429 100644 --- a/testing/buildbot/chromium.chromiumos.json +++ b/testing/buildbot/chromium.chromiumos.json
@@ -5793,9 +5793,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "isolate_profile_data": true, "merge": { "args": [], @@ -5807,8 +5807,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -5892,9 +5892,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "isolate_profile_data": true, "merge": { "args": [], @@ -5906,8 +5906,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [ @@ -5965,9 +5965,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "isolate_profile_data": true, "merge": { "args": [], @@ -5979,8 +5979,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -6061,9 +6061,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "isolate_profile_data": true, "merge": { "args": [], @@ -6075,8 +6075,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [ @@ -6116,9 +6116,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "isolate_profile_data": true, "merge": { "args": [], @@ -6130,8 +6130,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -6215,9 +6215,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "isolate_profile_data": true, "merge": { "args": [], @@ -6229,8 +6229,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json index 9b15bbd..9a45a30 100644 --- a/testing/buildbot/chromium.coverage.json +++ b/testing/buildbot/chromium.coverage.json
@@ -25656,9 +25656,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "isolate_profile_data": true, "merge": { "args": [], @@ -25670,8 +25670,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -25755,9 +25755,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "isolate_profile_data": true, "merge": { "args": [], @@ -25769,8 +25769,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [ @@ -25828,9 +25828,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "isolate_profile_data": true, "merge": { "args": [], @@ -25842,8 +25842,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -25924,9 +25924,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "isolate_profile_data": true, "merge": { "args": [], @@ -25938,8 +25938,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [ @@ -25979,9 +25979,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "isolate_profile_data": true, "merge": { "args": [], @@ -25993,8 +25993,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -26078,9 +26078,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "isolate_profile_data": true, "merge": { "args": [], @@ -26092,8 +26092,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 288be4af..cc2758ed 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -48693,9 +48693,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -48706,8 +48706,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -48792,9 +48792,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -48805,8 +48805,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [ @@ -48865,9 +48865,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -48878,8 +48878,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -48961,9 +48961,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -48974,8 +48974,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [ @@ -49016,9 +49016,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -49029,8 +49029,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -49115,9 +49115,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -49128,8 +49128,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [ @@ -50554,9 +50554,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -50567,8 +50567,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -50653,9 +50653,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -50666,8 +50666,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [ @@ -50726,9 +50726,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -50739,8 +50739,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -50822,9 +50822,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -50835,8 +50835,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [ @@ -50877,9 +50877,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -50890,8 +50890,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -50976,9 +50976,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -50989,8 +50989,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [ @@ -51664,9 +51664,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -51677,8 +51677,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -51760,9 +51760,9 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome" + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "merge": { "args": [], "script": "//testing/merge_scripts/standard_gtest_merge.py" @@ -51773,8 +51773,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index 78c7fde..fc248d1 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -18497,12 +18497,12 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "isolate_profile_data": true, "merge": { "args": [], @@ -18514,8 +18514,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -18605,12 +18605,12 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "isolate_profile_data": true, "merge": { "args": [], @@ -18622,8 +18622,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [ @@ -18689,12 +18689,12 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "isolate_profile_data": true, "merge": { "args": [], @@ -18706,8 +18706,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -18794,12 +18794,12 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "isolate_profile_data": true, "merge": { "args": [], @@ -18811,8 +18811,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [ @@ -18855,12 +18855,12 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 111.0.5563.54", + "description": "Run with ash-chrome version 112.0.5615.18", "isolate_profile_data": true, "merge": { "args": [], @@ -18872,8 +18872,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v111.0.5563.54", - "revision": "version:111.0.5563.54" + "location": "lacros_version_skew_tests_v112.0.5615.18", + "revision": "version:112.0.5615.18" } ], "dimension_sets": [ @@ -18963,12 +18963,12 @@ { "args": [ "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter", - "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome", + "--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome", "--test-launcher-print-test-stdio=always", "--combine-ash-logs-on-bots", "--asan-symbolize-output" ], - "description": "Run with ash-chrome version 110.0.5481.181", + "description": "Run with ash-chrome version 111.0.5563.71", "isolate_profile_data": true, "merge": { "args": [], @@ -18980,8 +18980,8 @@ "cipd_packages": [ { "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip", - "location": "lacros_version_skew_tests_v110.0.5481.181", - "revision": "version:110.0.5481.181" + "location": "lacros_version_skew_tests_v111.0.5563.71", + "revision": "version:111.0.5563.71" } ], "dimension_sets": [
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl index a3e729a3..c130ddb 100644 --- a/testing/buildbot/variants.pyl +++ b/testing/buildbot/variants.pyl
@@ -54,32 +54,32 @@ }, 'LACROS_VERSION_SKEW_BETA': { 'args': [ - '--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.54/test_ash_chrome', + '--ash-chrome-path-override=../../lacros_version_skew_tests_v112.0.5615.18/test_ash_chrome', ], - 'description': 'Run with ash-chrome version 111.0.5563.54', + 'description': 'Run with ash-chrome version 112.0.5615.18', 'identifier': 'Lacros version skew testing ash beta', 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip', - 'location': 'lacros_version_skew_tests_v111.0.5563.54', - 'revision': 'version:111.0.5563.54', + 'location': 'lacros_version_skew_tests_v112.0.5615.18', + 'revision': 'version:112.0.5615.18', }, ], }, }, 'LACROS_VERSION_SKEW_STABLE': { 'args': [ - '--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5481.181/test_ash_chrome', + '--ash-chrome-path-override=../../lacros_version_skew_tests_v111.0.5563.71/test_ash_chrome', ], - 'description': 'Run with ash-chrome version 110.0.5481.181', + 'description': 'Run with ash-chrome version 111.0.5563.71', 'identifier': 'Lacros version skew testing ash stable', 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip', - 'location': 'lacros_version_skew_tests_v110.0.5481.181', - 'revision': 'version:110.0.5481.181', + 'location': 'lacros_version_skew_tests_v111.0.5563.71', + 'revision': 'version:111.0.5563.71', }, ], },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index cccdcc8b..92d2aeb 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -296,6 +296,21 @@ ] } ], + "AndroidMetricsAsyncMetricLogging": [ + { + "platforms": [ + "android_webview" + ], + "experiments": [ + { + "name": "AndroidMetricsAsyncMetricLogging", + "enable_features": [ + "AndroidMetricsAsyncMetricLogging" + ] + } + ] + } + ], "AndroidOmniboxUxRevampPhase2": [ { "platforms": [ @@ -560,36 +575,6 @@ ] } ], - "ArcEnableTTSCacheSetupRelaunch": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ArcEnableTTSCacheSetup" - ] - } - ] - } - ], - "ArcEnableTTSCaching": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "Enabled", - "enable_features": [ - "ArcEnableTTSCaching" - ] - } - ] - } - ], "ArcGameMode": [ { "platforms": [
diff --git a/third_party/.gitignore b/third_party/.gitignore index 8f47cc9..81a57c70 100644 --- a/third_party/.gitignore +++ b/third_party/.gitignore
@@ -295,6 +295,7 @@ /win_toolchain/.timestamps /win_toolchain/files /wix +/wlcs/src /wuffs/src /xdg-utils /xnnpack/src
diff --git a/third_party/aosp_dalvik/3pp/3pp.pb b/third_party/aosp_dalvik/3pp/3pp.pb index 0f0a1e0d..234d92ad 100644 --- a/third_party/aosp_dalvik/3pp/3pp.pb +++ b/third_party/aosp_dalvik/3pp/3pp.pb
@@ -4,7 +4,7 @@ download_url: "https://android.googlesource.com/platform/dalvik/+archive/090cb5952bab050da27003badb2d27e279e62115.tar.gz" version: "13.0.0_r24" } - patch_version: 'cr0' + patch_version: 'cr1' subdir: 'lib' unpack_archive: true }
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc index 805c82a..97b0125 100644 --- a/third_party/blink/common/features.cc +++ b/third_party/blink/common/features.cc
@@ -463,9 +463,12 @@ // Drop touch-end dispatch from `InputHandlerProxy` when all other touch-events // in current interaction sequence are dropeed. +// +// TODO(https://crbug.com/1417126): This is disabled because of a suspicious +// flake in AR/XR tests. BASE_FEATURE(kDroppedTouchSequenceIncludesTouchEnd, "DroppedTouchSequenceIncludesTouchEnd", - base::FEATURE_ENABLED_BY_DEFAULT); + base::FEATURE_DISABLED_BY_DEFAULT); // File handling icons. https://crbug.com/1218213 BASE_FEATURE(kFileHandlingIcons,
diff --git a/third_party/blink/public/common/tokens/multi_token.h b/third_party/blink/public/common/tokens/multi_token.h index 64e901b..4b324945 100644 --- a/third_party/blink/public/common/tokens/multi_token.h +++ b/third_party/blink/public/common/tokens/multi_token.h
@@ -15,6 +15,7 @@ #include <limits> #include <type_traits> +#include "base/types/variant_util.h" #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" @@ -166,19 +167,13 @@ // currently held. template <typename T, EnableIfIsSupportedToken<T> = 0> static constexpr Tag IndexOf() { - return static_cast<Tag>( - absl::variant<IndexOfHelper<Tokens>...>(IndexOfHelper<T>()).index()); + return static_cast<Tag>(base::VariantIndexOfType<Storage, T>()); } // Equivalent to `value().ToString()`. std::string ToString() const; private: - // Helper struct for IndexOf(); a `base::TokenType` is never usable as a - // literal type but an IndexOfHelper<base::TokenType> is. - template <typename T> - struct IndexOfHelper {}; - Storage storage_; };
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl index f254e070..3e1f216 100644 --- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl +++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -10900,7 +10900,19 @@ # This domain allows interacting with the FedCM dialog. experimental domain FedCm + # Corresponds to IdentityRequestAccount + type Account extends object + properties + string accountId + string email + string name + string givenName + string pictureUrl + string idpConfigUrl + event dialogShown + parameters + array of Account accounts command enable command disable
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom index 73450fcd..f12dafb 100644 --- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom +++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -3828,6 +3828,12 @@ kPrivateAggregationApiFledgeExtensions = 4487, kDeprecatedInterestGroupDailyUpdateUrl = 4488, kCSSColorGradientColorSpace = 4489, + kDanglingMarkupInWindowName = 4490, + kDanglingMarkupInWindowNameNotEndsWithNewLineOrGT = 4491, + kDanglingMarkupInWindowNameNotEndsWithGT = 4492, + kDanglingMarkupInTarget = 4493, + kDanglingMarkupInTargetNotEndsWithGT = 4494, + kDanglingMarkupInTargetNotEndsWithNewLineOrGT = 4495, // Add new features immediately above this line. Don't change assigned // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/README.md b/third_party/blink/renderer/README.md index 30b91738..1e3494eb 100644 --- a/third_party/blink/renderer/README.md +++ b/third_party/blink/renderer/README.md
@@ -14,7 +14,7 @@ and should not be used outside of it. Use [Blink's public API](../public) in code outside of Blink. -### `core/` +### [`core/`](core/README.md) The `core/` directory implements the essence of the Web Platform defined by specs and IDL interfaces. Due to historical reasons, `core/` contains a lot of features with @@ -35,7 +35,7 @@ For example, `modules/crypto` implements WebCrypto API. -### `platform/` +### [`platform/`](platform/README.md) The `platform/` directory is a collection of lower level features of Blink that are factored out of a monolithic `core/`. These features follow the same principles as `modules/`,
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni index a34c04e..82036b3 100644 --- a/third_party/blink/renderer/bindings/idl_in_modules.gni +++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -651,6 +651,7 @@ "//third_party/blink/renderer/modules/sanitizer_api/sanitizer.idl", "//third_party/blink/renderer/modules/sanitizer_api/sanitizer_config.idl", "//third_party/blink/renderer/modules/scheduler/scheduler.idl", + "//third_party/blink/renderer/modules/scheduler/window_idle_tasks.idl", "//third_party/blink/renderer/modules/scheduler/scheduler_post_task_callback.idl", "//third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.idl", "//third_party/blink/renderer/modules/scheduler/script_wrappable_task_state.idl",
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc index 5d00dee..a531c0c 100644 --- a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc +++ b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
@@ -128,14 +128,6 @@ CSSTimingData::GetRepeated(animation_data->TimingFunctionList(), i))); list->Append(*ComputedStyleUtils::ValueForAnimationDelayStart( CSSTimingData::GetRepeated(animation_data->DelayStartList(), i))); - if (CSSAnimationData::InitialDelayEnd() != - CSSTimingData::GetRepeated(animation_data->DelayEndList(), i)) { - DCHECK_EQ(shorthand.length(), 10u); - DCHECK_EQ(shorthand.properties()[3]->PropertyID(), - CSSPropertyID::kAnimationDelayEnd); - list->Append(*ComputedStyleUtils::ValueForAnimationDelayEnd( - CSSTimingData::GetRepeated(animation_data->DelayEndList(), i))); - } list->Append(*ComputedStyleUtils::ValueForAnimationIterationCount( CSSTimingData::GetRepeated(animation_data->IterationCountList(), i))); list->Append(*ComputedStyleUtils::ValueForAnimationDirection( @@ -146,16 +138,16 @@ CSSTimingData::GetRepeated(animation_data->PlayStateList(), i))); list->Append(*MakeGarbageCollected<CSSCustomIdentValue>( animation_data->NameList()[i])); - // When serializing shorthands, a component value must be omitted - // if doesn't change the meaning of the overall value. - // https://drafts.csswg.org/cssom/#serializing-css-values + // The shorthand can not represent the following properties if they have + // non-initial values. This is because they are always reset to their + // initial value by the shorthand. if (CSSAnimationData::InitialTimeline() != animation_data->GetTimeline(i)) { - DCHECK_EQ(shorthand.length(), 10u); - DCHECK_EQ(shorthand.properties()[9]->PropertyID(), - CSSPropertyID::kAnimationTimeline); - list->Append(*ComputedStyleUtils::ValueForAnimationTimeline( - animation_data->GetTimeline(i))); + return nullptr; + } + if (CSSAnimationData::InitialDelayEnd() != + CSSTimingData::GetRepeated(animation_data->DelayEndList(), i)) { + return nullptr; } animations_list->Append(*list); }
diff --git a/third_party/blink/renderer/core/css/style_property_serializer.cc b/third_party/blink/renderer/core/css/style_property_serializer.cc index fd37849..49517a5c 100644 --- a/third_party/blink/renderer/core/css/style_property_serializer.cc +++ b/third_party/blink/renderer/core/css/style_property_serializer.cc
@@ -1536,22 +1536,24 @@ bool is_initial_value = value->IsInitialValue(); - // When serializing shorthands, a component value must be omitted - // if doesn't change the meaning of the overall value. - // https://drafts.csswg.org/cssom/#serializing-css-values + // The shorthand can not represent the following properties if they have + // non-initial values. This is because they are always reset to their + // initial value by the shorthand. if (property->IDEquals(CSSPropertyID::kAnimationTimeline)) { - if (auto* ident = DynamicTo<CSSIdentifierValue>(value)) { - if (ident->GetValueID() == - CSSAnimationData::InitialTimeline().GetKeyword()) { - DCHECK(RuntimeEnabledFeatures::CSSScrollTimelineEnabled()); - is_initial_value = true; - } + auto* ident = DynamicTo<CSSIdentifierValue>(value); + if (!ident || (ident->GetValueID() != + CSSAnimationData::InitialTimeline().GetKeyword())) { + DCHECK(RuntimeEnabledFeatures::CSSScrollTimelineEnabled()); + return g_empty_string; } + is_initial_value = true; } - if (property->IDEquals(CSSPropertyID::kAnimationDelayEnd)) { - is_initial_value = CSSToStyleMap::MapAnimationDelayEnd(*value) == - CSSTimingData::InitialDelayEnd(); + if (CSSToStyleMap::MapAnimationDelayEnd(*value) != + CSSTimingData::InitialDelayEnd()) { + return g_empty_string; + } + is_initial_value = true; } if (!is_initial_value) {
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/third_party/blink/renderer/core/display_lock/display_lock_context.cc index 5e20b62d..81a9808 100644 --- a/third_party/blink/renderer/core/display_lock/display_lock_context.cc +++ b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
@@ -1044,6 +1044,7 @@ if (!object_element->UseFallbackContent()) return nullptr; } else if (IsA<HTMLImageElement>(*element_) || + IsA<HTMLCanvasElement>(*element_) || (element_->IsFormControlElement() && !element_->IsOutputElement()) || element_->IsMediaElement() || element_->IsFrameOwnerElement() ||
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index c1ee4bcd..8ee4be4 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2836,6 +2836,7 @@ if (UNLIKELY(HasUndoStack())) { frame->GetEditor().GetUndoStack().ElementRemoved(this); } + frame->GetEditor().ElementRemoved(this); frame->GetEventHandler().ElementRemoved(this); } }
diff --git a/third_party/blink/renderer/core/dom/scripted_idle_task_controller.cc b/third_party/blink/renderer/core/dom/scripted_idle_task_controller.cc index 2f5f9b57..5925eca 100644 --- a/third_party/blink/renderer/core/dom/scripted_idle_task_controller.cc +++ b/third_party/blink/renderer/core/dom/scripted_idle_task_controller.cc
@@ -75,17 +75,6 @@ } // namespace internal -V8IdleTask::V8IdleTask(V8IdleRequestCallback* callback) : callback_(callback) {} - -void V8IdleTask::Trace(Visitor* visitor) const { - visitor->Trace(callback_); - IdleTask::Trace(visitor); -} - -void V8IdleTask::invoke(IdleDeadline* deadline) { - callback_->InvokeAndReportException(nullptr, deadline); -} - ScriptedIdleTaskController::ScriptedIdleTaskController( ExecutionContext* context) : ExecutionContextLifecycleStateObserver(context),
diff --git a/third_party/blink/renderer/core/dom/scripted_idle_task_controller.h b/third_party/blink/renderer/core/dom/scripted_idle_task_controller.h index 930b5e2..fe2e38db 100644 --- a/third_party/blink/renderer/core/dom/scripted_idle_task_controller.h +++ b/third_party/blink/renderer/core/dom/scripted_idle_task_controller.h
@@ -5,7 +5,6 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_SCRIPTED_IDLE_TASK_CONTROLLER_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_SCRIPTED_IDLE_TASK_CONTROLLER_H_ -#include "third_party/blink/renderer/bindings/core/v8/v8_idle_request_callback.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/dom/idle_deadline.h" #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_state_observer.h" @@ -25,9 +24,10 @@ class IdleRequestOptions; class ThreadScheduler; -// |IdleTask| is an interface type which generalizes tasks which are invoked -// on idle. The tasks need to define what to do on idle in |invoke|. -class IdleTask : public GarbageCollected<IdleTask>, public NameClient { +// `IdleTask` is an interface type which generalizes tasks which are invoked +// on idle. The tasks need to define what to do on idle in `invoke`. +class CORE_EXPORT IdleTask : public GarbageCollected<IdleTask>, + public NameClient { public: virtual void Trace(Visitor* visitor) const {} const char* NameInHeapSnapshot() const override { return "IdleTask"; } @@ -39,24 +39,10 @@ probe::AsyncTaskContext async_task_context_; }; -// |V8IdleTask| is the adapter class for the conversion from -// |V8IdleRequestCallback| to |IdleTask|. -class V8IdleTask : public IdleTask { - public: - static V8IdleTask* Create(V8IdleRequestCallback* callback) { - return MakeGarbageCollected<V8IdleTask>(callback); - } - - explicit V8IdleTask(V8IdleRequestCallback*); - ~V8IdleTask() override = default; - - void invoke(IdleDeadline*) override; - void Trace(Visitor*) const override; - - private: - Member<V8IdleRequestCallback> callback_; -}; - +// `ScriptedIdleTaskController` manages scheduling and running `IdleTask`s. This +// provides some higher level functionality on top of the thread scheduler's +// idle tasks, e.g. timeouts and providing an `IdleDeadline` to callbacks, which +// is used both by the requestIdleCallback API and internally in blink. class CORE_EXPORT ScriptedIdleTaskController : public GarbageCollected<ScriptedIdleTaskController>, public ExecutionContextLifecycleStateObserver,
diff --git a/third_party/blink/renderer/core/editing/editor.cc b/third_party/blink/renderer/core/editing/editor.cc index 8be6be2..1648ab9 100644 --- a/third_party/blink/renderer/core/editing/editor.cc +++ b/third_party/blink/renderer/core/editing/editor.cc
@@ -948,6 +948,16 @@ InputEvent::InputType::kInsertReplacementText); } +void Editor::ElementRemoved(Element* element) { + if (!RuntimeEnabledFeatures::DontLeakDetachedInputEnabled()) { + return; + } + if (last_edit_command_ && + last_edit_command_->EndingSelection().RootEditableElement() == element) { + last_edit_command_ = nullptr; + } +} + void Editor::Trace(Visitor* visitor) const { visitor->Trace(frame_); visitor->Trace(last_edit_command_);
diff --git a/third_party/blink/renderer/core/editing/editor.h b/third_party/blink/renderer/core/editing/editor.h index e5f9571..20141e8 100644 --- a/third_party/blink/renderer/core/editing/editor.h +++ b/third_party/blink/renderer/core/editing/editor.h
@@ -147,6 +147,8 @@ void SetStartNewKillRingSequence(bool); + void ElementRemoved(Element* element); + void Clear(); SelectionInDOMTree SelectionForCommand(Event*);
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc index aa977dbc..fee3b48e 100644 --- a/third_party/blink/renderer/core/frame/local_dom_window.cc +++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -1964,18 +1964,6 @@ return GetAgent()->IsOriginKeyed(); } -int LocalDOMWindow::requestIdleCallback(V8IdleRequestCallback* callback, - const IdleRequestOptions* options) { - SetCurrentTaskAsCallbackParent(callback); - if (!GetFrame()) - return 0; - return document_->RequestIdleCallback(V8IdleTask::Create(callback), options); -} - -void LocalDOMWindow::cancelIdleCallback(int id) { - document()->CancelIdleCallback(id); -} - CustomElementRegistry* LocalDOMWindow::customElements( ScriptState* script_state) const { if (!script_state->World().IsMainWorld())
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.h b/third_party/blink/renderer/core/frame/local_dom_window.h index 6d4acf5..03356ba 100644 --- a/third_party/blink/renderer/core/frame/local_dom_window.h +++ b/third_party/blink/renderer/core/frame/local_dom_window.h
@@ -74,7 +74,6 @@ class Fence; class FrameConsole; class History; -class IdleRequestOptions; class InputMethodController; class LocalFrame; class MediaQueryList; @@ -93,7 +92,6 @@ class StyleMedia; class TrustedTypePolicyFactory; class V8FrameRequestCallback; -class V8IdleRequestCallback; class V8VoidFunction; struct WebPictureInPictureWindowOptions; class WindowAgent; @@ -345,10 +343,6 @@ // https://html.spec.whatwg.org/C/#dom-originagentcluster bool originAgentCluster() const; - // Idle callback extensions - int requestIdleCallback(V8IdleRequestCallback*, const IdleRequestOptions*); - void cancelIdleCallback(int id); - // Custom elements CustomElementRegistry* customElements(ScriptState*) const; CustomElementRegistry* customElements() const;
diff --git a/third_party/blink/renderer/core/frame/window.idl b/third_party/blink/renderer/core/frame/window.idl index cff8b45..4da19b8 100644 --- a/third_party/blink/renderer/core/frame/window.idl +++ b/third_party/blink/renderer/core/frame/window.idl
@@ -108,11 +108,6 @@ [Replaceable, SameObject] readonly attribute External external; - // Cooperative Scheduling of Background Tasks - // https://www.w3.org/TR/requestidlecallback/#window_extensions - [Measure] long requestIdleCallback(IdleRequestCallback callback, optional IdleRequestOptions options = {}); - void cancelIdleCallback(long handle); - // CSS Object Model (CSSOM) // https://drafts.csswg.org/cssom/#extensions-to-the-window-interface [Affects=Nothing, NewObject] CSSStyleDeclaration getComputedStyle(Element elt, optional DOMString? pseudoElt);
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.cc b/third_party/blink/renderer/core/html/html_iframe_element.cc index 93308e2..0075c90 100644 --- a/third_party/blink/renderer/core/html/html_iframe_element.cc +++ b/third_party/blink/renderer/core/html/html_iframe_element.cc
@@ -190,6 +190,18 @@ UseCounter::Count(GetDocument(), WebFeature::kFrameNameContainsNewline); if (name_.Contains('<')) UseCounter::Count(GetDocument(), WebFeature::kFrameNameContainsBrace); + if (name_.Contains('\n') && name_.Contains('<')) { + UseCounter::Count(GetDocument(), WebFeature::kDanglingMarkupInWindowName); + if (!name_.EndsWith('>')) { + UseCounter::Count(GetDocument(), + WebFeature::kDanglingMarkupInWindowNameNotEndsWithGT); + if (!name_.EndsWith('\n')) { + UseCounter::Count( + GetDocument(), + WebFeature::kDanglingMarkupInWindowNameNotEndsWithNewLineOrGT); + } + } + } } else if (name == html_names::kSandboxAttr) { sandbox_->DidUpdateAttributeValue(params.old_value, value);
diff --git a/third_party/blink/renderer/core/layout/layout_html_canvas.cc b/third_party/blink/renderer/core/layout/layout_html_canvas.cc index 097f167..9f88dd2 100644 --- a/third_party/blink/renderer/core/layout/layout_html_canvas.cc +++ b/third_party/blink/renderer/core/layout/layout_html_canvas.cc
@@ -43,6 +43,9 @@ void LayoutHTMLCanvas::PaintReplaced(const PaintInfo& paint_info, const PhysicalOffset& paint_offset) const { NOT_DESTROYED(); + if (ChildPaintBlockedByDisplayLock()) { + return; + } HTMLCanvasPainter(*this).PaintReplaced(paint_info, paint_offset); }
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_line_resolver.cc b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_line_resolver.cc index 130f767..91aa9f1 100644 --- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_line_resolver.cc +++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_line_resolver.cc
@@ -7,6 +7,7 @@ #include <algorithm> #include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_data.h" #include "third_party/blink/renderer/core/layout/ng/grid/ng_grid_named_line_collection.h" +#include "third_party/blink/renderer/core/style/computed_grid_track_list.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/core/style/grid_area.h" #include "third_party/blink/renderer/core/style/grid_position.h" @@ -48,13 +49,17 @@ if (subgrid_area.rows.IsTranslatedDefinite()) subgridded_row_span_size_ = subgrid_area.SpanSize(kForRows); + // TODO(kschmi) - use a collector design (similar to + // `OrderedNamedLinesCollector`) to collect all of the lines first and then + // do a single step of filtering and adding them to the subgrid list. Also + // consider moving these to class methods. auto MergeNamedGridLinesWithParent = [](NamedGridLinesMap& subgrid_map, const NamedGridLinesMap& parent_map, GridSpan subgrid_span, bool is_opposite_direction_to_parent) -> void { // Update `subgrid_map` to a merged map from a parent grid or subgrid map // (`parent_map`). The map is a key-value store with keys as the line name // and the value as an array of ascending indices. - for (auto& pair : parent_map) { + for (const auto& pair : parent_map) { // TODO(kschmi) : Benchmark whether this is faster with an std::map, which // would eliminate the need for sorting and removing duplicates below. // Perf will vary based on the number of named lines defined. @@ -78,9 +83,12 @@ // to index 0 and don't need to be offset. const auto& existing_entry = subgrid_map.find(pair.key); if (existing_entry != subgrid_map.end()) { - for (auto& value : existing_entry->value) + for (const auto& value : existing_entry->value) { merged_list.push_back(value); + } + // TODO(kschmi): Reverse the list if `is_opposite_direction_to_parent` + // and there was no existing entry, as it will be sorted backwards. std::sort(merged_list.begin(), merged_list.end()); // Remove any duplicated entries in the sorted list. Duplicates can @@ -97,16 +105,13 @@ merged_list.end()); } - // Override the existing subgrid's line names map with the new merged list - // for this particular line name entry. If `merged_list` list is empty, - // (this can happen when all entries for a particular line name are out - // of the subgrid range), erase the entry entirely, as - // `NGGridNamedLineCollection` doesn't support named line entries without - // values. - if (merged_list.empty()) - subgrid_map.erase(pair.key); - else + // Override the existing subgrid's line names map with the new merged + // list for this particular line name entry. `merged_list` list can be + // empty if all entries for a particular line name are out of the + // subgrid range. + if (!merged_list.empty()) { subgrid_map.Set(pair.key, merged_list); + } } }; auto MergeImplicitLinesWithParent = [&](NamedGridLinesMap& subgrid_map, @@ -116,7 +121,7 @@ // First, clamp the existing `subgrid_map` to the subgrid range before // merging. These are positive and relative to index 0, so we only need to // clamp values above `subgrid_span_size`. - for (auto& pair : subgrid_map) { + for (const auto& pair : subgrid_map) { WTF::Vector<wtf_size_t> clamped_list; for (const auto& position : pair.value) { if (position > subgrid_span_size) @@ -130,7 +135,7 @@ // Update `subgrid_map` to a merged map from a parent grid or subgrid map // (`parent_map`). The map is a key-value store with keys as the implicit // line name and the value as an array of ascending indices. - for (auto& pair : parent_map) { + for (const auto& pair : parent_map) { WTF::Vector<wtf_size_t> merged_list; for (const auto& position : pair.value) { auto IsGridAreaInSubgridRange = [&]() -> bool { @@ -163,7 +168,8 @@ const auto& opposite_line_entry = parent_map.find(opposite_line_name); if (opposite_line_entry != parent_map.end()) { - for (auto& opposite_position : opposite_line_entry->value) { + for (const auto& opposite_position : + opposite_line_entry->value) { if (subgrid_span.Contains(opposite_position)) return true; } @@ -198,21 +204,107 @@ // index 0 and don't need to be offset. const auto& existing_entry = subgrid_map.find(pair.key); if (existing_entry != subgrid_map.end()) { - for (auto& value : existing_entry->value) + for (const auto& value : existing_entry->value) { merged_list.push_back(value); + } std::sort(merged_list.begin(), merged_list.end()); } // Override the existing subgrid's line names map with the new merged - // list for this particular line name entry. If `merged_list` list is - // empty, (this can happen when all entries for a particular line name - // are out of the subgrid range), erase the entry entirely, as - // `NGGridNamedLineCollection` doesn't support named line entries - // without values. - if (merged_list.empty()) - subgrid_map.erase(pair.key); - else + // list for this particular line name entry. `merged_list` list can be + // empty if all entries for a particular line name are out of the + // subgrid range. + if (!merged_list.empty()) { subgrid_map.Set(pair.key, merged_list); + } + } + } + }; + auto ExpandAutoRepeatTracksFromParent = + [](NamedGridLinesMap& subgrid_map, + const NamedGridLinesMap& parent_auto_repeat_map, + const blink::ComputedGridTrackList& track_list, GridSpan subgrid_span, + wtf_size_t auto_repetitions, + bool is_opposite_direction_to_parent) -> void { + const wtf_size_t auto_repeat_track_count = + track_list.TrackList().AutoRepeatTrackCount(); + const wtf_size_t auto_repeat_total_tracks = + auto_repeat_track_count * auto_repetitions; + if (auto_repeat_total_tracks == 0) { + return; + } + + // First, we need to offset the existing (non auto repeat) line names that + // come after the auto repeater. This is because they were parsed without + // knowledge of the number of repeats. Now that we know how many auto + // repeats there are, we need to shift the existing entries by the total + // number of auto repeat tracks. + // TODO(kschmi): Do we also need to do this for implicit lines? + const wtf_size_t insertion_point = track_list.auto_repeat_insertion_point; + const wtf_size_t last_auto_repeat_index = + insertion_point + auto_repeat_total_tracks; + for (const auto& pair : subgrid_map) { + WTF::Vector<wtf_size_t> shifted_list; + for (const auto& position : pair.value) { + if (position >= insertion_point) { + wtf_size_t expanded_position = position + last_auto_repeat_index; + // These have already been offset relative to index 0, so explicitly + // do not offset by `subgrid_span` like we do below. + if (subgrid_span.Contains(expanded_position)) { + shifted_list.push_back(expanded_position); + } + } + } + subgrid_map.Set(pair.key, shifted_list); + } + + // Now expand the auto repeaters into `subgrid_map`. + for (const auto& pair : parent_auto_repeat_map) { + Vector<wtf_size_t, 16> merged_list; + for (const auto& position : pair.value) { + // The outer loop is the number of repeats. + for (wtf_size_t i = 0; i < auto_repetitions; ++i) { + // The inner loop expands out a single repeater. + for (wtf_size_t j = 0; j < auto_repeat_track_count; ++j) { + // The expanded position always starts at the insertion point, then + // factors in the line name index, incremented by both auto repeat + // loops. + wtf_size_t expanded_position = insertion_point + position + i + j; + + // Filter out parent named lines that are out of the subgrid range. + // Also offset entries by `subgrid_start_line` before inserting them + // into the merged map so they are all relative to offset 0. These + // are already in ascending order so there's no need to sort. + if (subgrid_span.Contains(expanded_position)) { + if (is_opposite_direction_to_parent) { + merged_list.push_back(subgrid_span.EndLine() - + expanded_position); + } else { + merged_list.push_back(expanded_position - + subgrid_span.StartLine()); + } + } + } + } + + // If there's a name collision, merge the values and sort. These are + // from the subgrid and not the parent, so they are already relative to + // index 0 and don't need to be offset. + const auto& existing_entry = subgrid_map.find(pair.key); + if (existing_entry != subgrid_map.end()) { + for (const auto& value : existing_entry->value) { + merged_list.push_back(value); + } + // TODO(kschmi): Reverse the list if `is_opposite_direction_to_parent` + // and there was no existing entry, as it will be sorted backwards. + std::sort(merged_list.begin(), merged_list.end()); + } + + // If the merged list is empty, it means that all of the entries from + // the parent were out of the subgrid range. + if (!merged_list.empty()) { + subgrid_map.Set(pair.key, merged_list); + } } } }; @@ -230,6 +322,13 @@ *subgridded_columns_merged_implicit_grid_line_names_, parent_line_resolver.ImplicitNamedLinesMap(kForColumns), subgrid_area.columns); + // Expand auto repeaters from the parent into the named line map. + ExpandAutoRepeatTracksFromParent( + *subgridded_columns_merged_explicit_grid_line_names_, + parent_line_resolver.AutoRepeatLineNamesMap(kForColumns), + parent_line_resolver.ComputedGridTrackList(kForColumns), + subgrid_area.columns, parent_line_resolver.AutoRepetitions(kForColumns), + is_opposite_direction_to_parent); } if (subgrid_area.rows.IsTranslatedDefinite()) { MergeNamedGridLinesWithParent( @@ -240,6 +339,13 @@ *subgridded_rows_merged_implicit_grid_line_names_, parent_line_resolver.ImplicitNamedLinesMap(kForRows), subgrid_area.rows); + // Expand auto repeaters from the parent into the named line map. + ExpandAutoRepeatTracksFromParent( + *subgridded_rows_merged_explicit_grid_line_names_, + parent_line_resolver.AutoRepeatLineNamesMap(kForRows), + parent_line_resolver.ComputedGridTrackList(kForRows), subgrid_area.rows, + parent_line_resolver.AutoRepetitions(kForRows), + is_opposite_direction_to_parent); } } @@ -497,6 +603,13 @@ : ComputedGridTrackList(track_direction).named_grid_lines; } +const NamedGridLinesMap& NGGridLineResolver::AutoRepeatLineNamesMap( + GridTrackSizingDirection track_direction) const { + // Auto repeat line names always come from the style object, as they get + // merged into the explicit line names map for subgrids. + return ComputedGridTrackList(track_direction).auto_repeat_named_grid_lines; +} + const blink::ComputedGridTrackList& NGGridLineResolver::ComputedGridTrackList( GridTrackSizingDirection track_direction) const { // TODO(kschmi): Refactor so this isn't necessary and handle auto-repeats
diff --git a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_line_resolver.h b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_line_resolver.h index b07aaa9..1970a8d 100644 --- a/third_party/blink/renderer/core/layout/ng/grid/ng_grid_line_resolver.h +++ b/third_party/blink/renderer/core/layout/ng/grid/ng_grid_line_resolver.h
@@ -76,6 +76,9 @@ const NamedGridLinesMap& ExplicitNamedLinesMap( GridTrackSizingDirection track_direction) const; + const NamedGridLinesMap& AutoRepeatLineNamesMap( + GridTrackSizingDirection track_direction) const; + const blink::ComputedGridTrackList& ComputedGridTrackList( GridTrackSizingDirection track_direction) const;
diff --git a/third_party/blink/renderer/core/page/frame_tree.cc b/third_party/blink/renderer/core/page/frame_tree.cc index 7c0ce10..198c449 100644 --- a/third_party/blink/renderer/core/page/frame_tree.cc +++ b/third_party/blink/renderer/core/page/frame_tree.cc
@@ -42,6 +42,29 @@ const unsigned kInvalidChildCount = ~0U; +void LogDanglingMarkupHistogram(Document* document, const AtomicString& name) { + document->CountUse(WebFeature::kDanglingMarkupInTarget); + if (!name.EndsWith('>')) { + document->CountUse(WebFeature::kDanglingMarkupInTargetNotEndsWithGT); + if (!name.EndsWith('\n')) { + document->CountUse( + WebFeature::kDanglingMarkupInTargetNotEndsWithNewLineOrGT); + } + } +} + +bool ContainsNewLineAndLessThan(const AtomicString& name) { + return name.Contains('\n') && name.Contains('<'); +} + +bool IsRequestFromHtml(FrameLoadRequest& request) { + return request.ClientRedirectReason() == + ClientNavigationReason::kFormSubmissionGet || + request.ClientRedirectReason() == + ClientNavigationReason::kFormSubmissionPost || + request.ClientRedirectReason() == ClientNavigationReason::kAnchorClick; +} + } // namespace FrameTree::FrameTree(Frame* this_frame) @@ -210,6 +233,12 @@ if (request.GetNavigationPolicy() != kNavigationPolicyCurrentTab) return FindResult(current_frame, false); + // Log use counters if the name contains both '\n' and '<'. + if (ContainsNewLineAndLessThan(name) && IsRequestFromHtml(request) && + current_frame->GetDocument()) { + LogDanglingMarkupHistogram(current_frame->GetDocument(), name); + } + const KURL& url = request.GetResourceRequest().Url(); Frame* frame = FindFrameForNavigationInternal(name, url, &request); bool new_window = false;
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc b/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc index c0b4b5bf..359a7dc3 100644 --- a/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc +++ b/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
@@ -2496,9 +2496,8 @@ EXPECT_EQ(nullptr, ScrollableAreaByDOMElementId("displaynone")); } -// Tests that the compositor gets a scroll node for scrollable input boxes, -// which are unique as they are not a composited scroller but also do not have -// NonCompositedMainThreadScrollingReasons. +// Tests that the compositor gets a scroll node for a non-composited (due to +// non-opaque background) scrollable input box. TEST_P(UnifiedScrollingSimTest, ScrollNodeForInputBox) { // This test requires scroll unification. if (!base::FeatureList::IsEnabled(::features::kScrollUnification)) @@ -2518,7 +2517,8 @@ Compositor().BeginFrame(); auto* scrollable_area = ScrollableAreaByDOMElementId("textinput"); - ASSERT_EQ(0u, scrollable_area->GetNonCompositedMainThreadScrollingReasons()); + ASSERT_EQ(cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText, + scrollable_area->GetNonCompositedMainThreadScrollingReasons()); const auto* scroll_node = ScrollNodeForScrollableArea(scrollable_area); ASSERT_TRUE(scroll_node);
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc index 57f7b86..f44f9df 100644 --- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc +++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -2456,20 +2456,6 @@ return !properties->ScrollTranslation()->HasDirectCompositingReasons(); } -static bool LayerNodeMayNeedCompositedScrolling(const PaintLayer* layer) { - // Don't force composite scroll for select or text input elements. - if (Node* node = layer->GetLayoutObject().GetNode()) { - if (IsA<HTMLSelectElement>(node)) - return false; - if (TextControlElement* text_control = EnclosingTextControl(node)) { - if (IsA<HTMLInputElement>(text_control)) { - return false; - } - } - } - return true; -} - bool PaintLayerScrollableArea::ComputeNeedsCompositedScrolling( bool force_prefer_compositing_to_lcd_text) { const auto* box = GetLayoutBox(); @@ -2502,8 +2488,7 @@ } if (!force_prefer_compositing_to_lcd_text && - (RuntimeEnabledFeatures::PreferNonCompositedScrollingEnabled() || - !LayerNodeMayNeedCompositedScrolling(layer_))) { + RuntimeEnabledFeatures::PreferNonCompositedScrollingEnabled()) { return false; }
diff --git a/third_party/blink/renderer/core/speculation_rules/build.gni b/third_party/blink/renderer/core/speculation_rules/build.gni index af1eb80..2386349 100644 --- a/third_party/blink/renderer/core/speculation_rules/build.gni +++ b/third_party/blink/renderer/core/speculation_rules/build.gni
@@ -13,6 +13,8 @@ "speculation_rule.h", "speculation_rule_set.cc", "speculation_rule_set.h", + "speculation_rules_features.cc", + "speculation_rules_features.h", "speculation_rules_header.cc", "speculation_rules_header.h", "speculation_rules_metrics.cc",
diff --git a/third_party/blink/renderer/core/speculation_rules/document_rule_predicate.cc b/third_party/blink/renderer/core/speculation_rules/document_rule_predicate.cc index 95d8bf7..6c4cd39 100644 --- a/third_party/blink/renderer/core/speculation_rules/document_rule_predicate.cc +++ b/third_party/blink/renderer/core/speculation_rules/document_rule_predicate.cc
@@ -14,6 +14,7 @@ #include "third_party/blink/renderer/core/dom/node_computed_style.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/core/html/html_anchor_element.h" +#include "third_party/blink/renderer/core/speculation_rules/speculation_rules_features.h" #include "third_party/blink/renderer/core/url_pattern/url_pattern.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h" #include "third_party/blink/renderer/platform/heap/member.h" @@ -569,8 +570,8 @@ // If predicateType is "selector_matches" if (predicate_type == "selector_matches" && input->size() == 1) { - const bool selector_matches_enabled = RuntimeEnabledFeatures:: - SpeculationRulesDocumentRulesSelectorMatchesEnabled(execution_context); + const bool selector_matches_enabled = + speculation_rules::SelectorMatchesEnabled(execution_context); if (!selector_matches_enabled) { SetParseErrorMessage(out_error, "\"selector_matches\" is currently unsupported.");
diff --git a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc index 602cc37..d7fa7d77 100644 --- a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc +++ b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
@@ -24,6 +24,7 @@ #include "third_party/blink/renderer/core/probe/core_probes.h" #include "third_party/blink/renderer/core/speculation_rules/document_rule_predicate.h" #include "third_party/blink/renderer/core/speculation_rules/speculation_candidate.h" +#include "third_party/blink/renderer/core/speculation_rules/speculation_rules_features.h" #include "third_party/blink/renderer/core/speculation_rules/speculation_rules_metrics.h" #include "third_party/blink/renderer/platform/scheduler/public/event_loop.h" #include "third_party/blink/renderer/platform/weborigin/referrer.h" @@ -762,9 +763,8 @@ if (was_selector_matches_enabled_) { return true; } - was_selector_matches_enabled_ = RuntimeEnabledFeatures:: - SpeculationRulesDocumentRulesSelectorMatchesEnabled( - GetSupplementable()->GetExecutionContext()); + was_selector_matches_enabled_ = speculation_rules::SelectorMatchesEnabled( + GetSupplementable()->GetExecutionContext()); return was_selector_matches_enabled_; }
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc index dd25121f..aff135260 100644 --- a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc +++ b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc
@@ -13,6 +13,7 @@ #include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/core/inspector/identifiers_factory.h" #include "third_party/blink/renderer/core/speculation_rules/document_rule_predicate.h" +#include "third_party/blink/renderer/core/speculation_rules/speculation_rules_features.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/json/json_parser.h" #include "third_party/blink/renderer/platform/json/json_values.h" @@ -76,7 +77,7 @@ "referrer_policy", "relative_to"}; const auto kConditionalKnownKeys = [context]() { Vector<const char*, 4> conditional_known_keys; - if (RuntimeEnabledFeatures::SpeculationRulesEagernessEnabled(context)) { + if (speculation_rules::EagernessEnabled(context)) { conditional_known_keys.push_back("eagerness"); } if (RuntimeEnabledFeatures::SpeculationRulesNoVarySearchHintEnabled( @@ -303,7 +304,7 @@ absl::optional<mojom::blink::SpeculationEagerness> eagerness; if (JSONValue* eagerness_value = input->Get("eagerness")) { // Feature gated due to known keys check above. - DCHECK(RuntimeEnabledFeatures::SpeculationRulesEagernessEnabled(context)); + DCHECK(speculation_rules::EagernessEnabled(context)); String eagerness_str; if (!eagerness_value->AsString(&eagerness_str)) {
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rules_features.cc b/third_party/blink/renderer/core/speculation_rules/speculation_rules_features.cc new file mode 100644 index 0000000..8009d7c --- /dev/null +++ b/third_party/blink/renderer/core/speculation_rules/speculation_rules_features.cc
@@ -0,0 +1,26 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/speculation_rules/speculation_rules_features.h" + +#include "base/feature_list.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/renderer/platform/runtime_enabled_features.h" + +namespace blink::speculation_rules { + +bool EagernessEnabled(const FeatureContext* context) { + return RuntimeEnabledFeatures::SpeculationRulesEagernessEnabled(context) && + base::FeatureList::IsEnabled( + blink::features::kSpeculationRulesEagerness); +} + +bool SelectorMatchesEnabled(const FeatureContext* context) { + return RuntimeEnabledFeatures:: + SpeculationRulesDocumentRulesSelectorMatchesEnabled(context) && + base::FeatureList::IsEnabled( + blink::features::kSpeculationRulesDocumentRulesSelectorMatches); +} + +} // namespace blink::speculation_rules
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rules_features.h b/third_party/blink/renderer/core/speculation_rules/speculation_rules_features.h new file mode 100644 index 0000000..b6965e5 --- /dev/null +++ b/third_party/blink/renderer/core/speculation_rules/speculation_rules_features.h
@@ -0,0 +1,28 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_SPECULATION_RULES_SPECULATION_RULES_FEATURES_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_SPECULATION_RULES_SPECULATION_RULES_FEATURES_H_ + +namespace blink { + +class FeatureContext; + +namespace speculation_rules { + +// You might be asking, why not just check RuntimeEnabledFeatures? +// +// The answer is that the REF is turned on by an origin trial (namely, +// SpeculationRulesPrefetchFuture), and that works even if the associated +// base::Feature is disabled. The simplest solution seems to be this little +// hack -- just check that the base::Feature is actually on before actually +// using the feature. This suffices because these are not used to control WebIDL +// exposure. +bool EagernessEnabled(const FeatureContext*); +bool SelectorMatchesEnabled(const FeatureContext*); + +} // namespace speculation_rules +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_SPECULATION_RULES_SPECULATION_RULES_FEATURES_H_
diff --git a/third_party/blink/renderer/core/svg/svg_a_element.cc b/third_party/blink/renderer/core/svg/svg_a_element.cc index ad27ac7..a7aab3bc 100644 --- a/third_party/blink/renderer/core/svg/svg_a_element.cc +++ b/third_party/blink/renderer/core/svg/svg_a_element.cc
@@ -140,6 +140,8 @@ GetDocument().domWindow(), ResourceRequest(GetDocument().CompleteURL(url))); frame_request.SetNavigationPolicy(NavigationPolicyFromEvent(&event)); + frame_request.SetClientRedirectReason( + ClientNavigationReason::kAnchorClick); frame_request.SetTriggeringEventInfo( event.isTrusted() ? mojom::blink::TriggeringEventInfo::kFromTrustedEvent
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc index 727c8e6f..456dc77 100644 --- a/third_party/blink/renderer/core/testing/internals.cc +++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -2133,6 +2133,18 @@ .ForceInvocationForTesting(); } +bool Internals::hasLastEditCommand(Document* document, + ExceptionState& exception_state) { + if (!document || !document->GetFrame()) { + exception_state.ThrowDOMException( + DOMExceptionCode::kInvalidAccessError, + "No frame can be obtained from the provided document."); + return false; + } + + return document->GetFrame()->GetEditor().LastEditCommand(); +} + Vector<AtomicString> Internals::userPreferredLanguages() const { return blink::UserPreferredLanguages(); }
diff --git a/third_party/blink/renderer/core/testing/internals.h b/third_party/blink/renderer/core/testing/internals.h index 75d2205..d72743d 100644 --- a/third_party/blink/renderer/core/testing/internals.h +++ b/third_party/blink/renderer/core/testing/internals.h
@@ -292,6 +292,8 @@ String idleTimeSpellCheckerState(Document*, ExceptionState&); void runIdleTimeSpellChecker(Document*, ExceptionState&); + bool hasLastEditCommand(Document*, ExceptionState&); + Vector<AtomicString> userPreferredLanguages() const; void setUserPreferredLanguages(const Vector<String>&); void setSystemTimeZone(const String&);
diff --git a/third_party/blink/renderer/core/testing/internals.idl b/third_party/blink/renderer/core/testing/internals.idl index 2b21f09..e607475 100644 --- a/third_party/blink/renderer/core/testing/internals.idl +++ b/third_party/blink/renderer/core/testing/internals.idl
@@ -148,6 +148,8 @@ [RaisesException] DOMString idleTimeSpellCheckerState(Document document); [RaisesException] void runIdleTimeSpellChecker(Document document); + [RaisesException] boolean hasLastEditCommand(Document document); + sequence<DOMString> userPreferredLanguages(); void setUserPreferredLanguages(sequence<DOMString> languages); void setSystemTimeZone(DOMString timezone);
diff --git a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager_test.cc b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager_test.cc index 531cc5b..65c63e8 100644 --- a/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager_test.cc +++ b/third_party/blink/renderer/modules/media/audio/audio_renderer_mixer_manager_test.cc
@@ -721,7 +721,7 @@ EXPECT_EQ(output_sample_rate, mixer->get_output_params_for_testing().sample_rate()); -#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_MAC) || \ +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_APPLE) || \ BUILDFLAG(IS_FUCHSIA) // Use 10 ms buffer (441 frames per buffer). EXPECT_EQ(output_sample_rate / 100,
diff --git a/third_party/blink/renderer/modules/scheduler/BUILD.gn b/third_party/blink/renderer/modules/scheduler/BUILD.gn index 0d799dd..47c380d6 100644 --- a/third_party/blink/renderer/modules/scheduler/BUILD.gn +++ b/third_party/blink/renderer/modules/scheduler/BUILD.gn
@@ -20,5 +20,7 @@ "task_attribution_tracker_impl.h", "task_priority_change_event.cc", "task_priority_change_event.h", + "window_idle_tasks.cc", + "window_idle_tasks.h", ] }
diff --git a/third_party/blink/renderer/modules/scheduler/window_idle_tasks.cc b/third_party/blink/renderer/modules/scheduler/window_idle_tasks.cc new file mode 100644 index 0000000..a08764b --- /dev/null +++ b/third_party/blink/renderer/modules/scheduler/window_idle_tasks.cc
@@ -0,0 +1,66 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/scheduler/window_idle_tasks.h" + +#include "third_party/blink/renderer/bindings/core/v8/v8_idle_request_callback.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_idle_request_options.h" +#include "third_party/blink/renderer/core/dom/scripted_idle_task_controller.h" +#include "third_party/blink/renderer/core/frame/local_dom_window.h" +#include "third_party/blink/renderer/platform/bindings/script_state.h" +#include "third_party/blink/renderer/platform/heap/garbage_collected.h" +#include "third_party/blink/renderer/platform/scheduler/public/task_attribution_tracker.h" +#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" + +namespace blink { + +namespace { + +// `V8IdleTask` is the adapter class for the conversion from +// `V8IdleRequestCallback` to `IdleTask`. +class V8IdleTask : public IdleTask { + public: + static V8IdleTask* Create(V8IdleRequestCallback* callback) { + return MakeGarbageCollected<V8IdleTask>(callback); + } + + explicit V8IdleTask(V8IdleRequestCallback* callback) : callback_(callback) {} + + ~V8IdleTask() override = default; + + void invoke(IdleDeadline* deadline) override { + callback_->InvokeAndReportException(nullptr, deadline); + } + + void Trace(Visitor* visitor) const override { + visitor->Trace(callback_); + IdleTask::Trace(visitor); + } + + private: + Member<V8IdleRequestCallback> callback_; +}; + +} // namespace + +int WindowIdleTasks::requestIdleCallback(LocalDOMWindow& window, + V8IdleRequestCallback* callback, + const IdleRequestOptions* options) { + if (!window.GetFrame()) { + return 0; + } + ScriptState* script_state = callback->CallbackRelevantScriptState(); + auto* tracker = ThreadScheduler::Current()->GetTaskAttributionTracker(); + if (tracker && script_state->World().IsMainWorld()) { + callback->SetParentTaskId(tracker->RunningTaskAttributionId(script_state)); + } + return window.document()->RequestIdleCallback(V8IdleTask::Create(callback), + options); +} + +void WindowIdleTasks::cancelIdleCallback(LocalDOMWindow& window, int id) { + window.document()->CancelIdleCallback(id); +} + +} // namespace blink
diff --git a/third_party/blink/renderer/modules/scheduler/window_idle_tasks.h b/third_party/blink/renderer/modules/scheduler/window_idle_tasks.h new file mode 100644 index 0000000..271c36d --- /dev/null +++ b/third_party/blink/renderer/modules/scheduler/window_idle_tasks.h
@@ -0,0 +1,27 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_WINDOW_IDLE_TASKS_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_WINDOW_IDLE_TASKS_H_ + +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" + +namespace blink { +class IdleRequestOptions; +class LocalDOMWindow; +class V8IdleRequestCallback; + +class WindowIdleTasks { + STATIC_ONLY(WindowIdleTasks); + + public: + static int requestIdleCallback(LocalDOMWindow&, + V8IdleRequestCallback*, + const IdleRequestOptions*); + static void cancelIdleCallback(LocalDOMWindow&, int id); +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_WINDOW_IDLE_TASKS_H_
diff --git a/third_party/blink/renderer/modules/scheduler/window_idle_tasks.idl b/third_party/blink/renderer/modules/scheduler/window_idle_tasks.idl new file mode 100644 index 0000000..cc3c089 --- /dev/null +++ b/third_party/blink/renderer/modules/scheduler/window_idle_tasks.idl
@@ -0,0 +1,12 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Cooperative Scheduling of Background Tasks +// https://www.w3.org/TR/requestidlecallback/#window_extensions +[ + ImplementedAs=WindowIdleTasks +] partial interface Window { + [Measure] long requestIdleCallback(IdleRequestCallback callback, optional IdleRequestOptions options = {}); + void cancelIdleCallback(long handle); +};
diff --git a/third_party/blink/renderer/platform/README.md b/third_party/blink/renderer/platform/README.md new file mode 100644 index 0000000..1605814e --- /dev/null +++ b/third_party/blink/renderer/platform/README.md
@@ -0,0 +1,32 @@ +# Blink Renderer Platform + +The `renderer/platform/` directory contains lower-level, self-contained +abstractions that `core/` and `modules/` can depend on. + +See [renderer/README.md](../README.md) for more about the relationship of +`platform/` to `core/` and `modules/`. + +Here is a non-exhaustive list of some major things in `renderer/platform/`: + +* [Runtime Enabled Features](RuntimeEnabledFeatures.md) are runtime flags for + new web-exposed features +* [`bindings/`](bindings/README.md) contains reusable components for the V8-DOM + bindings layer +* `exported/` implements some classes in the [Blink Public + API](../../public/README.md) which are declared in + [`public/platform/`](../../public/platform/), including + [blink::Platform](../../public/platform/platform.h) which initializes Blink +* [`fonts/`](fonts/README.md) and `text/` contain Blink's font and text stack +* [`graphics/`](graphics/README.md) contains graphics support code including + the [Blink compositing algorithm](graphics/compositing/README.md) +* [`heap/`](heap/README.md) contains the Blink GC system (a.k.a. Oilpan) +* [`loader/`](loader/README.md) contains functionality for loading resources + from the network +* [`scheduler/`](scheduler/README.md) contains the Blink Scheduler which + coordinates task execution in renderer processes +* [`widget/`](widget/) handles input and compositing; + [WidgetBase](widget/widget_base.h) owns + [LayerTreeView](widget/compositing/layer_tree_view.h) which wraps + [`cc/`](../../../../cc/README.md) (the renderer compositor) +* [`wtf/`](wtf/README.md) (Web Template Framework) is a library of containers + and other basic functionalities
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc index 4a9e2b6..17e9100c 100644 --- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc +++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer_test.cc
@@ -565,7 +565,13 @@ }}); } -TEST_F(ShapeResultBloberizerTest, SupplementaryMultiRunNG) { +// TODO(crbug.com/1422946): not yet supported on iOS. +#if BUILDFLAG(IS_IOS) +#define MAYBE_SupplementaryMultiRunNG DISABLED_SupplementaryMultiRunNG +#else +#define MAYBE_SupplementaryMultiRunNG SupplementaryMultiRunNG +#endif // BUILDFLAG(IS_IOS) +TEST_F(ShapeResultBloberizerTest, MAYBE_SupplementaryMultiRunNG) { TextDirection direction = TextDirection::kLtr; // 𠜎𠜱𠝹𠱓𠱸𠲖𠳏𠳕 const UChar kStrSupp[] = {0xD841, 0xDF0E, 0xD841, 0xDF31, 0xD841, 0xDF79,
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index 028ab25..5db71617 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -839,7 +839,7 @@ // image-set values without the `-webkit-` prefix // will be parsed. name: "CSSImageSet", - status: "experimental", + status: "stable", }, { name: "CSSIndependentTransformProperties", @@ -1362,6 +1362,12 @@ base_feature: "none", }, { + // Kill-switch for fixes to a bug that detached input elements are retained. + // See crbug.com/1413100 + name: "DontLeakDetachedInput", + status: "stable", + }, + { name: "EarlyHintsPreloadForNavigationOptIn", origin_trial_feature_name: "EarlyHintsPreloadForNavigation", status: "stable", @@ -3111,11 +3117,17 @@ }, { name: "SpeculationRulesDocumentRulesSelectorMatches", - base_feature: "none", + base_feature_status: "enabled", + copied_from_base_feature_if: "overridden", + origin_trial_feature_name: "SpeculationRulesPrefetchFuture", + origin_trial_allows_third_party: true, }, { name: "SpeculationRulesEagerness", - base_feature: "none", + base_feature_status: "enabled", + copied_from_base_feature_if: "overridden", + origin_trial_feature_name: "SpeculationRulesPrefetchFuture", + origin_trial_allows_third_party: true, }, { name: "SpeculationRulesFetchFromHeader", @@ -3133,6 +3145,7 @@ }, { name: "SpeculationRulesPointerHoverHeuristics", + base_feature_status: "enabled", }, // This feature exists solely to be the target of implied_by for features // that are part of this trial but which may also be enabled another way.
diff --git a/third_party/blink/tools/blinkpy/tool/blink_tool.py b/third_party/blink/tools/blinkpy/tool/blink_tool.py index c19043e..aa2bcfa 100644 --- a/third_party/blink/tools/blinkpy/tool/blink_tool.py +++ b/third_party/blink/tools/blinkpy/tool/blink_tool.py
@@ -74,7 +74,7 @@ self._path = path self.commands = [ AnalyzeBaselines(), - CopyExistingBaselines(), + CopyExistingBaselines(self), CrashLog(), FlakyTests(), OptimizeBaselines(),
diff --git a/third_party/blink/tools/blinkpy/tool/blink_tool_unittest.py b/third_party/blink/tools/blinkpy/tool/blink_tool_unittest.py index 77529f86..ed1c900 100644 --- a/third_party/blink/tools/blinkpy/tool/blink_tool_unittest.py +++ b/third_party/blink/tools/blinkpy/tool/blink_tool_unittest.py
@@ -10,6 +10,8 @@ from blinkpy.tool.blink_tool import BlinkTool +@unittest.skip('Temporarily disabled while crbug.com/1324638 is being fixed; ' + 'reenable once `copy-existing-baselines` is removed') # Avoid creating a real `Git` object, since it runs a command in its # constructor. @patch('blinkpy.tool.blink_tool.BlinkTool.git',
diff --git a/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines.py b/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines.py index 9c881ba..d0aa675 100644 --- a/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines.py +++ b/third_party/blink/tools/blinkpy/tool/commands/copy_existing_baselines.py
@@ -6,7 +6,6 @@ from blinkpy.common.checkout.baseline_copier import BaselineCopier from blinkpy.tool.commands.rebaseline import AbstractRebaseliningCommand -from blinkpy.web_tests.models.test_expectations import TestExpectationsCache _log = logging.getLogger(__name__) @@ -17,24 +16,24 @@ 'order to ensure new baselines don\'t break existing passing ' 'platforms.') - def __init__(self): - super(CopyExistingBaselines, self).__init__(options=[ + def __init__(self, tool): + super().__init__(options=[ self.test_option, self.suffixes_option, self.port_name_option, self.flag_specific_option, self.results_directory_option, ]) - self._exp_cache = TestExpectationsCache() + self._copier = BaselineCopier(tool) def execute(self, options, args, tool): # TODO(crbug.com/1324638): Remove this command. Have `rebaseline-cl` # call `find_baselines_to_copy(...)` directly to find implied all-pass # baselines, then copy all ports/tests together if OK. - copier = BaselineCopier(tool) port = tool.port_factory.get(options.port_name) if options.flag_specific: port.set_option_default('flag_specific', options.flag_specific) for suffix in options.suffixes.split(','): - copier.write_copies( - copier.find_baselines_to_copy(options.test, suffix, [port])) + self._copier.write_copies( + self._copier.find_baselines_to_copy(options.test, suffix, + [port]))
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 2d51c0e..e6f21211 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -1118,7 +1118,6 @@ crbug.com/882385 external/wpt/css/css-contain/quote-scoping-002.html [ Failure ] crbug.com/882385 external/wpt/css/css-contain/quote-scoping-003.html [ Failure ] crbug.com/882385 external/wpt/css/css-contain/quote-scoping-004.html [ Failure ] -crbug.com/1315275 external/wpt/css/css-contain/content-visibility/content-visibility-canvas.html [ Failure ] crbug.com/1315275 external/wpt/css/css-contain/content-visibility/content-visibility-video.html [ Failure ] crbug.com/1400979 external/wpt/css/css-contain/container-queries/nested-query-containers.html [ Crash Failure Pass Timeout ] @@ -3486,11 +3485,7 @@ crbug.com/618969 external/wpt/css/css-grid/subgrid/orthogonal-writing-mode-002.html [ Failure ] crbug.com/618969 external/wpt/css/css-grid/subgrid/orthogonal-writing-mode-003.html [ Failure ] crbug.com/618969 external/wpt/css/css-grid/subgrid/orthogonal-writing-mode-004.html [ Failure ] -crbug.com/618969 external/wpt/css/css-grid/subgrid/parent-repeat-auto-fit-001.html [ Failure ] -crbug.com/618969 external/wpt/css/css-grid/subgrid/parent-repeat-auto-fit-002.html [ Failure ] -crbug.com/618969 external/wpt/css/css-grid/subgrid/repeat-auto-fill-002.html [ Failure ] crbug.com/618969 external/wpt/css/css-grid/subgrid/repeat-auto-fill-003.html [ Failure ] -crbug.com/618969 external/wpt/css/css-grid/subgrid/repeat-auto-fill-004.html [ Failure ] crbug.com/618969 external/wpt/css/css-grid/subgrid/subgrid-baseline-001.html [ Failure ] crbug.com/618969 external/wpt/css/css-grid/subgrid/subgrid-baseline-002.html [ Crash Failure Timeout ]
diff --git a/third_party/blink/web_tests/compositing/dont-composite-select-elements.html b/third_party/blink/web_tests/compositing/dont-composite-select-elements.html deleted file mode 100644 index 24bc0bf..0000000 --- a/third_party/blink/web_tests/compositing/dont-composite-select-elements.html +++ /dev/null
@@ -1,18 +0,0 @@ -<!doctype HTML> -<script src="../resources/testharness.js"></script> -<script src="../resources/testharnessreport.js"></script> -<select size="2"> - <option> value 1</option> - <option> value 2</option> - <option> value 3</option> - <option> value 4</option> -</select> -<script> -if (window.internals) - internals.settings.setPreferCompositingToLCDTextEnabled(true); -test(function() { - var json = JSON.parse(internals.layerTreeAsText(document)); - // The <select> element's scroller should be painted into the root layer. - assert_equals(json["layers"].length, 1); -}, "test"); -</script>
diff --git a/third_party/blink/web_tests/compositing/dont-composite-text-input-elements.html b/third_party/blink/web_tests/compositing/dont-composite-text-input-elements.html deleted file mode 100644 index f5a2f74..0000000 --- a/third_party/blink/web_tests/compositing/dont-composite-text-input-elements.html +++ /dev/null
@@ -1,13 +0,0 @@ -<!doctype HTML> -<script src="../resources/testharness.js"></script> -<script src="../resources/testharnessreport.js"></script> -<input width=10 value="This is a long truncated text entry" style="font-size:40pt;"/> -<script> -if (window.internals) - internals.settings.setPreferCompositingToLCDTextEnabled(true); -test(function() { - var json = JSON.parse(internals.layerTreeAsText(document)); - // The <input> element's scroller should be painted into the root layer. - assert_equals(json["layers"].length, 1); -}, "test"); -</script>
diff --git a/third_party/blink/web_tests/compositing/select-element.html b/third_party/blink/web_tests/compositing/select-element.html new file mode 100644 index 0000000..6e9aa2e --- /dev/null +++ b/third_party/blink/web_tests/compositing/select-element.html
@@ -0,0 +1,25 @@ +<!doctype HTML> +<script src="../resources/testharness.js"></script> +<script src="../resources/testharnessreport.js"></script> +<select id="target" size="2"> + <option> value 1</option> + <option> value 2</option> + <option> value 3</option> + <option> value 4</option> +</select> +<script> +if (window.internals) { + internals.settings.setPreferCompositingToLCDTextEnabled(true); +} +function assertLayers(expected_count) { + var json = JSON.parse(internals.layerTreeAsText(document)); + assert_equals(json.layers.length, expected_count, json); +} +test(() => { + assertLayers(4); +}, "scrollable"); +test(() => { + target.size = 4; + assertLayers(1); +}, "not scrollable"); +</script>
diff --git a/third_party/blink/web_tests/compositing/text-input-element.html b/third_party/blink/web_tests/compositing/text-input-element.html new file mode 100644 index 0000000..75ffaf5 --- /dev/null +++ b/third_party/blink/web_tests/compositing/text-input-element.html
@@ -0,0 +1,20 @@ +<!doctype HTML> +<script src="../resources/testharness.js"></script> +<script src="../resources/testharnessreport.js"></script> +<input id="target" width=10 value="This is a long truncated text entry" style="font-size:40pt;"/> +<script> +if (window.internals) { + internals.settings.setPreferCompositingToLCDTextEnabled(true); +} +function assertLayers(expected_count) { + var json = JSON.parse(internals.layerTreeAsText(document)); + assert_equals(json.layers.length, expected_count, json); +} +test(() => { + assertLayers(3); +}, "scrollable"); +test(() => { + target.value = "short"; + assertLayers(1); +}, "not scrollable"); +</script>
diff --git a/third_party/blink/web_tests/editing/inserting/typing-leaks-detached-input.html b/third_party/blink/web_tests/editing/inserting/typing-leaks-detached-input.html new file mode 100644 index 0000000..304f6efc --- /dev/null +++ b/third_party/blink/web_tests/editing/inserting/typing-leaks-detached-input.html
@@ -0,0 +1,70 @@ +<!DOCTYPE html> +<title>Tests that typing should not retain detached subtree</title> +<script src="../../resources/testharness.js"></script> +<script src="../../resources/testharnessreport.js"></script> + +<div id="root"></div> + +<script> +const n = 100; + +function createSubtree() { + let last = document.createElement('div'); + last.spellcheck = false; + last.contentEditable = true; + last.id = 'target'; + for (let i = 0; i < n; ++i) { + let div = document.createElement('div'); + div.appendChild(last); + last = div; + } + root.appendChild(last); +} + +function raf() { + return new Promise(resolve => requestAnimationFrame(resolve)); +} + +promise_setup(async () => { + assert_own_property(window, 'internals', 'This test requires internals') +}) + +promise_test(async () => { + gc(); + let nodesBefore = internals.numberOfLiveNodes(); + + createSubtree(); + document.getElementById('target').focus(); + document.execCommand('insertText', false, 'foo'); + assert_true(internals.hasLastEditCommand(document)); + + root.firstChild.remove(); + + await raf(); + await raf(); + + gc(); + let nodesAfter = internals.numberOfLiveNodes(); + + // Give the test some leeway, which should be safe since the size of the + // detached subtree is much larger than this. + assert_less_than_equal(nodesAfter, nodesBefore + 20); + assert_false(internals.hasLastEditCommand(document)); +}, 'Typing should not retain detached subtree'); + +promise_test(async () => { + createSubtree(); + document.getElementById('target').focus(); + document.execCommand('insertText', false, 'foo'); + document.getElementById('target').blur(); + assert_true(internals.hasLastEditCommand(document)); + + await raf(); + await raf(); + + gc(); + assert_true(internals.hasLastEditCommand(document)); +}, 'Last edit command should be retained if node is still connected'); +</script> + +</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/content-visibility-canvas.html.ini b/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/content-visibility-canvas.html.ini deleted file mode 100644 index d1d71c8..0000000 --- a/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/content-visibility-canvas.html.ini +++ /dev/null
@@ -1,2 +0,0 @@ -[content-visibility-canvas.html] - expected: FAIL
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/auto-007-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/auto-007-expected.txt deleted file mode 100644 index 3cba385e..0000000 --- a/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/auto-007-expected.txt +++ /dev/null
@@ -1,51 +0,0 @@ -This is a testharness.js-based test. -PASS .test 1 -PASS .test 2 -PASS .test 3 -PASS .test 4 -PASS .test 5 -PASS .test 6 -PASS .test 7 -PASS .test 8 -PASS .test 9 -PASS .test 10 -PASS .test 11 -PASS .test 12 -PASS .test 13 -PASS .test 14 -PASS .test 15 -PASS .test 16 -PASS .test 17 -PASS .test 18 -PASS .test 19 -PASS .test 20 -PASS .test 21 -PASS .test 22 -PASS .test 23 -PASS .test 24 -FAIL .test 25 assert_equals: -<canvas class="test auto-width" data-expected-client-width="300" data-expected-client-height="0"></canvas> -clientWidth expected 300 but got 1 -FAIL .test 26 assert_equals: -<canvas class="test auto-height" data-expected-client-width="0" data-expected-client-height="150"></canvas> -clientHeight expected 150 but got 2 -FAIL .test 27 assert_equals: -<canvas class="test auto-both" data-expected-client-width="300" data-expected-client-height="150"></canvas> -clientWidth expected 300 but got 3 -PASS .test 28 -PASS .test 29 -PASS .test 30 -PASS .test 31 -PASS .test 32 -PASS .test 33 -PASS .test 34 -PASS .test 35 -PASS .test 36 -PASS .test 37 -PASS .test 38 -PASS .test 39 -PASS .test 40 -PASS .test 41 -PASS .test 42 -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/auto-007.html.ini b/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/auto-007.html.ini deleted file mode 100644 index 9182f6c..0000000 --- a/third_party/blink/web_tests/external/wpt/css/css-sizing/contain-intrinsic-size/auto-007.html.ini +++ /dev/null
@@ -1,15 +0,0 @@ -[auto-007.html] - [.test 7] - expected: FAIL - - [.test 9] - expected: FAIL - - [.test 25] - expected: FAIL - - [.test 26] - expected: FAIL - - [.test 27] - expected: FAIL
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-shorthand.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-shorthand.html index 60d6c76..87e66d0 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-shorthand.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-shorthand.html
@@ -40,4 +40,53 @@ 'animation-name': 'anim1, anim2, anim3', 'animation-timeline': 'auto, auto, auto' }); + +test((t) => { + t.add_cleanup(() => { + target.style = ''; + }); + + target.style.animation = 'anim 1s'; + target.style.animationTimeline = 'timeline'; + assert_equals(target.style.animation, ''); + assert_equals(target.style.animationName, 'anim'); + assert_equals(target.style.animationDuration, '1s'); +}, 'Animation shorthand can not represent non-initial timelines (specified)'); + +test((t) => { + t.add_cleanup(() => { + target.style = ''; + }); + + target.style.animation = 'anim 1s'; + target.style.animationTimeline = 'timeline'; + assert_equals(getComputedStyle(target).animation, ''); + assert_equals(getComputedStyle(target).animationName, 'anim'); + assert_equals(getComputedStyle(target).animationDuration, '1s'); +}, 'Animation shorthand can not represent non-initial timelines (computed)'); + +test((t) => { + t.add_cleanup(() => { + target.style = ''; + }); + + target.style.animation = 'anim 1s'; + target.style.animationDelayEnd = '42s'; + assert_equals(target.style.animation, ''); + assert_equals(target.style.animationName, 'anim'); + assert_equals(target.style.animationDuration, '1s'); +}, 'Animation shorthand can not represent non-initial animation-delay-end (specified)'); + +test((t) => { + t.add_cleanup(() => { + target.style = ''; + }); + + target.style.animation = 'anim 1s'; + target.style.animationDelayEnd = '42s'; + assert_equals(getComputedStyle(target).animation, ''); + assert_equals(getComputedStyle(target).animationName, 'anim'); + assert_equals(getComputedStyle(target).animationDuration, '1s'); +}, 'Animation shorthand can not represent non-initial animation-delay-end (computed)'); + </script>
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/fast/forms/form-element-geometry-expected.png b/third_party/blink/web_tests/flag-specific/highdpi/fast/forms/form-element-geometry-expected.png index 05249f1e..2583671 100644 --- a/third_party/blink/web_tests/flag-specific/highdpi/fast/forms/form-element-geometry-expected.png +++ b/third_party/blink/web_tests/flag-specific/highdpi/fast/forms/form-element-geometry-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/fragmentation/outline-crossing-columns-expected.png b/third_party/blink/web_tests/flag-specific/highdpi/fragmentation/outline-crossing-columns-expected.png index 906ae22..06acb4e 100644 --- a/third_party/blink/web_tests/flag-specific/highdpi/fragmentation/outline-crossing-columns-expected.png +++ b/third_party/blink/web_tests/flag-specific/highdpi/fragmentation/outline-crossing-columns-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-expected.txt index c69fae62..fc2bf6a 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-expected.txt +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-expected.txt
@@ -1,3 +1,13 @@ Check that the dialogShown event works after enabling the FedCm domain OK +accounts: [ + [0] : { + accountId : 1234 + email : john_doe@idp.example + givenName : John + idpConfigUrl : https://devtools.test:8443/resources/fedcm/fedcm.json + name : John Doe + pictureUrl : https://idp.example/profile/123 + } +]
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event.js index 16d66e14..8fb0327 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event.js +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event.js
@@ -11,6 +11,11 @@ const result = await session.evaluateAsync("triggerDialog()"); testRunner.log(result); - await dp.FedCm.onceDialogShown(); + let msg = await dp.FedCm.onceDialogShown(); + if (msg.error) { + testRunner.log(msg.error); + } else { + testRunner.log(msg.params.accounts, "accounts: "); + } testRunner.completeTest(); })
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt index cb66a88a..8e7f1da 100644 --- a/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt +++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -10,7 +10,6 @@ "name": "LayoutNGTextControlSingleLine INPUT id='root'", "position": [-1, -1], "bounds": [67, 24], - "contentsOpaqueForText": true, "backgroundColor": "#FFFFFF", "invalidations": [ [0, 0, 67, 24] @@ -18,6 +17,20 @@ "transform": 1 }, { + "name": "LayoutNGTextControlInnerEditor DIV", + "bounds": [57, 16], + "drawsContent": false, + "transform": 2 + }, + { + "name": "LayoutNGTextControlInnerEditor DIV", + "bounds": [74, 16], + "invalidations": [ + [0, 0, 74, 16] + ], + "transform": 3 + }, + { "name": "Caret", "position": [56, 0], "bounds": [1, 16], @@ -56,6 +69,16 @@ [0, 0, 1, 0], [4, 3, 0, 1] ] + }, + { + "id": 3, + "parent": 2, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [-17, 0, 0, 1] + ] } ] }
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png b/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png index b9066c4..1127fe6 100644 --- a/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png +++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt index 099340a..465c136 100644 --- a/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt +++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
@@ -1,13 +1,56 @@ { "layers": [ { - "name": "Scrolling background of LayoutView #document", + "name": "Scrolling background of LayoutNGView #document", "bounds": [800, 600], "contentsOpaque": true, "backgroundColor": "#FFFFFF", "invalidations": [ [7, 7, 67, 24] ] + }, + { + "name": "LayoutNGTextControlInnerEditor DIV", + "bounds": [57, 16], + "drawsContent": false, + "transform": 1 + }, + { + "name": "LayoutNGTextControlInnerEditor DIV", + "bounds": [74, 16], + "invalidations": [ + [0, 0, 74, 16] + ], + "transform": 2 + }, + { + "name": "LayoutNGTextControlSingleLine INPUT id='target'", + "position": [7, 7], + "bounds": [67, 24], + "invalidations": [ + [0, 0, 67, 24] + ] + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [12, 11, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [-17, 0, 0, 1] + ] } ] }
diff --git a/third_party/blink/web_tests/platform/linux/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/blink/web_tests/platform/linux/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png new file mode 100644 index 0000000..0ccecb0 --- /dev/null +++ b/third_party/blink/web_tests/platform/linux/virtual/prefer_compositing_to_lcd_text/scrollbars/listbox-scrollbar-combinations-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/fast/forms/form-element-geometry-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/fast/forms/form-element-geometry-expected.png new file mode 100644 index 0000000..3b2cbcd --- /dev/null +++ b/third_party/blink/web_tests/platform/mac-mac10.13/fast/forms/form-element-geometry-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/basic-inputs-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/basic-inputs-expected.png index f5cf310..ecd1c69 100644 --- a/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/basic-inputs-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/fast/forms/basic-inputs-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac11-arm64/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/blink/web_tests/platform/mac-mac11-arm64/scrollbars/listbox-scrollbar-combinations-expected.png index 5366fc5..692175e 100644 --- a/third_party/blink/web_tests/platform/mac-mac11-arm64/scrollbars/listbox-scrollbar-combinations-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac11-arm64/scrollbars/listbox-scrollbar-combinations-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/forms/basic-inputs-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/forms/basic-inputs-expected.png index f5cf310..ecd1c69 100644 --- a/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/forms/basic-inputs-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/fast/forms/basic-inputs-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac12-arm64/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/blink/web_tests/platform/mac-mac12-arm64/scrollbars/listbox-scrollbar-combinations-expected.png index 5366fc5..692175e 100644 --- a/third_party/blink/web_tests/platform/mac-mac12-arm64/scrollbars/listbox-scrollbar-combinations-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac12-arm64/scrollbars/listbox-scrollbar-combinations-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/forms/basic-inputs-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/forms/basic-inputs-expected.png index f5cf310..ecd1c69 100644 --- a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/forms/basic-inputs-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/forms/basic-inputs-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/scrollbars/listbox-scrollbar-combinations-expected.png index 5366fc5..692175e 100644 --- a/third_party/blink/web_tests/platform/mac-mac13-arm64/scrollbars/listbox-scrollbar-combinations-expected.png +++ b/third_party/blink/web_tests/platform/mac-mac13-arm64/scrollbars/listbox-scrollbar-combinations-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/basic-inputs-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/basic-inputs-expected.png index cd7ebc1..68c1ee31 100644 --- a/third_party/blink/web_tests/platform/mac/fast/forms/basic-inputs-expected.png +++ b/third_party/blink/web_tests/platform/mac/fast/forms/basic-inputs-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png index a6372d3..5ad0792 100644 --- a/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png +++ b/third_party/blink/web_tests/platform/mac/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/form-element-geometry-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/form-element-geometry-expected.png index 3b2cbcd..ee3ff37 100644 --- a/third_party/blink/web_tests/platform/mac/fast/forms/form-element-geometry-expected.png +++ b/third_party/blink/web_tests/platform/mac/fast/forms/form-element-geometry-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-appearance-basic-expected.png index 27bb60b..eb4bb3c9 100644 --- a/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-appearance-basic-expected.png +++ b/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-clip-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-clip-expected.png index 753b8dc..33893c95 100644 --- a/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-clip-expected.png +++ b/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-clip-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-scrollbar-incremental-load-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-scrollbar-incremental-load-expected.png index e79b3e3..683e496 100644 --- a/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-scrollbar-incremental-load-expected.png +++ b/third_party/blink/web_tests/platform/mac/fast/forms/select/listbox-scrollbar-incremental-load-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/select/menulist-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/select/menulist-appearance-basic-expected.png index 51422d6..44d9b79 100644 --- a/third_party/blink/web_tests/platform/mac/fast/forms/select/menulist-appearance-basic-expected.png +++ b/third_party/blink/web_tests/platform/mac/fast/forms/select/menulist-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/select/select-initial-position-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/select/select-initial-position-expected.png index bbc04aa..4699800 100644 --- a/third_party/blink/web_tests/platform/mac/fast/forms/select/select-initial-position-expected.png +++ b/third_party/blink/web_tests/platform/mac/fast/forms/select/select-initial-position-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-expected.png index dafa474..ac74ed9c 100644 --- a/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-expected.png +++ b/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-inherited-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-inherited-expected.png index 9d850842..bbf2cae5 100644 --- a/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-inherited-expected.png +++ b/third_party/blink/web_tests/platform/mac/fast/forms/select/select-overflow-scroll-inherited-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fragmentation/outline-crossing-columns-expected.png b/third_party/blink/web_tests/platform/mac/fragmentation/outline-crossing-columns-expected.png index 55da340..da974862 100644 --- a/third_party/blink/web_tests/platform/mac/fragmentation/outline-crossing-columns-expected.png +++ b/third_party/blink/web_tests/platform/mac/fragmentation/outline-crossing-columns-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt index c6d15230..800b3a4b 100644 --- a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt +++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/caret-invalidation-in-overflow-scroll-expected.txt
@@ -7,6 +7,17 @@ "backgroundColor": "#FFFFFF" }, { + "name": "LayoutNGTextControlInnerEditor DIV", + "bounds": [20, 15], + "drawsContent": false, + "transform": 1 + }, + { + "name": "LayoutNGTextControlInnerEditor DIV", + "bounds": [168, 15], + "transform": 2 + }, + { "name": "Caret", "position": [16, 0], "bounds": [1, 15], @@ -31,6 +42,16 @@ [0, 0, 1, 0], [391, 11, 0, 1] ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [-148, 0, 0, 1] + ] } ] }
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt index 56fc61f..89c1343 100644 --- a/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt +++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -10,7 +10,6 @@ "name": "LayoutNGTextControlSingleLine INPUT id='root'", "position": [-1, -1], "bounds": [50, 23], - "contentsOpaqueForText": true, "backgroundColor": "#FFFFFF", "invalidations": [ [0, 0, 50, 23] @@ -18,6 +17,20 @@ "transform": 1 }, { + "name": "LayoutNGTextControlInnerEditor DIV", + "bounds": [40, 15], + "drawsContent": false, + "transform": 2 + }, + { + "name": "LayoutNGTextControlInnerEditor DIV", + "bounds": [72, 15], + "invalidations": [ + [0, 0, 72, 15] + ], + "transform": 3 + }, + { "name": "Caret", "position": [38, 0], "bounds": [1, 15], @@ -56,6 +69,16 @@ [0, 0, 1, 0], [4, 3, 0, 1] ] + }, + { + "id": 3, + "parent": 2, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [-32, 0, 0, 1] + ] } ] }
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt index 38798783..367b49a 100644 --- a/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt +++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
@@ -1,13 +1,56 @@ { "layers": [ { - "name": "Scrolling background of LayoutView #document", + "name": "Scrolling background of LayoutNGView #document", "bounds": [800, 600], "contentsOpaque": true, "backgroundColor": "#FFFFFF", "invalidations": [ [7, 7, 50, 23] ] + }, + { + "name": "LayoutNGTextControlInnerEditor DIV", + "bounds": [40, 15], + "drawsContent": false, + "transform": 1 + }, + { + "name": "LayoutNGTextControlInnerEditor DIV", + "bounds": [72, 15], + "invalidations": [ + [0, 0, 72, 15] + ], + "transform": 2 + }, + { + "name": "LayoutNGTextControlSingleLine INPUT id='target'", + "position": [7, 7], + "bounds": [50, 23], + "invalidations": [ + [0, 0, 50, 23] + ] + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [12, 11, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [-32, 0, 0, 1] + ] } ] }
diff --git a/third_party/blink/web_tests/platform/mac/scrollbars/listbox-scrollbar-combinations-expected.png b/third_party/blink/web_tests/platform/mac/scrollbars/listbox-scrollbar-combinations-expected.png index 29392b2..e09af38 100644 --- a/third_party/blink/web_tests/platform/mac/scrollbars/listbox-scrollbar-combinations-expected.png +++ b/third_party/blink/web_tests/platform/mac/scrollbars/listbox-scrollbar-combinations-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png index b602db9..8ab891b 100644 --- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png +++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png index 59581e1..db35eba 100644 --- a/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png +++ b/third_party/blink/web_tests/platform/mac/virtual/dark-color-scheme/fast/forms/color-scheme/select/select-multiple-appearance-basic-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt index 18826eb5..c08c049 100644 --- a/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt +++ b/third_party/blink/web_tests/platform/win/paint/invalidation/scroll/invalidate-caret-in-composited-scrolling-container-expected.txt
@@ -10,7 +10,6 @@ "name": "LayoutNGTextControlSingleLine INPUT id='root'", "position": [-1, -1], "bounds": [74, 24], - "contentsOpaqueForText": true, "backgroundColor": "#FFFFFF", "invalidations": [ [0, 0, 74, 24] @@ -18,6 +17,20 @@ "transform": 1 }, { + "name": "LayoutNGTextControlInnerEditor DIV", + "bounds": [64, 16], + "drawsContent": false, + "transform": 2 + }, + { + "name": "LayoutNGTextControlInnerEditor DIV", + "bounds": [74, 16], + "invalidations": [ + [0, 0, 74, 16] + ], + "transform": 3 + }, + { "name": "Caret", "position": [63, 0], "bounds": [1, 16], @@ -56,6 +69,16 @@ [0, 0, 1, 0], [4, 3, 0, 1] ] + }, + { + "id": 3, + "parent": 2, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [-10, 0, 0, 1] + ] } ] }
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt index 701632d..2b4686a0 100644 --- a/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt +++ b/third_party/blink/web_tests/platform/win/paint/invalidation/selection/selection-in-composited-scrolling-container-expected.txt
@@ -1,13 +1,56 @@ { "layers": [ { - "name": "Scrolling background of LayoutView #document", + "name": "Scrolling background of LayoutNGView #document", "bounds": [800, 600], "contentsOpaque": true, "backgroundColor": "#FFFFFF", "invalidations": [ [7, 7, 74, 24] ] + }, + { + "name": "LayoutNGTextControlInnerEditor DIV", + "bounds": [64, 16], + "drawsContent": false, + "transform": 1 + }, + { + "name": "LayoutNGTextControlInnerEditor DIV", + "bounds": [74, 16], + "invalidations": [ + [0, 0, 74, 16] + ], + "transform": 2 + }, + { + "name": "LayoutNGTextControlSingleLine INPUT id='target'", + "position": [7, 7], + "bounds": [74, 24], + "invalidations": [ + [0, 0, 74, 24] + ] + } + ], + "transforms": [ + { + "id": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [12, 11, 0, 1] + ] + }, + { + "id": 2, + "parent": 1, + "transform": [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [-10, 0, 0, 1] + ] } ] }
diff --git a/third_party/wlcs/LICENSE b/third_party/wlcs/LICENSE new file mode 100644 index 0000000..44325404 --- /dev/null +++ b/third_party/wlcs/LICENSE
@@ -0,0 +1,676 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. +
diff --git a/third_party/wlcs/OWNERS b/third_party/wlcs/OWNERS new file mode 100644 index 0000000..e2a876d --- /dev/null +++ b/third_party/wlcs/OWNERS
@@ -0,0 +1,2 @@ +file://chrome/browser/ash/guest_os/OWNERS +file://components/exo/OWNERS
diff --git a/third_party/wlcs/README.chromium b/third_party/wlcs/README.chromium new file mode 100644 index 0000000..f333888 --- /dev/null +++ b/third_party/wlcs/README.chromium
@@ -0,0 +1,24 @@ +Name: Wayland Conformance Test Suite +Short Name: wlcs +URL: https://github.com/MirServer/wlcs +Version: 0 +Date: 2023-02-24 +Revision: 2930ad4b5ca602446ad211b49fb1827303ce9f4b +License: GPL2 and GPL3 +License File: NOT_SHIPPED +Security Critical: no + +Description: +WLCS is a test suite which verifies protocol-level conformance for +wayland compositors. This code will be used in two ways: +1) A subset of header files (distributed under GPL3) will be #included + in chromium code to allow the test harness to interface with our + code. +2) Test binaries can be built by automated builders or local developers + in order to gauge the conformance of chromium's wayland compositor + against the suite. +This code is licensed under the GPL2 and GPL3, see src/COPYING.* for +details. + +Local Modifications: +<none>
diff --git a/third_party/zlib/BUILD.gn b/third_party/zlib/BUILD.gn index b85067a1..d20c468 100644 --- a/third_party/zlib/BUILD.gn +++ b/third_party/zlib/BUILD.gn
@@ -4,6 +4,12 @@ import("//build/config/compiler/compiler.gni") +declare_args() { + # Expose zlib's symbols, used by Node.js to provide zlib APIs for its native + # modules. + zlib_symbols_visible = false +} + if (build_with_chromium) { import("//testing/test.gni") } @@ -14,6 +20,10 @@ config("zlib_config") { include_dirs = [ "." ] + + if (zlib_symbols_visible) { + defines = [ "ZLIB_DLL" ] + } } config("zlib_internal_config") { @@ -358,6 +368,11 @@ configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code" ] + if (zlib_symbols_visible) { + configs -= [ "//build/config/gcc:symbol_visibility_hidden" ] + configs += [ "//build/config/gcc:symbol_visibility_default" ] + } + public_configs = [ ":zlib_config" ] configs += [
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 8460cae05..ae87da0 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -382,7 +382,7 @@ 'build2': 'ios_simulator_debug_static_bot_xctest_reclient', }, 'Linux Builder (j-500) (reclient)': 'gpu_tests_release_bot_reclient', - 'Linux Builder (reclient compare)': 'gpu_tests_release_bot_reclient', + 'Linux Builder (reclient compare)': 'gpu_tests_release_bot_remote_links_small_reclient', 'Linux Viz': 'release_trybot_minimal_symbols_reclient', # TODO(crbug.com/1260232): remove this after the migration. 'Mac Builder (reclient compare)': 'gpu_tests_release_bot_minimal_symbols_reclient',
diff --git a/tools/mb/mb_config_expectations/chromium.fyi.json b/tools/mb/mb_config_expectations/chromium.fyi.json index c27e36f..71293b9 100644 --- a/tools/mb/mb_config_expectations/chromium.fyi.json +++ b/tools/mb/mb_config_expectations/chromium.fyi.json
@@ -243,12 +243,16 @@ }, "Linux Builder (reclient compare)": { "gn_args": { + "concurrent_links": 50, "dcheck_always_on": false, + "enable_nacl": false, "ffmpeg_branding": "Chrome", "is_component_build": false, "is_debug": false, "proprietary_codecs": true, - "use_remoteexec": true + "rbe_link_cfg_file": "../../buildtools/reclient_cfgs/chromium-browser-clang/rewrapper_linux_link_small.cfg", + "use_remoteexec": true, + "use_remoteexec_links": true } }, "Linux Viz": {
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 267fe24..475ac395 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -4585,18 +4585,6 @@ <int value="7" label="Decorated Media Custom View Style"/> </enum> -<enum name="ArcNoWindowReason"> - <int value="0" label="NoHandler"/> - <int value="1" label="NoHandler (from crash)"/> - <int value="2" label="NoRootBounds"/> - <int value="3" label="NoRootBounds (from crash)"/> - <int value="4" label="NoScreenBounds"/> - <int value="5" label="NoScreenBounds (from crash)"/> - <int value="6" label="Flag disabled"/> - <int value="7" label="Not ARCVM"/> - <int value="8" label="No exo helper"/> -</enum> - <enum name="ArcOptInAction"> <summary>Defines Arc OptIn actions</summary> <int value="0" label="DEPRECATED: Opted Out"/> @@ -25495,6 +25483,11 @@ <int value="50" label="Calculator"/> <int value="51" label="FirmwareUpdateApp"/> <int value="52" label="Google TV"/> + <int value="53" label="Google Calendar"/> + <int value="54" label="Google Chat"/> + <int value="55" label="Google Meet"/> + <int value="56" label="Google Maps"/> + <int value="57" label="Google Messages"/> </enum> <enum name="DefaultBrowserAsyncAttemptResult"> @@ -36624,7 +36617,7 @@ <int value="63" label="kDeprecated_ExternallyConnectableAllUrls"/> <int value="64" label="kFeedbackPrivate"/> <int value="65" label="kFileBrowserHandler"/> - <int value="66" label="kFileBrowserHandlerInternal"/> + <int value="66" label="kDeleted_FileBrowserHandlerInternal"/> <int value="67" label="kFileManagerPrivate"/> <int value="68" label="kFileSystem"/> <int value="69" label="kFileSystemDirectory"/> @@ -42386,6 +42379,12 @@ <int value="4487" label="PrivateAggregationApiFledgeExtensions"/> <int value="4488" label="DeprecatedInterestGroupDailyUpdateUrl"/> <int value="4489" label="CSSColorGradientColorSpace"/> + <int value="4490" label="DanglingMarkupInWindowName"/> + <int value="4491" label="DanglingMarkupInWindowNameNotEndsWithNewLineOrGT"/> + <int value="4492" label="DanglingMarkupInWindowNameNotEndsWithGT"/> + <int value="4493" label="DanglingMarkupInTarget"/> + <int value="4494" label="DanglingMarkupInTargetNotEndsWithGT"/> + <int value="4495" label="DanglingMarkupInTargetNotEndsWithNewLineOrGT"/> </enum> <enum name="FeaturePolicyAllowlistType"> @@ -49378,9 +49377,23 @@ label="Search canceled because the search backend isn't available"/> </enum> +<enum name="HelpAppSearchConceptReadStatus"> + <int value="0" label="Ok"/> + <int value="1" label="File not exist"/> + <int value="2" label="Read error"/> + <int value="3" label="Parse error"/> +</enum> + +<enum name="HelpAppSearchConceptWriteStatus"> + <int value="0" label="Ok"/> + <int value="1" label="Write error"/> + <int value="2" label="Serialization error"/> + <int value="3" label="Replace error"/> +</enum> + <enum name="HelpAppSearchHandlerSearchResultStatus"> <int value="0" label="Not ready, empty index"/> - <int value="1" label="Not ready, other status (not empty index)"/> + <int value="1" label="Not ready, updating"/> <int value="2" label="Ready and success"/> <int value="3" label="Ready, empty index (not success)"/> <int value="4" label="Ready, other status (not success, not empty index)"/> @@ -60195,6 +60208,7 @@ <int value="-990704221" label="TerminalSSH:enabled"/> <int value="-990187062" label="SendTabToSelfShowSendingUI:enabled"/> <int value="-990031172" label="FedCmIframeSupport:disabled"/> + <int value="-990002281" label="OmniboxAdaptNarrowTabletWindows:disabled"/> <int value="-989671895" label="OfflineIndicatorAlwaysHttpProbe:enabled"/> <int value="-989140298" label="UseSyncInvalidations:disabled"/> <int value="-989050085" label="AppStoreBillingDebug:enabled"/> @@ -62101,7 +62115,6 @@ <int value="86233339" label="MessagesForAndroidOfferNotification:disabled"/> <int value="86900696" label="SanitizerAPIv0:enabled"/> <int value="87306743" label="VariationsFakeCrashAfterStartup:disabled"/> - <int value="88249612" label="CalendarView:enabled"/> <int value="88437020" label="FeaturePolicy:enabled"/> <int value="88863813" label="DesktopPWAsDetailedInstallDialog:enabled"/> <int value="89357752" label="PdfXfaSupport:enabled"/> @@ -63410,7 +63423,6 @@ label="AutofillRationalizeStreetAddressAndAddressLine:disabled"/> <int value="836406476" label="EnableTouchableAppContextMenu:enabled"/> <int value="837350465" label="SystemProxyForSystemServices:enabled"/> - <int value="838300788" label="CalendarView:disabled"/> <int value="838887742" label="manual-enhanced-bookmarks"/> <int value="839230937" label="AutofillEnableCardNicknameUpstream:disabled"/> <int value="839798268" label="SafeBrowsingTelemetryForApkDownloads:disabled"/> @@ -64795,6 +64807,7 @@ <int value="1621298798" label="VrBrowserKeyboard:enabled"/> <int value="1622131033" label="ozone-test-single-overlay-support"/> <int value="1622672308" label="ReaderMode:enabled"/> + <int value="1623345369" label="OmniboxAdaptNarrowTabletWindows:enabled"/> <int value="1625988511" label="DurableClientHintsCache:enabled"/> <int value="1626824478" label="ExperimentalAppBanners:disabled"/> <int value="1627416551" label="ImeInputLogicMozc:disabled"/> @@ -81219,6 +81232,18 @@ <int value="3" label="Reset from allowed state"/> </enum> +<enum name="PermissionChangeInfo"> + <int value="0" label="Infobar shown, page reloaded, permission used"/> + <int value="1" label="Infobar shown, page reloaded, permission not used"/> + <int value="2" label="Infobar shown, no page reloaded, permission used"/> + <int value="3" label="Infobar shown, no page reloaded, permission not used"/> + <int value="4" label="Infobar not shown, page reloaded, permission used"/> + <int value="5" label="Infobar not shown, page reloaded, permission not used"/> + <int value="6" label="Infobar not shown, no page reloaded, permission used"/> + <int value="7" + label="Infobar not shown, no page reloaded, permission not used"/> +</enum> + <enum name="PermissionEmbargoStatus"> <int value="0" label="NOT_EMBARGOED"/> <int value="1" label="BLACKLISTED"/> @@ -90485,6 +90510,7 @@ <int value="3" label="SUCCESS_LANDING_REFERRER"/> <int value="4" label="INVALID_URL"/> <int value="5" label="NAVIGATION_EVENT_NOT_FOUND"/> + <int value="6" label="SUCCESS_REFERRER"/> </enum> <enum name="SafeBrowsingBinaryUploadResult"> @@ -112668,6 +112694,7 @@ <int value="54" label="Show Chrome What's New"/> <int value="55" label="Trigger Lacros data migration"/> <int value="56" label="Menu opened"/> + <int value="57" label="Visited Chrome Web Store via extensions sub menu."/> </enum> <enum name="WrongConfigurationMetric">
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml index 4b22051..4c8b135 100644 --- a/tools/metrics/histograms/metadata/accessibility/histograms.xml +++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -1463,6 +1463,20 @@ </summary> </histogram> +<histogram + name="Accessibility.LiveCaption.{SodaLanguageCode}.SessionContainsRecognizedSpeech" + enum="BooleanEnabled" expires_after="2023-06-01"> + <owner>evliu@google.com</owner> + <owner>chrome-media-ux@google.com</owner> + <owner>chrome-a11y-core@google.com</owner> + <summary> + Whether the speech recognition session contains any recognized speech. This + is logged once per media stream upon the destruction of the + SpeechRecognitionRecognizerImpl. + </summary> + <token key="SodaLanguageCode" variants="SodaLanguageCode"/> +</histogram> + <histogram name="Accessibility.LiveCaption2" enum="BooleanEnabled" expires_after="2023-11-30"> <owner>katie@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/help_app/histograms.xml b/tools/metrics/histograms/metadata/help_app/histograms.xml index 29c30e9..395007b1 100644 --- a/tools/metrics/histograms/metadata/help_app/histograms.xml +++ b/tools/metrics/histograms/metadata/help_app/histograms.xml
@@ -34,16 +34,57 @@ </summary> </histogram> +<histogram name="Discover.SearchConcept.ReadStatus" + enum="HelpAppSearchConceptReadStatus" expires_after="2023-08-08"> + <owner>chenjih@google.com</owner> + <owner>tby@chromium.org</owner> + <owner>showoff-eng@google.com</owner> + <summary> + Various status codes on reading persistence file from disk, in the search + handler used by the ChromeOS launcher. Emitted once when a read happens to + the persistence file, and this happens once per login. Some number of file + not exist values is expected, because the persistence file does not exist + before it is written for the first time. + </summary> +</histogram> + +<histogram name="Discover.SearchConcept.WriteStatus" + enum="HelpAppSearchConceptWriteStatus" expires_after="2023-08-08"> + <owner>chenjih@google.com</owner> + <owner>tby@chromium.org</owner> + <owner>showoff-eng@google.com</owner> + <summary> + Various status codes on writing persistence file back to disk, in the search + handler used by the ChromeOS launcher. Emitted once when a write happens to + the persistence file, and this happens once per new web contents arrives, + which is expected to be approx. once per login. + </summary> +</histogram> + +<histogram name="Discover.SearchHandler.SearchAvailableTime" units="ms" + expires_after="2023-08-08"> + <owner>chenjih@google.com</owner> + <owner>tby@chromium.org</owner> + <owner>showoff-eng@google.com</owner> + <summary> + The latency of the help search availability after the help app search + handler constructed. Recorded each time a help app search handler is + constructed. + </summary> +</histogram> + <histogram name="Discover.SearchHandler.SearchResultStatus" enum="HelpAppSearchHandlerSearchResultStatus" expires_after="2023-08-08"> + <owner>chenjih@google.com</owner> + <owner>tby@chromium.org</owner> <owner>zufeng@google.com</owner> <owner>showoff-eng@google.com</owner> <summary> The end result of a search using the help app search handler. Logged once per time a search finishes. Not logged if the search is canceled by a new search starting. Use this to calculate the proportion of searches where the - search handler is ready vs not ready. "Ready" means the first - update finished and the search handler is ready to handle searches. + search handler is ready vs not ready. "Ready" means the update + finished and the search handler is ready to handle searches. </summary> </histogram>
diff --git a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml index 270c1a8..78c8ef0 100644 --- a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml +++ b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
@@ -6265,6 +6265,10 @@ <affected-histogram name="Media.VideoCapture.Windows.ImageCaptureOutcome"/> </histogram_suffixes> +<!-- TODO(crbug.com/401026): Convert to Patterned Histograms: + chromium.googlesource.com/chromium/src/+/HEAD/tools/metrics/histograms/README.md#patterned-histograms +--> + <histogram_suffixes name="WrenchMenuActionTimings" separator="."> <suffix name="About" label=""/> <suffix name="AppInfo" label=""/> @@ -6313,6 +6317,7 @@ <suffix name="SiteSettings" label=""/> <suffix name="TaskManager" label=""/> <suffix name="ViewSource" label=""/> + <suffix name="VisitChromeWebStore" label=""/> <suffix name="Win8MetroRestart" label=""/> <suffix name="WinDesktopRestart" label=""/> <suffix name="ZoomMinus" label=""/>
diff --git a/tools/metrics/histograms/metadata/permissions/histograms.xml b/tools/metrics/histograms/metadata/permissions/histograms.xml index af528b0..c0980a20 100644 --- a/tools/metrics/histograms/metadata/permissions/histograms.xml +++ b/tools/metrics/histograms/metadata/permissions/histograms.xml
@@ -654,6 +654,20 @@ <token key="PermissionType" variants="Top2PermissionTypes"/> </histogram> +<histogram + name="Permissions.PageInfo.Changed.{PermissionType}.Reallowed.Outcome" + enum="PermissionChangeInfo" expires_after="2023-10-31"> + <owner>elklm@chromium.org</owner> + <owner>src/components/permissions/PERMISSIONS_OWNERS</owner> + <summary> + Records if a user manually reallowed permission state via PageInfo. Recorded + when the user closes a tab, makes cross-origin navigation, or if permission + is used by an origin. Grouped by whether permission was used, the page was + reloaded and a "Reload this page" infobar was shown. + </summary> + <token key="PermissionType" variants="AllPermissionTypes"/> +</histogram> + <histogram name="Permissions.PageInfo.Changed.{PermissionType}.{ReloadInfoBar}" enum="PermissionChangeAction" expires_after="2023-10-31"> <owner>elklm@chromium.org</owner>
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc index d2fb716..7dcf7090 100644 --- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc +++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -5353,66 +5353,16 @@ TEST_F(AXPlatformNodeTextRangeProviderTest, FindTextWithEmbeddedObjectCharacter) { - // ++1 kRootWebArea - // ++++2 kList - // ++++++3 kListItem - // ++++++++4 kStaticText - // ++++++++++5 kInlineTextBox - // ++++++6 kListItem - // ++++++++7 kStaticText - // ++++++++++8 kInlineTextBox - ui::AXNodeData root_1; - ui::AXNodeData list_2; - ui::AXNodeData list_item_3; - ui::AXNodeData static_text_4; - ui::AXNodeData inline_box_5; - ui::AXNodeData list_item_6; - ui::AXNodeData static_text_7; - ui::AXNodeData inline_box_8; - - root_1.id = 1; - list_2.id = 2; - list_item_3.id = 3; - static_text_4.id = 4; - inline_box_5.id = 5; - list_item_6.id = 6; - static_text_7.id = 7; - inline_box_8.id = 8; - - root_1.role = ax::mojom::Role::kRootWebArea; - root_1.child_ids = {list_2.id}; - - list_2.role = ax::mojom::Role::kList; - list_2.child_ids = {list_item_3.id, list_item_6.id}; - - list_item_3.role = ax::mojom::Role::kListItem; - list_item_3.child_ids = {static_text_4.id}; - - static_text_4.role = ax::mojom::Role::kStaticText; - static_text_4.SetName("foo"); - static_text_4.child_ids = {inline_box_5.id}; - - inline_box_5.role = ax::mojom::Role::kInlineTextBox; - inline_box_5.SetName("foo"); - - list_item_6.role = ax::mojom::Role::kListItem; - list_item_6.child_ids = {static_text_7.id}; - - static_text_7.role = ax::mojom::Role::kStaticText; - static_text_7.child_ids = {inline_box_8.id}; - static_text_7.SetName("bar"); - - inline_box_8.role = ax::mojom::Role::kInlineTextBox; - inline_box_8.SetName("bar"); - - ui::AXTreeUpdate update; - ui::AXTreeData tree_data; - tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID(); - update.tree_data = tree_data; - update.has_tree_data = true; - update.root_id = root_1.id; - update.nodes = {root_1, list_2, list_item_3, static_text_4, - inline_box_5, list_item_6, static_text_7, inline_box_8}; + TestAXTreeUpdate update(std::string(R"HTML( + ++1 kRootWebArea + ++++2 kList + ++++++3 kListItem + ++++++++4 kStaticText name="foo" + ++++++++++5 kInlineTextBox name="foo" + ++++++6 kListItem + ++++++++7 kStaticText name="bar" + ++++++++++8 kInlineTextBox name="bar" + )HTML")); Init(update); @@ -6573,75 +6523,25 @@ TEST_F(AXPlatformNodeTextRangeProviderTest, TestNormalizeTextRangeForceSameAnchorOnDegenerateRange) { - // ++1 kRootWebArea - // ++++2 kGenericContainer - // ++++++3 kImage - // ++++4 kTextField - // ++++++5 kGenericContainer - // ++++++++6 kStaticText - // ++++++++++7 kInlineTextBox - ui::AXNodeData root_1; - ui::AXNodeData generic_container_2; - ui::AXNodeData line_break_3; - ui::AXNodeData text_field_4; - ui::AXNodeData generic_container_5; - ui::AXNodeData static_text_6; - ui::AXNodeData inline_box_7; + TestAXTreeUpdate update(std::string(R"HTML( + ++1 kRootWebArea + ++++2 kGenericContainer boolAttribute=kIsLineBreakingObject,true + ++++++3 kImage + ++++4 kTextField state=kEditable + ++++++5 kGenericContainer + ++++++++6 kStaticText name="3.14" + ++++++++++7 kInlineTextBox name="3.14" + )HTML")); - root_1.id = 1; - generic_container_2.id = 2; - line_break_3.id = 3; - text_field_4.id = 4; - generic_container_5.id = 5; - static_text_6.id = 6; - inline_box_7.id = 7; - - root_1.role = ax::mojom::Role::kRootWebArea; - root_1.child_ids = {generic_container_2.id, text_field_4.id}; - - generic_container_2.role = ax::mojom::Role::kGenericContainer; - generic_container_2.AddBoolAttribute( - ax::mojom::BoolAttribute::kIsLineBreakingObject, true); - generic_container_2.child_ids = {line_break_3.id}; - - line_break_3.role = ax::mojom::Role::kLineBreak; - - text_field_4.role = ax::mojom::Role::kTextField; - text_field_4.AddState(ax::mojom::State::kEditable); - text_field_4.child_ids = {generic_container_5.id}; - text_field_4.SetValue("3.14"); - - generic_container_5.role = ax::mojom::Role::kGenericContainer; - generic_container_5.child_ids = {static_text_6.id}; - - static_text_6.role = ax::mojom::Role::kStaticText; - static_text_6.child_ids = {inline_box_7.id}; - static_text_6.SetName("3.14"); - - inline_box_7.role = ax::mojom::Role::kInlineTextBox; - inline_box_7.SetName("3.14"); - - ui::AXTreeUpdate update; - ui::AXTreeData tree_data; - tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID(); - update.tree_data = tree_data; - update.has_tree_data = true; - update.root_id = root_1.id; - update.nodes.push_back(root_1); - update.nodes.push_back(generic_container_2); - update.nodes.push_back(line_break_3); - update.nodes.push_back(text_field_4); - update.nodes.push_back(generic_container_5); - update.nodes.push_back(static_text_6); - update.nodes.push_back(inline_box_7); + update.nodes[3].SetValue("3.14"); const AXTree* tree = Init(update); - const AXNode* line_break_3_node = tree->GetFromId(line_break_3.id); - const AXNode* inline_box_7_node = tree->GetFromId(inline_box_7.id); + const AXNode* line_break_3_node = tree->GetFromId(3); + const AXNode* inline_box_7_node = tree->GetFromId(7); AXPlatformNodeWin* owner = static_cast<AXPlatformNodeWin*>( - AXPlatformNodeFromNode(GetNodeFromTree(tree_data.tree_id, 1))); + AXPlatformNodeFromNode(GetNodeFromTree(tree->GetAXTreeID(), 1))); // start: TextPosition, anchor_id=3, text_offset=1, annotated_text=/xFFFC<> // end : TextPosition, anchor_id=7, text_offset=0, annotated_text=<p>i @@ -7517,75 +7417,22 @@ // normalize to other positions, so we should expect the // 'UIA_IsReadOnlyAttributeId' attribute queried at this position to return // false. - // ++1 kRootWebArea - // ++++2 kTextField editable value="hello" - // ++++++3 kGenericContainer editable isLineBreakingObject=true - // ++++++++4 kStaticText editable name="hello" - // ++++++++++5 kInlineTextBox editable name="hello" - // ++++6 kStaticText name="abc" - // ++++++7 kInlineTextBox name="abc" - AXNodeData root_1; - AXNodeData text_field_2; - AXNodeData generic_container_3; - AXNodeData static_text_4; - AXNodeData inline_text_5; - AXNodeData static_text_6; - AXNodeData inline_text_7; - - root_1.id = 1; - text_field_2.id = 2; - generic_container_3.id = 3; - static_text_4.id = 4; - inline_text_5.id = 5; - static_text_6.id = 6; - inline_text_7.id = 7; - - root_1.role = ax::mojom::Role::kRootWebArea; - root_1.child_ids = {text_field_2.id, static_text_6.id}; - - text_field_2.role = ax::mojom::Role::kTextField; - text_field_2.AddState(ax::mojom::State::kEditable); - text_field_2.SetValue("hello"); - text_field_2.child_ids = {generic_container_3.id}; - - generic_container_3.role = ax::mojom::Role::kGenericContainer; - generic_container_3.AddState(ax::mojom::State::kEditable); - generic_container_3.AddBoolAttribute( - ax::mojom::BoolAttribute::kIsLineBreakingObject, true); - generic_container_3.child_ids = {static_text_4.id}; - - static_text_4.role = ax::mojom::Role::kStaticText; - static_text_4.SetName("hello"); - static_text_4.AddState(ax::mojom::State::kEditable); - static_text_4.child_ids = {inline_text_5.id}; - - inline_text_5.role = ax::mojom::Role::kInlineTextBox; - inline_text_5.SetName("hello"); - inline_text_5.AddState(ax::mojom::State::kEditable); - - static_text_6.role = ax::mojom::Role::kStaticText; - static_text_6.SetName("abc"); - static_text_6.child_ids = {inline_text_7.id}; - - inline_text_7.role = ax::mojom::Role::kInlineTextBox; - inline_text_7.SetName("abc"); - - ui::AXTreeUpdate update; - ui::AXTreeID tree_id = ui::AXTreeID::CreateNewAXTreeID(); - update.root_id = root_1.id; - update.tree_data.tree_id = tree_id; - update.has_tree_data = true; - update.nodes = {root_1, text_field_2, generic_container_3, - static_text_4, inline_text_5, static_text_6, - inline_text_7}; + TestAXTreeUpdate update(std::string(R"HTML( + ++1 kRootWebArea + ++++2 kTextField state=kEditable + ++++++3 kGenericContainer state=kEditable boolAttribute=kIsLineBreakingObject,true + ++++++++4 kStaticText state=kEditable name="hello" + ++++++++++5 kInlineTextBox state=kEditable name="hello" + ++++6 kStaticText name="abc" + )HTML")); const AXTree* tree = Init(update); - const AXNode* inline_text_5_node = tree->GetFromId(inline_text_5.id); + const AXNode* inline_text_5_node = tree->GetFromId(5); // Making |owner| AXID:1 so that |TestAXNodeWrapper::BuildAllWrappers| // will build the entire tree. AXPlatformNodeWin* owner = static_cast<AXPlatformNodeWin*>( - AXPlatformNodeFromNode(GetNodeFromTree(tree_id, 1))); + AXPlatformNodeFromNode(GetNodeFromTree(tree->GetAXTreeID(), 1))); ComPtr<AXPlatformNodeTextRangeProviderWin> range; base::win::ScopedVariant expected_variant; @@ -7637,98 +7484,32 @@ // ends at the beginning of the next paragraph. The range only contains the // generated newline character. The readonly attribute value returned should // be the one of the common anchor of the start and end endpoint. - - // ++1 kRootWebArea - // ++++2 kGenericContainer - // ++++++3 kImage - // ++++++4 kTextField editable - // ++++5 kGenericContainer editable - // ++++++6 kImage - // ++++++7 kTextField editable - // ++++8 kGenericContainer - // ++++++9 kTextField editable - // ++++++10 kTextField editable - AXNodeData root_1; - AXNodeData generic_container_2; - AXNodeData image_3; - AXNodeData text_field_4; - AXNodeData generic_container_5; - AXNodeData image_6; - AXNodeData text_field_7; - AXNodeData generic_container_8; - AXNodeData text_field_9; - AXNodeData text_field_10; - - root_1.id = 1; - generic_container_2.id = 2; - image_3.id = 3; - text_field_4.id = 4; - generic_container_5.id = 5; - image_6.id = 6; - text_field_7.id = 7; - generic_container_8.id = 8; - text_field_9.id = 9; - text_field_10.id = 10; - - root_1.role = ax::mojom::Role::kRootWebArea; - root_1.child_ids = {generic_container_2.id, generic_container_5.id, - generic_container_8.id}; - - generic_container_2.role = ax::mojom::Role::kGenericContainer; - generic_container_2.child_ids = {image_3.id, text_field_4.id}; - - image_3.role = ax::mojom::Role::kImage; - image_3.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, - true); - - text_field_4.role = ax::mojom::Role::kTextField; - text_field_4.AddState(ax::mojom::State::kEditable); - - generic_container_5.role = ax::mojom::Role::kGenericContainer; - generic_container_5.AddState(ax::mojom::State::kEditable); - generic_container_5.child_ids = {image_6.id, text_field_7.id}; - - image_6.role = ax::mojom::Role::kImage; - image_6.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, - true); - - text_field_7.role = ax::mojom::Role::kTextField; - text_field_7.AddState(ax::mojom::State::kEditable); - - generic_container_8.role = ax::mojom::Role::kGenericContainer; - generic_container_8.child_ids = {text_field_9.id, text_field_10.id}; - - text_field_9.role = ax::mojom::Role::kTextField; - text_field_9.AddState(ax::mojom::State::kEditable); - text_field_9.AddBoolAttribute(ax::mojom::BoolAttribute::kIsLineBreakingObject, - true); - - text_field_10.role = ax::mojom::Role::kTextField; - text_field_10.AddState(ax::mojom::State::kEditable); - - ui::AXTreeUpdate update; - ui::AXTreeID tree_id = ui::AXTreeID::CreateNewAXTreeID(); - update.root_id = root_1.id; - update.tree_data.tree_id = tree_id; - update.has_tree_data = true; - update.nodes = {root_1, generic_container_2, image_3, - text_field_4, generic_container_5, image_6, - text_field_7, generic_container_8, text_field_9, - text_field_10}; + TestAXTreeUpdate update(std::string(R"HTML( + ++1 kRootWebArea + ++++2 kGenericContainer + ++++++3 kImage boolAttribute=kIsLineBreakingObject,true + ++++++4 kTextField state=kEditable + ++++5 kGenericContainer state=kEditable + ++++++6 kImage boolAttribute=kIsLineBreakingObject,true + ++++++7 kTextField state=kEditable + ++++8 kGenericContainer + ++++++9 kTextField state=kEditable boolAttribute=kIsLineBreakingObject,true + ++++++10 kTextField state=kEditable + )HTML")); const AXTree* tree = Init(update); - const AXNode* image_3_node = tree->GetFromId(image_3.id); - const AXNode* image_6_node = tree->GetFromId(image_6.id); - const AXNode* text_field_4_node = tree->GetFromId(text_field_4.id); - const AXNode* text_field_7_node = tree->GetFromId(text_field_7.id); - const AXNode* text_field_9_node = tree->GetFromId(text_field_9.id); - const AXNode* text_field_10_node = tree->GetFromId(text_field_10.id); + const AXNode* image_3_node = tree->GetFromId(3); + const AXNode* image_6_node = tree->GetFromId(6); + const AXNode* text_field_4_node = tree->GetFromId(4); + const AXNode* text_field_7_node = tree->GetFromId(7); + const AXNode* text_field_9_node = tree->GetFromId(9); + const AXNode* text_field_10_node = tree->GetFromId(10); // Making |owner| AXID:1 so that |TestAXNodeWrapper::BuildAllWrappers| // will build the entire tree. AXPlatformNodeWin* owner = static_cast<AXPlatformNodeWin*>( - AXPlatformNodeFromNode(GetNodeFromTree(tree_id, 1))); + AXPlatformNodeFromNode(GetNodeFromTree(tree->GetAXTreeID(), 1))); base::win::ScopedVariant expected_variant;
diff --git a/ui/aura/window_event_dispatcher.cc b/ui/aura/window_event_dispatcher.cc index 3820ceb2..720f9cc7 100644 --- a/ui/aura/window_event_dispatcher.cc +++ b/ui/aura/window_event_dispatcher.cc
@@ -505,10 +505,7 @@ observer_notifiers_.push(std::make_unique<ObserverNotifier>(this, *event)); } -void WindowEventDispatcher::OnEventProcessingFinished( - ui::Event* event, - ui::EventTarget* target, - const ui::EventDispatchDetails& details) { +void WindowEventDispatcher::OnEventProcessingFinished(ui::Event* event) { if (in_shutdown_) return;
diff --git a/ui/aura/window_event_dispatcher.h b/ui/aura/window_event_dispatcher.h index 511fdb1..3afd4ed 100644 --- a/ui/aura/window_event_dispatcher.h +++ b/ui/aura/window_event_dispatcher.h
@@ -222,10 +222,7 @@ // Overridden from ui::EventProcessor: ui::EventTarget* GetRootForEvent(ui::Event* event) override; void OnEventProcessingStarted(ui::Event* event) override; - void OnEventProcessingFinished( - ui::Event* event, - ui::EventTarget* target, - const ui::EventDispatchDetails& details) override; + void OnEventProcessingFinished(ui::Event* event) override; // Overridden from ui::EventDispatcherDelegate. bool CanDispatchToTarget(ui::EventTarget* target) override;
diff --git a/ui/chromeos/events/event_rewriter_chromeos.cc b/ui/chromeos/events/event_rewriter_chromeos.cc index cbe8925..56b3a17 100644 --- a/ui/chromeos/events/event_rewriter_chromeos.cc +++ b/ui/chromeos/events/event_rewriter_chromeos.cc
@@ -1860,7 +1860,8 @@ // No System No Fn -> System // Yes Fn No Unchanged // Yes System No Unchanged - if (ForceTopRowAsFunctionKeys() == flip_remapping) { + if (ForceTopRowAsFunctionKeys(key_event.source_device_id()) == + flip_remapping) { // Rewrite the F1-F12 keys on a Chromebook keyboard to system keys. // This is the original Chrome OS layout. static const KeyboardRemapping kFkeysToSystemKeys1[] = { @@ -2256,7 +2257,8 @@ // If the scan code appears in the top row mapping it is an action key. const bool is_action_key = (key_iter != scan_code_map.end()); if (is_action_key) { - if (flip_remapping != ForceTopRowAsFunctionKeys()) { + if (flip_remapping != + ForceTopRowAsFunctionKeys(key_event.source_device_id())) { ApplyRemapping(key_iter->second, state); } @@ -2405,7 +2407,8 @@ std::size(kActionToFnKeys))) { // Incoming key code is an action key. Check if it needs to be mapped back // to its corresponding function key. - if (flip_remapping != ForceTopRowAsFunctionKeys()) { + if (flip_remapping != + ForceTopRowAsFunctionKeys(key_event.source_device_id())) { // On Drallion, mirror mode toggle is on its own key so don't remap it. if (layout == KeyboardCapability::KeyboardTopRowLayout:: kKbdTopRowLayoutDrallion && @@ -2431,20 +2434,19 @@ state->code = DomCode::F12; state->key = DomKey::F12; } - // At this point, the search modifier flag should be cleared if the - // remapping was supposed to be flipped. + // If the mapping should be flipped when command is down, the flag needs to + // be cleared. if (flip_remapping) { state->flags &= ~EF_COMMAND_DOWN; } - return true; } return false; } -bool EventRewriterChromeOS::ForceTopRowAsFunctionKeys() const { - return delegate_ && delegate_->TopRowKeysAreFunctionKeys(); +bool EventRewriterChromeOS::ForceTopRowAsFunctionKeys(int device_id) const { + return delegate_ && delegate_->TopRowKeysAreFunctionKeys(device_id); } KeyboardCapability::DeviceType EventRewriterChromeOS::KeyboardDeviceAdded(
diff --git a/ui/chromeos/events/event_rewriter_chromeos.h b/ui/chromeos/events/event_rewriter_chromeos.h index e5d1810..82a9854 100644 --- a/ui/chromeos/events/event_rewriter_chromeos.h +++ b/ui/chromeos/events/event_rewriter_chromeos.h
@@ -103,7 +103,7 @@ // function keys instead of having them rewritten into back, forward, // brightness, volume, etc. or if the user has specified that they desire // top-row keys to be treated as function keys globally. - virtual bool TopRowKeysAreFunctionKeys() const = 0; + virtual bool TopRowKeysAreFunctionKeys(int device_id) const = 0; // Returns true if the |key_code| and |flags| have been resgistered for // extensions and EventRewriterChromeOS will not rewrite the event. @@ -246,7 +246,7 @@ // By default the top row (F1-F12) keys are system keys for back, forward, // brightness, volume, etc. However, windows for v2 apps can optionally // request raw function keys for these keys. - bool ForceTopRowAsFunctionKeys() const; + bool ForceTopRowAsFunctionKeys(int device_id) const; // Adds a device to |device_id_to_info_| only if no failure occurs in // identifying the keyboard, and returns the device type of this keyboard
diff --git a/ui/events/event_processor.cc b/ui/events/event_processor.cc index 4937e23..2bf8068d 100644 --- a/ui/events/event_processor.cc +++ b/ui/events/event_processor.cc
@@ -77,16 +77,13 @@ target = targeter->FindNextBestTarget(target, event_to_dispatch); } } - OnEventProcessingFinished(event, target, details); + OnEventProcessingFinished(event); return details; } void EventProcessor::OnEventProcessingStarted(Event* event) { } -void EventProcessor::OnEventProcessingFinished( - Event* event, - EventTarget* target, - const EventDispatchDetails& details) {} +void EventProcessor::OnEventProcessingFinished(Event* event) {} } // namespace ui
diff --git a/ui/events/event_processor.h b/ui/events/event_processor.h index 10cdc5ad..b65b0ce9 100644 --- a/ui/events/event_processor.h +++ b/ui/events/event_processor.h
@@ -50,9 +50,7 @@ // dispatching of |event| will be performed by this EventProcessor). Note // that the last target to which |event| was dispatched may have been // destroyed. - virtual void OnEventProcessingFinished(Event* event, - EventTarget* target, - const EventDispatchDetails& details); + virtual void OnEventProcessingFinished(Event* event); private: base::WeakPtrFactory<EventProcessor> weak_ptr_factory_{this};
diff --git a/ui/events/test/test_event_processor.cc b/ui/events/test/test_event_processor.cc index 1ab483d9..c36d3da 100644 --- a/ui/events/test/test_event_processor.cc +++ b/ui/events/test/test_event_processor.cc
@@ -55,10 +55,7 @@ event->SetHandled(); } -void TestEventProcessor::OnEventProcessingFinished( - Event* event, - EventTarget* target, - const EventDispatchDetails& details) { +void TestEventProcessor::OnEventProcessingFinished(Event* event) { num_times_processing_finished_++; }
diff --git a/ui/events/test/test_event_processor.h b/ui/events/test/test_event_processor.h index 86e42dc..4b36763 100644 --- a/ui/events/test/test_event_processor.h +++ b/ui/events/test/test_event_processor.h
@@ -43,9 +43,7 @@ EventTargeter* GetDefaultEventTargeter() override; EventDispatchDetails OnEventFromSource(Event* event) override; void OnEventProcessingStarted(Event* event) override; - void OnEventProcessingFinished(Event* event, - EventTarget* target, - const EventDispatchDetails& details) override; + void OnEventProcessingFinished(Event* event) override; private: std::unique_ptr<EventTarget> root_;
diff --git a/ui/file_manager/file_manager/background/js/mock_volume_manager.js b/ui/file_manager/file_manager/background/js/mock_volume_manager.js index eb352f5..01c4a47 100644 --- a/ui/file_manager/file_manager/background/js/mock_volume_manager.js +++ b/ui/file_manager/file_manager/background/js/mock_volume_manager.js
@@ -120,7 +120,7 @@ * Current implementation can handle only fake entries. * * @param {!Entry|!FilesAppEntry} entry A fake entry. - * @return {!EntryLocation} Location information. + * @return {!EntryLocation|null} Location information. */ getLocationInfo(entry) { if (util.isFakeEntry(entry)) { @@ -160,6 +160,11 @@ } const volumeInfo = this.getVolumeInfo(entry); + // For filtered out volumes, its volume info won't exist in the volume info + // list. + if (!volumeInfo) { + return null; + } const rootType = VolumeManagerCommon.getRootTypeFromVolumeType( assert(volumeInfo.volumeType)); const isRootEntry = util.isSameEntry(entry, volumeInfo.fileSystem.root);
diff --git a/ui/file_manager/file_manager/common/js/files_app_entry_types.js b/ui/file_manager/file_manager/common/js/files_app_entry_types.js index c3c1450..a14e7de 100644 --- a/ui/file_manager/file_manager/common/js/files_app_entry_types.js +++ b/ui/file_manager/file_manager/common/js/files_app_entry_types.js
@@ -355,7 +355,9 @@ } this.type_name = 'VolumeEntry'; - // TODO(lucmult): consider deriving this from volumeInfo. + // TODO(b/271485133): consider deriving this from volumeInfo. Setting + // rootType here breaks some integration tests, e.g. + // saveAsDlpRestrictedAndroid. this.rootType = null; this.disabled_ = false; @@ -756,6 +758,21 @@ createReader() { return new StaticReader([]); } + + /** + * FakeEntry can be a placeholder for the real volume, if so this field will + * be the volume type of the volume it represents. + * @return {VolumeManagerCommon.VolumeType|null} + */ + get volumeType() { + // Recent rootType has no corresponding volume type, and it will throw error + // in the below getVolumeTypeFromRootType() call, we need to return null + // here. + if (this.rootType === VolumeManagerCommon.RootType.RECENT) { + return null; + } + return VolumeManagerCommon.getVolumeTypeFromRootType(this.rootType); + } } /** @@ -802,4 +819,12 @@ toURL() { return `fake-entry://guest-os/${this.guest_id}`; } + + /** @override */ + get volumeType() { + if (this.vm_type === chrome.fileManagerPrivate.VmType.ARCVM) { + return VolumeManagerCommon.VolumeType.ANDROID_FILES; + } + return VolumeManagerCommon.VolumeType.GUEST_OS; + } }
diff --git a/ui/file_manager/file_manager/externs/files_app_entry_interfaces.js b/ui/file_manager/file_manager/externs/files_app_entry_interfaces.js index d570f86..00d9d02 100644 --- a/ui/file_manager/file_manager/externs/files_app_entry_interfaces.js +++ b/ui/file_manager/file_manager/externs/files_app_entry_interfaces.js
@@ -207,4 +207,11 @@ * @return {string} */ get iconName() {} + + /** + * FakeEntry can be a placeholder for the real volume, if so this field will + * be the volume type of the volume it represents. + * @return {VolumeManagerCommon.VolumeType|null} + */ + get volumeType() {} }
diff --git a/ui/file_manager/file_manager/state/for_tests.ts b/ui/file_manager/file_manager/state/for_tests.ts index bcca805..da10e47 100644 --- a/ui/file_manager/file_manager/state/for_tests.ts +++ b/ui/file_manager/file_manager/state/for_tests.ts
@@ -6,13 +6,11 @@ import {MockVolumeManager} from '../background/js/mock_volume_manager.js'; import {DialogType} from '../common/js/dialog_type.js'; -import {VolumeManagerCommon} from '../common/js/volume_manager_types.js'; import {Crostini} from '../externs/background/crostini.js'; import {FilesAppDirEntry} from '../externs/files_app_entry_interfaces.js'; -import {FileData, FileKey, PropStatus, State, Volume} from '../externs/ts/state.js'; -import {constants} from '../foreground/js/constants.js'; +import {FileKey, PropStatus, State} from '../externs/ts/state.js'; +import {VolumeInfo} from '../externs/volume_info.js'; import {FileSelectionHandler} from '../foreground/js/file_selection.js'; -import {MetadataItem} from '../foreground/js/metadata/metadata_item.js'; import {MetadataModel} from '../foreground/js/metadata/metadata_model.js'; import {MockMetadataModel} from '../foreground/js/metadata/mock_metadata.js'; import {createFakeDirectoryModel} from '../foreground/js/mock_directory_model.js'; @@ -77,6 +75,21 @@ } /** + * Store state might include objects (e.g. Entry type) which can not stringified + * by JSON, here we implement a custom "replacer" to handle that. + */ +function jsonStringifyStoreState(state: any): string { + return JSON.stringify(state, (key, value) => { + // Currently only the key with "entry" (inside `FileData`) can't be + // stringified, we just return its URL. + if (key === 'entry') { + return value.toURL(); + } + return value; + }, 2); +} + +/** * Waits for a part of the Store to be in the expected state. * * Waits a maximum of 10 seconds, since in the unittest the Store manipulation @@ -92,7 +105,9 @@ let got: any; const timeout = new Promise((_, reject) => { setTimeout(() => { - reject(new Error(`waitDeepEquals timed out waiting for \n${want}`)); + reject(new Error(`waitDeepEquals timed out.\nWANT:\n${ + jsonStringifyStoreState( + want)}\nGOT:\n${jsonStringifyStoreState(got)}`)); }, 10000); }); @@ -105,8 +120,6 @@ if (error.constructor?.name === 'AssertionError') { return false; } - console.log(error.stack); - console.error(error); throw error; } }); @@ -115,9 +128,9 @@ } /** Setup store and initialize it with empty state. */ -export function setupStore(): Store { +export function setupStore(initialState: State = getEmptyState()): Store { const store = getStore(); - store.init(getEmptyState()); + store.init(initialState); return store; } @@ -139,98 +152,39 @@ } /** - * Create a fake FileData with partial information. Only the fields listed are - * required, other fields are optional. + * Create a fake VolumeMetadata with VolumeInfo, VolumeInfo can be created by + * MockVolumeManager.createMockVolumeInfo. */ -export function createFakeFileData( - partialFileData: Pick<FileData, 'entry'|'label'|'type'>& - Partial<Omit<FileData, 'entry'|'label'|'type'>>, - ): FileData { - const defaultFileData = { - icon: constants.ICON_TYPES.FOLDER, - volumeType: null, - isDirectory: true, - metadata: {} as MetadataItem, - isRootEntry: false, - isEjectable: false, - shouldDelayLoadingChildren: false, - children: [], - expanded: false, - }; - return { - ...defaultFileData, - ...partialFileData, - }; -} - -/** Create a fake VolumeMetadata. */ export function createFakeVolumeMetadata( - partialMetadata: - Pick<chrome.fileManagerPrivate.VolumeMetadata, 'volumeId'|'volumeType'>& - Partial<Omit< - chrome.fileManagerPrivate.VolumeMetadata, 'volumeId'|'volumeType'>>, + volumeInfo: VolumeInfo, ): chrome.fileManagerPrivate.VolumeMetadata { - const defaultMetadata = { + return { + volumeId: volumeInfo.volumeId, + volumeType: volumeInfo.volumeType, profile: { - displayName: 'foobar@chromium.org', - isCurrentProfile: true, + ...volumeInfo.profile, profileId: '', }, - configurable: false, - watchable: true, - source: VolumeManagerCommon.Source.SYSTEM, - volumeLabel: undefined, + configurable: volumeInfo.configurable, + watchable: volumeInfo.watchable, + source: volumeInfo.source, + volumeLabel: volumeInfo.label, fileSystemId: undefined, - providerId: undefined, + providerId: volumeInfo.providerId, sourcePath: undefined, - deviceType: undefined, - devicePath: undefined, + deviceType: volumeInfo.deviceType, + devicePath: volumeInfo.devicePath, isParentDevice: undefined, - isReadOnly: false, - isReadOnlyRemovableDevice: false, - hasMedia: false, + isReadOnly: volumeInfo.isReadOnly, + isReadOnlyRemovableDevice: volumeInfo.isReadOnlyRemovableDevice, + hasMedia: volumeInfo.hasMedia, mountCondition: undefined, mountContext: undefined, - diskFileSystemType: undefined, - iconSet: {icon16x16Url: '', icon32x32Url: ''}, - driveLabel: '', - remoteMountPath: undefined, + diskFileSystemType: volumeInfo.diskFileSystemType, + iconSet: volumeInfo.iconSet, + driveLabel: volumeInfo.driveLabel, + remoteMountPath: volumeInfo.remoteMountPath, hidden: false, - vmType: undefined, - }; - return { - ...defaultMetadata, - ...partialMetadata, - }; -} - -/** - * Create a fake Volume. Only the fields listed are required, other fields are - * optional. - */ -export function createFakeVolume( - partialVolume: Pick<Volume, 'volumeId'|'volumeType'|'rootKey'|'label'>& - Partial<Omit<Volume, 'volumeId'|'volumeType'|'rootKey'|'label'>>): Volume { - const defaultVolume = { - status: PropStatus.SUCCESS, - source: VolumeManagerCommon.Source.SYSTEM, - error: undefined, - deviceType: undefined, - devicePath: undefined, - isReadOnly: false, - isReadOnlyRemovableDevice: false, - providerId: undefined, - configurable: false, - watchable: true, - diskFileSystemType: '', - iconSet: {icon16x16Url: '', icon32x32Url: ''}, - driveLabel: '', - vmType: undefined, - isDisabled: false, - prefixKey: undefined, - }; - return { - ...defaultVolume, - ...partialVolume, + vmType: volumeInfo.vmType, }; }
diff --git a/ui/file_manager/file_manager/state/reducers/all_entries.ts b/ui/file_manager/file_manager/state/reducers/all_entries.ts index ad578bdc..9602619 100644 --- a/ui/file_manager/file_manager/state/reducers/all_entries.ts +++ b/ui/file_manager/file_manager/state/reducers/all_entries.ts
@@ -4,7 +4,7 @@ import {isVolumeEntry, sortEntries} from '../../common/js/entry_utils.js'; import {FileType} from '../../common/js/file_type.js'; -import {EntryList, FakeEntryImpl, GuestOsPlaceholder, VolumeEntry} from '../../common/js/files_app_entry_types.js'; +import {EntryList, VolumeEntry} from '../../common/js/files_app_entry_types.js'; import {str, util} from '../../common/js/util.js'; import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js'; import {EntryLocation} from '../../externs/entry_location.js'; @@ -217,34 +217,22 @@ */ export function convertEntryToFileData(entry: Entry|FilesAppEntry): FileData { // TODO: get VolumeManager/MetadataModel properly. - const volumeManager = window.fileManager?.volumeManager; - const metadataModel = window.fileManager?.metadataModel; - const volumeInfo = volumeManager?.getVolumeInfo(entry); - const locationInfo = volumeManager?.getLocationInfo(entry); + const {volumeManager, metadataModel} = window.fileManager; + const volumeInfo = volumeManager.getVolumeInfo(entry); + const locationInfo = volumeManager.getLocationInfo(entry); // getEntryLabel() can accept locationInfo=null, but TS doesn't recognize the // type definition in closure, hence the ! here. const label = util.getEntryLabel(locationInfo!, entry); const volumeType = volumeInfo?.volumeType || null; const icon = getEntryIcon(entry, locationInfo, volumeType); - // TODO(b/271485133): populate rootType for VolumeEntry in its constructor, - // add volumeType property to FakeEntry so we don't need the following nested - // if logic to check. - if (entry instanceof VolumeEntry) { - entry.disabled = volumeManager?.isDisabled(volumeType!); - entry.rootType = locationInfo?.rootType; - } else if (entry instanceof FakeEntryImpl) { - if (entry.rootType === VolumeManagerCommon.RootType.CROSTINI) { - entry.disabled = volumeManager?.isDisabled(entry.rootType); - } else if (entry instanceof GuestOsPlaceholder) { - if (entry.vm_type == chrome.fileManagerPrivate.VmType.ARCVM) { - entry.disabled = volumeManager.isDisabled( - VolumeManagerCommon.VolumeType.ANDROID_FILES); - } else { - entry.disabled = - volumeManager.isDisabled(VolumeManagerCommon.VolumeType.GUEST_OS); - } - } + /** + * Update disabled attribute if entry supports disabled attribute and has a + * non-null volumeType. + */ + if ('disabled' in entry && 'volumeType' in entry && entry.volumeType) { + entry.disabled = volumeManager.isDisabled( + entry.volumeType as VolumeManagerCommon.VolumeType); } const metadata = metadataModel ? @@ -361,6 +349,7 @@ case VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT: return EntryType.ENTRY_LIST; case VolumeManagerCommon.RootType.CROSTINI: + case VolumeManagerCommon.RootType.ANDROID_FILES: return EntryType.PLACEHOLDER; case VolumeManagerCommon.RootType.DRIVE_OFFLINE: case VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME: @@ -437,6 +426,7 @@ myFilesEntryList = new EntryList( str('MY_FILES_ROOT_LABEL'), VolumeManagerCommon.RootType.MY_FILES); appendEntry(state, myFilesEntryList); + state.uiEntries = [...state.uiEntries, myFilesEntryList.toURL()]; } return { @@ -485,6 +475,9 @@ // Also remove it from the children field. myFilesFileData.children = myFilesFileData.children.filter( childKey => childKey !== childEntry.toURL()); + // And remove it from the uiEntries if existed. + state.uiEntries = state.uiEntries.filter( + uiEntryKey => uiEntryKey !== childEntry.toURL()); } } appendChildIfNotExisted(myFilesEntry, newVolumeEntry); @@ -514,6 +507,9 @@ appendChildIfNotExisted(myFilesVolumeEntry!, childEntry); myFilesEntryList.removeChildEntry(childEntry); } + // Remove MyFiles entry list from the uiEntries. + state.uiEntries = state.uiEntries.filter( + uiEntryKey => uiEntryKey !== myFilesEntryListKey); } } @@ -528,6 +524,7 @@ str('DRIVE_DIRECTORY_LABEL'), VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT); appendEntry(state, googleDrive); + state.uiEntries = [...state.uiEntries, googleDrive.toURL()]; } appendChildIfNotExisted(googleDrive, myDrive!); @@ -560,7 +557,7 @@ fakeEntries[VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME]; if (fakeSharedWithMe) { appendEntry(state, fakeSharedWithMe); - state.uiEntries.push(fakeSharedWithMe.toURL()); + state.uiEntries = [...state.uiEntries, fakeSharedWithMe.toURL()]; appendChildIfNotExisted(googleDrive, fakeSharedWithMe); } @@ -568,7 +565,7 @@ const fakeOffline = fakeEntries[VolumeManagerCommon.RootType.DRIVE_OFFLINE]; if (fakeOffline) { appendEntry(state, fakeOffline); - state.uiEntries.push(fakeOffline.toURL()); + state.uiEntries = [...state.uiEntries, fakeOffline.toURL()]; appendChildIfNotExisted(googleDrive, fakeOffline); } } @@ -592,6 +589,7 @@ volumeMetadata.driveLabel || '', VolumeManagerCommon.RootType.REMOVABLE, volumeMetadata.devicePath); appendEntry(state, parentEntry); + state.uiEntries = [...state.uiEntries, parentEntry.toURL()]; // Removable devices with group, its parent should always be ejectable. state.allEntries[parentKey].isEjectable = true; }
diff --git a/ui/file_manager/file_manager/state/reducers/all_entries_unittest.ts b/ui/file_manager/file_manager/state/reducers/all_entries_unittest.ts index 861834d..794ef553 100644 --- a/ui/file_manager/file_manager/state/reducers/all_entries_unittest.ts +++ b/ui/file_manager/file_manager/state/reducers/all_entries_unittest.ts
@@ -2,26 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {assertArrayEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js'; +import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js'; import {MockVolumeManager} from '../../background/js/mock_volume_manager.js'; import {EntryList, FakeEntryImpl, VolumeEntry} from '../../common/js/files_app_entry_types.js'; import {MockFileSystem} from '../../common/js/mock_entry.js'; import {waitUntil} from '../../common/js/test_error_reporting.js'; import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js'; -import {EntryType, FileData} from '../../externs/ts/state.js'; +import {EntryType, FileData, State} from '../../externs/ts/state.js'; import {VolumeInfo} from '../../externs/volume_info.js'; -import {constants} from '../../foreground/js/constants.js'; import {MetadataItem} from '../../foreground/js/metadata/metadata_item.js'; import {MockMetadataModel} from '../../foreground/js/metadata/mock_metadata.js'; import {ActionType} from '../actions.js'; -import {addChildEntries as addChildEntriesAction, ClearStaleCachedEntriesAction} from '../actions/all_entries.js'; -import {addVolume} from '../actions/volumes.js'; -import {allEntriesSize, assertAllEntriesEqual, cd, changeSelection, createFakeFileData, createFakeVolume, createFakeVolumeMetadata, setUpFileManagerOnWindow, setupStore, updMetadata} from '../for_tests.js'; +import {addChildEntries, ClearStaleCachedEntriesAction} from '../actions/all_entries.js'; +import {allEntriesSize, assertAllEntriesEqual, cd, changeSelection, createFakeVolumeMetadata, setUpFileManagerOnWindow, setupStore, updMetadata, waitDeepEquals} from '../for_tests.js'; import {getEmptyState, Store} from '../store.js'; -import {addChildEntries, cacheEntries, clearCachedEntries, getMyFiles} from './all_entries.js'; -import {driveRootEntryListKey, makeRemovableParentKey, myFilesEntryListKey} from './volumes.js'; +import {clearCachedEntries, convertEntryToFileData, getMyFiles} from './all_entries.js'; +import {convertVolumeInfoAndMetadataToVolume, myFilesEntryListKey} from './volumes.js'; let store: Store; let fileSystem: MockFileSystem; @@ -47,25 +45,19 @@ /** Generate MyFiles entry with fake entry list. */ function createMyFilesDataWithEntryList(): FileData { - return createFakeFileData({ - entry: new EntryList('My files', VolumeManagerCommon.RootType.MY_FILES), - label: 'My files', - type: EntryType.ENTRY_LIST, - }); + const myFilesEntryList = + new EntryList('My files', VolumeManagerCommon.RootType.MY_FILES); + return convertEntryToFileData(myFilesEntryList); } /** Generate MyFiles entry with real volume entry. */ function createMyFilesDataWithVolumeEntry(): {fileData: FileData, volumeInfo: VolumeInfo} { - const {volumeManager} = window.fileManager; + const volumeManager = new MockVolumeManager(); const downloadsVolumeInfo = volumeManager.getCurrentProfileVolumeInfo( VolumeManagerCommon.VolumeType.DOWNLOADS)!; - const fileData = createFakeFileData({ - entry: new VolumeEntry(downloadsVolumeInfo), - volumeType: VolumeManagerCommon.VolumeType.DOWNLOADS, - label: 'My files', - type: EntryType.VOLUME_ROOT, - }); + const myFilesVolumeEntry = new VolumeEntry(downloadsVolumeInfo); + const fileData = convertEntryToFileData(myFilesVolumeEntry); return {fileData, volumeInfo: downloadsVolumeInfo}; } @@ -248,12 +240,9 @@ const currentState = getEmptyState(); // Add MyFiles volume to the store. const {fileData, volumeInfo} = createMyFilesDataWithVolumeEntry(); - const volume = createFakeVolume({ - volumeType: volumeInfo.volumeType, - volumeId: volumeInfo.volumeId, - label: volumeInfo.label, - rootKey: volumeInfo.displayRoot.toURL(), - }); + const volumeMetadata = createFakeVolumeMetadata(volumeInfo); + const volume = + convertVolumeInfoAndMetadataToVolume(volumeInfo, volumeMetadata); currentState.allEntries[fileData.entry.toURL()] = fileData; currentState.volumes[volumeInfo.volumeId] = volume; const {myFilesEntry, myFilesVolume} = getMyFiles(currentState); @@ -280,288 +269,76 @@ assertEquals(null, myFilesVolume); } -/** Tests that MyFiles volume entry can be cached correctly. */ -export function testCacheEntriesForMyFilesVolume() { - const currentState = getEmptyState(); - const myFilesFileData = createMyFilesDataWithEntryList(); - const myFilesEntryList = myFilesFileData.entry as EntryList; - // Put MyFiles entry in the store and add ui entries as its children. - currentState.allEntries[myFilesEntryList.toURL()] = myFilesFileData; - const playFilesEntry = new FakeEntryImpl( - 'Play files', VolumeManagerCommon.RootType.ANDROID_FILES); - myFilesEntryList.addEntry(playFilesEntry); - const linuxFilesEntry = - new FakeEntryImpl('Linux files', VolumeManagerCommon.RootType.CROSTINI); - myFilesEntryList.addEntry(linuxFilesEntry); - - const {volumeManager} = window.fileManager; - const myFilesVolumeInfo = volumeManager.getCurrentProfileVolumeInfo( - VolumeManagerCommon.VolumeType.DOWNLOADS)!; - const myFilesVolumeMetadata = createFakeVolumeMetadata({ - volumeId: myFilesVolumeInfo.volumeId, - volumeType: myFilesVolumeInfo.volumeType, - }); - cacheEntries(currentState, addVolume({ - volumeInfo: myFilesVolumeInfo, - volumeMetadata: myFilesVolumeMetadata, - })); - - // cacheEntries() updates state in place. - const newState = currentState; - // Expect all existing ui children will be added to the real MyFiles entry. - const myFilesVolumeEntry: VolumeEntry = - newState.allEntries[myFilesVolumeInfo.displayRoot!.toURL()].entry; - const uiChildren = myFilesVolumeEntry.getUIChildren(); - assertEquals(2, uiChildren.length); - assertEquals(playFilesEntry, uiChildren[0]); - assertEquals(linuxFilesEntry, uiChildren[1]); - assertEquals(0, myFilesEntryList.getUIChildren().length); -} - -/** Tests that volume nested in MyFiles volume can be cached correctly. */ -export function testCacheEntriesForNestedVolumeInMyFilesVolume() { - const currentState = getEmptyState(); - // Put MyFiles and play files ui entry in the store. - const {fileData, volumeInfo} = createMyFilesDataWithVolumeEntry(); - const myFilesVolumeEntry = fileData.entry as VolumeEntry; - const myFilesVolume = createFakeVolume({ - volumeType: volumeInfo.volumeType, - volumeId: volumeInfo.volumeId, - label: volumeInfo.label, - rootKey: volumeInfo.displayRoot.toURL(), - }); - currentState.allEntries[fileData.entry.toURL()] = fileData; - currentState.volumes[volumeInfo.volumeId] = myFilesVolume; - // Placeholder ui entry and the volume entry it represents have the same - // label. - const label = 'Play files'; - const playFilesUiEntry = - new FakeEntryImpl(label, VolumeManagerCommon.RootType.ANDROID_FILES); - myFilesVolumeEntry.addEntry(playFilesUiEntry); - fileData.children.push(playFilesUiEntry.toURL()); - - const {volumeManager} = window.fileManager; - const playFilesVolumeInfo = MockVolumeManager.createMockVolumeInfo( - VolumeManagerCommon.VolumeType.ANDROID_FILES, 'playFilesId', label); - volumeManager.volumeInfoList.add(playFilesVolumeInfo); - const playFilesVolumeMetadata = createFakeVolumeMetadata({ - volumeType: playFilesVolumeInfo.volumeType, - volumeId: playFilesVolumeInfo.volumeId, - }); - cacheEntries(currentState, addVolume({ - volumeInfo: playFilesVolumeInfo, - volumeMetadata: playFilesVolumeMetadata, - })); - // cacheEntries() updates state in place. - const newState = currentState; - // Expect the new play file volume will be nested inside MyFiles and the old - // placeholder will be removed. - const playFilesVolumeEntry = - newState.allEntries[playFilesVolumeInfo.displayRoot!.toURL()].entry; - const newMyFilesFileData: FileData = - newState.allEntries[myFilesVolumeEntry.toURL()]; - assertEquals(1, myFilesVolumeEntry.getUIChildren().length); - assertEquals(playFilesVolumeEntry, myFilesVolumeEntry.getUIChildren()[0]); - assertEquals(1, newMyFilesFileData.children.length); - assertEquals(playFilesVolumeEntry.toURL(), newMyFilesFileData.children[0]); -} - -/** Tests that drive volume can be cached correctly. */ -export function testAddDriveVolume(done: () => void) { - const currentState = getEmptyState(); - - const {volumeManager} = window.fileManager; - const driveVolumeInfo = volumeManager.getCurrentProfileVolumeInfo( - VolumeManagerCommon.VolumeType.DRIVE)!; - const driveVolumeMetadata = createFakeVolumeMetadata({ - volumeType: driveVolumeInfo.volumeType, - volumeId: driveVolumeInfo.volumeId, - }); - // DriveFS takes time to resolve. - driveVolumeInfo.resolveDisplayRoot(() => { - cacheEntries(currentState, addVolume({ - volumeInfo: driveVolumeInfo, - volumeMetadata: driveVolumeMetadata, - })); - // cacheEntries() updates state in place. - const newState = currentState; - // Expect all fake entries inside Drive will be added as its children. - const driveFakeRootEntry: EntryList = - newState.allEntries[driveRootEntryListKey].entry; - assertEquals( - VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT, - driveFakeRootEntry.rootType); - const driveChildren = driveFakeRootEntry.getUIChildren(); - assertEquals(5, driveChildren.length); - // My Drive. - const myDriveEntry: VolumeEntry = - newState.allEntries[driveChildren[0]!.toURL()].entry; - assertEquals(VolumeManagerCommon.RootType.DRIVE, myDriveEntry.rootType); - assertEquals(myDriveEntry, driveChildren[0]); - // Shared drives root. - const sharedDrivesRootEntry: DirectoryEntry = - newState.allEntries[driveChildren[1]!.toURL()].entry; - assertEquals('/team_drives', sharedDrivesRootEntry.fullPath); - assertEquals(sharedDrivesRootEntry, driveChildren[1]); - // Computers root. - const computersRootEntry: DirectoryEntry = - newState.allEntries[driveChildren[2]!.toURL()].entry; - assertEquals('/Computers', computersRootEntry.fullPath); - assertEquals(computersRootEntry, driveChildren[2]); - // Shared with me. - const sharedWithMeEntry: FakeEntryImpl = - newState.allEntries[driveChildren[3]!.toURL()].entry; - assertEquals( - VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME, - sharedWithMeEntry.rootType); - assertEquals(sharedWithMeEntry, driveChildren[3]); - // Offline. - const offlineEntry: FakeEntryImpl = - newState.allEntries[driveChildren[4]!.toURL()].entry; - assertEquals(offlineEntry, driveChildren[4]); - assertEquals( - VolumeManagerCommon.RootType.DRIVE_OFFLINE, offlineEntry.rootType); - assertArrayEquals( - [sharedWithMeEntry.toURL(), offlineEntry.toURL()], newState.uiEntries); - - done(); - }); -} - -/** Tests that multiple partition volumes can be cached correctly. */ -export function testCacheEntriesForMultipleUsbPartitionsGrouping() { - const currentState = getEmptyState(); - // Add partition-1 into the store. - const {volumeManager} = window.fileManager; - const partition1VolumeInfo = MockVolumeManager.createMockVolumeInfo( - VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:partition1', - 'Partition 1', '/device/path/1'); - volumeManager.volumeInfoList.add(partition1VolumeInfo); - const partition1VolumeEntry = new VolumeEntry(partition1VolumeInfo); - const partition1FileData = createFakeFileData({ - entry: partition1VolumeEntry, - label: partition1VolumeInfo.label, - type: EntryType.VOLUME_ROOT, - }); - const partition1Volume = createFakeVolume({ - volumeId: partition1VolumeInfo.volumeId, - volumeType: VolumeManagerCommon.VolumeType.REMOVABLE, - rootKey: partition1VolumeInfo.displayRoot!.toURL(), - label: partition1VolumeInfo.label, - devicePath: partition1VolumeInfo.devicePath, - driveLabel: 'USB_Drive', - }); - currentState.volumes[partition1Volume.volumeId] = partition1Volume; - currentState.allEntries[partition1VolumeEntry.toURL()] = partition1FileData; - - const partition2VolumeInfo = MockVolumeManager.createMockVolumeInfo( - VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:partition2', - 'Partition 2', partition1Volume.devicePath); - volumeManager.volumeInfoList.add(partition2VolumeInfo); - const partition2VolumeMetadata = createFakeVolumeMetadata({ - volumeType: partition2VolumeInfo.volumeType, - volumeId: partition2VolumeInfo.volumeId, - devicePath: partition1Volume.devicePath, - driveLabel: partition1Volume.driveLabel, - }); - cacheEntries(currentState, addVolume({ - volumeInfo: partition2VolumeInfo, - volumeMetadata: partition2VolumeMetadata, - })); - // cacheEntries() updates state in place. - const newState = currentState; - // Expect a fake parent entry list will be created. - const parentEntryFileData: FileData = - newState.allEntries[makeRemovableParentKey(partition1Volume)]; - const parentEntry = parentEntryFileData.entry as EntryList; - assertEquals('USB_Drive', parentEntry.label); - assertEquals(VolumeManagerCommon.RootType.REMOVABLE, parentEntry.rootType); - assertTrue(parentEntryFileData.isEjectable); - // Expect both partition1 and partition2 will be added as children. - const partition2VolumeEntry: VolumeEntry = - newState.allEntries[partition2VolumeInfo.displayRoot!.toURL()].entry; - assertEquals(2, parentEntry.getUIChildren().length); - assertEquals(partition1VolumeEntry, parentEntry.getUIChildren()[0]); - assertEquals(partition2VolumeEntry, parentEntry.getUIChildren()[1]); - assertEquals( - constants.ICON_TYPES.UNKNOWN_REMOVABLE, - newState.allEntries[partition1VolumeEntry.toURL()].icon); - assertEquals( - constants.ICON_TYPES.UNKNOWN_REMOVABLE, - newState.allEntries[partition2VolumeEntry.toURL()].icon); -} - /** Tests that child entries can be added to the store correctly. */ -export function testAddChildEntries() { - const currentState = getEmptyState(); +export async function testAddChildEntries(done: () => void) { + const initialState = getEmptyState(); // Add parent/children entries to the store. - const fakeFs = new MockFileSystem('fake-fs'); - fakeFs.populate([ - '/aaa/', - '/aaa/1/', - '/aaa/2/', - '/aaa/2/123/', + fileSystem.populate([ + '/a/', + '/a/1/', + '/a/2/', + '/a/2/b/', ]); - currentState.allEntries[fakeFs.entries['/aaa'].toURL()] = createFakeFileData({ - entry: fakeFs.entries['/aaa'], - label: 'AAA', - type: EntryType.FS_API, - }); - currentState.allEntries[fakeFs.entries['/aaa/1'].toURL()] = - createFakeFileData({ - entry: fakeFs.entries['/aaa/1'], - label: 'AAA 1', - type: EntryType.FS_API, - }); - currentState.allEntries[fakeFs.entries['/aaa/2'].toURL()] = - createFakeFileData({ - entry: fakeFs.entries['/aaa/2'], - label: 'AAA 2', - type: EntryType.FS_API, - shouldDelayLoadingChildren: true, - }); - currentState.allEntries[fakeFs.entries['/aaa/2/123'].toURL()] = - createFakeFileData({ - entry: fakeFs.entries['/aaa/2/123'], - label: 'AAA 123', - type: EntryType.FS_API, - }); + const aEntry = fileSystem.entries['/a']; + initialState.allEntries[aEntry.toURL()] = convertEntryToFileData(aEntry); + // Make sure aEntry won't be cleared. + initialState.uiEntries.push(aEntry.toURL()); - // Add child entries for /aaa/. - const newState1 = addChildEntries( - currentState, addChildEntriesAction({ - parentKey: fakeFs.entries['/aaa'].toURL(), - entries: [fakeFs.entries['/aaa/1'], fakeFs.entries['/aaa/2']], - })); - // Expect the children filed is updated. - const newChildren1 = - newState1.allEntries[fakeFs.entries['/aaa'].toURL()].children; - assertEquals(2, newChildren1.length); - assertEquals(fakeFs.entries['/aaa/1'].toURL(), newChildren1[0]); - assertEquals(fakeFs.entries['/aaa/2'].toURL(), newChildren1[1]); + const store = setupStore(initialState); - // Add child entries for /aaa/2 who has shouldDelayLoadingChildren. - assertFalse(currentState.allEntries[fakeFs.entries['/aaa/2/123'].toURL()] - .shouldDelayLoadingChildren); - const newState2 = - addChildEntries(currentState, addChildEntriesAction({ - parentKey: fakeFs.entries['/aaa/2'].toURL(), - entries: [fakeFs.entries['/aaa/2/123']], - })); - // Expect child entry also has shouldDelayLoadingChildren=true. - const newChildren2 = - newState2.allEntries[fakeFs.entries['/aaa/2'].toURL()].children; - assertEquals(1, newChildren2.length); - assertEquals(fakeFs.entries['/aaa/2/123'].toURL(), newChildren2[0]); - assertTrue(newState2.allEntries[fakeFs.entries['/aaa/2/123'].toURL()] - .shouldDelayLoadingChildren); + // Dispatch an action to add child entries for /aaa/. + const a1Entry = fileSystem.entries['/a/1']; + const a2Entry = fileSystem.entries['/a/2']; + store.dispatch(addChildEntries({ + parentKey: aEntry.toURL(), + entries: [a1Entry, a2Entry], + })); - // Add child entries for non-existed parent. - const newState3 = addChildEntries(currentState, addChildEntriesAction({ - parentKey: 'non-exist-key', - entries: [fakeFs.entries['/aaa/1']], - })); - // Expect nothing happens. - assertEquals(currentState, newState3); + // Expect the children filed of /a is updated. + const want1: State['allEntries'] = { + [aEntry.toURL()]: { + ...convertEntryToFileData(aEntry), + children: [a1Entry.toURL(), a2Entry.toURL()], + }, + [a1Entry.toURL()]: convertEntryToFileData(a1Entry), + [a2Entry.toURL()]: convertEntryToFileData(a2Entry), + }; + await waitDeepEquals(store, want1, (state) => state.allEntries); + + // Set shouldDelayLoadingChildren=true for /a/2. + store.getState().allEntries[a2Entry.toURL()].shouldDelayLoadingChildren = + true; + // Dispatch an action to add child entries for /a/2. + const bEntry = fileSystem.entries['/a/2/b']; + store.dispatch(addChildEntries({ + parentKey: a2Entry.toURL(), + entries: [bEntry], + })); + + // Expect child entry /a/2/b also has shouldDelayLoadingChildren=true. + const want2: State['allEntries'] = { + ...want1, + [a2Entry.toURL()]: { + ...convertEntryToFileData(a2Entry), + shouldDelayLoadingChildren: true, + children: [bEntry.toURL()], + }, + [bEntry.toURL()]: { + ...convertEntryToFileData(bEntry), + shouldDelayLoadingChildren: true, + }, + }; + await waitDeepEquals(store, want2, (state) => state.allEntries); + + // Dispatch an action to add child entries for non-existed parent entry. + store.dispatch(addChildEntries({ + parentKey: 'non-exist-key', + entries: [a1Entry], + })); + + // Expect nothing changes in the store. + await waitDeepEquals(store, want2, (state) => state.allEntries); + + done(); }
diff --git a/ui/file_manager/file_manager/state/reducers/android_apps_unittest.ts b/ui/file_manager/file_manager/state/reducers/android_apps_unittest.ts index 8b44457..923fd4c 100644 --- a/ui/file_manager/file_manager/state/reducers/android_apps_unittest.ts +++ b/ui/file_manager/file_manager/state/reducers/android_apps_unittest.ts
@@ -2,16 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {assertEquals} from 'chrome://webui-test/chai_assert.js'; - -import {addAndroidApps as addAndroidAppsAction} from '../actions/android_apps.js'; -import {getEmptyState} from '../store.js'; - -import {addAndroidApps} from './android_apps.js'; +import {State} from '../../externs/ts/state.js'; +import {addAndroidApps} from '../actions/android_apps.js'; +import {setupStore, waitDeepEquals} from '../for_tests.js'; /** Tests that android apps can be added correctly to the store. */ -export function testAddAndroidApps() { - const currentState = getEmptyState(); +export async function testAddAndroidApps(done: () => void) { const androidApps: chrome.fileManagerPrivate.AndroidApp[] = [ { name: 'App 1', @@ -26,15 +22,17 @@ iconSet: {icon16x16Url: 'url3', icon32x32Url: 'url4'}, }, ]; - const newState = - addAndroidApps(currentState, addAndroidAppsAction({apps: androidApps})); - const keys = Object.keys(newState.androidApps); - assertEquals(2, keys.length); - assertEquals('com.test.app1', keys[0]); - assertEquals('com.test.app2', keys[1]); - assertEquals('App 1', newState.androidApps[keys[0]!].name); - assertEquals('App 2', newState.androidApps[keys[1]!].name); - assertEquals('Activity1', newState.androidApps[keys[0]!].activityName); - assertEquals('url1', newState.androidApps[keys[0]!].iconSet.icon16x16Url); - assertEquals('url4', newState.androidApps[keys[1]!].iconSet.icon32x32Url); + + // Dispatch an action to add android apps. + const store = setupStore(); + store.dispatch(addAndroidApps({apps: androidApps})); + + // Expect both android apps are existed in the store. + const want: State['androidApps'] = { + 'com.test.app1': androidApps[0], + 'com.test.app2': androidApps[1], + }; + await waitDeepEquals(store, want, (state) => state.androidApps); + + done(); }
diff --git a/ui/file_manager/file_manager/state/reducers/current_directory.ts b/ui/file_manager/file_manager/state/reducers/current_directory.ts index 6b8a3b78..2e7d9cd8 100644 --- a/ui/file_manager/file_manager/state/reducers/current_directory.ts +++ b/ui/file_manager/file_manager/state/reducers/current_directory.ts
@@ -67,7 +67,7 @@ // At the end of the change directory, DirectoryContents will send an Action // with the Entry to be cached. if (fileData) { - const volumeManager = window.fileManager?.volumeManager; + const {volumeManager} = window.fileManager; if (!volumeManager) { console.debug(`VolumeManager not available yet.`); currentDirectory = currentState.currentDirectory || currentDirectory;
diff --git a/ui/file_manager/file_manager/state/reducers/folder_shortcuts_unittest.ts b/ui/file_manager/file_manager/state/reducers/folder_shortcuts_unittest.ts index cc2c652..ef376df 100644 --- a/ui/file_manager/file_manager/state/reducers/folder_shortcuts_unittest.ts +++ b/ui/file_manager/file_manager/state/reducers/folder_shortcuts_unittest.ts
@@ -2,16 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {assertEquals} from 'chrome://webui-test/chai_assert.js'; - import {MockFileSystem} from '../../common/js/mock_entry.js'; -import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js'; -import {EntryType} from '../../externs/ts/state.js'; -import {addFolderShortcut as addFolderShortcutAction, refreshFolderShortcut as refreshFolderShortcutAction, removeFolderShortcut as removeFolderShortcutAction} from '../actions/folder_shortcuts.js'; -import {createFakeFileData} from '../for_tests.js'; +import {State} from '../../externs/ts/state.js'; +import {addFolderShortcut, refreshFolderShortcut, removeFolderShortcut} from '../actions/folder_shortcuts.js'; +import {setUpFileManagerOnWindow, setupStore, waitDeepEquals} from '../for_tests.js'; import {getEmptyState} from '../store.js'; -import {addFolderShortcut, refreshFolderShortcut, removeFolderShortcut} from './folder_shortcuts.js'; +import {convertEntryToFileData} from './all_entries.js'; + +export function setUp() { + setUpFileManagerOnWindow(); +} /** Generate a fake file system with fake file entries. */ function setupFileSystem(): MockFileSystem { @@ -25,92 +26,137 @@ return fileSystem; } - /** Tests folder shortcuts can be refreshed correctly. */ -export function testRefreshFolderShortcuts() { - const currentState = getEmptyState(); +export async function testRefreshFolderShortcuts(done: () => void) { + const initialState = getEmptyState(); // Add shortcut-1 to the store. const fileSystem = setupFileSystem(); const shortcutEntry1: DirectoryEntry = fileSystem.entries['/shortcut-1']; + initialState.allEntries[shortcutEntry1.toURL()] = + convertEntryToFileData(shortcutEntry1); + initialState.folderShortcuts.push(shortcutEntry1.toURL()); + + const store = setupStore(initialState); + + // Dispatch a refresh action with shortcut 2 and 3. const shortcutEntry2: DirectoryEntry = fileSystem.entries['/shortcut-2']; const shortcutEntry3: DirectoryEntry = fileSystem.entries['/shortcut-3']; - currentState.allEntries[shortcutEntry1.toURL()] = createFakeFileData({ - entry: shortcutEntry1, - volumeType: VolumeManagerCommon.VolumeType.DRIVE, - label: 'shortcut 1', - type: EntryType.FS_API, - }); - currentState.folderShortcuts.push(shortcutEntry1.toURL()); + store.dispatch( + refreshFolderShortcut({entries: [shortcutEntry2, shortcutEntry3]})); - const newState = refreshFolderShortcut( - currentState, - refreshFolderShortcutAction({entries: [shortcutEntry2, shortcutEntry3]})); - assertEquals(2, newState.folderShortcuts.length); - assertEquals(shortcutEntry2.toURL(), newState.folderShortcuts[0]); - assertEquals(shortcutEntry3.toURL(), newState.folderShortcuts[1]); + // Expect all shortcut entries are in allEntries, and only shortcut 2 and 3 + // are in the folderShortcuts. + const want: Partial<State> = { + allEntries: { + [shortcutEntry2.toURL()]: convertEntryToFileData(shortcutEntry2), + [shortcutEntry3.toURL()]: convertEntryToFileData(shortcutEntry3), + }, + folderShortcuts: [shortcutEntry2.toURL(), shortcutEntry3.toURL()], + }; + await waitDeepEquals(store, want, (state) => ({ + allEntries: state.allEntries, + folderShortcuts: state.folderShortcuts, + })); + + done(); } /** Tests folder shortcut can be added correctly. */ -export function testAddFolderShortcut() { - const currentState = getEmptyState(); +export async function testAddFolderShortcut(done: () => void) { + const initialState = getEmptyState(); // Add shortcut-1 and shortcut-3 to the store. const fileSystem = setupFileSystem(); const shortcutEntry1: DirectoryEntry = fileSystem.entries['/shortcut-1']; - const shortcutEntry2: DirectoryEntry = fileSystem.entries['/shortcut-2']; const shortcutEntry3: DirectoryEntry = fileSystem.entries['/shortcut-3']; - const shortcutEntry4: DirectoryEntry = fileSystem.entries['/shortcut-4']; - currentState.allEntries[shortcutEntry1.toURL()] = createFakeFileData({ - entry: shortcutEntry1, - volumeType: VolumeManagerCommon.VolumeType.DRIVE, - label: 'shortcut 1', - type: EntryType.FS_API, - }); - currentState.allEntries[shortcutEntry3.toURL()] = createFakeFileData({ - entry: shortcutEntry3, - volumeType: VolumeManagerCommon.VolumeType.DRIVE, - label: 'shortcut 3', - type: EntryType.FS_API, - }); - currentState.folderShortcuts.push(shortcutEntry1.toURL()); - currentState.folderShortcuts.push(shortcutEntry3.toURL()); + initialState.allEntries[shortcutEntry1.toURL()] = + convertEntryToFileData(shortcutEntry1); + initialState.allEntries[shortcutEntry3.toURL()] = + convertEntryToFileData(shortcutEntry3); + initialState.folderShortcuts.push(shortcutEntry1.toURL()); + initialState.folderShortcuts.push(shortcutEntry3.toURL()); - // Add a new shortcut. - const newState1 = addFolderShortcut( - currentState, addFolderShortcutAction({entry: shortcutEntry2})); - assertEquals(3, newState1.folderShortcuts.length); - assertEquals(shortcutEntry2.toURL(), newState1.folderShortcuts[1]); - // Add an already existed shortcut. - const newState2 = addFolderShortcut( - currentState, addFolderShortcutAction({entry: shortcutEntry1})); - assertEquals(newState2.folderShortcuts, currentState.folderShortcuts); - // Add another entry to check sorting. - const newState3 = addFolderShortcut( - currentState, addFolderShortcutAction({entry: shortcutEntry4})); - assertEquals(3, newState3.folderShortcuts.length); - assertEquals(shortcutEntry4.toURL(), newState3.folderShortcuts[2]); + const store = setupStore(initialState); + + // Dispatch an action to add shortcut 4. + const shortcutEntry4: DirectoryEntry = fileSystem.entries['/shortcut-4']; + store.dispatch(addFolderShortcut({entry: shortcutEntry4})); + + // Expect the newly added shortcut 4 is in the store. + const want1: Partial<State> = { + allEntries: { + [shortcutEntry1.toURL()]: convertEntryToFileData(shortcutEntry1), + [shortcutEntry3.toURL()]: convertEntryToFileData(shortcutEntry3), + [shortcutEntry4.toURL()]: convertEntryToFileData(shortcutEntry4), + }, + folderShortcuts: [ + shortcutEntry1.toURL(), + shortcutEntry3.toURL(), + shortcutEntry4.toURL(), + ], + }; + await waitDeepEquals(store, want1, (state) => ({ + allEntries: state.allEntries, + folderShortcuts: state.folderShortcuts, + })); + + // Dispatch another action to add already existed shortcut 1. + store.dispatch(addFolderShortcut({entry: shortcutEntry1})); + + // Expect no changes in the store. + await waitDeepEquals(store, want1, (state) => ({ + allEntries: state.allEntries, + folderShortcuts: state.folderShortcuts, + })); + + // Dispatch another action to add shortcut 2 to check sorting. + const shortcutEntry2: DirectoryEntry = fileSystem.entries['/shortcut-2']; + store.dispatch(addFolderShortcut({entry: shortcutEntry2})); + + // Expect shortcut 2 will be inserted in the middle. + const want2: Partial<State> = { + allEntries: { + ...want1.allEntries, + [shortcutEntry2.toURL()]: convertEntryToFileData(shortcutEntry2), + }, + folderShortcuts: [ + shortcutEntry1.toURL(), + shortcutEntry2.toURL(), + shortcutEntry3.toURL(), + shortcutEntry4.toURL(), + ], + }; + await waitDeepEquals(store, want2, (state) => ({ + allEntries: state.allEntries, + folderShortcuts: state.folderShortcuts, + })); + + done(); } /** Tests folder shortcut can be removed correctly. */ -export function testRemoveFolderShortcut() { - const currentState = getEmptyState(); +export async function testRemoveFolderShortcut(done: () => void) { + const initialState = getEmptyState(); // Add shortcut-1 to the store. const fileSystem = setupFileSystem(); const shortcutEntry1: DirectoryEntry = fileSystem.entries['/shortcut-1']; - const shortcutEntry2: DirectoryEntry = fileSystem.entries['/shortcut-2']; - currentState.allEntries[shortcutEntry1.toURL()] = createFakeFileData({ - entry: shortcutEntry1, - volumeType: VolumeManagerCommon.VolumeType.DRIVE, - label: 'shortcut 1', - type: EntryType.FS_API, - }); - currentState.folderShortcuts.push(shortcutEntry1.toURL()); + initialState.allEntries[shortcutEntry1.toURL()] = + convertEntryToFileData(shortcutEntry1); + initialState.folderShortcuts.push(shortcutEntry1.toURL()); - // Remove a shortcut. - const newState1 = removeFolderShortcut( - currentState, removeFolderShortcutAction({key: shortcutEntry1.toURL()})); - assertEquals(0, newState1.folderShortcuts.length); - // Remove a non-exist shortcut. - const newState2 = removeFolderShortcut( - currentState, removeFolderShortcutAction({key: shortcutEntry2.toURL()})); - assertEquals(newState2.folderShortcuts, currentState.folderShortcuts); + const store = setupStore(initialState); + + // Dispatch an action to remove shortcut 1. + store.dispatch(removeFolderShortcut({key: shortcutEntry1.toURL()})); + + // Expect shortcut 1 is removed from the store. + await waitDeepEquals(store, [], (state) => state.folderShortcuts); + + // Dispatch another action to remove non-existed shortcut 2. + const shortcutEntry2: DirectoryEntry = fileSystem.entries['/shortcut-2']; + store.dispatch(removeFolderShortcut({key: shortcutEntry2.toURL()})); + + // Expect no changes in the store. + await waitDeepEquals(store, [], (state) => state.folderShortcuts); + + done(); }
diff --git a/ui/file_manager/file_manager/state/reducers/navigation_unittest.ts b/ui/file_manager/file_manager/state/reducers/navigation_unittest.ts index 6dbb326..21e32d23 100644 --- a/ui/file_manager/file_manager/state/reducers/navigation_unittest.ts +++ b/ui/file_manager/file_manager/state/reducers/navigation_unittest.ts
@@ -2,20 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; - import {MockVolumeManager} from '../../background/js/mock_volume_manager.js'; import {EntryList, FakeEntryImpl, VolumeEntry} from '../../common/js/files_app_entry_types.js'; import {MockFileEntry, MockFileSystem} from '../../common/js/mock_entry.js'; import {TrashRootEntry} from '../../common/js/trash.js'; import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js'; -import {EntryType, FileData, NavigationSection, NavigationType, Volume} from '../../externs/ts/state.js'; -import {refreshNavigationRoots as refreshNavigationRootsAction, updateNavigationEntry as updateNavigationEntryAction} from '../actions/navigation.js'; -import {createFakeFileData, createFakeVolume, setUpFileManagerOnWindow} from '../for_tests.js'; +import {FileData, NavigationSection, NavigationType, State, Volume} from '../../externs/ts/state.js'; +import {refreshNavigationRoots, updateNavigationEntry} from '../actions/navigation.js'; +import {createFakeVolumeMetadata, setUpFileManagerOnWindow, setupStore, waitDeepEquals} from '../for_tests.js'; import {getEmptyState} from '../store.js'; -import {refreshNavigationRoots, updateNavigationEntry} from './navigation.js'; -import {driveRootEntryListKey, myFilesEntryListKey, recentRootKey, trashRootKey} from './volumes.js'; +import {convertEntryToFileData} from './all_entries.js'; +import {convertVolumeInfoAndMetadataToVolume, driveRootEntryListKey, myFilesEntryListKey, recentRootKey, trashRootKey} from './volumes.js'; export function setUp() { setUpFileManagerOnWindow(); @@ -27,11 +25,7 @@ 'Recent', VolumeManagerCommon.RootType.RECENT, chrome.fileManagerPrivate.SourceRestriction.ANY_SOURCE, chrome.fileManagerPrivate.FileCategory.ALL); - return createFakeFileData({ - entry: recentEntry, - label: 'Recent', - type: EntryType.RECENT, - }); + return convertEntryToFileData(recentEntry); } /** Create FileData for shortcut entry. */ @@ -39,11 +33,10 @@ fileSystemName: string, entryName: string, label: string): FileData { const fakeFs = new MockFileSystem(fileSystemName); const shortcutEntry = MockFileEntry.create(fakeFs, `/root/${entryName}`); - return createFakeFileData({ - entry: shortcutEntry, + return { + ...convertEntryToFileData(shortcutEntry), label, - type: EntryType.FS_API, - }); + }; } /** Create FileData for MyFiles entry. */ @@ -52,40 +45,23 @@ const downloadsVolumeInfo = volumeManager.getCurrentProfileVolumeInfo( VolumeManagerCommon.VolumeType.DOWNLOADS)!; const myFilesEntry = new VolumeEntry(downloadsVolumeInfo); - const fileData = createFakeFileData({ - entry: myFilesEntry, - volumeType: VolumeManagerCommon.VolumeType.DOWNLOADS, - label: 'My files', - type: EntryType.VOLUME_ROOT, - }); - const volume = createFakeVolume({ - volumeId: downloadsVolumeInfo.volumeId, - volumeType: fileData.volumeType!, - label: fileData.label, - rootKey: fileData.entry.toURL(), - }); + const fileData = convertEntryToFileData(myFilesEntry); + const volume = convertVolumeInfoAndMetadataToVolume( + downloadsVolumeInfo, createFakeVolumeMetadata(downloadsVolumeInfo)); return {fileData, volume}; } /** Create FileData for drive root entry. */ -function createDriveRootEntryFileData(): FileData { - const driveEntry = new EntryList( +function createDriveRootEntryListFileData(): FileData { + const driveRootEntryList = new EntryList( 'Google Drive', VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT); - return createFakeFileData({ - entry: driveEntry, - label: 'Google Drive', - type: EntryType.ENTRY_LIST, - }); + return convertEntryToFileData(driveRootEntryList); } /** Create FileData for trash entry. */ function createTrashEntryFileData(): FileData { const trashEntry = new TrashRootEntry(); - return createFakeFileData({ - entry: trashEntry, - label: 'Bin', - type: EntryType.TRASH, - }); + return convertEntryToFileData(trashEntry); } /** Create android apps. */ @@ -118,18 +94,9 @@ const {volumeManager} = window.fileManager; volumeManager.volumeInfoList.add(volumeInfo); const volumeEntry = new VolumeEntry(volumeInfo); - const fileData = createFakeFileData({ - entry: volumeEntry, - label, - type: EntryType.VOLUME_ROOT, - }); - const volume = createFakeVolume({ - volumeId: volumeInfo.volumeId, - volumeType: volumeInfo.volumeType, - label, - rootKey: volumeEntry.toURL(), - devicePath, - }); + const fileData = convertEntryToFileData(volumeEntry); + const volume = convertVolumeInfoAndMetadataToVolume( + volumeInfo, createFakeVolumeMetadata(volumeInfo)); return {fileData, volume}; } @@ -139,44 +106,44 @@ * 2. manages NavigationSection for the relevant volumes. * 3. keeps MTP/Archive/Removable volumes on the original order. */ -export function testNavigationRoots() { - const currentState = getEmptyState(); +export async function testNavigationRoots(done: () => void) { + const initialState = getEmptyState(); // Put recent entry in the store. const recentEntryFileData = createRecentFileData(); - currentState.allEntries[recentRootKey] = recentEntryFileData; + initialState.allEntries[recentRootKey] = recentEntryFileData; // Put 2 shortcut entries in the store. const shortcutEntryFileData1 = createShortcutEntryFileData('drive', 'shortcut1', 'Shortcut 1'); - currentState.allEntries[shortcutEntryFileData1.entry.toURL()] = + initialState.allEntries[shortcutEntryFileData1.entry.toURL()] = shortcutEntryFileData1; - currentState.folderShortcuts.push(shortcutEntryFileData1.entry.toURL()); + initialState.folderShortcuts.push(shortcutEntryFileData1.entry.toURL()); const shortcutEntryFileData2 = createShortcutEntryFileData('drive', 'shortcut2', 'Shortcut 2'); - currentState.allEntries[shortcutEntryFileData2.entry.toURL()] = + initialState.allEntries[shortcutEntryFileData2.entry.toURL()] = shortcutEntryFileData2; - currentState.folderShortcuts.push(shortcutEntryFileData2.entry.toURL()); + initialState.folderShortcuts.push(shortcutEntryFileData2.entry.toURL()); // Put MyFiles entry in the store. const myFilesVolume = createMyFilesEntryFileData(); - currentState.allEntries[myFilesVolume.fileData.entry.toURL()] = + initialState.allEntries[myFilesVolume.fileData.entry.toURL()] = myFilesVolume.fileData; - currentState.volumes[myFilesVolume.volume.volumeId] = myFilesVolume.volume; + initialState.volumes[myFilesVolume.volume.volumeId] = myFilesVolume.volume; // Put drive entry in the store. - const driveRootEntryFileData = createDriveRootEntryFileData(); - currentState.allEntries[driveRootEntryListKey] = driveRootEntryFileData; + const driveRootEntryFileData = createDriveRootEntryListFileData(); + initialState.allEntries[driveRootEntryListKey] = driveRootEntryFileData; // Put trash entry in the store. const trashEntryFileData = createTrashEntryFileData(); - currentState.allEntries[trashRootKey] = trashEntryFileData; + initialState.allEntries[trashRootKey] = trashEntryFileData; // Put the android apps in the store. const androidAppsData = createAndroidApps(); - currentState.androidApps[androidAppsData[0].packageName] = androidAppsData[0]; - currentState.androidApps[androidAppsData[1].packageName] = androidAppsData[1]; + initialState.androidApps[androidAppsData[0].packageName] = androidAppsData[0]; + initialState.androidApps[androidAppsData[1].packageName] = androidAppsData[1]; // Create different volumes. const providerVolume1 = createVolumeFileData( VolumeManagerCommon.VolumeType.PROVIDED, 'provided:prov1'); - currentState.allEntries[providerVolume1.fileData.entry.toURL()] = + initialState.allEntries[providerVolume1.fileData.entry.toURL()] = providerVolume1.fileData; - currentState.volumes[providerVolume1.volume.volumeId] = + initialState.volumes[providerVolume1.volume.volumeId] = providerVolume1.volume; // Set the device paths of the removable volumes to different strings to @@ -184,53 +151,56 @@ const hogeVolume = createVolumeFileData( VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:hoge', 'Hoge', 'device/path/1'); - currentState.allEntries[hogeVolume.fileData.entry.toURL()] = + initialState.allEntries[hogeVolume.fileData.entry.toURL()] = hogeVolume.fileData; - currentState.volumes[hogeVolume.volume.volumeId] = hogeVolume.volume; + initialState.volumes[hogeVolume.volume.volumeId] = hogeVolume.volume; const fugaVolume = createVolumeFileData( VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:fuga', 'Fuga', 'device/path/2'); - currentState.allEntries[fugaVolume.fileData.entry.toURL()] = + initialState.allEntries[fugaVolume.fileData.entry.toURL()] = fugaVolume.fileData; - currentState.volumes[fugaVolume.volume.volumeId] = fugaVolume.volume; + initialState.volumes[fugaVolume.volume.volumeId] = fugaVolume.volume; const archiveVolume = createVolumeFileData( VolumeManagerCommon.VolumeType.ARCHIVE, 'archive:a-rar'); - currentState.allEntries[archiveVolume.fileData.entry.toURL()] = + initialState.allEntries[archiveVolume.fileData.entry.toURL()] = archiveVolume.fileData; - currentState.volumes[archiveVolume.volume.volumeId] = archiveVolume.volume; + initialState.volumes[archiveVolume.volume.volumeId] = archiveVolume.volume; const mtpVolume = createVolumeFileData(VolumeManagerCommon.VolumeType.MTP, 'mtp:a-phone'); - currentState.allEntries[mtpVolume.fileData.entry.toURL()] = + initialState.allEntries[mtpVolume.fileData.entry.toURL()] = mtpVolume.fileData; - currentState.volumes[mtpVolume.volume.volumeId] = mtpVolume.volume; + initialState.volumes[mtpVolume.volume.volumeId] = mtpVolume.volume; const providerVolume2 = createVolumeFileData( VolumeManagerCommon.VolumeType.PROVIDED, 'provided:prov2'); - currentState.allEntries[providerVolume2.fileData.entry.toURL()] = + initialState.allEntries[providerVolume2.fileData.entry.toURL()] = providerVolume2.fileData; - currentState.volumes[providerVolume2.volume.volumeId] = + initialState.volumes[providerVolume2.volume.volumeId] = providerVolume2.volume; const androidFilesVolume = createVolumeFileData( VolumeManagerCommon.VolumeType.ANDROID_FILES, 'android_files:droid'); androidFilesVolume.volume.prefixKey = myFilesVolume.fileData.entry.toURL(); - currentState.allEntries[androidFilesVolume.fileData.entry.toURL()] = + initialState.allEntries[androidFilesVolume.fileData.entry.toURL()] = androidFilesVolume.fileData; - currentState.volumes[androidFilesVolume.volume.volumeId] = + initialState.volumes[androidFilesVolume.volume.volumeId] = androidFilesVolume.volume; const smbVolume = createVolumeFileData( VolumeManagerCommon.VolumeType.SMB, 'smb:file-share'); - currentState.allEntries[smbVolume.fileData.entry.toURL()] = + initialState.allEntries[smbVolume.fileData.entry.toURL()] = smbVolume.fileData; - currentState.volumes[smbVolume.volume.volumeId] = smbVolume.volume; + initialState.volumes[smbVolume.volume.volumeId] = smbVolume.volume; - const newState = - refreshNavigationRoots(currentState, refreshNavigationRootsAction()); - // Navigation roots built above: + const store = setupStore(initialState); + + // Dispatch an action to refresh navigation roots. + store.dispatch(refreshNavigationRoots()); + + // Expect navigation roots being built in the store: // 1. fake-entry://recent // 2. /root/shortcut1 // 3. /root/shortcut2 @@ -253,357 +223,449 @@ // Check items order and that MTP/Archive/Removable respect the original // order. - const {roots} = newState.navigation; - assertEquals(15, roots.length); + const want: State['navigation']['roots'] = [ + // recent. + { + key: recentEntryFileData.entry.toURL(), + section: NavigationSection.TOP, + separator: false, + type: NavigationType.RECENT, + }, + // shortcut1. + { + key: shortcutEntryFileData1.entry.toURL(), + section: NavigationSection.TOP, + separator: false, + type: NavigationType.SHORTCUT, + }, + // shortcut2. + { + key: shortcutEntryFileData2.entry.toURL(), + section: NavigationSection.TOP, + separator: false, + type: NavigationType.SHORTCUT, + }, + // My Files. + { + key: myFilesVolume.fileData.entry.toURL(), + section: NavigationSection.MY_FILES, + separator: true, + type: NavigationType.VOLUME, + }, + // Drive. + { + key: driveRootEntryFileData.entry.toURL(), + section: NavigationSection.CLOUD, + separator: true, + type: NavigationType.DRIVE, + }, + // Trash. + { + key: trashEntryFileData.entry.toURL(), + section: NavigationSection.TRASH, + separator: true, + type: NavigationType.TRASH, + }, + // FSP, and SMB are grouped together. + // smb:file-share. + { + key: smbVolume.fileData.entry.toURL(), + section: NavigationSection.CLOUD, + separator: true, + type: NavigationType.VOLUME, + }, + // provided:prov1. + { + key: providerVolume1.fileData.entry.toURL(), + section: NavigationSection.CLOUD, + separator: false, + type: NavigationType.VOLUME, + }, + // provided:prov2. + { + key: providerVolume2.fileData.entry.toURL(), + section: NavigationSection.CLOUD, + separator: false, + type: NavigationType.VOLUME, + }, + // MTP/Archive/Removable are grouped together. + // removable:hoge. + { + key: hogeVolume.fileData.entry.toURL(), + section: NavigationSection.REMOVABLE, + separator: true, + type: NavigationType.VOLUME, + }, + // removable:fuga. + { + key: fugaVolume.fileData.entry.toURL(), + section: NavigationSection.REMOVABLE, + separator: false, + type: NavigationType.VOLUME, + }, + // archive:a-rar. + { + key: archiveVolume.fileData.entry.toURL(), + section: NavigationSection.REMOVABLE, + separator: false, + type: NavigationType.VOLUME, + }, + // mtp:a-phone. + { + key: mtpVolume.fileData.entry.toURL(), + section: NavigationSection.REMOVABLE, + separator: false, + type: NavigationType.VOLUME, + }, + // android:app1. + { + key: androidAppsData[0].packageName, + section: NavigationSection.ANDROID_APPS, + separator: true, + type: NavigationType.ANDROID_APPS, + }, + // android:app2. + { + key: androidAppsData[1].packageName, + section: NavigationSection.ANDROID_APPS, + separator: false, + type: NavigationType.ANDROID_APPS, + }, + ]; + await waitDeepEquals(store, want, (state) => state.navigation.roots); - // recent. - assertEquals(recentEntryFileData.entry.toURL(), roots[0]!.key); - assertEquals(NavigationSection.TOP, roots[0]!.section); - assertEquals(false, roots[0]!.separator); - assertEquals(NavigationType.RECENT, roots[0]!.type); - // shortcut1. - assertEquals(shortcutEntryFileData1.entry.toURL(), roots[1]!.key); - assertEquals(NavigationSection.TOP, roots[1]!.section); - assertEquals(false, roots[1]!.separator); - assertEquals(NavigationType.SHORTCUT, roots[1]!.type); - // shortcut2. - assertEquals(shortcutEntryFileData2.entry.toURL(), roots[2]!.key); - assertEquals(NavigationSection.TOP, roots[2]!.section); - assertEquals(false, roots[2]!.separator); - assertEquals(NavigationType.SHORTCUT, roots[2]!.type); - - // My Files. - assertEquals(myFilesVolume.fileData.entry.toURL(), roots[3]!.key); - assertEquals(NavigationSection.MY_FILES, roots[3]!.section); - assertEquals(true, roots[3]!.separator); - assertEquals(NavigationType.VOLUME, roots[3]!.type); - - // Drive. - assertEquals(driveRootEntryFileData.entry.toURL(), roots[4]!.key); - assertEquals(NavigationSection.CLOUD, roots[4]!.section); - assertEquals(true, roots[4]!.separator); - assertEquals(NavigationType.DRIVE, roots[4]!.type); - - // Trash. - assertEquals(trashEntryFileData.entry.toURL(), roots[5]!.key); - assertEquals(NavigationSection.TRASH, roots[5]!.section); - assertEquals(true, roots[5]!.separator); - assertEquals(NavigationType.TRASH, roots[5]!.type); - - // FSP, and SMB are grouped together. - // smb:file-share. - assertEquals(smbVolume.fileData.entry.toURL(), roots[6]!.key); - assertEquals(NavigationSection.CLOUD, roots[6]!.section); - assertEquals(true, roots[6]!.separator); - assertEquals(NavigationType.VOLUME, roots[6]!.type); - // provided:prov1. - assertEquals(providerVolume1.fileData.entry.toURL(), roots[7]!.key); - assertEquals(NavigationSection.CLOUD, roots[7]!.section); - assertEquals(false, roots[7]!.separator); - assertEquals(NavigationType.VOLUME, roots[7]!.type); - // provided:prov2. - assertEquals(providerVolume2.fileData.entry.toURL(), roots[8]!.key); - assertEquals(NavigationSection.CLOUD, roots[8]!.section); - assertEquals(false, roots[8]!.separator); - assertEquals(NavigationType.VOLUME, roots[8]!.type); - - // MTP/Archive/Removable are grouped together. - // removable:hoge. - assertEquals(hogeVolume.fileData.entry.toURL(), roots[9]!.key); - assertEquals(NavigationSection.REMOVABLE, roots[9]!.section); - assertEquals(true, roots[9]!.separator); - assertEquals(NavigationType.VOLUME, roots[9]!.type); - // removable:fuga. - assertEquals(fugaVolume.fileData.entry.toURL(), roots[10]!.key); - assertEquals(NavigationSection.REMOVABLE, roots[10]!.section); - assertEquals(false, roots[10]!.separator); - assertEquals(NavigationType.VOLUME, roots[10]!.type); - // archive:a-rar. - assertEquals(archiveVolume.fileData.entry.toURL(), roots[11]!.key); - assertEquals(NavigationSection.REMOVABLE, roots[11]!.section); - assertEquals(false, roots[11]!.separator); - assertEquals(NavigationType.VOLUME, roots[11]!.type); - // mtp:a-phone. - assertEquals(mtpVolume.fileData.entry.toURL(), roots[12]!.key); - assertEquals(NavigationSection.REMOVABLE, roots[12]!.section); - assertEquals(false, roots[12]!.separator); - assertEquals(NavigationType.VOLUME, roots[12]!.type); - - // android:app1 - assertEquals(androidAppsData[0].packageName, roots[13]!.key); - assertEquals(NavigationSection.ANDROID_APPS, roots[13]!.section); - assertEquals(true, roots[13]!.separator); - assertEquals(NavigationType.ANDROID_APPS, roots[13]!.type); - // android:app2 - assertEquals(androidAppsData[1].packageName, roots[14]!.key); - assertEquals(NavigationSection.ANDROID_APPS, roots[14]!.section); - assertEquals(false, roots[14]!.separator); - assertEquals(NavigationType.ANDROID_APPS, roots[14]!.type); + done(); } /** * Tests navigation roots with no Recents. */ -export function testNavigationRootsWithoutRecents() { - const currentState = getEmptyState(); +export async function testNavigationRootsWithoutRecents(done: () => void) { + const initialState = getEmptyState(); // Put shortcut entry in the store. const shortcutEntryFileData = createShortcutEntryFileData('drive', 'shortcut', 'Shortcut'); - currentState.allEntries[shortcutEntryFileData.entry.toURL()] = + initialState.allEntries[shortcutEntryFileData.entry.toURL()] = shortcutEntryFileData; - currentState.folderShortcuts.push(shortcutEntryFileData.entry.toURL()); + initialState.folderShortcuts.push(shortcutEntryFileData.entry.toURL()); // Put MyFiles entry in the store. const myFilesVolume = createMyFilesEntryFileData(); - currentState.allEntries[myFilesVolume.fileData.entry.toURL()] = + initialState.allEntries[myFilesVolume.fileData.entry.toURL()] = myFilesVolume.fileData; - currentState.volumes[myFilesVolume.volume.volumeId] = myFilesVolume.volume; + initialState.volumes[myFilesVolume.volume.volumeId] = myFilesVolume.volume; - const newState = - refreshNavigationRoots(currentState, refreshNavigationRootsAction()); + const store = setupStore(initialState); + + // Dispatch an action to refresh navigation roots. + store.dispatch(refreshNavigationRoots()); // Expect 2 navigation roots. - const {roots} = newState.navigation; - assertEquals(2, roots.length); - assertDeepEquals( - { - key: shortcutEntryFileData.entry.toURL(), - section: NavigationSection.TOP, - separator: false, - type: NavigationType.SHORTCUT, - }, - roots[0]); - assertEquals(myFilesVolume.fileData.entry.toURL(), roots[1]!.key); + const want: State['navigation']['roots'] = [ + // shortcut. + { + key: shortcutEntryFileData.entry.toURL(), + section: NavigationSection.TOP, + separator: false, + type: NavigationType.SHORTCUT, + }, + // My Files volume. + { + key: myFilesVolume.fileData.entry.toURL(), + section: NavigationSection.MY_FILES, + separator: true, + type: NavigationType.VOLUME, + }, + ]; + await waitDeepEquals(store, want, (state) => state.navigation.roots); + + done(); } /** * Tests navigation roots with fake MyFiles. */ -export function testNavigationRootsWithFakeMyFiles() { - const currentState = getEmptyState(); +export async function testNavigationRootsWithFakeMyFiles(done: () => void) { + const initialState = getEmptyState(); // Put recent entry in the store. const recentEntryFileData = createRecentFileData(); - currentState.allEntries[recentRootKey] = recentEntryFileData; + initialState.allEntries[recentRootKey] = recentEntryFileData; // Put MyFiles entry in the store. const myFilesEntryList = new EntryList('My files', VolumeManagerCommon.RootType.MY_FILES); - currentState.allEntries[myFilesEntryList.toURL()] = createFakeFileData({ - entry: myFilesEntryList, - label: 'My files', - type: EntryType.ENTRY_LIST, - }); + initialState.allEntries[myFilesEntryList.toURL()] = + convertEntryToFileData(myFilesEntryList); - const newState = - refreshNavigationRoots(currentState, refreshNavigationRootsAction()); + const store = setupStore(initialState); + + // Dispatch an action to refresh navigation roots. + store.dispatch(refreshNavigationRoots()); // Expect 2 navigation roots. - const {roots} = newState.navigation; - assertEquals(2, roots.length); - // The type of MyFiles navigation root should be ENTRY_LIST. - assertDeepEquals( - { - key: myFilesEntryList.toURL(), - section: NavigationSection.MY_FILES, - separator: true, - type: NavigationType.ENTRY_LIST, - }, - roots[1]); + const want: State['navigation']['roots'] = [ + // recent. + { + key: recentEntryFileData.entry.toURL(), + section: NavigationSection.TOP, + separator: false, + type: NavigationType.RECENT, + }, + // My Files entry list. + { + key: myFilesEntryList.toURL(), + section: NavigationSection.MY_FILES, + separator: true, + type: NavigationType.ENTRY_LIST, + }, + ]; + await waitDeepEquals(store, want, (state) => state.navigation.roots); + + done(); } /** * Tests navigation roots with volumes. */ -export function testNavigationRootsWithVolumes() { - const currentState = getEmptyState(); +export async function testNavigationRootsWithVolumes(done: () => void) { + const initialState = getEmptyState(); // Put recent entry in the store. const recentEntryFileData = createRecentFileData(); - currentState.allEntries[recentRootKey] = recentEntryFileData; + initialState.allEntries[recentRootKey] = recentEntryFileData; // Put MyFiles entry in the store. const myFilesVolume = createMyFilesEntryFileData(); - currentState.allEntries[myFilesVolume.fileData.entry.toURL()] = + initialState.allEntries[myFilesVolume.fileData.entry.toURL()] = myFilesVolume.fileData; - currentState.volumes[myFilesVolume.volume.volumeId] = myFilesVolume.volume; + initialState.volumes[myFilesVolume.volume.volumeId] = myFilesVolume.volume; // Put drive entry in the store. - const driveRootEntryFileData = createDriveRootEntryFileData(); - currentState.allEntries[driveRootEntryListKey] = driveRootEntryFileData; + const driveRootEntryFileData = createDriveRootEntryListFileData(); + initialState.allEntries[driveRootEntryListKey] = driveRootEntryFileData; // Put removable volume 'hoge' in the store. const hogeVolume = createVolumeFileData( VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:hoge', 'Hoge', 'device/path/1'); - currentState.allEntries[hogeVolume.fileData.entry.toURL()] = + initialState.allEntries[hogeVolume.fileData.entry.toURL()] = hogeVolume.fileData; - currentState.volumes[hogeVolume.volume.volumeId] = hogeVolume.volume; + initialState.volumes[hogeVolume.volume.volumeId] = hogeVolume.volume; // Create a shortcut for the 'hoge' volume in the store. const hogeShortcutEntryFileData = createShortcutEntryFileData( hogeVolume.volume.volumeId, 'shortcut-hoge', 'Hoge shortcut'); - currentState.allEntries[hogeShortcutEntryFileData.entry.toURL()] = + initialState.allEntries[hogeShortcutEntryFileData.entry.toURL()] = hogeShortcutEntryFileData; - currentState.folderShortcuts.push(hogeShortcutEntryFileData.entry.toURL()); + initialState.folderShortcuts.push(hogeShortcutEntryFileData.entry.toURL()); // Put removable volume 'fuga' in the store. Not a partition, so set a // different device path to 'hoge'. const fugaVolume = createVolumeFileData( VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:fuga', 'Fuga', 'device/path/2'); - currentState.allEntries[fugaVolume.fileData.entry.toURL()] = + initialState.allEntries[fugaVolume.fileData.entry.toURL()] = fugaVolume.fileData; - currentState.volumes[fugaVolume.volume.volumeId] = fugaVolume.volume; + initialState.volumes[fugaVolume.volume.volumeId] = fugaVolume.volume; - const newState = - refreshNavigationRoots(currentState, refreshNavigationRootsAction()); + const store = setupStore(initialState); + + // Dispatch an action to refresh navigation roots. + store.dispatch(refreshNavigationRoots()); // Expect 6 navigation roots. - const {roots} = newState.navigation; - assertEquals(6, roots.length); - assertEquals(recentEntryFileData.entry.toURL(), roots[0]!.key); - assertDeepEquals( - { - key: hogeShortcutEntryFileData.entry.toURL(), - section: NavigationSection.TOP, - separator: false, - type: NavigationType.SHORTCUT, - }, - roots[1]); - assertEquals(myFilesVolume.fileData.entry.toURL(), roots[2]!.key); - assertEquals(driveRootEntryFileData.entry.toURL(), roots[3]!.key); - assertDeepEquals( - { - key: hogeVolume.fileData.entry.toURL(), - section: NavigationSection.REMOVABLE, - separator: true, - type: NavigationType.VOLUME, - }, - roots[4]); - assertDeepEquals( - { - key: fugaVolume.fileData.entry.toURL(), - section: NavigationSection.REMOVABLE, - separator: false, - type: NavigationType.VOLUME, - }, - roots[5]); + const want: State['navigation']['roots'] = [ + // recent. + { + key: recentEntryFileData.entry.toURL(), + section: NavigationSection.TOP, + separator: false, + type: NavigationType.RECENT, + }, + // hoge shortcut. + { + key: hogeShortcutEntryFileData.entry.toURL(), + section: NavigationSection.TOP, + separator: false, + type: NavigationType.SHORTCUT, + }, + // My Files. + { + key: myFilesVolume.fileData.entry.toURL(), + section: NavigationSection.MY_FILES, + separator: true, + type: NavigationType.VOLUME, + }, + // Drive. + { + key: driveRootEntryFileData.entry.toURL(), + section: NavigationSection.CLOUD, + separator: true, + type: NavigationType.DRIVE, + }, + // hoge volume. + { + key: hogeVolume.fileData.entry.toURL(), + section: NavigationSection.REMOVABLE, + separator: true, + type: NavigationType.VOLUME, + }, + // fuga volume. + { + key: fugaVolume.fileData.entry.toURL(), + section: NavigationSection.REMOVABLE, + separator: false, + type: NavigationType.VOLUME, + }, + ]; + await waitDeepEquals(store, want, (state) => state.navigation.roots); + + done(); } /** * Tests that for multiple partition volumes, only the parent entry will be * added to the navigation roots. */ -export function testMultipleUsbPartitionsGrouping() { - const currentState = getEmptyState(); +export async function testMultipleUsbPartitionsGrouping(done: () => void) { + const initialState = getEmptyState(); // Add parent entry list to the store. const devicePath = 'device/path/1'; const parentEntry = new EntryList( 'Partition wrap', VolumeManagerCommon.RootType.REMOVABLE, devicePath); - currentState.allEntries[parentEntry.toURL()] = createFakeFileData({ - entry: parentEntry, - label: 'Partition wrap', - type: EntryType.ENTRY_LIST, - }); + initialState.allEntries[parentEntry.toURL()] = + convertEntryToFileData(parentEntry); // Create 3 volumes with the same device path so the partitions are grouped. const partitionVolume1 = createVolumeFileData( VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:partition1', 'partition1', devicePath); partitionVolume1.volume.prefixKey = parentEntry.toURL(); - currentState.allEntries[partitionVolume1.fileData.entry.toURL()] = + initialState.allEntries[partitionVolume1.fileData.entry.toURL()] = partitionVolume1.fileData; - currentState.volumes[partitionVolume1.volume.volumeId] = + initialState.volumes[partitionVolume1.volume.volumeId] = partitionVolume1.volume; const partitionVolume2 = createVolumeFileData( VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:partition2', 'partition2', devicePath); - currentState.allEntries[partitionVolume2.fileData.entry.toURL()] = + initialState.allEntries[partitionVolume2.fileData.entry.toURL()] = partitionVolume2.fileData; - currentState.volumes[partitionVolume2.volume.volumeId] = + initialState.volumes[partitionVolume2.volume.volumeId] = partitionVolume2.volume; partitionVolume2.volume.prefixKey = parentEntry.toURL(); const partitionVolume3 = createVolumeFileData( VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:partition3', 'partition3', devicePath); - currentState.allEntries[partitionVolume3.fileData.entry.toURL()] = + initialState.allEntries[partitionVolume3.fileData.entry.toURL()] = partitionVolume3.fileData; - currentState.volumes[partitionVolume3.volume.volumeId] = + initialState.volumes[partitionVolume3.volume.volumeId] = partitionVolume3.volume; partitionVolume3.volume.prefixKey = parentEntry.toURL(); - const newState = - refreshNavigationRoots(currentState, refreshNavigationRootsAction()); + const store = setupStore(initialState); + + // Dispatch an action to refresh navigation roots. + store.dispatch(refreshNavigationRoots()); // Expect only the parent entry and MyFiles being added to the navigation // roots. - const {roots} = newState.navigation; - assertEquals(2, roots.length); - assertEquals(myFilesEntryListKey, roots[0]!.key); - assertDeepEquals( - { - key: parentEntry.toURL(), - section: NavigationSection.REMOVABLE, - separator: true, - type: NavigationType.VOLUME, - }, - roots[1]); + const want: State['navigation']['roots'] = [ + // My Files entry list. + { + key: myFilesEntryListKey, + section: NavigationSection.MY_FILES, + separator: true, + type: NavigationType.ENTRY_LIST, + }, + // parent entry for all removable partitions. + { + key: parentEntry.toURL(), + section: NavigationSection.REMOVABLE, + separator: true, + type: NavigationType.VOLUME, + }, + ]; + await waitDeepEquals(store, want, (state) => state.navigation.roots); + + done(); } /** * Tests that the volumes filtered by the volume manager won't be shown in the * navigation roots. */ -export function testNavigationRootsWithFilteredVolume() { - const currentState = getEmptyState(); - // Put 2 volumes in the store. +export async function testNavigationRootsWithFilteredVolume(done: () => void) { + const initialState = getEmptyState(); + // Put volume1 in the store. const volume1 = createVolumeFileData( VolumeManagerCommon.VolumeType.REMOVABLE, 'removable1'); - currentState.allEntries[volume1.fileData.entry.toURL()] = volume1.fileData; - currentState.volumes[volume1.volume.volumeId] = volume1.volume; - // Volume2 is not added to VolumeManager's volumeInfoList. + initialState.allEntries[volume1.fileData.entry.toURL()] = volume1.fileData; + initialState.volumes[volume1.volume.volumeId] = volume1.volume; + // Put volume2 in the store. Note: without calling createVolumeFileData(), + // volume2 won't be in volumeManager's volumeInfoList, thus should be filtered + // out. const volumeInfo2 = MockVolumeManager.createMockVolumeInfo( VolumeManagerCommon.VolumeType.REMOVABLE, 'removable2'); const volumeEntry2 = new VolumeEntry(volumeInfo2); - currentState.allEntries[volumeEntry2.toURL()] = createFakeFileData({ - entry: volumeEntry2, - label: volumeInfo2.label, - type: EntryType.VOLUME_ROOT, - }); - currentState.volumes[volumeInfo2.volumeId] = createFakeVolume({ - volumeId: volumeInfo2.volumeId, - volumeType: volumeInfo2.volumeType, - label: volumeInfo2.label, - rootKey: volumeEntry2.toURL(), - }); + initialState.allEntries[volumeEntry2.toURL()] = + convertEntryToFileData(volumeEntry2); + initialState.volumes[volumeInfo2.volumeId] = + convertVolumeInfoAndMetadataToVolume( + volumeInfo2, createFakeVolumeMetadata(volumeInfo2)); - const newState = - refreshNavigationRoots(currentState, refreshNavigationRootsAction()); + const store = setupStore(initialState); + + // Dispatch an action to refresh navigation roots. + store.dispatch(refreshNavigationRoots()); // Expect only volume1 and MyFiles in the navigation roots. - const {roots} = newState.navigation; - assertEquals(2, roots.length); - assertEquals(myFilesEntryListKey, roots[0]!.key); - assertEquals(volume1.fileData.entry.toURL(), roots[1]!.key); + const want: State['navigation']['roots'] = [ + // My Files entry list. + { + key: myFilesEntryListKey, + section: NavigationSection.MY_FILES, + separator: true, + type: NavigationType.ENTRY_LIST, + }, + // volume1. + { + key: volume1.fileData.entry.toURL(), + section: NavigationSection.REMOVABLE, + separator: true, + type: NavigationType.VOLUME, + }, + ]; + await waitDeepEquals(store, want, (state) => state.navigation.roots); + + done(); } /** Tests that navigation entry can be updated correctly. */ -export function testUpdateNavigationEntry() { - const currentState = getEmptyState(); +export async function testUpdateNavigationEntry(done: () => void) { + const initialState = getEmptyState(); // Add MyFiles entry to the store. const myFilesVolume = createMyFilesEntryFileData(); const myFilesEntryKey = myFilesVolume.fileData.entry.toURL(); - currentState.allEntries[myFilesEntryKey] = myFilesVolume.fileData; + initialState.allEntries[myFilesEntryKey] = myFilesVolume.fileData; - assertFalse(currentState.allEntries[myFilesEntryKey].expanded); - const newState = updateNavigationEntry( - currentState, - updateNavigationEntryAction({key: myFilesEntryKey, expanded: true})); - assertTrue(newState.allEntries[myFilesEntryKey].expanded); + const store = setupStore(initialState); + + // Dispatch an action to update navigation entry. + store.dispatch(updateNavigationEntry({key: myFilesEntryKey, expanded: true})); + + // Expect MyFiles entry is expanded in the store. + await waitDeepEquals( + store, true, (state) => state.allEntries[myFilesEntryKey].expanded); + + done(); } /** Tests that navigation entry won't be updated without valid file data. */ -export function testUpdateNavigationEntryWithoutValidFileData() { - const currentState = getEmptyState(); +export async function testUpdateNavigationEntryWithoutValidFileData( + done: () => void) { + const initialState = getEmptyState(); + const store = setupStore(initialState); - const newState = updateNavigationEntry( - currentState, - updateNavigationEntryAction({key: 'not-exist-key', expanded: true})); + // Dispatch an action to update an non existed navigation entry. + store.dispatch(updateNavigationEntry({key: 'not-exist-key', expanded: true})); + // Check state won't be touched. - assertEquals(currentState, newState); + await waitDeepEquals(store, initialState, (state) => state); + + done(); }
diff --git a/ui/file_manager/file_manager/state/reducers/ui_entries_unittest.ts b/ui/file_manager/file_manager/state/reducers/ui_entries_unittest.ts index ae1d835..e9b5644 100644 --- a/ui/file_manager/file_manager/state/reducers/ui_entries_unittest.ts +++ b/ui/file_manager/file_manager/state/reducers/ui_entries_unittest.ts
@@ -2,18 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {assertArrayEquals, assertEquals} from 'chrome://webui-test/chai_assert.js'; +import {assertEquals} from 'chrome://webui-test/chai_assert.js'; import {MockVolumeManager} from '../../background/js/mock_volume_manager.js'; import {FakeEntryImpl, GuestOsPlaceholder, VolumeEntry} from '../../common/js/files_app_entry_types.js'; +import {waitUntil} from '../../common/js/test_error_reporting.js'; import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js'; -import {EntryType, FileData, State} from '../../externs/ts/state.js'; +import {FileData, State} from '../../externs/ts/state.js'; import {VolumeInfo} from '../../externs/volume_info.js'; -import {addUiEntry as addUiEntryAction, removeUiEntry as removeUiEntryAction} from '../actions/ui_entries.js'; -import {createFakeFileData, createFakeVolume, setUpFileManagerOnWindow} from '../for_tests.js'; +import {addUiEntry, removeUiEntry} from '../actions/ui_entries.js'; +import {createFakeVolumeMetadata, setUpFileManagerOnWindow, setupStore, waitDeepEquals} from '../for_tests.js'; import {getEmptyState} from '../store.js'; -import {addUiEntry, removeUiEntry} from './ui_entries.js'; +import {convertEntryToFileData} from './all_entries.js'; +import {convertVolumeInfoAndMetadataToVolume} from './volumes.js'; export function setUp() { // sortEntries() from addUiEntry() reducer requires volumeManager and @@ -27,224 +29,289 @@ const {volumeManager} = window.fileManager; const downloadsVolumeInfo = volumeManager.getCurrentProfileVolumeInfo( VolumeManagerCommon.VolumeType.DOWNLOADS)!; - const fileData = createFakeFileData({ - entry: new VolumeEntry(downloadsVolumeInfo), - volumeType: VolumeManagerCommon.VolumeType.DOWNLOADS, - label: 'My files', - type: EntryType.VOLUME_ROOT, - }); + const fileData = convertEntryToFileData(new VolumeEntry(downloadsVolumeInfo)); return {fileData, volumeInfo: downloadsVolumeInfo}; } -/** Tests a normal ui entry can be added correctly. */ -export function testAddUiEntry() { - const currentState: State = getEmptyState(); +/** Tests a normal UI entry can be added correctly. */ +export async function testAddUiEntry(done: () => void) { + const initialState = getEmptyState(); + const store = setupStore(initialState); + + // Dispatch an action to add a UI entry. const uiEntry = new FakeEntryImpl('Ui entry', VolumeManagerCommon.RootType.RECENT); - const newState = addUiEntry(currentState, addUiEntryAction({entry: uiEntry})); - assertEquals(1, newState.uiEntries.length); - assertEquals(uiEntry.toURL(), newState.uiEntries[0]); + store.dispatch(addUiEntry({entry: uiEntry})); + + // Expect the newly added entry is in the store. + const want: Partial<State> = { + allEntries: { + [uiEntry.toURL()]: convertEntryToFileData(uiEntry), + }, + uiEntries: [uiEntry.toURL()], + }; + await waitDeepEquals(store, want, (state) => ({ + allEntries: state.allEntries, + uiEntries: state.uiEntries, + })); + + done(); } -/** Tests that a duplicate ui entry won't be added. */ -export function testAddDuplicateUiEntry() { - const currentState: State = getEmptyState(); +/** Tests that a duplicate UI entry won't be added. */ +export async function testAddDuplicateUiEntry(done: () => void) { + const initialState = getEmptyState(); + // Add one UI entry in the store. const uiEntry = new FakeEntryImpl('Ui entry', VolumeManagerCommon.RootType.RECENT); - currentState.uiEntries.push(uiEntry.toURL()); - const newState = addUiEntry(currentState, addUiEntryAction({entry: uiEntry})); - assertEquals(1, newState.uiEntries.length); - assertEquals(uiEntry.toURL(), newState.uiEntries[0]); - // The reference of uiEntries won't change. - assertEquals(currentState.uiEntries, newState.uiEntries); + initialState.uiEntries.push(uiEntry.toURL()); + + const store = setupStore(initialState); + + // Dispatch an action to add an already existed UI entry. + store.dispatch(addUiEntry({entry: uiEntry})); + + // Expect nothing changes in the store. + const want: State['uiEntries'] = [uiEntry.toURL()]; + await waitDeepEquals(store, want, (state) => state.uiEntries); + + done(); } /** - * Tests that adding ui entry for MyFiles will reset the children filed of + * Tests that adding UI entry for MyFiles will reset the children filed of * MyFiles entry. */ -export function testAddUiEntryForMyFiles() { - const currentState: State = getEmptyState(); +export async function testAddUiEntryForMyFiles(done: () => void) { + const initialState = getEmptyState(); // Setup MyFiles entry in the store. const {fileData, volumeInfo} = createMyFilesDataWithVolumeEntry(); const myFilesEntry = fileData.entry as VolumeEntry; - const myFilesVolume = createFakeVolume({ - volumeType: volumeInfo.volumeType, - volumeId: volumeInfo.volumeId, - label: volumeInfo.label, - rootKey: volumeInfo.displayRoot.toURL(), - }); - currentState.allEntries[fileData.entry.toURL()] = fileData; - currentState.volumes[volumeInfo.volumeId] = myFilesVolume; + const myFilesVolume = convertVolumeInfoAndMetadataToVolume( + volumeInfo, createFakeVolumeMetadata(volumeInfo)); + initialState.allEntries[fileData.entry.toURL()] = fileData; + initialState.volumes[volumeInfo.volumeId] = myFilesVolume; // Add children to the MyFiles entry. const childEntry = new GuestOsPlaceholder( 'Play files', 0, chrome.fileManagerPrivate.VmType.ARCVM); - currentState.allEntries[childEntry.toURL()] = createFakeFileData({ - entry: childEntry, - label: 'Play files', - type: EntryType.PLACEHOLDER, - }); + initialState.allEntries[childEntry.toURL()] = + convertEntryToFileData(childEntry); myFilesEntry.addEntry(childEntry); fileData.children.push(childEntry.toURL()); + initialState.uiEntries.push(childEntry.toURL()); + const store = setupStore(initialState); + + // Dispatch an action to add a new UI entry which belongs to MyFiles. const uiEntry = new FakeEntryImpl('Linux files', VolumeManagerCommon.RootType.CROSTINI); - // Before calling addUiEntry(), the new uiEntry should already be in store, - // this is handled by cacheEntries(), we should emulate the process here. This - // is required for sortEntries(). - currentState.allEntries[uiEntry.toURL()] = createFakeFileData({ - entry: uiEntry, - label: 'Linux files', - type: EntryType.PLACEHOLDER, - }); - const newState = addUiEntry(currentState, addUiEntryAction({entry: uiEntry})); - assertEquals(1, newState.uiEntries.length); - assertEquals(uiEntry.toURL(), newState.uiEntries[0]); - // Check the ui entry is added to MyFiles entry. + store.dispatch(addUiEntry({entry: uiEntry})); + + // Expect 2 ui entries in the store. + const want: Partial<State> = { + allEntries: { + [myFilesEntry.toURL()]: { + ...fileData, + children: [ + // Children are in sorted order. + uiEntry.toURL(), + childEntry.toURL(), + ], + }, + [childEntry.toURL()]: convertEntryToFileData(childEntry), + [uiEntry.toURL()]: convertEntryToFileData(uiEntry), + }, + // No sorting order, order is based on the push order. + uiEntries: [childEntry.toURL(), uiEntry.toURL()], + }; + await waitDeepEquals(store, want, (state) => ({ + allEntries: state.allEntries, + uiEntries: state.uiEntries, + })); + + // Check the UI entry is added to MyFiles entry. assertEquals(2, myFilesEntry.getUIChildren().length); assertEquals(uiEntry, myFilesEntry.getUIChildren()[1]); - // Check the children of MyFiles entry gets updated and resorted. - assertArrayEquals( - [ - uiEntry.toURL(), - childEntry.toURL(), - ], - newState.allEntries[myFilesEntry.toURL()].children); + + done(); } /** - * Tests that ui entry won't be added to MyFiles if it's already existed. + * Tests that UI entry won't be added to MyFiles if it's already existed. */ -export function testAddDuplicateUiEntryForMyFiles() { - const currentState: State = getEmptyState(); +export async function testAddDuplicateUiEntryForMyFiles(done: () => void) { + const initialState = getEmptyState(); const uiEntry = new GuestOsPlaceholder( 'Play files', 0, chrome.fileManagerPrivate.VmType.ARCVM); // Setup MyFiles entry and add the new ui entry in the store. const {fileData, volumeInfo} = createMyFilesDataWithVolumeEntry(); const myFilesEntry = fileData.entry as VolumeEntry; - const myFilesVolume = createFakeVolume({ - volumeType: volumeInfo.volumeType, - volumeId: volumeInfo.volumeId, - label: volumeInfo.label, - rootKey: volumeInfo.displayRoot.toURL(), - }); - currentState.allEntries[fileData.entry.toURL()] = fileData; - currentState.volumes[volumeInfo.volumeId] = myFilesVolume; + const myFilesVolume = convertVolumeInfoAndMetadataToVolume( + volumeInfo, createFakeVolumeMetadata(volumeInfo)); + initialState.allEntries[fileData.entry.toURL()] = fileData; + initialState.volumes[volumeInfo.volumeId] = myFilesVolume; myFilesEntry.addEntry(uiEntry); + fileData.children.push(uiEntry.toURL()); + initialState.uiEntries.push(uiEntry.toURL()); - const newState = addUiEntry(currentState, addUiEntryAction({entry: uiEntry})); - assertEquals(1, newState.uiEntries.length); - assertEquals(uiEntry.toURL(), newState.uiEntries[0]); - // Check the ui entry is not being added to MyFiles entry again. + const store = setupStore(initialState); + + // Dispatch an action to add an already existed UI entry. + store.dispatch(addUiEntry({entry: uiEntry})); + + // Expect no changes in the store. + await waitDeepEquals(store, initialState, (state) => state); + + // Check the UI entry is not being added to MyFiles entry again. assertEquals(1, myFilesEntry.getUIChildren().length); assertEquals(uiEntry, myFilesEntry.getUIChildren()[0]); - // Check the children of MyFiles has not been touched. - assertEquals( - newState.allEntries[myFilesEntry.toURL()].children, - currentState.allEntries[myFilesEntry.toURL()].children); + + done(); } /** - * Tests that ui entry won't be added to MyFiles if the corresponding volume + * Tests that UI entry won't be added to MyFiles if the corresponding volume * is already existed. */ -export function testAddDuplicateUiEntryForMyFilesWhenVolumeExists() { - const currentState: State = getEmptyState(); - // Placeholder ui entry and the volume entry it represents have the same +export async function testAddDuplicateUiEntryForMyFilesWhenVolumeExists( + done: () => void) { + const initialState = getEmptyState(); + // Placeholder UI entry and the volume entry it represents have the same // label. const label = 'Play files'; - const uiEntry = - new GuestOsPlaceholder(label, 0, chrome.fileManagerPrivate.VmType.ARCVM); - // Setup MyFiles entry and add the new volume entry in the store. + // Setup MyFiles entry and add the volume entry in the store. const {fileData, volumeInfo} = createMyFilesDataWithVolumeEntry(); const myFilesEntry = fileData.entry as VolumeEntry; - const myFilesVolume = createFakeVolume({ - volumeType: volumeInfo.volumeType, - volumeId: volumeInfo.volumeId, - label: volumeInfo.label, - rootKey: volumeInfo.displayRoot.toURL(), - }); - currentState.allEntries[fileData.entry.toURL()] = fileData; - currentState.volumes[volumeInfo.volumeId] = myFilesVolume; + const myFilesVolume = convertVolumeInfoAndMetadataToVolume( + volumeInfo, createFakeVolumeMetadata(volumeInfo)); + initialState.allEntries[fileData.entry.toURL()] = fileData; + initialState.volumes[volumeInfo.volumeId] = myFilesVolume; const playFilesVolumeInfo = MockVolumeManager.createMockVolumeInfo( VolumeManagerCommon.VolumeType.ANDROID_FILES, label); const playFilesVolumeEntry = new VolumeEntry(playFilesVolumeInfo); myFilesEntry.addEntry(playFilesVolumeEntry); + fileData.children.push(playFilesVolumeEntry.toURL()); - const newState = addUiEntry(currentState, addUiEntryAction({entry: uiEntry})); - // Check the ui entry has not been touched. - assertEquals(currentState.uiEntries, newState.uiEntries); - // Check the ui entry is not being added to MyFiles entry again. + const store = setupStore(initialState); + + // Dispatch an action to add UI entry. + const uiEntry = + new GuestOsPlaceholder(label, 0, chrome.fileManagerPrivate.VmType.ARCVM); + store.dispatch(addUiEntry({entry: uiEntry})); + + // Expect the UI entry is not being added to the store. + await waitDeepEquals(store, [], (state) => state.uiEntries); + + // Check the UI entry is not being added to MyFiles entry again. assertEquals(1, myFilesEntry.getUIChildren().length); assertEquals(playFilesVolumeEntry, myFilesEntry.getUIChildren()[0]); - // Check the children of MyFiles has not been touched. - assertEquals( - newState.allEntries[myFilesEntry.toURL()].children, - currentState.allEntries[myFilesEntry.toURL()].children); -} -/** Tests that ui entry can be removed from store correctly. */ -export function testRemoveUiEntry() { - const currentState: State = getEmptyState(); - const uiEntry = - new FakeEntryImpl('Ui entry', VolumeManagerCommon.RootType.RECENT); - // Setup the ui entry in both uiEntries and allEntries in the store. - currentState.allEntries[uiEntry.toURL()] = createFakeFileData({ - entry: uiEntry, - label: 'Ui entry', - type: EntryType.PLACEHOLDER, - }); - currentState.uiEntries.push(uiEntry.toURL()); - - const newState = - removeUiEntry(currentState, removeUiEntryAction({key: uiEntry.toURL()})); - assertEquals(0, newState.uiEntries.length); -} - -/** Tests that removing non-existed ui entry won't do anything. */ -export function testRemoveNonExistedUiEntry() { - const currentState: State = getEmptyState(); - const uiEntry = - new FakeEntryImpl('Ui entry', VolumeManagerCommon.RootType.TRASH); - const newState = - removeUiEntry(currentState, removeUiEntryAction({key: uiEntry.toURL()})); - // Check that uiEntries won't be touched. - assertEquals(newState.uiEntries, currentState.uiEntries); + done(); } /** - * Tests removing ui entry from MyFiles will reset the children field of + * Tests that UI entry will be disabled if the corresponding volume + * type is disabled in the volume manager. + */ +export async function testAddUiEntryWithDisabledVolumeType(done: () => void) { + const initialState = getEmptyState(); + const store = setupStore(initialState); + + // Dispatch an action to add UI entry. + const {volumeManager} = window.fileManager; + // Disable Android files volume type. + volumeManager.isDisabled = (volumeType) => { + return volumeType === VolumeManagerCommon.VolumeType.ANDROID_FILES; + }; + const uiEntry = new GuestOsPlaceholder( + 'Play files', 0, chrome.fileManagerPrivate.VmType.ARCVM); + store.dispatch(addUiEntry({entry: uiEntry})); + + // Expect the UI entry is being disabled. + await waitUntil(() => uiEntry.disabled === true); + + done(); +} + +/** Tests that UI entry can be removed from store correctly. */ +export async function testRemoveUiEntry(done: () => void) { + const initialState = getEmptyState(); + const uiEntry = + new FakeEntryImpl('Ui entry', VolumeManagerCommon.RootType.RECENT); + // Setup the UI entry in both uiEntries and allEntries in the store. + initialState.allEntries[uiEntry.toURL()] = convertEntryToFileData(uiEntry); + initialState.uiEntries.push(uiEntry.toURL()); + + const store = setupStore(initialState); + + // Dispatch an action to remove the UI entry. + store.dispatch(removeUiEntry({key: uiEntry.toURL()})); + + // Expect the UI entry has been removed. + await waitDeepEquals(store, [], (state) => state.uiEntries); + + done(); +} + +/** Tests that removing non-existed UI entry won't do anything. */ +export async function testRemoveNonExistedUiEntry(done: () => void) { + const initialState = getEmptyState(); + const store = setupStore(initialState); + + // Dispatch an action to remove a non-existed UI entry. + const uiEntry = + new FakeEntryImpl('Ui entry', VolumeManagerCommon.RootType.TRASH); + store.dispatch(removeUiEntry({key: uiEntry.toURL()})); + + // Expect nothing changes in the store. + await waitDeepEquals(store, initialState, (state) => state); + + done(); +} + +/** + * Tests removing UI entry from MyFiles will reset the children field of * MyFiles entry. */ -export function testRemoveUiEntryFromMyFiles() { - const currentState: State = getEmptyState(); +export async function testRemoveUiEntryFromMyFiles(done: () => void) { + const initialState = getEmptyState(); const uiEntry = new FakeEntryImpl('Linux files', VolumeManagerCommon.RootType.CROSTINI); // Setup MyFiles entry and add the ui entry in the store. const {fileData, volumeInfo} = createMyFilesDataWithVolumeEntry(); const myFilesEntry = fileData.entry as VolumeEntry; - const myFilesVolume = createFakeVolume({ - volumeType: volumeInfo.volumeType, - volumeId: volumeInfo.volumeId, - label: volumeInfo.label, - rootKey: volumeInfo.displayRoot.toURL(), - }); - currentState.allEntries[fileData.entry.toURL()] = fileData; - currentState.volumes[volumeInfo.volumeId] = myFilesVolume; + const myFilesVolume = convertVolumeInfoAndMetadataToVolume( + volumeInfo, createFakeVolumeMetadata(volumeInfo)); + initialState.allEntries[fileData.entry.toURL()] = fileData; + initialState.volumes[volumeInfo.volumeId] = myFilesVolume; myFilesEntry.addEntry(uiEntry); fileData.children.push(uiEntry.toURL()); - currentState.allEntries[uiEntry.toURL()] = createFakeFileData({ - entry: uiEntry, - volumeType: VolumeManagerCommon.VolumeType.CROSTINI, - label: 'Linux files', - type: EntryType.PLACEHOLDER, - }); - currentState.uiEntries.push(uiEntry.toURL()); + initialState.allEntries[uiEntry.toURL()] = convertEntryToFileData(uiEntry); + initialState.uiEntries.push(uiEntry.toURL()); - const newState = - removeUiEntry(currentState, removeUiEntryAction({key: uiEntry.toURL()})); - assertEquals(0, newState.uiEntries.length); - // Check the ui entry has also been removed from MyFiles entry. + const store = setupStore(initialState); + + // Dispatch an action to + store.dispatch(removeUiEntry({key: uiEntry.toURL()})); + + // Expect the entry has been removed from MyFiles. + const want: Partial<State> = { + allEntries: { + [myFilesEntry.toURL()]: { + ...convertEntryToFileData(myFilesEntry), + children: [], + }, + [uiEntry.toURL()]: convertEntryToFileData(uiEntry), + }, + uiEntries: [], + }; + await waitDeepEquals(store, want, (state) => ({ + allEntries: state.allEntries, + uiEntries: state.uiEntries, + })); + + // Check the UI entry has also been removed from MyFiles entry. assertEquals(0, myFilesEntry.getUIChildren().length); - assertEquals(0, newState.allEntries[myFilesEntry.toURL()].children.length); + + done(); }
diff --git a/ui/file_manager/file_manager/state/reducers/volumes_unittest.ts b/ui/file_manager/file_manager/state/reducers/volumes_unittest.ts index 55bff85..ab1748a 100644 --- a/ui/file_manager/file_manager/state/reducers/volumes_unittest.ts +++ b/ui/file_manager/file_manager/state/reducers/volumes_unittest.ts
@@ -2,226 +2,380 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {assertEquals} from 'chrome://webui-test/chai_assert.js'; - import {MockVolumeManager} from '../../background/js/mock_volume_manager.js'; -import {EntryList, VolumeEntry} from '../../common/js/files_app_entry_types.js'; +import {EntryList, FakeEntryImpl, VolumeEntry} from '../../common/js/files_app_entry_types.js'; +import {waitUntil} from '../../common/js/test_error_reporting.js'; +import {str} from '../../common/js/util.js'; import {VolumeManagerCommon} from '../../common/js/volume_manager_types.js'; -import {EntryType, FileData, PropStatus} from '../../externs/ts/state.js'; +import {FileData, State} from '../../externs/ts/state.js'; import {VolumeInfo} from '../../externs/volume_info.js'; -import {addVolume as addVolumeAction, removeVolume as removeVolumeAction} from '../actions/volumes.js'; -import {createFakeFileData, createFakeVolume, createFakeVolumeMetadata} from '../for_tests.js'; -import {getEmptyState} from '../store.js'; +import {constants} from '../../foreground/js/constants.js'; +import {addVolume, removeVolume} from '../actions/volumes.js'; +import {createFakeVolumeMetadata, setUpFileManagerOnWindow, setupStore, waitDeepEquals} from '../for_tests.js'; +import {getEmptyState, getEntry} from '../store.js'; -import {addVolume, driveRootEntryListKey, removeVolume} from './volumes.js'; +import {convertEntryToFileData} from './all_entries.js'; +import {convertVolumeInfoAndMetadataToVolume, driveRootEntryListKey} from './volumes.js'; + +export function setUp() { + setUpFileManagerOnWindow(); +} + +/** Generate MyFiles entry with fake entry list. */ +function createMyFilesDataWithEntryList(): FileData { + const myFilesEntryList = + new EntryList('My files', VolumeManagerCommon.RootType.MY_FILES); + return convertEntryToFileData(myFilesEntryList); +} /** Generate MyFiles entry with real volume entry. */ function createMyFilesDataWithVolumeEntry(): {fileData: FileData, volumeInfo: VolumeInfo} { - const volumeManager = new MockVolumeManager(); + const {volumeManager} = window.fileManager; const downloadsVolumeInfo = volumeManager.getCurrentProfileVolumeInfo( VolumeManagerCommon.VolumeType.DOWNLOADS)!; - const fileData = createFakeFileData({ - entry: new VolumeEntry(downloadsVolumeInfo), - volumeType: VolumeManagerCommon.VolumeType.DOWNLOADS, - label: 'My files', - type: EntryType.VOLUME_ROOT, - }); + const fileData = convertEntryToFileData(new VolumeEntry(downloadsVolumeInfo)); return {fileData, volumeInfo: downloadsVolumeInfo}; } /** Tests that MyFiles volume can be added correctly. */ -export function testAddMyFilesVolume() { - const currentState = getEmptyState(); - const {fileData, volumeInfo} = createMyFilesDataWithVolumeEntry(); - const myFilesVolume = createFakeVolume({ - volumeType: volumeInfo.volumeType, - volumeId: volumeInfo.volumeId, - label: volumeInfo.label, - rootKey: volumeInfo.displayRoot.toURL(), - }); - currentState.allEntries[fileData.entry.toURL()] = fileData; - currentState.volumes[volumeInfo.volumeId] = myFilesVolume; - // Mark its volume entry as disabled. - (fileData.entry as VolumeEntry).disabled = true; - // Put MyFiles and its sub volumes in the store. - const playFilesVolume = createFakeVolume({ - volumeId: 'playFilesId', - volumeType: VolumeManagerCommon.VolumeType.ANDROID_FILES, - label: 'Play files', - rootKey: 'filesystem:chrome://android-files-url', - }); - currentState.volumes[playFilesVolume.volumeId] = playFilesVolume; - const crostiniFilesVolume = createFakeVolume({ - volumeId: 'volumeInMyFiles2', - volumeType: VolumeManagerCommon.VolumeType.CROSTINI, - label: 'Linux files', - rootKey: 'filesystem:chrome://crostini-files-url', - }); - currentState.volumes[crostiniFilesVolume.volumeId] = crostiniFilesVolume; +export async function testAddMyFilesVolume(done: () => void) { + const initialState = getEmptyState(); + // Put MyFiles entry list in the store. + const myFilesFileData = createMyFilesDataWithEntryList(); + const myFilesEntryList = myFilesFileData.entry as EntryList; + initialState.allEntries[myFilesEntryList.toURL()] = myFilesFileData; + initialState.uiEntries.push(myFilesEntryList.toURL()); + // Put Play files placeholder UI entry in the store. + const playFilesEntry = new FakeEntryImpl( + 'Play files', VolumeManagerCommon.RootType.ANDROID_FILES); + initialState.allEntries[playFilesEntry.toURL()] = + convertEntryToFileData(playFilesEntry); + myFilesEntryList.addEntry(playFilesEntry); + initialState.uiEntries.push(playFilesEntry.toURL()); + myFilesFileData.children.push(playFilesEntry.toURL()); + // Put Linux files volume entry in the store. + const linuxFilesVolumeInfo = MockVolumeManager.createMockVolumeInfo( + VolumeManagerCommon.VolumeType.CROSTINI, 'linuxFilesId', 'Linux files'); + const linuxFilesEntry = new VolumeEntry(linuxFilesVolumeInfo); + const {volumeManager} = window.fileManager; + volumeManager.volumeInfoList.add(linuxFilesVolumeInfo); + initialState.allEntries[linuxFilesEntry.toURL()] = + convertEntryToFileData(linuxFilesEntry); + const linuxFilesVolume = convertVolumeInfoAndMetadataToVolume( + linuxFilesVolumeInfo, createFakeVolumeMetadata(linuxFilesVolumeInfo)); + initialState.volumes[linuxFilesVolume.volumeId] = linuxFilesVolume; + myFilesFileData.children.push(linuxFilesEntry.toURL()); + myFilesEntryList.addEntry(linuxFilesEntry); - const volumeMetadata = createFakeVolumeMetadata( - {volumeType: volumeInfo.volumeType, volumeId: volumeInfo.volumeId}); - const newState = addVolume(currentState, addVolumeAction({ - volumeInfo, - volumeMetadata, - })); - const volume = newState.volumes[volumeInfo.volumeId]; - // Check all volume fields are set correctly. - assertEquals(volume.volumeId, volumeMetadata.volumeId); - assertEquals(volume.volumeType, volumeMetadata.volumeType); - assertEquals(volume.rootKey, volumeInfo.displayRoot.toURL()); - assertEquals(volume.status, PropStatus.SUCCESS); - assertEquals(volume.label, volumeInfo.label); - assertEquals(volume.error, volumeMetadata.mountCondition); - assertEquals(volume.deviceType, volumeMetadata.deviceType); - assertEquals(volume.devicePath, volumeMetadata.devicePath); - assertEquals(volume.isReadOnly, volumeMetadata.isReadOnly); - assertEquals( - volume.isReadOnlyRemovableDevice, - volumeMetadata.isReadOnlyRemovableDevice); - assertEquals(volume.providerId, volumeMetadata.providerId); - assertEquals(volume.configurable, volumeMetadata.configurable); - assertEquals(volume.watchable, volumeMetadata.watchable); - assertEquals(volume.source, volumeMetadata.source); - assertEquals(volume.diskFileSystemType, volumeMetadata.diskFileSystemType); - assertEquals(volume.iconSet, volumeMetadata.iconSet); - assertEquals(volume.driveLabel, volumeMetadata.driveLabel); - assertEquals(volume.vmType, volumeMetadata.vmType); - // Because its volume entry is disabled. - assertEquals(volume.isDisabled, true); - assertEquals(volume.prefixKey, undefined); - // Check all child volumes has prefix key setup. - assertEquals( - fileData.entry.toURL(), - newState.volumes[playFilesVolume.volumeId].prefixKey); - assertEquals( - fileData.entry.toURL(), - newState.volumes[crostiniFilesVolume.volumeId].prefixKey); + const store = setupStore(initialState); + + // Dispatch an action to add MyFiles volume. + const {fileData, volumeInfo} = createMyFilesDataWithVolumeEntry(); + const myFilesVolumeEntry = fileData.entry as VolumeEntry; + const volumeMetadata = createFakeVolumeMetadata(volumeInfo); + store.dispatch(addVolume({ + volumeInfo, + volumeMetadata, + })); + + // Expect the newly added volume is in the store. + myFilesVolumeEntry.addEntry(playFilesEntry); + myFilesVolumeEntry.addEntry(linuxFilesEntry); + const want: Partial<State> = { + allEntries: { + [playFilesEntry.toURL()]: convertEntryToFileData(playFilesEntry), + [linuxFilesEntry.toURL()]: convertEntryToFileData(linuxFilesEntry), + [myFilesVolumeEntry.toURL()]: fileData, + }, + volumes: { + [linuxFilesVolumeInfo.volumeId]: { + ...linuxFilesVolume, + // Updated to MyFiles volume key. + prefixKey: fileData.entry.toURL(), + }, + [volumeInfo.volumeId]: + convertVolumeInfoAndMetadataToVolume(volumeInfo, volumeMetadata), + }, + uiEntries: [playFilesEntry.toURL()], + }; + await waitDeepEquals(store, want, (state) => ({ + allEntries: state.allEntries, + volumes: state.volumes, + uiEntries: state.uiEntries, + })); + + done(); } /** Tests that volume nested in MyFiles can be added correctly. */ -export function testAddNestedMyFilesVolume() { - const currentState = getEmptyState(); +export async function testAddNestedMyFilesVolume(done: () => void) { + const initialState = getEmptyState(); // Put MyFiles in the store. const {fileData, volumeInfo} = createMyFilesDataWithVolumeEntry(); - const myFilesVolume = createFakeVolume({ - volumeType: volumeInfo.volumeType, - volumeId: volumeInfo.volumeId, - label: volumeInfo.label, - rootKey: volumeInfo.displayRoot.toURL(), - }); - currentState.allEntries[fileData.entry.toURL()] = fileData; - currentState.volumes[volumeInfo.volumeId] = myFilesVolume; + const myFilesVolumeEntry = fileData.entry as VolumeEntry; + const myFilesVolume = convertVolumeInfoAndMetadataToVolume( + volumeInfo, createFakeVolumeMetadata(volumeInfo)); + initialState.allEntries[myFilesVolumeEntry.toURL()] = fileData; + initialState.volumes[volumeInfo.volumeId] = myFilesVolume; + // Put Play files placeholder UI entry in the store. + const playFilesUiEntry = new FakeEntryImpl( + 'Play files', VolumeManagerCommon.RootType.ANDROID_FILES); + myFilesVolumeEntry.addEntry(playFilesUiEntry); + fileData.children.push(playFilesUiEntry.toURL()); + initialState.uiEntries.push(playFilesUiEntry.toURL()); + initialState.allEntries[playFilesUiEntry.toURL()] = + convertEntryToFileData(playFilesUiEntry); + const store = setupStore(initialState); + + // Dispatch an action to add Play files volume. + const {volumeManager} = window.fileManager; const playFilesVolumeInfo = MockVolumeManager.createMockVolumeInfo( VolumeManagerCommon.VolumeType.ANDROID_FILES, 'playFilesId', - 'Play files'); - const playFilesVolumeMetadata = createFakeVolumeMetadata({ - volumeType: playFilesVolumeInfo.volumeType, - volumeId: playFilesVolumeInfo.volumeId, - }); - const newState = addVolume(currentState, addVolumeAction({ - volumeInfo: playFilesVolumeInfo, - volumeMetadata: playFilesVolumeMetadata, - })); - // Check the newly added volume has prefix key setup. - assertEquals( - fileData.entry.toURL(), - newState.volumes[playFilesVolumeInfo.volumeId].prefixKey); + playFilesUiEntry.label); + volumeManager.volumeInfoList.add(playFilesVolumeInfo); + const playFilesVolumeMetadata = createFakeVolumeMetadata(playFilesVolumeInfo); + store.dispatch(addVolume({ + volumeInfo: playFilesVolumeInfo, + volumeMetadata: playFilesVolumeMetadata, + })); + + // Expect the new play file volume will be nested inside MyFiles and the old + // placeholder will be removed. + const playFilesVolumeEntry = new VolumeEntry(playFilesVolumeInfo); + myFilesVolumeEntry.addEntry(playFilesVolumeEntry); + const want: Partial<State> = { + allEntries: { + [myFilesVolumeEntry.toURL()]: { + ...fileData, + children: [playFilesVolumeEntry.toURL()], + }, + [playFilesVolumeEntry.toURL()]: + convertEntryToFileData(playFilesVolumeEntry), + }, + volumes: { + [myFilesVolume.volumeId]: myFilesVolume, + [playFilesVolumeInfo.volumeId]: { + ...convertVolumeInfoAndMetadataToVolume( + playFilesVolumeInfo, playFilesVolumeMetadata), + prefixKey: myFilesVolumeEntry.toURL(), + }, + }, + uiEntries: [], + }; + await waitDeepEquals(store, want, (state) => ({ + allEntries: state.allEntries, + volumes: state.volumes, + uiEntries: state.uiEntries, + })); + + done(); } /** Tests that drive volume can be added correctly. */ -export function testAddDriveVolume(done: () => void) { - const currentState = getEmptyState(); - // Put FakeDriveRoot in the store. - const fakeDriveRootEntry = new EntryList( - 'Google Drive', VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT); - currentState.allEntries[driveRootEntryListKey] = createFakeFileData({ - entry: fakeDriveRootEntry, - label: 'Google Drive', - type: EntryType.ENTRY_LIST, - }); +export async function testAddDriveVolume(done: () => void) { + const initialState = getEmptyState(); + const store = setupStore(initialState); - const volumeManager = new MockVolumeManager(); + // Dispatch an action to add Drive volume. + const {volumeManager} = window.fileManager; const driveVolumeInfo = volumeManager.getCurrentProfileVolumeInfo( VolumeManagerCommon.VolumeType.DRIVE)!; - const driveVolumeMetadata = createFakeVolumeMetadata({ - volumeType: driveVolumeInfo.volumeType, - volumeId: driveVolumeInfo.volumeId, - }); + const driveVolumeMetadata = createFakeVolumeMetadata(driveVolumeInfo); // DriveFS takes time to resolve. - driveVolumeInfo.resolveDisplayRoot(() => { - const newState = addVolume(currentState, addVolumeAction({ - volumeInfo: driveVolumeInfo, - volumeMetadata: driveVolumeMetadata, - })); - // Check the newly added volume has prefix key setup. - assertEquals( - fakeDriveRootEntry.toURL(), - newState.volumes[driveVolumeInfo.volumeId].prefixKey); + await driveVolumeInfo.resolveDisplayRoot(); + store.dispatch(addVolume({ + volumeInfo: driveVolumeInfo, + volumeMetadata: driveVolumeMetadata, + })); - done(); - }); + // Expect all fake entries inside Drive will be added as its children. + const myFilesFileData = createMyFilesDataWithEntryList(); + const driveFakeRootEntryList = new EntryList( + str('DRIVE_DIRECTORY_LABEL'), + VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT); + const driveVolumeEntry = new VolumeEntry(driveVolumeInfo); + const {sharedDriveDisplayRoot, computersDisplayRoot, fakeEntries} = + driveVolumeInfo; + const fakeSharedWithMeEntry = + fakeEntries[VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME]; + const fakeOfflineEntry = + fakeEntries[VolumeManagerCommon.RootType.DRIVE_OFFLINE]; + driveFakeRootEntryList.addEntry(driveVolumeEntry); + driveFakeRootEntryList.addEntry(sharedDriveDisplayRoot); + driveFakeRootEntryList.addEntry(computersDisplayRoot); + driveFakeRootEntryList.addEntry(fakeSharedWithMeEntry); + driveFakeRootEntryList.addEntry(fakeOfflineEntry); + const want: Partial<State> = { + allEntries: { + // My Drive. + [driveVolumeEntry.toURL()]: convertEntryToFileData(driveVolumeEntry), + // My Files entry list. + [myFilesFileData.entry.toURL()]: myFilesFileData, + // Fake Drive root entry list. + [driveRootEntryListKey]: convertEntryToFileData(driveFakeRootEntryList), + // Shared with me. + [fakeSharedWithMeEntry.toURL()]: + convertEntryToFileData(fakeSharedWithMeEntry), + // Offline. + [fakeOfflineEntry.toURL()]: convertEntryToFileData(fakeOfflineEntry), + // Shared drives and Computers won't be here because they will be cleared. + }, + volumes: { + [driveVolumeInfo.volumeId]: { + ...convertVolumeInfoAndMetadataToVolume( + driveVolumeInfo, driveVolumeMetadata), + prefixKey: driveRootEntryListKey, + }, + }, + uiEntries: [ + myFilesFileData.entry.toURL(), + driveRootEntryListKey, + fakeSharedWithMeEntry.toURL(), + fakeOfflineEntry.toURL(), + ], + }; + await waitDeepEquals(store, want, (state) => ({ + allEntries: state.allEntries, + volumes: state.volumes, + uiEntries: state.uiEntries, + })); + + done(); } /** Tests that multiple partition volumes can be added correctly. */ -export function testAddVolumeForMultipleUsbPartitionsGrouping() { - const currentState = getEmptyState(); - // Add USB/partition-1 into the store. - const devicePath = 'device/path/1'; - const partition1 = createFakeVolume({ - volumeId: 'removable:partition1', - volumeType: VolumeManagerCommon.VolumeType.REMOVABLE, - rootKey: 'partition1-url', - label: 'Partition 1', - devicePath, - driveLabel: 'USB_Drive', - }); - currentState.volumes[partition1.volumeId] = partition1; - // Add its parent entry to the store. - const parentEntry = new EntryList( - partition1.driveLabel!, VolumeManagerCommon.RootType.REMOVABLE, - partition1.devicePath); - currentState.allEntries[parentEntry.toURL()] = createFakeFileData({ - entry: parentEntry, - label: partition1.driveLabel!, - type: EntryType.ENTRY_LIST, - }); +export async function testAddVolumeForMultipleUsbPartitionsGrouping( + done: () => void) { + const initialState = getEmptyState(); + // Put partition-1 volume in the store. + const {volumeManager} = window.fileManager; + const partition1VolumeInfo = MockVolumeManager.createMockVolumeInfo( + VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:partition1', + 'Partition 1', '/device/path/1'); + volumeManager.volumeInfoList.add(partition1VolumeInfo); + const partition1VolumeEntry = new VolumeEntry(partition1VolumeInfo); + const partition1FileData = convertEntryToFileData(partition1VolumeEntry); + const partition1VolumeMetadata = + createFakeVolumeMetadata(partition1VolumeInfo); + partition1VolumeMetadata.driveLabel = 'USB_Drive'; + const partition1Volume = convertVolumeInfoAndMetadataToVolume( + partition1VolumeInfo, partition1VolumeMetadata); + initialState.volumes[partition1Volume.volumeId] = partition1Volume; + initialState.allEntries[partition1VolumeEntry.toURL()] = partition1FileData; + const store = setupStore(initialState); + + // Dispatch an action to add partition-2 volume. const partition2VolumeInfo = MockVolumeManager.createMockVolumeInfo( VolumeManagerCommon.VolumeType.REMOVABLE, 'removable:partition2', - 'Partition 2', devicePath); - const partition2VolumeMetadata = createFakeVolumeMetadata({ - volumeType: partition2VolumeInfo.volumeType, - volumeId: partition2VolumeInfo.volumeId, - devicePath: partition1.devicePath, - driveLabel: partition1.driveLabel, + 'Partition 2', partition1VolumeInfo.devicePath); + const partition2VolumeMetadata = + createFakeVolumeMetadata(partition2VolumeInfo); + partition2VolumeMetadata.driveLabel = partition1VolumeMetadata.driveLabel; + store.dispatch(addVolume({ + volumeInfo: partition2VolumeInfo, + volumeMetadata: partition2VolumeMetadata, + })); + + // Expect the partition-2 volume is in the store and there will be a wrapper + // entry created to group both partition-1 and partition-2. + const myFilesFileData = createMyFilesDataWithEntryList(); + const partition2VolumeEntry = new VolumeEntry(partition2VolumeInfo); + const parentEntry = new EntryList( + partition1VolumeMetadata.driveLabel, + VolumeManagerCommon.RootType.REMOVABLE, partition1VolumeInfo.devicePath); + parentEntry.addEntry(partition1VolumeEntry); + parentEntry.addEntry(partition2VolumeEntry); + const want: Partial<State> = { + allEntries: { + // Partition-1 volume. + [partition1VolumeEntry.toURL()]: { + ...partition1FileData, + icon: constants.ICON_TYPES.UNKNOWN_REMOVABLE, + }, + // Partition-2 volume. + [partition2VolumeEntry.toURL()]: { + ...convertEntryToFileData(partition2VolumeEntry), + icon: constants.ICON_TYPES.UNKNOWN_REMOVABLE, + }, + // My Files entry list. + [myFilesFileData.entry.toURL()]: myFilesFileData, + // Parent wrapper entry. + [parentEntry.toURL()]: { + ...convertEntryToFileData(parentEntry), + isEjectable: true, + }, + }, + volumes: { + [partition1VolumeInfo.volumeId]: { + ...partition1Volume, + prefixKey: parentEntry.toURL(), + }, + [partition2VolumeInfo.volumeId]: { + ...convertVolumeInfoAndMetadataToVolume( + partition2VolumeInfo, partition2VolumeMetadata), + prefixKey: parentEntry.toURL(), + }, + }, + }; + await waitDeepEquals(store, want, (state) => ({ + allEntries: state.allEntries, + volumes: state.volumes, + })); + + done(); +} + +/** + * Tests that volume will be disabled if it is disabled in the volume manager. + */ +export async function testAddDisabledVolume(done: () => void) { + const initialState = getEmptyState(); + const store = setupStore(initialState); + + // Dispatch an action to add crostini volume. + const {volumeManager} = window.fileManager; + const volumeInfo = MockVolumeManager.createMockVolumeInfo( + VolumeManagerCommon.VolumeType.CROSTINI, 'crostini', 'Linux files'); + volumeManager.volumeInfoList.add(volumeInfo); + const volumeMetadata = createFakeVolumeMetadata(volumeInfo); + // Disable crostini volume type. + volumeManager.isDisabled = (volumeType) => { + return volumeType === VolumeManagerCommon.VolumeType.CROSTINI; + }; + store.dispatch(addVolume({volumeInfo, volumeMetadata})); + + // Expect the volume entry is being disabled. + await waitUntil(() => { + const volumeEntry = + getEntry(store.getState(), volumeInfo.displayRoot.toURL()) as + VolumeEntry; + return volumeEntry && volumeEntry.disabled === true; }); - const newState = addVolume(currentState, addVolumeAction({ - volumeInfo: partition2VolumeInfo, - volumeMetadata: partition2VolumeMetadata, - })); - // Check the newly added volume and all existing volumes belonging to the same - // group have prefix key setup. - assertEquals( - parentEntry.toURL(), newState.volumes[partition1.volumeId].prefixKey); - assertEquals( - parentEntry.toURL(), - newState.volumes[partition2VolumeInfo.volumeId].prefixKey); + + done(); } /** Tests that volume can be removed correctly. */ -export function testRemoveVolume() { - const currentState = getEmptyState(); - const volume = createFakeVolume({ - volumeId: 'test', - volumeType: VolumeManagerCommon.VolumeType.ARCHIVE, - label: 'test.zip', - rootKey: 'test-root', - }); - currentState.volumes[volume.volumeId] = volume; - const newState = removeVolume( - currentState, removeVolumeAction({volumeId: volume.volumeId})); - assertEquals(undefined, newState.volumes[volume.volumeId]); +export async function testRemoveVolume(done: () => void) { + const initialState = getEmptyState(); + const {volumeManager} = window.fileManager; + const volumeInfo = MockVolumeManager.createMockVolumeInfo( + VolumeManagerCommon.VolumeType.ARCHIVE, 'test', 'test.zip'); + volumeManager.volumeInfoList.add(volumeInfo); + const volume = convertVolumeInfoAndMetadataToVolume( + volumeInfo, createFakeVolumeMetadata(volumeInfo)); + initialState.volumes[volume.volumeId] = volume; + + const store = setupStore(initialState); + + // Dispatch an action to remove the volume. + store.dispatch(removeVolume({volumeId: volume.volumeId})); + + // Expect the volume will be removed from the store. + await waitDeepEquals(store, {}, (state) => state.volumes); + + done(); }
diff --git a/ui/file_manager/file_manager/widgets/xf_icon_unittest.ts b/ui/file_manager/file_manager/widgets/xf_icon_unittest.ts index a4bf3c5..b5410ef 100644 --- a/ui/file_manager/file_manager/widgets/xf_icon_unittest.ts +++ b/ui/file_manager/file_manager/widgets/xf_icon_unittest.ts
@@ -77,10 +77,10 @@ const span = getSpanFromIcon(icon); assertTrue(span.classList.contains('keep-color')); - assertTrue(window.getComputedStyle(span).backgroundImage.includes( - '-webkit-image-set')); - assertTrue(window.getComputedStyle(span).backgroundImage.includes('1x')); - assertFalse(window.getComputedStyle(span).backgroundImage.includes('2x')); + assertTrue( + window.getComputedStyle(span).backgroundImage.includes('image-set')); + assertTrue(window.getComputedStyle(span).backgroundImage.includes('1dppx')); + assertFalse(window.getComputedStyle(span).backgroundImage.includes('2dppx')); done(); } @@ -96,10 +96,10 @@ const span = getSpanFromIcon(icon); assertTrue(span.classList.contains('keep-color')); - assertTrue(window.getComputedStyle(span).backgroundImage.includes( - '-webkit-image-set')); - assertFalse(window.getComputedStyle(span).backgroundImage.includes('1x')); - assertTrue(window.getComputedStyle(span).backgroundImage.includes('2x')); + assertTrue( + window.getComputedStyle(span).backgroundImage.includes('image-set')); + assertFalse(window.getComputedStyle(span).backgroundImage.includes('1dppx')); + assertTrue(window.getComputedStyle(span).backgroundImage.includes('2dppx')); done(); } @@ -115,10 +115,10 @@ const span = getSpanFromIcon(icon); assertTrue(span.classList.contains('keep-color')); - assertTrue(window.getComputedStyle(span).backgroundImage.includes( - '-webkit-image-set')); - assertTrue(window.getComputedStyle(span).backgroundImage.includes('1x')); - assertTrue(window.getComputedStyle(span).backgroundImage.includes('2x')); + assertTrue( + window.getComputedStyle(span).backgroundImage.includes('image-set')); + assertTrue(window.getComputedStyle(span).backgroundImage.includes('1dppx')); + assertTrue(window.getComputedStyle(span).backgroundImage.includes('2dppx')); done(); }
diff --git a/ui/views/controls/menu/menu_host_root_view.cc b/ui/views/controls/menu/menu_host_root_view.cc index b5172aa2..4f9c3ea9 100644 --- a/ui/views/controls/menu/menu_host_root_view.cc +++ b/ui/views/controls/menu/menu_host_root_view.cc
@@ -74,11 +74,8 @@ return RootView::GetTooltipHandlerForPoint(point); } -void MenuHostRootView::OnEventProcessingFinished( - ui::Event* event, - ui::EventTarget* target, - const ui::EventDispatchDetails& details) { - RootView::OnEventProcessingFinished(event, target, details); +void MenuHostRootView::OnEventProcessingFinished(ui::Event* event) { + RootView::OnEventProcessingFinished(event); // Forward unhandled gesture events to our menu controller. // TODO(tdanderson): Investigate whether this should be moved into a
diff --git a/ui/views/controls/menu/menu_host_root_view.h b/ui/views/controls/menu/menu_host_root_view.h index 3c63954b4..87b5c81 100644 --- a/ui/views/controls/menu/menu_host_root_view.h +++ b/ui/views/controls/menu/menu_host_root_view.h
@@ -48,10 +48,7 @@ private: // ui::EventProcessor: - void OnEventProcessingFinished( - ui::Event* event, - ui::EventTarget* target, - const ui::EventDispatchDetails& details) override; + void OnEventProcessingFinished(ui::Event* event) override; // Returns the MenuController for this MenuHostRootView. MenuController* GetMenuController();
diff --git a/ui/views/widget/root_view.cc b/ui/views/widget/root_view.cc index 7025f7d..78b57f7 100644 --- a/ui/views/widget/root_view.cc +++ b/ui/views/widget/root_view.cc
@@ -396,10 +396,7 @@ gesture_handler_set_before_processing_ = !!gesture_handler_; } -void RootView::OnEventProcessingFinished( - ui::Event* event, - ui::EventTarget* target, - const ui::EventDispatchDetails& details) { +void RootView::OnEventProcessingFinished(ui::Event* event) { VLOG(5) << "RootView::OnEventProcessingFinished(" << event->ToString() << ")"; // If |event| was not handled and |gesture_handler_| was not set by the // dispatch of a previous gesture event, then no default gesture handler
diff --git a/ui/views/widget/root_view.h b/ui/views/widget/root_view.h index 228be4a..a786ee1 100644 --- a/ui/views/widget/root_view.h +++ b/ui/views/widget/root_view.h
@@ -110,10 +110,7 @@ ui::EventTarget* GetRootForEvent(ui::Event* event) override; ui::EventTargeter* GetDefaultEventTargeter() override; void OnEventProcessingStarted(ui::Event* event) override; - void OnEventProcessingFinished( - ui::Event* event, - ui::EventTarget* target, - const ui::EventDispatchDetails& details) override; + void OnEventProcessingFinished(ui::Event* event) override; // View: const Widget* GetWidget() const override;
diff --git a/ui/webui/resources/js/icon.ts b/ui/webui/resources/js/icon.ts index 9758d6b..6c2a3b2 100644 --- a/ui/webui/resources/js/icon.ts +++ b/ui/webui/resources/js/icon.ts
@@ -54,14 +54,14 @@ } /** - * Generates a CSS -webkit-image-set for a chrome:// url. + * Generates a CSS image-set for a chrome:// url. * An entry in the image set is added for each of getSupportedScaleFactors(). * The scale-factor-specific url is generated by replacing the first instance * of 'scalefactor' in |path| with the numeric scale factor. * * @param path The URL to generate an image set for. * 'scalefactor' should be a substring of |path|. - * @return The CSS -webkit-image-set. + * @return The CSS image-set. */ function getImageSet(path: string): string { const supportedScaleFactors = getSupportedScaleFactors(); @@ -83,7 +83,7 @@ s += ', '; } } - return '-webkit-image-set(' + s + ')'; + return 'image-set(' + s + ')'; } /** @@ -109,10 +109,10 @@ } /** - * Creates a CSS -webkit-image-set for a favicon. + * Creates a CSS image-set for a favicon. * * @param url URL of the favicon - * @return -webkit-image-set for the favicon + * @return image-set for the favicon */ export function getFavicon(url: string): string { const faviconUrl = getBaseFaviconUrl(); @@ -121,7 +121,7 @@ } /** - * Creates a CSS -webkit-image-set for a favicon request based on a page URL. + * Creates a CSS image-set for a favicon request based on a page URL. * * @param url URL of the original page * @param isSyncedUrlForHistoryUi Should be set to true only if the @@ -133,7 +133,7 @@ * @param forceLightMode Flag to force the service to show the light * mode version of the default favicon. * - * @return -webkit-image-set for the favicon. + * @return image-set for the favicon. */ export function getFaviconForPageURL( url: string, isSyncedUrlForHistoryUi: boolean,